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.
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.
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