Git
English ▾ 主题 ▾ 最新版本 ▾ gitdiffcore 最后更新于 2.44.0

名称

gitdiffcore - 微调 diff 输出

概要

git diff *

描述

diff 命令 git diff-indexgit diff-filesgit diff-tree 可以被告知在显示 diff 输出之前以非常规的方式操作它们找到的差异。这种操作统称为“diffcore 变换”。本简短说明描述了它们是什么以及如何使用它们来生成比传统类型更容易理解的 diff 输出。

操作链

git diff-* 系列首先比较两组文件

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

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

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

在所有这些情况下,命令本身首先可选地通过其命令行上给出的任何路径规范来限制这两组文件,并比较这两组结果文件中相应路径。

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

: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 检查修改前后文件内容之间的更改范围(即,在上述示例中,内容分别具有“bcd1234…”和“0123456…”作为其 SHA-1 内容 ID)。删除原始内容和插入新材料的数量相加,如果超过“中断分数”,则文件对将被分成两个。中断分数默认为原始文件和结果文件(即,如果编辑缩小了文件,则使用结果文件的大小;如果编辑延长了文件,则使用原始文件的大小)大小的 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”选项后提供数字来自定义为使用与默认 50% 不同的相似度分数(例如,“-M8”表示使用 8/10 = 80%)。

请注意,当重命名检测打开但复制和中断检测都关闭时,重命名检测会添加一个初步步骤,首先检查文件是否在保持文件名相同的同时跨目录移动。如果有一个文件添加到目录中,其内容与从不同目录中删除的同名文件足够相似,它将标记它们为重命名,并将其排除在随后的二次步骤之外(即,对所有未匹配的文件进行成对比较以找到“最佳”匹配,由最高的内容相似度决定)。因此,例如,如果删除的 docs/ext.txt 和添加的 docs/config/ext.txt 足够相似,它们将被标记为重命名,并防止添加的 docs/ext.md(可能与删除的 docs/ext.txt 更相似)在后面的步骤中被视为重命名目标。出于这个原因,初步的“匹配相同文件名”步骤使用稍微更高的阈值来标记文件对为重命名,并停止考虑其他候选对象以获得更好的匹配。在此初步传递中,每个文件最多进行一次比较;因此,如果在精确重命名检测之后,整个目录层次结构中仍有几个剩余的 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 将此类文件对视为重命名/复制检测的候选对象,但如果以这种方式分解的文件对没有与其他文件对匹配以创建重命名/复制,则此转换会将它们合并回原始的“修改”。

“更改范围”参数可以从默认的 80%(即,除非删除了超过 80% 的原始内容,否则断开的对将合并回单个修改)进行调整,方法是向 -B 选项提供第二个数字,例如

  • -B50/60(为 diffcore-break 提供 50% 的“中断分数”,为 diffcore-merge-broken 使用 60%)。

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

请注意,早期实现将断开的对保留为单独的创建和删除补丁。这是一个不必要的 hack,最新的实现始终将所有断开的对合并回修改,但生成的补丁输出格式不同,以便在进行此类完全重写时更容易查看,方法是显示旧版本的完整内容,并在其前面加上-,然后是新版本的完整内容,并在其前面加上+

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

此转换将文件对的集合限制为那些在原像和后像之间以某种方式更改指定字符串的文件对。-S<block-of-text> 和 -G<regular-expression> 选项用于指定查找这些字符串的不同方式。

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

“-G<regular-expression>” (助记符:grep)检测其文本差异具有添加或删除的行且与给定正则表达式匹配的文件对。这意味着它将检测文件内(或重命名检测认为是相同文件)移动,这属于噪声。实现运行两次 diff 和 grep,这可能非常昂贵。为了加快速度,将忽略没有 textconv 过滤器的二进制文件。

-S-G在没有--pickaxe-all的情况下使用时,输出中只保留满足各自条件的文件对。当使用--pickaxe-all时,如果更改集中即使有一个文件对满足各自条件,则保留整个更改集。此行为旨在使在整个更改集的上下文中审查更改更容易。

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

这用于根据用户(或项目)的喜好重新排序文件对,并由git diff-*命令的 -O 选项控制。

这将获取一个文本文件,其中每一行都是一个 shell glob 模式。与文件中较早行上的 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