Git 不存储文件名更改。
每个提交存储一个完整的树,例如,可能提交 1234567... 有文件 README 和 foo.txt 并且提交 fedcba9... 有文件 readme.txt 和 foo。如果您要求 git 比较提交 1234567 和提交 fedcba9,并且 README 与readme.txt 足够相似1,git 会说将一个提交转换为另一个提交的方式是重命名文件。 (如果一个提交是另一个提交的父提交,则子提交的git show 将显示重命名,因为git show 在git show 时间计算此更改。)
另一方面,如果第二个readme文件差别太大,但README与foo足够相似,git会说改1234567实现fedcba9的方法是重命名README 到 foo。
关键是 git 会在您要求比较时 计算,而不是更早。提交之间没有任何内容显示“重命名某些文件”。 Git 只是比较提交并决定然后文件是否足够相似。
出于您的目的,这最终意味着对于提交到复制或部分复制序列中的每个提交,您必须决定保留哪些路径名以及丢弃哪些路径名。如何实现这一点主要取决于您。 git log 命令确实有一个 --follow 标志来激活有限数量的重命名检测,因为它从子提交到其父级向后工作,git blame 自动尝试做同样的事情;您可以使用这些(一次一个路径名)来提出表单的映射:
in: commits A..B C..D E..F
use path: dir/file.ext dir/frill.txt lib/frill.next
例如。但是没有内置的东西可以做到这一点,而且不会特别容易。我首先将git log --follow 与--raw 或--name-status 输出结合起来,看看是否检测到任何有趣的重命名。如果有的话,这些是提交边界,您希望在提交时更改保留和丢弃的路径(无论是使用filter-branch 还是其他方法)。
如果这不起作用,或者您需要更多控制,请考虑在各个提交对之间运行 git diff --name-status(提交对信息来自 git rev-list)。
1只要您要求重命名检测,“完全相同”就足够相似,任何东西都低至“50% 相似”。您可以使用提供给git diff 的-M 标志的可选值来调整所需的相似度。
编辑:这似乎工作正常。我在 git 自己的 builtin/var.c 上使用过,它曾经有两个以前的名字,根据这个:
$ git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c
81b50f3ce40bfdd66e5d967bf82be001039a9a98
:100644 100644 2280518... 2280518... R100 builtin-var.c builtin/var.c
55b6745d633b9501576eb02183da0b0fb1cee964
:100644 100644 d9892f8... 2280518... R096 var.c builtin-var.c
--diff-filter 抑制除了重命名输出之外的所有内容,以便我们可以看到似乎是哪个提交重命名了文件。把它变成更有用的东西需要更多的工作,但这可能会让你走得很远:
git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c |
while true; do
if ! read hash; then break; fi
IFS=$'\t' read mode_etc oldname newname
read blankline
echo in $hash, rename $oldname to $newname
done
生产的:
in 81b50f3ce40bfdd66e5d967bf82be001039a9a98, rename builtin-var.c to builtin/var.c
in 55b6745d633b9501576eb02183da0b0fb1cee964, rename var.c to builtin-var.c