我有这个:
a--b--c--d--e--f--g--h <--dev branch
\ \
x--y--z--q--r <--feature branch
我想使用Visual Studio Code或Visual Studio等工具在r
和d
之间执行并排差异。
据我所知,这些工具只会给出我想要的差异以反应合并。所以我想这样做:
a--b--c--d---------M <--dev branch
\ \ /
x--y--z--q--r <--feature branch
这里'M'不是提交,它只是一个未提交的合并,它允许我轻松地执行r
和d
之间的并排差异。我没有删除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。我不明白合并分支(我的第一个例子)如何不给我所有来自该分支的更改。这打破了分支的全部意义,是吗?
有什么想法吗?
答案 0 :(得分:0)
下面的说明描述如何进行临时合并(有或没有提交),但请参阅下面的所有部分。注意:现在已经多次编辑了问题和答案。我将试着在这里就所有不同的问题发表评论。
d
和r
您可以使用git checkout
选择d
或r
(如果您不提交结果,则无关紧要):
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。)
原始图表中r
和d
的合并基础与r
和d
的合并基础不同你的第二个(编辑前)图。这是第一张图:
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
。因此,d
和r
的合并基础为d
(这意味着Git将要求d
进行实际合并)。这可能不是你想要的。
与此同时,你有一次:
--no-ff
这是不同的图,其中a--b--c--d---------M <--dev branch
\ /
x--y--z--q--r <--feature branch
不是合并提交。如果我们将z
和d
向后追溯到可从两者都可到达的第一个提交,我们就会到达提交r
。因此,提交b
是此图的合并基础。
当我们 - 或者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 checkout
和z
用于树,只是因为他们的链接可以将我们带到q
(这就是为什么我们可以忽略d
的向后链接之一。)
让我们说,仅仅是为了说明,我们(相当辛苦地)将z
和a-b-c-d
复制到新的提交中,除非我们正在做所有这些,我们不会这样做。实际上对x-y-q-z-r
进行合并,给我们这个图:
z
当我们制作这些内容时,我们使用a'-b'-c'-d'
\
x'-y'-z'-q'-r'
的{em>相同快照和a'
,a
和b'
的快照一样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;。