How to revert multiple git commits?

一曲冷凌霜 提交于 2019-12-16 20:05:09

问题


I have a git repository that looks like this:

A -> B -> C -> D -> HEAD

I want the head of the branch to point to A, i.e. I want B, C, D, and HEAD to disappear and I want head to be synonymous with A.

It sounds like I can either try to rebase (doesn't apply, since I've pushed changes in between), or revert. But how do I revert multiple commits? Do I revert one at a time? Is the order important?


回答1:


Expanding what I wrote in a comment

The general rule is that you should not rewrite (change) history that you have published, because somebody might have based their work on it. If you rewrite (change) history, you would make problems with merging their changes and with updating for them.

So the solution is to create a new commit which reverts changes that you want to get rid of. You can do this using git revert command.

You have the following situation:

A <-- B  <-- C <-- D                                               <-- master <-- HEAD

(arrows here refers to the direction of the pointer: the "parent" reference in the case of commits, the top commit in the case of branch head (branch ref), and the name of branch in the case of HEAD reference).

What you need to create is the following:

A <-- B  <-- C <-- D <-- [(BCD)^-1]                   <-- master <-- HEAD

where "[(BCD)^-1]" means the commit that reverts changes in commits B, C, D. Mathematics tells us that (BCD)^-1 = D^-1 C^-1 B^-1, so you can get the required situation using the following commands:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

Alternate solution would be to checkout contents of commit A, and commit this state:

$ git checkout -f A -- .
$ git commit -a

Then you would have the following situation:

A <-- B  <-- C <-- D <-- A'                       <-- master <-- HEAD

The commit A' has the same contents as commit A, but is a different commit (commit message, parents, commit date).

The solution by Jeff Ferland, modified by Charles Bailey builds upon the same idea, but uses git reset:

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit



回答2:


For doing so you just have to use the revert command, specifying the range of commits you want to get reverted.

Taking into account your example, you'd have to do this (assuming you're on branch 'master'):

git revert master~3..master

This will create a new commit in your local with the inverse commit of B, C and D (meaning that it will undo changes introduced by these commits):

A <- B <- C <- D <- BCD' <- HEAD



回答3:


Clean way which I found useful

git revert --no-commit HEAD~3..

This command reverts last 3 commits with only one commit.

Also doesn't rewrite history.




回答4:


Similar to Jakub's answer, this allows you to easily select consecutive commits to revert.

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'



回答5:


git reset --hard a
git reset --mixed d
git commit

That will act as a revert for all of them at once. Give a good commit message.




回答6:


First be sure that your working copy is not modified. Then:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

and then just commit. Don't forget to document what's the reason for revert.




回答7:


I'm so frustrated that this question can't just be answered. Every other question is in relation to how to revert correctly and preserve history. This question says "I want the head of the branch to point to A, i.e. I want B, C, D, and HEAD to disappear and I want head to be synonymous with A."

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

I learned a lot reading Jakub's post, but some guy in the company (with access to push to our "testing" branch without Pull-Request) pushed like 5 bad commits trying to fix and fix and fix a mistake he made 5 commits ago. Not only that, but one or two Pull Requests were accepted, which were now bad. So forget it, I found the last good commit (abc1234) and just ran the basic script:

git checkout testing
git reset --hard abc1234
git push -f

I told the other 5 guys working in this repo that they better make note of their changes for the last few hours and Wipe/Re-Branch from the latest testing. End of the story.




回答8:


This is an expansion of one of the solutions provided in Jakub's answer

I was faced with a situation where the commits I needed to roll back were somewhat complex, with several of the commits being merge commits, and I needed to avoid rewriting history. I was not able to use a series of git revert commands because I eventually ran into conflicts between the reversion changes being added. I ended up using the following steps.

First, check out the contents of the target commit while leaving HEAD at the tip of the branch:

$ git checkout -f <target-commit> -- .

(The -- makes sure <target-commit> is interpreted as a commit rather than a file; the . refers to the current directory.)

Then, determine what files were added in the commits being rolled back, and thus need to be deleted:

$ git diff --name-status --cached <target-commit>

Files that were added should show up with an "A" at the beginning of the line, and there should be no other differences. Now, if any files need to be removed, stage these files for removal:

$ git rm <filespec>[ <filespec> ...]

Finally, commit the reversion:

$ git commit -m 'revert to <target-commit>'

If desired, make sure that we're back to the desired state:

$git diff <target-commit> <current-commit>

There should be no differences.




回答9:


The easy way to revert a group of commits on shared repository (that people use and you want to preserve the history) is to use git revert in conjunction with git rev-list. The latter one will provide you with a list of commits, the former will do the revert itself.

There two ways to do that. If you want the revert multiple commits in a single commit use:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

this will revert a group of commits you need, but leave all the changes on your working tree, you should commit them all as usual.

Another option is to have a single commit per reverted change:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

For instance, if you have a commit tree like

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

to revert the changes from eee to bbb, run

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done



回答10:


None of those worked for me, so I had three commits to revert (the last three commits), so I did:

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

Worked like a charm :)




回答11:


In my opinion a very easy and clean way could be:

go back to A

git checkout -f A

point master's head to the current state

git symbolic-ref HEAD refs/heads/master

save

git commit



回答12:


If you want to temporarily revert the commits of a feature, then you can use the series of following commands.

Here is how it works

git log --pretty=oneline | grep 'feature_name' | cut -d ' ' -f1 | xargs -n1 git revert --no-edit



来源:https://stackoverflow.com/questions/1463340/how-to-revert-multiple-git-commits

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!