41.1.3 常用属性介绍

1.text

属性text用于显式地指定文件的类型:二进制(-text)、文本文件(text)或是开启文件类型的智能判别(text=auto)。对于文本文件,Git会对其进行换行符转换。本篇第40章“40.3换行符问题”中已经详细介绍了属性text的用法,并且在本章“41.1.1属性定义”的示例中对属性text的取值做了总结,在此不再赘述。

在“40.3换行符问题”一节中,我们还知道可以通过在Git配置文件中设置core.autocrlf配置变量,来开启Git对文件类型的智能判别,并对文本文件开启换行符转换。那么Git的配置变量core.autocrlf和属性text有什么异同呢?

将Git的配置变量core.autocrlf设置为true或input,相当于设置了属性text=auto。但是Git配置文件中的配置变量只能在本地进行设置并且只对本地版本库有效,不能通过共享版本库传递到其他用户的本地版本库中,因而core.autocrlf开启的换行符转换不能跟其他用户共享,或者说不能将换行符转换策略设置为整个项目(版本库)的强制规范。属性文件则不同,可以被检入到版本库中并通过共享版本库传递给其他用户,因此可以通过在检入的.gitattributes文件中设置text属性,或者干脆设置text=auto属性,强制同一项目的所有用户在提交文本文件时都要规范换行符。

建议所有可能要进行跨平台开发的项目都在项目根目录中检入一个.gitattributes文件,根据文件扩展名设置文件的text属性,或者设置即将介绍的eol属性。

2.eol

属性eol用于设定文本文件的换行符格式。对于设置了eol属性的文件,如果没有设定text属性时,默认会设置text属性为true。属性eol的取值如下:

eol=crlf

当文件检入版本库时,blob对象使用LF作为换行符。当检出到工作区时,使用CRLF作为换行符。

eol=lf

当文件检入版本库时,blob对象使用LF作为换行符,检出的时候工作区的文件也使用LF作为换行符。

除了通过属性设定换行符格式外,还可以在Git的配置文件中通过core.eol配置变量来设定。两者的区别在于配置文件中的core.eol配置变量设置的换行符是一个默认值,没有通过eol属性指定换行符格式的文本文件会采用core.eol的设置。变量core.eol的值可以设定为lf、crlf和native。默认core.eol的取值为native,即采用操作系统标准的换行符格式。

下面的示例通过属性文件设置文件的换行符格式。


*.vcproj eol=crlf

*.sh eol=lf


扩展名为.vcproj的文件使用CRLF作为换行符,而扩展名为.sh的文件则使用LF作为换行符。在版本库中检入类似的属性文件,会使得Git客户端无论在什么操作系统中都能够在工作区检出一致的换行符格式,这样无论是在Windows上还是在Linux上使用git archive命令将工作区文件打包,导出的文件都会保持正确的换行符格式。

3.ident

属性ident开启文本文件中的关键字扩展,即关键字$Id$的自动扩展。当检出到工作区时,$Id$自动扩展为$Id:,后面紧接着40位SHA1哈希值(相应blob对象的哈希值),然后以一个$字符结尾。当文件检入时,要把内容中出现的以$Id:开始,以$结束的内容替换为$Id$再保存到blob对象中。

这个功能可以说是对CVS相应功能的致敬。自动扩展的内容使用的是blob的哈希值而非提交本身的哈希值,因此并无太大的实际意义,不建议使用。如果希望在文本文件中扩展出提交者姓名、提交ID等更有实际意义的内容,可以参照后面介绍的属性export-subst。

4.filter

属性filter为文件设置一个自定义转换过滤器,以便文件在检入版本库及检出到工作区时进行相应的转换。定义转换过滤器通过Git配置文件来完成,因此这个属性应该只在本地进行设置,而不要也不能通过检入到版本库中的.gitattributes文件来传递。

例如下面的属性文件设置了所有的C语言源文件在检入和检出的时候使用名为indent的代码格式化过滤器。


*.c filter=indent


然后还要通过Git配置文件设定indent过滤器,示例如下:


[filter "indent"]

clean=indent

smudge=cat


