Trying to Understand Revert And Merges in Git
Disclaimer: I'm not an expert in Git. This post is me trying to understand how it works, by trying to explain it to someone else. If I've said something wrong in this post, please don't hesitate to correct me by leaving a comment below or getting in touch with me.
So, in the past, I got confused by how a git revert
command affected the merges. Part of it is due to my TFS background. But anyway, as it turns out, it works exactly how it is supposed to work, once you have a better understanding of how Git works.
A git revert
rolls back a specific commit made in the repo. In addition to that, it adds a new commit to indicate that the previous commit was rolled back.
To better explain how this affects merges, I'll try to go over a scenario where I merge branches, then revert a commit in one branch, then try to merge the branches again. So here goes.
Imagine that I have two branches, develop
and master
. The develop
branch was created based off the master
branch, so they should have the same code in them.
Let's say that in the develop
branch, there were new commits added. Let's call them Commit 1A, Commit 1B and Commit 1C.
Then I decided to merge develop
into master
.
After the merge, both branches are in sync again. If you look at the history of commits into both branches, at the top of the list would be Commit 1C, then Commit 1B then Commit 1A.
Depending on how you do your merges, the master
branch would normally have an extra commit showing the merge from the develop
branch. Let's call that Commit 2A. But after that commit though, you would still see Commit 1C, then Commit 1B then Commit 1A, just like in the develop
branch.
Anyway the point is, at this moment in time, both branches have the same set of changes/commits. Everything that is in the develop
branch, is also in the master
branch.
Table below shows the history of commits for both branches.
develop | master |
---|---|
Commit 1C | Commit 2A |
Commit 1B | Commit 1C |
Commit 1A | Commit 1B |
... | Commit 1A |
But then I decided to revert a change in the master
branch because it broke something. Let's say I reverted Commit 1B in the master
branch. Since a git revert
command creates a new commit, it will show up in the history for the master
branch too. Let's call this Commit 2B.
Around the same time, I also pushed new code to the develop
branch. Let's say that was Commit 1D.
The updated table below shows the commits for both branches at this point in time.
develop | master |
---|---|
Commit 1D | Commit 2B |
Commit 1C | Commit 2A |
Commit 1B | Commit 1C |
Commit 1A | Commit 1B |
... | Commit 1A |
And so what happens now when I merge the develop
branch into the master
branch?
Previously, I expected that if I merged develop
into master
at this point in time:
- I would get Commit 1D added to the
master
branch and... - I would get Commit 1B added back to the
master
branch...
But that is not how Git works. At least not for the 2nd expectation. That may be the case with a merge in TFS, but not in Git. And that's because of the effect of the git revert
command and how Git compares differences between branches during a merge.
The git revert
command creates a new commit to track the rolling back of a previous commit. In this case, it created Commit 2B. When you merge develop
into the master
branch at this point, Git will look at the commit history in the develop
branch and will say, “Hey, the master branch has all of the same commits except Commit 1D, so I'll only add Commit 1D into the master branch.”
So what about Commit 1B that was reverted in the master
branch, but is still in the develop
branch? Why was that not merged?
Since merges are directional (source branch to target branch), Git sees that Commit 1B in the develop
branch (source branch) is already in the history of commits for the master
branch (target branch). So as far as a merge goes, Commit 1B is already in the master
branch and so there is no need to add it during a merge.
Now if you were to reverse the direction of the merge, if you were to merge the master
branch into the develop
branch, Git will now see that Commit 1B was reverted in the master
branch (source branch) via Commit 2B. And so Git will then revert Commit 1B in the develop
branch (target branch) by copying the changes from Commit 2B as part of the merge.
So, there you go. I hope this post can help someone else who had trouble understanding how reverts and merges work in Git.
Tags: #Git
Useful References:
Discuss... or leave a comment below.