Git commit lost after merge

We have 3 branches (A, B, C), as shown below:

---\--A1--\------Am------Merge1---An---Merge2--- \ \ / / \ \--C1---C2---/ / \ / \--B1--------------Bn---------/ 

The problem appears in Merge2. Some commit on branch C ( not all, but some , let C2) is lost on branch A after Merge2, which is present between Merge1 and Merge2.

When Merge2 is executed, there is only one file conflict that is not related to the lost commit (C2). And we resolve the config and successfully complete the merge.

It seems that C2 is reversing Merge2 on branch A without any log.

What happened? What could be causing this situation?

+16
source share
2 answers

You mean not that the commit itself was lost, but that the changes made (to a file) to this commit were canceled (un-m ade).

It is worth noting that commits do not “make changes” to files. Instead, each commit stores a complete set of files, intact and intact. When you ask Git to show you a commit (let's say its ID is c2c2c2... ):

 $ git show c2c2c2 

what git does:

  1. extract commit c2c2c2...
  2. fetch parent commit c2c2c2...
  3. create a list of differences from parent to child

This is how Git manages to show you what has changed: it compares "what you had before this commit" with "what you had at that commit." (Git can do this quite quickly, optimized, because each file comes down to a unique “fingerprint” hash, and Git can simply compare fingerprints (hashes) at first. If the hashes match, the files are the same. Extracting real file data can only be difficult in If the hashes are different.)

This process - saving entire files instead of accumulating changes - is called "snapshot storage." (Other version control systems tend to accumulate changes called “delta storage.” They do this because saving changes to files obviously takes up much less space than saving files. Git cleverly bypasses the problem and starts, using anyway less disk space than older delta-based version control systems.)

Why "Git stores snapshots, not deltas" is so important here

Fixing a merge is a special, one specific and obvious way. Take a look at your own chart and see Merge1 . What commit comes right in front of him?

The answer here is that both Am and C2 come “right before” Merge1 . That is, commit Merge1 has two parent commits.

If you ask Git to show that you are doing Merge1 , which parent should it be compared to?

Things get especially weird here. The two git log -p and git show commands seem very similar. In fact, they are very similar. The only obvious difference is that git log -p shows more than one commit, while git show shows only one commit that you told it to show. You can run git log -n 1 -p <commit> to show only one commit, and now it seems that they are exactly the same.

They are not!

When you use git show to commit the merge, Git tries to solve the "what to commit for comparison" problem by comparing both parents at the same time. The resulting diff is called combined diff.

However, when you use git log -p merges, Git simply throws up metaphorical hands, says: "I can’t show patches against two parents," surrenders and proceeds to the next commit. In other words, git log -p doesn't even try to use diff to merge.

But wait, there are more

Now, in this case, you might be tempted to see if you can figure out what happened to your file from the c2c2c2... commit c2c2c2... using git show for two merges, in particular for Merge2 , where the changes were Merge2 . But git show creates a combined diff by default, and a combined diff intentionally skips most of the diff. In particular, the combined diff lists only files that have been modified from all parents .

Suppose the file in which your changes from C2 were undone is the f2 file. And, according to the graph, the two parents of Merge2 are An (which has f2 way you want) and Bn (which doesn't).

In fact, it happened here that during the merge that Merge2 created, you somehow told Git to use the f2 version from the Bn commit. That is, the file f2 in Merge2 exactly the same as the file f2 in Bn , and differs from f2 in the commit An .

If you use git show to watch Merge2 , the combined diff will skip f2 because it is the same as f2 in Bn .

The same is true, only worse with git log -p : it skips merging completely because it is too difficult to show the differences.

Even without -p , when you request "modified files", git log exits, completely skipping merging. That is why you cannot see this in the output of the log.

(Also, the reason that git log master -- f2 never shows the C2 commit itself is because adding the file name to the git log options leads to a “simplification of the history.” In what I consider to be somewhat erroneous behavior, Git shuts down. Simplifying too much history so that it never shows a C2 commit. Adding --full-history before -- f2 restores C2 in the displayed set of commits. However, merging is still missing as git log skips it .)

How to see the changes

There is a solution. Both git show and git log have the optional -m flag, which "separates" merges. That is, instead of considering Merge2 as a merge commit, they split the merge into two "virtual commits." One of them will be “ Merge2 vs An ” and you will see all the differences between the two commits, and the other will be “ Merge2 vs An,” and you will see all the differences between the two commits. This will show that the file f2 been reinstalled to the same one as in Bn , having lost the version from C2 that appears in An but not in Bn .

(Include --full-history as well as -m to verify that the C2 commit is being detected.)

How did this happen first

This part is not entirely clear. You said that there was a merge conflict, which means that git merge stopped and received manual help from a person. At some point during this help, the person probably updated the index with the version of the file f2 from Bn (or at least the version f2 , in which the change was not made back to C2 ).

This can happen during merges, and it's a little insidious, because Git shows merges with these compressed (combined) differences, or in the case of git log -p , by git log -p , by default. This is something to watch out for, especially if the merger requires manual resolution of the conflict. In most cases, this type of merge error can be found using automated tests. (Of course, not everything can be verified.)

+16
source

I also had this problem and here is how I fixed it:

  1. Used git log to show commit history.

  2. Used git show <filename> to show the changes made to the stubborn file.

  3. It didn’t work. I searched for StackOverflow, tried it several times.

  4. Closed and reopened the file. Voila.

A recently opened file in my Atom text editor displayed the expected changes.

0
source

All Articles