Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git Changes to file lost after merge with remote (SourceTree, GitHub)

I've had multiple occasions reported from multiple teams just recently that certain changes are being lost after merging with our origin repo on GitHub.com. The team members are all using SourceTree as their git client.

The common thread that I've found is that it appears that the repo thinks that the file appears to have X number of commits on the branch before the merge and then X-Y commits after the merge. From looking at the branch's commit log, the commits are still there, but for some reason they are not applied to the file in question. In Source tree this is the same, but if you "Follow renamed files" it brings in all of the commits. There was absolutely no change in the file name or the folder structure.

What could be going on here?

https://www.dropbox.com/s/zrcb9tw2ptpb3b0/FileHistory_Commit.png?dl=0 https://www.dropbox.com/s/uc5v5d2bfztoicn/FileHistory_Develop.png?dl=0

like image 219
Michael Sheldon Avatar asked May 08 '15 20:05

Michael Sheldon


1 Answers

It is important to keep in mind that git commits are not tied to specific files, and they are not defined by diffs; each commit is simply a snapshot of the entire repository. Therefore, when you look at the "history" of a file, it is up to the tool you are using to decide which commits to "associate" with that file. This is normally done in the obvious way: if a commit's version of a file is different than the commit's parent's version of the file, then that commit is included in the file's history.

Where things get complicated is when a commit has more than one parent, i.e. in a merge. Normally, you don't want to see these in a file's history if there is diff with only one of the parents. To see why, consider the common case where you make change X in a branch, commit it in commit A, and then later on merge that branch into master with commit G. If you look at a diff between G and it's primary parent, you will see change X. But that is not where X was "really" introduced; that happened in commit A. So when looking at the log of the file, you would not want to see commit G, you would just want to see commit A. The exception to this is when conflicts are resolved; in that case a change would be really introduced by the merge itself. This would be indicated by their being a diff in the file when compared with the primary and secondary parents. When you click on "Follow renamed files" SourceTree seems to include the merge commits, even though it has nothing to do with renamed files.

Now that's all fine and dandy when merges are performed correctly. In your case, however, you have some botched merges - that is why commits are being lost. Your repo is private, so I cannot look at it and tell you precisely which merges were botched and how, but here is what probably happened. A developer did a git pull which resulted in merge conflicts, which prevented the automatic commit that usually happens with pulls/merges. Along with the conflicted files, the developer saw changes in their staging area that they did not make. They figured that they did not want to commit random changes, so they undid those changes, fixed their merge conflicts, and committed.

It is those very changes that they undid that are now "lost". When you commit a merge, you are making a promise that the snapshot you are committing contains all the changes reachable by both parents. This means that when you do a diff of a correct merge with it's primary parent (which is what the developer sees when SourceTree lists "changed files"), you should see all the changes brought in by the secondary parent.

So bottom line is this: when you are in the middle of a merge and you see changes you did not make suddenly appear, you must commit those changes, or abort the merge.

like image 188
David Deutsch Avatar answered Oct 10 '22 10:10

David Deutsch