题 撤消git rebase


有谁知道如何轻松撤消git rebase?

想到的唯一方法是手动进行:

  • git checkout两个分支的提交父级
  • 然后从那里创建一个临时分支
  • 樱桃 - 手工挑选所有提交
  • 用手动创建的分支替换我重新定位的分支

在我目前的情况下,这将是有效的,因为我可以很容易地发现两个分支的提交(一个是我的东西,另一个是我的同事的东西)。

然而,我的方法让我感到不理想和容易出错(假设我刚刚用自己的2个分支进行了重新定位)。

有任何想法吗?

澄清:我正在谈论一个重播,在此期间重播了一堆提交。不仅仅是一个。


2434
2017-09-25 17:59


起源


另请注意,在rebase期间,您可以排除提交或压缩它们;如果没有指向原始节点的指针或筛选reflog,这些更改都不可恢复,因此不能使用。 - ANeves
stackoverflow.com/a/692763/2458438 理想情况下,这应该是公认的答案。 - DDM


答案:


最简单的方法是找到分支的头部提交,就像在rebase开始之前一样 引用日志...

git reflog

并将当前分支重置为它(通常需要注意的是在重置之前完全确定 --hard 选项)。

假设旧提交是 HEAD@{5} 在ref日志中:

git reset --hard HEAD@{5}

在Windows中,您可能需要引用引用:

git reset --hard "HEAD@{5}"

你可以通过做一个来检查候选老头的历史 git log HEAD@{5} (视窗:  git log "HEAD@{5}")。

如果你没有禁用每个分支reflogs,你应该能够做到 git reflog branchname@{1} 因为一个rebase在重新连接到最终头部之前分离了分支头。我会仔细检查这个,但我最近没有证实这一点。

默认情况下,为非裸存储库激活所有reflog:

[core]
    logAllRefUpdates = true

3371
2017-09-25 19:56



Git reflog很棒,只记得你可以用更好的格式化输出 git log -g (来自Scott Chacon的提示 progit.org/book)。 - karmi
@Zach: git rebase --abort (-i 毫无意义 --abort)是放弃尚未完成的基础 - 无论是因为存在冲突,还是因为它是互动的,或两者兼而有之;这不是要取消成功的变革,这就是问题所在。你会使用 rebase --abort 要么 reset --hard 取决于你所处的情况。你不应该同时做两件事。 - CB Bailey
谢谢!在我的Git安装中,我需要将“HEAD {22}”放在引号中才能工作。 - SidJ
这确实挽救了我的生命...不幸的是,在我的生活中 haste 我做到了 git reset --hard HEAD@{5}  - 这是我想要做的两件事。毋庸置疑,感谢上帝让Git允许我在做了另一个之后继续进行修正 git reflog 并且意识到我想要去的提交刚刚提升了1,我很快就纠正了这一切并且一切都很好。 =) - marcamillion
为了以防万一,请先备份: git tag BACKUP。如果出现问题,您可以返回它: git reset --hard BACKUP - kolypto


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

git reset --hard ORIG_HEAD

但是,那 resetrebase 和 merge 都保存你的原件 HEAD 指针进入 ORIG_HEAD 所以,如果你已经完成了任何这些命令,因为你要尝试撤消rebase,那么你将不得不使用reflog。


1169
2018-03-28 13:24



谢谢。这帮我恢复了壁球! - kakyo
以防万一 ORIG_HEAD 不再有用,你也可以使用 branchName@{n} 语法,在哪里 n 是分支指针的第n个先前位置。所以,例如,如果你改变 featureA 分支到你的 master分支,但你不喜欢rebase的结果,那么你可以干脆做 git reset --hard featureA@{1} 将分支重置回到您执行rebase之前的确切位置。您可以在以下位置阅读有关分支@ {n}语法的更多信息 官方Git文档进行修订。
这是最简单的。用它来跟进 git rebase --abort 虽然。 - Seph
这应该是IMO接受的答案。 - Tomáš Fejfar
ORIG_HEAD 非常非常有用,如果你只是做了一个改变,并希望以一种理智和平的方式还原它。它应该是TOP VOTED答案(或者也是接受的答案)。它需要一个凸起。最高投票回答要求做一个 git reset --hard HEAD@{5} 涉及不必要的人头计数,更有可能出错。 - DDM


查尔斯的答案有效,但你可能想这样做:

git rebase --abort

之后清理 reset

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


320
2017-07-27 18:21