定义过滤器只要设置两条命令,一条是名为clean的配置设定的的命令,用于在文件检入时执行,另外一条是名为smudge的配置设定的命令,用于将文件检出到工作区。对于本例,在代码检入时执行indent命令对代码格式化后,再保存到版本库中。当检出到工作区时,执行cat命令实际上相当于直接将blob对象复制到工作区。

5.diff

和前面介绍的属性不同,属性diff不会对文件检入检出造成影响,而只是在查看文件历史变更时起作用。属性diff可以取值如下:

diff

进行版本间的差异比较时,以文本方式进行比较,即使文件看起来像是二进制文件(包含NULL字符),或者被设置为二进制文件(-text)。

-diff

不以文本方式进行差异比较,而以二进制方式进行比较。默认二进制文件不进行差异比较,因此包含-diff属性设置的文件在差异比较时不显示内容上的差异。对于有些文本文件(如postscript文件)进行差异比较没有意义,可以对其设置-diff属性,避免在显示提交版本间的差异时造成干扰。

!diff

不设置diff属性,相当于在执行差异比较时要对文件内容进行智能判别,如果文件看起来像是文本文件,则显示文本格式的差异比较。

diff=<driver>

设定一个外部的驱动用于文件的差异比较。例如对于Word文档的差异比较就可以通过这种方式进行配置。

Word文档属于二进制文件,默认不显示差异比较。在Linux上有一个名为antiword的应用软件可以将Word文档转换为文本文件显示,借助该软件就可以实现在Linux(包括Mac OS X)上显示Word文件版本间的差异。

下面的Git配置就定义了一个名为antiword的适用于Word差异比较的驱动:


[diff "antiword"]

textconv=antiword


其中textconv属性用于设定一个文件转换命令行,这里设置为antiword,用于将Word文档转换为纯文本。

然后还需要设置属性,修改版本库下的.git/info/attributes文件就可以了,新增的属性设置如下:


*.doc diff=antiword


关于更多的差异比较的外部驱动的设置,可以执行git help—web attributes来参见相关的帮助。

6.merge

属性merge用于为文件设置指定的合并策略,受影响的Git命令有:git merge、git revert和git cherry-pick等。属性merge可以取值如下:

merge

使用内置的三向合并策略。

-merge

将当前分支的文件版本设置为暂时的合并结果,并且声明合并发生了冲突,这实际上是二进制文件默认的合并方式。可以对文本文件设置该属性,使得在合并时的行为类似于二进制文件。

!merge

和定义了merge属性的效果类似,使用内置的三向合并策略。然而当通过Git配置文件的merge.default配置变量设置了合并策略后,如果没有为文件设置merge属性,则使用merge.default设定的策略。

merge=<driver>

使用指定的合并驱动执行三向文件合并。驱动可以是内置的三个驱动,也可以是用户通过Git配置文件自定义的驱动。

下面重点说一说通过枚举值来指定在合并时使用的内置驱动和自定义驱动。先来看看Git提供的三个内置驱动:

merge=text

默认文本文件在进行三向合并时使用的驱动。会在合并后的文本文件中用特殊的标识<<<<<<<、=======和>>>>>>>来标记冲突的内容。

merge=binary

默认二进制文件在进行三向合并时使用的驱动。会在工作区中保持当前分支中的版本不变,但是会通过在三个暂存区中进行冲突标识,使得文件处于冲突状态。

merge=union

在文本文件进行三向合并的过程中,不使用冲突标识符来标记冲突,而是将冲突双方的内容简单地罗列在文件中。用户应该对合并后的文件进行检查。请慎用此合并驱动。

用户还可以自定义驱动。例如Topgit就使用自定义合并驱动的方式来控制两个Topgit管理文件.topmsg和.topdeps的合并行为。

Topgit会在版本库的配置文件.git/info/config中添加下面的设置来定义一个名为ours的合并驱动。注意不要将此ours驱动和本书第3篇第16章“16.6合并策略”一节中介绍的ours合并策略弄混淆。


[merge "ours"]

name=\ "always keep ours\" merge driver

driver=touch%A


