Getting geeky with Git #9. Understanding the revert feature

Git

This entry is part 9 of 11 in the Getting geeky with Git

Sometimes, when working on our project with Git, we might commit some code that we are not happy with. One of the solutions that Git provides to undo that is the revert feature. This article looks into how it works and what we need to look out for when using it.

The idea behind Git Revert

While we might be tempted to think of the revert feature as a means of undoing our work, this is not the complete picture. When reverting, Git analyzes the changes we want to undo. Then, it creates a commit on top of them that reverses the changes.

Above, we’ve created the file add committed it to the repository. Let’s take a look at the log.

commit 075913625d998dbccd74613f90dd21f74d725ca6 (HEAD -> master)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 17:23:53 2021 +0200

Added the index.js file

We can see the hash of the commit above. It serves as an identifier generated based on the contents of the commit. We can use it to refer to this commit when attempting a revert.

If you want to know more about commit hashes, check out Getting geeky with Git #2. Building blocks of a commit

Usually just a few first characters of the hash is enought to uniquely identify a commit.

Running the above command opens a text editor.

Above, we can specify the commit message that will be attached when reverting the changes. The crucial thing here is that Git keeps this operation in history.

commit 9c3b233aa064a1d12436d869ed8ecc0e4ce9b39b (HEAD -> master)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 17:36:57 2021 +0200

Revert “Added the index.js file”

This reverts commit 075913625d998dbccd74613f90dd21f74d725ca6.

commit 075913625d998dbccd74613f90dd21f74d725ca6
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 17:23:53 2021 +0200

Added the index.js file

Let’s look closer into the revert commit.

We can see that the revert commit deletes the file.

Reverting a merge

So far, we’ve only reverted a simple commit. Reverting merges requires us to dig a little deeper into the revert feature.

Above, we’ve created the and branches. Let’s now merge it to the branch.

Let’s look into the logs to figure out what happened when we’ve merged two branches into our branch.

commit 2e1ca724207ee83814c7593c7f992bba58abdd3a (HEAD -> master)
Merge: 924a7a3 e3416e7
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 18:26:22 2021 +0200

Merge branch ‘feature-b’

commit e3416e72e78761f40a880050427764b262cd05d5 (feature-b)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 18:26:06 2021 +0200

Implemented feature B

commit 924a7a3a93d9f0d26372b55cb26b9d76622f378a (feature-a)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 18:12:00 2021 +0200

Implemented feature A

In the logs, we can see that we only have one merge commit. This is because Git performed a fast forward merge when merging into .

A fast forward merge can occur when there is a linear path from the current branch tip to the target branch. It wasn’t the case when merging into , and therefore, Git created a merge commit.

If you want to know more about merging, check out Getting geeky with Git #4. Fast-forward merge and merge strategies

Since Git didn’t create a merge commit for , performing a revert is as simple as reverting the commit that implements feature A.

Setting a mainline

It gets a little bit more complicated when we want to revert . To understand it better, let’s take a closer look at the merge commit.

parent 924a7a3a93d9f0d26372b55cb26b9d76622f378a
parent e3416e72e78761f40a880050427764b262cd05d5
author marcin <wanago.marcin@gmail.com> 1621787182 +0200
committer marcin <wanago.marcin@gmail.com> 1621787182 +0200

Merge branch ‘feature-b’

In the second part of this series, we’ve learned that a commit’s parent is the previous commit. When Git creates a merge commit, it has multiple parents. Each parent is the tip of the branch involved in the merging process.

When we perform a revert on a merge commit, git doesn’t automatically know which branch we merged the changes into. Git refers to it as the mainline branch. When reverting the above merge, we have two possibilities:

When we set the with a number, we indicate which parent is mainline. To be able to do that, let’s use .

commit 924a7a3a93d9f0d26372b55cb26b9d76622f378a (feature-a)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 18:12:00 2021 +0200

Implemented feature A

commit e3416e72e78761f40a880050427764b262cd05d5 (feature-b)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun May 23 18:26:06 2021 +0200

Implemented feature B

Above, we can see, that the last commit on the first parent implements feature A. Therefore, we know that it is the master branch. To perform a successful revert, we need to set the to .

Issues to consider

The most important thing to grasp from this article is that reverting commits doesn’t erase them from Git history. Instead, it creates new commits that aim to reverse the changes. The above can cause some unforeseen consequences.

Let’s once again create a branch with a commit.

Imagine a situation in which you merge into by accident and you want to revert it.

The keyword is the pointer to the commit our repository is checked out on. In this case, it is the commit we’ve merged from . If you want to know more, check out Getting geeky with Git #3. The branch is a reference

The crucial thing about the above commands is that now the master branch contains a history of deleting the file. This can prove to be quite an issue later.

Now, let’s say that we’ve continued working on the branch. Meanwhile, our team pushed some important work to the branch that we now need. Let’s merge to .

Because the master branch contains a history of deleting the file, merging it causes the file to disappear in the branch also.

Summary

The crucial thing to understand about reverting is that it doesn’t erase the changes from Git history. This can lead to issues similar to the one described above. Therefore, it might be a good idea to avoid reverting. If we are not aiming to revert from branches used by other developers, a suitable alternative would be to perform an interactive rebase. If you want to know more, check out the sixth part of this series.

Series Navigation<< Getting geeky with Git #8. Improving our debugging flow with Bisect and WorktreeGetting geeky with Git #10. The overview of Git hooks with Husky >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments