我通常没有使用git重命名的问题,但我遇到了一个非常困难的问题,我正在尝试解决.
由于各种原因,我的情况是我们有一个文件dir1/file.由于很久以前的决定,它完全是错误的地方,需要转移到dir2/file.
但是,有很多代码需要更改,出于各种原因,我们必须将文件保留在新位置和旧位置一段时间.
所以,自然(ish)方法是这样做:
git mv dir1/file dir2/file
git commit -a
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好:
> git diff master --name-status --find-renames
R100 dir1/file dir2/file
Run Code Online (Sandbox Code Playgroud)
那么我们呢
ln -s ../dir2/file dir1/file
git commit -a
Run Code Online (Sandbox Code Playgroud)
但这发生了
> git diff master --name-status --find-renames
A dir2/file
T dir1/file
Run Code Online (Sandbox Code Playgroud)
如果任何人改变dir1/file了主人并且我试图拉它,我会被告知存在合并冲突dir1/file1并dir2/file1保持不变.我想通过阅读git跟踪内容的其他帖子,但它似乎跟踪文件名和内容.完全忽略了内容已经移动的事实.
那么我怎么能得到git来识别我实际上已经重命名了一个文件然后添加了一个新文件,它恰好与旧文件同名?
注意:我不想做多次推送.有几个这样的文件受到影响,并且有人对其中一个并行进行更改的可能性非常高,并且无法保证他们能够执行拉动以获得重命名然后拉动以获得软链接.
另外的例子.我正在从python模块中删除一个函数,该模块__init__.py本来就不应该在那里,__init__.py应该是空的.这也没有被发现作为重命名.即使新文件__init__.py的内容与原始文件的内容99%相同,并且新__init__.py内容的内容与旧内容的0%相同.一切都很好,直到我添加一个具有相同名称的文件.
事实上,Git确实跟踪内容而不是 - 或者更确切地说,我们应该说"除了" - 名称.差异是错误的,因为git diff(必然)尝试映射名称并比较两个单独提交的内容(或一个提交和当前工作目录,或一个提交和当前索引等,但这些只是主题的变体"比较两个提交").
更具体地讲,当 git diff比较树木1 T1和T2,它假定在默认情况下,唯一的候选人在改名是那些有些文件名存在于T1中而不是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出现问题:现在出现在两者中,而dir2/file2现在只出现在C中,并且git diff没有意识到存在重命名候选者.
有一个标志,--find-copies-harder或者您可以指定-C不止一次 - 这(不出所料)使复制/重命名检测代码更加困难.在这种情况下,git将考虑"看起来未更改"(两个树中具有相同名称)的文件可能已被复制或重命名为"显示为新"的另一个文件(存在于第二个树但不存在于第一个树中)的可能性.默认情况下不启用此功能,因为完全通用版本的计算密集程度非常高.
不幸的是,没有办法控制计算差异集时使用的差异选项git merge.merge命令设置一些默认值(-M50%等)并执行几个差异,并且不允许您设置--find-copies-harder.因此即使这适用于手册git diff,它也无法解决您的合并冲突.
请注意,当您进行合并时,2 git只计算两组diff:从merge-base 3到当前HEAD,以及从merge-base到merged-in commit(git合并提交,而不是分支) :结果合并该分支的事实,当该提交是分支的提示时,是一种"故意巧合").因此,它是可以作出一个提交重命名和符号链接作为第二,但要获得git merge"看到"重命名,还必须执行两个独立git merge秒.它并不是特别令人高兴,但为了解决这个问题,你必须让git的diff机器变得更聪明,这样它至少可以弄清楚,如果文件类型更改"找到副本/重命名",则更有可能找到重命名有点难".
(注意,将这个添加到diff机器会修复这两个问题 - git diff没有看到重命名,而git merge没有立即看到重命名.)
1这里的"树"是指完整的文件树,而不是git的tree对象.
2更具体地说,这是双父合并的情况.八达通合并的处理方式不同.我没有挖到章鱼合并的内脏,也无法真正说出更多关于那些的内容.
3合并库依赖于要合并的两个(或更多)提交,并使用default(recursive)策略复杂化,如果有多个合并库候选者,git计算"虚拟合并库",这是不一定与任何实际提交相同.细节不是我在这里可以正确解释的:我知道一般的想法,但不知道git中的细节,并且在任何情况下它都很少重要,与你的问题没有直接关系.有一个非常好的例子,在这里,如果你想了解更多,虽然示例使用一些相当ClearCase的类似的术语.