82

This is not a major problem, just something I want to know whether or not is possible.

Let's say we have two commits, abcd123 and wxyz789, that occur at non-adjacent, separate places, far back in a repo's history. Now let's say we want to revert them. Doing

git revert abcd123 wxyz789

would result in two separate commits, one reverting abcd123 and the other reverting wxyz789.

This is all fine and well, but what if the mistakes we want to fix in the two commits are logically linked, and for the purposes of self-documentation we'd like to make one single commit containing one single "I broke something so now I'm reverting files x, y and z" comment? Is there a git command that does this?

(I am of course aware that it is possible to create a commit where I just manually fix all the changes and then push. This is painful for all the obbious reasons.)

0

3 Answers 3

100

You can do:

git revert abcd123
git revert --no-commit wxyz789
git commit --amend

... and then write an appropriate commit message describing the combined effect of reverting both commits.

2
  • 50
    More concise: git revert abcd123 wxyz789 --no-commit git commit
    – RMP
    Commented Nov 17, 2015 at 16:45
  • 2
    More thorough answer here: stackoverflow.com/questions/1463340/revert-multiple-git-commits. Basically add the --no-commit flag to all the commits you want reverted, then commit the result and add a message for the whole batch.
    – Todd
    Commented Dec 14, 2015 at 16:47
49

In case of complicated reverts, which changes each other, the revert --no-commit might be problematic.

My simple solution was to do real revert, and the squash:

git revert <all commits>
git rebase -i

And then mark all the reverts as squash, except the first one, to create a single commit.

4
  • 4
    +1. For git learners: 1. read up a bit on what commits are, 2. become proficient with interactive rebase: squashing, removing, editing, rebasing, re-ordering commits. Once you make your mind familiar with it (hint: it's just time travelling), this solution becomes way more natural than switching around with stuff like --no-commit. (And don't be afraid of the "don't rebase published" rule -- as long as you do it on your local or private branch, you can do anything.) Commented Dec 15, 2016 at 9:19
  • 1
    Extra tips: First one can be changed from pick to reword (for entering a new commit message) or edit (for staging the changes without committing). For other reverts, you can use fixup instead of squash if you don't care about the commit messages.
    – ADTC
    Commented Mar 10, 2017 at 3:59
  • @AloisMahdal I actually tried to revert and rebase the already pushed commits to origin, and it worked well. It didn't modify the history of the commits, it just created a new single commit. I wonder why they say don't do interactive rebase for pushed commits in origin, or how will it modify the git history. Do you have any idea?
    – KarenAnne
    Commented Jul 23, 2019 at 4:02
  • @KarenAnne it's related to what exactly are you changing in the rebase. In my example (of git revert + git rebase) you just create some local commits and squash them to a single local commit. However, if you will try to rebase an existing commit, it will change the history. As an example, try to run git rebase -i origin/master~2 and try to change the already-merged commits Commented Jul 23, 2019 at 7:03
28
git revert -n <commits>
git commit

The first command will do all the reverts without create any commits and stage the final result (the -n option). After that, the commit command creates a single commit.

4
  • 1
    Perfect. Succinct. And it lets you add your commit message after all the reverts rather than going back to amend a commit that other answers suggest. Commented Jun 19, 2017 at 15:09
  • 1
    Except that it doesn't work. I need to roll back the last 5 commits, of which 3 are these fantastic Git merges that occur whenever you pull in someone elses changes. So this errors with 'commit <###> is a merge but no -m option given'. Each merge has two parents, apparently I have to determine which parent of each of these merges I need to keep. How the hell I even work that out let alone how I specify that to the revert command I have no idea.
    – Neutrino
    Commented Sep 4, 2017 at 15:10
  • @Neutrino you should be able to use --no-merges to avoid getting merge commits.
    – MattJenko
    Commented Jan 29, 2019 at 5:13
  • While that's interesting to know it doesn't solve the problem. I want merge commits in my commit stream, I just don't want to have to manually identify which parent the revert option should follow for each one and then have to feed that into the revert process. It seems to me that the revert option should be smart enough to choose the parent that is on the branch that I'm reverting from.
    – Neutrino
    Commented Jan 29, 2019 at 9:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.