К сожалению, пока .gitignore
(или даже любой файл) отслеживается (что означает в индексе ), логически отдельная копия этого файла переходит в каждый коммит вы делаете.В результате этого невозможно достичь того, чего вы хотите.
Самое близкое, что вы можете сделать, как упомянул phd , хранить в каждом новом коммите .gitignore
запись типа символьная ссылка (режим 120000
в Git-internal-ese).Тогда, даже если у каждого коммита есть логически отдельная (возможно, физически общая) копия целевого пути пути ссылки, когда Git будет читать содержимое .gitignore
, он будет читать содержимое целевого пути, а не .gitignore
файл рабочего дерева, который был только что скопирован из любого коммита, который вы указали git checkout
, чтобы выйти.
Однако вы можете автоматизировать процесс обновления .gitignore
файлов при нескольких фиксациях.Самый простой способ сделать это, вероятно, использовать git worktree add
для создания отдельного рабочего дерева, в котором будут выполняться обновления.Предполагается, что ваша версия Git по крайней мере 2.5, а лучше 2.15 (чтобы избежать ошибок в git worktree
).
Ниже приведен полностью непроверенный сценарий, который для каждой ветви удаленного отслеживания гарантирует, чтоКоммит tip этой ветви удаленного отслеживания содержит .gitignore
, совпадающий с текущим в основной ветви в репозитории с использованием добавленного рабочего дерева.Для этого он использует режим отсоединенного HEAD (а также, когда это уместно, одновременно выдвигает более одного коммита).Он неправильно обрабатывает несколько удаленных имен с одним URL;для этого удалите git fetch --all
и раскомментируйте очевидную строку в new_remote
.
#! /bin/sh
#
# git-update-ignores-across-remote-tracking-branches
. git-sh-setup # get script goodies, and make sure we're at top level
require_work_tree # make sure we have a work-tree, too
# Where is our ignore file? (absolute path)
IFILE=$(readlink -f .gitignore) || die "cannot find .gitignore file"
# set up a temporary file; remove it on exit
TF=$(mktemp) || die "cannot create temporary file"
trap "rm -f $TF" 0 1 2 3 15
# Use a work-tree in ../update-ignores
if [ ! -d ../update-ignores ]; then
[ -e ../update-ignores ] &&
die "../update-ignores exists but is not a directory"
git worktree add ../update-ignores --detach ||
die "unable to create ../update-ignores"
else
# Should use git worktree list --porcelain to verify that
# ../update-ignores is an added, detached work-tree, but
# I leave that to someone else. It might also be good to
# leave remote-tracking names for other added work-trees
# alone, but again, that's for someone else to write.
fi
# Find upstream of current branch, if we're on a branch and there is
# an upstream - we won't attempt to do anything to that one, so as to
# avoid creating headaches for the main work-tree. Note that this
# sets UPSTREAM="" if the rev-parse fails.
UPSTREAM=$(git rev-parse --symbolic-full-name HEAD@{u} 2>/dev/null)
# Now attempt to update remote-tracking names. Update all remotes
# first so that we are in sync, then list all names into temporary file.
# From here on, we'll work in the update-ignores work-tree.
cd ../update-ignores
require_clean_work_tree "update ignores"
git fetch --all || die "unable to fetch --all"
git for-each-ref --format='%(refname)' refs/remotes > $TF
REMOTE=
UPDATED=
# Function: push UPDATED to REMOTE. Set REMOTE to $1 and clear UPDATED.
# Does nothing if UPDATED or REMOTE are empty, so safe to use an extra time.
new_remote() {
local u="$UPDATED" r="$REMOTE"
if [ "$u" != "" -a "$r" != "" ]; then
git push $r $u || die "failed to push!"
fi
UPDATED=
REMOTE=$1
# [ -z "$REMOTE" ] || git fetch $REMOTE || die "unable to fetch from $REMOTE"
}
while read name; do
# skip the upstream of the main repo
[ $name == "$UPSTREAM" ] && continue
# Update this branch's .gitignore, and remember to push this commit.
# If we're switching remotes, clean out what we've done so far.
shortname=${name##refs/remotes/} # e.g., origin/master or r/feature/X
remote=${shortname%%/*} # e.g., origin or r
branch=${shortname#remote/} # e.g., master or feature/X
# if we're changing remotes, clear out the old one
[ $remote != $REMOTE ] && new_remote $remote
# switch detached HEAD to commit corresponding to remote-tracking name
git checkout -q $name || die "unable to check out $name"
# update .gitignore (but skip all this if it's correct)
cmp -s .gitignore $IFILE 2>/dev/null && continue
cp $IFILE .gitignore || die "unable to copy $IFILE to .gitignore"
git add .gitignore || die "unable to add .gitignore"
# UGH: terrible commit message below, please fix
git commit -q -m "update .gitignore" || die "unable to commit"
commit=$(git rev-parse HEAD) || die "failed to rev-parse HEAD"
# remember to push this commit (by hash ID) to refs/heads/$shortname
# on $REMOTE (which is correct because of new_remote above)
UPDATED="$UPDATED $commit:refs/heads/$shortname"
done < $TF
# push any accumulated commits, or do nothing if none accumulated
new_remote
# and we're done!
exit 0