Let's assume we have a history like this:
G1 - G2 - G3 - B1 - B2 - B3
Where G1-G3 are 'good' commits and B1-B3 are 'bad' commits and we want to revert them.
If changes are not pushed to the server yet then the easy way is
to reset the state to previous commit with
$ git reset --hard HEAD~3
Here we can refer to
This way the last good commit
G1 - G2 - G3 - B1 - B2 - B3 \ \ \ \-- HEAD \ \ \------ HEAD~1 \ \---------- HEAD~2 \-------------- HEAD~3
But if changes are pushed already then it is better to use
And here is how to do this for multiple commits:
$ git revert --no-commit HEAD~2^..HEAD
$ git revert --no-commit HEAD~3..HEAD
We need to revert a range of revisions from B1 to B3.
The range specified with two dots like
<rev1>..<rev2> includes only
commits reachable from
<rev2>, but not reachable from
man -7 gitrevisions).
Since we need to include B1 (represented by HEAD~2) we use HEAD~2^ (its parent) or HEAD~3 (also parent of HEAD~2).
HEAD~2^ syntax is more convenient if commit SHAs are used to name commits.
--no-commit option tells git to do the revert, but do not
commit it automatically.
So now we can review the repository state and commit it. After that we will get the history like this:
G1 - G2 - G3 - B1 - B2 - B3 - R`
R' is a revert commit which will return repository state to the commit
Run git diff to check this (output should be empty):
$ git diff HEAD~4 HEAD
Another way to run revert is to specify commits one by one from newest to oldest:
$ git revert --no-commit HEAD HEAD~1 HEAD~2
In this case there is no need to specify HEAD~3 since it is a good commit we do not want to revert.