融入历史

时间:2017-03-14 02:51:59

标签: git merge

我有这个:

a--b--c--d--e--f--g--h  <--dev branch
    \     \
     x--y--z--q--r      <--feature branch

我想使用Visual Studio Code或Visual Studio等工具在rd之间执行并排差异。

据我所知,这些工具只会给出我想要的差异以反应合并。所以我想这样做:

a--b--c--d---------M    <--dev branch
    \     \       /
     x--y--z--q--r      <--feature branch

这里'M'不是提交,它只是一个未提交的合并,它允许我轻松地执行rd之间的并排差异。我没有删除e--f--g--h,我只是暂时忽略它们。

为实现这一目标,我应该发出哪些git命令?

更新

使用torek的回答,我这样做了:

git checkout d    //here 'd' is short-hand for a commit hash
git merge --no-commit feature   //this is the 'feature' branch

这导致r的更改,仅从r更改为显示为编辑。我不明白为什么q的变化也没有出现。然后我尝试了这个:

git checkout d
git merge --no-commit q

现在q的变化显而易见,正如预期的那样。但我仍然试图得到一个差异,向我展示所有的变化:x,y,z,q,r。我不明白合并分支(我的第一个例子)如何不给我所有来自该分支的更改。这打破了分支的全部意义,是吗?

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

下面的说明描述如何进行临时合并(有或没有提交),但请参阅下面的所有部分。注意:现在已经多次编辑了问题和答案。我将试着在这里就所有不同的问题发表评论。

合并非分支提交,例如合并dr

您可以使用git checkout选择dr(如果您不提交结果,则无关紧要):

git checkout <hash-id>

这会产生一个分离的HEAD ,就像Git所说的那样:你现在不在任何分支上,或者等效地,你在一个名为HEAD的临时分支上。

接下来,让Git使用--no-commit合并另一个提交:

git merge --no-commit <hash-id>

这将像往常一样找到合并基础(由于下一部分的粗体),像往常一样执行合并操作(合并为动词),然后停止即使有没有冲突。没有--no-commit,只有在发生冲突时才会停止。合并结果现在位于索引中,如果没有冲突就可以提交,或者如果存在则要解决。 (一旦解决了任何冲突,运行git commit将提交索引。或者,当然,您可以省略--no-commit让Git自动执行此操作。在所有情况下,新提交将是您的新的,仍然分离的HEAD。它的第一个父项将是您签出的提交,其第二个父项将是您传递给git merge的ID的提交。)

要中止合并,如果你没有制作合并,请使用git merge --abort。要重新使用某个分支 - 如果你做了,那么将放弃合并提交,只需git checkout一个分支名称。 (要将HEAD移动到其他位置,在放弃合并提交时将其分离,只需git checkout该哈希ID。)

问题#1:合并基础

原始图表中rd合并基础rd的合并基础不同你的第二个(编辑前)图。这是第一张图:

a--b--c--d--e--f--g--h  <--dev branch
    \     \
     x--y--z--q--r      <--feature branch

在这里,如果我们从提交r开始并向后工作(如Git那样),我们将跟随合并提交z的父来查看提交{{1} }和y在同一时间。如果我们从提交d开始并向后工作(如Git那样),那么我们处于提交d。因此,dr的合并基础为d(这意味着Git将要求d进行实际合并)。这可能不是你想要的。

与此同时,你有一次:

--no-ff

这是不同的图,其中a--b--c--d---------M <--dev branch \ / x--y--z--q--r <--feature branch 不是合并提交。如果我们将zd向后追溯到可从两者都可到达的第一个提交,我们就会到达提交r。因此,提交b图的合并基础。

问题#2:合并并不关心关于中间提交

