撤销 Git Rebase

4247
我如何轻松撤销 git rebase? 一种冗长的手动方法是:
  1. 检出两个分支的提交父级
  2. 创建和检出临时分支
  3. 手动挑选所有提交
  4. 将有问题的重建分支重置为指向临时分支
在我的当前情况下,这可以工作,因为我可以轻松地识别两个分支的提交(一个是我的东西,另一个是我的同事的东西)。然而,我的方法让我感觉不太理想并且容易出错(假设我刚刚重建了两个自己的分支)。 澄清一下:我说的是在重新播放了多个提交期间进行的rebase,而不仅仅是一个。

commit:是你想要重置到HEAD@{#}的内容。不是rebase:reset:merge:checkout: - cachius
19个回答

5864

最简单的方法是在reflog中找到重置开始前分支的最新提交...

git reflog

并将当前分支重置为它(在使用--hard选项进行重置之前,请务必确保绝对确定)。

假设旧提交记录在引用日志中为HEAD@{2}

git reset --hard HEAD@{2}

在Windows中,您可能需要引用该参考信息:
git reset --hard "HEAD@{2}"

您可以通过执行 git log HEAD@{2}Windows:git log "HEAD@{2}")来查看候选老头的历史。

如果您没有禁用分支引用日志,您应该可以简单地执行 git reflog branchname@{1},因为在重新基于之前会将分支头分离,然后再附加到最终头上。不过,请再次确认此行为,因为我最近没有验证过。

默认情况下,对于非裸仓库,所有引用日志都是激活的:

[core]
    logAllRefUpdates = true

207
Git reflog很棒,只需记住可以使用git log -g获得更好的格式化输出(来自Scott Chacon的http://progit.org/book中的提示)。 - karmi
3
以下是Allan的回答,需要执行git rebase -i --abort才行,仅以上面提到的操作不足以解决问题。 - Hazok
89
@Zach说:git rebase --abort(使用-i选项与--abort一起使用没有意义)是用于放弃未完成的变基操作,可能是因为出现了冲突,也可能是因为它是交互式的,或者两者都有;这不是关于撤消成功的变基操作,这正是问题所在。根据你的情况,你应该使用rebase --abortreset --hard,不需要同时使用两个命令。 - CB Bailey
428
以防万一,先备份一下:git tag BACKUP。如果出了问题,可以返回备份:git reset --hard BACKUP - kolypto
32
如果你提交了很多次,你正在寻找的 HEAD@{#} 将以 commit: 为前缀,而不是 rebase:。听起来很明显,但我曾经被这个搞糊涂了一会儿。 - Warpling
显示剩余3条评论

2011

实际上,rebase 会将你的起点保存到 ORIG_HEAD 中,所以这通常很简单:

Actually, rebase saves your starting point to ORIG_HEAD so this is usually as simple as:

git reset --hard ORIG_HEAD

然而,resetrebase以及merge命令都会将你的原始HEAD指针保存到ORIG_HEAD中。因此,如果自重新设置后你执行了这些命令中的任何一个,那么你必须使用引用日志才能撤销你尝试撤销的变基操作。


54
如果ORIG_HEAD不再有用,你也可以使用branchName@{n}语法,其中n是分支指针的第n个先前位置。例如,如果你将featureA分支变基到master分支,但是你不喜欢变基的结果,那么你可以简单地执行git reset --hard featureA@{1}将分支重置回在变基之前的确切位置。你可以在官方Git修订文档中了解更多关于branch@{n}语法的内容。 - user456814
21
这很简单。不过记得在之后加上 git rebase --abort - Seph
15
为什么你建议跟进使用 git rebase --abort - UpTheCreek
1
@Seph 我同意UpTheCreek的观点,因为在我的情况下它并不是必需的。也许当交互式变基过程中出现异常情况时才需要使用?我建议任何人先尝试 git status 命令,看看它是否提到了变基操作。无论如何,执行 git rebase --abort 命令应该是无害的。如果没有正在进行的变基操作,它只会抱怨 "fatal: No rebase in progress?"。 - Jacopo Tedeschi
4
能否先检查 ORIG_HEAD 的提交记录是什么? - Simon
显示剩余2条评论

530

Charles的回答有效,但您可能希望这样做:

git rebase --abort

要在reset之后进行清理。

否则,您可能会收到“Interactive rebase already started”的消息。


185
不是这个问题。这个问题询问如何撤销已完成的变基操作。 - Arunav Sanyal
6
虽然这可能不是答案,但这些信息很重要,而且答案中没有它。在使用reflogreset --hard之后,你可能仍需要中止rebase以使其恢复正常。 - mattmc3

116

将该分支重置为其旧提示的悬挂提交对象当然是最好的解决方案,因为它可以在不费力气的情况下恢复先前的状态。但是如果您不小心丢失了这些提交(例如,因为您在此期间清理了您的存储库,或者这是一个新克隆),则始终可以再次将分支变基。关键是使用--onto开关。

假设您有一个名为topic的主题分支,您在master提示为0deadbeef提交时从中分支出来。在topic分支上的某个时刻,您执行了git rebase master。现在您想撤消此操作。以下是如何完成的:

git rebase --onto 0deadbeef master topic

这将获取位于topic上但不在master上的所有提交,并将它们重放到0deadbeef之上。

使用--onto选项,您可以将自己的历史记录重新排列成几乎任何形状

玩得开心。:-)


3
我认为这是最好的选择,因为它很灵活。我从主分支上分离出分支b1,然后将分支b1合并到新分支b2上,现在想要将分支b1恢复成基于主分支的状态。我非常喜欢Git - 谢谢! - ripper234
2
这是最好的选择!它保留了我当前分支上所有的更改,并删除了所有不需要的更改! - Alicia Tang
出于某种原因,你的答案让我意识到我可以执行“git rebase -i commitish”然后编辑我不满意的提交 :) - Devin Rhode

