I have a repository with branches master and A and lots of merge activity between the two. How can I find the commit in my repository when branch A was created based on master?
My repository basically looks like this:
-- X -- A -- B -- C -- D -- F (master)
\ / \ /
\ / \ /
G -- H -- I -- J (branch A)
I'm looking for revision A, which is not what git merge-base (--all)
finds.
I recently needed to solve this problem as well and ended up writing a Ruby script for this: https://github.com/vaneyckt/git-find-branching-point
I was looking for the same thing, and I found this question. Thank you for asking it!
However, I found that the answers I see here don't seem to quite give the answer you asked for (or that I was looking for) -- they seem to give the
G
commit, instead of theA
commit.So, I've created the following tree (letters assigned in chronological order), so I could test things out:
This looks a little different than yours, because I wanted to make sure that I got (referring to this graph, not yours) B, but not A (and not D or E). Here are the letters attached to SHA prefixes and commit messages (my repo can be cloned from here, if that's interesting to anyone):
So, the goal: find B. Here are three ways that I found, after a bit of tinkering:
1. visually, with gitk:
You should visually see a tree like this (as viewed from master):
or here (as viewed from topic):
in both cases, I've selected the commit that is
B
in my graph. Once you click on it, its full SHA is presented in a text input field just below the graph.2. visually, but from the terminal:
git log --graph --oneline --all
(Edit/side-note: adding
--decorate
can also be interesting; it adds an indication of branch names, tags, etc. Not adding this to the command-line above since the output below doesn't reflect its use.)which shows (assuming
git config --global color.ui auto
):Or, in straight text:
in either case, we see the 6aafd7f commit as the lowest common point, i.e.
B
in my graph, orA
in yours.3. With shell magic:
You don't specify in your question whether you wanted something like the above, or a single command that'll just get you the one revision, and nothing else. Well, here's the latter:
Which you can also put into your ~/.gitconfig as (note: trailing dash is important; thanks Brian for bringing attention to that):
Which could be done via the following (convoluted with quoting) command-line:
Note:
zsh
could just as easily have beenbash
, butsh
will not work -- the<()
syntax doesn't exist in vanillash
. (Thank you again, @conny, for making me aware of it in a comment on another answer on this page!)Note: Alternate version of the above:
Thanks to liori for pointing out that the above could fall down when comparing identical branches, and coming up with an alternate diff form which removes the sed form from the mix, and makes this "safer" (i.e. it returns a result (namely, the most recent commit) even when you compare master to master):
As a .git-config line:
From the shell:
So, in my test tree (which was unavailable for a while, sorry; it's back), that now works on both master and topic (giving commits G and B, respectively). Thanks again, liori, for the alternate form.
So, that's what I [and liori] came up with. It seems to work for me. It also allows an additional couple of aliases that might prove handy:
Happy git-ing!
surely I'm missing something, but IMO, all the problems above are caused because we are always trying to find the branch point going back in the history, and that causes all sort of problems because of the merging combinations available.
Instead, I've followed a different approach, based in the fact that both branches share a lot of history, exactly all the history before branching is 100% the same, so instead of going back, my proposal is about going forward (from 1st commit), looking for the 1st difference in both branches. The branch point will be, simply, the parent of the first difference found.
In practice:
And it's solving all my usual cases. Sure there are border ones not covered but... ciao :-)
Given that so many of the answers in this thread do not give the answer the question was asking for, here is a summary of the results of each solution, along with the script I used to replicate the repository given in the question.
The log
Creating a repository with the structure given, we get the git log of:
My only addition, is the tag which makes it explicit about the point at which we created the branch and thus the commit we wish to find.
The solution which works
The only solution which works is the one provided by lindes correctly returns
A
:As Charles Bailey points out though, this solution is very brittle.
If you
branch_A
intomaster
and then mergemaster
intobranch_A
without intervening commits then lindes' solution only gives you the most recent first divergance.That means that for my workflow, I think I'm going to have to stick with tagging the branch point of long running branches, since I can't guarantee that they can be reliably be found later.
This really all boils down to
git
s lack of whathg
calls named branches. The blogger jhw calls these lineages vs. families in his article Why I Like Mercurial More Than Git and his follow-up article More On Mercurial vs. Git (with Graphs!). I would recommend people read them to see why some mercurial converts miss not having named branches ingit
.The solutions which don't work
The solution provided by mipadi returns two answers,
I
andC
:The solution provided by Greg Hewgill return
I
The solution provided by Karl returns
X
:The script
I doubt the git version makes much difference to this, but:
Thanks to Charles Bailey for showing me a more compact way to script the example repository.
In general, this is not possible. In a branch history a branch-and-merge before a named branch was branched off and an intermediate branch of two named branches look the same.
In git, branches are just the current names of the tips of sections of history. They don't really have a strong identity.
This isn't usually a big issue as the merge-base (see Greg Hewgill's answer) of two commits is usually much more useful, giving the most recent commit which the two branches shared.
A solution relying on the order of parents of a commit obviously won't work in situations where a branch has been fully integrated at some point in the branch's history.
This technique also falls down if an integration merge has been made with the parents reversed (e.g. a temporary branch was used to perform a test merge into master and then fast-forwarded into the feature branch to build on further).
After a lot of research and discussions, it's clear there's no magic bullet that would work in all situations, at least not in the current version of Git.
That's why I wrote a couple of patches that add the concept of a
tail
branch. Each time a branch is created, a pointer to the original point is created too, thetail
ref. This ref gets updated every time the branch is rebased.To find out the branch point of the devel branch, all you have to do is use
devel@{tail}
, that's it.https://github.com/felipec/git/commits/fc/tail