git的多个命令都需要指定pathspec。本文探讨一下git的pathspec。

git的pathspec概念在gitglossay: pathspec中有说明。主要有两种格式,一种长格式和一种短格式。长格式比如::(glob,exclude)foo/bar.txt。长格式的某些关键字具有短格式,比如exlcude关键字的短格式是!,短格式比如:!/foo/bar.txt

:(glob,exclude)foo/bar.txt这个pathspec中,路径是foo/bar.txt,git在匹配的时候会把路径一分为二,最后一个/以及之前的部分被认为是目录部分,/之后的部分被认为是文件名部分。这两个部分分开匹配。

下面来做一些实验,假设有一个git仓库,在不同的目录存储若干个aa.txt文件,其git ls-files "*.txt"输出是

aa.txt
bb/aa.txt
cc/bb/aa.txt

执行git ls-files "*.txt" ":!bb/aa.txt"命令,输出:

aa.txt
cc/bb/aa.txt

上面的pathspec是:!bb/aa.txt!是exclude的缩写,也就意味着过滤bb/aa.txt

执行git ls-files "*.txt" ":!**/bb/aa.txt"

aa.txt
bb/aa.txt

:!**/bb/aa.txtcc/bb/aa.txt给过滤掉了。

执行git ls-files "*.txt" ":!/bb/**",得到:

aa.txt
cc/bb/aa.txt

bb/aa.txt:!/bb/**过滤掉了。

在上面的例子中都使用了**作为通配符,其实**只有在指定了glob选项的时候才有特殊含义,否则和普通的*没有两样,参考fnmatch

下面是采用*的例子:

执行git ls-files "*.txt" ":!*/bb/*"

aa.txt
bb/aa.txt

上面的:!*/bb/*只过滤掉了cc/bb/aa.txt,却没有过滤掉bb/aa.txt。因为:!*/bb/*中的bb前面有一个/

去掉bb前面的/,执行git ls-files "*.txt" ":!*bb/*"

aa.txt

也可以开启glob选项,让**生效。执行git ls-files "*.txt" ":(glob,exclude)**/cc/**"

aa.txt
bb/aa.txt

在glob开启的情况下,**可以跨路径匹配多个目录成分。

下面是另外一个例子,你可以在.git/info/spare-checkout中书写规则,比如:

Document/*.jpg
fnmatch(3)
:(glob)/Document/**
:!:/Document/**/

然后在gitconfig开启中开启spare-checkout选项,就可以在checkout的时候避免checkout某些文件。

参考

(完)