每个分支使用 gitconfig

Using gitconfig per branch

我们公司使用了很多定制的开源项目。每当我贡献上游分支时,我都会更改为使用我个人的 email/name。有没有办法让每个分支都有 gitconfig?

比如我想要的是

[remote 'gerrit']
name = 'Personal Name'

[branch 'origin']
name = 'Name in company'

我不知道有什么工具可以在更改分支后自动更改用户名和电子邮件,但是您可以在推送之前设置该信息,方法是使用 filter-branch 等重写历史记录的工具(另外将其设置为 pre -push hook 来自动化这个过程)。您可以仅为特定分支设置更改作者信息。

查看下方 link 了解详细信息: Changing author info

您可以为此使用 post-checkout 挂钩。 运行

$ touch .git/hooks/post-checkout 
$ chmod a+x .git/hooks/post-checkout 

post-checkout 脚本添加内容(根据需要编辑名称和分支)

#!/bin/bash
#  "0" - checking out file. "1" - checking out branch.
[[ "" == "0" ]] && exit 0 
branch=$(git status --short -b | cut -d' ' -f2-)
case $branch in
  gerrit*)
    git config user.name "Personal Name"
    echo "changed user.name to Personal Name"
    ;;
  master*)
    git config user.name "Company Name"
    echo "changed user.name to Company Name"
    ;;
  *)
    echo "Some other branch, what should user.name be?"
    ;;
esac

Git配置支持conditional includes,这将是一个很好的解决方案,但目前唯一支持的条件是存储库的路径。树枝或遥控器不适合作为条件。当我们进行提交时,本地头不必指向分支(分离的 HEAD 状态),本地分支也不必有跟踪分支。此外,本地分支的名称不必与其跟踪分支的名称匹配。

我想到的第一个可能的解决方案是post-commit。提交完成后,在 post-commit 中获取正确的名称和电子邮件,然后在 运行 git -c user.name foo -c user.email bar commit --amend --no-edit 中使用名称和电子邮件重写提交。经过几次尝试,我发现它会导致无限循环。提交调用 post-commit 到 运行 另一个 git commit

然后我尝试导出变量,例如 GIT_AUTHOR_NAMEpre-commit 中作者和提交者的姓名和电子邮件相关的变量。它也失败了。我认为这些值必须在 git commit 的最开始传递并且第一次调用 pre-commit 已经太晚了,更不用说 prepare-commit-msgcommit-msgpost-commit 稍后调用。

第三次尝试是像 git $(foo) commit 这样的别名,其中 foo returns 根据分支名称及其远程的正确 -c user.name=xx -c user.email=yy。但是git alias涉及一个bash脚本是一个泥坑,我就放弃了。

终于转回post-commit:

#!/bin/bash

head=`git rev-parse --abbrev-ref HEAD`
remote=`git config --get branch."${head}".remote`
if [ "$remote" = "origin" ];then
    git filter-branch -f --env-filter '
        export GIT_AUTHOR_NAME=xx \
        export GIT_AUTHOR_EMAIL=xx@foo.com \
        export GIT_COMMITTER_NAME=xx \
        export GIT_COMMITTER_EMAIL=xx@foo.com' \
        HEAD^..HEAD
elif [ "$remote" = "gerrit" ];then
    git filter-branch -f --env-filter '
        export GIT_AUTHOR_NAME=yy \
        export GIT_AUTHOR_EMAIL=yy@bar.com \
        export GIT_COMMITTER_NAME=yy \
        export GIT_COMMITTER_EMAIL=yy@bar.com' \
        HEAD^..HEAD
else
    echo no amend
fi

它在某种程度上起作用。但是错误是显而易见的。其中之一是,在 git cherry-pick 作者是其他人的提交之后,作者姓名和电子邮件将在 git filter-branch 完成后成为您的。此外,如第一段所述,如果您希望分支和远程成为此钩子中的条件,那么您必须遵循一些严格的流程。没有分离的 HEAD,配置中没有缺少 branch.<name>.remote

我认为理想的解决方案可能是别名或函数。这个 answer 也给出了一个很好的提示。为了平衡自动化水平和便利性和健壮性,最好为两种情况使用两个别名,例如 git -c user.name=foo user.email=foo@com commit。您决定在不同分支上使用哪个别名。

使用Git 2.23 (Q3 2019),不需要post-checkout hook,可以正式使用git config conditional includes,无需脚本!
条件包含机制学会了选择基于 HEAD 当前所在的分支.

参见 commit 07b2c0e (05 Jun 2019) by Denton Liu (Denton-L)
(由 Junio C Hamano -- gitster -- in commit 3707986 合并,2019 年 7 月 9 日)

