假设我有三个提交的主分支
A--B----C
我创建了新的分支dev
并在那里创建了3个提交
A--B----C------C1------C2
\
E---F----G
现在我像这样与大师合并
git merge dev --squash
现在我认为新图表是这样但不确定
A--B----C------C1------C2--------M----
\ /
E---F----G-----------/
之后我需要将dev
分支与master合并以获得c1和c2
所以它会像这样
A--B----C------C1------C2--------M----
\ / \
E---F----G-----------/----m2 ---H--I
dev
和master
将并行
我想知道
我想知道如果我使用rebase而不是合并两个分支应该并行的话,图表是什么
如果对第一个问题的答案是肯定的,那么如果我使用rebase命令,个别历史记录提交将会消失
答案 0 :(得分:4)
让我用一些标签重新绘制第二张图:
A--B--C-----C1----C2 <-- master
\
E--F----G <-- dev
(我假设C1
和C2
仅代表master
E
,F
和G
上发生的一些提交被添加到dev
)。
如果你现在在master
并运行git merge dev --squash
(然后提交结果:--squash
禁止最后提交步骤),你得到 this ,这与你画的非常不同:
A--B--C-----C1----C2---M <-- master
\
E--F----G <-- dev
如果您在没有 git merge
的情况下运行--squash
,您将获得所绘制的内容。 --squash
标志将引用文档:
生成工作树和索引状态,就像真正合并一样 发生了(合并信息除外),但实际上并没有 提交或移动HEAD,或记录$ GIT_DIR / MERGE_HEAD 到 导致下一个git commit命令创建合并提交。
换句话说,壁球合并根本不是合并(这就是为什么我有,呃,&#34;问题&#34;有一些git&#39的术语:-) ...一些结账结帐,有些不是;有些合并是合并,有些则不合并;分支只是分支提示,除非它们不是,等等!)。 A&#34;壁球合并&#34;确实使用底层合并代码来确定下一个提交的内容应该是什么,但是如果你进行实际提交(上面标有M
的那个),它只是一个普通的非合并提交,完成工作 - 提交的更改E
到G
,并将这些更改复制到C2
中的最新版本。把它想象为&#34;有人向你发送差异,将版本C
与版本G
进行比较,然后手动将所有这些更改插入版本C2
并提交(应该可能被称为C3
,但我们称之为M
而不是#34;。
如果您想要真正的合并,请在不使用 --squash
的情况下进行合并。这实际上会给你你画的东西,我将在这里重新绘制:
A--B--C-----C1----C2---M <-- master
\ /
E--F----G----/ <-- dev
(标签dev
仍然指向提交G
。)
接下来,如果您想要了解C1
和C2
中的更改并将其放在dev
上,您可以执行以下两项操作之一:
git checkout dev && git merge master
或:
git checkout dev && git merge --no-ff master
这里的区别在于您是否获得了您提交的提交m2
。如果没有--no-ff
,git会观察到dev
和master
的合并会产生提交M
,只会将dev
标签移到指向M
的位置:
A--B--C-----C1----C2---M <-- dev, master
\ /
E--F----G----/
但是使用--no-ff
,合并必须产生一个新的,不同的合并提交(同一个树,但是不同的提交,但是具有不同的父年龄):
A--B--C-----C1----C2---M <-- master
\ X
E--F----G------m2 <-- dev
此处合并M
将C2
作为其第一个父级,G
作为其第二个父级。这表明它是G2
(&#34;非第一个&#34;父级)合并到master
(第一个父级)。另一方面,合并m2
作为第一个父项G
,第二个父项C2
。
换句话说,树(工作目录)是相同的,父项的 set (list-ignoring-order)是相同的,甚至提交消息和时间戳也可以是相同的:但是父母的顺序是不同的,仅此就足以保证这些将是不同的提交。
你问题的标题是关于变基,而不是合并。 Rebase完全是一个不同的操作。让我们说你在分行dev
上运行git rebase master
。
要做一个rebase,git的作用是什么(实质上 - 应用了一些优化,以及一些带有merge提交的极端情况),简单地说是&#34; cherry pick&#34;每个分支都会提交,并应用那些&#34;樱桃&#34;到其他一些分支。让我们回到第一张图:
A--B--C--C1--C2 <-- master
\
E--F--G <-- dev
这里的想法是&#34;选择&#34;提交E
,然后F
,然后G
并按顺序应用它们。让我们首先将E
复制到E'
,应用于C2
之上:
E'
/
A--B--C--C1--C2 <-- master
\
E--F--G <-- dev
接下来,我们将F
复制到F'
,如下所示:
E'--F'
/
A--B--C--C1--C2 <-- master
\
E--F--G <-- dev
还有一个承诺要复制G
到G'
:
E'--F'--G'
/
A--B--C--C1--C2 <-- master
\
E--F--G <-- dev
除了一件事之外我们都完成了:我们需要一个新的提交链标签。我们称之为dev
!糟糕,等等,我们已经有了一个标签dev
。好吧,让我们剥离旧的dev
标签,然后将其粘贴到新的G'
上:
E'--F'--G' <-- dev
/
A--B--C--C1--C2 <-- master
\
E--F--G
Voila,git rebase
完成了。这就是它的作用:将旧提交复制到新提交。
旧的会发生什么?嗯,他们是我所说的&#34;被遗弃的&#34;。它们仍在您的存储库中,但它们的唯一标签仅存储在&#34; reflogs&#34;中。 reflog条目将使它们保持30天左右的默认到期超时。 (它是可配置的,实际上有两种不同的超时,但最好将其视为&#34;一个月左右的时间&#34;。)因此,在大约一个月内,git将垃圾收集废弃的提交。
有时候,重新定位是错误的,因为它为共享你的git repo的其他人创造了更多的工作(直接,或者更有可能,在你推动工作的某个中央存储库中)。假设其他人提交了E
到G
,他们做了更多工作,并在H
之上提交了G
。然后,您将E
复制到G
,然后放弃原来的E
到G
。您现在已经他们的工作,提交H
,需要更多工作:例如,他们可能需要在您的H
上重新定位G'
如果没有其他人通过E
进行您的提交G
,您可以确定您没有为不存在的其他人做任何额外的工作。一个rebase只会影响你,所以大多数&#34;坏&#34;走了。
最后,对于rebase vs merge(以及是否合并,是否以及何时使用--no-ff
)vs&#34;压缩合并并放弃原始开发链&#34; vs any-else-you-can-up-up-with。在git中进行提交,并使用合并,挑选和重新定位等操作它们的整个想法是让它更容易维护工作。所有摆弄提交图表都是毫无意义的,除非它使维护实际工作变得更容易。
答案 1 :(得分:0)
我会尽力解释......
当您在dev:
之上重新设置master时A--B----C-----E---F----G-------------C1------C2
\ /
E---F----G-----------/
之后你可以从master
拉到dev而不会发生冲突
当你在master上重新开发dev时:
A--B----C------C1------C2----
\
----C1------C2------E---F----G-----------
之后你可以从dev
拉到没有冲突的主人
当您重新定位dev
分支而不是更改master
时,确定第二个变体要好得多......