Using gitconfig per branch

前端 未结 5 518
故里飘歌
故里飘歌 2020-12-10 18:01

Our company uses many customized opensource project. Whenever I contribute upstream branch I have change to use my personal email/name. Is there any way to have gitconfig pe

相关标签:
5条回答
  • 2020-12-10 18:36

    I am not aware of any tool which automatically changes username and email after changing branch, but you can set that information just before push by using tool for rewriting history like filter-branch (and additionally set it as pre-push hook to automate the process). You can set changing author info only for specific branches.

    Check below link for details: Changing author info

    0 讨论(0)
  • 2020-12-10 18:36

    Git config supports conditional includes, which would be a nice solution, but at present the only supported condition is the path of the repository. Branches or remotes are not suitable to be conditions. When we make a commit, the local head doesn't have to point to a branch (detached HEAD state), and a local branch doesn't have to have a tracking branch. Besides, the name of a local branch doesn't have to match with the name of its tracking branch.

    The first possible solution I think of is post-commit. After a commit is done, in post-commit get the proper name and email and then run git -c user.name foo -c user.email bar commit --amend --no-edit to rewrite the commit with the name and email. After several tries, I find out that it results in an infinitive loop. A commit invokes post-commit to run another git commit.

    Then I try exporting variables like GIT_AUTHOR_NAME related with the name and email of the author and committer in pre-commit. It fails too. I think these values have to be passed at the very beginning of git commit and the first invoked pre-commit is already too late, not to mention prepare-commit-msg, commit-msg or post-commit that are invoked later.

    The third try is an alias like git $(foo) commit where foo returns the proper -c user.name=xx -c user.email=yy according to the branch name and its remote. But git alias involving a bash script is a mud pit and I just give it up.

    Finally I turn back to 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
    

    It works to some degree. But bugs are obvious. One of them is that, after git cherry-pick a commit whose author is someone else, the author name and email will become yours after git filter-branch is done. Besides, as mentioned in the first paragraph, if you want branches and remotes to be conditions like in this hook, then you must follow some strict flows. No detached HEAD, no absence of branch.<name>.remote in the config.

    I think the ideal solution could be an alias or a function. This answer also gives a nice hint. To balance the level of automation and convenience and robustness, it would be better to have two alias for two cases, like git -c user.name=foo user.email=foo@com commit. You decide which alias to use on different branches.

    0 讨论(0)
  • 2020-12-10 18:45

    You can use post-checkout hook for this. Run

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

    Add contents to post-checkout script (edit names and branches as neccessary)

    #!/bin/bash
    # $3 "0" - checking out file. "1" - checking out branch.
    [[ "$3" == "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
    
    0 讨论(0)
  • 2020-12-10 18:53

    With Git 2.23 (Q3 2019), no need for post-checkout hook, and you can use officially git config conditional includes, without script!
    The conditional inclusion mechanism learned to base the choice on the branch the HEAD currently is on.

    See commit 07b2c0e (05 Jun 2019) by Denton Liu (Denton-L).
    (Merged by Junio C Hamano -- gitster -- in commit 3707986, 09 Jul 2019)

    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.

    The git config man page now includes:

    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.

    So in your case:

    [includeIf "onbranch:gerrit"]
      path=gerrit
    

    And in .git/gerrit file:

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

    Example:

    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
    

    Set your config files per branch:

    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
    

    Check that it is working!

    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
    

    From master to next to pu branches: three different user.name! One per branch.

    No hooks. No script.


    As illustrated with Git 2.30 (Q1 2021), make sure to use Git 2.24+ or you might get some strange error messages:

    See commit f1beaae (19 Nov 2020) by Johannes Schindelin (dscho).
    (Merged by Junio C Hamano -- gitster -- in commit 1242501, 30 Nov 2020)

    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.

    0 讨论(0)
  • FYI: Make sure that indeed git is in correct version. Here's my question as a reference: includeIf with branches in git

    Posting it because a perf

    0 讨论(0)
提交回复
热议问题