How to revert multiple commits as part of a single commit
Asked Answered
M

3

81

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.)

Mirella answered 2/5, 2012 at 14:16 Comment(0)
D
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.

Doucet answered 2/5, 2012 at 14:25 Comment(2)
More concise: git revert abcd123 wxyz789 --no-commit git commitDeon
More thorough answer here: https://mcmap.net/q/12035/-how-can-i-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.Procathedral
E
48

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.

Earphone answered 23/10, 2013 at 8:47 Comment(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.)Emigrant
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.Sada
@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?Troopship
@Troopship 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 commitsEarphone
I
26
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.

Ioannina answered 8/12, 2015 at 2:8 Comment(4)
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.Iives
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.Restriction
@Restriction you should be able to use --no-merges to avoid getting merge commits.Rhatany
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.Restriction

© 2022 - 2024 — McMap. All rights reserved.