git blame 所做的几乎是找到所需信息的唯一方法。但是,您可以稍微简化操作,这对于您的目的可能已经足够了,也许也足够快。
请记住,每个提交都有每个文件的完整快照。分支名称标识某些提交链中的 last 提交。所以当你有:
... <-F <-G <-H <-- branch
名称branch 包含提交H 的原始哈希ID。在提交H 中,有很多文件,每个文件都有很多行。这些文件的形式是它们在提交 H 中的形式,仅此而已 - 除了提交 H 包含先前提交 G 的哈希 ID。
您可以使用哈希 ID this 来定位提交 G 并提取所有 its 文件,当 G 中的文件与 H 中的文件完全匹配时,这意味着——至少在git blame 术语中——G 文件中的所有行都归因于G,如果不是早期的提交。因此G 和H 中不同 的文件应归于H。 git blame 命令逐行运行,如果它们不同,则将单个 行 归因于提交 H,但也许出于您的目的,将整个文件归因于 H 就足够了。
如果您决定该文件应该归属于提交G,现在是时候从提交G 中提取提交F 的哈希ID,并使用它来读取提交@ 中的所有文件987654344@。如果F 中的任何给定文件与G 中的副本匹配,则归属移回F;否则它会保留在G。
你必须重复这个过程,直到你完全用完提交:
A <-B <-C ... <-H
由于提交 A 有 no 父级,A 中的任何文件在 last 提交期间一直未更改,都将归因于提交 @987654352 @。但是,一旦您将 H 中存在的所有文件完全归因于链中稍后的某个提交,您就可以停止向后遍历。将此与 git blame 进行比较,只要至少有一个 行 归因于某个较早的提交,它就必须继续向后看:您可能会在 git blame 必须之前很久就停下来。
此外,由于 Git 的内部数据结构,可以非常快速地判断某个较早提交中的文件是否与稍后提交中的同名文件完全匹配:每次提交中的每个文件都由哈希 ID 表示。如果哈希 ID 相同,则文件的内容在两次提交中是逐位相同的。如果不是,他们不是。
没有方便的 in-Git 命令可以准确地执行您想要的操作,1 如果您确实打算像这样遍历历史记录,您必须决定如何处理合并。请记住,合并提交有一个快照,但与非合并提交不同的是,它有 两个或更多个父节点:
...--o--K
\
M--o--o--...--o <-- last
/
...--o--L
如果M 中的文件与K 和/或L 中的一个或多个文件匹配,您应该遵循哪个提交? git log 命令有它自己的方法——git log <start-point> -- <path> 将通过跟随一个父级来简化历史记录,该父级是从一组父级中随机选择的,它与给定文件具有相同的哈希 ID。
请注意,您可以使用git rev-list,也可以使用--parents,来生成您可以选择检查的散列ID 集。 rev-list 命令是大多数其他 Git 命令的主力,包括git blame 本身,用于跟踪这样的历史记录。 (注意:git log 命令与git rev-list 使用相同的源构建,但有一些小的命令行选项差异和不同的默认输出。)
1虽然git log <start-point> -- <path> 在这里很有用,但对每条路径运行一次会太慢,而且在不给出单独路径的情况下运行它是无效的。