定义的合并驱动的名称由merge..name给出,合并时执行的命令则由配置merge..driver给出。本例中使用了命令touch%A,含义为对当前分支中的文件进行简单的触碰(更新文件时间戳),亦即合并冲突时采用本地版本,丢弃其他版本。

Topgit还会在版本库.git/info/attributes属性文件中包含下面的属性设置:


.topmsg merge=ours

.topdeps merge=ours


上述设置的含义为在遇到合并冲突时,对这两个Topgit管理文件采用在Git配置文件中设定的ours合并驱动。Topgit之所以要这么实现是因为不同特性分支的管理文件之间并无关联,也不需要合并,在遇到冲突时只使用自己的版本即可。这对于要经常执行变基和分支合并的Topgit来说,设置这个策略可以简化管理,但是这个合并设置在特定情况下也存在不合理之处。例如两个用户工作在同一分支上,同时更改了.topmsg文件以修改特性分支的描述,在合并时会覆盖对方的修改,这显然是不好的行为。但是权衡利弊,还是如此实现最好。

7.whitespace

Git可以对文本文件中空白字符的使用是否规范做出检查,在进行文件差异比较时,将使用不当的空白字符用红色进行标记(开启color.diff.whitespace[1])。也可以在执行git apply时通过参数—whitespace=error防止错误的空白字符应用到提交中。

Git默认开启对下面三类错误空白字符的检查。

blank-at-eol

在行尾出现的空白字符(换行符之前)被视为误用。

space-before-tab

在行首缩进中出现在TAB字符前面的空白字符视为误用。

blank-at-eof

在文件末尾的空白行视为误用。

Git还支持对更多空白字符的误用做出检测,包括:

indent-with-non-tab

用8个或更多的空格进行缩进视为误用。

tab-in-indent

在行首的缩进中使用TAB字符视为误用。显然这个设置和上面的indent-with-non-tab互斥。

trailing-space

相当于同时启用blank-at-eol和blank-at-eof。

cr-at-eol

将行尾的CR(回车)字符视为换行符的一部分。也就是说,在行尾前出现的CR字符不会引起trailing-space报错。

tabwidth=<n>

设置一个TAB字符相当于几个空格,默认为8个。

可以通过Git配置文件中的core.whitespace配置变量,设置开启更多的空白字符检查,将要开启的空白字符检查项用逗号分开即可。

如果希望对特定路径进行空白字符检查,则可以通过属性whitespace进行设置。属性whitespace可以有如下设置:

whitespace

开启所有的空白字符误用检查。

-whitespace

不对空白字符进行误用检查。

!whitespace

使用core.whitespace配置变量的设置进行空白字符误用检查。

whitespace=……

和core.whitespace的语法一样,用逗号分隔各个空白字符检查项。

8.export-ignore

设置了该属性的文件和目录在执行git archive时不予导出。

9.export-subst

如果为文件设置了属性export-subst,则在使用git archive导出项目文件时,会展开相应文件内容中的占位符,然后再添加到归档中。注意如果在使用git archive导出时使用树ID,而没有使用提交或里程碑,则不会展开占位符。

占位符的格式为$Format:PLACEHOLDERS$,其中PLACEHOLDERS使用git log—pretty=format:相同的参数(具体请参见git help log显示的帮助页面)。例如:$Format:%H$将展开为提交的哈希值,$Format:%an$将展开为提交者姓名。

10.delta

如果设置属性delta为false,则不对该路径指向的blob文件执行Delta压缩。

11.encoding

设置文件所使用的字符集,以便使用GUI工具(如gitk和git-gui)时能够正确显示文件内容。基于性能上的考虑,gitk默认不检查该属性,除非通过gitk的偏好设置启用"Support per-file encodings"。

如果没有为文件设置encoding属性,则使用git.encoding配置变量。

12.binary

属性binary严格来说是一个宏,相当于-text-diff。即禁止换行符转换,以及禁止以文本方式显示文件差异。

用户也可以自定义宏。自定义宏只能在工作区根目录中的.gitattributes文件中添加,以内置的binary宏为例,相当于在属性文件中进行了如下的设置:


[attr]binary-diff-text


[1]如果设置color.ui配置变量为true,则针对所有Git命令开启颜色输出。