设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.50.1 无更改
-
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
文件是一个简单的文本文件,它为路径名指定 属性
。
gitattributes
文件中的每一行都采用以下形式
pattern attr1 attr2 ...
即,一个模式后面跟着一个属性列表,由空格分隔。前导和尾随空格被忽略。以#开头的行被忽略。以双引号开头的模式以 C 风格引用。当模式匹配到相关路径时,该行中列出的属性将被赋予该路径。
对于给定路径,每个属性可以处于以下状态之一
当多个模式匹配同一路径时,后来的行会覆盖早前的行。这种覆盖是针对每个属性进行的。
模式匹配路径的规则与 .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
文件中。
有时您需要将路径的某个属性设置覆盖为 未指定
状态。这可以通过在属性名称前加上一个感叹号 !
来实现。
影响
Git 的某些操作可以通过为路径分配特定属性来影响。目前,以下操作是属性感知的。
检出和检入
这些属性影响当诸如 git switch、git checkout 和 git merge 等命令运行时,仓库中存储的内容如何复制到工作树文件。它们还影响 Git 在 git add 和 git 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
此属性标记路径,使其在检出时在工作树中使用特定的行结束符样式。它仅在设置了 text
或 text=auto
时才生效(参见上文),但如果 text
未指定,指定 eol
会自动设置 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
注意
|
当在跨平台项目中使用 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 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 可以通过一次过滤器命令调用处理所有 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>.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 是为路径生成文本补丁还是将路径视为二进制文件。它还可以影响 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" 配置以指定差异算法,从 myers
、patience
、minimal
或 histogram
中选择。
[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 diff 和 git 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 使用其常规差异工具生成输出。选择此方法有几个优点
-
易用性。编写二进制到文本的转换通常比执行自己的差异简单得多。在许多情况下,现有程序可以用作 textconv 过滤器(例如 exif、odt2txt)。
-
Git 差异功能。通过只执行转换步骤,您仍然可以使用 Git 的许多差异功能,包括着色、词差异和合并的组合差异。
-
缓存。Textconv 缓存可以加速重复的差异,例如您通过运行
git
log
-p
可能触发的那些。
将文件标记为二进制
Git 通常通过检查内容开头来正确猜测 blob 是包含文本还是二进制数据。然而,有时您可能希望覆盖它的决定,无论是由于 blob 在文件中包含后来的二进制数据,还是因为内容,尽管技术上由文本字符组成,但对人类读者来说是不透明的。例如,许多 Postscript 文件只包含 ASCII 字符,但会产生嘈杂且无意义的差异。
将文件标记为二进制的最简单方法是在 .gitattributes
文件中取消设置 diff 属性
*.ps -diff
这将导致 Git 生成 Binary
files
differ
(或二进制补丁,如果启用了二进制补丁)而不是常规差异。
但是,可能还需要指定其他差异驱动程序属性。例如,您可能希望使用 textconv
将 Postscript 文件转换为 ASCII 表示以供人工查看,但否则将它们视为二进制文件。您不能同时指定 -diff
和 diff=ps
属性。解决方案是使用 diff.*.binary
配置选项
[diff "ps"] textconv = ps2ascii binary = true
执行三向合并
merge
merge
属性影响在 git
merge
和其他命令(例如 git
revert
和 git
cherry-pick
)期间需要文件级合并时,文件的三个版本如何合并。
- 设置
-
内置的三向合并驱动程序用于合并内容,其方式类似于
RCS
套件的 merge 命令。这适用于普通文本文件。 - 未设置
-
将当前分支的版本作为暂定的合并结果,并声明合并存在冲突。这适用于没有明确合并语义的二进制文件。
- 未指定
-
默认情况下,这会使用与
merge
属性设置时相同的内置三向合并驱动程序。但是,merge.default
配置变量可以指定不同的合并驱动程序,用于merge
属性未指定的路径。 - 字符串
-
使用指定的自定义合并驱动程序执行三向合并。内置的三向合并驱动程序可以通过请求“text”驱动程序显式指定;内置的“take the current branch”驱动程序可以通过“binary”请求。
定义自定义合并驱动程序
合并驱动程序的定义是在 .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])。
使用宏属性
您不希望对任何您跟踪的二进制文件应用行结束符转换,也不希望生成文本差异。您需要指定例如
*.jpg -text -diff
但这可能会变得很麻烦,当您有许多属性时。使用宏属性,您可以定义一个属性,当它被设置时,同时设置或取消设置许多其他属性。系统知道一个内置的宏属性 binary
*.jpg binary
设置“binary”属性也会像上面那样取消设置“text”和“diff”属性。请注意,宏属性只能是“设置”状态,尽管设置一个可能会导致设置或取消设置其他属性,甚至将其他属性恢复到“未指定”状态。
定义宏属性
自定义宏属性只能在顶级 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