Git:手动合并未检测到的重命名中的更改

时间:2017-03-30 18:09:01

标签: git merge git-merge file-rename

此问题使用Git 2.7.0.windows.1,因此某些git命令可能已过时。

如果我的git merge命令没有检测到重命名的文件,我怎么能告诉git手动合并两个应该是单个文件的文件中的更改,而不启动合并并使用较低的重命名阈值?

复制步骤:

git init
echo "//Hello world!" > hw.h
git add . && git commit -m "Initial commit"
git checkout -b someBranch
mv hw.h hw.hpp
echo "//Foobar" > hw.hpp
git add . && git commit -m "Change hw to HPP & change content"
git checkout master
echo "//Boofar" > hw.h
git add . && git commit -m "Change content of hw"
git merge -X rename-threshold=100% someBranch

您将遇到合并冲突,但没有冲突的块。也就是说,你应该得到的唯一冲突/错误是:

CONFLICT (modify/delete): hw.h deleted in branchB and modified in HEAD. Version HEAD of hw.h left in tree.
Automatic merge failed; fix conflicts and then commit the result.

git status --porcelain将显示:

UD hw.h
A  hw.hpp

通常,在合并时,将检测重命名的阈值设置得足够低以检测重命名是理想的。例如Some people recommend 5%。在我的情况下,我正在进行大规模合并(> 1000个文件和大约9m LOC),我不得不将重命名阈值提高到足以避免任何误报&#34 ;;几乎百分之一低,我得到了大量错误检测到的重命名(我知道,重复的代码很糟糕)。在我最后使用的价值中,我只获得了一小部分错过的重命名,这似乎是一个更好的选择。

TL;降低重命名阈值的DR不适合我;如果不开始合并,我怎么能告诉git将hw.hhw.hpp视为单个文件(有冲突),而不是如上所示的两个文件?

1 个答案:

答案 0 :(得分:3)

这方面的工具有点笨拙,但他们 那里。

您需要确保合并本身在提交之前停止。在你的情况下,这会自动发生。对于棘手的合并,Git认为它正确地执行但不是,你会添加--no-commit,但这会影响接下来的几个步骤。我们暂时忽略这个问题。

接下来,您需要获取相关文件的所有三个版本。由于Git因冲突而停止,我们处于良好状态:这三个版本都可以通过索引访问。请记住,我们关注的三个版本是合并基础--ours--theirs

如果Git 正确检测到重命名,则所有三个版本都将位于名称下的索引中。既然没有,它们就不是:我们需要两个名字。 (使用“Git认为它正确合并”的情况下,文件的合并基本版本根本不在索引中,我们必须以其他方式检索它。)在这种情况下,这两个名称是{{ 1}}和hw.h,现在我们这样做:

hw.hpp

(重命名不是绝对必要的,只是为了帮助保持一切,并且很好地说明。)

现在我们要将一个文件与$ git show :1:hw.h > hw.h.base # extract base version $ git show :2:hw.h > hw.h # extract ours $ mv hw.hpp hw.h.theirs # move theirs into place 合并:

git merge-file

这使用您配置的$ git merge-file hw.h hw.h.base hw.h.theirs ,以便合并文件中的内容看起来正如您所期望的那样,但冲突行上的标签略有不同。我设置了merge.conflictStyle,所以我得到了:

diff3

您现在可以照常解决此问题,$ cat hw.h <<<<<<< hw.h //Boofar ||||||| hw.h.base //Hello world! ======= //Foobar >>>>>>> hw.h.theirs 额外rm.base个文件,.theirs最终结果git add和{{1} }}。 (这取决于你何时到git rm --cached hw.hpp:在提交之前的任何时间点这样做是安全的,但是一旦完成,你就不能再从索引获得“他们的”了;参见下文)。

请注意,git commitgit rm --cached hw.hpp也可以使用“我们的”和“他们的”版本。要获得没有索引的基本版本,我们必须运行git show HEAD:path来查找其哈希ID(然后假设有一个合并基础以及 1 ),并且{{1 }}。如果Git认为它已正确完成合并,那么我们必须这样做。

另请注意,如果您真的想要 - 我想这只有在您想要使用其他工具时才会出现这种情况,这需要它 - 您可以使用git show MERGE_HEAD:path来改变周围的条目在索引中,将git merge-base HEAD MERGE_HEAD移动到git show <hash>:path的第3行,以便 显示为“他们的”,并在git update-index中以这种方式显示。对于这个特例:

hw.hpp

哈希来自hw.h,是git status的哈希值。 (您需要第二步删除 $ printf '100644 bbda177a6ecfe285153467ff8fd332de5ecfb2f8 3\thw.h' | git update-index --index-info 索引条目。)

1 使用git ls-files --stage查找所有合并基础。如果有多个,您可以任意选择一个(这是hw.hpp所做的),或者尝试将所有合并基础合并到虚拟合并库中。要合并两个合并库,您会找到他们自己的合并库,并使用该合并库合并两个库,就像它们是分支提示一样。根据需要进行递归和迭代 - 这是Git对默认hw.hpp策略所做的事情 - 直到您拥有该文件的单个合并基本版本。