设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.50.1 → 2.52.0 无更改
-
2.50.0
2025-06-16
- 2.49.1 无更改
-
2.49.0
2025-03-14
- 2.46.1 → 2.48.2 无更改
-
2.46.0
2024-07-29
- 2.44.1 → 2.45.4 无更改
-
2.44.0
2024-02-23
- 2.43.1 → 2.43.7 无更改
-
2.43.0
2023-11-20
- 2.42.1 → 2.42.4 无更改
-
2.42.0
2023-08-21
- 2.41.1 → 2.41.3 无更改
-
2.41.0
2023-06-01
- 2.40.1 → 2.40.4 无更改
-
2.40.0
2023-03-12
- 2.36.1 → 2.39.5 无变更
-
2.36.0
2022-04-18
- 2.32.1 → 2.35.8 无更改
-
2.32.0
2021-06-06
- 2.30.1 → 2.31.8 无更改
-
2.30.0
2020-12-27
- 2.27.1 → 2.29.3 无更改
-
2.27.0
2020-06-01
- 2.25.1 → 2.26.3 无更改
-
2.25.0
2020-01-13
- 2.24.1 → 2.24.4 无更改
-
2.24.0
2019-11-04
- 2.23.1 → 2.23.4 无更改
-
2.23.0
2019-08-16
- 2.22.1 → 2.22.5 无更改
-
2.22.0
2019-06-07
- 2.21.1 → 2.21.4 无更改
-
2.21.0
2019-02-24
- 2.19.3 → 2.20.5 无更改
-
2.19.2
2018-11-21
- 2.18.1 → 2.19.1 无变更
-
2.18.0
2018-06-21
- 2.17.1 → 2.17.6 无更改
-
2.17.0
2018-04-02
-
2.16.6
2019-12-06
- 2.15.4 无更改
-
2.14.6
2019-12-06
-
2.13.7
2018-05-22
- 2.12.5 无更改
-
2.11.4
2017-09-22
-
2.10.5
2017-09-22
-
2.9.5
2017-07-30
- 2.7.6 → 2.8.6 无更改
-
2.6.7
2017-05-05
-
2.5.6
2017-05-05
-
2.4.12
2017-05-05
- 2.3.10 无更改
-
2.2.3
2015-09-04
- 2.1.4 无更改
-
2.0.5
2014-12-17
描述
一个 gitattributes 文件是一个简单的文本文件,它为路径名赋予 attributes(属性)。
gitattributes 文件中的每一行格式如下:
pattern attr1 attr2 ...
即,一个模式后跟一个属性列表,用空格分隔。行首和行尾的空格将被忽略。以 # 开头的行将被忽略。以双引号开头的模式将按 C 风格进行引用。当模式匹配到相关路径时,该行中列出的属性将被赋予该路径。
对于给定的路径,每个属性可以处于以下状态之一:
当多个模式匹配到同一路径时,后面的行会覆盖前面的行。这种覆盖是按属性进行的。
模式匹配路径的规则与 .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(未指定)状态。这可以通过在属性名前加上感叹号 ! 来实现。
效果
通过为路径分配特定属性,可以影响 Git 的某些操作。目前,以下操作是属性感知型操作。
检出和检入
这些属性会影响当执行 git switch、git checkout 和 git merge 等命令时,存储在仓库中的内容如何复制到工作树文件。它们还会影响 Git 在 git add 和 git commit 时如何将您在工作树中准备好的内容存储到仓库中。
text
此属性将路径标记为文本文件,从而启用换行符转换:当匹配的文件被添加到索引时,文件的行尾会被标准化为 LF。反之,当文件从索引复制到工作目录时,根据 eol 属性、Git 配置和平台(参见下方 eol 的解释),其行尾可能会从 LF 转换为 CRLF。
- 已设置
-
在路径上设置
text属性会在检入和检出时启用换行符转换,如上所述。每次检入文件时,行尾都会被标准化为 LF,即使文件之前以 CRLF 行尾添加到 Git 中。 - 未设置
-
在路径上取消设置
text属性会告诉 Git 在检入或检出时不要尝试任何换行符转换。 - 设置为字符串值 "auto"
-
当
text设置为 "auto" 时,Git 会自行决定文件是文本还是二进制。如果它是文本且文件尚未以 CRLF 行尾存储在 Git 中,则在检入和检出时会进行如上所述的换行符转换。否则,在检入或检出时不会进行转换。 - 未指定
-
如果
text属性未指定,Git 将使用core.autocrlf配置变量来确定是否应转换文件。
任何其他值都会导致 Git 表现得好像 text 一直是未指定状态。
eol
此属性标记一个路径,使其在检出到工作树时使用特定的行尾样式。它仅在设置了 text 或 text=auto 时生效(参见上文),但指定 eol 会自动设置 text,前提是 text 未被指定。
- 设置为字符串值 "crlf"
-
此设置会在检出文件时将工作目录中的文件行尾转换为 CRLF。
- 设置为字符串值 "lf"
-
此设置会在检出文件时,在工作目录中使用与索引中相同的行尾。
- 未指定
-
如果文件的
eol属性未指定,其工作目录中的行尾将由core.autocrlf或core.eol配置变量决定(参见 git-config[1] 中这些选项的定义)。如果设置了text但未设置这两个变量中的任何一个,则在 Windows 上默认值为eol=crlf,在所有其他平台上默认为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
|
注意
|
当在跨平台项目中使用推送和拉取到中央仓库时启用了 text=auto 转换,包含 CRLFs 的文本文件应该被标准化。 |
从干净的工作目录开始
$ 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 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.true 来声明一个过滤器可以将本身无法使用但内容变为可用的内容。
注意:每当更改 clean 过滤器时,都应重新规范化仓库:$ git add --renormalize .
例如,在 .gitattributes 中,您会为路径分配 filter 属性。
*.c filter=indent
然后,您将在 .git/config 中定义 "filter.indent.clean" 和 "filter.indent.smudge" 配置,以指定一对命令,用于在检入(运行 "clean")和检出(因为命令是 "cat",所以不进行更改)C 程序源文件时修改其内容。
[filter "indent"] clean = indent smudge = cat
为了获得最佳结果,`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
过滤器应响应一个由 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
Git 在 "error"/"abort" 状态设置时既不会停止也不会重启过滤器进程。然而,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>.clean 或 filter.<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 是生成文本补丁还是将路径视为二进制文件。它还可以影响将在块头 @@ -k,l +n,m @@ 行上显示的内容,告诉 Git 使用外部命令生成差异,或要求 Git 在生成差异之前将二进制文件转换为文本格式。
- 已设置
-
将
diff属性设置为该路径的文本,即使它们包含通常不会出现在文本文件中的字节值(例如 NUL)。 - 未设置
-
将
diff属性设置为未设置的路径将生成Binary files differ(或在启用二进制补丁的情况下生成二进制补丁)。 - 未指定
-
将
diff属性设置为未指定的路径将首先检查其内容,如果看起来像文本且小于 `core.bigFileThreshold`,则将其视为文本。否则,将生成Binary files differ。 - 字符串
-
Diff is shown using the specified diff driver. Each driver may specify one or more options, as described in the following section. The options for the diff driver "foo" are defined by the configuration variables in the "diff.foo" section of the Git config file.
定义外部 diff 驱动
diff 驱动程序的定义是在 gitconfig 中完成的,而不是在 gitattributes 文件中,所以严格来说,本手册页不适合讨论它。然而……
要定义一个名为 jcdiff 的外部 diff 驱动,请在你的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)中添加一个如下所示的节:
[diff "jcdiff"] command = j-c-diff
当 Git 需要显示具有 diff 属性且值为 jcdiff 的路径的 diff 时,它会调用上述配置中指定的命令,即 j-c-diff,并带 7 个参数,就像调用 GIT_EXTERNAL_DIFF 程序一样。详情请参阅 git[1]。
如果程序能够忽略某些更改(类似于 git diff --ignore-space-change),那么也请将 trustExitCode 选项设置为 true。此时,程序应在发现重要更改时返回退出码 1,在未发现重要更改时返回 0。
设置内部 diff 算法
diff 算法可以通过 diff.algorithm 配置键来设置,但有时按路径设置 diff 算法可能更有帮助。例如,你可能希望对 .json 文件使用 minimal diff 算法,对 .c 文件使用 histogram 算法,等等,而不必每次都通过命令行传递算法。
首先,在 .gitattributes 文件中,为路径分配 diff 属性。
*.json diff=<name>
然后,定义一个 "diff.<name>.algorithm" 配置来指定 diff 算法,可从 myers、patience、minimal 或 histogram 中选择。
[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 配置选项用于定义一个执行此类转换的程序。该程序应该接受一个参数,即要转换的文件名,并将结果文本输出到 stdout。
例如,要显示文件 EXIF 信息而不是二进制信息的 diff(假设你已安装 exif 工具),请在你的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)中添加以下节:
[diff "jpg"] textconv = exif
|
注意
|
文本转换通常是单向转换;在此示例中,我们丢失了实际的图像内容,只关注文本数据。这意味着由 textconv 生成的 diff *不* 适合应用。因此,只有 git diff 和 git log 系列命令(即 log、whatchanged、show)才会执行文本转换。git format-patch 永远不会生成此输出。如果你想发送二进制文件的文本转换 diff 给某人(例如,因为它能快速传达你所做的更改),你应该单独生成它,并将其作为注释 *额外* 发送你通常会发送的二进制 diff。 |
由于文本转换可能很慢,尤其是在使用 git log -p 执行大量转换时,Git 提供了一种机制来缓存输出并在将来的 diff 中使用它。要启用缓存,请在你的 diff 驱动程序的配置中设置 "cachetextconv" 变量。例如:
[diff "jpg"] textconv = exif cachetextconv = true
这将在无限期内缓存运行 "exif" 在每个 blob 上的结果。如果你更改了 diff 驱动程序的 textconv 配置变量,Git 将自动使缓存条目失效并重新运行 textconv 过滤器。如果你想手动使缓存失效(例如,因为你的 "exif" 版本已更新并现在产生更好的输出),你可以使用 git update-ref -d refs/notes/textconv/jpg 手动删除缓存(其中 "jpg" 是 diff 驱动程序的名称,如上面的示例所示)。
选择 textconv 与外部 diff
如果你想显示仓库中二进制文件或特殊格式文件的差异,可以选择使用外部 diff 命令,或者使用 textconv 将它们转换为可 diff 的文本格式。你选择哪种方法取决于你的具体情况。
使用外部 diff 命令的优势在于灵活性。你不必局限于查找行定向的更改,也不必要求输出类似于统一 diff。你可以自由地以最适合你的数据格式的方式查找和报告更改。
相比之下,textconv 的限制要大得多。你提供了一个将数据转换为行定向文本格式的转换,然后 Git 使用其常规 diff 工具来生成输出。选择此方法有几个优点:
-
易用性。编写一个二进制到文本的转换通常比自己执行 diff 要简单得多。在许多情况下,可以现有的程序用作 textconv 过滤器(例如,exif、odt2txt)。
-
Git diff 功能。通过仅自己执行转换步骤,你仍然可以使用 Git 的许多 diff 功能,包括着色、单词 diff 以及合并的组合 diff。
-
缓存。Textconv 缓存可以加速重复的 diff,例如你在运行
gitlog-p时可能触发的 diff。
标记文件为二进制
Git 通常通过检查内容开头来正确猜测 blob 是包含文本还是二进制数据。但是,有时你可能想覆盖它的决定,无论是由于 blob 在文件后面包含二进制数据,还是由于内容虽然技术上由文本字符组成,但对人类读者来说是不透明的。例如,许多 PostScript 文件仅包含 ASCII 字符,但会产生嘈杂且无意义的 diff。
将文件标记为二进制的最简单方法是在 .gitattributes 文件中取消设置 diff 属性:
*.ps -diff
这将导致 Git 生成 Binary files differ(如果启用了二进制补丁,则生成二进制补丁),而不是常规 diff。
然而,有时也可能需要指定其他 diff 驱动属性。例如,你可能希望使用 textconv 将 PostScript 文件转换为 ASCII 表示以供人类查看,但否则将其视为二进制文件。你不能同时指定 -diff 和 diff=ps 属性。解决方案是使用 diff.*.binary 配置选项:
[diff "ps"] textconv = ps2ascii binary = true
执行三路合并
merge
当在 git merge、git revert 和 git cherry-pick 等命令需要进行文件级合并时,merge 属性会影响三份文件版本的合并方式。
- Set
-
内置的 3 路合并驱动程序用于以类似
RCS套件的 *merge* 命令的方式合并内容。这适用于普通的文本文件。 - Unset
-
将当前分支的版本作为暂定的合并结果,并声明合并存在冲突。这适用于没有明确合并语义的二进制文件。
- Unspecified
-
默认情况下,这使用与
merge属性设置为相同情况的内置 3 路合并驱动程序。然而,merge.default配置变量可以命名不同的合并驱动程序,用于merge属性未指定的路径。 - String
-
使用指定的自定义合并驱动程序执行 3 路合并。可以通过请求 "text" 驱动程序来显式指定内置的 3 路合并驱动程序;可以通过请求 "binary" 来请求内置的 "take the current branch" 驱动程序。
定义自定义合并驱动
合并驱动程序的定义是在 .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 来传递。
检查空白错误
whitespace
core.whitespace 配置变量允许你定义 *diff* 和 *apply* 在项目中所有路径上应将哪些内容视为空白错误(参见 git-config[1])。此属性允许你按路径进行更精细控制。
创建归档
export-subst
如果为文件设置了 export-subst 属性,则 Git 在将此文件添加到归档时会展开几个占位符。展开取决于是否能获取提交 ID,即如果 git-archive[1] 被赋予的是树而不是提交或标签,则不会进行任何替换。占位符与 git-log[1] 的 --pretty=format: 选项相同,只是它们需要被包装起来,如下所示:$Format:PLACEHOLDERS$。例如,字符串 $Format:%H$ 将被提交哈希值替换。然而,每个归档只展开一个 %(describe) 占位符,以防止拒绝服务攻击。
在 GUI 工具中查看文件
encoding
此属性的值指定 GUI 工具(例如 gitk[1] 和 git-gui[1])应该使用的字符编码来显示相关文件的内容。请注意,由于性能考虑,gitk[1] 不会使用此属性,除非你在其选项中手动启用每个文件的编码。
如果未设置此属性或其值无效,则将使用 gui.encoding 配置变量的值(参见 git-config[1])。
使用宏属性
你不想对你跟踪的任何二进制文件进行任何行尾转换,也不想为其生成文本 diff。你需要指定例如:
*.jpg -text -diff
但这可能很麻烦,当你有许多属性时。使用宏属性,你可以定义一个属性,当设置该属性时,它也会同时设置或取消设置其他属性。系统知道一个内置的宏属性,binary:
*.jpg binary
设置 "binary" 属性也会像上面一样取消设置 "text" 和 "diff" 属性。请注意,宏属性只能被 "Set",尽管设置一个宏属性可能会有设置或取消设置其他属性的效果,甚至将其他属性恢复到 "Unspecified" 状态。
定义宏属性
自定义宏属性只能在顶层 gitattributes 文件($GIT_DIR/info/attributes、工作树顶层的 .gitattributes 文件,或全局或系统范围的 gitattributes 文件)中定义,不能在工作树子目录的 .gitattributes 文件中定义。内置宏属性 "binary" 等同于:
[attr]binary -diff -merge -text
示例
如果你有这三个 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 的属性计算如下:
-
通过检查
t/.gitattributes(它与相关路径在同一个目录),Git 发现第一行匹配。merge属性被设置。它还发现第二行匹配,并且foo和bar属性被取消设置。 -
然后它检查
.gitattributes(它在父目录),并发现第一行匹配,但t/.gitattributes文件已经决定了merge、foo和bar属性如何被赋予给此路径,因此它保持foo和bar被取消设置。属性baz被设置。 -
最后,它检查
$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