Git
英文 ▾ 主题 ▾ 最新版本 ▾ gitattributes 最后更新于 2.46.0

名称

gitattributes - 定义每个路径的属性

概要

$GIT_DIR/info/attributes, .gitattributes

描述

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

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文件中。

有时您需要将路径的属性设置覆盖到未指定状态。这可以通过列出以感叹号!为前缀的属性名称来完成。

保留的内置_* 属性

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。相反,当从索引复制到工作目录时,其行尾可能会根据eol属性、Git 配置和平台从 LF 转换为 CRLF(请参阅下面对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 网页前端默认情况下不会可视化这些文件的内容。

在这些情况下,您可以使用 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 的客户端将检出 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 checkoutgit add)的速度。

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

例如,如果您的 *.ps1 文件以 UTF-16 编码(带 BOM),并且您希望 Git 根据您的平台执行自动行结束符转换,则使用以下属性。

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

如果您的 *.ps1 文件以 UTF-16 小端编码(不带 BOM),并且您希望 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 add --all。如果配置了长期运行的 process 过滤器,则它始终优先于配置的单个 blob 过滤器。有关用于与 process 过滤器通信的协议的说明,请参见下面的部分。

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

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

这两个过滤器行为不同,默认情况下,过滤器被视为前者,将内容整理成更方便的形状。配置中缺少过滤器驱动程序定义,或退出状态为非零的过滤器驱动程序,不是错误,而是使过滤器成为无操作的直通。

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

注意:每次更改干净过滤器时,都应重新规范化仓库:$ 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 可以使用单个过滤器调用处理所有 Blob,以持续整个 Git 命令的生命周期。这是通过使用长时间运行的进程协议(在 technical/long-running-process-protocol.txt 中描述)实现的。

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

之后,Git 发送一个以刷新数据包结尾的“key=value”对列表。该列表将至少包含过滤器命令(基于支持的功能)和相对于仓库根目录的文件路径名。在刷新数据包之后,Git 发送以零个或多个 pkt-line 数据包和一个刷新数据包终止的内容。请注意,过滤器在接收内容和最终刷新数据包之前不得发送任何响应。另请注意,“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”对列表进行响应。如果过滤器没有遇到问题,则该列表必须包含“success”状态。在这些数据包之后,预计过滤器将以零个或多个 pkt-line 数据包发送内容,并在末尾使用刷新数据包。最后,预计将出现第二个以刷新数据包结尾的“key=value”对列表。过滤器可以在第二个列表中更改状态或保持状态不变并使用空列表。请注意,无论如何,空列表都必须以刷新数据包结尾。

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

如果结果内容为空,则预计过滤器将以“success”状态和一个刷新数据包进行响应,以表示空内容。

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”和刷新数据包来延迟过滤当前 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。该列表必须以刷新数据包结尾,然后是“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 属性,则合并任何未设置该属性的位置通常会导致合并冲突。

为了防止这些不必要的合并冲突,可以通过设置 merge.renormalize 配置变量,让 Git 在解决三方合并时运行所有三个文件阶段的虚拟检出和检入。这可以防止由检入转换引起的更改在已转换文件与未转换文件合并时导致虚假合并冲突。

只要“smudge→clean”的结果与“clean”相同,即使在已经涂抹的文件上也是如此,此策略将自动解决所有与过滤器相关的冲突。不以这种方式运行的过滤器可能会导致必须手动解决的其他合并冲突。

生成 diff 文本

diff

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

已设置

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

未设置

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

未指定

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

字符串

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

定义外部 diff 驱动程序

diff 驱动程序的定义是在 gitconfig 中完成的,而不是 gitattributes 文件中,因此严格来说,本手册页谈论它是不合适的。但是…​

要定义外部 diff 驱动程序 jcdiff,请向您的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)添加如下所示的部分

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

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

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

设置内部 diff 算法

可以通过 diff.algorithm 配置键设置 diff 算法,但有时可能需要按路径设置 diff 算法。例如,可能希望对 .json 文件使用 minimal diff 算法,对 .c 文件使用 histogram 算法,依此类推,而无需每次都通过命令行传递算法。

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

*.json diff=<name>

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

[diff "<name>"]
  algorithm = histogram

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

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

定义自定义合并块标题

文本 diff 输出中的每一组更改(称为“合并块”)都以一行开头,格式如下:

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

这称为合并块标题。“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

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

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

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

执行二进制文件的文本 diff

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

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

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

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

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

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

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

选择 textconv 与外部 diff

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

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

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

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

  2. Git diff 功能。通过仅自己执行转换步骤,您仍然可以使用 Git 的许多 diff 功能,包括颜色化、单词 diff 和合并的组合 diff。

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

将文件标记为二进制文件

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

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

*.ps -diff

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

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

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

执行三方合并

merge

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

Set

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

Unset

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

Unspecified

默认情况下,它使用与设置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.txt导致冲突时留下更长(而不是通常的 7 个字符长)的冲突标记。

Documentation/git-merge.txt	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