我试图解决cd /some/new/folder/somewhere
git init
echo 'a on master' > a
git add a
git commit -m 'add a on master'
mkdir f
git mv a f/
git commit -m 'move a to f on master'
git checkout -b hotfix HEAD^
echo 'new content a on hotfix' > a
mkdir f
git add a
git mv a f/
git commit -m 'move a to f on hotfix with different content'
git checkout master
git merge hotfix
(git版本2.9)中的冲突 - 冲突是建议“其他”文件中的文件。分支被删除,但事实并非如此。这是一个(相当)简短的可重复配方:
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现在建议修补程序分支中的f / a 已删除,但情况显然不是这样吗?
<<<<<<< HEAD
a on master
=======
new content a on hotfix
>>>>>>> hotfix
我期待一个正常的&#39;像这样的f / a冲突:
select2
我无法理解为什么git会将冲突建议为重命名/删除冲突?
感谢您的帮助。
答案 0 :(得分:2)
很好的问题。
您的示例中有一个缺点:
文件a
由一行组成,因此编辑此行意味着“两个文件之间100%的差异”。
git
重命名检测算法永远不会检测到两个100%不同的文件作为重命名。
如果您更改示例:
a
hotfix
合并不会触发任何冲突,并导致f/a
包含修补程序修改。
希望这意味着在您的真实场景中,git说created vs deleted
的案例数量有限。
如果手动处理它们是可行的,这里可以看到你通常的三向合并:
# find the merge-base :
$ git merge-base master hotfix
3e12717099f3dc7b83b3d267e5cbd580ec8e69a1
# get the content you expect for "ours", "base" and "theirs" :
$ git show master:f/a > f/a.ours
$ git show hotfix:f/a > f/a.theirs
# here is the manual part : I don't know how to have an automatic detection
# of the correct base file :
$ git show 3e12717:a > f/a.base
# start a 3-way merge :
$ meld f/a.ours f/a.base f/a.theirs
# if you are satisfied with the content in a.base,
# and want to use it as your new a :
$ mv f/a.base f/a
$ git add f/a
答案 1 :(得分:2)
我无法理解为什么git会将冲突建议为重命名/删除冲突?
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
的工作原理。让我们先从第一个开始。
合并的目标是在两个不同的开发线上组合两组更改(通常由两个不同的人制作,但在我们的例子中,由一个人制作&#34;戴着两个不同的帽子&#34;,一次一个,就像它一样)。这些更改必须从一些常见的起点开始,Git称之为合并基础。
要执行此合并,Git必须找到合并基础。对于像这样的常规合并,合并基础是在HEAD(当前提交)和目标提交之间共享的提交。在我的特定情况下,名为hotfix
的目标解析为提交哈希b45a155...
:
$ git rev-parse hotfix
b45a15547101d836d84dbdf4758d71dc91c93353
而HEAD是2ca7d2d...
:
$ git rev-parse HEAD
2ca7d2d15d4d537edb828a7f3bfff3a2182630ec
这两个提交的合并基础是初始提交d763d32 add a on master
:
$ 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
我们实际上并不需要所有这些哈希,但有时候以具体的形式看待它们真好。关键是,我们有两组不同的变化:&#34;我们做了什么&#34;从d763d32
到2ca7d2d
,以及&#34;他们做了什么&#34;从d763d32
转到b45a155
。
我们可以通过运行git diff
:
$ git diff d763d32 2ca7d2d # using raw IDs
$ git diff hotfix...master # using the special "..." syntax
那是&#34;我们做了什么&#34;。我马上就会展示它。
接下来,我们(或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
找到两个指定提交的合并基础(hotfix
的提示和master
的提示),然后将该合并基础与第二次提交,即主人的提示。它检测到重命名:原始文件a
与新文件f/a
完全相同。因此,我们有一个重命名。
第二个差异虽然......啊,但是有一个问题:
$ 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
合并基础中a
的旧内容与提示提交中f/a
的新内容差别太大。 Git不仅 没有找到重命名,它永远不会找到重命名:文件太不同了。它丝毫不像原版。
实际上,在重命名文件时,它们往往会保留很多甚至所有原始内容,就像在merge-base-to-master-tip更改中一样。但是,如果他们没有保留足够的&#34;但是,Git 不会检测到重命名。
幸运的是,自2012年1月我写this answer以来,Git已经存在了很长时间。它今天仍然适用 - 您可以使用-X rename-threshold=number
在合并期间调整重命名检测阈值水平并且几乎每个人都有比1.7.4更新的Git。但是,它的缺点仍然适用。您可能还想阅读我在2016年4月撰写的this other answer。
如果您有许多文件并需要自动重命名检测,则可能需要花哨。如果您只有一个文件,则可以手动合并文件,自行执行&#34;重命名检测&#34;,使用git merge-file
生成合并结果。只需将三个修订版(base,HEAD和其他版本)提取到临时文件中,然后使用git merge-file
合并这三个版本以生成所需的结果。用正确的版本替换Git有点蹩脚的版本,并git add
它,你就可以了。