如何轻松撤消 git rebase?想到的唯一方法是手动进行:
git checkout 两个分支的提交父级
然后从那里创建一个临时分支
手工挑选所有提交
用手动创建的分支替换我重新设置的分支
在我目前的情况下,这是可行的,因为我可以轻松地发现来自两个分支的提交(一个是我的东西,另一个是我同事的东西)。然而,我的方法给我的印象是次优且容易出错(假设我刚刚用我自己的 2 个分支重新设置了基础)。
澄清:我说的是重放多个提交的rebase。不止一个。
最简单的方法是找到分支的头部提交,因为它是在 reflog 中开始变基之前立即...
git reflog
并将当前分支重置为它(通常需要注意的是在使用 --hard
选项重置之前绝对确定)。
假设 ref 日志中的旧提交是 HEAD@{2}
:
git reset --hard HEAD@{2}
在 Windows 中,您可能需要引用参考:
git reset --hard "HEAD@{2}"
您只需执行 git log HEAD@
(Windows: git log "HEAD@{2}"
git log "HEAD@{2}"
)即可检查候选老头的历史记录。
如果您没有禁用每个分支 reflogs,您应该能够简单地执行 git reflog branchname@{1}
,因为 rebase 在重新连接到最终头之前会分离分支头。我会仔细检查这一点,尽管我最近没有验证这一点。
默认情况下,所有 reflogs 都会为非裸存储库激活:
[core]
logAllRefUpdates = true
实际上,rebase 会将您的起点保存到 ORIG_HEAD
,所以这通常很简单:
git reset --hard ORIG_HEAD
但是,reset
、rebase
和 merge
都将原始 HEAD
指针保存到 ORIG_HEAD
中,因此,如果您在尝试撤消变基后执行了这些命令中的任何一个,那么您将必须使用 reflog。
ORIG_HEAD
不再有用,您还可以使用 branchName@{n}
语法,其中 n
是分支指针的第 n 个先前位置。因此,例如,如果您将 featureA
分支重新设置为 master
分支,但您不喜欢重新设置的结果,那么您可以简单地执行 git reset --hard featureA@the official Git docs for revisions
将分支重置回它之前的位置做了rebase。您可以在 the official Git docs for revisions 阅读有关 branch@{n} 语法的更多信息。
git rebase --abort
跟进它。
git rebase --abort
吗?
git status
看看它是否提到了变基。无论如何,做 git rebase --abort
应该是无害的。如果没有正在进行的变基,它只会抱怨“fatal: No rebase in progress?
”而失败。
Charles's answer 有效,但您可能希望这样做:
git rebase --abort
在 reset
之后进行清理。
否则,您可能会收到消息“Interactive rebase already started
”。
将分支重置为其旧提示的悬空提交对象当然是最好的解决方案,因为它无需花费任何努力即可恢复先前的状态。但是如果你碰巧丢失了这些提交(例如,因为你同时垃圾收集了你的存储库,或者这是一个新的克隆),你总是可以再次重新设置分支。关键是 --onto
开关。
假设您有一个名为 topic
的主题分支,当 master
的尖端是 0deadbeef
提交时,您从 master
分支。在 topic
分支上的某个时刻,您执行了 git rebase master
。现在您要撤消此操作。就是这样:
git rebase --onto 0deadbeef master topic
这将获取 topic
上所有不在 master
上的提交,并在 0deadbeef
上重放它们。
使用 --onto
,您可以将您的历史重新排列成几乎任何形状。
玩得开心。 :-)
如果您已将分支推送到远程存储库(通常是原点),然后您已成功完成变基(没有合并)(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
实际上,在执行任何重要操作之前,我在分支上放置了一个备份标记(大多数变基都是微不足道的,但如果它看起来很复杂,我会这样做)。
然后,恢复就像 git reset --hard BACKUP
一样简单。
branchName@{n}
语法,这里 n
是分支指针的第 n 个优先位置。因此,例如,如果您将 featureA
分支重新设置为 master
分支,但您不喜欢重新设置的结果,那么您只需执行 git reset --hard featureA@the official Git docs for revisions
即可将分支重置回您之前的位置做了rebase。您可以在 the official Git docs for revisions 阅读有关 branch@{n}
语法的更多信息。
git reset --hard origin/{branchName}
是重置由 rebase 完成的所有本地更改的正确解决方案。
使用 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
一旦确认这包含了我丢失的更改,我就松了一口气。
git log
git checkout -b lost_changes
rebase (finish):
后跟 3 个提交 ID。第二个ID是正确的。
rebase (finish)
。因为我想要在我 rebase 之前提交,所以我在 rebase 之前的行上使用了 2nd 提交 id(=== 在 WITH rebase 行上的 1st 提交 id命令)。然后我使用了命令 git reset --hard <commit id>
。中提琴!恢复得好像我从未执行过变基一样。提交 ID 的格式似乎是 idBeforeCommand idAfterCommand Who Date/time Command我>
对于多次提交,请记住任何提交都会引用导致该提交的所有历史记录。所以在查尔斯的回答中,将“旧提交”读作“最新的旧提交”。如果您重置为该提交,则导致该提交的所有历史记录都将重新出现。这应该做你想要的。
如果您成功地针对远程分支重新设置基础并且无法git rebase --abort
,您仍然可以采取一些技巧来保存您的工作并且没有强制推送。假设您当前错误地重新建立基础的分支称为 your-branch
并正在跟踪 origin/your-branch
git branch -m your-branch-rebased # 重命名当前分支
git checkout origin/your-branch # checkout 到源已知的最新状态
git checkout -b 你的分支
检查 git log your-branch-rebased,与 git log your-branch 进行比较,并定义 your-branch 中缺少的提交
git cherry-pick COMMIT_HASH 为 your-branch-rebased 中的每个提交
推动你的改变。请注意,两个本地分支与 remote/your-branch 相关联,您应该只推送 your-branch
如果您不想进行硬重置...
您可以从 reflog 签出提交,然后将其保存为新分支:
git reflog
在你开始变基之前找到提交。您可能需要进一步向下滚动才能找到它(按 Enter 或 PageDown)。记下 HEAD 编号并替换 57:
git checkout HEAD@{57}
查看分支/提交,如果正确,则使用此 HEAD 创建一个新分支:
git checkout -b new_branch_name
按照@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)
$
另一种不需要进行硬重置的方法是使用您想要的起点创建一个新分支。
与其他解决方案一样,您使用 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
假设我将 master 重新定位到我的功能分支,我得到了 30 个新的提交,这些提交破坏了一些东西。我发现通常最容易删除错误的提交。
git rebase -i HEAD~31
最后 31 次提交的交互式变基(如果你选择太多也没关系)。
只需获取您想要摆脱的提交并用“d”而不是“pick”标记它们。现在,提交被有效地删除,撤消了变基(如果您只删除了变基时刚刚获得的提交)。
如果您在分支上,您可以使用:
git reset --hard @{1}
不仅有 HEAD 的参考日志(由 git reflog
获得),还有每个分支的 reflogs(由 git reflog <branch>
获得)。因此,如果您在 master
上,那么 git reflog master
将列出对该分支的所有更改。您可以通过 master@
、git reflog
master@
等来引用该更改。
git reflog <branch>
git rebase
通常会多次更改 HEAD,但当前分支只会更新一次。
@{1}
只是一个 shortcut for the current branch,因此如果您在 master
上,它等于 master@shortcut for the current branch
。
如果您在交互式 rebase
中使用 git reset
,git reset --hard ORIG_HEAD
将不起作用。
尽管事实上它应该是可自动化的(至少大部分情况下),但这些答案都不是完全自动的,这让我很恼火。我创建了一组别名来尝试解决这个问题:
# 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"
您应该能够对此进行调整以允许非常轻松地返回任意数量的变基(处理 args 是最棘手的部分),如果您快速连续地执行多个变基并在此过程中搞砸了一些事情,这将很有用。
注意事项
如果任何提交消息以“rebase (start)”开头,它会感到困惑(请不要这样做)。您可以通过为您的正则表达式匹配以下内容来使正则表达式更具弹性以改善情况:
--grep-reflog "^rebase (start): checkout "
警告:未测试(正则表达式可能需要调整)
我没有这样做的原因是因为我不是 100% 的变基总是从结帐开始。谁能证实这一点?
[如果您对函数开头的 null (:
) 命令感到好奇,这是为别名设置 bash 补全的一种方式]
我通常做的是git reset #commit_hash
到我认为 rebase 无效的最后一次提交。
然后git pull
现在,您的分支应该与 master 完全匹配,并且 rebased 提交不应该在其中。
现在可以只挑选这个分支上的提交。
我用 reset 和 reflog 尝试了所有建议,但没有成功。恢复 IntelliJ 本地历史解决了丢失文件的问题
如果你在 git rebase 中搞砸了一些东西,例如 git rebase --abort
,当你有未提交的文件时,它们将会丢失并且 git reflog
将无济于事。这发生在我身上,你需要在这里跳出框框思考。如果您像我一样幸运并且使用 IntelliJ Webstorm,那么无论您在版本控制软件中犯了什么错误,您都可以 right-click->local history
并恢复到文件/文件夹的先前状态。运行另一个故障保护总是好的。
git rebase --abort
中止活动的变基,它不会撤消变基。此外,同时使用两个 VCS 是一个坏主意。它是 Jetbrains 软件中的一个不错的功能,但您不应该同时使用这两种功能。最好只学习 Git,尤其是在 Stack Overflow 上回答有关 Git 的问题时。
git log -g
获得更好的格式化输出(来自 Scott Chacon 的 progit.org/book 的提示)。git rebase -i --abort
也是必需的。仅以上是不够的。git rebase --abort
(-i
对--abort
没有意义)是为了放弃尚未完成的变基——要么是因为存在冲突,要么是因为它是交互式的,或者两者兼而有之;这与撤消成功的变基无关,这就是问题所在。您可以使用rebase --abort
或reset --hard
,具体取决于您所处的情况。您不需要同时使用这两种方法。git tag BACKUP
。如果出现问题,您可以返回它:git reset --hard BACKUP
commit:
开头,而不是rebase:
。听起来很明显,但它让我有点困惑。