Git命令中的pathspec是什么?

什么是pathspec

pathspec是git命令中的一个可选项,它可以用于限制git命令的作用范围,这个范围通常是指仓库中的子集(包括文件和文件夹)。git中的很多命令都可以带上该选项,比如说"git ls-files"、“git ls-tree”、“git add”、“git grep”、“git diff"和"git checkout”。
image.png

使用pathspec

pathspec可以写成多种格式去匹配git仓库中的文件。

方式一:绝对路径或相对路径

pathspec最简单的用法就是直接写成文件路径字符串。举个例子来讲,假设项目中有如下结构的文件:

└─ fruits.txt
└─ books.js
└─ src
	 └─ clothes.txt
	 └─ pants.js

可以使用git ls-files加上列出路径下的文件:

git ls-files .
### 控制台输出结果
books.js
fruits.txt
src/clothes.txt
src/pants.js

git ls-files fruits.txt
### 控制台输出
furits.txt

git ls-files ./src/clothes.txt
#### 控制台输出
src/clothes.txt

git ls-files src
### 控制台输出
src/clothes.txt
src/pants.js

上述几个命令中的git ls-files后跟的路径就是pathspec,可以是相对路径和绝对路径。

还可以输入多个参数来查看多个路径文件:

git ls-files fruits.txt ./src

### 控制台输出结果
fruits.txt
src/clothes.txt
src/pants.js

方法二:使用通配符

pathspec还可以使用通配符的这种模式去匹配文件和文件夹,通配符有"*“,”?“和”[]“三种符号。需要注意的是:”*"通配符还会在子文件夹中进行文件匹配,其他两种则不会。

*通配符

假设一个项目中有如下目录结构:

└─ fruits.txt
└─ books.js
└─ src
	 └─ clothes.txt
	 └─ pants.js

可以使用"*"通配符匹配项目中的所有带txt后缀的文件:

git ls-files '*.txt'
### 控制台输出
fruits.txt
src/clothes.txt

控制台输出了根目录的fruits.txt和src文件夹下的clothes.txt。

这里需要注意的是,在使用*通配符作为pathspec的时候,如果想同时匹配子集文件夹中的文件则需要在外面加上引号。因为加引号和不加引号会被git作为两种不同的方式进行处理。

在使用pathspec的时候没有加上引号的情况下,会默认使用shell查找文件的方式。比如:

git ls-files *.txt
### 控制台输出
fruits.txt

上面结果只输出了根目录的fruits.txt,和直接使用shell输出文件相同:

ls *.txt
### 控制台输出
fruits.txt

两种方式都只会输出根目录匹配到的fruits.txt文件。

如果在pathspec外面加上了引号,那么引号内的内容会则会以fnmatch(3)的方式去解析文件和文件夹。同时,pathspec也包含"*"通配符的情况下,则还会在子文件夹中继续寻找文件。

其他几种"*"通配符的使用场景:

git ls-files '*.*' // 匹配所有文件,包含子文件夹中的文件
### 控制台输出
books.js
fruits.txt
src/clothes.txt
src/pants.js

git ls-files 'src/*.*' // 只包含src下的所有文件
### 控制台输出
src/clothes.txt
src/pants.js

git ls-files '*/*.js' // 子目录下的js文件,如果src下还有子目录且包含js文件,也会被打印
### 控制台输出
src/pants.js

以上几种方式有点类似于正则表达式的匹配规则。

?通配符

在pathspec中使用?通配符,可以匹配任意单个字符。

假设项目中有如下目录结构:

└─ books.js
└─ books.ts
└─ src
	 └─ books.js
	 └─ books.ts

这时候如果要想找到文件名是books且后缀名是.ts和.js的文件,就可以使用?去匹配,操作如下:

git ls-files 'books.?s' // 匹配books.js和books.ts文件
### 控制台输出
books.js
books.ts

结果如上,在控制台输出了根目录下的books.js和books.ts,但是在src下的相同名称的文件并没有被匹配到。说明?通配符并不会在子目录文件夹中查找。这一点是和*通配符是不一样的。