111

如果您将分支推送到远程存储库(通常是 origin),然后成功地进行了变基(不合并)(git rebase --abort 会显示 "No rebase in progress"),则可以使用以下命令轻松重置分支

git reset --hard origin/{branchName}

例如:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean

105
git reset --hard origin/{branchName}

这是通过重新设置基准线重置所有本地更改的正确解决方案。


7
这是一个非常干净的解决方案。谢谢你的发布。在执行变基之前,我总是将代码推送到远程,这篇文章救了我的命,因为我与主分支在一个大项目中相差约3天时间。谢谢 :) - Adrian
10
如果有本地提交但未推送,这可能会导致问题。请注意。 - Onewildgamer

78

在进行任何非微不足道的操作之前,我会在分支上打上一个备份标签(大多数rebase是微不足道的操作,但如果看起来有任何复杂性,我会这样做)。

然后,恢复备份很容易,只需运行命令git reset --hard BACKUP即可。


14
实际上你甚至不需要创建备份分支,你可以直接使用branchName@{n}的语法,这里的n代表该分支指针向前第n个位置。例如,如果你将featureA分支变基到master分支上,但不喜欢变基的结果,那么你只需运行git reset --hard featureA@{1}将分支重置回进行变基之前的状态。你可以在Git修订官方文档中了解更多有关branch@{n}语法的信息。 - user456814

37
使用reflog对我没用。

对我有用的是类似于这里描述的方法。在.git/logs/refs中打开以被rebase的分支命名的文件,并找到包含"rebase finished"的行,类似于:

5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

请查看该行列出的第二个提交。
git checkout 88552c8f

一旦确认这包含了我的丢失更改,我就分支并松了一口气。
git log
git checkout -b lost_changes

2
谢谢,你救了我的命。对我来说,我有一个rebase(finish):后跟着3个提交ID。第二个ID是正确的。 - PatPatchPatrick
我的列表列出了2个提交ID,并且显示“rebase(完成)”。由于我想要在rebase之前的提交,所以我在rebase命令所在行的第一个提交ID(===与rebase命令所在行的第二个提交ID相对应)之前使用了第二个提交ID。然后我使用了命令“git reset --hard <commit id>”。哇!恢复得好像我从未执行过rebase一样。提交ID似乎是idBeforeCommand idAfterCommand Who Date/time Command的格式。 - SherylHohman

19

对于多个提交(commit),请记住任何一个提交都引用了指向该提交的所有历史记录。因此,在查尔斯(Charles)的答案中,将“旧提交”解读为“旧提交中最新的提交”。如果您重置到该提交,那么该提交之前的所有历史记录将重新出现。这应该可以实现你想要的效果。


18
如果你成功地对齐了远程分支并且不能使用 git rebase --abort,你仍然可以做一些小技巧来保存你的工作而不需要强制推送。假设被错误对齐的当前分支叫做 your-branch 并且正在跟踪 origin/your-branch:
- git branch -m your-branch-rebased # 重命名当前分支 - git checkout origin/your-branch # 切换到已知于远程的最新状态 - git checkout -b your-branch - 检查 git log your-branch-rebased,与 git log your-branch 进行比较,并定义那些从 your-branch 中缺失的提交 - 对于 your-branch-rebased 中的每个提交,执行 git cherry-pick COMMIT_HASH - 推送您的更改。请注意,两个本地分支与 remote/your-branch 相关联,您应该只推送 your-branch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接