How I reverted several git commits in a single commit

By | February 26, 2011

I hate to publicly admit this, but I recently made four commits that should have been merged into one commit, including two with embarrassing commit messages like, “third commit without testing, for shame!” I’m thoroughly shocked that fellow coder, Dan McGee hasn’t already attacked me for my misdemeanor.

Please forgive me, I was tired and in a hurry and was working on something that was easier tested on the production server and most certainly deserve to be attacked by a velociraptor.

To complicate matters, there was a fifth commit in the middle of these four commits that was pertaining to an irrelevant task, and several other users had committed changes after those commits.

Fastforward to today. Those four commits made in a hurry, now have to be reverted. As with any task, there are several ways to do this using git, but none of them are immediately obvious. git reset –keep was out of the question because of the newer commits. I think I could have git rebased the changes out of a new branch and merged them, but the method that made the most sense to me was to revert them independently, and then squash them.

Here’s how my history looked:


The four C commits are the ones I want to revert. Ex was an extraneous commit I want to keep and the O commits were made by other authors later.

This was the desired end state:


where R is a commit reverting the changes made in the four C commits. I didn’t want to simply erase the C commits, (which can be done easily with git rebase), as embarrassing as they are, because they are public history that had been pushed to other users.

My process was to run git revert several times:

Possibly there is a way to do all of this in one command, I’m not sure. This left me with:


where the four R commits are reversions of the four C commits.

Then I ran:

git rebase -i is my favourite method of rebasing. It lists the five most recent commits in vim asking me what to do with each one. You can choose several options for each commit. Here is what I chose:

pick O3 says to include that commit and leave it unchanged. When rebasing, I usually go one commit earlier than I expect to make sure I’m modifying the correct history. The reword commit simply allows me to change the commit message of R1 to “Revert the XYZ changes because I no longer need them” The squash commits mean that those three R commits are merged into the previous commit — R1. And my end state is as desired:


I’m pretty sure there are other other ways to do this. I chose this multi-step process because it allows me to understand what is going on at each step and to double check that I haven’t accidentally removed, merged, or reverted a commit I didn’t mean to.

2 thoughts on “How I reverted several git commits in a single commit

  1. Jason Chu

    I think this is the sort of situation where git replace can be used. I don’t fully understand replace, but this is how I think it works.

    Because every state on a git tree is represented by a SHA1 hash which takes into account all previous history, two tree objects can be equivalent even if they were created by entirely different commit objects (or commit history). git replace lets you define a new commit object that describes a new history about how you got there from here. The old history still exists and is totally accessible, but the new history can be cleaned up.

    In your case, you’d revert all four changes, then create a new branch that didn’t include the original commits or the reverts. Then you’d use git replace to effectively show the history as never containing those commits.

    This has the added benefit of never being jumped into when doing something like a git bisect.

  2. Adam

    Nice. I’m just trying to learn some git myself, and this was useful to me. Thanks.


Leave a Reply

Your email address will not be published. Required fields are marked *