Here’s an interesting side effect of the way git cleverly and silently handles identical changes during a merge, combined with the way history is stored and searched. I’ll demonstrate by setting up the simplest possible scenario. In an empty directory:
git init echo -e "line 1\nline two\nline 3" >test.txt git add test.txt git commit -m "Initial file" git checkout -b fred sed -i -e 's/two/2/' test.txt echo -e "line 4" >>test.txt git commit -a -m "Fixed line 2 and added line 4" git checkout master sed -i -e 's/two/2/' test.txt git commit -a -m "Fix line 2" git merge fred
So just to recap what we’ve done in English – we start out with a test.txt that looks like this:
line 1 line two line 3
Then Fred creates a new branch to add some new functionality (a fourth line) and at the same time notices the error in line two and changes ‘two’ to ’2′. Fred’s commit results in this, which git sees as two changes (hunks) one for line 2 and one for line 4:
line 1 line 2 line 3 line 4
Meanwhile, back on the master branch, Eric has noticed the same error, and changes ‘two’ to ’2′ too. So the commit on the master branch results in this, a single change:
line 1 line 2 line 3
Finally, the changes from Fred’s branch are merged back into master. Note that despite both branches not only having changed the same file, but even the same line of the same file, the merge is completely straightforward and automatic – no user intervention is required.
So here’s the gotcha (or at least, it got me). To see why, you probably have to imagine lots more unrelated commits on both branches, and lots of time passing. Eric sees Fred’s commit and thinks “Weird, didn’t I make that change already? I’ll ask git:”
git log test.txtTo his surprise, searching the log for commits that changed test.txt produces no mention of the commit labelled “Fix line 2″. In fact, it shows all the commits EXCEPT that one. So he finds his commit in a more specific way and has a look at what it changed:
git log --name-only --grep="Fix line 2"
The commit is there, and it says it changes test.txt, so there is an apparent inconsistency between what the two forms of git log are reporting here.
In fact, git log is deliberately simplifying the history here. In the default mode, commits from side branches that don’t contribute anything to the final state of the tree are pruned from the output. The way to get the full output is:
git log --full-history test.txt
With the –full-history option added, the output contains all the commits. Worth remembering.

No comments
Comments feed for this article
Trackback link: http://ciarang.com/posts/a-git-gotcha-identical-changes/trackback