如何运行Git diff并显示移动文件的新位置而不是旧位置?

时间:2019-04-24 17:33:49

标签: git

我正在使用以下命令: git --no-pager diff --name-only 'origin/oldbranch'..'origin/newerbranch'

这很完美,我可以获得自旧分支以来已被修改,删除或添加的所有文件的列表。

但是,如果文件已移动但未修改,我会得到文件所在的位置,而不是新位置。

让git diff告诉我文件的新位置似乎不是一个简单的选择。看来我必须使用git log --name-only -- "**/themovedfile.txt"来获取该文件所在位置的所有结果。虽然我可以写一些逻辑来解决这个问题,但这似乎有些过分。

有更简单的方法吗?

1 个答案:

答案 0 :(得分:1)

这里有几个值得注意的项目,但让我们从答案开始:

git diff --name-only L R

使用您配置的默认值(例如diff.renames和diff.renameLimit`)比较两个特定的提交,并仅提供在提交 L 中不同的文件名(对于左)和 R (右)。如果您颠倒这两个名称,则会得到相同的输出。如果重命名检测有效,您将获得出现在提交 R:

中的名称。
$ git diff 99177b34db^ 99177b34db --name-only
contrib/hooks/multimail/CHANGES
contrib/hooks/multimail/CONTRIBUTING.rst
contrib/hooks/multimail/README.Git
contrib/hooks/multimail/README.rst
contrib/hooks/multimail/doc/gitolite.rst
contrib/hooks/multimail/git_multimail.py
contrib/hooks/multimail/migrate-mailhook-config
contrib/hooks/multimail/post-receive.example

切换到--name-status为我们提供了更多信息:

$ git diff 99177b34db^ 99177b34db --name-status
M       contrib/hooks/multimail/CHANGES
M       contrib/hooks/multimail/CONTRIBUTING.rst
M       contrib/hooks/multimail/README.Git
R095    contrib/hooks/multimail/README  contrib/hooks/multimail/README.rst
M       contrib/hooks/multimail/doc/gitolite.rst
M       contrib/hooks/multimail/git_multimail.py
M       contrib/hooks/multimail/migrate-mailhook-config
M       contrib/hooks/multimail/post-receive.example

状态R表示Git已检测到重命名。现在,您获得了这两个名称:左边的一个来自 L ,右边的一个来自 R

两点符号

在这里,我写的是99177b34db^ 99177b34db而不是99177b34db^..99177b34db。在许多Git命令中,它们有很大的不同。但是对于diff命令,它们的含义完全相同。。没有理由使用这两个点,也没有理由避免这两个点。分支名称的规则禁止两个相邻的点:feature.onefeature.two之类的分支名称是合法的,而feature..two是不合法的。

(不要将两点符号与三点符号混淆。这在git diff中也有特殊含义。)

使用散列与分支名称

我使用了99177b34db而不是分支名称。您写道:

origin/oldbranch..origin/newerbranch

(单引号在这里是不必要的,因此为简单起见,我将其删除)。在许多Git命令中,分支名称与其所解析的提交哈希ID的含义相同:

$ git rev-parse master
b5101f929789889c2e536d915698f58d5c5c6b7a

您可以使用原始哈希ID或它的缩写形式来表示此特定提交。使用分支 name 的原因是,当您向分支添加新的提交时,名称​​ 所标识的提交哈希ID会随着时间而变化。这些ID对人类几乎没有用,而名称是由人类创建并为人类创建的。

(一些命令,例如git checkout,对名称进行了更有趣的操作,因此对于这些命令,名称不能与哈希ID互换。)

重命名检测是一个选项

git diff正在做的是比较两个快照。每个提交代表每个源文件的完整,完整快照。 Git将左侧提交与右侧提交进行比较,然后将两者进行比较。对于两个提交中都相同的文件,git diff什么也不说。对于不同的文件或仅在一次提交或另一次提交中的文件,git diff会说些什么。

对于--name-only,它表示 R 中文件的名称不同。使用--name-status,它给出了使它们与众不同的名称​​和:它们可以被修改(即,在两个提交中都存在但内容不同),或者被新添加,即在中出现仅限> R 或已删除,即仅提交 L

启用重命名检测后,如果在 L 中删除了一些文件,并且在 R 中创建了其他命名的文件,则git diff会执行一系列操作进行额外的计算,以确定 R 中的新文件是否实际上与 L 中的文件“相同”。尽管Git提供了一个简单而难以理解的答案,但是这种相同性(或 identity (请参阅https://en.wikipedia.org/wiki/Ship_of_Theseus)的概念实际上相当复杂:如果Git计算出的相似性索引,则两个文件是相同的达到某个阈值。默认值为“ 50%相似”。

检测到的重命名的字母代码为R;其次是相似性指数。索引越高,文件越相似。 100是为完全相同的保留的,这在Git中是一种特殊情况:如果两个文件完全相同,它们实际上只会存储一次 ,Git可以非常快速地检测到此完全匹配。

关闭重命名检测后,即使这两个文件都是位,您也只会从 L 中删除一个文件,并向 R 中添加一个名称不同的文件。位相同。由于重命名检测是一个配置项,因此在同一两次提交上运行git diff的两个不同用户可能会看到不同的结果。