0

I have two branches: master and feature. I want to check which commit the feature branch was created from. To do this, I'm having to check each commit individually with

git branch -r --contains HEAD~X

which only returns both branches when I reach the desired commit. Is there any easier way to check this? Thanks!

sevs
  • 73
  • 1
  • 6
  • Are you looking for this https://stackoverflow.com/questions/18407526/git-how-to-find-first-commit-of-specific-branch ? – pmod Nov 29 '18 at 17:40
  • not _exactly_ what I was searching for, but it **does** solve my problem. Thanks! – sevs Nov 29 '18 at 17:50
  • 1
    Possible duplicate of [Git - how to find first commit of specific branch](https://stackoverflow.com/questions/18407526/git-how-to-find-first-commit-of-specific-branch) – Schleis Nov 29 '18 at 18:20

2 Answers2

0

Look into using git merge-base (https://git-scm.com/docs/git-merge-base).

git merge-base master feature will give you the first common ancestor. If you haven't merged master into feature since the initial creation of the feature branch, then this will give you the branching point.

John
  • 2,395
  • 15
  • 21
0

Note that if there's only a simple linear path from feature back to master, you're good with the accepted answer to Git - how to find first commit of specific branch:

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

Here git log --oneline master..feature will list commit G, then F, then E, so the last output line will be that for commit E. (If you just want the hash ID, use git rev-list master..feature | tail -1.)

But consider:

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

Here master..feature still lists commit E, even if you wanted commit F: you need develop..feature to have git log list G then F and then stop.

Or:

A--B--C--D   <-- master
    \
     \   F--G   <-- feature1
      \ /    \
       E      J--K  <-- feature3
        \    /
         H--I   <-- feature2

Here, commit B is shared by all branches; commit E is on all branches except master; F and G are shared by feature1 and feature3; H and I are shared by feature2 and feature3; and J and K are exclusive to feature3.

Using master..feature3, you'll enumerate commits E through K, but the order of, specifically, F through I is changeable. (E will come out after all four, F will not be mentioned before G is listed, and H will not be mentioned before I is listed, but that still leaves a good number of possible orderings.) You can choose the order, within limits, using the various sorting controls; the default is to go by committer date, when there are multiple possible commits for git log to emit at this point.

In general, Git works by starting at a tip commit, as pointed-to by one of these branch names, and working backwards. When reaching a place where there's a join-up (like merge commit J), Git starts walking both paths at the same time. With git log, it prints those commits in some order based on the sorting order you chose. (Eventually the paths usually re-converge, as in this case they do at commit E. At that point, git log can resume doing one commit at a time.)

The commits themselves are all fixed in place for as long as they exist—you can draw the graph a bit differently, but the parent of E is always B, for instance—but the names, the branch names that point to specific commits, are all moveable: any name can be moved at any time to point to any one specific commit. Names can be added or deleted at any time, with one caveat: if you delete all the names that let you find a commit, it can be very hard to get that commit back.1 In general, Git commands and Git range listings like A..B and so on, resolve the names to the commits, then use the commit graph to do their job.2


1If git gc runs at the wrong time, and you have reflogs disabled or the reflog entries are too old and stale, the Grim Garbage Collector can throw out any unreachable commit. At that point, the commit is really gone, and even knowing its hash ID won't save you.

2The major exception here is git diff: if you give git diff a range, it just treats it as if you gave it two different commit hash IDs. That is, git diff A..B does not walk the graph at all, it just finds A and B and then acts exactly like git diff A B. The git diff command also has special treatment for the three-dot syntax, A...B.

git rebase has also acquired a special case for A...B (with three dots) in its --onto syntax.

torek
  • 448,244
  • 59
  • 642
  • 775