git log --name-status显示重命名后单个文件的新名称

4
当使用命令 git log --name-status -- <filename> 时,如何显示在提交中被重命名的文件的新名称?如果命令行中没有指定特定的文件名,则 --name-status 只会显示完整的重命名操作。以下是来自我的一个存储库的示例。提供了文件名后,我只能看到删除操作,但无法看到新的文件名:
❯ git log --name-status --oneline -- setup.py
f4b8f93 Use uwsgi.ini and remove legacy runserver stuff
A       setup.py
5bb21a8 Rename setup.py to setup_pymake.py
D       setup.py

在没有文件名的情况下使用--name-status,可以得到完整的细节:

❯ git log --name-status --oneline 5bb21a8
5bb21a8 Rename setup.py to setup_pymake.py
R100    setup.py        setup_pymake.py

当追踪单个文件时,我希望也能得到这种信息。


编辑:只有在提交顺序如下(从旧到新)的情况下,才会出现此特定情况:

  1. 创建 setup.py
  2. setup.py 重命名为 setup_pymake.py
  3. 创建另一个 setup.py

如果没有最后一个步骤,则不会出现此奇怪的行为。我不确定Git从提交3回溯到2是否合理。


编辑2:--follow 在这里无法改变任何内容,因为原始问题似乎是有一个“不想要”的跟踪。


1
尝试使用 --follow 进行操作,但请注意 --follow 的实现是一种不太正规的技巧,只适用于特定情况。(我相信这应该属于那些特殊情况之一。) - torek
这里使用“--follow”没有任何区别。 - languitar
啊,我明白了,你有两个不同的文件,都叫做 setup.py。这是有问题的。:-) 不过由于 Git 是逐步提交的,你可以预测 Git 在这里的行为。 - torek
1个回答

1

好的,新的问题编辑显示了一个误解:如果您想让git跟踪您感兴趣的特定内容的更改,您必须将git指向该内容。您拥有的是在setup.py中添加的一组内容,然后将该内容重命名到其他位置,然后在setup.py中添加了一组全新的内容,并且您希望跟踪以前的setup.py的历史记录,而不是现在的setup.py

要找到“以前的setup.py”,

oldsetup=`git log -1 --pretty=%h --diff-filter=D -- setup.py`

oldsetup设置为最近删除setup.py的提交的足够长的哈希值,您可以使用以下方法跟踪该内容的历史记录

git log $oldsetup --oneline --name-status --follow -- setup.py

编辑3:...嗯,这并没有显示最终的重命名目标。为了获得您想要的结果,需要跟随重命名源和重命名目标的所有内容,您将不得不构建自己的路径跟踪器,该跟踪器可以利用所有路径的--name-status输出。如果要手动执行此操作,请查找所有添加和删除,并在每个位置显示完全检查的路径状态。

threads=`git log --diff-filter=ADRC --pretty=%h -- path` 

git log --no-walk --name-status --oneline $threads

当你自动化时,从这里开始。 --name-status 输出是为人类消费而消化的,对于脚本编写,您“应该”使用 --raw 输出。
如果你想展示每个在 `setup.py` 中的内容集合所发生的事情,你需要熟练掌握 `sed` 或者其他足够强大的工具来完成这项工作。请参考下面的内容,你可能需要查看 `--raw` 选项而不是 `--name-status` 来帮助你追踪你试图收集的所有线程。
重命名检测会让Git检查所有其他更改与每个路径的更改,但指定路径会关闭Git检查除该路径外的任何更改。恰好路径规范是最后进入的,并关闭了检查所有其他逻辑。
你需要的是让Git查看一切,然后向你展示影响该路径的更改的全部内容。它并没有内置这个功能,但一旦你知道如何使用行聚集获取sed的惯用语,就不难从完整输出中获取所需内容。
以下是执行您想要的操作的命令:
git log --name-status -m --oneline \
| sed '/^[A-Z]/{/\tsetup\.py/!d;H;$!d};x;/\tsetup\.py/!d'

这里承认对于未经训练的人而言,它并不比纯线噪声更好。但这是你的启蒙。实际上并不难。sed 有两个缓冲区,"模式"缓冲区和"保持"缓冲区。上面的 sed 脚本通过在"保持"缓冲器中累积行群来运作,然后当它看到 eof 或下一个群的开始时,将其与累积群交换并仅打印有趣的部分。 Sed脚本命令由;或换行符分隔(旧版sed不支持分号,需要换行符分隔)。它们可以用{/}括起来进行分组,并在到达每一行时运行。操作命令通常是单个字母,例如p表示打印,d表示删除,等等;它们可以带有模式或范围以选择特定的行。因此,/^[A-Z]/{...}表示大写字母开头的行上的sed命令将在花括号中执行。熟悉sed的人会知道/continuation/{...H;$!d};x是累加行套路。当sed执行完x命令后,模式缓冲区就有了整个套路。
为了尝试一个更简单的例子,这里有一个实现git log --grep的示例:
git log \
| sed '/^commit /!{H;$!d};x;/pattern/!d'

如果一行不以commit 开头,那么它就是一个续行,会被追加到保持缓冲区(H)中。如果它不是文件中的最后一行($!,这是一个与/^commit /!类似的行选择模式),则停止模式处理,d表示不要再对此缓冲区执行任何操作,在下一行再次运行脚本(但保留保持缓冲区)。因此,如果执行到x,我们要么有一个提交行,要么是最后一个提交消息的最后一行。x交换保存缓冲区和模式缓冲区。模式缓冲区现在具有前一个提交的第一行及其所有附加后续行。如果整个群集不包括我们想要的模式,则被删除,但我们想要的所有群集都被打印出来。
你需要的命令只包括标题行和影响setup.py的任何更改行。
/^[A-Z]/{/\tsetup\.py/!d

只打印包含此更改行的提交,最后加上/\tsetup.py/!d

-m选项告诉git log显示合并提交的每个父级之间的差异;通常它不显示那些被轻松合并的更改(因为这些更改最初出现在一个即将被列出的提交中)。我在这里包括它主要是为了展示它的存在。

¹sed的默认循环是将每一行顺序加载到模式缓冲区并运行整个脚本;如果脚本没有删除缓冲区或以其他方式避免达到脚本的末尾,则在结束时打印缓冲区中的任何内容。


感谢详细的解释。然而,这不会列出重命名之前的修订版本,因为sed不知道先前的文件名(我也不知道)。 - languitar
啊,Git做了一个你不知道的区分。那个,你想要的是--follow选项。git log --oneline --name-status --follow -- the/current/path - jthill
(顺便说一句,你实际上可以用 sed 模糊地实现这个功能,但这样做太过于粗暴和常见,以至于某些人有动力用 C 语言来实现它。) - jthill
我已经在上面添加了更多关于这里似乎存在的特殊情况的信息。--follow 在这里也没有帮助。 - languitar

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