如何在Git中对移动/重命名的文件进行差异比较?

148
我使用了 git mv 命令移动了一个文件。现在我想要对比新文件和旧文件(旧文件已经不存在了,但是它的名字还在)。
如何做到这一点呢?

6
很快(git 2.9,2016年6月),简单的“git diff -- yourRenamedFile”就足够了。请参见下面的我的答案 - VonC
@VonC,你不还需要包括旧文件名和新文件名吗? - TTT
@TTT 当在树或提交级别(而不是文件本身)进行差异比较时不会出现此问题。 - VonC
6个回答

157

您需要使用 -M 选项让 git 在比较差异时自动检测移动的文件。仅使用 git diff(如 knittl 所述)对我来说无效。

因此,简单地说:git diff -M 就可以了。

有关此选项的文档如下:

-M[<n>], --find-renames[=<n>]
       Detect renames. If n is specified, it is a threshold on the similarity index 
       (i.e. amount of addition/deletions compared to the file’s size). For example, 
       -M90% means git should consider a delete/add pair to be a rename if more than
       90% of the file hasn’t changed.

8
救命稻草!现在我的Git差异比以前好多了。1)总是使用这个选项安全吗?2)我能把这个选项添加到我的~/.gitconfig文件中作为默认行为吗? - kevinarpe
8
请注意,重命名检测仅在旧文件和新文件都出现在git diff处理的文件集合中时才起作用。在单个(重命名)文件上运行git diff -M不会报告重命名。 - Leon
1
这对我不起作用,但是 git log --follow -- file_after_move.txt 很好用。它显示了整个历史记录,包括移动之前的内容。有什么想法吗?我正在运行 git version 2.11.0.windows.1 - bouvierr
1
-C 选项用于检测副本,非常有用且类似。我将其与 -M 一起使用,以查看我已将一个文件重构为两个文件的差异(两个文件的名称都与原始文件不匹配)。 - cp.engr
但是...如何进行difftool?例如vim? - Robin Hsu

107

除了knittl所写的内容,你还可以使用以下命令:

git diff HEAD:./oldfilename newfilename

HEAD:./oldfilename 表示在最近一次提交(HEAD)中,相对于当前目录的旧文件名(oldfilename)。

如果您的 git 版本不够新,则需要使用以下命令代替:

git diff HEAD:path/to/oldfilename newfilename

11
谢谢你。除了使用 git diff head ./oldfilename newfilename 之外,您还可以指定特定的提交版本号,例如:git diff 39fa7c77e85c51d43ea0cf30d33aec8721812e9e:./oldfilename newfilename。请注意,这将比较当前文件与选定的提交版本之间的差异。 - Chris Bloom
11
如果不清楚,你也可以指定分支名称或任何其他引用,例如:git diff branch:old/filen.name newfilename - jricher
如果你在目录中输入 cd 命令并且不在 commit:path 前面添加 --,第一种形式会对我起作用。Git 在此处的语法非常挑剔。 - dhardy
1
@dhardy <commit-ish>:<pathname> 语法是一个对象标识符,类似于 Git;在 -- 之后,Git 只会期望文件名。 - Jakub Narębski
这个语法对我有效,其中-M只显示整个文件已更改-git version 2.30.1(Apple Git-130)。它可以带或不带前导的./。-谢谢。 - Ed Randall

23

从git 2.9版本(2016年6月)开始,您将不再需要添加-M参数。 git diff默认使用-M参数。

请参见提交记录5404c11提交记录9501d19提交记录a9276a6提交记录f07fc9e提交记录62df1e6,这些提交是由Matthieu Moy (moy)于 2016年2月25日所做的。
(并入 提交记录5d2a30d,由 Junio C Hamano (gitster)合并于2016年4月3日)

diff:默认启用diff.renames

重命名检测是一个非常方便的功能,新用户不应该因为不知道如何启用它而错过。从现在开始,使用git diff命令时,diff.renames参数将会默认启用。

需要深入文档才能从中受益。

激活重命名检测可能会遇到一些异议,因为它有时会失败,而且有时速度较慢。但是重命名检测已经在几种情况下默认激活了,例如"git status"和"git merge",因此激活diff.renames并不会从根本上改变情况。当重命名检测失败时,现在在"git diff"和"git status"之间会一致失败。
此设置不影响内部命令,因此良好编写的脚本不会受到影响。 此功能的新测试在此处

4
无论出于何种原因,使用HEAD:./oldfilename(或绝对路径)都没有起作用,但是HEAD:oldfilename有效(感谢cmn):
git diff HEAD:oldfilename newfilename
git diff 2a80f45:oldfilename f65f3b3:newfilename

HTH


也许你的git版本太旧,无法理解“HEAD:./oldfilename”? - Jakub Narębski
1
这是此处提供的最通用的答案。如果文件已经被编辑过多,-M似乎无法工作。 - dremodaris

2

git diff -M激活重命名检测,正如其他人所说的那样(正如@VonC指出的那样,它在git 2.9中默认激活)。但是,如果您有一个大的更改集,不精确的重命名检测仍然可能被关闭。Git将显示以下警告,很容易在您查看的差异中忽略:

warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your diff.renameLimit variable to at least 450 and retry the command.

在这种情况下,按照git的建议设置配置选项,例如:
git config diff.renamelimit 450

并重新运行您的diff命令。


-6

只需运行git diff而不带任何参数,或者git diff -- newfilename。Git足够智能,可以比较正确的文件/内容(即重命名前的原始内容与重命名后的更改内容)。


2
在大多数情况下,git 并不够智能。仅仅使用 git mv 命令移动单个文件,然后将暂存状态与另一个完全相同的分支进行比较,将会产生“所有内容都被删除并重新创建”的差异,除非使用 -M 选项。 - Reinderien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接