How to retrieve branch names in a custom Git merge driver?

独自空忆成欢 提交于 2019-12-05 23:41:09

问题


I'm writing a custom merge driver that needs to be aware of the names of the branches it is merging. I managed to retrieve the name of the branch being merged into (destination) with git symbolic-ref HEAD and the name of the branch being merged in (source) from the GITHEAD_<SHA> environment variable.

# retrieve merged branch name from an env var GITHEAD_<sha>=<branchName> 
# we cannot use a sym ref of MERGE_HEAD, as it doesn't yet exist 
gitHead=$(env | grep GITHEAD) # e.g. GITHEAD_<sha>=release/1.43 
# cut out everything up to the last "=" sign 
source="${gitHead##*=}"

# retrieve base branch name from a sym ref of HEAD 
branch=$(git symbolic-ref HEAD) # e.g. refs/heads/master 
# cut out "refs/heads"
destination="${branch#refs/heads/}"

echo "Merging from $source into $destination"

Is this the right way of doing this? Particularly, retrieving the source name from an environment variable seems flaky. Note that MERGE_HEAD is not present at this point, so I cannot use the same approach as with HEAD.


回答1:


Yes, this is as close as you can get in a merge driver.

A merge strategy gets the correct name by looking up each GITHEAD_%s where each %s argument is filled in from the argument(s) given to the merge strategy, one per "remote head". See my answer to a related question for details. This is not documented anywhere, it's just from the source. Unfortunately, by the time you get down to a merge driver, this information is lost: there are no % directives that retrieve those names, even though there could be. (Adding some % directives should be easy: just increase the size of the dictionary and add appropriate directives and strbuf objects.)

If you're handling a normal two-heads-and-a-base merge, there's only one GITHEAD_* variable and it's the other head, but if you are handling an octopus merge, you are out of luck.




回答2:


I have been handling this task for a few weeks and I have found a few corner cases that are not correctly handled by this script. Searching around and taking a look at Git sources I have arrived at this script:

#!/bin/bash 
# ${1} is the base common file 
# ${2} is the file as modified by the base branch, and where the results must be 
# ${3} is the file as modified by the incoming branch / stash
# ${4} is the path of the file being merged

# does not support octopus merge.

branch=$(git symbolic-ref --short HEAD || git name-rev --name-only HEAD)
githeadNum=$(env | grep GITHEAD | wc -l) # pathces do not create any GITHEAD var


# get a GITHEAD with message 'Stashed changes'
# if we are applying a stash, it must exist.
# see https://github.com/git/git/blob/2d08f3deb9feb73dc8d21d75bfd367839fc1322c/git-stash.sh#L616
stash=$(env | grep -E 'GITHEAD_[0-9a-f]{40}\b*=\b*Stashed changes' | wc -l) # e.g. GITHEAD_<sha>=Stashed changes 

if [ "$stash" -eq "1" ]
then
    # we are in a stash 
else
    if [ "$githeadNum" -eq "0" ]
    then
        # we are in a patch
    else
        # normal merge

        # only one GITHEAD, merging
        gitHeadName=$(env | grep GITHEAD)
        source="${gitHeadName##*=}"
    fi
fi

exit 1 

I recover the destination branch as

$(git symbolic-ref --short HEAD || git name-rev --name-only HEAD)

I found the first part failing for rebase, so in case of a rebase the second part should work.

For the source, I parse the GITHEAD env variable but check for patches or stash application, since I want to handle those cases differently (and patches do not leave GITHEAD)

For stash apply:

stash=$(env | grep -E 'GITHEAD_[0-9a-f]{40}\b*=\b*Stashed changes' | wc -l)

$stash will be 1 in case we are in a stash, 0 otherwhise

For patches I count number of GITHEAD, which must be zero:

githeadNum=$(env | grep GITHEAD | wc -l)


来源:https://stackoverflow.com/questions/24302644/how-to-retrieve-branch-names-in-a-custom-git-merge-driver

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!