撤销git rebase

如何轻松撤消git rebase?一个冗长的手动方法是:

  1. 将提交父级签出到两个分支
  2. 创建并签出一个临时分支
  3. 用手挑选所有提交
  4. 重置错误的重新基于分支以指向临时分支

在我目前的情况下,这是有效的,因为我可以很容易地发现来自两个分支的提交(一个是我的东西,另一个是我同事的东西)。然而,我的方法给我的印象是次优和容易出错(假设我刚刚用我自己的两个分支重新基于)。

澄清:我说的是重放多个提交的rebase,而不仅仅是一个。

1658524 次浏览

最简单的方法是找到分支的头提交,因为它是在重排

git reflog

并将当前分支重置为它(在使用--hard选项重置之前,通常需要注意绝对确定)。

假设旧提交在ref日志中为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},因为rebase在重新连接到最终头之前分离分支头。我会仔细检查这一点,尽管我最近没有验证这一点。

默认情况下,所有重新发布都会为非裸存储库激活:

[core]logAllRefUpdates = true

对于多次提交,请记住任何提交都引用了导致该提交的所有历史记录。所以在Charles的回答中,将“旧提交”读为“旧提交中的最新提交”。如果你重置为该提交,那么导致该提交的所有历史记录都将重新出现。这应该可以做你想要的。

将分支重置为其旧提示的悬空提交对象当然是最好的解决方案,因为它无需花费任何努力即可恢复之前的状态。但是如果您碰巧丢失了这些提交(f.ex.因为您在此期间垃圾收集了存储库,或者这是一个新的克隆),您可以随时重新调整分支的基础。关键是--onto开关。

假设你有一个想象中称为topic的主题分支,当master的尖端是0deadbeef提交时,你将其分支了master。在topic分支的某个时候,你做了git rebase master。现在你想撤消它。以下是方法:

git rebase --onto 0deadbeef master topic

这将接受不在master上的topic上的所有提交,并在0deadbeef之上重播它们。

使用--onto,您可以将历史重新排列为几乎任何形状

玩得开心:-)

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

git reset --hard ORIG_HEAD

但是,resetrebasemerge都将您的原始HEAD指针保存到ORIG_HEAD中,因此,如果您在尝试撤消rebase后执行了任何这些命令,那么您将不得不使用reflg。

实际上,在执行任何重要操作之前,我在分支上放置了一个备份标记(大多数回调都是微不足道的,但如果它看起来很复杂,我会这样做)。

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

查尔斯的回答可以工作,但你可能想这样做:

git rebase --abort

reset之后清理。

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

使用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 loggit checkout -b lost_changes

按照@Allan和@Zearin的解决方案,我希望我可以简单地做一个评论,但我没有足够的声誉,所以我使用了以下命令:

而不是做git rebase -i --abort(注意-i)我必须简单地做git rebase --abort没有-i)。

同时使用-i--abort会导致Git向我显示使用/选项列表。

所以我以前和现在的分支机构与这个解决方案的状态是:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)$ git rebase --abort
matbhz@myPc /my/project/environment (branch-123)$

在情况您已将您的分支推送到远程存储库(通常是起源),然后你已经完成了一个成功的rebase(没有合并)(git rebase --abort给出了“没有正在进行的rebase”),你可以很容易地使用重置分支命令:

git重置--硬源/{分支名称}

示例:

$ ~/work/projects/{ProjectName} $ git statusOn 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 statusOn branch {branchName}Your branch is up-to-date with 'origin/{branchName}.
nothing to commit, working directory clean

如果您在git rebase中搞砸了一些事情,例如git rebase --abort,而您有未提交的文件,它们将丢失,git reflog将无济于事。这发生在我身上,您需要在这里跳出框框思考。如果您像我一样幸运并使用IntelliJ WebStorm,那么您可以right-click->local history并可以恢复到文件/文件夹的先前状态,无论您在版本控制软件方面犯了什么错误。运行另一个故障保险总是好的。

