English ▾ 主题 ▾ 最新版本 ▾ gitattributes 上次更新于 2.49.0

名称

gitattributes - 定义每个路径的属性

概要

$GIT_DIR/info/attributes, .gitattributes

描述

gitattributes 文件是一个简单的文本文件,它为 attributes 赋予路径名。

gitattributes 文件中的每一行都采用以下形式

pattern attr1 attr2 ...

即,一个模式后跟一个属性列表,用空格分隔。忽略开头和结尾的空格。以 *#* 开头的行将被忽略。以双引号开头的模式以 C 样式引用。当模式与相关路径匹配时,该行上列出的属性将赋予该路径。

对于给定的路径,每个属性都可以处于以下状态之一

设置

该路径具有具有特殊值 "true" 的属性;这仅通过在属性列表中列出属性的名称来指定。

取消设置

该路径具有具有特殊值 "false" 的属性;这通过在属性列表中列出以短划线 - 开头的属性名称来指定。

设置为值

该路径具有具有指定字符串值的属性;这通过在属性列表中列出属性的名称,后跟等号 = 及其值来指定。

未指定

没有模式与该路径匹配,并且没有任何说明该路径是否具有该属性,则该路径的属性被认为是未指定的。

当多个模式与该路径匹配时,后面的行将覆盖前面的行。此覆盖是按属性进行的。

