4

One of the biggest surprises for me while learning Git was the fact that revisions are not permanently associated with a specific branch; rather, a branch merely points to a particular revision. Once I've fully internalised this concept, I realised that I don't understand how to actually use this feature properly.

I've read the apparently famous essay about a successful Git branching model, which depicts the branches as lines, and revisions as being clearly associated with one line. Consider this excerpt:

                                                      enter image description here

This shows two feature branches (call them featureA and featureB), and a develop branch. Moreover, featureB was fully merged into develop and then briefly diverged again, only to be re-merged.

This looks nice, tidy and understandable, and also happens to be what the repo would literally look like at the end of this process in something like Mercurial. I understand this much, I like it, and I'd like to develop like this (and I did, in Mercurial). However, in Git, the final state doesn't look like that. It looks like this:

                                        enter image description here

In other words, the only information we have about the two feature branches is that they are now at the same revision as the develop branch. As a result, the best we can infer for the whole tree is this:

                                              enter image description here

In other words, all that nice information about the history of how the development occurred is lost; it was never recorded in the first place. Observe how in the first graph, three revisions are clearly on featureB before they were merged into develop, but I don't see how we can know that those three revisions were on featureB before the merge.

Is this correct? Is the above the best I can recover after-the-fact in Git, and is the screenshot from the essay therefore rather misleading? Or is there something important I'm missing?

Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • Follow-up question: [How to record a history of releases in Git?](http://stackoverflow.com/questions/17654416/how-to-record-a-history-of-releases-for-a-web-project-in-git) – Roman Starkov Jul 15 '13 at 12:37

3 Answers3

2

Yes, it is correct, the three branches (pointers) will just refer the same commit.

But with git, it is about more about the sequence of commits than it is about a branch.
A branch is just a pointer in a graph of commits, in order to:

  • help the local developer to quickly switch to one or to merge one with another
  • help publishing (pushing) to an upstream repo a collection of commits (you don't push all the branches by default, only the one want to be public).

If you really need to store "featureA" somewhere, it would be in the commit message, but that wouldn't necessary indicate a branch: you can cherry-pick a commit, rebase a branch, rename of delete a branch (pointer) at any time.
In the end, the story told by the sequence of commits is more important than the fact said commit belongs or belonged to one or several branches.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 2
    Thank you. I just wish the referenced article made it a lot more clear that the diagrams show what you did, and not what you end up with. – Roman Starkov Jul 15 '13 at 12:28
1

You are partially correct, but are also missing something. Git is all about the sequence of commits and the graph they create. But you are missing three things in your final graph:

  • (minor) Your last merge is an octopus merge (merging more than two branches). You would not do that in that situation, and I don’t know why the image from git-flow contains one either.
  • The parents of a merge have an order. So there is a first parent and a second parent. In other words: git records what was merge into what.
  • Merges have a commit message

So you can easily reconstruct the develop branch, by starting at its head and following only the first parent (git log --first-parent).

If you want to know which feature was merged, you can have a look at the commit message in the merge.

But note that this only holds true if you use git this way. Merging featureA into develop and merging develop into featureA will hold a very similar result and a lot of people don’t realize that there’s a difference.

Chronial
  • 66,706
  • 14
  • 93
  • 99
0

As @VonC noted, in git-land (as opposed to Mercurial World? :-) ) it often seems that this is considered to be a feature, or at least, not a bug. But if you want your labels to persist in separate spaces, you can make this happen in git, by doing your merges with --no-ff. This will create an otherwise "unnecessary" / "empty" merge commit, that serves to keep the parent pointers separate and therefore serves to keep the branches separate as well.

torek
  • 448,244
  • 59
  • 642
  • 775
  • I am aware of the `--no-ff` and what it does, but you still won't know which of those older commits were on `featureB`, will you? – Roman Starkov Jul 15 '13 at 12:26
  • You will if you follow the parent links "in order". After merging `featureB` into `develop` with `--no-ff` to create an apparently pointless commit node, you can then merge `develop` into `featureB` (also with `--no-ff`) and continue adding more `featureB` stuff. The first parent of the merge on `develop` will be another `develop` commit, and the first parent of the (different) merge on `featureB` will be another `featureB` commit (with the second parent being the merge to which `devel` now points). – torek Jul 15 '13 at 21:16