当我们 - 或者Git-看看这个图形子集时(我已经删除了部分&#34;来到&#34;并且显然没有贡献任何东西):

b

为了弄清楚如何进行合并,我们不妨看一下这个图形子集:

a--b--c--d
    \     \
     x--y--z--q--r

也就是说,d \ --z--q--r a-b-c都不重要。此外,x-y细分仅与相关,因为我们需要它从z-q遍历到r。这是因为每个提交已保存的 - 我们通过执行其中一个哈希ID d得到的源代码树 - 本身已完成。我们不需要 git checkoutz用于,只是因为他们的链接可以将我们带到q(这就是为什么我们可以忽略d的向后链接之一。)

让我们说,仅仅是为了说明,我们(相当辛苦地)将za-b-c-d复制到新的提交中,除非我们正在做所有这些,我们不会这样做。实际上对x-y-q-z-r进行合并,给我们这个图:

z

当我们制作这些内容时,我们使用a'-b'-c'-d' \ x'-y'-z'-q'-r' 的{​​em>相同快照和a'ab'的快照一样on(例如,通过复制原始提交中的所有文件版本)。即使b匹配z',就z他们获得的内容而言,它只是git checkout 历史< / em> linkage不会将其显示为合并。 1

现在如果我们要求Git合并z'd',那么合并基础 - 当我们向后移动时,图表首先加入的点 - 提交r'。但是 - 这里是这个部分的问题 - 方式 Git合并这些是运行:

b'

然后将两个差异组合成一个应用于git diff <hash of b'> <hash of d'> # first set of changes git diff <hash of b'> <hash of r'> # second set of changes 的大变更集。

第一个diff 忽略b'中存储的源树,当然,因为那不是我们从哪里开始的 - 但它也是忽略a'中的那个,因为从c'更改为b'的所有内容仍然在 c'中,除非必须退出有意地d',在这种情况下不应该采取。

同样,第二个diff 忽略d'x'y'z'中存储的源树。它们无关紧要:q'中的

或者,当然,也许是意外r'中移除。但Git怎么会知道这个?所有Git关心的都是最后的快照。无论如何,在到达最终快照之间采取的步骤并不重要 - 不是VCS。 (基于评论,当有人创建r'r'时,听起来有点出错。有些人做出错误的合并,所以很容易相信有人r错了。)

还有其他版本控制系统实际完成每个步骤。 Mercurial,其他非常像Git,这样做,例如 2 。但这也无济于事:如果由于某种原因,z 退出 z中发现的部分或全部更改,则会告诉Mercurial <合并的意图是坚持退出,因此Mercurial的合并源代码树与Git的匹配。

1 事实上,r'q'的结果。壁球&#34;合并&#34;不是 a 合并(名词),而是使用Git的合并机制合并(动词)提交。如果您要完全删除合并的分支,那么这通常只是一个好主意:您将合并到drink up all the tasty goodness of the branch's milkshake,然后将分支的空壳扔到垃圾中箱中。

2 好吧,有点儿。 Mercurial只需通过提交链中的每个提交跟踪目录状态,从两个提示返回到合并库,以便恢复文件的身份。它有时必须使用多个增量来构造特定的文件版本,但Mercurial偶尔会再次存储该文件的完整副本,以避免非常长的增量链。这实际上非常类似于Git的包文件,它同样将对象存储为delta链,具有最大链深度。

悲伤的结论:没有灵丹妙药

这一切都给我们带来了一个相当悲伤的结论:如果有人在构建一系列提交时破坏了源代码树,那么就没有神奇的解决方法。破坏是在合并期间还是进行常规的非合并提交并不重要。如果Joe修复了模块z'然后Bob打破了它,那么如何鲍勃打破它并不重要,你现在有一个破碎的芒果。什么好的VCS会给你的是找到它破坏的地方的能力,但仍需要通过提交进行提交,除非......

快乐的结论:有时候一个魔术弹

如果您有自动破损测试,破坏是持久的(不会从提交到提交),您可以使用git merge --squash(或在Mercurial中,{ {1}})在大量提交中运行自动化测试。这些允许你将一个提交标记为&#34; good&#34;和一个&#34;坏&#34;。他们尝试在两者之间中途进行提交。无论是好还是坏,将问题/修复边界划分为仍有问题的一半。通过反复划分半分,换句话说 - 您的VCS可以在O(log 2 n )测试中查明一个特定的错误提交,其中 n 是好与坏之间的提交次数。

当然,设置这些有一些开销。对于少数小变更提交,可能更容易看到&#34;手工&#34;。