I can’t understand why git is suggesting the conflict as rename/delete conflict?
Git检测到了重命名.更确切地说,它检测到了一个,但没有检测到另一个.
我使用了您的示例脚本并获得了相同的结果:
CONFLICT (rename/delete): f/a deleted in hotfix and renamed in HEAD.
Version HEAD of f/a left in tree.
Automatic merge failed; fix conflicts and then commit the result.
这里有两个关键:git merge如何执行合并,以及git diff如何工作.让我们先从第一个开始.
如何合并(作为动词)
合并的目标是在两个不同的发展线上组合两组变化(通常由两个不同的人制作,但在我们的例子中,由一个人“戴两个不同的帽子”制作,一次一个,如它是).这些更改必须从一些常见的起点开始,Git称之为合并基础.
要执行此合并,Git必须找到合并基础.对于像这样的常规合并,合并基础是在HEAD(当前提交)和目标提交之间共享的提交.在我的特定情况下,名为hotfix的目标解析为提交哈希b45a155 ……:
$git rev-parse hotfix
b45a15547101d836d84dbdf4758d71dc91c93353
而HEAD是2ca7d2d ……:
$git rev-parse HEAD
2ca7d2d15d4d537edb828a7f3bfff3a2182630ec
这两个提交的合并基础是初始提交d763d32在主服务器上添加一个:
$git merge-base --all HEAD hotfix
d763d32af0cafdb0378b96b25e56fd70d63213d1
$git log --graph --decorate --oneline --all
* b45a155 (hotfix) move a to f on hotfix with different content
| * 2ca7d2d (HEAD -> master) move a to f on master
|/
* d763d32 add a on master
我们实际上并不需要所有这些哈希值,但有时候以具体形式看待它们会很高兴.关键是,我们有两组不同的变化:“我们做了什么”从d763d32到2ca7d2d,以及“他们做了什么”从d763d32到b45a155.
我们可以通过运行git diff找到第一个这样的东西:
$git diff d763d32 2ca7d2d # using raw IDs
$git diff hotfix...master # using the special "..." syntax
这就是“我们做了什么”.我马上就会展示一下.
接下来,我们(或Git)可以通过再次运行git diff找到第二个这样的东西:
$git diff d763d32 b45a155 # using raw IDs
$git diff master...hotfix # using the syntax again
git diff如何执行差异
这个解释在重要的时候会得到很长的细节和精细的细节,最终它确实非常重要.让我们把它外包给another StackOverflow answer.总之,Git会尝试检测重命名.是否可以检测到它们取决于许多细节.
但在我们的特定情况下,发生的事情是Git在从merge-base到tip-of-master时检测到重命名:
$git diff hotfix...master
diff --git a/a b/f/a
similarity index 100%
rename from a
rename to f/a
这里的三点语法告诉git diff找到两个指定提交的合并基础(修补程序的提示和master的提示),然后将第二个提交的diff合并基础,即master的提示.它检测到重命名:原始文件a与新文件f / a 100%相同.因此,我们有一个重命名.
第二个差异,虽然……啊,有一个问题:
$git diff master...hotfix
diff --git a/a b/a
deleted file mode 100644
index 81d07e3..0000000
--- a/a
+++ /dev/null
@@ -1 +0,0 @@
-a on master
diff --git a/f/a b/f/a
new file mode 100644
index 0000000..158795c
--- /dev/null
+++ b/f/a
@@ -0,0 +1 @@
+new content a on hotfix
合并基础中的旧内容与提示中的f / a的新内容有很大不同. Git不仅没有找到重命名,它永远不会找到重命名:文件太不同了.它丝毫不像原版.
重命名检测通常比这更好
实际上,当文件被重命名时,它们往往会保留很多甚至所有原始内容,就像你的merge-base-to-master-tip更改一样.但是,如果它们没有保留“足够”,Git将不会检测到重命名.
幸运的是,自从我在2012年1月写了this answer以来,Git已经存在了很长时间.它今天仍然适用 – 您可以使用-X rename-threshold = number来调整合并期间的重命名检测阈值级别 – 几乎每个人都有Git比1.7.4更新.但是,它的缺点仍然适用.您可能还想阅读我在2016年4月写的this other answer.
如果您有许多文件并需要自动重命名检测,您可能需要花哨.如果您只有一个文件,则可以手动合并文件,进行自己的“重命名检测”,使用git merge-file生成合并结果.只需将三个修订版(base,HEAD和其他版本)解压缩到临时文件中,然后使用git merge-file合并这三个版本以生成所需的结果.用正确的版本替换Git有点蹩脚的版本,git添加它,你很高兴.