简体中文 ▾ 主题 ▾ 最新版本 ▾ gitdiffcore 最后更新于 2.44.0

名称

gitdiffcore - 调整差异输出

概要

git diff *

描述

git diff-indexgit diff-filesgit diff-tree 等差异命令可以在显示差异输出之前,以非传统方式处理它们发现的差异。这种处理统称为“diffcore 转换”。本简要说明描述了它们是什么以及如何使用它们来生成比传统差异输出更容易理解的差异输出。

操作链

git diff-* 系列命令的工作原理是首先比较两组文件

  • git diff-index 比较“树”对象的内容与工作目录(当不使用 --cached 标志时),或比较“树”对象与索引文件(当使用 --cached 标志时);

  • git diff-files 比较索引文件与工作目录的内容;

  • git diff-tree 比较两个“树”对象的内容;

在所有这些情况下,命令本身首先通过命令行上给定的任何路径规范(pathspecs)可选地限制两组文件,然后比较两组结果文件中对应的路径。

路径规范用于限制差异操作的范围。它们会移除指定路径名集之外的文件对。例如,如果输入的文件对集包含

:100644 100644 bcd1234... 0123456... M junkfile

但如果命令调用是 git diff-files myfile,那么 junkfile 条目将从列表中删除,因为只考虑“myfile”。

比较结果从这些命令传递到内部称为“diffcore”的机制,其格式类似于不使用 -p 选项时的输出。例如:

in-place edit  :100644 100644 bcd1234... 0123456... M file0
create         :000000 100644 0000000... 1234567... A file4
delete         :100644 000000 1234567... 0000000... D file5
unmerged       :000000 000000 0000000... 0000000... U file6

diffcore 机制接收此类比较结果列表(其中每个结果称为“文件对”,尽管在这一点上每个都只涉及单个文件),并将其转换为另一个列表。目前有 5 种此类转换:

  • diffcore-break

  • diffcore-rename

  • diffcore-merge-broken

  • diffcore-pickaxe

  • diffcore-order

  • diffcore-rotate

这些转换按顺序应用。git diff-* 命令找到的文件对集用作 diffcore-break 的输入,diffcore-break 的输出用作下一个转换的输入。最终结果然后传递给输出例程,并生成 diff-raw 格式(参见 git diff-* 命令手册的“输出格式”部分)或 diff-patch 格式。

diffcore-break:用于拆分完全重写

链中的第二个转换是 diffcore-break,由 git diff-* 命令的 -B 选项控制。它用于检测表示“完全重写”的文件对,并将其拆分为表示删除和创建的两个文件对。例如,如果输入包含以下文件对:

:100644 100644 bcd1234... 0123456... M file0

如果它检测到文件“file0”被完全重写,它会将其更改为:

:100644 000000 bcd1234... 0000000... D file0
:000000 100644 0000000... 0123456... A file0

为了拆分文件对,diffcore-break 检查文件修改前后内容(即,在上述示例中,其 SHA-1 内容 ID 为“bcd1234…”和“0123456…”的内容)之间的更改程度。原始内容的删除量和新材料的插入量相加,如果超过“断点分数”,则文件对被拆分为两部分。断点分数默认为原始和结果中较小者大小的 50%(即,如果编辑缩小了文件,则使用结果的大小;如果编辑延长了文件,则使用原始的大小),并且可以通过在“-B”选项后给出一个数字(例如,“-B75”表示使用 75%)进行自定义。

diffcore-rename:用于检测重命名和复制

此转换用于检测重命名和复制,由 git diff-* 命令的 -M 选项(检测重命名)和 -C 选项(同时检测复制)控制。如果输入包含这些文件对:

:100644 000000 0123456... 0000000... D fileX
:000000 100644 0000000... 0123456... A file0

并且已删除文件 fileX 的内容与已创建文件 file0 的内容足够相似,则重命名检测会合并这些文件对并创建:

:100644 100644 0123456... 0123456... R100 fileX file0

当使用“-C”选项时,修改过的文件和已删除文件(如果使用“--find-copies-harder”选项,也包括未修改文件)的原始内容被视为重命名/复制操作中源文件的候选。如果输入是这些文件对,它们涉及修改过的文件 fileY 和新创建的文件 file0,

:100644 100644 0123456... 1234567... M fileY
:000000 100644 0000000... bcd3456... A file0

文件 fileY 的原始内容和文件 file0 的结果内容进行比较,如果它们足够相似,则更改为:

:100644 100644 0123456... 1234567... M fileY
:100644 100644 0123456... bcd3456... C100 fileY file0

在重命名和复制检测中,都使用 diffcore-break 中使用的相同“更改程度”算法来确定两个文件是否“足够相似”,并且可以通过在“-M”或“-C”选项后给出一个数字(例如,“-M8”表示使用 8/10 = 80%)来定制不同于默认 50% 的相似度分数。

