Git 中文参考(五)(3)https://developer.aliyun.com/article/1565847
长时间运行的过滤器
如果通过filter..process
定义过滤器命令(字符串值),则 Git 可以在单个 Git 命令的整个生命周期内使用单个过滤器调用处理所有 blob。这是通过使用长时间运行的进程协议(在 technical / long-running-process-protocol.txt 中描述)实现的。
当 Git 遇到需要清理或污迹的第一个文件时,它会启动过滤器并执行握手。在握手中,Git 发送的欢迎消息是“git-filter-client”,只支持版本 2,支持的功能是“干净”,“涂抹”和“延迟”。
然后 Git 发送一个以 flush 数据包终止的“key = value”对列表。该列表至少包含 filter 命令(基于支持的功能)以及要相对于存储库根目录进行筛选的文件的路径名。在刷新数据包之后,Git 发送内容分为零个或多个 pkt-line 数据包和一个刷新数据包以终止内容。请注意,过滤器在收到内容和最终刷新数据包之前不得发送任何响应。另请注意,“key = value”对的“值”可以包含“=”字符,而键永远不会包含该字符。
packet: git> command=smudge packet: git> pathname=path/testfile.dat packet: git> 0000 packet: git> CONTENT packet: git> 0000
期望过滤器响应一个以 flush 数据包终止的“key = value”对的列表。如果过滤器没有遇到问题,则列表必须包含“成功”状态。在这些数据包之后,预期过滤器将在最后发送零个或多个 pkt-line 数据包和一个 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!
如果结果内容为空,则期望过滤器以“成功”状态和刷新分组响应以发信号通知空内容。
packet: git< status=success packet: git< 0000 packet: git< 0000 # empty content! packet: git< 0000 # empty list, keep "status=success" unchanged!
如果过滤器不能或不想处理内容,则应该以“错误”状态响应。
packet: git< status=error packet: git< 0000
如果过滤器在处理过程中遇到错误,则可以在(部分或完全)发送内容后发送状态“错误”。
packet: git< status=success packet: git< 0000 packet: git< HALF_WRITTEN_ERRONEOUS_CONTENT packet: git< 0000 packet: git< status=error packet: git< 0000
如果过滤器不能或不想处理内容以及 Git 过程生命周期内的任何未来内容,则期望在协议中的任何点以“中止”状态响应。
packet: git< status=abort packet: git< 0000
如果设置了“错误”/“中止”状态,Git 既不会停止也不会重新启动过滤器进程。但是,Git 根据filter..required
标志设置其退出代码,模仿filter..clean
/ filter..smudge
机制的行为。
如果过滤器在通信期间死亡或者不遵守协议,那么 Git 将停止过滤器进程并使用下一个需要处理的文件重新启动它。根据filter..required
标志,Git 会将其解释为错误。
延迟
如果过滤器支持“延迟”功能,那么 Git 可以在过滤器命令和路径名之后发送标志“can-delay”。该标志表示过滤器可以通过响应没有内容但具有状态“延迟”和刷新分组来延迟过滤当前 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
如果过滤器支持“延迟”功能,则它必须支持“list_available_blobs”命令。如果 Git 发送此命令,那么过滤器应该返回一个路径名列表,这些路径名表示先前已经延迟并且现在可用的 blob。该列表必须以刷新数据包结束,然后是“成功”状态,该状态也以刷新数据包终止。如果延迟路径的 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..clean
或filter..smudge
命令与filter..process
一起使用,因为前者使用的是与后者不同的进程间通信协议。
签入/签出属性之间的交互
在签入代码路径中,首先使用filter
驱动程序转换 worktree 文件(如果指定并定义相应的驱动程序),然后使用ident
(如果指定)处理结果,最后使用text
处理(再次) ,如果指定和适用)。
在签出代码路径中,首先使用text
转换 blob 内容,然后使用ident
转换为filter
。
合并具有不同签入/签出属性的分支
如果您为文件添加了导致该文件的规范存储库格式更改的属性,例如添加 clean / smudge 过滤器或 text / eol / ident 属性,那么合并属性不存在的任何内容通常会导致合并冲突。
为了防止这些不必要的合并冲突,可以告诉 Git 在通过设置merge.renormalize
配置变量解析三向合并时运行文件的所有三个阶段的虚拟签出和签入。当转换后的文件与未转换的文件合并时,这可以防止由签入转换引起的更改导致虚假合并冲突。
只要“涂抹→清洁”产生与“干净”相同的输出,即使对于已经弄脏的文件,此策略也会自动解决所有与过滤器相关的冲突。不以这种方式操作的过滤器可能会导致必须手动解决的其他合并冲突。
生成差异文本
diff
属性diff
影响 Git 如何为特定文件生成差异。它可以告诉 Git 是为路径生成文本补丁还是将路径视为二进制文件。它还可以影响在 hunk header @@ -k,l +n,m @@
行上显示的行,告诉 Git 使用外部命令生成 diff,或者在生成 diff 之前让 Git 将二进制文件转换为文本格式。
Set
设置diff
属性的路径被视为文本,即使它们包含通常永远不会出现在文本文件中的字节值,例如 NUL。
Unset
未设置diff
属性的路径将生成Binary files differ
(如果启用了二进制补丁,则生成二进制补丁)。
Unspecified
首先指定diff
属性未指定的路径检查其内容,如果它看起来像文本并且小于 core.bigFileThreshold,则将其视为文本。否则会产生Binary files differ
。
String
使用指定的 diff 驱动程序显示 Diff。每个驱动程序可以指定一个或多个选项,如以下部分所述。 diff 驱动程序“foo”的选项由 Git 配置文件的“diff.foo”部分中的配置变量定义。
定义外部差异驱动程序
diff 驱动程序的定义是在gitconfig
中完成的,而不是gitattributes
文件,所以严格来说这个手册页是一个错误的地方来讨论它。然而…
要定义外部差异驱动程序jcdiff
,请在$GIT_DIR/config
文件(或$HOME/.gitconfig
文件)中添加一个部分,如下所示:
[diff "jcdiff"] command = j-c-diff
当 Git 需要显示diff
属性设置为jcdiff
的路径的 diff 时,它会调用您使用上述配置指定的命令,即j-c-diff
,带有 7 个参数,就像调用GIT_EXTERNAL_DIFF
程序一样。有关详细信息,请参阅 git [1] 。
定义自定义的 hunk-header
文本差异输出中的每组更改(称为“hunk”)都以以下形式的行为前缀:
@@ -k,l +n,m @@ TEXT
这称为 _ 块头 _。默认情况下,“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 语言的源代码。bibtex
适用于带有 BibTeX 编码参考的文件。cpp
适用于 C 和 C ++语言的源代码。csharp
适用于 C#语言的源代码。css
适用于级联样式表。fortran
适用于 Fortran 语言的源代码。fountain
适用于 Fountain 文档。golang
适用于 Go 语言的源代码。html
适用于 HTML / XHTML 文档。java
适用于 Java 语言的源代码。matlab
适用于 MATLAB 语言的源代码。objc
适用于 Objective-C 语言的源代码。pascal
适用于 Pascal / Delphi 语言的源代码。perl
适用于 Perl 语言的源代码。php
适用于 PHP 语言的源代码。python
适用于 Python 语言的源代码。ruby
适用于 Ruby 语言的源代码。tex
适用于 LaTeX 文档的源代码。
自定义单词差异
您可以通过在“diff。*。wordRegex”配置变量中指定适当的正则表达式来自定义git diff --word-diff
用于分割行中单词的规则。例如,在 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 diff
和git log
系列命令(即 log,whatchanged,show)才能执行文本转换。 git format-patch
永远不会生成此输出。如果你想向某人发送二进制文件的文本转换差异(例如,因为它会快速传达你所做的更改),你应该单独生成它并将其作为注释发送 _ 除了 _ 您可能发送的常用二进制差异。 |
因为文本转换速度很慢,特别是在使用git log -p
进行大量转换时,Git 提供了一种缓存输出并在将来的差异中使用它的机制。要启用缓存,请在 diff 驱动程序的配置中设置“cachetextconv”变量。例如:
[diff "jpg"] textconv = exif cachetextconv = true
这将缓存每个 blob 上无限期运行“exif”的结果。如果更改 diff 驱动程序的 textconv 配置变量,Git 将自动使缓存条目无效并重新运行 textconv 过滤器。如果你想手动使缓存失效(例如,因为你的“exif”版本已更新并且现在产生更好的输出),你可以用git update-ref -d refs/notes/textconv/jpg
手动删除缓存(其中“jpg”是差异驱动程序的名称,如上例所示)。
选择 textconv 与外部差异
如果要在存储库中显示二进制或特殊格式的 blob 之间的差异,可以选择使用外部 diff 命令,或使用 textconv 将它们转换为可扩展的文本格式。您选择哪种方法取决于您的具体情况。
使用外部 diff 命令的优点是灵活性。您不一定要找到面向行的更改,输出也不一定要像统一的 diff 那样。您可以以最合适的方式为数据格式定位和报告更改。
相比之下,textconv 更具限制性。您提供了将数据转换为面向行的文本格式,Git 使用其常规 diff 工具生成输出。选择此方法有几个好处:
- 便于使用。编写二进制文本转换通常要比执行自己的 diff 更简单。在许多情况下,现有程序可以用作 textconv 过滤器(例如,exif,odt2txt)。
- Git diff 功能。通过自己仅执行转换步骤,您仍然可以利用 Git 的许多差异功能,包括着色,字差异和合并差异进行合并。
- 缓存。 Textconv 缓存可以加速重复的差异,例如您可能通过运行
git log -p
触发的差异。
将文件标记为二进制文件
Git 通常通过检查内容的开头来正确猜测 blob 是否包含文本或二进制数据。但是,有时您可能希望覆盖其决定,因为 blob 在文件中稍后包含二进制数据,或者因为内容虽然在技术上由文本字符组成,但对于人类读者来说是不透明的。例如,许多 postscript 文件仅包含 ASCII 字符,但会产生嘈杂且无意义的差异。
将文件标记为二进制文件的最简单方法是取消设置.gitattributes
文件中的 diff 属性:
*.ps -diff
这将导致 Git 生成Binary files differ
(或二进制补丁,如果启用了二进制补丁)而不是常规差异。
但是,也可能需要指定其他 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 路合并驱动程序;可以使用“二进制”来请求内置的“取当前分支”驱动程序。
内置合并驱动程序
定义了一些内置的低级合并驱动程序,可以通过merge
属性询问。
text
通常的 3 向文件级合并文本文件。冲突区域标有冲突标记<<<<<<<
,=======
和>>>>>>>
。分支的版本显示在=======
标记之前,合并分支的版本显示在=======
标记之后。
binary
保留工作树中的分支版本,但保留路径处于冲突状态以供用户进行整理。
union
对文本文件运行 3 向文件级别合并,但从两个版本中获取行,而不是留下冲突标记。这往往会以随机顺序在结果文件中保留添加的行,用户应验证结果。如果您不理解其含义,请不要使用此功能。
定义自定义合并驱动程序
合并驱动程序的定义在.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
命名的文件中,如果它设法干净地合并它们,则退出为零状态,如果存在冲突则为非零。
merge.*.recursive
变量指定当共同驱动器被调用以在共同祖先之间进行内部合并时,要使用的其他合并驱动程序。如果未指定,则驱动程序本身用于内部合并和最终合并。
合并驱动程序可以通过占位符%P
了解合并结果的路径名。
conflict-marker-size
此属性控制冲突合并期间留在工作树文件中的冲突标记的长度。仅将值设置为正整数具有任何有意义的效果。
例如,.gitattributes
中的这一行可用于告诉合并机器在合并文件Documentation/git-merge.txt
导致冲突时留下更长的时间(而不是通常的 7 个字符长)冲突标记。
Documentation/git-merge.txt conflict-marker-size=32
检查空白错误
whitespace
core.whitespace
配置变量允许您定义 diff 和 _ 适用 _ 应该考虑项目中所有路径的空白错误(参见 git-config [1] )。此属性为每个路径提供更精细的控制。
Set
注意 Git 已知的所有类型的潜在空白错误。标签宽度取自core.whitespace
配置变量的值。
Unset
不要注意任何错误。
Unspecified
使用core.whitespace
配置变量的值来确定要注意的错误。
String
以与core.whitespace
配置变量相同的格式指定逗号单独的常见空白问题列表。
创建档案
export-ignore
具有export-ignore
属性的文件和目录不会添加到存档文件中。
export-subst
如果为文件设置了属性export-subst
,那么在将此文件添加到存档时,Git 将展开多个占位符。扩展取决于提交 ID 的可用性,即,如果 git-archive [1] 已被赋予树而不是提交或标记,则不会进行替换。占位符与 git-log [1] 的选项--pretty=format:
的占位符相同,只是它们需要像这样包装:文件中的$Format:PLACEHOLDERS$
。例如。字符串$Format:%H$
将被提交哈希替换。
包装物
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
文件:
(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
也可以看看
GIT
部分 git [1] 套件
Git 中文参考(五)(5)https://developer.aliyun.com/article/1565849