【发布时间】:2014-03-30 01:17:26
【问题描述】:
我的 Git 日志中有一段有点棘手的历史,我正试图完全理解它。
在解释提交顺序之前,让我粘贴相关文件的 Git 日志图像(使用 SmartGit 可视化历史):
...
显示的是与我的问题相关的 Git 历史记录,不相关的中间部分被剪掉了。
两名开发人员对相关文件进行了代码更改,开发人员 1 和开发人员 2。
开发者 1
- 签出图片中标记为“origin/staging”的分支(但在签出时也是“origin/development”)李>
- 添加一个小代码块(见下文)
- 提交并推送 - 此推送标记为“jon-dev-test-merge”,上面
- 对上一次提交的代码进行简单的空白更改,并提交/推送此空白更改,上面标记为“jon-dev-test-merge-2”
- 意识到他应该在一个单独的分支上工作,所以重新开始并检查“origing/staging”(当时,'origin/development'),进入一个名为 'jons_dev' 的新分支,他将其推送到该分支,并在 origin 上设置跟踪
- 将相同的小代码块(不包括空白更改)添加到这个新分支(蓝线)(在标记为“test-merge-sales”之前的某个时间提交)李>
- 后来,看到 Developer 2 对 'origin/development' 分支进行了更改,将 'origin/development' 合并到他的 'jons_dev' 分支
同时...
开发者 2
- 签出图片中标记为“origin/staging”的分支(但在签出时也是“origin/development”)李>
- 对另一个文件进行代码更改(与上述开发人员 1 中的代码更改完全无关) - 这是在他的本地开发机器上的工作副本中
- 将开发人员 1 对“origin/development”分支的代码更改拉到他的本地计算机并合并到一个仅限本地的分支/工作副本中;合并成功而没有冲突。请注意,我们没有看到开发人员 2 合并到他的本地分支(因为他没有将该分支推送到原点),但只有从他的本地分支合并(参见下一个项目符号点)回到原来的 '起源/发展'当时的分支。
- 在他的本地机器上从原点合并到跟踪的开发分支,并将合并推回原点 - 上面标记为“vladimir-test-merge”
到目前为止,一切都很好。
这是我的问题。
在了解上述事件顺序的过程中,我注意到了一些奇怪的事情 - 'origin/development' 分支对于有问题的文件没有任何变化需要被合并到标记为“origin/development”的合并中 - 结果证明,在这个文件中,对这个文件的相同更改,并行进行,不包括空格,因此是存在于两个文件中,因此只需要来自“jons_dev”分支的更改(这就是执行合并的方式)。
但是,我从合并中注意到了一些东西,涉及 Git 用于合并和确定冲突的方法,我无法解释。
为了以最简单的方式演示我的问题,我首先创建了屏幕截图中指示的测试分支 - 'test-merge-sales' 和 'jon-dev-测试合并'/'jon-dev-test-merge-2'。然后,我检查了分支“test-merge-sales”并在该分支中执行了两次单独的合并(取消了两个测试之间的合并)。
这两次合并的相关结果如下所示。 (附录:由于问题下方有cmets,第二个合并场景很容易解释。但是,FIRST合并场景仍然是一个问题。)
(0) 基础文件
在显示两个合并的 3 向屏幕截图之前,这里是文件相关部分的屏幕截图因为它存在于分支之前的“origin/staging”分支中分歧:
基础文件:
该图显示了在任何一个开发者进行任何更改之前,该文件对于两个开发者的外观。
还存在 cmets,它们准确地解释了每个开发人员为了达到下面案例 (1) 中所示的合并前状态所做的代码更改。
从 cmets 中可以看出,开发人员 1 在文件中的给定位置添加了一个函数 - print_customer_part_order_history,然后是第二个函数 print_sales_analysis_page。同时,另一个分支添加了一个函数 - print_customer_part_order_history - 在文件中完全相同的位置。代码是相同的,包括空格。
这是文件进入合并案例 (1) 的状态,如下所示。
(1) 从 'jon-dev-test-merge' 合并到 'test-merge-sales' 分支
注意:这个合并场景是我的主要问题。由于问题下方的 cmets,与其他合并场景(下面的#2)相关的问题已经得到解答。
此合并没有导致冲突。打开合并文件的差异查看器,以下是相关(合并)行的屏幕截图:
请注意,Git 通过包含两个相同的函数(并行添加)'print_customer_part_order_history()'来合并文件 - 没有合并冲突。 (这是开发人员 1 并行添加到两个分支的代码 sn-p。)因此,该函数在合并代码中出现了两次。
注意:'test-merge-sales' 分支在两个分支的突出显示的代码块中都有相同的空格 - 前导空格。
问题 1:为什么 Git 决定不存在合并冲突?在文件中的同一位置并行添加了两个代码块。即使代码块相同,我认为这应该是合并冲突。
(2) 从 'jon-dev-test-merge-2' 合并到 'test-merge-sales' 分支
注意:由于此问题下方的 cmets,与此合并场景相关的问题已得到解答。
要合并的代码的唯一区别是 Developer 1 将前导空格更改为制表符。 但是,在这种情况下,只有空格不同,Git 已声明存在合并冲突。
问题 2:为什么 - 只有空格不同 - Git 会决定在一种情况下没有合并冲突,而在另一种情况下, 会发生合并冲突?
上面确定了我的两个问题,关于 Git 如何处理合并和合并冲突。
谢谢!
附录 我在名为“(0) Base File”的部分中添加了一个额外的屏幕截图 - BEFORE 两个分支分歧的文件的相关文本以及描述性文本。谢谢!
【问题讨论】:
-
短版:合并有效地运行两个
git diffs:base-vs-head1,base-vs-head2。如果差异匹配(可能的行偏移更改),则合并是非冲突的。除非使用-w完成差异,否则空格更改不匹配。合并允许-X ignore-space-change和类似选项(请参阅文档)控制git diff结果。 -
至于“为什么”:git 不知道你的文件中有什么——也许空格对文件的语义很重要。 (例如,python。)
-
@torek - 谢谢。关于 diffs 匹配:我不明白为什么在上面显示的 #1 合并中,在两个分支上并行添加相同的代码块(包括空格)的差异会成功与代码块合并在合并文件中出现两次,而不是出现冲突。
-
这是“三向”合并的正常、理想行为。给定一些转换(添加 A、删除 D 或从旧到新更改),如果 same 转换发生 base->head1 和 base->head2,则复制一份。如果只发生 一个 转换,则复制一份。如果发生了两个不同的转换,则声明冲突。 (请注意,此公式有一些可用的调整,这只是默认行为。)
-
@torek - 场景(来自您的评论)
if the same transformation occurred base->head1 and base->head2, take one copy of it似乎正是问题中“Merge #1”的预合并文件中发生的情况 - 但 Git Merge 采用了 两份。我不确定我错过了什么。