3

I have experience with Mercurial in which we close branch after merge. However, we don't delete the source branch so that if an issue is found, we can track all the merges in the master branch to identify grossly (the branch) where the issue comes from. Then we track the commits of the branch to identify the culprit.

In git, I read that we should delete branch after merge. My questions are

  1. Why? If a bug is found, how can we identify the bugged commit which has been merged to master?

  2. In general, how do you git guys track the history to identify the commit that bugs?

smarber
  • 4,829
  • 7
  • 37
  • 78
AlexTP
  • 213
  • 1
  • 3
  • 6
  • 2
    You can grep commit messages and find the merge commit of interest. Moreover, Git provides a `bisect` subcommand to home in on a commit that introduced a bug. – jub0bs Apr 18 '18 at 09:17
  • 1
    I don't see how preserving branches helps to identify issues. Anyway, as @Jubobs mentionned, git provide the super powerful subcommand `bisect` – smarber Apr 18 '18 at 09:37
  • https://stackoverflow.com/a/2613954/2303202 tldr: deleting branch does not delete commits if it was merged – max630 Apr 18 '18 at 09:58
  • 1
    BTW, as in mercurial "branch" is just a string property of commits, it is literally impossible to delete a branch without modifying the history. "Closing" a branch is adding a most recent "closing" commit, so that the branch is not shown in list of active branches – max630 Apr 18 '18 at 10:00

2 Answers2

7

First, compared to Mercurial's branches, Git branches are "anonymous".

This means, a commit which was recorded "on a" branch (most of them are, but this is not required) bears no information about the branch it was committed to. This is intentional. In this model, a branch is merely "a pointer" to some commit which—through the parent/child(ren) relations—can be used to traverse the history of changes backwards.

The consequence is that any commit recorded in a repository may be reachable via any number of branches (and tags). So, in this model, any commit may "be on" any number of branches at the same time.

Second, a merge commit not only records the name of the branch being merged (this is merely the default behaviour; it's perfectly legal to merge a chain of history by naming its tip commit or override/edit the merge message) but actually the tip commit of that branch at the time of the merge. So this information is recorded in the merge commit itself, and that line of history is permanently there: say, if you have merged branch B into branch A resulting in a merge commit M, you can refer to the tip of that branch B at the time of the merge via M^2 which means "the second parent of M" (the first parent is the tip of A at the time of the merge).

Given that Git branches are lightweight, should you need to "get back in time" to "work on" B at the time of the merge, you can just do

$ git checkout -b oldB M^2

which is "create a new branch named «oldB» pointing at the 2nd parent of the merge commit «M» and check it out".

After you're done with exploring the old state of B, just get rid of that "oldB" branch.


To round up, Git is best approached from another angle: think in term of the graph of interconnected commits with branches merely pointing to certain "entry points" to that graph.

kostix
  • 51,517
  • 14
  • 93
  • 176
4

Just to add a bit to kostix's answer: In Mercurial, commits are permanently affixed to, and hence contained within, exactly one branch. The branch that contains a commit is the branch that was current when the commit was made.

This means that a diagram that looks like this is sensible:

branch1:  C1 <-C2 <-C3 <-M
                        /
branch2:    C4 <-C5 <-C6

since the commits will always be on those branches.

This is not true in Git: as noted, each Git commit is on zero or more branches, and the set changes dynamically over time. We should put the branch names on the right and have arrows coming out of them, pointing to commits, rather than putting the name on the left and letting it flow rightward "against" the backwards-pointing arrows:

C1 <-C2 <-C3 <-M   <-- branch1
              /
  C4 <-C5 <-C6   <-- branch2

This lets us move the names around, even to other rows of the diagram, without worrying about which commits are to their right. The flow is entirely right-to-left, not mixed.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks, I upvote. Do you have a simple case in which the Git approach is better than the Mercurial one? – AlexTP Apr 18 '18 at 21:37
  • 2
    I wrote about this in my (unfinished) book. The Mercurial method is much clearer to newbies and much simpler to explain, but ultimately it causes problems over the long term. See the last paragraph currently on pages 42-43 [here](http://web.torek.net/torek/tmp/book.pdf). – torek Apr 18 '18 at 21:57
  • 1
    @torek I was unaware (until now) that you were writing a book! Any ETA? – jub0bs Apr 24 '18 at 23:51
  • @Jubobs: the project is quite stalled now that I have a full time job again (alas!). On some weekends I get a little bit of work done now and then. – torek Apr 25 '18 at 00:14
  • @torek Best of luck, both with the book and the new job! – jub0bs Apr 25 '18 at 00:15