【问题标题】:Why do rebased commit ids differ from cherry-picked ids?为什么重新定位的提交 id 与精选的 id 不同?
【发布时间】:2016-07-18 21:46:09
【问题描述】:

这个问题源于一个令人讨厌的小合并冲突,当我不小心从我的跟踪分支中挑选出我的跟踪分支而不是重新设置它时,我自己陷入了这种冲突。修复它很容易,但我仍然想弄清楚为什么它首先会被发布。

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

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

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

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

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

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

我现在 cherry-pick 456tracking 导致以下跟踪状态:

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

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

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

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

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

【问题讨论】:

  • 好吧,提交 ID 中有一个时间戳。有趣的问题是它们是否“树相同”?

标签: git rebase cherry-pick


【解决方案1】:

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

Cherry-pick 总是复制

git cherry-pick &lt;commit&gt; 所做的本质上是:

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

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

rebase 找出哪些提交需要被复制

另一方面,Rebase 通过获取当前分支 (tracking) 拥有的所有提交的列表来工作,而 &lt;upstream&gt; 分支 (tracked) 没有。具体来说,这些是git rev-list 将列出的提交:

$ git rev-list tracked..tracking
$ 

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

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

tracking 开始,我们沿着标记提交的箭头向左工作,但从tracked 开始,我们再次按照箭头和un标记提交向左工作。由于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' 或任何 提交复制的地方,然后它会剥离旧的分支标签(tracking)并将其粘贴到新观点:

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

当没有E 可以复制时,我们会改为:

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

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

【讨论】:

    【解决方案2】:

    git rebase 将“在另一个基本提示之上重新应用提交”,而git cherry-pick 将“应用一些现有提交引入的更改”。

    换句话说,变基会将commits 直接应用到您当前的分支之上,而cherry Picking 会将提交中的更改 应用到您当前的分支(然后使一个新的提交)。


    rebase 文档甚至在概要之后明确说明了这一点:

    如果指定,git rebase 将在执行任何其他操作之前自动执行git checkout &lt;branch&gt;。否则它会保留在当前分支上。

    所以,当使用git rebase tracked 时,你基本上只是做了git checkout tracked 并快进了你的tracking 分支...

    【讨论】:

      猜你喜欢
      • 2015-05-20
      • 1970-01-01
      • 2019-03-08
      • 1970-01-01
      • 2015-08-18
      • 2021-10-27
      • 2011-10-23
      • 2017-10-21
      • 2014-10-14
      相关资源
      最近更新 更多