撤消git rebase

时间:2008-09-25 17:59:22

标签: git rebase git-rebase undo

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

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

  • git checkout两个分支的提交父级
  • 然后从那里创建一个临时分支
  • cherry-pick所有提交手工
  • 通过手动创建的分支
  • 替换我重新定位的分支

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

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

有什么想法吗?

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

21 个答案:

答案 0 :(得分:3778)

最简单的方法是找到分支的头部提交,就像在reflog中的rebase开始之前......

git reflog

并将当前分支重置为它(关于在使用--hard选项重置之前绝对确定的常见警告)。

假设旧的提交在参考日志中是HEAD@{5}

git reset --hard HEAD@{5}

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

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

您只需执行git log HEAD@{5} Windows: git log "HEAD@{5}")即可查看候选老头的历史记录。

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

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

[core]
    logAllRefUpdates = true

答案 1 :(得分:1334)

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

git reset --hard ORIG_HEAD

但是,resetrebasemerge都将您的原始HEAD指针保存到ORIG_HEAD中,因此,如果您已完成其中任何一项命令因为你试图撤消的rebase然后你将不得不使用reflog。

答案 2 :(得分:344)

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

git rebase --abort

reset之后进行清理。

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

答案 3 :(得分:100)

git reflog将向您显示重新设置基准之前和之后的所有更改,并允许您找到正确的重置值。但令我惊讶的是,现在还没有人提到这种超级简单的方法:

重新设置基准将旧状态保留为ORIG_HEAD,因此您可以通过运行以下命令来还原最后一个重新设置的基准:

git reset --hard ORIG_HEAD

答案 4 :(得分:81)

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

假设您有一个富有想象力的主题分支topic,当master的提示是master提交时,您分支0deadbeef。在topic分支上的某个时刻,您执行了git rebase master。现在你要撤消这个。方法如下:

git rebase --onto 0deadbeef master topic

这将使topic上的所有提交都不在master上,并在0deadbeef之上重播。

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

玩得开心。 : - )

答案 5 :(得分:66)

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

然后,恢复就像git reset --hard BACKUP一样简单。

答案 6 :(得分:59)

如果你已经将你的分支推送到远程存储库(通常是它的来源)然后你已经完成了一个成功的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

答案 7 :(得分:51)

如果您还没有完成改造,并且在其中间,以下工作:

git rebase --abort

答案 8 :(得分:17)

使用reflog对我不起作用。

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

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

签出该行上列出的第二个提交。

git checkout 88552c8f

一旦确认,这包含了我失去的变化,我分道扬and,松了一口气。

git log
git checkout -b lost_changes

答案 9 :(得分:15)

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

答案 10 :(得分:11)

根据@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)
$

答案 11 :(得分:11)

如果您成功地针对远程分支进行了重新设置而无法git rebase --abort,您仍然可以采取一些技巧来保存您的工作并且不会强制推送。 假设您错误重新定位的当前分支称为your-branch并且正在跟踪origin/your-branch

  • git branch -m your-branch-rebased#rename current branch
  • git checkout origin/your-branch #checkout到原始已知的最新状态
  • 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

答案 12 :(得分:3)

假设我将master重新绑定到我的功能分支,并且我得到了30个新的提交,这些提交会破坏某些功能。我发现通常最简单的方法就是删除糟糕的提交。

git rebase -i HEAD~31

最近31次提交的交互式rebase(如果选择的方式太多,则不会受到影响)。

简单地接受你想要删除的提交,并用“d”而不是“pick”标记它们。现在删除了提交,有效地删除了rebase(如果你只删除你在重新设置时得到的提交)。

答案 13 :(得分:3)

尽管这些答案应该是可自动化的(至少在大多数情况下是这样),但这些答案中的任何一个都不是全自动的,这使我无休止。我创建了一组别名来尝试解决此问题:

# Useful commands
#################

# Undo the last rebase
undo-rebase = "! f() { : git reset ; PREV_COMMIT=`git x-rev-before-rebase` && git reset --merge \"$PREV_COMMIT\" \"$@\";}; f"

# See what changed since the last rebase
rdiff = "!f() { : git diff ; git diff `git x-rev-before-rebase` "$@";}; f"

# Helpers
########
# Get the revision before the last rebase started
x-rev-before-rebase = !git reflog --skip=1 -1 \"`git x-start-of-rebase`\" --format=\"%gD\"

# Get the revision that started the rebase
x-start-of-rebase = reflog --grep-reflog '^rebase (start)' -1 --format="%gD"

您应该能够对此进行调整,以允许轻松地返回任意数量的rebase(处理args是最棘手的部分),如果您快速连续进行许多rebase并弄乱了某些东西,这将很有用。方式。

注意事项

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

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

警告:未经测试(正则表达式可能需要调整)

我没有执行此操作的原因是因为我并不是100%总是以 重新结帐。有人可以确认吗?

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

答案 14 :(得分:1)

如果您在分支机构,则可以使用:

git reset --hard @{1}

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

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

@{1}仅仅是shortcut for the current branch,因此如果您在master@{1}上,它就等于master

git reset --hard ORIG_HEAD在交互式git reset中使用rebase时将不起作用。

答案 15 :(得分:1)

我尝试了所有带有reset和reflog的建议,但均未成功。恢复IntelliJ的本地历史记录可以解决文件丢失的问题

答案 16 :(得分:0)

另一种不需要硬重置的方法是使用您想要的起点创建一个新分支。

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

git reflog

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

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

最后,您使用 git branch 命令。

git branch recover-branch e86a52b851e

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

答案 17 :(得分:0)

我通常要做的是 git reset #commit_hash

到我认为变基无效的最后一次提交。

然后git pull

现在,您的分支应该完全匹配master,并且不应该包含重新提交的提交。

现在,人们可以只摘取该分支上的提交。

答案 18 :(得分:0)

对于新手/太害怕进行硬重置的人,您可以从reflog中检出提交,然后将其保存为新分支。

git reflog

在开始重新设置基础之前查找提交。您可能需要进一步向下滚动才能找到它(按Enter或PageDown)。记下HEAD编号并替换57:

git checkout HEAD@{57}

查看分支/提交,如果看起来不错,请使用此HEAD创建一个新分支:

git checkout -b new_branch_name

答案 19 :(得分:0)

git reset --hard origin / {branchName}

是重置所有通过重新设置完成的本地更改的正确解决方案。

答案 20 :(得分:-3)

如果你在git rebase中搞砸了一些东西,例如git rebase --abort,虽然您有未提交的文件,但它们会丢失,git reflog无效。这发生在我身上,你需要在这里开箱即用。如果您像我一样幸运并使用IntelliJ Webstorm,那么您可以right-click->local history并且无论您使用版本控制软件犯了什么错误,都可以恢复到文件/文件夹的先前状态。另一个故障安全运行总是好的。