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

Posted

技术标签:

【中文标题】Git rebase - 在分叉点模式下提交选择【英文标题】:Git rebase - commit select in fork-point mode 【发布时间】:2017-07-18 02:08:47 【问题描述】:

阅读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 时,您的主题分支在它之上启动。此模式使用 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 选项的实际用例吗?

【问题讨论】:

使用 Git 2.24 (Q4 2019),也可以考虑git rebase --keep-base <upstream> 【参考方案1】:

$fork_point 将是 B3 是正确的。

相信这里的意图是省略 B3 作为“不是你的提交”。

我认为 Git 人在这里绘制的图表不太好。以下是我如何重绘和重写它而不做太多更改(尽管我可能只是重新编写每个提交)。


您首先克隆(或更新到)某个 (origin) 存储库,其图形以提交 B3 结尾,然后创建一个主题分支并进行一些提交:

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

随着时间的推移,加上额外的git fetch-es 和git commits,您的提交图现在看起来像这样:

...--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 作为B2' 的副本,在他们的foo 上保留了一份B1 作为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),而您自己的topicFB3,但也没有B2B1。因此--fork-point 选择B3 作为最新(tip-most)共享提交,而不是F


这里的关键句子/想法是上游存储库编写者打算完全放弃提交B3

(你应该如何确定这一点有点神秘。如果需要变基,例如丢弃一个不应该有的文件,B2'B1' 是副本可能并不明显已提交——并且在B1 中,这就是为什么B1 也被丢弃的原因。这个文件现在在B2'B3' 中被省略的事实使得它们不是补丁等效的,因此不是明显的副本。)

(请注意,您自己的master 也仍然指向B3!)

【讨论】:

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

以上是关于Git rebase - 在分叉点模式下提交选择的主要内容,如果未能解决你的问题,请参考以下文章

Git rebase使用

git rebase的作用

git rebase

Git rebase使用

git rebase需要每个提交的签名

Git rebase命令实战