git - 为什么重新调整的提交 ID 与精心挑选的 ID 不同?

标签 git rebase cherry-pick

这个问题源于一个令人讨厌的小 merge 冲突,当我不小心从跟踪分支中挑选到跟踪分支而不是重新定位它时,我陷入了这个冲突。修复它非常容易,但我仍然试图弄清楚为什么它首先会出现问题。

假设我有以下分支( tracking 基于 tracked ),其中包含一系列提交,括号中包含哈希值,箭头指向父提交。

tracked: a(123) <- b(234) <- c(345)

tracking: a(123) <- b(234) <- c(345)

假设一个提交 ID 为 d 的新提交 456 进入 tracked,则分支的状态如下:

tracked: a(123) <- b(234) <- c(345) <- d(456)

tracking: a(123) <- b(234) <- c(345)

我现在将 cherry-pick 456 转移到 tracking 上,导致以下跟踪状态:

tracking: a(123) <- b(234) <- c(345) <- d(somethingnot456)

但是,如果我只是执行 git rebase tracked ,它会是:

tracking: a(123) <- b(234) <- c(345) <- d(456)

那么为什么上面的 id 不同呢?

我看到了很多关于 rebasecherry-pick 的问题,但我还没有找到这个特定问题的答案。谢谢。

最佳答案

Rebase 和(重复的)cherry-pick 本质上是同一件事,但它们并不是 100%完全同一件事。在这种特殊情况下,关键是复制什么内容,其实什么也没有。

让我以我喜欢的表达 Git 图形片段的方式重新绘制您的示例。而不是:

tracked: a(123) <- b(234) <- c(345)

tracking: a(123) <- b(234) <- c(345)

让我们把它画成:

A(123) <- B(234) <- C(345)   <-- tracking, tracked

因为毕竟,每次提交都是唯一的:只有一份 A 副本、一份 B 副本、一份 C 副本,并且很快将成为 D 副本之一。同时,两个标签( trackingtracked )都指向提交 C ,其哈希值是 345whatever

现在,您将新的提交 D(456) 添加到 tracked (因此 tracking 仍然指向 C(345) :

A(123) <- B(234) <- C(345)          <-- tracking
                          \
                           D(456)   <-- tracked

精挑细选总是复制

git cherry-pick <commit> 的作用本质上是:

  1. 将给定提交与其父提交进行比较(因此,DC )
  2. 在当前分支 ( tracking ) 上应用相同的更改,并且
  3. 使用相同的消息但不同的 ID 进行提交。

这当然只是您以前见过的。您当前的分支 ( tracking ) 获取新的提交 D' : D 的副本,但编号不同。

Rebase 发现哪些提交需要被复制

另一方面,Rebase 的工作原理是获取当前分支 ( tracking ) 具有但 <upstream> 分支 ( tracked ) 不具有的所有提交的列表。具体来说,这些是 git rev-list 将列出的提交:

$ git rev-list tracked..tracking
$ 

没有这样的提交,这从图中很容易看出。我们甚至不需要哈希值:

A <- B <- C     <-- tracking
           \
            D   <-- tracked

tracking 开始,我们按照标记提交的箭头向左移动,但然后从 tracked 开始,我们再次按照箭头向左移动并标记提交。由于 D 会返回到 C ,因此这会取消所有内容的标记,并且我们根本不会复制任何内容。

如果我们对 tracking 进行了一次提交,但被跟踪:

A--B--C--E   <-- tracking
       \
        D    <-- tracked

然后 rebase 将复制 E ,创建一个新的(不同 ID)提交 E'E 的副本将放在 D 之后,如下所示:

A--B--C--E   <-- tracking
       \
        D    <-- tracked
         \
          E' [rebase in progress]

然后,rebase移动分支标签

一旦 git rebase 完成所有复制,它会记录它停止的位置 - 如果没有任何内容可复制,则在 D 处;在 E' ,或者甚至 F'G' 或其他任何地方(如果有were 提交复制),然后剥离旧分支标签 ( tracking ) 并将其粘贴到新点上:

A--B--C--E   [abandoned]
       \
        D    <-- tracked
         \
          E' <-- tracking

当没有 E 可供复制时,我们会得到以下内容:

A--B--C
       \
        D   <-- tracked, tracking

即,两个分支标签现在都指向提交 D ,它根本没有被复制。 (也没有理由保留图中向下的小腿,并且没有要放弃的提交 - 放弃 E 不会放弃 C ,因为 C 可以从 D 中找到。)

关于git - 为什么重新调整的提交 ID 与精心挑选的 ID 不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38446493/

相关文章:

git - 择优选择二进制 merge 与文本文件的冲突

git - Cherry 选择压扁的提交

git - 来自其他分支的文件在 master 分支中可见

Git 将一个分支 rebase 到另一个分支之上

git - 通过 rebase 保持远程存储库同步

git - 如何交互式地挑选

SVN 用户的 Git

ruby - 让 bundler 使用 http : instead of git:?

git check-ignore 输出为空但仍然被忽略

Git rebase而不是merge,正确的方法是什么?