这个问题源于一个令人讨厌的小合并冲突,当我不小心从我的跟踪分支中挑选到我的跟踪分支而不是变基时,我自己陷入了这种冲突。修复它很容易,但仍然试图让我弄清楚为什么它首先是一个问题。
假设我有以下分支(tracking
基于 tracked
(,其中包含一系列括号中带有哈希的提交,以及指向父提交的箭头。
tracked: a(123) <- b(234) <- c(345)
tracking: a(123) <- b(234) <- c(345)
假设一个提交 ID 456
的新提交d
进入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 会有所不同呢?
我见过很多关于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
之一。 同时两个标签(tracking
和tracked
(都指向提交C
,其哈希值为345whatever
。
现在您将新的提交D(456)
添加到tracked
(因此tracking
仍然指向C(345)
:
A(123) <- B(234) <- C(345) <-- tracking
D(456) <-- tracked
樱桃采摘总是复制
git cherry-pick <commit>
所做的本质上是:
- 将给定的提交与其父提交进行比较(因此,
D
vsC
( - 在当前分支 (
tracking
上 (,应用相同的更改,并且 - 使用相同的消息但不同的 ID 进行新提交。
这当然只是你以前看到的。 你当前的分支(tracking
(获取新的提交D'
:D
的副本,但编号不同。
变基查找需要复制的提交
另一方面,变基的工作原理是获取当前分支(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
然后变基将复制E
,使一个新的(不同的ID(提交E'
。 E
的副本将在D
之后,如下所示:
A--B--C--E <-- tracking
D <-- tracked
E' [rebase in progress]
然后,变基移动分支标签
一旦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
,因为可以从D
中找到C
。
git rebase
将"在另一个基本提示之上重新应用提交",而git cherry-pick
将"应用一些现有提交引入的更改"。
换句话说,变基将直接在当前分支之上应用提交,而樱桃采摘会将提交中的更改应用到当前分支上(然后进行新的提交(。
变基文档甚至在概要之后明确指出了这一点:
如果指定,git rebase 将在执行任何其他操作之前执行自动
git checkout <branch>
。否则,它将保留在当前分支上。
所以,当使用git rebase tracked
时,你基本上只是做了git checkout tracked
并快进你的tracking
分支......