How to rollback changes with Git

Bad things happen…
Sometimes you commit a change that you want to rollback afterwards. There are multiple ways to rollback changes with Git. Each solution has it advantages and matches another use case. Here is an overview of some of the possible approaches that I tend to use.

Rollback of the latest (single) commit only

Imagine you have committed a change that you want to rollback – so you actually want to rollback the latest commit only.

You could either just cut off the latest commit from the commit history, or you could revert the latest commit.

Cut off latest commit from history
This is the scenario where you cut off the latest commit:

FROM:               TO:
A - B - C           A - B

While this is a good solution if you are either working locally only, or you haven’t pushed the “bad” commit.

!!! You should AVOID THIS SOLUTION if you are using a remote repository and have already pushed the “bad” commit. You could push the changes with a force push, but you have been warned. Don’t do this! ;-) !!!

You can accomplish this by using a hard reset:

$ git reset --hard B

Revert commit, resulting in a linear commit history
This is the scenario where you revert the latest commit by creating a new commit that is the opposite of the previous commit:

FROM:               TO:
A - B - C           A - B - C - D

Where the file tree of D is identical to B, which means that D is the revert commit of C.
You can accomplish this by using the revert command:

$ git revert C

This is my preferred solution if I am using a remote repository and have already pushed the “bad” commit.

Rollback of multiple commits

Now imagine you don’t want to just rollback the latest commits, but multiple commits – in our example going back to the file tree of commit A.
The solution using a hard reset does work for this scenario, too, as you can reset to any given commit. But as said before, this should be avoided when using a remote repository.

In that case you should go for a revert commit, reverting the changes C and B.

FROM:               TO:
A - B - C           A - B - C - D

Where the file tree of D is identical to A, which means D is the revert commit of C and B.

Rollback using multiple revert commands
You can accomplish this by using the revert command:

$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "Going back to A"

By using the –no-commit switch, you can combine multiple reverts into one single commit (in this case commit D).

This could also be a nice solution if you want to rollback certain commits that are not in series.

Rollback using a temporary branch
If you don’t want to revert every single commit (e.g. you want to revert hundreds of commits, going back to a very old state), there is another solution.
You can bring your working copy to the state of commit A and then commit the delta to commit C.

We do this by creating a temporary branch from commit A, switch to that branch, do a soft reset to the latest commit C (moving the branch pointer to commit C, but keeping the working copy at the state of commit A), commit (the revert commit between A and C) and merge the temporary branch into the original branch. You can now push your original branch and delete the temporary branch.
(thanks to Steffen Schäfer for pointing this out)

So for the master branch, here is the example:

$ git branch temp A
$ git checkout temp
$ git reset --soft master
$ git commit -m "going back to A"
$ git checkout master
# I recommend doing a fetch and merge, to make sure we are up to date with the remote repository.
$ git merge temp
$ git push

# delete the temporary branch
$ git branch -d temp

Rollback using Git reflog
There is even a simpler approach that I found at StackOverflow using more Git magic (Git reflog).

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is C
$ git commit -a
Short URL for this post: http://blog.oio.de/lyIrX
This entry was posted in Build, config and deploy and tagged , , , , , , . Bookmark the permalink.

Leave a Reply