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

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

我希望分支的头部指向A,即,我希望B、C、D和head消失,我希望头部与A同义。

听起来我可以尝试重新设置基础(不适用,因为我已经在两者之间进行了更改),也可以恢复。但如何恢复多次提交?我一次回复一个吗?订单重要吗?


当前回答

扩展我在评论中写的内容

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

因此,解决方案是创建一个新的提交,以恢复您想要删除的更改。您可以使用git-restore命令执行此操作。

您遇到以下情况:

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

(这里的箭头指的是指针的方向:提交时的“父”引用,分支头(分支引用)时的顶部提交,head引用时的分支名称)。

您需要创建以下内容:

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

其中[(BCD)^-1]表示恢复提交B、C、D中更改的提交。数学告诉我们(BCD

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message for all of them"

适用于除合并提交以外的所有操作。


另一种解决方案是签出提交A的内容,并提交此状态。也适用于合并提交。但是,添加的文件不会被删除。如果您有任何本地更改,请先将其保存:

$ git checkout -f A -- . # checkout that revision over the top of local files
$ git commit -a

那么您将出现以下情况:

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

提交A'的内容与提交A相同,但提交不同(提交消息、父级、提交日期)。


杰夫·费兰(Jeff Ferland)的替代解决方案,由查尔斯·贝利(Charles Bailey)修改,基于同样的想法,但使用了git重置。在这里,它被稍微修改了一下,这种方式适用于一切:

$ git reset --hard A
$ git reset --soft D # (or ORIG_HEAD or @{1} [previous location of HEAD]), all of which are D
$ git commit

其他回答

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

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

git revert master~3..master

或git revert B…D或git return D C B

这将使用B、C和D的反向提交在本地创建一个新的提交(这意味着它将撤消这些提交带来的更改):

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

还原共享存储库上的一组提交的简单方法是将git-rever与git-rev列表结合使用。后者将为您提供一个提交列表,前者将自己进行还原。

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

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

这将恢复您需要的一组提交,但将所有更改保留在工作树上,之后应照常提交。

另一种选择是对每个还原的更改进行一次提交:

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

例如,如果您有一个提交树

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

要将更改从eee还原为bbb,请运行

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

扩展我在评论中写的内容

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

因此,解决方案是创建一个新的提交,以恢复您想要删除的更改。您可以使用git-restore命令执行此操作。

您遇到以下情况:

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

(这里的箭头指的是指针的方向:提交时的“父”引用,分支头(分支引用)时的顶部提交,head引用时的分支名称)。

您需要创建以下内容:

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

其中[(BCD)^-1]表示恢复提交B、C、D中更改的提交。数学告诉我们(BCD

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message for all of them"

适用于除合并提交以外的所有操作。


另一种解决方案是签出提交A的内容,并提交此状态。也适用于合并提交。但是,添加的文件不会被删除。如果您有任何本地更改,请先将其保存:

$ git checkout -f A -- . # checkout that revision over the top of local files
$ git commit -a

那么您将出现以下情况:

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

提交A'的内容与提交A相同,但提交不同(提交消息、父级、提交日期)。


杰夫·费兰(Jeff Ferland)的替代解决方案,由查尔斯·贝利(Charles Bailey)修改,基于同样的想法,但使用了git重置。在这里,它被稍微修改了一下,这种方式适用于一切:

$ git reset --hard A
$ git reset --soft D # (or ORIG_HEAD or @{1} [previous location of HEAD]), all of which are D
$ git commit
git reset --hard a
git reset --mixed d
git commit

这将同时对所有人起到恢复作用。给出一个好的承诺信息。

我真的想避免硬重置,这就是我想到的。

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

返回A(后退4步):

git pull                  # Get latest changes
git reset --soft HEAD~4   # Set back 4 steps
git stash                 # Stash the reset
git pull                  # Go back to head
git stash pop             # Pop the reset 
git commit -m "Revert"    # Commit the changes