请注意,当重命名检测开启但复制和断点检测都关闭时,重命名检测会添加一个初步步骤,首先检查文件是否在目录之间移动但文件名保持不变。如果一个目录中添加的文件内容与从不同目录中删除的同名文件内容足够相似,它会将它们标记为重命名,并将其排除在后续的二次步骤之外(该步骤会成对比较所有不匹配的文件以找到“最佳”匹配,由最高内容相似度决定)。因此,例如,如果一个已删除的 docs/ext.txt 和一个已添加的 docs/config/ext.txt 足够相似,它们将被标记为重命名,并阻止一个可能与已删除的 docs/ext.txt 更相似的已添加 docs/ext.md 在后续步骤中被视为重命名目标。因此,初步的“匹配相同文件名”步骤使用稍高的阈值来将文件对标记为重命名,并停止考虑其他更好的匹配候选。在此初步遍历中,每个文件最多进行一次比较;因此,如果在精确重命名检测后,整个目录层次结构中仍有多个 ext.txt 文件,则对于这些文件,可能会跳过此初步步骤。

注意。当“-C”选项与 --find-copies-harder 选项一起使用时,git diff-* 命令会将未修改的文件对以及已修改的文件对都提供给 diffcore 机制。这使得复制检测器可以考虑将未修改的文件作为复制源候选,但代价是会使其变慢。如果没有 --find-copies-hardergit diff-* 命令只能在被复制的文件恰好在同一变更集中被修改时检测到复制。

diffcore-merge-broken:用于将完全重写重新合并

此转换用于将由 diffcore-break 拆分但未被 diffcore-rename 转换为重命名/复制的文件对重新合并为单个修改。当使用 diffcore-break 时,此转换总是运行。

为了将拆分的文件对重新合并,它使用了与 diffcore-break 和 diffcore-rename 不同的“更改程度”计算方法。它只计算原始内容的删除量,不计算插入量。如果你从一个 100 行的文档中只删除了 10 行,即使你添加了 910 行新内容以形成一个新的 1000 行文档,你也没有进行完全重写。diffcore-break 拆分这种情况是为了帮助 diffcore-rename 将此类文件对视为重命名/复制检测的候选,但如果以这种方式拆分的文件对未与其他文件对匹配以创建重命名/复制,则此转换会将它们重新合并回原始的“修改”。

“更改程度”参数可以通过给 -B 选项提供第二个数字来调整,而不是默认的 80%(即,除非原始材料的删除量超过 80%,否则拆分对将重新合并为单个修改),例如:

  • -B50/60(给 diffcore-break 50% 的“断点分数”,对 diffcore-merge-broken 使用 60%)。

  • -B/60(同上,因为 diffcore-break 默认为 50%)。

请注意,早期实现会将拆分的文件对作为单独的创建和删除补丁。这是一种不必要的技巧,最新实现总是将所有拆分的文件对重新合并回修改中,但对于此类完全重写,生成的补丁输出格式会有所不同,以便于审查,它会显示以 - 为前缀的旧版本的所有内容,后跟以 + 为前缀的新版本的所有内容。

diffcore-pickaxe:用于检测指定字符串的添加/删除

此转换将文件对集限制为那些以特定方式更改前像(preimage)和后像(postimage)之间指定字符串的文件对。-S<文本块> 和 -G<正则表达式> 选项用于指定查找这些字符串的不同方式。

“-S<文本块>”检测那些前像和后像中指定文本块出现次数不同的文件对。根据定义,它不会检测文件内移动。此外,当一个变更集整体移动一个文件而不影响目标字符串时,diffcore-rename 会像往常一样启动,而 -S 会忽略该文件对(因为在该重命名检测到的文件对中,该字符串的出现次数没有改变)。当与 --pickaxe-regex 一起使用时,<文本块> 将被视为扩展 POSIX 正则表达式进行匹配,而不是字面字符串。

“-G<正则表达式>”(助记:grep)检测那些文本差异中包含与给定正则表达式匹配的添加或删除行的文件对。这意味着它将检测文件内(或重命名检测认为是同一文件)的移动,这会产生噪音。此实现会运行两次 diff 并进行 grep,这可能会非常耗时。为了加快速度,没有 textconv 过滤器的二进制文件将被忽略。

当使用 -S-G 而不带 --pickaxe-all 时,输出中只保留符合各自条件的文件对。当使用 --pickaxe-all 时,如果一个变更集中即使只有一个文件对符合各自条件,则整个变更集都会被保留。此行为旨在使在整个变更集上下文中审查更改变得更容易。

diffcore-order:用于根据文件名排序输出

此功能用于根据用户(或项目)的偏好对文件对进行重新排序,由 git diff-* 命令的 -O 选项控制。

这需要一个文本文件,其中每一行都是一个 shell glob 模式。文件中较早行匹配 glob 模式的文件对会在较晚行匹配的文件对之前输出,而那些不匹配任何 glob 模式的文件对则最后输出。

例如,核心 Git 的典型 orderfile 可能看起来像这样:

README
Makefile
Documentation
*.h
*.c
t

diffcore-rotate:用于更改输出从哪个路径开始

此转换接受一个路径名,并旋转文件对集,使给定路径名的文件对排在最前面,可选地丢弃其之前的路径。这用于实现 --skip-to--rotate-to 选项。当指定的路径名不在文件对集中时会报错,但与“git log”系列命令一起使用时报错并不实用,因为期望“git log”命令显示的每个提交都修改给定路径是不合理的。因此,当与“git log”一起使用时,输出将从与给定路径名排序相同或紧随其后的第一个文件对开始。

将此转换与 diffcore-order 结合使用会产生意外结果,因为当 diffcore-order 生效时,此转换的输入可能未排序。

GIT

Git[1] 套件的一部分

scroll-to-top