这个在我的提示中删除了“| REBASE”部分。 +1 - Wouter Thielen
我认为应该是 git rebase --abort 在(取决于您的git版本)。 - Seph
那不是问题。问题是如何撤消已完成的rebase。 - Arunav Sanyal
@ArunavSanyal这是撤消失败的最后希望.. - aswzen


将分支重置为其旧提示的悬空提交对象当然是最佳解决方案,因为它可以在不花费任何精力的情况下恢复之前的状态。但是,如果您碰巧丢失了这些提交(f.ex。因为您在此期间垃圾收集了您的存储库,或者这是一个新的克隆),您可以再次重新绑定该分支。关键是这个 --onto 开关。

假设你有一个富有想象力的主题分支 topic,你分道扬.. master 当尖端 master 是的 0deadbeef 承诺。在某些时候,在 topic 分支,你做到了 git rebase master。现在你要撤消这个。就是这样:

git rebase --onto 0deadbeef master topic

这将承担所有提交 topic 没有 master 并重播它们 0deadbeef

--onto,你可以重新安排你的历史 任何形状

玩的开心。 :-)


75
2017-09-26 02:08



我认为这是最好的选择,因为它的灵活性。我将b1从master分支出来,然后将b1重新命名为新的分支b2,然后又想将b1恢复为基于master。我只是喜欢git - 谢谢! - ripper234
这是最好的选择!它保留了我当前分支上的所有更改,并删除了所有不需要的更改! - Alicia Tang
我会说这是一个组合 --onto 和 -i 你可以将历史重新排列成任何形状。使用gitk(或mac上的gitx)来查看你所做的形状:-)。 - rjmunro


在我做任何非常重要的操作之前,我实际上在分支上放了一个备份标签(大多数rebase都很简单,但如果它看起来很复杂,我会这样做)。

然后,恢复就像一样容易 git reset --hard BACKUP


62
2018-05-12 20:57



我也是这样做的。 git diff BACKUP..HEAD也很有用,可以确保你改变了你想要的东西。 - Paul Bone
我以前也这样做,但是因为我对reflog更加满意,所以我不再认为这是必要的。每次更改HEAD时,reflog基本上都是代表您执行此操作。 - Pete Hodgson
好吧,我更喜欢有意义的名字,因为在reflog中搜索正确的项目有时根本不好玩。 - Alex Gontmakher
你实际上甚至不需要制作备份分支,你可以简单地使用 branchName@{n} 语法,这里 n 是分支指针的第n个先前位置。所以,例如,如果你改变 featureA 分支到你的 master分支,但你不喜欢rebase的结果,那么你可以干脆做 git reset --hard featureA@{1} 将分支重置回到您执行rebase之前的确切位置。你可以阅读更多关于 branch@{n} 语法在 官方Git文档进行修订。
实际上,根据你甚至不需要使用上面的语法 Pat Notz的回答, 原本的 HEAD 分支暂时存储在 ORIG_HEAD。但是使用备份分支标签的技术也有效,它只是更多的步骤。


以防万一 您已将分支推送到远程存储库 (通常是它的起源)然后你做了一个成功的rebase(没有合并)(git rebase --abort 你可以很容易地给出“没有进行任何改变” 重置分支 运用 命令:

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

53
2017-09-28 12:43



这对我来说是正确的答案。在rebase之前的Rebase和commit具有相同的提交ID,并且返回HEAD {1}只是不会还原rebase! - Bill Kotsias


如果您尚未完成rebase,并且在其中间,则以下工作:

git rebase --abort

49
2017-10-15 20:20



这对我有用。我有合并冲突,所以它被认为是“在它的中间”。 - Fakeer


对于多次提交,请记住任何提交都会引用导致该提交的所有历史记录。所以在查尔斯的回答中,将“旧提交”称为“旧提交的最新内容”。如果重置为该提交,则会再次出现导致该提交的所有历史记录。这应该做你想要的。


14
2017-09-25 21:36



没错,谢谢提醒;-) - webmat


运用 reflog 对我不起作用。

对我有用的东西与描述的类似 这里。打开以重新分支的分支命名的.git / logs / refs中的文件,找到包含“rebase finsihed”的行,如:

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

14
2018-02-20 13:59



或这个: stackoverflow.com/questions/1108853/... - cregox
哇 - 从那个链接,“有一个警告:我失去了分支的历史,但在这种情况下它并不重要。我很高兴能找回我的变化。” ? - ruffin