使用 git diff 在单个文件上显示重命名/移动状态
当我使用 git mv 移动或重命名文件时,git 在全局 diff 输出中显示移动/重命名操作:
me@myhost:~/test$ git mv foo.txt bar.txt
me@myhost:~/test$ git diff --staged
diff --git a/foo.txt b/bar.txt
similarity index 100%
rename from foo.txt
rename to bar.txt
但是,当我在文件上调用 git diff 时,它显示为新的:
me@myhost:~/test$ git diff --staged bar.txt
diff --git a/bar.txt b/bar.txt
new file mode 100644
index 0000000..fad95f3
--- /dev/null
+++ b/bar.txt
@@ -0,0 +1 @@
+Hey there!
为什么此文件的 git diff 输出会根据我是否使用文件名调用它而有所不同?当我传递文件名时,是否有一个标志或其他方式来查看重命名/移动状态?
Stack Overflow 上的大多数类似问题都建议在调用中添加 --find-renames
参数。不过,这似乎是我的情况的默认行为,因为不带参数的 git diff 已经正确显示了重命名。但是,将 --find-renames
传递给带有文件名的第二个调用并没有什么区别。
When I am moving or renaming a file with git mv
, git shows the move/rename action in the global diff output:
me@myhost:~/test$ git mv foo.txt bar.txt
me@myhost:~/test$ git diff --staged
diff --git a/foo.txt b/bar.txt
similarity index 100%
rename from foo.txt
rename to bar.txt
When I call git diff
on the file, though, it show up as new:
me@myhost:~/test$ git diff --staged bar.txt
diff --git a/bar.txt b/bar.txt
new file mode 100644
index 0000000..fad95f3
--- /dev/null
+++ b/bar.txt
@@ -0,0 +1 @@
+Hey there!
Why is the output of git diff
for this file different depending on whether I call it with or without the filename? Is there a flag or another way to see the rename/move status when I pass the filename?
Most of the similar questions here on Stack Overflow suggest adding the --find-renames
argument to the call. This seems to be the default behaviour in my case, though, since git diff
without arguments already shows the rename correctly. Passing --find-renames
to the second call with the filename does not make a difference, however.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
请注意,为简单起见,我们假设您提交了更改并正在比较提交;无论您事先使用
--staged
进行比较还是之后使用提交进行比较,结果都是相同的。将文件规范视为查看差异的镜头。
当完整地查看这两个提交时,Git 会看到:
Git 看到 F1 和 F2 指向同一个 blob,并且由于 F1 消失了,并且 F2 出现了相同的 blob,Git 可以推断这是一个重命名。如果文件也被编辑过,Git 可以进行启发式处理(顺便说一句,这是可配置的),以确定 blob 之间的差异是否足够接近,仍然可以将其称为重命名。
当通过文件名镜头查看这两个提交时,Git 会看到:
Git 将此视为添加。
你能做什么?
你可以将镜头放大以包含两个文件名。使用示例语法来暂存移动,请考虑以下语句:
或者在提交之后,这里是用于比较当前提交和先前提交的类似语法:
这样做的明显缺点是您还必须知道先前的名称。根据您最终想要实现的目标,也许您可以将多个命令链接在一起来解析前后文件名,并将它们都用作 diff 文件规范。这是一个示例起点,仅向您展示全局镜头的重命名:
这可能会导致您遇到类似的情况(使用 Bash):
这是一个很棒的 问题与解答,其中包含有关各种diff 过滤器选项。
Note, for simplicity, let's assume you committed the change and are comparing commits; the result will be the same whether you diff beforehand with
--staged
or afterward using the commits.Think of the file specification as a lens in which to view the diff through.
When viewing the two commits in their entirety, Git sees:
Git sees that F1 and F2 are pointing to the same blob and since F1 is gone, and F2 appears with the same blob, Git can infer that is a rename. If the file was also edited, Git can do heuristics (which is configurable, btw), to determine if the differences between the blobs are close enough to still call it a rename.
When viewing the two commits through the filename lens, Git sees:
Git sees this as an add.
What can you do?
You could make the lens larger to include both filenames. Using your example syntax for staging the move, consider these statements:
Or after you commit it, here's the similar syntax for comparing the current and previous commits:
The obvious downside to this is you must know the previous name as well. Depending on what you're ultimately trying to achieve, perhaps you could chain multiple commands together to parse out the before and after filenames, and use both of them as the diff file specification. Here's an example starting point that shows you the renames only for the global lens:
Which could lead you to something like this (using Bash):
Here's a great question and answer with details on the various diff filter options.