config: learn the "onbranch:" includeIf condition

Currently, if a user wishes to have individual settings per branch, they are required to manually keep track of the settings in their head and manually set the options on the command-line or change the config at each branch.

Teach config the "onbranch:" includeIf condition so that it can conditionally include configuration files if the branch that is checked out in the current worktree matches the pattern given.

git config man page 现在包括:

onbranch:

The data that follows the keyword onbranch: is taken to be a pattern with standard globbing wildcards and two additional ones, **/ and /**, that can match multiple path components.

If we are in a worktree where the name of the branch that is currently checked out matches the pattern, the include condition is met.

If the pattern ends with /, ** will be automatically added.
For example, the pattern foo/ becomes foo/**.

In other words, it matches all branches that begin with foo/. This is useful if your branches are organized hierarchically and you would like to apply a configuration to all the branches in that hierarchy.

所以在你的情况下:

[includeIf "onbranch:gerrit"]
  path=gerrit

并且在 .git/gerrit 文件中:

[remote 'gerrit']
  name = 'Personal Name'

示例:

vonc@vonvb:~/gits/src/git$ git version
git version 2.23.0.b4


vonc@vonvb:~/gits/src/git$ git config includeIf.onbranch:next.path user1
vonc@vonvb:~/gits/src/git$ git config includeIf.onbranch:pu.path user2
vonc@vonvb:~/gits/src/git$ git config --local -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
...
includeif.onbranch:next.path=user1
includeif.onbranch:pu.path=user2

设置每个分支的配置文件:

vonc@vonvb:~/gits/src/git$ git config --file=.git/user1 user.name user1
vonc@vonvb:~/gits/src/git$ git config --file=.git/user1 user.email user1@email.com

vonc@vonvb:~/gits/src/git$ more .git/user1
[user]
    name = user1
    email = user1@email.com


vonc@vonvb:~/gits/src/git$ git config --file=.git/user2 user.name user2
vonc@vonvb:~/gits/src/git$ git config --file=.git/user2 user.email user2@email.com

vonc@vonvb:~/gits/src/git$ more .git/user2
[user]
    name = user2
    email = user2@email.com

检查它是否正常工作!

vonc@vonvb:~/gits/src/git$ git config user.name
VonC

vonc@vonvb:~/gits/src/git$ git checkout next
Branch 'next' set up to track remote branch 'next' from 'origin'.
Switched to a new branch 'next'
vonc@vonvb:~/gits/src/git$ git config user.name
user1

vonc@vonvb:~/gits/src/git$ git checkout pu
Branch 'pu' set up to track remote branch 'pu' from 'origin'.
Switched to a new branch 'pu'
vonc@vonvb:~/gits/src/git$ git config user.name
user2

vonc@vonvb:~/gits/src/git$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
vonc@vonvb:~/gits/src/git$ git config user.name
VonC

masternext再到pu分支:三个不同的user.name每个分支一个.

没有钩子。没有脚本。


如 Git 2.30(2021 年第一季度)所示,请务必使用 Git 2.24+,否则您可能会收到一些奇怪的错误消息:

参见 commit f1beaae (19 Nov 2020) by Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- in commit 1242501 合并,2020 年 11 月 30 日)

t1309: use a neutral branch name in the onbranch test cases

Signed-off-by: Johannes Schindelin

The onbranch test cases touched by this patch do not actually try to include any other config. Their purpose is to avoid regressing on two bugs in the include.onbranch:<name>.path code that we fixed in the past, bugs that are actually unrelated to any concrete branch name.

The first bug was fixed in 85fe0e800ca ("config: work around bug with includeif:onbranch and early config", 2019-07-31, Git v2.23.0-rc1 -- merge).
Essentially, when reading early config, there would be a catch-22 trying to access the refs, and therefore we simply cannot evaluate the condition at that point. The test case ensures that we avoid emitting this bogus message:

BUG: refs.c:1851: attempting to get main_ref_store outside of repository  

The second test case concerns the non-Git scenario, where we simply do not have a current branch to begin with (because we don't have a repository in the first place), and the test case was introduced in 22932d9169f ("config: stop checking whether the_repository is NULL", 2019-08-06, Git v2.24.0-rc0 -- merge listed in batch #2) to ensure that we don't cause a segmentation fault should the code still incorrectly try to look at any ref.

In short, neither of these two test cases will ever look at a current branch name, even in case of regressions. Therefore, the actual branch name does not matter at all. We can therefore easily avoid racially-charged branch names here, and that's what this patch does.

仅供参考:确保 git 确实是正确的版本。 这是我的问题作为参考:

张贴它是因为性能