>假设我有这个:
A - B - C - E - F [integration]
G - H - I [feature]
提交I
后,我们使用集成来变基:
git fetch origin
git rebase integration
所以现在我们有:
A - B - C - E - F [integration]
B - C - E - F - G - H - I [feature]
然后假设我们将功能分支合并到集成中,然后我们得到:
A - B - C - E - F - G - H - I [integration]
B - C - E - F - G - H - I [feature]
(我认为这是对的),但我看不出这与根本不变基有什么不同?
你的画误导了你。
记住这些事情:
- 提交的"真实名称"是其哈希 ID。 每个提交都存储其父提交的哈希 ID,
- 或者对于合并提交,存储其所有父提交的哈希 ID。
- 根本无法更改任何提交,但可以将提交复制到新的替换项。
如果它有帮助,请将提交视为大而坚固的东西:例如,构成建筑物的砖块和梁。 (就像超大的乐高积木一样,每块积木都有一些连接到其他积木的连接器,我们将积木拼接在一起制作链条。 这些连接是通过哈希 ID 进行的:它们来自子提交并指向父提交。
另一方面,分支名称是非常轻量级的项目。 它们就像便签,你在提交上拍打,然后剥落并在不同的提交上拍打。
所以如果你有这个:
A <-B <-C <-E <-F <-- integration
G <-H <-I <-- feature
您无法获取第二张图片,因为现有的提交G
记录A
哈希 ID。不能有F
作为父级的G
。 如果可能的话,您也不应该绘制两次提交:提交是唯一的东西,只有一个提交G
。
git cherry-pick
是复制提交的构建块
通常,我们发现自己处于这样一种情况,即我们有一个像G
这样的提交,这本来是可以的,但如果它稍微不同一点,我们会更喜欢它。 我们想要一个新的副本,它类似于G
,但F
作为其父级,并且具有与原始G
不同的源树快照。 让我们调用新的提交G'
以将其与G
区分开来,但提醒我们它很像G
。 我们希望F
和G'
之间的差异与A
和G
之间的差异相同,从而也考虑由于提交B-C-E-F
而需要的任何更改。 所以我们想要的是一个看起来像这样的提交图:
G' <-- new-and-improved-feature
/
A--B--C--E--F <-- integration
G--H--I <-- feature
如果我们将提交H
复制到一个新的和改进的H'
,并将I
复制到一个新的和改进的I'
,我们得到这个:
G'-H'-I' <-- new-and-improved-feature
/
A--B--C--E--F <-- integration
G--H--I <-- feature
git reset
移动标签
git reset
所做的——嗯,它可以做的几件事之一,但这就是我们现在用它做的——是移动分支名称粘性标签。
有一个粘性标签,我们在上面写了feature
这个词。 该粘性标签现在附在提交I
。 但是我们只用了三次git cherry-pick
,在我们新的和改进的设置中,将G-H-I
序列复制到G'-H'-I'
序列。
如果我们现在让 Git 剥离标签feature
提交I
,并将其粘贴到提交I'
上,我们得到这个:
G'-H'-I' <-- feature (HEAD), new-and-improved-feature
/
A--B--C--E--F <-- integration
G--H--I <-- ORIG_HEAD
为了实现这一点,我们运行:git checkout feature; git reset new-and-improved-feature
。
git reset
命令将此特殊名称设置为ORIG_HEAD
,以记住feature
过去去过的地方。 现在标签feature
附加到提交I'
,但是有一些方法可以找到I
,包括这个ORIG_HEAD
技巧。
(我们不再需要"新功能和改进功能"标签,因此现在可以将其删除。
请注意,没有更改任何提交。 原始G
仍在存储库中。 运行git log ORIG_HEAD
,我们仍然可以看到它,至少在我们执行另一个使用ORIG_HEAD
来记住其他提交的 Git 命令之前。 我们将看到I
,然后是H
,然后是G
。 我们还可以使用reflogforfeature
来查找提交G
、H
和I
的哈希 ID。 只要我们有哈希 ID 或哈希 ID 的名称,我们就可以找到提交。 (这些 reflog 条目最终会过期 - 它们有一个日期戳,一三个月后,Git 会删除 reflog 条目。
如果我们使用名称feature
,但是,我们会找到新副本而不是原始提交。 这使得提交看起来已经改变,只要我们不密切关注并注意到实际上这些是新的提交。
底线是这样的:在我们复制提交之后,如果我们使用git reset
放弃原始副本而支持新的和改进的副本,我们将只看到新的和改进的副本,我们可以表现得像提交已经更改一样。 提交没有改变,如果有人真的仔细观察,他们会发现我们的秘密,但如果其他人从来不知道原件,他们就无法发现这些是 廉价的仿冒 改进副本。
git rebase
= 樱桃采摘加复位
这给我们带来了结论:git rebase
从根本上git cherry-pick
一组提交,后跟git reset
。 也就是说,我们首先将提交复制到新的和(我们希望)改进的版本;然后我们使用git reset
来尝试诱骗每个人使用改进的提交来代替原始提交。
任何仍然拥有原件的人都不会被愚弄!如果其他人(其他 Git 存储库)仍然有原始提交,我们必须说服他们也切换到新的改进提交。 但是,如果我们是唯一可以访问提交的人,我们只需要欺骗自己,这可能更容易。
从本质上讲,我读到并发现的是,只有当您想删除提交历史记录时,您才会进行变基。Git 重置将更改参考文件中的引用历史记录。
我找到了两者在堆栈溢出上的区别的一个很好的解释:这里
我希望这有助于澄清哪种选择是哪种情况下工作的最佳工具。