模式与路径匹配的规则与 .gitignore 文件中的规则相同(请参阅 gitignore[5]),但有几个例外

  • 禁止使用否定模式

  • 与目录匹配的模式不会递归匹配该目录内的路径(因此,在属性文件中使用尾部斜杠 path/ 语法毫无意义;请改用 path/**

在决定将哪些属性分配给路径时,Git 会查阅 $GIT_DIR/info/attributes 文件(该文件具有最高优先级)、与相关路径位于同一目录中的 .gitattributes 文件及其父目录,直到工作树的顶层(包含 .gitattributes 的目录距离相关路径越远,其优先级越低)。最后,考虑全局和系统范围的文件(它们具有最低优先级)。

当工作树中缺少 .gitattributes 文件时,索引中的路径将用作后备。在检出过程中,使用索引中的 .gitattributes,然后将工作树中的文件用作后备。

如果你希望仅影响单个存储库(即,为特定于该存储库的某个用户的工作流程的文件分配属性),则应将属性放置在 $GIT_DIR/info/attributes 文件中。应将应进行版本控制并分发到其他存储库的属性(即,所有用户都感兴趣的属性)放入 .gitattributes 文件中。应影响单个用户的所有存储库的属性应放置在由 core.attributesFile 配置选项指定的文件中(请参阅 git-config[1])。其默认值为 $XDG_CONFIG_HOME/git/attributes。如果未设置 $XDG_CONFIG_HOME 或为空,则改用 $HOME/.config/git/attributes。应影响系统上所有用户的属性应放置在 $(prefix)/etc/gitattributes 文件中。

有时你需要将路径的属性设置覆盖为 Unspecified 状态。这可以通过列出以感叹号 ! 开头的属性名称来完成。

保留的内置_* 属性

builtin_* 是用于内置属性值的保留命名空间。此命名空间下的任何用户定义的属性都将被忽略并触发警告。

builtin_objectmode

此属性用于按其文件位模式(40000、120000、160000、100755、100644)过滤文件。例如::(attr:builtin_objectmode=160000)。你也可以使用 git check-attr builtin_objectmode -- <file> 检查这些值。如果对象不在索引中,则 git check-attr --cached 将返回未指定。

影响

Git 的某些操作可能会受到将特定属性分配给路径的影响。当前,以下操作可以识别属性。

检出和检入

这些属性影响在运行 git switchgit checkoutgit merge 等命令时,存储库中存储的内容如何复制到工作树文件。它们还会影响 Git 在 git addgit commit 时如何将你在工作树中准备的内容存储在存储库中。

text

此属性将路径标记为文本文件,从而启用行尾转换:当将匹配的文件添加到索引时,该文件的行尾将在索引中规范化为 LF。相反,当文件从索引复制到工作目录时,其行尾可能会从 LF 转换为 CRLF,具体取决于 eol 属性、Git 配置和平台(请参阅下面对 eol 的说明)。

设置

在路径上设置 text 属性会启用对检入和检出的行尾转换,如上所述。每次检入文件时,行尾都会在索引中规范化为 LF,即使该文件之前已使用 CRLF 行尾添加到 Git 中也是如此。

取消设置

取消设置路径上的 text 属性会告诉 Git 在检入或检出时不尝试任何行尾转换。

设置为字符串值 "auto"

text 设置为 "auto" 时,Git 会自行决定该文件是文本文件还是二进制文件。如果它是文本文件,并且该文件以前未使用 CRLF 结尾位于 Git 中,则如上所述,行尾将在检入和检出时进行转换。否则,在检入或检出时不进行任何转换。

未指定

如果未指定 text 属性,则 Git 使用 core.autocrlf 配置变量来确定是否应转换该文件。

任何其他值都会导致 Git 的行为就像 text 保持未指定一样。

eol

此属性标记一个路径,以便在检出时在工作树中使用特定的行尾样式。它仅在设置了 texttext=auto 时才有效(请参阅上文),但如果 text 保持未指定状态,则指定 eol 会自动设置 text

设置为字符串值 "crlf"

此设置会在检出文件时将工作目录中文件的行尾转换为 CRLF。

设置为字符串值 "lf"

此设置在检出文件时,在工作目录中使用与索引中相同的行尾。

未指定

如果未指定文件的 eol 属性,则工作目录中文件的行尾由 core.autocrlfcore.eol 配置变量确定(请参阅 git-config[1] 中这些选项的定义)。如果设置了 text,但未设置这两个变量中的任何一个,则 Windows 上的默认值为 eol=crlf,所有其他平台上的默认值为 eol=lf

crlf 属性的向后兼容性

为了向后兼容,crlf 属性的解释如下

crlf		text
-crlf		-text
crlf=input	eol=lf

行尾转换

虽然 Git 通常会保持文件内容不变,但可以将其配置为将行尾规范化为存储库中的 LF,并可以选择在检出文件时将其转换为 CRLF。

如果你只是想在工作目录中具有 CRLF 行尾,而与你正在使用的存储库无关,则可以设置配置变量 "core.autocrlf",而无需使用任何属性。

[core]
	autocrlf = true

这不会强制规范化文本文件,但会确保你添加到仓库的文本文件在线尾符被规范化为 LF 后再添加,以及确保仓库中已经规范化的文件保持规范化。

如果你想确保任何贡献者添加到仓库的文本文件都经过线尾符规范化,你可以将所有文件的 text 属性设置为 “auto”。

*	text=auto

这些属性允许对线尾符的转换进行细粒度控制。以下是一个示例,它将使 Git 规范化 .txt、.vcproj 和 .sh 文件,确保 .vcproj 文件在工作目录中具有 CRLF,.sh 文件具有 LF,并阻止 .jpg 文件被规范化,无论其内容如何。

*               text=auto
*.txt		text
*.vcproj	text eol=crlf
*.sh		text eol=lf
*.jpg		-text
注意
在跨平台项目中使用 push 和 pull 到中心仓库时,如果启用了 text=auto 转换,则包含 CRLF 的文本文件应被规范化。

从一个干净的工作目录开始

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

如果任何不应被规范化的文件出现在 *git status* 中,请在运行 *git add -u* 之前取消设置其 text 属性。

manual.pdf	-text

相反,Git 未检测到的文本文件可以手动启用规范化。

weirdchars.txt	text

如果 core.safecrlf 设置为 “true” 或 “warn”,Git 会验证当前 core.autocrlf 设置下的转换是否可逆。 对于 “true”,Git 会拒绝不可逆的转换; 对于 “warn”,Git 仅打印警告但接受不可逆的转换。 安全性触发器用于防止对工作树中的文件进行此类转换,但有一些例外。 即使……​

  • *git add* 本身不会触及工作树中的文件,但下次检出会,因此会触发安全性检查;

  • *git apply* 使用补丁更新文本文件会触及工作树中的文件,但该操作是关于文本文件的,而 CRLF 转换是关于修复线尾符不一致的,因此不会触发安全性检查;

  • *git diff* 本身不会触及工作树中的文件,但通常会运行它来检查你打算在下次 *git add* 中进行的更改。 为了尽早发现潜在问题,会触发安全性检查。

working-tree-encoding

Git 将以 ASCII 或其超集之一(例如 UTF-8、ISO-8859-1、…​)编码的文件识别为文本文件。 以某些其他编码(例如 UTF-16)编码的文件被解释为二进制文件,因此内置的 Git 文本处理工具(例如 *git diff*)以及大多数 Git Web 前端默认情况下不显示这些文件的内容。

在这些情况下,你可以使用 working-tree-encoding 属性告诉 Git 工作目录中文件的编码。 如果具有此属性的文件被添加到 Git,则 Git 会将内容从指定的编码重新编码为 UTF-8。 最后,Git 将 UTF-8 编码的内容存储在其内部数据结构(称为 “索引”)中。 检出时,内容会重新编码回指定的编码。

请注意,使用 working-tree-encoding 属性可能会遇到一些陷阱

  • 替代 Git 实现(例如 JGit 或 libgit2)和旧版本的 Git(截至 2018 年 3 月)不支持 working-tree-encoding 属性。 如果你决定在仓库中使用 working-tree-encoding 属性,强烈建议确保所有与仓库交互的客户端都支持它。

    例如,Microsoft Visual Studio 资源文件 (*.rc) 或 PowerShell 脚本文件 (*.ps1) 有时以 UTF-16 编码。 如果你将 *.ps1 文件声明为 UTF-16 并添加 foo.ps1,并且启用了 working-tree-encoding 的 Git 客户端,则 foo.ps1 将在内部存储为 UTF-8。 不支持 working-tree-encoding 的客户端会将 foo.ps1 检出为 UTF-8 编码的文件。 这通常会给此文件的用户带来麻烦。

    如果不支持 working-tree-encoding 属性的 Git 客户端添加一个新文件 bar.ps1,则 bar.ps1 将 “按原样” 存储在内部(在本例中可能为 UTF-16)。 支持 working-tree-encoding 的客户端会将内部内容解释为 UTF-8,并尝试在检出时将其转换为 UTF-16。 该操作将失败并导致错误。

  • 将内容重新编码为非 UTF 编码可能会导致错误,因为转换可能不是 UTF-8 往返安全的。 如果你怀疑你的编码不是往返安全的,请将其添加到 core.checkRoundtripEncoding 以使 Git 检查往返编码(请参阅 git-config[1])。 众所周知,SHIFT-JIS(日语字符集)与 UTF-8 存在往返问题,默认情况下会进行检查。

  • 重新编码内容需要资源,这可能会减慢某些 Git 操作(例如 *git checkout* 或 *git add*)。

仅当你无法以 UTF-8 编码存储文件并且你希望 Git 能够将内容作为文本处理时,才使用 working-tree-encoding 属性。

例如,如果你的 **.ps1* 文件是带字节顺序标记 (BOM) 的 UTF-16 编码,并且你希望 Git 根据你的平台执行自动行尾转换,请使用以下属性。

*.ps1		text working-tree-encoding=UTF-16

如果你的 **.ps1* 文件是不带 BOM 的 UTF-16 小端编码,并且你希望 Git 在工作目录中使用 Windows 行尾符,请使用以下属性(如果要使用带 BOM 的 UTF-16 小端,请使用 UTF-16LE-BOM 而不是 UTF-16LE)。 请注意,如果使用 working-tree-encoding 属性以避免歧义,强烈建议使用 eol 显式定义行尾符。

*.ps1		text working-tree-encoding=UTF-16LE eol=crlf

你可以使用以下命令获取平台上所有可用编码的列表

iconv --list

如果你不知道文件的编码,则可以使用 file 命令来猜测编码

file foo.ps1

ident

当为路径设置属性 ident 时,Git 会在检出时将 blob 对象中的 $Id$ 替换为 $Id:,后跟 40 个字符的十六进制 blob 对象名称,后跟一个美元符号 $。 工作树文件中任何以 $Id: 开头并以 $ 结尾的字节序列都会在签入时被替换为 $Id$

filter

可以将 filter 属性设置为一个字符串值,该值指定配置中定义的筛选器驱动程序。

筛选器驱动程序由 clean 命令和 smudge 命令组成,两者都可以不指定。 检出时,如果指定了 smudge 命令,则该命令会从标准输入接收 blob 对象,并使用其标准输出更新工作树文件。 类似地,clean 命令用于在签入时转换工作树文件的内容。 默认情况下,这些命令仅处理单个 blob 并终止。 如果使用长时间运行的 process 筛选器代替 clean 和/或 smudge 筛选器,则 Git 可以在单个 Git 命令的整个生命周期内使用单个筛选器命令调用处理所有 blob,例如 git add --all。 如果配置了长时间运行的 process 筛选器,则它始终优先于配置的单个 blob 筛选器。 有关用于与 process 筛选器通信的协议的描述,请参阅下面的部分。

内容过滤的一个用途是将内容调整为更方便平台、文件系统和用户使用的形式。 对于此操作模式,这里的关键短语是 “更方便”,而不是 “将不可用的东西变成可用”。 换句话说,目的是如果有人取消设置筛选器驱动程序定义,或者没有适当的筛选器程序,则该项目仍然可用。

内容过滤的另一个用途是存储无法直接在仓库中使用的内容(例如,指向 Git 外部存储的真实内容的 UUID,或加密内容),并在检出时将其转换为可用形式(例如,下载外部内容,或解密加密内容)。

这两种筛选器的行为不同,默认情况下,筛选器被视为前者,即将内容调整为更方便的形式。 配置中缺少筛选器驱动程序定义,或筛选器驱动程序以非零状态退出,这不是错误,而是使筛选器成为空操作传递。

你可以通过将 filter.<driver>.required 配置变量设置为 true 来声明筛选器将本身不可用的内容转换为可用内容。

注意:每当更改 clean 筛选器时,应重新规范化仓库:$ git add --renormalize .

例如,在 .gitattributes 中,你将为路径分配 filter 属性。

*.c	filter=indent

然后,你将在 .git/config 中定义 “filter.indent.clean” 和 “filter.indent.smudge” 配置,以指定一对命令来修改 C 程序的内容,当源文件被签入(运行 “clean”)和检出时(由于命令是 “cat”,因此不进行更改)。

[filter "indent"]
	clean = indent
	smudge = cat

为了获得最佳效果,如果再次运行 clean,则不应进一步更改其输出(“clean→clean” 应等效于 “clean”),并且多个 smudge 命令不应更改 clean 的输出(“smudge→smudge→clean” 应等效于 “clean”)。 请参阅下面的合并部分。

“indent” 筛选器在这方面表现良好:它不会修改已经正确缩进的输入。 在这种情况下,缺少 smudge 筛选器意味着 clean 筛选器必须接受其自身的输出而不进行修改。

如果筛选器必须成功才能使存储的内容可用,则可以在配置中声明该筛选器为 required

[filter "crypt"]
	clean = openssl enc ...
	smudge = openssl enc -d ...
	required

筛选器命令行上的序列 "%f" 被替换为筛选器正在处理的文件的名称。 筛选器可能会在关键字替换中使用它。 例如

[filter "p4"]
	clean = git-p4-filter --clean %f
	smudge = git-p4-filter --smudge %f

请注意,"%f" 是正在处理的路径的名称。 根据正在筛选的版本,磁盘上相应的文件可能不存在,或者可能具有不同的内容。 因此,smudge 和 clean 命令不应尝试访问磁盘上的文件,而只能充当提供给它们的标准输入内容的筛选器。

长时间运行的筛选器进程

如果筛选器命令(字符串值)通过 filter.<driver>.process 定义,则 Git 可以在单个 Git 命令的整个生命周期内使用单个筛选器调用处理所有 blob。 这是通过使用长时间运行的进程协议(在 Documentation/technical/long-running-process-protocol.adoc 中描述)实现的。

当 Git 遇到第一个需要清理或涂抹的文件时,它会启动筛选器并执行握手。 在握手中,Git 发送的欢迎消息是 "git-filter-client",仅支持版本 2,支持的功能包括 "clean"、"smudge" 和 "delay"。

之后,Git 会发送一个以刷新数据包结尾的 “key=value” 对列表。 该列表将至少包含筛选器命令(基于支持的功能)和要筛选的相对于仓库根目录的文件路径名。 紧接着刷新数据包,Git 会将内容拆分为零个或多个 pkt-line 数据包和一个刷新数据包,以终止内容。 请注意,筛选器在收到内容和最终刷新数据包之前不得发送任何响应。 另请注意,“key=value” 对的 “value” 可以包含 “=” 字符,而键永远不会包含该字符。

packet:          git> command=smudge
packet:          git> pathname=path/testfile.dat
packet:          git> 0000
packet:          git> CONTENT
packet:          git> 0000

过滤器应返回以 flush 数据包结尾的 "key=value" 键值对列表。如果过滤器没有遇到问题,则该列表必须包含一个 "success" 状态。 在这些数据包之后,过滤器应以零个或多个 pkt-line 数据包的形式发送内容,并在最后发送一个 flush 数据包。 最后,应返回第二个以 flush 数据包结尾的 "key=value" 键值对列表。 过滤器可以在第二个列表中更改状态,也可以保持状态不变,使用空列表。 请注意,无论如何,空列表都必须以 flush 数据包结尾。

packet:          git< status=success
packet:          git< 0000
packet:          git< SMUDGED_CONTENT
packet:          git< 0000
packet:          git< 0000  # empty list, keep "status=success" unchanged!

如果结果内容为空,则过滤器应返回 "success" 状态和一个 flush 数据包,以表示内容为空。

packet:          git< status=success
packet:          git< 0000
packet:          git< 0000  # empty content!
packet:          git< 0000  # empty list, keep "status=success" unchanged!

如果过滤器无法或不想处理内容,则应返回 "error" 状态。

packet:          git< status=error
packet:          git< 0000

如果过滤器在处理过程中遇到错误,则可以在发送(部分或全部)内容后发送 "error" 状态。

packet:          git< status=success
packet:          git< 0000
packet:          git< HALF_WRITTEN_ERRONEOUS_CONTENT
packet:          git< 0000
packet:          git< status=error
packet:          git< 0000

如果过滤器无法或不想处理内容,以及 Git 进程生命周期内的任何未来内容,则应在协议中的任何时候返回 "abort" 状态。

packet:          git< status=abort
packet:          git< 0000

如果设置了 "error"/"abort" 状态,Git 既不会停止也不会重启过滤器进程。 但是,Git 会根据 filter.<driver>.required 标志设置其退出代码,从而模仿 filter.<driver>.clean / filter.<driver>.smudge 机制的行为。

如果过滤器在通信过程中死亡,或者不遵守协议,那么 Git 将停止该过滤器进程,并使用下一个需要处理的文件重新启动它。根据 filter.<driver>.required 标志,Git 会将其解释为错误。

延迟

如果过滤器支持 "delay" 功能,那么 Git 可以在过滤器命令和路径名之后发送 "can-delay" 标志。 此标志表示过滤器可以通过不返回内容,而是返回 "delayed" 状态和一个 flush 数据包来延迟过滤当前 blob(例如,为了补偿网络延迟)。

packet:          git> command=smudge
packet:          git> pathname=path/testfile.dat
packet:          git> can-delay=1
packet:          git> 0000
packet:          git> CONTENT
packet:          git> 0000
packet:          git< status=delayed
packet:          git< 0000

如果过滤器支持 "delay" 功能,那么它必须支持 "list_available_blobs" 命令。 如果 Git 发送此命令,那么过滤器应返回一个路径名列表,表示之前已延迟但现在可用的 blob。 该列表必须以 flush 数据包结尾,然后是也是以 flush 数据包结尾的 "success" 状态。 如果尚未提供延迟路径的任何 blob,则过滤器应阻止响应,直到至少一个 blob 可用。 过滤器可以通过发送空列表来告诉 Git 它没有更多延迟的 blob。 一旦过滤器返回一个空列表,Git 就会停止询问。 Git 此时没有收到的所有 blob 都被认为是丢失的,并将导致错误。

packet:          git> command=list_available_blobs
packet:          git> 0000
packet:          git< pathname=path/testfile.dat
packet:          git< pathname=path/otherfile.dat
packet:          git< 0000
packet:          git< status=success
packet:          git< 0000

在 Git 收到路径名后,它将再次请求相应的 blob。 这些请求包含一个路径名和一个空内容部分。 过滤器应以上述通常方式返回经过涂抹的内容。

packet:          git> command=smudge
packet:          git> pathname=path/testfile.dat
packet:          git> 0000
packet:          git> 0000  # empty content!
packet:          git< status=success
packet:          git< 0000
packet:          git< SMUDGED_CONTENT
packet:          git< 0000
packet:          git< 0000  # empty list, keep "status=success" unchanged!

示例

可以在 Git 核心仓库的 contrib/long-running-filter/example.pl 中找到一个长时间运行的过滤器演示实现。 如果您开发自己的长时间运行的过滤器进程,那么 GIT_TRACE_PACKET 环境变量对于调试非常有用(请参阅 git[1])。

请注意,您不能将现有的 filter.<driver>.cleanfilter.<driver>.smudge 命令与 filter.<driver>.process 一起使用,因为前两者使用不同的进程间通信协议。

签入/签出属性之间的交互

在签入代码路径中,工作区文件首先使用 filter 驱动程序进行转换(如果已指定并定义了相应的驱动程序),然后使用 ident 处理结果(如果已指定),最后使用 text 处理结果(同样,如果已指定并适用)。

在签出代码路径中,blob 内容首先使用 text 进行转换,然后使用 ident,并提供给 filter

合并具有不同签入/签出属性的分支

如果您已将属性添加到某个文件,导致该文件的规范存储库格式发生更改,例如添加了 clean/smudge 过滤器或 text/eol/ident 属性,那么合并任何没有该属性的内容通常会导致合并冲突。

为了防止这些不必要的合并冲突,可以告诉 Git 对每个需要三向内容合并的文件的所有三个阶段运行虚拟签出和签入,方法是设置 merge.renormalize 配置变量。 这可以防止因签入转换引起的更改在合并已转换文件和未转换文件时导致虚假的合并冲突。

只要 "smudge→clean" 产生的结果与 "clean" 相同,即使在已经经过涂抹的文件上,此策略也会自动解决所有与过滤器相关的冲突。 以这种方式不执行操作的过滤器可能会导致必须手动解决的额外合并冲突。

生成 diff 文本

diff

属性 diff 影响 Git 如何为特定文件生成差异。 它可以告诉 Git 是为路径生成文本补丁,还是将路径视为二进制文件。 它还可以影响在 hunk 标头 @@ -k,l +n,m @@ 行上显示哪一行,告诉 Git 使用外部命令生成差异,或要求 Git 在生成差异之前将二进制文件转换为文本格式。

设置

设置了 diff 属性的路径被视为文本,即使它们包含通常永远不会出现在文本文件中的字节值,例如 NUL。

取消设置

取消设置了 diff 属性的路径将生成 Binary files differ(或者二进制补丁,如果启用了二进制补丁)。

未指定

未指定 diff 属性的路径首先会检查其内容,如果它看起来像文本并且小于 core.bigFileThreshold,则将其视为文本。 否则它会生成 Binary files differ

字符串

差异使用指定的 diff 驱动程序显示。 每个驱动程序可以指定一个或多个选项,如下节所述。 diff 驱动程序 "foo" 的选项由 Git 配置文件中 "diff.foo" 部分的配置变量定义。

定义外部差异驱动程序

差异驱动程序的定义是在 gitconfig 中完成的,而不是在 gitattributes 文件中,所以严格来说,本手册页是不应该谈论它的。 但是……

要定义一个外部差异驱动程序 jcdiff,请向您的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)添加一个像这样的部分

[diff "jcdiff"]
	command = j-c-diff

当 Git 需要显示 diff 属性设置为 jcdiff 的路径的差异时,它会使用上述配置调用您指定的命令,即 j-c-diff,带有 7 个参数,就像调用 GIT_EXTERNAL_DIFF 程序一样。 有关详细信息,请参阅 git[1]

如果程序能够忽略某些更改(类似于 git diff --ignore-space-change),那么也请将选项 trustExitCode 设置为 true。 如果它发现重大更改,则预计返回退出代码 1,如果没有发现,则返回 0。

设置内部差异算法

差异算法可以通过 diff.algorithm 配置键设置,但有时按路径设置差异算法可能会很有用。 例如,可能希望对 .json 文件使用 minimal 差异算法,对 .c 文件使用 histogram,等等,而无需每次都通过命令行传入算法。

首先,在 .gitattributes 中,为路径分配 diff 属性。

*.json diff=<name>

然后,定义一个 "diff.<name>.algorithm" 配置来指定差异算法,从 myerspatienceminimalhistogram 中选择。

[diff "<name>"]
  algorithm = histogram

此差异算法适用于面向用户的差异输出,如 git-diff(1)、git-show(1),也用于 --stat 输出。 合并机制将不会使用通过此方法设置的差异算法。

注意
如果为具有 diff=<name> 属性的路径定义了 diff.<name>.command,则它将作为外部差异驱动程序执行(参见上文),添加 diff.<name>.algorithm 没有效果,因为该算法不会传递给外部差异驱动程序。

定义自定义 hunk 标头

文本差异输出中的每个更改组(称为 "hunk")都以以下形式的行作为前缀

@@ -k,l +n,m @@ TEXT

这被称为hunk 标头。"TEXT" 部分默认是一行,以字母、下划线或美元符号开头; 这与 GNU diff -p 输出使用的相匹配。 然而,这种默认选择不适合某些内容,您可以使用自定义模式来进行选择。

首先,在 .gitattributes 中,您将为路径分配 diff 属性。

*.tex	diff=tex

然后,您将定义一个 "diff.tex.xfuncname" 配置来指定一个正则表达式,该表达式与您希望显示为 hunk 标头 "TEXT" 的行匹配。 向您的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)添加一个像这样的部分

[diff "tex"]
	xfuncname = "^(\\\\(sub)*section\\{.*)$"

注意:配置文件的解析器会吃掉单层反斜杠,因此您需要将反斜杠加倍; 上面的模式选择一个以反斜杠开头的行,以及零个或多个出现的 sub,后跟 section,后跟左大括号,直到行尾。

有一些内置模式可以简化此操作,而 tex 就是其中之一,因此您不必在配置文件中编写上述内容(您仍然需要通过属性机制,通过 .gitattributes 来启用此功能)。 以下内置模式可用

  • ada 适用于 Ada 语言的源代码。

  • bash 适用于 Bourne-Again SHell 语言的源代码。 涵盖了 POSIX shell 函数定义的超集。

  • bibtex 适用于包含 BibTeX 编码引用的文件。

  • cpp 适用于 C 和 C++ 语言的源代码。

  • csharp 适用于 C# 语言的源代码。

  • css 适用于层叠样式表。

  • dts 适用于设备树 (DTS) 文件。

  • elixir 适用于 Elixir 语言的源代码。

  • fortran 适用于 Fortran 语言的源代码。

  • fountain 适用于 Fountain 文档。

  • golang 适用于 Go 语言的源代码。

  • html 适用于 HTML/XHTML 文档。

  • java 适用于 Java 语言的源代码。

  • kotlin 适用于 Kotlin 语言的源代码。

  • markdown 适用于 Markdown 文档。

  • matlab 适用于 MATLAB 和 Octave 语言的源代码。

  • objc 适用于 Objective-C 语言的源代码。

  • pascal 适用于 Pascal/Delphi 语言的源代码。

  • perl 适用于 Perl 语言的源代码。

  • php 适用于 PHP 语言的源代码。

  • python 适用于 Python 语言的源代码。

  • ruby 适用于 Ruby 语言的源代码。

  • rust 适用于 Rust 语言的源代码。

  • scheme 适用于 Scheme 语言的源代码。

  • tex 适用于 LaTeX 文档的源代码。

自定义单词差异

你可以自定义 git diff --word-diff 用来分割一行中单词的规则,通过在 "diff.*.wordRegex" 配置变量中指定合适的正则表达式。例如,在 TeX 中,反斜杠后跟一系列字母组成一个命令,但多个这样的命令可以不带空格地运行在一起。要分隔它们,可以在你的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)中使用如下的正则表达式:

[diff "tex"]
	wordRegex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+"

为前一节中列出的所有语言提供了内置模式。

执行二进制文件的文本差异

有时,希望看到一些二进制文件的文本转换版本的差异。 例如,可以将文字处理器文档转换为 ASCII 文本表示形式,并显示该文本的差异。 即使此转换会丢失一些信息,但生成的差异对于人工查看仍然很有用(但不能直接应用)。

textconv 配置选项用于定义执行此类转换的程序。 该程序应采用一个参数,即要转换的文件的名称,并在 stdout 上生成结果文本。

例如,要显示文件的 exif 信息而不是二进制信息(假设你已安装 exif 工具),请将以下部分添加到你的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件):

[diff "jpg"]
	textconv = exif
注意
文本转换通常是单向转换;在此示例中,我们失去了实际的图像内容,而只关注文本数据。 这意味着 textconv 生成的差异适合应用。 因此,只有 git diffgit log 命令系列(即,log,whatchanged,show)才会执行文本转换。 git format-patch 永远不会生成此输出。 如果你想向某人发送二进制文件的文本转换差异(例如,因为它能快速传达你所做的更改),则应单独生成它,并将其作为注释附加到你可能发送的常规二进制差异。

由于文本转换可能很慢,尤其是在使用 git log -p 进行大量文本转换时,Git 提供了一种机制来缓存输出并在以后的差异中使用它。 要启用缓存,请在差异驱动程序的配置中设置 "cachetextconv" 变量。 例如:

[diff "jpg"]
	textconv = exif
	cachetextconv = true

这将无限期地缓存对每个 blob 运行 "exif" 的结果。 如果你更改了差异驱动程序的 textconv 配置变量,Git 将自动使缓存条目无效并重新运行 textconv 过滤器。 如果你想手动使缓存无效(例如,因为你的 "exif" 版本已更新,现在可以生成更好的输出),则可以使用 git update-ref -d refs/notes/textconv/jpg 手动删除缓存(其中 "jpg" 是差异驱动程序的名称,如上例所示)。

选择 textconv 还是外部差异

如果要在存储库中显示二进制或特殊格式 blob 之间的差异,可以选择使用外部 diff 命令,或者使用 textconv 将它们转换为可差异的文本格式。 你选择哪种方法取决于你的具体情况。

使用外部 diff 命令的优点是灵活性。 你不必局限于查找面向行的更改,也不需要输出类似于统一差异。 你可以自由地找到并报告最适合你的数据格式的更改。

相比之下,textconv 的限制要大得多。 你提供数据到面向行的文本格式的转换,Git 使用其常规差异工具来生成输出。 选择此方法有几个优点:

  1. 易于使用。 编写二进制到文本转换通常比执行你自己的差异要简单得多。 在许多情况下,现有程序可以用作 textconv 过滤器(例如,exif、odt2txt)。

  2. Git 差异功能。 通过仅执行转换步骤,你仍然可以使用 Git 的许多差异功能,包括着色、单词差异以及合并的组合差异。

  3. 缓存。 Textconv 缓存可以加快重复差异的速度,例如你可能通过运行 git log -p 触发的差异。

将文件标记为二进制文件

Git 通常通过检查内容的开头来正确猜测 blob 是否包含文本或二进制数据。 但是,有时你可能想要覆盖它的决定,要么是因为 blob 在文件的后面包含二进制数据,要么是因为内容虽然在技术上由文本字符组成,但对于人类读者来说是不透明的。 例如,许多 postscript 文件仅包含 ASCII 字符,但会产生嘈杂且无意义的差异。

将文件标记为二进制文件的最简单方法是取消设置 .gitattributes 文件中的 diff 属性:

*.ps -diff

这将导致 Git 生成 Binary files differ(如果启用了二进制补丁,则生成二进制补丁)而不是常规差异。

但是,也可能需要指定其他差异驱动程序属性。 例如,你可能想要使用 textconv 将 postscript 文件转换为 ASCII 表示形式以供人工查看,但在其他情况下将其视为二进制文件。 你不能同时指定 -diffdiff=ps 属性。 解决方案是使用 diff.*.binary 配置选项:

[diff "ps"]
  textconv = ps2ascii
  binary = true

执行三向合并

merge

当在 git merge 以及其他命令如 git revertgit cherry-pick 中需要进行文件级别的合并时,merge 属性会影响如何合并文件的三个版本。

设置

内置的三向合并驱动程序用于以类似于 RCS 套件的 *merge* 命令的方式合并内容。 这适用于普通的文本文件。

取消设置

从当前分支获取版本作为临时合并结果,并声明合并存在冲突。 这适用于没有明确定义的合并语义的二进制文件。

未指定

默认情况下,这使用与设置 merge 属性时相同的内置三向合并驱动程序。 但是,merge.default 配置变量可以命名不同的合并驱动程序,用于未指定 merge 属性的路径。

字符串

三向合并使用指定的自定义合并驱动程序执行。 可以通过请求 "text" 驱动程序来显式指定内置的三向合并驱动程序;可以使用 "binary" 请求内置的 "采用当前分支" 驱动程序。

内置合并驱动程序

定义了一些内置的低级合并驱动程序,可以通过 merge 属性请求。

text

用于文本文件的常规三向文件级别合并。 冲突区域标有冲突标记 <<<<<<<=======>>>>>>>。 来自你的分支的版本出现在 ======= 标记之前,来自合并分支的版本出现在 ======= 标记之后。

binary

在工作树中保留来自你的分支的版本,但将路径保留在冲突状态,以便用户进行整理。

union

对文本文件运行三向文件级别合并,但从两个版本中获取行,而不是留下冲突标记。 这往往会使结果文件中的添加行以随机顺序排列,用户应验证结果。 如果你不了解其含义,请不要使用此选项。

定义自定义合并驱动程序

合并驱动程序的定义在 .git/config 文件中完成,而不是在 gitattributes 文件中完成,因此严格来说,本手册页不适合讨论它。 然而……

要定义自定义合并驱动程序 filfre,请将一个部分添加到你的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件),如下所示:

[merge "filfre"]
	name = feel-free merge driver
	driver = filfre %O %A %B %L %P
	recursive = binary

merge.*.name 变量为驱动程序提供了一个人类可读的名称。

merge.*.driver 变量的值用于构造一个命令来运行公共祖先的版本 (%O)、当前版本 (%A) 和其他分支的版本 (%B)。 构建命令行时,这三个令牌将替换为包含这些版本内容的临时文件的名称。 此外,%L 将替换为冲突标记大小(见下文)。

合并驱动程序应通过覆盖操作将合并结果保留在以 %A 命名的文件中,如果能够干净地合并它们,则以零状态退出;如果存在冲突,则以非零状态退出。 当驱动程序崩溃(例如,被 SEGV 终止)时,预计它将以高于 128 的非零状态退出,在这种情况下,合并会导致失败(这与产生冲突不同)。

merge.*.recursive 变量指定在调用合并驱动程序以进行公共祖先之间的内部合并时,在有多个公共祖先的情况下,要使用哪个其他合并驱动程序。 如果未指定,则驱动程序本身将用于内部合并和最终合并。

合并驱动程序可以通过占位符 %P 了解合并结果将存储在其中的路径名。 可以分别使用 %S%X%Y 传递用于公共祖先、本地头和其他头的冲突标签。

conflict-marker-size

此属性控制在冲突合并期间留在工作树文件中的冲突标记的长度。 只有正整数才具有有意义的效果。

例如,.gitattributes 中的这一行可用于告诉合并机制,当合并文件 Documentation/git-merge.adoc 导致冲突时,留下更长的(而不是通常的 7 个字符长)冲突标记。

Documentation/git-merge.adoc	conflict-marker-size=32

检查空格错误

whitespace

core.whitespace 配置变量允许你定义 *diff* 和 *apply* 应该将什么视为空格错误,适用于项目中的所有路径(请参阅 git-config[1])。 此属性为你提供每个路径更精细的控制。

设置

注意 Git 已知的所有类型的潜在空格错误。 Tab 宽度取自 core.whitespace 配置变量的值。

取消设置

不要将任何内容视为错误。

未指定

使用 core.whitespace 配置变量的值来决定将哪些内容视为错误。

字符串

指定一个逗号分隔的常见空白问题列表,格式与 core.whitespace 配置变量相同。

创建归档

export-ignore

具有 export-ignore 属性的文件和目录不会被添加到归档文件中。

export-subst

如果文件设置了 export-subst 属性,那么 Git 在将此文件添加到归档时将展开几个占位符。展开取决于提交 ID 的可用性,即如果 git-archive[1] 被赋予一个树而不是一个提交或标签,那么将不会进行替换。这些占位符与 git-log[1]--pretty=format: 选项相同,除了它们需要像这样被包裹起来:文件中的 $Format:PLACEHOLDERS$。例如,字符串 $Format:%H$ 将被提交哈希替换。但是,每个归档文件只展开一个 %(describe) 占位符,以避免拒绝服务攻击。

打包对象

delta

对于路径的属性 delta 设置为 false 的 blob,将不会尝试增量压缩。

在 GUI 工具中查看文件

encoding

此属性的值指定 GUI 工具(例如 gitk[1]git-gui[1])应该用来显示相关文件内容的字符编码。请注意,由于性能考虑,除非您手动在其选项中启用每个文件的编码,否则 gitk[1] 不会使用此属性。

如果未设置此属性或其值无效,则将改用 gui.encoding 配置变量的值(请参阅 git-config[1])。

使用宏属性

您不希望将任何行尾转换应用于您跟踪的任何二进制文件,也不希望生成文本差异。你需要指定,例如:

*.jpg -text -diff

但是当您有很多属性时,这可能会变得很麻烦。 使用宏属性,您可以定义一个属性,当设置该属性时,也会同时设置或取消设置许多其他属性。 系统知道一个内置的宏属性,binary

*.jpg binary

设置 "binary" 属性也会取消设置如上的 "text" 和 "diff" 属性。 请注意,宏属性只能 "设置",尽管设置一个属性可能会产生设置或取消设置其他属性,甚至将其他属性返回到 "未指定" 状态的效果。

定义宏属性

自定义宏属性只能在顶级 gitattributes 文件 ($GIT_DIR/info/attributes, 工作树顶层的 .gitattributes 文件,或者全局或系统范围的 gitattributes 文件) 中定义,而不能在工作树子目录中的 .gitattributes 文件中定义。 内置宏属性 "binary" 等同于

[attr]binary -diff -merge -text

注释

在访问工作树中的 .gitattributes 文件时,Git 不会遵循符号链接。 这样可以保持从索引或树以及从文件系统访问文件时行为的一致性。

示例

如果您有以下三个 gitattributes 文件

(in $GIT_DIR/info/attributes)

a*	foo !bar -baz

(in .gitattributes)
abc	foo bar baz

(in t/.gitattributes)
ab*	merge=filfre
abc	-foo -bar
*.c	frotz

赋予路径 t/abc 的属性计算如下

  1. 通过检查 t/.gitattributes(它与所讨论的路径位于同一目录中),Git 发现第一行匹配。 merge 属性被设置。 它还发现第二行匹配,并且属性 foobar 被取消设置。

  2. 然后它检查 .gitattributes (它位于父目录中),并发现第一行匹配,但是 t/.gitattributes 文件已经决定了应该如何将 mergefoobar 属性赋予此路径,因此它保留 foobar 取消设置的状态。 属性 baz 被设置。

  3. 最后,它检查 $GIT_DIR/info/attributes。 此文件用于覆盖树内设置。 第一行是一个匹配,并且 foo 被设置,bar 恢复到未指定状态,并且 baz 被取消设置。

因此,t/abc 的属性分配变为

foo	set to true
bar	unspecified
baz	set to false
merge	set to string value "filfre"
frotz	unspecified

GIT

git[1] 套件的一部分

scroll-to-top