如何恢复多个git提交?

How to revert multiple git commits?

我有一个Git存储库,看起来像这样:

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

我希望分支的头指向A,也就是说,我希望B、C、D和头消失,我希望头与A同义。

听起来我要么尝试重新平衡(不适用,因为我在两者之间推送了更改),要么恢复。但如何恢复多个提交?我一次回复一个吗?订单重要吗?


扩展我在评论中所写的内容

一般规则是,您不应该重写(更改)已发布的历史记录,因为可能有人基于它进行了工作。如果重写(更改)历史记录,则在合并它们的更改和更新它们时会出现问题。

因此,解决方案是创建一个新的提交,它恢复您想要摆脱的更改。您可以使用git revert命令来执行此操作。

您有以下情况:

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

(此处箭头表示指针的方向:提交时的"父"引用,分支头时的顶部提交(分支引用),分支头时的名称)。

您需要创建以下内容:

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

其中"[(b c d)^-1]"表示恢复提交b、c、d中更改的提交。数学告诉我们(bcd)^-1=d^-1 c^-1 b^-1,因此可以使用以下命令获得所需情况:

1
2
3
4
$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m"the commit message"

另一种解决方案是签出commit a的内容,并提交此状态:

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

那么,您将遇到以下情况:

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

commit a'与commit a具有相同的内容,但是不同的commit(commit message、parents、commit date)。

Jeff Ferland的解决方案,由Charles Bailey修改,基于相同的想法,但使用了git reset:

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


为此,您只需使用revert命令,指定要恢复的提交范围。

考虑到您的示例,您必须这样做(假设您在分支"master"上):

1
git revert master~3..master

这将在您的本地创建一个新的提交,反向提交为b、c和d(意味着它将撤消这些提交带来的更改):

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


干净的方式,我觉得有用

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

此命令只使用一次提交来还原最后3次提交。

也不会重写历史。


与Jakub的答案类似,这允许您轻松地选择要恢复的连续提交。

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


1
2
3
git reset --hard a
git reset --mixed d
git commit

这将作为他们所有人的回归。给出一个好的提交消息。


首先确保您的工作副本没有被修改。然后:

1
git diff HEAD commit_sha_you_want_to_revert_to | git apply

然后承诺。不要忘记记录恢复的原因。


我很沮丧,这个问题不能简单地回答。其他每一个问题都与如何正确地还原和保存历史有关。这个问题说:"我希望分支的头指向A,也就是说,我希望B、C、D和头消失,我希望头与A同义。"

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

我在阅读Jakub的帖子时学到了很多东西,但是公司里的一些人(可以在没有拉请求的情况下进入我们的"测试"分支)像5个坏的承诺一样推,试图修复和修复他之前犯下的一个错误。不仅如此,还有一到两个请求被接受,这现在很糟糕。所以忘记它,我找到了最后一个好的提交(abc1234),并运行了基本脚本:

1
2
3
git checkout testing
git reset --hard abc1234
git push -f

我告诉在这个回购协议中工作的其他5个人,他们最好记录下最近几个小时的变化,并从最新的测试中清除/重新分支。故事的结尾。


这是对Jakub答案中提供的解决方案之一的扩展

我面临这样一种情况:我需要回滚的提交有些复杂,其中几个提交是合并提交,我需要避免重写历史。我不能使用一系列git revert命令,因为我最终在添加的恢复更改之间遇到了冲突。我最后使用了以下步骤。

首先,检查目标提交的内容,同时将头放在分支的尖端:

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

(确保被解释为提交而不是文件;the。指当前目录。)

然后,确定要回滚的提交中添加了哪些文件,因此需要删除:

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

添加的文件应在行首显示"A",并且不应存在其他差异。现在,如果需要删除任何文件,请准备删除这些文件:

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

最后,提交回复:

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

如果需要,请确保我们回到所需状态:

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

应该没有区别。


恢复共享存储库上的一组提交的简单方法是将git revert与git rev-list结合使用。后者将向您提供一个提交列表,前者将自动执行恢复。

有两种方法可以做到这一点。如果希望在一次提交中还原多个提交,请使用:

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

这将恢复您需要的一组提交,但将所有更改保留在您的工作树上,您应该像往常一样提交它们。

另一种选择是每次恢复的更改都有一个提交:

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

例如,如果您有一个类似提交树的

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

要将更改从EEE还原为BBB,请运行

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


这些都不适合我,所以我有三个承诺要恢复(最后三个承诺),所以我做到了:

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

工作得很有魅力:)


在我看来,一个非常简单和干净的方法可能是:

回到A

1
git checkout -f A

将大师的头指向当前状态

1
git symbolic-ref HEAD refs/heads/master

。节约

1
git commit


如果要临时恢复某个功能的提交,则可以使用以下一系列命令。

这是它的工作原理

git log--pretty=oneline grep'feature name'cut-d''-f1 xargs-n1 git revert--不编辑