设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.44.1 → 2.52.0 无更改
-
2.44.0
2024-02-23
- 2.43.1 → 2.43.7 无更改
-
2.43.0
2023-11-20
- 2.32.1 → 2.42.4 无更改
-
2.32.0
2021-06-06
- 2.31.1 → 2.31.8 无更改
-
2.31.0
2021-03-15
- 2.21.1 → 2.30.9 无更改
-
2.21.0
2019-02-24
- 2.13.7 → 2.20.5 无更改
-
2.12.5
2017-09-22
- 2.10.5 → 2.11.4 无更改
-
2.9.5
2017-07-30
- 2.5.6 → 2.8.6 无更改
-
2.4.12
2017-05-05
- 2.1.4 → 2.3.10 无更改
-
2.0.5
2014-12-17
描述
git diff-index, git diff-files, 和 git diff-tree 命令可以在显示 diff 输出之前,以非传统的方式被告知操作它们找到的差异。这些操作统称为“diffcore 转换”。这篇简短的说明描述了它们是什么,以及如何使用它们来产生比传统 diff 输出更容易理解的 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”在考虑范围内。
比较结果以一种类似于未启用 -p 选项时的输出格式,从这些命令传递给内部称为“diffcore”的机制。例如:
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 检查修改前后文件内容之间的变化程度(即,在上面的例子中,内容 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”选项后跟一个数字来定制相似度分数,使其不同于默认的 50%(例如,“-M8”表示使用 8/10 = 80%)。
请注意,当启用重命名检测但禁用复制和分割检测时,重命名检测会添加一个初步步骤,首先检查文件是否在移动到不同目录时保持文件名相同。如果存在一个添加到某个目录的文件,其内容与从另一个目录中删除的同名文件足够相似,它将把它们标记为重命名,并从稍后的二次步骤(即逐个比较所有未匹配的文件以查找“最佳”匹配,由最高内容相似度决定)中排除它们。因此,例如,如果一个已删除的 docs/ext.txt 和一个新添加的 docs/config/ext.txt 足够相似,它们将被标记为重命名,并阻止一个可能与已删除的 docs/ext.txt 更相似的新添加的 docs/ext.md 在稍后的步骤中被视为重命名目标。出于这个原因,初步的“匹配相同文件名”步骤使用稍高的阈值来将文件对标记为重命名,并停止考虑其他候选者以获得更好的匹配。在此初步过程中,每个文件最多执行一次比较;因此,如果在精确的重命名检测之后,目录结构中仍然存在多个 ext.txt 文件,则此初步步骤可能会跳过这些文件。
注意。当使用“-C”选项和 --find-copies-harder 选项时,git diff-* 命令会将未修改的文件对以及已修改的文件对输入到 diffcore 机制。这使得复制检测器将未修改的文件视为复制源候选,但代价是速度变慢。如果没有 --find-copies-harder,git 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 中包含与给定正则表达式匹配的添加或删除行的文件对。这意味着它会检测文件内部(或重命名检测认为的同一个文件)的移动,这会产生噪音。实现是运行两次 diff 并进行 grep,这可能非常耗时。为了加速,将忽略没有文本转换过滤器的二进制文件。
当 -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 生效时,此转换的输入可能未排序。