[]通配符

[]通配符和?的使用方式比较相似,不过它的匹配字符只能在一个集合中筛选。

假设项目中有如下目录结构:

└─ books.js
└─ books.ts
└─ books.ps
└─ src
	 └─ books.js
	 └─ books.ts
	 └─ books.ps

如果要想找到文件名是books且后缀名是.ts和.js的文件,就不能使用?了因为会匹配到.ps结尾的文件,这时候就可以使用[]通配符,操作如下:

git ls-files 'books.[jt]s' // 匹配books.js和books.ts文件
### 控制台输出
books.js
books.ts

结果如上,成功匹配到了books.js和books.ts文件并且没有匹配到books.ps文件。和?通配符相同也不会在子文件夹中继续查找。

[]通配符还有一些比较有趣的用法,比如明确要匹配的单个字符是0-9的数字。假设项目中有如下目录结构:

└─ books.1.js
└─ books.2.ts
└─ books.3.ps
└─ books.4.js
...
└─ books.9.js

现在需要查找所有文件名为books的文件,操作如下:

git ls-files 'books.[[:digit:]].[jtp]s'
### 控制台输出
books.1.js
books.2.ts
books.3.ps
books.4.js
...
books.9.js

结果如上,在控制台成功打印了想要匹配的文件。需要注意的是,使用该操作是需要两个中括号进行包裹。还有其他的操作可以参考手册

方法:使用魔法签名

魔法签名是一种更复杂且更加灵活的匹配方式,使用方式是在pathspect的开头加上:(signature)。signature有以下几种:top、icase、litera、glob、attr和exclude。

top

top签名可以让git命令总是从根目录开始执行不管当前是在哪层目录下。这样当想获知根目录有什么文件的时候,就不用再返回根目录。

假设项目中有如下结构:

└─ books.txt
└─ fruits.txt
└─ src
      └─ pants.txt
      └─ folder    ------ 此时在该目录下
      └─clothes.txt  

在src/folder文件夹中,如果想获取根目录的所有txt文件则可以这样操作:

git ls-files ':(top)*.txt'
### 控制台输出
books.txt
fruits.txt
 
git ls-files ':/*.txt' // 这是一种缩写格式
### 控制台输出
books.txt
fruits.txt

在控制台成功输出了根目录的所有txt文件,还可以将:(top)进行简写使用:/方式。

icase

icase的作用是用于忽略pathspec中的大小写。比如说用于匹配图片时,jpg和JPG的两种后缀的匹配。

假设项目中有如下结构:

└─ pig.jpg
└─ dog.JPG

现在需要匹配所有的jpg图片,可以使用如下操作:

git ls-files ':(icase)*.jpg'
### 控制台输出
pig.jpg
dog.JPG

这样就能忽略大小写,成功匹配到两张图片.

literal

literal可以将某些具有特殊意义的字符作为真正的字面量去匹配,比如说将*和?作为字符串而不是作为通配符。

假设项目中有如下结构:

└─ *.txt
└─ fruits.txt

如果直接使用"*.txt"进行匹配的话会输出*.txt文件和fruits.txt文件。但是现在只想匹配*.txt文件该怎么办?很简单,就是需要将*通配符进行转义,操作如下:

git ls-files ':(literal)*.txt'
#控制台输出
*.txt

如上直接将*转成了字面量字符串进行匹配。

glob

glob魔法签名的作用就是在匹配文件的时候从当前目录出发。格式是在开头加上:(glob),然后在路径中使用两个**。

glob魔法签名的第一种用法就是加在路径的最前面。假设项目中有如下目录结构:

└─ fruits.txt
└─ src
   └─ books.txt
   └─ folder
      └─ fruits.txt

如果只想匹配项目中所有的fruits.txt文件,可以用*通配符去实现么?试试看*/fruits.txt:

git ls-files '*/fruits.txt'
### 控制台输出
src/foler/fruits.txt

如上结果只能输出子目录中的fruits.txt文件,在根目录中的文件并没有被匹配到。这种情况就可以使用glob:

