假设我们有一个合并请求,将来自分支A的提交合并到分支B中,我们可以使用普通合并和挤压合并来执行合并。(所有的提交都会合并成一个提交),然后从分支A向分支B提交另一个类似的PR,为什么git仍然允许以正常的方式合并(所有的提交都会被保留)?我的意思是修改已经被合并到分支B中了,为什么在第二次合并的时候没有引起任何冲突呢?
dm7nw8vv1#
当你使用squash的时候,git不保留任何关于合并的提交的信息(可能除了注解),所以与真实的merge不同,git不知道 * 原始的 * 分支已经被合并了。这就是为什么当你处理长时间运行的分支时,不建议使用squash。在真实的合并中,已经合并的两个分支之间的共同祖先发生了变化,但是在挤压合并中,共同祖先不会移动,因此两个分支之间的后续合并将容易产生冲突,或者是在先前的挤压合并中处理的冲突,或者是新的冲突。为了用图形来解释它,假设你有这样的初学者:
* GGG blah blah (other-branch) * FFF * EEE * | DDD (main) * | CCC * | BBB |/ * AAA
字符串现在,最新的共同祖先是什么?AAA,对吗?现在,假设你做一个 * 真实的 * 合并,我们得到这样的东西:
AAA
* HHH (main) |\ | * GGG (other-branch) | * FFF | * EEE * | DDD * | CCC * | BBB |/ * AAA
型很好。最近的共同祖先是什么?
答案提示:它是GGG。在继续之前,请确保您消化它。
GGG
现在,假设你继续在 * 两个 * 分支上工作,最后得到这样的结果:
* NNN (main) * MMM * LLL | * KKK (other-branch) | * JJJ | * III * | HHH |\| | * GGG | * FFF | * EEE * | DDD * | CCC * | BBB |/ * AAA
型如果你再次合并 *,git需要考虑最后一个共同祖先之后的变化,我们已经知道是GGG,对吧?所以,git需要考虑合并:
* NNN (main) * MMM * LLL | * KKK (other-branch) | * JJJ | * III * | HHH |/ * GGG
型现在,让我们回过头来看看如果我们使用squash-merge的话会怎么样。在第一次squash-merge之后,我们会得到:
* HHH squash merge (main) | * GGG (other-branch) | * FFF | * EEE * | DDD * | CCC * | BBB |/ * AAA
型那么,现在.
* NNN (main) * MMM * LLL | * KKK (other-branch) | * JJJ | * III * | HHH squash merge | * GGG | * FFF | * EEE * | DDD * | CCC * | BBB |/ * AAA
型如果你尝试合并,git将不得不重新开始考虑来自AAA的更改,而不是GGG,因为它发生在以前.(这使得它们与git的bits * 不同 *),那么你会得到一堆冲突.(每个分支上的内容与原来的冲突会有一点不同,实际上.但它将是 * 相同的代码段 ) 加上 * 一些.只是为了好玩。所以,总而言之.现在,关于没有任何冲突:如果被合并的分支中有 * 完全 * 相同的更改,git * 不会 * 产生冲突.(没有额外的变化)然后到git,同样的东西来自两个分支,所以没关系。(就像cherry-picking)当git抱怨cherry-pick操作没有引入任何真实的 * 更改时,你需要决定怎么做(跳过它,create am empty commit).这是一个我想看看git是否不会抱怨并允许合并就像那样进行的场景。
5vf7fwbs2#
.如果我们先用squash merge进行合并(所有的提交都会被合并成一个提交),然后再提交另一个类似的PR.为什么git仍然允许以正常的方式进行合并(所有的提交都会被保留)?重申一下这个问题--首先你用squash合并一个分支,然后你用普通的合并再次合并同一个分支。在第一个PR和squash merge之后,你应该观察到第二个PR带来了一堆提交 * 但没有文件更改 *。这就是为什么没有任何冲突,因为如果状态没有变化,你就不会有冲突。它“允许”你这样做的原因是,当你合并的时候,你带来了新的提交,有时候,即使状态没有变化,这样做也是有意义的。一个常见的场景是,当你决定从开发分支中挑选一些提交时,到一个发布分支中,这样可以更快地部署它。之后,您可以将发布分支合并回开发分支,以确保它保持同步,但是由于这些变化已经在两个分支中,合并只带来了新的提交ID,而没有任何实际的变化。顺便说一句,在同一个分支的压缩合并之后再进行非压缩合并是没有意义的。(常规合并)或不合并(squash merge)。然后只选择一个。也许只有在squash merge之后对相同的分支执行常规合并才有意义,如果你已经做了squash合并,然后后悔了,然后意识到你想保持提交的粒度。注意相反的情况是不正确的;在常规合并之后故意挤压合并相同的分支是没有意义的,因为挤压合并实际上将添加零值,没有新内容,也没有要合并的现有提交。
2条答案
按热度按时间dm7nw8vv1#
当你使用squash的时候,git不保留任何关于合并的提交的信息(可能除了注解),所以与真实的merge不同,git不知道 * 原始的 * 分支已经被合并了。这就是为什么当你处理长时间运行的分支时,不建议使用squash。
在真实的合并中,已经合并的两个分支之间的共同祖先发生了变化,但是在挤压合并中,共同祖先不会移动,因此两个分支之间的后续合并将容易产生冲突,或者是在先前的挤压合并中处理的冲突,或者是新的冲突。
为了用图形来解释它,假设你有这样的初学者:
字符串
现在,最新的共同祖先是什么?
AAA
,对吗?现在,假设你做一个 * 真实的 * 合并,我们得到这样的东西:
型
很好。最近的共同祖先是什么?
答案提示:它是
GGG
。在继续之前,请确保您消化它。现在,假设你继续在 * 两个 * 分支上工作,最后得到这样的结果:
型
如果你再次合并 *,git需要考虑最后一个共同祖先之后的变化,我们已经知道是
GGG
,对吧?所以,git需要考虑合并:型
现在,让我们回过头来看看如果我们使用squash-merge的话会怎么样。在第一次squash-merge之后,我们会得到:
型
那么,现在.
型
如果你尝试合并,git将不得不重新开始考虑来自
AAA
的更改,而不是GGG
,因为它发生在以前.(这使得它们与git的bits * 不同 *),那么你会得到一堆冲突.(每个分支上的内容与原来的冲突会有一点不同,实际上.但它将是 * 相同的代码段 ) 加上 * 一些.只是为了好玩。所以,总而言之.
现在,关于没有任何冲突:如果被合并的分支中有 * 完全 * 相同的更改,git * 不会 * 产生冲突.(没有额外的变化)然后到git,同样的东西来自两个分支,所以没关系。(就像cherry-picking)当git抱怨cherry-pick操作没有引入任何真实的 * 更改时,你需要决定怎么做(跳过它,create am empty commit).这是一个我想看看git是否不会抱怨并允许合并就像那样进行的场景。
5vf7fwbs2#
.如果我们先用squash merge进行合并(所有的提交都会被合并成一个提交),然后再提交另一个类似的PR.为什么git仍然允许以正常的方式进行合并(所有的提交都会被保留)?
重申一下这个问题--首先你用squash合并一个分支,然后你用普通的合并再次合并同一个分支。
在第一个PR和squash merge之后,你应该观察到第二个PR带来了一堆提交 * 但没有文件更改 *。这就是为什么没有任何冲突,因为如果状态没有变化,你就不会有冲突。它“允许”你这样做的原因是,当你合并的时候,你带来了新的提交,有时候,即使状态没有变化,这样做也是有意义的。一个常见的场景是,当你决定从开发分支中挑选一些提交时,到一个发布分支中,这样可以更快地部署它。之后,您可以将发布分支合并回开发分支,以确保它保持同步,但是由于这些变化已经在两个分支中,合并只带来了新的提交ID,而没有任何实际的变化。
顺便说一句,在同一个分支的压缩合并之后再进行非压缩合并是没有意义的。(常规合并)或不合并(squash merge)。然后只选择一个。也许只有在squash merge之后对相同的分支执行常规合并才有意义,如果你已经做了squash合并,然后后悔了,然后意识到你想保持提交的粒度。注意相反的情况是不正确的;在常规合并之后故意挤压合并相同的分支是没有意义的,因为挤压合并实际上将添加零值,没有新内容,也没有要合并的现有提交。