阅读
git rebase
和 git merge-base
man 文档:
在使用 git checkout -b topic origin/master 创建的主题分支上工作后,远程跟踪分支 origin/master 的历史记录可能已被倒回并重建,从而导致此历史记录 形状:
o---B1 / ---o---o---B2--o---o---o---B (origin/master) \ B3 \ Derived (topic)
origin/master 曾经指向提交 B3、B2、B1,现在它指向 B,当 origin/master 位于 B3 时,您的主题分支是在它之上启动的。此模式使用 reflog origin/master 找到 B3 作为分叉点,以便主题可以在更新的 origin/master 之上重新基于:
$ fork_point=$(git merge-base --fork-point origin/master topic) $ git rebase --onto origin/master $fork_point topic
$fork_point
(如果我理解正确的话)将是提交对象B3
,因此提交B3..topic
将重新基于origin/master
分支。
Q1 为什么省略
B3
提交很有用? topic
分支的提交构建在 B3
提交之上,因此省略它意味着它的修改将在 origin/master
分支的故事中丢失。重新调整 B3
提交 和 topic
分支将带来更清晰的历史记录,不是吗?
Q2 有人可以链接/简要描述 git 工作流程中
--fork-point
选项的实际用例吗?
您是对的,
$fork_point
将是 B3
。
我相信这里的目的是省略
B3
作为“不是你的承诺”。
我认为 Git 人员在这里画的图表不太好。以下是我如何在不进行太多更改的情况下重绘和重写它(尽管我可能只是在每次提交时重新输入字母)。
您首先克隆(或以其他方式更新)一些 (
origin
) 存储库,其图表以提交 B3
结尾,然后创建一个主题分支并进行一些提交:
...--o---F---B3 <-- origin/master
\
G <-- topic
随着时间的推移,加上额外的
git fetch
-es 和 git commit
,你的提交图现在看起来像这样:
...--o---F---B3--B2--B1 <-- origin/master
\
G---H---I <-- topic
但是,突然之间,在另一个
git fetch
之后,你自己的提交图现在看起来像这样:
o---B1' <-- origin/foo
/
...o---F---B2'-o---o---o---B <-- origin/master
\
B3--G---H---I <-- topic
也就是说,Git 现在会认为提交 B3 属于您的主题分支,而事实上,您的工作从提交
G
开始。拥有名为 origin
的存储库的人实际上已经声明提交 B3
很糟糕,应该被丢弃。 (他们在 B2
上保留了一份 B2'
作为 master
,在他们的 B1
上保留了 B1'
作为 foo
。)
如果您简单地
git rebase
,您将复制原始提交B3
到新副本B3'
(同时也复制G-H-I
):
o---B1' <-- origin/foo
/
...o---F---B2'-o---o---o---B <-- origin/master
\
B3'-G'--H'--I' <-- topic
但你更喜欢:
o---B1' <-- origin/foo
/
...o---F---B2'-o---o---o---B <-- origin/master
\
G'--H'--I <-- topic
要让
git rebase
执行此操作,您必须指示 Git 定位提交 B3
。您对 origin/master
的引用日志包含所有 F
、B3
、B2
和 B1
(在至少一个引用日志条目下,包括本例中的 origin/master@{1}
),而您自己的 topic
有 F
和 B3
,但也没有 B2
和 B1
。因此 --fork-point
选择 B3
作为最新(最尖端)的共享提交,而不是 F
。
这里的关键句子/想法是上游存储库编写者打算完全放弃提交
B3
。
(你应该如何确定这一点有点神秘。如果需要变基,例如丢弃一个不应该提交的文件,
B2'
和B1'
是副本可能并不明显- 并且在 B1
中,这就是为什么 B1
也被丢弃。事实上,现在在 B2'
和 B3'
中省略了该文件,这使得它们不是补丁等效的,因此不是明显的副本。)
(请注意,您自己的
master
也仍然指向 B3
!)
我的回答没有解决原始问题,但提供了一些指导,如何在 B3 被认为是主题分支的一部分的情况下恢复并进行适当的变基(应该变基而不是删除)。
首先,我们假设错误的变基刚刚完成,我们需要从中恢复。我们可以使用以下命令来做到这一点:
$ git reset --hard ORIG_HEAD
现在我们要修复我们的分叉点变基。让我们找到坏的分叉点:
$ git merge-base --fork-point origin/master topic
<B3-full-hash>
或简称:
$ git rev-parse --short $(git merge-base --fork-point origin/master topic)
<B3-short-hash>
现在让我们找到上游分支不需要的引用日志条目:
$ git reflog show origin/master | grep <B3-short-hash>
<B3-short-hash> ... origin/master@{3}: ...
<B3-short-hash> ... origin/master@{7}: ...
...
可能有多个引用错误分叉点的引用日志条目。现在我们应该删除它们。列表中从最低到最高:
...
$ git reflog delete origin/master@{7}
$ git reflog delete origin/master@{3}
一旦完成
--fork-point
将不再能够找到坏的分叉点。我们可以再次重新运行我们的变基:
$ git rebase --fork-point origin/master
如图所示,这并不是那么简单的事情,仍然需要非常小心。因此,在历史上找到适当的基础/上游并运行
git rebase --onto=origin/master HEAD~2
可能会更容易。