简体中文 ▾ 主题 ▾ 最新版本 ▾ gitattributes 最后更新于 2.50.0

名称

gitattributes - 为每个路径定义属性

概要

$GIT_DIR/info/attributes, .gitattributes

描述

一个 gitattributes 文件是一个简单的文本文件,它为路径名指定 属性

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

pattern attr1 attr2 ...

即,一个模式后面跟着一个属性列表,由空格分隔。前导和尾随空格被忽略。以#开头的行被忽略。以双引号开头的模式以 C 风格引用。当模式匹配到相关路径时,该行中列出的属性将被赋予该路径。

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

设置

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

未设置

路径具有特殊值“false”的属性;这通过在属性列表中列出属性名称并在其前面加上一个破折号 - 来指定。

设置为一个值

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

未指定

没有模式匹配路径,也没有说明路径是否具有该属性,该路径的属性被认为是未指定的。

当多个模式匹配同一路径时,后来的行会覆盖早前的行。这种覆盖是针对每个属性进行的。

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

  • 负模式是被禁止的

  • 匹配目录的模式不会递归地匹配该目录内的路径(因此,在 attributes 文件中使用尾随斜杠 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 文件中。

有时您需要将路径的某个属性设置覆盖为 未指定 状态。这可以通过在属性名称前加上一个感叹号 ! 来实现。

保留的 BUILTIN_* 属性

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,并且您使用启用了 working-tree-encoding 的 Git 客户端添加 foo.ps1,那么 foo.ps1 将在内部存储为 UTF-8。一个没有 working-tree-encoding 支持的客户端将以 UTF-8 编码的文件检出 foo.ps1。这通常会给该文件的用户带来麻烦。

    如果不支持 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 checkoutgit 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 可以通过一次过滤器命令调用处理所有 blob,该调用持续单个 Git 命令的整个生命周期,例如 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 会发送一个以 flush 包终止的“key=value”对列表。该列表将至少包含过滤器命令(基于支持的功能)以及相对于仓库根目录的文件路径。紧接着 flush 包,Git 会发送零个或多个 pkt-line 包以及一个 flush 包来终止内容。请注意,在收到内容和最终的 flush 包之前,过滤器不得发送任何响应。另请注意,“key=value”对的“value”可以包含“=”字符,而 key 永远不会包含该字符。

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

过滤器应以“key=value”对列表响应,并以 flush 包终止。如果过滤器没有遇到问题,则该列表必须包含“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

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

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

延迟

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

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 包终止,后跟一个“success”状态,该状态也以一个 flush 包终止。如果延迟路径的 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 属性,那么合并任何没有这些属性的地方通常会导致合并冲突。

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

只要“smudge→clean”的结果与“clean”的结果相同,即使文件已经被涂抹,这个策略也将自动解决所有与过滤器相关的冲突。不以这种方式工作的过滤器可能会导致必须手动解决的额外合并冲突。

生成差异文本

diff

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

设置

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

未设置

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

未指定

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

字符串

差异使用指定的差异驱动程序显示。每个驱动程序可以指定一个或多个选项,如下节所述。差异驱动程序“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”)都以以下形式的行作为前缀

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

这被称为 hunk header(块头)。“TEXT”部分默认是以字母、下划线或美元符号开头的行;这与 GNU diff -p 输出使用的内容匹配。然而,此默认选择不适用于某些内容,您可以使用自定义模式进行选择。

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

*.tex	diff=tex

然后,您将定义 "diff.tex.xfuncname" 配置以指定一个正则表达式,该正则表达式匹配您希望作为块头“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 文档源代码。

自定义词差异

您可以通过在“diff.*.wordRegex”配置变量中指定合适的正则表达式来定制 git diff --word-diff 用于将一行拆分为单词的规则。例如,在 TeX 中,反斜杠后跟字母序列构成一个命令,但几个这样的命令可以连续运行而无需中间空格。要将它们分开,请在您的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)中使用如下正则表达式

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

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

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

有时,需要查看某些二进制文件文本转换版本的差异。例如,文字处理文档可以转换为 ASCII 文本表示,并显示文本的差异。尽管这种转换会丢失一些信息,但生成的差异对于人类查看很有用(但无法直接应用)。

textconv 配置选项用于定义执行此类转换的程序。该程序应接受一个参数,即要转换的文件名,并将结果文本输出到标准输出。

例如,要显示文件的 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 之间的差异,您可以选择使用外部差异命令,或者使用 textconv 将它们转换为可差异的文本格式。选择哪种方法取决于您的具体情况。

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

相比之下,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

merge 属性影响在 git merge 和其他命令(例如 git revertgit cherry-pick)期间需要文件级合并时,文件的三个版本如何合并。

设置

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

未设置

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

未指定

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

字符串

使用指定的自定义合并驱动程序执行三向合并。内置的三向合并驱动程序可以通过请求“text”驱动程序显式指定;内置的“take the current branch”驱动程序可以通过“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 配置变量允许您定义 diffapply 应将哪些空白视为项目所有路径的错误(参见 git-config[1])。此属性使您能够对每个路径进行更精细的控制。

设置

注意 Git 已知的所有潜在空白错误类型。制表符宽度取自 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

注意事项

Git 在访问工作树中的 .gitattributes 文件时不会跟随符号链接。这使得从索引或树(而不是从文件系统)访问文件时的行为保持一致。

示例

如果您有这三个 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