Home > front end >  Why does git revert behave differently in these cases?
Why does git revert behave differently in these cases?

Time:07-25

Let's say I have ProjectA and ProjectB in which I have only one file called test.txt in both projects and tracking it with git. After the first commit, the content of the file in both projects look like below.

one
two
three
four

After second commit, the content of the file in both project looks like below.

one
2
three
four

For third commit, I change the content a bit different

test.js in ProjectA after third commit

one
2
3
four

test.js in ProjectB after third commit

one
2
three
4

Now my question is.. after third commit, when I try to revert the changes made in second commit using git revert like below..

git revert secondCommitHashId, git ends up in merge conflict in ProjectA, but git happily gives me vim editor mode to enter revert message in ProjectB to finish the revert operation without merge conflict.

Why does git revert hashId behave different in these cases?

CodePudding user response:

A git revert is a merge operation. You have this history:

1--2--3

and you ask for

git revert 2

The base version is the project state at 2, "our" version is the state at 3, and "their" version (which is being merged in) is 1. In other words, the changes from 2 to 1 and from 2 to 3 are merged.

In both your examples, the change from 2 to 1 replaces the text 2 in line 2 by the word two (bad coincidences of 2s, unfortunately). The differences is in the projects: In Project A, the change from 2 to 3 changes the 3rd line, while in Project B, the change from 2 to 3 changes the 4th line.

In the former case, the diverging changes modify adjacent lines. Git flags this as a conflict.

In the latter case, there is an unmodified line (the third line) between the diverging changes. Here Git is able to separate the changes clearly and can resolve the merge.

CodePudding user response:

If you look at the conflict file, especially if you have your merge conflicts configured to use diff3 formatting, the answer is clear. This is what the conflicted file looks like (I've changed some names to make things clearer):

one
<<<<<<< third commit
2
3
||||||| second commit
2
three
=======
two
three
>>>>>>> first commit
four

In the middle is the second commit, with 2 three. Above that is where we are now, the third commit, with 2 3. Below it is the first commit, with two three.

Now, what is a revert? It is a reversed enactment of the reverted commit's diff from its parent, together with an enactment of the reverted commit's diff from where we are now.

So in this case, what reverting the second commit onto the third commit means is: to enact both the reversal of the second commit back to the first commit, and the change from the second commit to the third commit, simultaneously.

So Git needs to find a way to go from 2 three to both 2 3 and two three simultaneously. But there is no such way! Thus Git doesn't know what to do, and there is a conflict.


The only surprise here, perhaps, is that third line is part of the problematic hunk. Why doesn't Git think about just the second line, alone? It's because Git never does that. There's no such thing, in Git's mind, as a one-line hunk. It always considers the context. So if two consecutive lines differ for one of the diffs under consideration, they are both part of the hunk we are trying to resolve.

And that's why the same issue doesn't arise for your second example. In your second example, the third line does not change between the second the third commits, so the goal of the revert is to enact the reverse-change from one 2 three to one two three, plain and simple, while also enacting the change of 4 to four. And there is no problem doing that.

  • Related