如何恢复多个 Git 提交?
我有一个看起来像这样的 Git 存储库:
A <- B <- C <- D <- HEAD
我希望分支的头指向 A,即我希望 B、C、D 和 HEAD 消失,并且我希望 head 与 A 同义。
听起来像我可以尝试重新设置基准(不适用,因为我已经在中间推送了更改),或者恢复。但如何恢复多次提交呢?我是否一次恢复一个?顺序重要吗?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
扩展我在评论中所写的内容
一般规则是您不应该重写(更改)您已发布的历史记录,因为有人可能以此为基础进行他们的工作。如果您重写(更改)历史记录,则在合并更改和更新它们时会遇到问题。
因此,解决方案是创建一个新提交,它恢复您想要删除的更改。您可以使用 git revert 命令。
你有以下情况:(
这里的箭头指的是指针的方向:提交情况下的“父”引用,分支头(branch ref)情况下的顶部提交,以及情况下的分支名称HEAD 参考)。
您需要创建以下内容:
其中
[(BCD)^-1]
表示还原提交 B、C、D 中的更改的提交。数学告诉我们 (BCD)- 1 = D-1 C-1 B-1,因此可以使用以下命令获得所需的情况:对于除合并提交之外的所有内容。
替代解决方案是 签出提交 A 的 内容,并提交此状态。也适用于合并提交。但是,添加的文件不会被删除。如果你先有任何本地更改
git stash
它们:那么你会遇到以下情况:
提交 A' 与提交 A 具有相同的内容,但是不同的提交(提交消息、父项、提交日期)。
由 Jeff Ferland 提出的替代解决方案,由 Charles Bailey 修改 基于相同的想法,但使用 git reset。这里稍微修改一下,这种方式适用于一切:
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:
(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:
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:Works for everything except merge commits.
Alternate solution would be to checkout contents of commit A, and commit this state. Also works with merge commits. Added files will not be deleted, however. If you have any local changes
git stash
them first:Then you would have the following situation:
The commit A' has the same contents as commit A, but is a different commit (commit message, parents, commit date).
Alternate solution by Jeff Ferland, modified by Charles Bailey builds upon the same idea, but uses git reset. Here it is slightly modified, this way WORKS FOR EVERYTHING:
我发现有用的干净方式
此命令仅用一次提交即可恢复最后 3 次提交。
也不会重写历史,因此不需要强制推送。
..
帮助创建一个范围。含义HEAD~3..
与HEAD~3..HEAD
相同Clean way which I found useful
This command reverts last 3 commits with only one commit.
Also doesn't rewrite history, so doesn't require a force push.
The
..
helps create a range. MeaningHEAD~3..
is the same asHEAD~3..HEAD
为此,您只需使用 revert 命令,指定要恢复的提交范围。
考虑到您的示例,您必须执行此操作(假设您位于分支“master”上):
或
git revert B...D
或git revert DC B
code>这将在本地创建一个新的提交,其中包含 B、C 和 D 的反向提交(这意味着它将撤消这些提交引入的更改):
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'):
or
git revert B...D
orgit revert D C B
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):
这将立即恢复所有这些内容。给出一个好的提交消息。
That will act as a revert for all of them at once. Give a good commit message.
类似于 Jakub 的答案,这可以让您轻松选择要恢复的连续提交。
Similar to Jakub's answer, this allows you to easily select consecutive commits to revert.
我很沮丧这个问题不能简单地回答。其他所有问题都与如何正确恢复和保存历史有关。这个问题说“我希望分支的头指向 A,即我希望 B、C、D 和 HEAD 消失,并且我希望 head 与 A 同义。”
阅读 Jakub 的帖子,但是公司里的某个人(可以在没有 Pull-Request 的情况下推送到我们的“测试”分支)推送了五个错误的提交,试图修复、修复和修复他五次提交前犯的错误。不仅如此,还接受了一两个拉取请求,但现在这些请求很糟糕。所以算了吧;我找到了最后一个好的提交(abc1234),然后运行了基本脚本:
我告诉在这个存储库中工作的其他五个人,他们最好记下过去几个小时的更改,并从最新的测试中擦除/重新分支。故事结束。
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."
I learned a lot reading Jakub's post, but some guy in the company (with access to push to our "testing" branch without a Pull-Request) pushed like five bad commits, trying to fix and fix and fix a mistake he made five 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:
I told the other five guys working in this repository that they better make note of their changes for the last few hours and wipe/rebranch from the latest testing. End of the story.
首先确保您的工作副本未被修改。
然后:
然后就提交。不要忘记记录恢复的原因。
First be sure that your working copy is not modified.
Then:
And then just commit. Don't forget to document what the reason is for the revert.
使用 Git
Restore
您还可以使用
restore
命令:假设您希望
HEAD
看起来与A
完全相同。确保您已提取最新的main
。然后剪出一个新的树枝。restore
命令将所有内容 (.
) 更改为--source
提交时的内容。然后,您将其提交到本地分支并将其推送到原点。然后你可以针对它打开一个 PR。这样做的好处是不会改变其他人可能基于工作的任何历史。也为后人留下了有用的历史。
文档:git 恢复
Using Git
Restore
You can also use the
restore
command:Let's say you want
HEAD
to look exactly likeA
. Make sure you've pulled the latestmain
. Then cut a new branch.The
restore
command changes everything (.
) to what it was at the--source
commit. Then you commit that to your local branch and push it to the origin. You can then open a PR against it.This has the benefit of not changing any history that others might have based work on. It also leaves a useful history for folks in the future.
Docs: git restore
这是 中提供的解决方案之一的扩展雅库布的回答。
我面临着一种情况,我需要回滚的提交有些复杂,其中几个提交是合并提交,并且我需要避免重写历史记录。我无法使用一系列 git revert 命令,因为我最终遇到了添加的还原更改之间的冲突。我最终使用了以下步骤。
首先,检查目标提交的内容,同时将 HEAD 留在分支的尖端:(
-- 确保
被解释为提交而不是文件;指当前目录。)然后,确定在回滚的提交中添加了哪些文件,因此需要删除:
添加的文件应在行开头显示“A”,并且存在 .其他应该没有区别。现在,如果需要删除任何文件,请暂存这些文件以进行删除:
最后,提交还原:
如果需要,请确保我们回到所需的状态:
不应该有任何差异。
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:
(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:
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:
Finally, commit the reversion:
If desired, make sure that we're back to the desired state:
There should be no differences.
恢复共享存储库(人们使用的并且您想要保留历史记录)上的一组提交的简单方法是将
git revert
与 gitrev-list
结合使用。后者将为您提供提交列表,前者将自行进行恢复。有两种方法可以做到这一点。如果您希望在一次提交中恢复多个提交,请使用:
这将恢复您需要的一组提交,但将所有更改保留在工作树上,之后您应该照常提交它们。
另一种选择是对每个恢复的更改进行一次提交:
例如,如果您有一个提交树,想要
将更改从 eee 恢复到 bbb,请运行
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 gitrev-list
. The latter one will provide you with a list of commits, the former will do the revert itself.There are two ways to do that. If you want the revert multiple commits in a single commit use:
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 afterward.
Another option is to have a single commit per reverted change:
For instance, if you have a commit tree like
to revert the changes from eee to bbb, run
这些都不适合我,所以我有三个提交要恢复(最后三个提交),所以我做到了:
工作得像一个魅力:)
None of those worked for me, so I had three commits to revert (the last three commits), so I did:
Worked like a charm :)
在我看来,一个非常简单和干净的方法可能是:
回到A
点大师的头到当前状态
保存
In my opinion a very easy and clean way could be:
go back to A
point master's head to the current state
save
可能不如这里的其他方法优雅,但我总是使用
get reset --hard HEAD~N
来撤消多个提交,其中N
是您想要的提交数量回去。或者,如果不确定提交的确切数量,只需多次运行
git reset --hard HEAD^
(返回一次提交),直到达到所需的状态。Probably less elegant than other approaches here, but I've always used
get reset --hard HEAD~N
to undo multiple commits, whereN
is the number of commits you want to go back.Or, if unsure of the exact number of commits, just running
git reset --hard HEAD^
(which goes back one commit) multiple times until you reach the desired state.我发现自己需要恢复大范围的提交,然后重新恢复它们以帮助团队提出明确的拉取请求,而不必强制推送目标分支(直接提交到),
因为这会恢复所有提交从
HEAD
到git log -n $count
以相反的顺序,它可以在任意数量的提交中良好且干净地工作从
$branch_target
查看在此状态从
$branch_for_pull
查看 在此状态如果意图是使用变更集创建 N 个分支,但它们都提交到同一个分支,您仍然可以将它们全部恢复到基础提交,然后仅恢复所需的恢复,因为更改集应按逻辑顺序排序(尝试说快 5 倍)
使用类似
HEAD~7..HEAD~5
的语法可能有助于描述范围以精确分割revert-revert 分支在这里,当恢复最后 7 次提交 (
git log -n 7
) 时,这是有意义的,但在一个分支中恢复 5 次提交 (git log -n 5
) >) 和 2,然后是另一个 git log HEAD~12..HEAD~10 中最上面的 2 个(12 是 7 次提交 + 5 次提交,假设新的 PR 分支基于分支“在它之前,或者 FF(未压缩)将其“之前”的分支合并到原始目标分支的结果)I found myself needing to revert a long range of commits and then re-revert them to help a team present a clear pull request without having to force-push over their target branch (which was committed directly to)
Because this reverts all of the commits from
HEAD
togit log -n $count
in reverse order, it'll work well and cleanly with any number of commitsView from
$branch_target
at this stateView from
$branch_for_pull
at this stateIf the intention was to create N branches with changesets, but they were all committed to the same branch, you can still revert all of them back to the base commit, then only revert the reverts needed as the changesets should be ordered logically (try saying that 5x fast)
Using a syntax like
HEAD~7..HEAD~5
may help with describing the ranges to precisely split the revert-revert branchesHere, it would make sense when reverting the last 7 commits (
git log -n 7
), but restoring 5 with in one branch (git log -n 5
) and 2 then the top-most 2 in anothergit log HEAD~12..HEAD~10
(12 is 7 commits + 5 commits, assuming the new PR branch is based off either the branch "before" it, or the result of a FF (non-squashed) merge the branch "before" it into the original target branch)我真的想避免硬重置,这就是我想到的。
要返回到 A(向后 4 步):
I really wanted to avoid hard resets, this is what I came up with.
To go back to A (which is 4 steps back):
如果您
那么您可以
然后当您想要推送更改时请记住使用
-f flag 因为您修改了历史记录
If you
then you can
Then when you want to push your changes remember to use the
-f
flag because you modified the history我设法用最简单的解决方案解决了这个问题
注意:
D
、C
、B
、A
不是合并提交。I managed to solve this with the simplest solution
Note:
D
,C
,B
,A
were not merge commits.我们可以通过其他方式克隆分支
,然后运行以下命令。
Other way we can clone the branch
and then run below command.
如果您想暂时恢复某个功能的提交,则可以使用以下一系列命令。
这是它的工作原理
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