Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I determine why a Git merge deleted some lines of code?

Tags:

git

We had a release and the commit in question (daf7110) was in the history that was merged back to master. At this point (85a13270), master has the lines of code that were added by daf7110.

When one of our developers merged master into his branch (dev-branch), the lines of code went away. They never existed in the history of the developer's side of the merge. I verified this with:

git checkout c513d2 # <--last commit on dev-branch before the merge
git log -S string_that_was_added -- lib/File/It/Was/In.pm

So:

  1. The code never existed on dev-branch before the merge.
  2. It was on master, added since "git merge-base c513d2 85a13270"

How can I get more detail as to why Git's merge logic decided the code should not survive the merge? Or is there some other reason my commit would not have survived that merge?

like image 796
Ryan Olson Avatar asked Apr 12 '12 01:04

Ryan Olson


2 Answers

I don't know of a reason off the top of my head why this would happen, but I can give some debugging tips.

First, I would make sure that you don't have any odd settings. Do a fresh clone of a repository that has both of these branches, in an account that has no Git configuration. Try doing a merge. Does it still happen? If not, then check your settings, and see if there's anything odd; in particular, I would be suspicious of any rerere settings. Also, try doing the merge with --no-rerere-autoupdate to see if it still drops the lines in question.

After that, I would start by turning on the maximum verbosity for your merge, and try doing the merge again:

GIT_MERGE_VERBOSITY=5 git merge -v master

This will give you some more information on the decisions that the merge process made.

If that doesn't wind up telling you anything useful, you can also try to see if it happens with different merge strategies, or different options to the default, recursive merge strategy. Try git merge -s resolve to try a simpler, but stupider merge strategy. Do you still get the problem? How about trying different option for the recursive strategy, such as git merge -s recursive -X patience? Do the last with the extra verbosity turned on, and see if that makes different decisions than the default merge strategy.

If that doesn't work, then it's time to try manually reducing this down to a minimal test case. First, I would try to limit yourself down to just the file in question. Make sure you're working in a throwaway repository, and use git filter-branch to delete all files, but the one in question that the lines are missing from. When you do that, and try doing the merge again, do you get the same results? If not, it's possible that the lines are disappearing due to Git getting confused and thinking that they moved to another file when they really didn't.

If you can still reproduce the problem with just the one file, it's time to start walking back the revision history of each branch, doing a merge, until the problem goes away. For instance, starting with the development branch, jump back about halfway, try doing the merge again, and see if the lines from master still disappear. If they do, try jumping back further. If not, jump forward along the development branch, and try again. Once you've found the commit that introduces the problem on the development side, try the same on master, moving back the commit that you are merging in until the problem goes away.

Now that you've gone back as far as you can, try recreating a simplified version of the history before that point. Create a new Git repository, check in the merge base of those two commits. Then check in the two commits that you tracked back to above in separate branches. Can you merge those without problems, or does it depend on some aspect of the merge topology in between? If it does, try recreating the full merge topology; create just the commits necessary to do all of the merges in the history. Can that reproduce your problem?

At this point, you should have the simplest possible history to reproduce the problem, with just a single file demonstrating it (or will have found out something interesting when you weren't able to reduce it any further and reproduce the problem). I would recommend either posting this repository publicly so that we could take a look at it, if there isn't anything sensitive in that one file, or if there is, trying to reduce it down further, editing each commit to remove everything, but the relevant lines. You should also be able to replace each relevant line with a placeholder value, leaving you with a minimal test case that doesn't contain any of your actual code.

All of the above is a significant amount of work, but hopefully you will find something interesting before completing all of it, or may be able to skip ahead and produce a minimal test case without going through all of the intermediate steps. Once you have a more minimal test case, or results from one of the above steps, post them and we can take a closer look.

like image 100
Brian Campbell Avatar answered Nov 03 '22 03:11

Brian Campbell


I met a similar issue. It looks like it was due to the fact that the line of code in question was first introduced by a commit, and then removed by a revert. That branch was merged to the master.

Then that line was reintroduced on a development branch, but it was systematically stripped off when merging the development branch to the master. I assume that somehow Git keeps track of the revert and assume that this line is not needed and shall be removed.

like image 29
Dominique Avatar answered Nov 03 '22 02:11

Dominique