Git rebase - 在分叉点模式下选择提交

14

阅读 git rebasegit merge-base 的手册文档:

在使用命令 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时创建的。此模式使用origin/master的引用日志来找到B3作为分叉点,以便可以通过以下方式将主题分支重新设置在更新的origin/master之上:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic
如果我理解正确,将是提交对象B3,因此提交B3..topic将被变基到origin/master分支上。

Q1为什么省略B3提交是有用的?topic分支的提交建立在B3提交之上,因此省略它意味着在origin/master分支的历史记录中将缺少其修改。重新设置B3提交和topic分支会导致更清晰的历史记录,是吗?

Q2能否提供链接或简要描述git工作流程中--fork-point选项的实际用例?


在 Git 2.24(2019 年第四季度)中,您还可以考虑使用 git rebase --keep-base <upstream> - VonC
1个回答

17

你是正确的,$fork_point将会是B3

相信这里的意图是将B3省略为"不是你的提交"。

我认为Git团队绘制的图表不太好。以下是我如何重绘和改写它,而不会改变它太多(尽管我可能只是重新标记每个提交)。


你从克隆(或以其他方式更新到)以提交B3结束的某个(origin)存储库开始,创建一个主题分支并进行一些提交:

...--o---F---B3    <-- origin/master
              \
               G   <-- topic

随着额外的git fetchgit 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 是无用的,应该被丢弃(他们在他们的 master 上保留了复制品 B2',以及在他们的 foo 上保留了一个 B1')。

如果你只是简单地 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的引用日志中包含FB3B2B1(至少有一个引用日志条目包含在此案例中的origin/master@{1}),而你自己的topic也包含FB3,但是不包括B2B1。因此--fork-point选择B3作为最新的(顶部)共享提交记录,而不是F
这里的关键句/思想是上游仓库的编写者“打算完全放弃提交记录B3”。
(你如何知道这一点是个谜。如果需要进行变基以丢弃本不应该提交的文件 - 并且该文件在B1中出现,这就是为什么也要放弃B1,则可能不明显B2'B1'是复件。现在省略了这个文件使它们不是补丁等价的,因此不明显地是复件。)
(请注意,你自己的master也仍然指向B3!)

如果上游分支没有更改历史记录,是否存在使用 --fork-point 的用户案例?我看到的每个示例都描述了在您使用主题分支工作时上游修改了历史记录(例如rebase、amend或filter-branch)的情况。 - Mikko Rantalainen
1
@MikkoRantalainen:我认为不会:如果上游总是在不丢弃以前的提交的情况下增长,fork-point 就不应该做任何事情。尽管如此,在这种情况下它也不应该破坏任何东西。 - torek
如果我没错的话,这只能发生在有人强制推送主分支到远程仓库,导致图形突然像答案第三个图那样改变的情况下,对吗? - Number945
2
@BreakingBenjamin:是的,很明显--fork-point旨在解决上游强制推送的问题。我不认为这是一个很好的实现,但在这里没有单一正确的行为。Mercurial的Evolve扩展理论上是处理这个问题的更好方式(我不知道它在实践中的工作情况,因为我没有使用过),但Evolve不适合Git的实现。 - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接