不幸的是,只要.gitignore(或实际上任何文件)被跟踪(意思是在索引中),该文件的逻辑独立副本就会进入每一个你做出的承诺。这样做的结果是无法实现您想要的。
最接近的方法是,作为phd mentioned,在每个新提交中存储一个符号链接类型的.gitignore 条目(Git-internal-ese 中的模式120000)。然后,即使每次提交都有链接目标路径名的逻辑分离(可能是物理共享)副本,当 Git 去读取 .gitignore 的内容时,它也会读取目标路径名的内容,而不是 @ 987654326@ 工作树文件,该文件刚刚从您告诉git checkout 退出的任何提交中复制出来。
但是,您可以跨多个提交自动更新.gitignore 文件的过程。最简单的方法可能是使用git worktree add 创建一个单独的工作树来进行更新。这假设您的 Git 版本至少为 2.5,最好至少为 2.15(以避免git worktree 中的错误)。
以下是一个完全未经测试的脚本,它将针对每个远程跟踪分支确保该远程跟踪分支的提示提交包含与主存储库中当前分支中的匹配的.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