git ls-files ':(glob)**/fruits.txt'
### 控制台输出
fruits.txt
src/folder/fruits.txt

如上结果在根目录和子目录的fruits.txt文件都被匹配到了。

glob还可以加在路径的中间,来表示可以匹配0个或多个文件目录。假设有如下结构目录:

└─ index.html
└─ src
   └─ index.html
   └─ page.html
   └─ header
      └─ index.html
			└─ page.html
			└─ logo
      	 └─ index.html
				 └─ page.html

每一层目录下都有一个index.html和page.html,此时的目标是只想输出src下的所有index.html文件,如果只使用*的话是匹配不到src目录下的index.html文件的:

git ls-files 'src/*/index.html'
### 控制台输出
src/header/index.html
src/header/logo/index.html

这时候可以使用glob模式去匹配:

git ls-files ':(glob)src/**/index.html'
### 控制台输出
src/index.html
src/header/index.html
src/header/logo/index.html

glob还可以加在路径的末尾,表示匹配该路径下的所有文件,使用效果和*差不多,假设有如下目录结构:

└─ index.html
└─ src
   └─ index.html
   └─ header
      └─ index.html
			└─ logo
      	 └─ index.html

现在需要匹配src下的所有index.html文件:

git ls-files 'src/*'
### 控制台输出
src/index.html
src/header/index.html
src/header/logo/index.html

git ls-files ':(glob)src/**'
### 控制台输出
src/index.html
src/header/index.html
src/header/logo/index.html

attr

可以使用.gitattributes文件,将一些比较常用的路径缩用一个属性名去代替。

假设项目中有如下结构:

└─ index.html
└─ src
   └─ index.html
   └─ page.html
   └─ header
      └─ index.html
			└─ page.html
			└─ logo
      	 └─ index.html
				 └─ page.html

其中的logo目录是常用目录,可以将其添加到.gitattributes文件中为它设置快捷属性名:

src/header/logo/* logo

然后就可以在命令中使用:

git ls-files ':(attr:logo)'
### 控制台输出
src/header/logo/index.html
src/header/logo/page.html

还可以使用attr进行取反,也就是不包括快捷属性匹配的所有文件:

git ls-files ':(attr:!logo)'
### 控制台输出
index.html
src/index.html
src/page.html
src/header/index.html
src/header/page.html

.gitattributes文件的用法可以参考git的官网

exclude

这个签名的作用是匹配除了指定路径文件以外的所有文件。该签名具有缩写格式(!或^)

假设项目中有如下目录结构:

└─ index.html
└─ src
   └─ index.html
   └─ header
      └─ index.html
			└─ logo
      	 └─ index.html

假如想匹配除了src/header/logo下的所有文件,那么就可以用如下方法:

git ls-files ':(exclude)src/header/logo/*'
// 或
git ls-files ':!src/header/logo/*'
// 或
git ls-files ':^src/header/logo/*'
#### 控制台输出
index.html
src/index.html
src/header/index.html

组合使用魔法签名

在使用pathspec时可以将多个魔法签名写在一起,然后使用","去分隔。

假设项目中有如下结构:

└─ index.html
└─ src
   └─ index.html
   └─ header
      └─ index.html
			└─ logo
      	 └─ index.html
				 └─ avatar.JPG

如果想匹配除了src/header/logo/avatar.JPG以外的所有文件:

git ls-files ':!(icase,glob,exclude)src/**/*.jpg'
### 控制台输出
index.html
src/index.html
src/header/index.html
src/header/logo/index.html

但是需要注意的是glob和literal不能一起使用。否则就会报错:
image.png

总结

pathspec的作用就是用于限制git命令的作用域范围,但是可以使用通配符或者魔法签名等方式做到更加精细的控制,让git命令的作用域范围更加的灵活多变。

参考

https://marvinsblog.net/post/2019-11-24-git-pathspec-intro/
https://css-tricks.com/git-pathspecs-and-how-to-use-them/
https://git-scm.com/docs/gitglossary/en#def_tree-ish

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值