How to revert a commit in git

Git How-To

There are a few different ways to undo things in git but for the purpose of this post we will stick with the following scenario:

  • a repository with local and remote copies
  • multi-person collaborators
  • a recent change merged to remote (and potentially deployed) that needs to be reverted

git revert to the rescue!

TL; DR: steps to revert a commit on a remote repo:

  • on your local and main branch, pull the latest changes: git pull
  • create a new branch: git checkout -b remove_buggy_code
  • find the hash of the buggy commit with git log
  • git revert <commit_hash_to_be_reversed> [-m 1 --no-edit]

The command above will create a new merge commit where all the changes from the original merge commit are reverted. This is good because this keeps the repository’s history unchanged.

The optional tags shown above mean:

-m n: if you are reverting a merge commit you need to pass this flag with a number (this StackOverflow post explains why)

--no-edit: this will skip launching the default text editor

Let’s run through an example:

My local, main branch looks like this:

└❯ git log
commit 9ebef49af9f50996f32c863604b1fc6fdce71e26 (HEAD -> main, origin/main, origin/HEAD)
Author: Flavia Bastos <my_email>
Date:   Tue Oct 4 19:22:55 2022 -0300

    Fix typo on test name

Then someone merges a new commit to remote. When I pull the new changes, I see the new commit (86d1c24f17aa7) and a new merge commit (5aa51c6ea265cc) for those changes:

└❯ git log
commit 5aa51c6ea265cc43b93a66de97ca4d8fcafc69df (HEAD -> main, origin/main, origin/HEAD)
Merge: 9ebef49 86d1c24
Author: Someone Else <another_email>
Date:   Thu Oct 6 17:14:15 2022 -0400

    Merge pull request #1 from SomeoneElse/fancy_feature
    
    fancy code

commit 86d1c24f17aa7fd264a26d81533e53f6f48dea24 (origin/fancy_feature, fancy_feature)
Author: Someone Else <another_email>
Date:   Thu Oct 6 18:08:49 2022 -0300

    fancy code

commit 9ebef49af9f50996f32c863604b1fc6fdce71e26
Author: Flavia Bastos <my_email>
Date:   Tue Oct 4 19:22:55 2022 -0300

    Fix typo on test name

Note here that the merge commit (5aa51c6ea265cc) also outlines the parent commits in the “Merge” attribute:

Merge: 9ebef49 86d1c24

Now, it turns out that this fancy code was also buggy and it caused some things to break really bad ?

Let’s fix this quickly by creating a new branch from this latest main:

└❯ git checkout -b bug_fixer
Switched to a new branch 'bug_fixer'

From the output of the git log command above, we know that we want the most recent (top) commit hash, but there could be other commits on top of it. Always confirm with git log.

Now let’s revert the buggy commit (5aa51c6ea265cc) - note we are on a separate branch and I’m using the -m 1 flag, because I want to revert to its first parent 9ebef49 (-m 2 would revert to the second parent 86d1c24):

└❯ git revert 5aa51c6ea265cc43b93a66de97ca4d8fcafc69df -m 1 --no-edit
[bug_fixer 560ad2d] Revert "Merge pull request #1 from SomeoneElse/fancy_feature"
 Date: Thu Oct 6 18:30:50 2022 -0300
 1 file changed, 1 insertion(+), 1 deletion(-)

All that’s left now is to push this bug_fixer branch up to remote and merge this change:

└❯ git push origin bug_fixer
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 402 bytes | 402.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
remote: 
remote: Create a pull request for 'bug_fixer' on GitHub by visiting:
remote:      https://github.com/FlaviaBastos/my_project_name/pull/new/bug_fixer
remote: 
To github.com:FlaviaBastos/my_project_name.git
 * [new branch]      bug_fixer -> bug_fixer

After this last change is merged in the remote repo, go back to the main branch, pull the latest changes and confirm with git log that the buggy commit was reverted:

❯ git log
commit 4332e3e999c2056019cf64255f17b32a6ef1992b (HEAD -> main, origin/main, origin/HEAD)
Merge: 5aa51c6 560ad2d
Author: Someone Else <another_email>
Date:   Thu Oct 6 17:53:28 2022 -0400

    Merge pull request #2 from FlaviaBastos/bug_fixer
    
    Revert "Merge pull request #1 from SomeoneElse/fancy_feature"

commit 560ad2ddfd207fad2f609873270bdd3f645bb4b6 (origin/bug_fixer, bug_fixer)
Author: Flavia Bastos <my_email>
Date:   Thu Oct 6 18:30:50 2022 -0300

    Revert "Merge pull request #1 from SomeoneElse/fancy_feature"
    
    This reverts commit 5aa51c6ea265cc43b93a66de97ca4d8fcafc69df, reversing
    changes made to 9ebef49af9f50996f32c863604b1fc6fdce71e26.

commit 5aa51c6ea265cc43b93a66de97ca4d8fcafc69df
Merge: 9ebef49 86d1c24
Author: Someone Else <another_email>
Date:   Thu Oct 6 17:14:15 2022 -0400

    Merge pull request #1 from SomeoneElse/fancy_feature
    
    fancy code

commit 86d1c24f17aa7fd264a26d81533e53f6f48dea24 (origin/fancy_feature, fancy_feature)
Author: Flavia Bastos <my_email>

Phew! Crisis averted!

But what about reset --hard? Doesn’t it revert commits too?

Yes, it does. Only locally though. git reset will move the head (the pointer) of your repo to whatever commit you set it to, effectively removing anything after it - chopping anything above the head, if you will. The diff is lost. If you then do a git status, you will not see those other commits anymore, nor anything new that needs to be staged and you are rewriting history. This is only safe to do if you are working solo or on a branch that has not been pushed to remote yet.

If you need to push the changes to a remote repo, you need to use git revert.

Learn more

git revert documentation: https://git-scm.com/docs/git-revert


If you found this helpful, please share this article!

The post How to revert a commit in git was originally published at flaviabastos.ca