简体   繁体   中英

git - Find commit from which was branch created

Is it possible to find commit (ideally only the commit hash) from which was branch created, even if the branch was already merged ?

For example:

master   A-B-C-D-E
          \   /
feature    F-G

feature branch is already merged in master , but it still exists and can be checkout to. I would like to find, that feature branch started from A .

( Disclaimer : off-topic answer - not adressing already merged branches)


If feature and master are not yet merged , you can just get their "merge-base" ( doc ):

git merge-base master feature

It will output your commit A 's long-form hash.

Branches don't really have parent branches. (Related: How to find the nearest parent of a Git branch? ) You can find the hash of A pretty easily, but consider this diagram:

H--I--L--N--O--R---T--U   <-- master
 \     \     \    /
  J--K--M--P--Q--S   <-- feature

Which commit(s) would you like to find here? O , L , and H are all viable candidates for "where feature started from". But maybe feature didn't start from any of those; maybe it started from P , which used to have a label main-feature . See the related question for details.

To find commit A from your diagram, which I'll reproduce here a form I consider more accurate (the name master merely points directly to commit E for instance—commits F and G are on branch master too:):

A--B--C--D--E   <-- master
 \      /
  F----G   <-- feature

we can start from commit E (tip of master) and step back to commit D (the merge). We know we have hit D when one of its parents is the tip of feature , ie, is commit G . We then get both parent hash IDs from commit D :

git rev-parse <hash-of-D>^@

and make sure there are exactly two of them (a three-parent merge makes this hard), and then invoke git merge-base --all on those two parent hash IDs. This immediately produces the hash ID of commit A .

When we use this same procedure on my sample graph, we step back from U to T , discover that it has two parents and the second is S which is the tip of feature , and ask Git to find the merge base of R and S . The output of this git merge-base --all is the hash of commit O .

To do this relatively quickly in shell script, start with:

git rev-list --merges --first-parent master

which produces a list of all merge commit hash IDs reachable by walking first-parent descendants of master . (You may or may not want --first-parent ; if you've been using git pull heavily, the first parent linkages of master may be somewhat damaged by "foxtrot merges" .) That is, this gives us commit hashes T and D in the two diagrams here (and probably more). If you omit --first-parent , think about what you want from cases where other branches have merged into master , bringing with them commits from the feature branch in question, and decide whether you might want --topo-order in your git rev-list .

Next, walk through these commits, getting their parent hashes:

looking_for=$(git rev-parse feature)  # this is the hash ID we're looking for
found=false
for hash in (output of above git rev-list); do
    set -- $(git rev-parse ${hash}^@)
    case $# in
    2) # two parents, good
        if [ $2 == $looking_for ]; then found=true; break; fi;;
    *) # more than two parents, we might go wrong
        echo "commit $hash has $# parents, help"; exit 1;;
    esac
done
if ! $found; then
    echo "unable to find the commit hash you were looking for"
    exit 1
fi

At this point, you have the desired commit pair in $1 and $2 due to the way set parses arguments, so:

# found the desired commit pair, so now just invoke git merge-base
set -- $(git merge-base --all $1 $2)
case $# in
1) echo "the commit you are looking for is $1"; exit 0;;
*) echo "I found $# candidate commits $@"; exit 1;;
esac

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM