A Git Gotcha – Identical Changes

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.txt

To 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.

Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">