跨所有分支的相同全局远程 gitignore 文件
same global remote gitignore file across all branches
我想要实现的是拥有一个 .gitignore 文件(由 git 跟踪),该文件在远程存储库(托管在 [=26] 中的所有分支中同步=]),因此也在相应的本地分支机构上。我目前使用的 .gitignore 文件并不完美,所以我偶尔(有时每天多次)必须更新它。然后的问题是我必须 checkout
.git 手动忽略所有分支中的文件,随着创建更多分支,这变得越来越麻烦。所以对于我做的每个分支(更新后的 .gitignore in branch master)
git checkout some-outdated-branch
git checkout master .gitignore
git add .gitignore
git rm -r --cached .
git add .
git commit -m "Updated .gitignore and fixed tracked files"
因为这对于多个分支来说相对耗时,所以我试图寻找一种方法来在分支 master 中(或在单独的 gitignore-branch 分支中拥有一个 .gitignore 文件) 自动同步所有分支(本地,以及推送时远程)。
这里的问题是我不想使用 git config --global core.excludesfile /path/to/local/.gitignore
(建议 here) as I want my project partners to also use that specific .gitignore file and not have to change the git config
file for this. In this comment 其他人正在问这个问题,但还没有得到回答。我在 Stack Overflow 上也找不到任何关于我的答案问题。
简短摘要
我只想编辑一个分支上的 .gitignore 文件,并以一种省时省力的方式(自动)将该更改与所有其他分支同步。之后,我想将所有分支中的更改推送到远程存储库(最好只使用一行或几行代码,而不必为每个分支重新提交带有相应提交消息的提交)。
不幸的是,只要.gitignore
(或任何文件)被跟踪(意味着在索引中),该文件的逻辑上独立的副本进入您所做的 每个 提交。这样做的结果是不可能达到你想要的。
最接近的是,如 ,在每个新提交中存储一个 .gitignore
类型为符号 link 的条目(模式 120000
在 Git-internal-ese 中)。然后,即使每个提交都有 link 的目标路径名的逻辑上独立的(可能是物理上共享的)副本,当 Git 去读取 .gitignore
的内容时,它将读取目标路径名的内容,而不是刚刚从您告诉 git checkout
退出的任何提交中复制的 .gitignore
工作树文件的内容。
但是,您可以自动执行跨多个提交更新 .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 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=
# [ -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
我想要实现的是拥有一个 .gitignore 文件(由 git 跟踪),该文件在远程存储库(托管在 [=26] 中的所有分支中同步=]),因此也在相应的本地分支机构上。我目前使用的 .gitignore 文件并不完美,所以我偶尔(有时每天多次)必须更新它。然后的问题是我必须 checkout
.git 手动忽略所有分支中的文件,随着创建更多分支,这变得越来越麻烦。所以对于我做的每个分支(更新后的 .gitignore in branch master)
git checkout some-outdated-branch
git checkout master .gitignore
git add .gitignore
git rm -r --cached .
git add .
git commit -m "Updated .gitignore and fixed tracked files"
因为这对于多个分支来说相对耗时,所以我试图寻找一种方法来在分支 master 中(或在单独的 gitignore-branch 分支中拥有一个 .gitignore 文件) 自动同步所有分支(本地,以及推送时远程)。
这里的问题是我不想使用 git config --global core.excludesfile /path/to/local/.gitignore
(建议 here) as I want my project partners to also use that specific .gitignore file and not have to change the git config
file for this. In this comment 其他人正在问这个问题,但还没有得到回答。我在 Stack Overflow 上也找不到任何关于我的答案问题。
简短摘要
我只想编辑一个分支上的 .gitignore 文件,并以一种省时省力的方式(自动)将该更改与所有其他分支同步。之后,我想将所有分支中的更改推送到远程存储库(最好只使用一行或几行代码,而不必为每个分支重新提交带有相应提交消息的提交)。
不幸的是,只要.gitignore
(或任何文件)被跟踪(意味着在索引中),该文件的逻辑上独立的副本进入您所做的 每个 提交。这样做的结果是不可能达到你想要的。
最接近的是,如 .gitignore
类型为符号 link 的条目(模式 120000
在 Git-internal-ese 中)。然后,即使每个提交都有 link 的目标路径名的逻辑上独立的(可能是物理上共享的)副本,当 Git 去读取 .gitignore
的内容时,它将读取目标路径名的内容,而不是刚刚从您告诉 git checkout
退出的任何提交中复制的 .gitignore
工作树文件的内容。
但是,您可以自动执行跨多个提交更新 .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 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=
# [ -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