This is slightly simplified from the real story, but hopefully close enough. Suppose I initially have one branch: develop. Then I create a new branch: release. At this point, release and develop both point to commit XYZ. I do some work on the release branch, and occasionally merge it into develop. Meanwhile, I do lots of other work on the develop branch. Then one day I wake up and wonder when did my branches first diverge? That is, I want to find the commit hash of commit XYZ. The obvious temptation is to use "git merge-base" but this just finds the point when I last merged release into develop, which is WAY later than commit XYZ.
Asked
Active
Viewed 61 times
1
-
2@RomainValeri AFAIU the question is not about branch labels but about commit's DAG: how to traverse the DAG starting with two branch heads back to the root commit finding the first (longest distance from both heads) commit where the arcs diverged. – phd Apr 10 '23 at 22:00
-
1As phd says, this should not require reflogs; this should in principle be derivable simply based on the current DAG and the current positions of develop and release branches. I can even describe the commit I want semi-formally: I want the "latest" commit such that ALL of its ancestors are shared by both branches. – Mark VY Apr 10 '23 at 22:12
-
This seems to be a non-trivial task in general. See [this](https://stackoverflow.com/a/4991675/1725151) answer to “Finding a branch point with Git?”. Also see [this](https://lore.kernel.org/git/20230310214515.39154-1-felipe.contreras@gmail.com/) patch series which proposes an explicit representation as a ref/pointer. – Guildenstern Apr 18 '23 at 21:08
-
1thank you for both links! indeed, if git explicitly tracked this info that would be ideal... I see a lot of that email thread is people discussing "how to maintain this info in the face of rebasing and such" but in the case that prompted this question, I know for a fact that the branch was never rebased. So still super-useful even without accurate updates – Mark VY Apr 18 '23 at 23:48
-
Possibly relevant (caveat previous “this should not require reflogs”): [find when a branch was created using the reflog](https://stackoverflow.com/a/18278195/1725151) – Guildenstern Apr 29 '23 at 20:59
-
nice, but what if it wasn't create on my machine :) – Mark VY May 01 '23 at 18:31
1 Answers
3
Since both branches have the same root, I think it should be possible to generate a list of commits back to the root commit for each branch, and then compare those two lists reversed until there is a difference. Then the common ancestor commit is the last one before the first difference.
The following command should then print the commit id of the common ancestor commit:
diff -u <(git log --reverse --oneline develop) <(git log --reverse --oneline release) \
| sed '1,3d; /^[+-]/,$d' | tail -1 | awk '{print $1}'
Edit: The sed argument is two delete (d
) commands, separated with semicolon. The first delete is removing lines 1 to 3 (inclusive). The second delete is removing from the first line that starts with either plus or minus, until the last line ($
).

hlovdal
- 26,565
- 10
- 94
- 165
-
care to explain the sed magic here? or at least a hint as which part of the manual to look in? (I don't know sed) – Mark VY Apr 11 '23 at 02:41
-
I think I get sed stuff now: we skip the first three lines, and then omit anything starting with a plus or minus, thus keeping only those lines that `diff` says are common to both. Nice! Surprisingly fast for something that walk whole history twice. I think I prefer `--pretty=reference` instead of `--oneline` since dates help me stay oriented. The one problem I see is that I think you shouldn't have `--reverse` above, since that gives us the most recent merge of release into dev, whereas I want to know where they first diverged. – Mark VY Apr 11 '23 at 03:19