Git和文件重命名和替换

时间:2014-09-12 07:31:48

标签: git rename

我通常没有使用git重命名的问题,但我遇到了一个非常困难的问题,我正试图解决。

出于各种原因,我的情况是文件dir1/file。由于很久以前的决定,它完全是错误的地方,需要转移到dir2/file

然而,有很多代码需要更改,出于各种原因,我们必须将文件保留在新位置和旧位置一段时间。

所以,自然(ish)方法就是这样做:

git mv dir1/file dir2/file
git commit -a

到目前为止很好:

> git diff master --name-status --find-renames
R100 dir1/file dir2/file

然后我们做

ln -s ../dir2/file dir1/file
git commit -a

但发生了这种情况

> git diff master --name-status --find-renames
A    dir2/file
T    dir1/file

如果有人在主人身上更改了dir1/file并且我试图将其拉出来,我会被告知与dir1/file1发生合并冲突并且dir2/file1保持不变。我想通过阅读git跟踪内容的其他帖子,但它似乎跟踪文件名和内容。完全忽略了内容已经移动的事实。

那么我怎么能得到git来识别我实际上已经重命名了一个文件然后添加了一个新文件,它恰好与旧文件同名?

注意:我不想做多次推送。有几个这样的文件受到影响,并且有人对其中一个并行进行更改的可能性非常高,并且无法保证他们能够执行拉动以获得重命名然后拉动以获得软链接

另外的例子。我从python模块中删除了一个函数__init__.py,它应该永远不会出现在那里,__init__.py应该是空的。这也没有被发现作为重命名。即使新文件的内容与原始__init__.py 99%相同,并且新__init__.py的内容与旧内容的0%相同。一切都很好,直到我添加一个同名的文件。

1 个答案:

答案 0 :(得分:5)

事实上,Git确实跟踪内容而不是 - 或者更确切地说,除了" -names之外,我们应该说"。差异是错误的,因为git diff(必然)尝试映射名称并比较两个单独提交的内容(或一个提交和当前工作目录,或一个提交和当前索引等,但这些只是关于主题的变体"比较两个提交")。

更具体地说, git diff比较树 1 T1 T2,默认情况下,重命名的唯一候选 T1 中存在某些文件名但中不存在的候选 {em> T2 中存在T2 和其他(不同)文件名,但 T1 中不存在。

因此,当你进行第一次提交时,你有两次提交 - 让这些A和B调用两棵树dir1/file1"失踪"来自A和dir2/file2出现在B.这是重命名检测的候选者,并且由于文件内容100%相同,git很容易发现重命名并为您提供R100差异输出。

进行第二次提交时,使用第三个树添加commit C.比较B和C工作正常:dir2/file出现在两者中,新的符号链接dir1/file仅出现在C中,并且此对的diff输出也很好。在比较A和C时会出现问题:现在dir1/file1出现在{和2}中,而dir2/file2仅出现在C中,而git diff没有意识到重命名候选人。< / p>

有一个标记--find-copies-harder - 或者您可以多次指定-C - 这(不出所料)会使复制/重命名检测代码更加困难。在这种情况下,git将考虑一个&#34;看起来不变的文件的可能性&#34; (在两个树中具有相同的名称)可能已被复制或重命名为另一个&#34;看起来是新的&#34; (存在于第二个树中,但不存在于第一个树中)。默认情况下不启用此功能,因为完全通用版本的计算量非常大。


不幸的是,没有办法控制为git merge计算差异集时使用的差异选项。 merge命令设置一些默认值(-M50%等)并执行多个差异,并且不允许您设置--find-copies-harder。因此,即使这适用于手册git diff,也无法解决您的合并冲突。

请注意,当您进行合并时, 2 git只计算两组差异:从merge-base 3 到当前HEAD,从merge-base到merged-in commit(git合并一个提交,而不是一个分支:结果合并该分支的事实,当该提交是一个分支的尖端时,是一种&#34;故意巧合&#34;)。因此, 可以将重命名设置为一个提交,将符号链接设置为第二个,但要将git merge设置为&#34;请参阅&#34;重命名,你还必须做两个单独的git merge。它并不是特别令人愉快,但为了解决这个问题,你必须让git的diff机器变得更聪明,这样它至少可以弄清楚文件类型改变会带来多大的影响。找到重命名的可能性更大,如果它更难找到副本/重命名&#34;。

(请注意,将此添加到diff机器会解决这两个问题 - git diff没有看到重命名,而git merge也没有立即看到rename-all。)


1 &#34; trees&#34;这里我指的是完整的文件树,而不是git的tree个对象。

2 更具体地说,这是双父合并的情况。八达通合并的处理方式不同。我没有挖掘章鱼合并的内脏,也不能真正说出更多关于那些的内容。

3 如果有多个合并,则merge-base依赖于要合并的两个(或更多)提交,并使用默认(recursive)策略使事情复杂化-base候选者,git计算一个&#34;虚拟合并基础&#34;,它不一定与任何实际提交相同。细节不是我在这里可以正确解释的:我知道一般的想法,但不知道git中的细节,并且在任何情况下,它很少重要,与你的问题没有直接关系。如果您想要阅读更多内容,那么here就是一个相当不错的示例,尽管该示例使用了一些类似Clearcase的术语。

相关问题