有人知道如何轻松撤消git-rebase吗?
唯一能想到的方法就是手动操作:
- Git将提交父级签出到两个分支
- 然后从那里创建一个临时分支
- Cherry用手挑选所有承诺
- 用手动创建的分支替换我在其中重新定位的分支
在我目前的情况下,这是可行的,因为我可以很容易地发现来自两个分支的提交(一个是我的东西,另一个是我同事的东西)。
然而,我的方法让我觉得是次优和容易出错的(假设我刚刚用自己的两个分支重新调整了平衡)。
有什么想法吗?
澄清:我说的是一个REBASE,在此期间重播了大量提交。不止一个。
- 另外请注意,在一个REBASE过程中,您可以排除提交,或者挤压提交;如果没有指向原始节点的指针或通过reflog筛选,这些更改是不可恢复的,因此cherrypicking将不起作用。
最简单的方法是找到分支的头提交,因为它就在reflog中启动rebase之前……
并将当前分支重置为它(在使用--hard选项重置之前,通常需要确保绝对可靠)。
假设引用日志中的旧提交是HEAD@{5}:
1
| git reset --hard HEAD@{5} |
在Windows中,可能需要引用引用:
1
| git reset --hard"HEAD@{5}" |
你只需做一个git log HEAD@{5}(windows:git log"HEAD@{5}")就可以检查候选老人头的历史。
如果您没有禁用每个分支回流,那么您应该能够简单地执行git reflog branchname@{1},因为在重新连接到最后一个分支之前,钢筋会分离分支头。我会仔细检查这个,因为我最近还没有核实过。
默认情况下,为非裸存储库激活所有重新登录:
1 2
| [core]
logAllRefUpdates = true |
- Git reflog非常棒,请记住,使用git log -g可以获得更好的格式化输出(提示来自scott chacon的progit.org/book)。
- 根据艾伦的回答,也需要一个git rebase -i --abort。仅上述内容是不够的。
- @扎克:git rebase --abort(-i与--abort毫无关系)是指放弃一个尚未完成的钢筋网——要么是因为存在冲突,要么是因为它是交互式的,要么两者都是;这不是为了撤销一个成功的钢筋网,这就是问题所在。根据您所处的情况,您可以使用rebase --abort或reset --hard。你不应该两者兼得。
- stackoverflow.com/questions/134882/undoing-a-git-rebase/…是否更好(更灵活、更结构化,不需要您浏览旧日志)?
- @开膛手234:这取决于你想做什么。如果你只想回到你之前的状态,那么reset是最简单的。您可以执行另一个REBASE来重新创建以前的状态,但取决于REBASE的严重性,您可能需要重新解决一些冲突,以便像重新创建之前一样,小心地返回准确的树。
- 以防万一,先做一个备份:git tag BACKUP。如果出了什么问题,你可以回去:git reset --hard BACKUP。
- 如果你做了很多承诺,你要找的人的头会在前面加上commit:,而不是rebase:。听起来很明显,但我有点困惑。
- 在一次意外的重新平衡后加入聚会:d.一个git reset --hard ORIG_HEAD会不会在意外的重新平衡后立即也做这个把戏?
- git reset --hard BACKUP是相同的代码版本,但git-wise与原始版本不完全相同。例如,钢筋详情仍在回流表中。不过,也许没关系。
- 这对我不起作用。我可以恢复状态,但如果我再次运行相同的交互式REBASE,它只会给我noop,而不是我最初从上游获得的提交列表。如何恢复?
- 很好的工具,再做一次,使TAG BACKUP在HEADs之间跳跃之前可以消除移动量。
- @你在窗户上吗?我是的,吉特一直扔着error: unknown switch `e',吐出使用说明。直到我引用了那个该死的东西。
- 为了再次进行重新平衡,我需要git rerere forget [path_to_file(s)],因为Git将继续重复相同(错误)的合并决议。
- reflog很好,但是您可以通过在头上创建一个本地标记来避免需要进行reflog,然后再进行rebase。如果你的钢筋错误了,你可以很容易地找到你的旧头,而不需要重新登录,只需使用标签。我认为在重新定位之前本地标记提交是"最佳实践"。只是要确保你不会推你的标签。当然,如果您知道要使用n的值,那么使用branchname_n也是一种很好的方法。
- 建议您先复制整个Git文件夹(为了安全起见),然后在命令行或Git文件夹内的PowerShell上执行git reflog以找到要返回的头@n提交,然后执行git reset --hard"HEAD@{5}"。这使我少了几天的工作。
- fwiw osx也需要报价,即git reset --hard"HEAD@{x}"。
- 如何摆脱与refs/heads/[branch name]消息分离的头?这条信息是否意味着它不是一个"真正的"分支(如果这有意义的话)?
- 在我的Git版本中,成功的reset --hard之后的rebase --abort撤销了重置,使我在成功的重置之后回到状态。我不得不按照git rebase的建议手动删除.git/rebase-merge文件。
实际上,rebase将您的起点保存到ORIG_HEAD,因此这通常简单如下:
1
| git reset --hard ORIG_HEAD |
但是,reset、rebase和merge都将原来的HEAD指针保存到ORIG_HEAD中,因此,如果您在尝试撤消的rebase之后执行了这些命令,则必须使用reflog。
- 这正是我需要的,我犯了一个错误,就是在git-tfs中使用了rebase。这让我回到了可以承诺的地步。
- 不适合我。我借了根钢筋。git reset是否如您所建议的那样,现在git log显示一些仍然重新平衡的提交消息,并且完全缺少其他消息。
- 如果ORIG_HEAD不再有用,也可以使用branchName@{n}语法,其中n是分支指针的第n个优先位置。例如,如果您将featureA分支重新设置为master分支,但您不喜欢重新设置的结果,那么您只需执行git reset --hard featureA@{1}即可将分支重新设置为重新设置之前的位置。您可以在正式的git文档中阅读更多关于分支@n语法的内容,以便进行修订。
- 此外,这里引用了官方文件中有关REBASE的ORIG_HEAD的话:"在重置之前,orig_head被设置为指向分支的尖端。"
- 这是最简单的。不过,还是用一个git rebase --abort来跟进。
- 就支持这一点而言,我认为这可能只在一个中间阶段起作用。我对这个问题的解释是,它不一定局限于那个案件。即使是这样,我也会说江户十一〔11〕更好。
- @在完全成功的重新平衡后,Dabblick这个方法对我很有用,在git 2.17.0上没有冲突。
- 无法通过此方法执行,但是的,我知道,如果您没有在要撤消的一个钢筋网之后执行任何重置钢筋网或合并操作,也可以使用此方法。head@n方法对我有效
- 让我补充一下:git reset --hard ORIG_HEAD可以反复使用,反复回滚。比如说,如果A---Rebase to---B---Rebase to---C,现在我在C,我可以用两次git reset --hard ORIG_HEAD回到A。
- @你能解释一下为什么你建议跟进git rebase --abort吗?
查尔斯的回答很有效,但你可能想这样做:
清理后的reset。
否则,您可能会收到消息"Interactive rebase already started"。
- 这个删除了我提示中的"rebase"部分。+ 1
- 我认为应该是中的git rebase --abort(可能取决于您的Git版本)。
- 这不是问题。问题是如何撤消已完成的钢筋。
- @如果撤销失败,这是最后的希望。
git reflog将显示重新平衡前后的所有更改,并允许您找到要重置的正确更改。但我很惊讶没有人提到这另一个非常简单的方法:
rebase将旧状态保留为ORIG_HEAD,因此可以通过运行以下命令恢复最后一个rebase:
1
| git reset --hard ORIG_HEAD |
- 这只在您不在交互重新设置期间手动调用"git reset"的情况下有效!此外,如果您在重新平衡时应用修正提交,我当前版本的Git(2.20.1)似乎会修改orig头部。
将分支重置为其旧提示的悬空提交对象当然是最佳解决方案,因为它可以在不花费任何精力的情况下恢复以前的状态。但是,如果您碰巧丢失了那些提交(例如,因为您同时垃圾收集了存储库,或者这是一个新的克隆),那么您总是可以再次重新调整分支的基址。关键是--onto开关。
假设你有一个主题分支,想象中叫做topic,当master的尖端是0deadbeef的承诺时,你把master分支了。在某个时候,在topic分支上,你做了git rebase master。现在要撤消此操作。以下是如何:
1
| git rebase --onto 0deadbeef master topic |
这将接受所有不在master上的topic的承诺,并将其重放在0deadbeef之上。
使用--onto,您可以将历史重新排列成几乎任何形状。
玩得高兴。-)
- 我认为这是最好的选择,因为它的灵活性。我将b1从master分支,然后将b1重新配置为新的b2分支,然后希望b1再次基于master。我只是喜欢吉特-谢谢!
- 这是最好的选择!它保留了我当前分支上的所有更改,并删除了所有不需要的更改!
- 我想说的是,通过--onto和-i的组合,你可以将你的历史重新安排成几乎任何形式。使用Gitk(或Mac上的Gitx)查看您制作的形状:-。
实际上,在执行任何重要的操作之前,我在分支上放置了一个备份标记(大多数Rebase都是很小的,但是如果它看起来很复杂,我会这样做)。
然后,恢复和git reset --hard BACKUP一样简单。
- 我也这么做。它对Git Diff备份也很有用..请确保更改了您的目标。
- 这个基本的预防措施救了我很多次…重新平衡的错误代价高昂。
- 我以前也这样做过,但由于我对反射更为适应,我不再觉得这是必要的。反射波基本上是在你每次换头的时候代表你这样做的。
- 嗯,我更喜欢有意义的名字,因为在reflog中搜索正确的项目有时一点也不有趣。
- 实际上您甚至不需要创建备份分支,只需使用branchName@{n}语法,这里n是分支指针的第n个优先位置。例如,如果您将featureA分支重新设置为master分支,但您不喜欢重新设置的结果,那么您只需执行git reset --hard featureA@{1}即可将分支重新设置为重新设置之前的位置。您可以在正式的git文档中阅读更多关于branch@{n}语法的内容,以进行修订。
- 实际上,您甚至不需要使用上面的语法,根据pat notz的回答,分支的原始HEAD临时存储在ORIG_HEAD中。但是使用备份分支标签的技术也可以工作,这只是更多的步骤。
- 使用orig_head只在交互重新设置期间不手动调用"git reset"的情况下有效!另外,如果您在重新平衡时应用修正提交,我当前版本的Git(2.20.1)似乎会修改orig头部。
如果您已将分支推送到远程存储库(通常是源站),然后成功执行完全重新平衡(不合并)(git rebase --abort给出"没有正在进行的重新平衡"),则可以使用命令:
git reset --hard origin/{branchName}
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $ ~/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 |
- 这对我来说是正确的答案。rebase和commit在rebase之前具有相同的commit id,返回head 1不会恢复rebase!
如果您还没有完成钢筋,在钢筋中间,以下工作:
- 这对我有用。我有合并冲突,所以它有资格成为"中间人"。
使用reflog对我来说不起作用。
对我有用的东西和这里描述的相似。在.git/logs/refs中打开文件,该文件以重新设定基的分支命名,并找到包含"重新设定基finsihed"的行,如下所示:
1
| 5fce6b51 88552c8f Kris Leech < [email protected]> 1329744625 +0000 rebase finished: refs/heads/integrate onto 9e460878 |
签出该行上列出的第二个提交。
一旦证实这包含了我丢失的变化,我就支支吾吾,松了一口气。
1 2
| git log
git checkout -b lost_changes |
- 或者这样:stackoverflow.com/questions/1108853/&hellip;
- 哇——从那个链接上来说,"有一个警告:我失去了分支的历史,但在这个案例中,这并不重要。我很高兴能找回我的更改。"?
对于多个提交,请记住,任何提交都引用了该提交之前的所有历史记录。所以在查尔斯的回答中,把"旧承诺"读作"旧承诺中的最新承诺"。如果您重置为该提交,则该提交之前的所有历史记录都将重新出现。这应该是你想要的。
如果您成功地对远程分支进行了重新平衡,并且不能执行ecx1(0),那么您仍然可以使用一些技巧来保存您的工作,并且没有强制执行。假设您当前错误地重新平衡的分支称为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缺少的承诺。
- git cherry-pick COMMIT_HASH中的每一个承诺
- 推动你的改变。请注意,有两个本地分支机构与remote/your-branch关联,您只需推your-branch即可。
在@allan和@zearin的解决方案之后,我希望我可以简单地做一个评论,但是我没有足够的声誉,所以我使用了以下命令:
我不做git rebase -i --abort(注-i),只做git rebase --abort(没有-i)。
同时使用-i和--abort会导致git向我显示使用/选项列表。
因此,我以前和现在使用此解决方案的分支状态是:
1 2 3 4 5
| matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort
matbhz@myPc /my/project/environment (branch-123)
$ |
比如说,我将master重置为我的功能分支,我得到了30个新的提交,这些提交会破坏一些东西。我发现,通常消除错误的承诺是最容易的。
最后31次提交的交互式REBASE(如果您选择太多的方式,不会有任何影响)。
只需接受您想要摆脱的承诺,并用"d"而不是"pick"标记它们。现在,提交被有效地删除,从而撤消了重新平衡(如果只删除重新平衡时得到的提交)。
如果您在分支机构,可以使用:
不仅有一个head的参考日志(由git reflog获得),也有每个分支的reflogs(由git reflog 获得)。所以,如果你在master上,那么git reflog master将列出对该分支的所有更改。您可以参考master@{1}、master@{2}等所做的更改。
git rebase通常会多次更改头部,但当前分支只会更新一次。
@{1}只是当前分支的一个捷径,所以如果你在master上,它等于master@{1}。
如果您在交互的rebase期间使用git reset,git reset --hard ORIG_HEAD将不起作用。
如果您在Git-Rebase中搞乱了某些东西,例如git rebase --abort,而您有未提交的文件,这些文件将丢失,git reflog将无济于事。这件事发生在我身上,你需要跳出这个框框来思考。如果你像我一样幸运,并且使用intellij webstorm,那么无论你在版本控制软件上犯了什么错误,你都可以执行cx1(2)并恢复到文件/文件夹的以前状态。再次运行故障保护总是很好的。
- git rebase --abort中止一个活动的REBASE,它不会撤消一个REBASE。同时使用两个VCS也是个坏主意。这是JetBrains软件的一个很好的功能,但你不应该同时使用这两种功能。最好只学习git,尤其是在回答关于git的堆栈溢出问题时。