如果您成功地重新基于远程分支并且不能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中缺少的提交
  • git cherry-pick COMMIT_HASH对于your-branch-rebased中的每个提交
  • 推送您的更改。请注意,两个本地分支与remote/your-branch相关联,您应该只推送your-branch

假设我将master重新定位到我的功能分支,我得到了30个新的提交,这些提交破坏了一些东西。我发现通常最容易的方法就是删除错误的提交。

git rebase -i HEAD~31

最后31次提交的交互式rebase(如果你选择太多,它不会受到伤害)。

只需将要删除的提交标记为“d”而不是“选择”。现在,提交被删除,有效地撤消了rebase(如果您仅删除了rebase时刚刚获得的提交)。

如果你在一个分支上,你可以使用:

git reset --hard @{1}

HEAD(由git reflog获得)不仅有参考日志,每个分支(由git reflog <branch>获得)也有重新发布。因此,如果您在master上,那么git reflog master将列出该分支的所有更改。您可以通过master@{1}master@{2}等引用该更改。

git rebase通常会多次更改HEAD,但当前分支只会更新一次。

@{1}只是当前分支的快捷方式,所以如果你在master上,它等于master@{1}

如果您在交互式rebase期间使用git resetgit reset --hard ORIG_HEAD将不起作用。

git reset --hard origin/{branchName}

是通过rebase重置所有本地更改的正确解决方案。

如果你不想做硬重置

您可以从reflg中签出提交,然后将其保存为新分支:

git reflog

在开始重新定位之前找到提交。您可能需要进一步向下滚动才能找到它(按回车键或PageDown)。记下HEAD编号并替换57:

git checkout HEAD@{57}

查看分支/提交,如果正确,则使用此HEAD创建一个新分支:

git checkout -b new_branch_name

我通常做的是git reset #commit_hash

到我认为rebase没有效果的最后一次提交。

然后git pull

现在您的分支应该与master完全匹配,并且重新基于的提交不应该在其中。

现在可以在这个分支上选择提交。

我尝试了所有关于重置和重排的建议,但没有任何成功。恢复IntelliJ的本地历史记录解决了文件丢失的问题

这些答案都不是全自动的,这让我非常恼火,尽管它应该是自动化的(至少大部分是)。我创建了一组别名来尝试补救这个问题:

# Useful commands#################
# Undo the last rebaseundo-rebase = "! f() { : git reset ; PREV_COMMIT=`git x-rev-before-rebase` && git reset --merge \"$PREV_COMMIT\" \"$@\";}; f"
# See what changed since the last rebaserdiff = "!f() { : git diff ; git diff `git x-rev-before-rebase` "$@";}; f"
# Helpers######### Get the revision before the last rebase startedx-rev-before-rebase = !git reflog --skip=1 -1 \"`git x-start-of-rebase`\" --format=\"%gD\"
# Get the revision that started the rebasex-start-of-rebase = reflog --grep-reflog '^rebase (start)' -1 --format="%gD"

你应该能够调整这一点,以便很容易地返回任意数量的回调(杂耍args是最棘手的部分),如果你快速连续执行一些回调并在过程中搞砸了一些事情,这可能会很有用。

警告

如果任何提交消息以“rebase(start)”开头,它会感到困惑(请不要这样做)。您可以通过为您的正则表达式匹配类似的内容来使正则表达式更具弹性以改善这种情况:

--grep-reflog "^rebase (start): checkout "

警告:未测试(regex可能需要调整)

我没有这样做的原因是因为我不是100%认为rebase总是以结帐开始。有人能确认这一点吗?

[如果您对函数开头的null(:)命令感到好奇,这是为别名设置bash完成的一种方式]

另一种方法不需要做硬重置是创建一个具有所需起点的新分支。

与其他解决方案一样,您可以使用reflg来找到正确的起点。

git reflog

(您也可以在此处使用git log -g了解更多详细信息)

然后注意对提交SHA(例如:e86a52b851e)的引用。

最后,您使用git分支命令。

git branch recover-branch e86a52b851e

参考:https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery#_data_recovery