.gitignore文件中单个/*和双/**尾随星号的区别是什么?

.gitignore文件中单个/*和双/**尾随星号的区别是什么?,git,gitignore,fnmatch,Git,Gitignore,Fnmatch,在.gitignore文件中考虑以下两种模式 foo/* foo/** 各国: 星号*与除斜杠以外的任何东西都匹配。[……] 尾随的/**匹配内部的所有内容。例如,abc/**匹配目录abc中相对于.gitignore文件位置的所有文件,深度无限 在我看来,当直接在斜杠后的模式末尾使用时,这听起来是一样的。我确实测试了一些案例——有、没有foo下面的子目录和各种否定模式——并没有观察到任何差异 是否存在选择/**而不是/*的情况? 起初,我希望看到一个具有如下模式的用例,但是没有,因为这两种

.gitignore
文件中考虑以下两种模式

foo/*
foo/**
各国:

星号
*
与除斜杠以外的任何东西都匹配。[……]

尾随的
/**
匹配内部的所有内容。例如,
abc/**
匹配目录
abc
中相对于.gitignore文件位置的所有文件,深度无限

在我看来,当直接在斜杠后的模式末尾使用时,这听起来是一样的。我确实测试了一些案例——有、没有
foo
下面的子目录和各种否定模式——并没有观察到任何差异

是否存在选择
/**
而不是
/*
的情况?


起初,我希望看到一个具有如下模式的用例,但是没有,因为这两种模式都会忽略其中的所有内容,而且规范还表示“[…]如果文件的父目录被排除[…],则不可能重新包含该文件。”

技术上的差异已经足够明显了。如果您使用的fnmatch函数处理
**
,1并作为模式和字符串对传入:

fnmatch(pattern="foo/**", string="foo/bar/baz")
它会匹配的。但是,使用模式
foo/*
,它将不匹配

然而,由于处理
.gitignore
s的方式,这里没有纯粹正面模式的意义。这是因为你用斜体字写的句子。Git在深度优先搜索工作树之前或期间读取排除文件(
.gitignore
.Git/info/exclude
,以及您的全局排除文件)。这种深度优先搜索使用这种一般形式的代码。我在这里使用了Python作为语法,但并没有真正尝试让所有的功能都发挥作用(也没有尝试提高效率,而Git在内部来说效率很低)

#对目录中的每个文件调用给定函数fn
#(请注意,我们已经承诺读取目录)。
def搜索(目录、排除、fn):
尝试:
以open(os.path.join(dir,“.gitignore”))作为流:
排除=排除。更多(目录、流)
除FileNotFoundError外:
忽略缺少一个.git忽略
所有_文件=os.listdir(dir)
对于所有_文件中的名称:
完整路径=os.path.join(dir,name)
is_dir=os.path.isdir(完整路径)
如果排除.is_排除(名称、路径、is_目录):
继续#不添加此文件或搜索此目录
如果是_dir:
搜索(完整路径,排除,fn)
其他:
fn(完整路径)
(我们将通过
cd
-ing到工作树的顶部并使用
search(“.”,repo.top\u excluder,add\u file)
或类似的方式启动整个过程。这里的top\u excluder字段包含我们的全局和每次回购模式。请注意
排除.more()
必须使用一种数据结构,该结构在递归
搜索
调用返回时自动清除子目录排除,并且需要处理排除程序文件优先级,因为更深的
.gitignore
覆盖外层
.gitignore

这种处理被排除目录的方式是,它根本不需要查看它的内部。这就是这样一个事实的根源:如果只考虑到积极的排除(没有
!foo/**
之类的事情),那么这里就不需要
**
:如果我们已经确定某个目录将被排除,那么它已经与其中的所有内容一起被排除

但我们不仅仅有积极的模式:我们也有消极的模式。考虑,例如,这个非常简单的<代码> GiTigGORE < /Cord>文件:

# ignore things named skip unless they're directories
*skip
!*skip/
否定,
*skip/
,覆盖
*skip
,但仅当名为
fooskip
barskip
的文件或任何实际为目录的文件时。因此,我们确实查看了
fooskip/
,当我们在其中时,我们跳过另一个名为
quoxskip
的文件,但不跳过名为
plughskip
的子目录

这意味着击败Git优化的一个简单方法是:

!*/
这样一行放在
.gitignore
文件的适当位置(靠近或在末尾),会导致搜索所有目录,即使它们会被忽略规则忽略。也就是说,我们的
excludes.is_excluded()
调用将接收本地文件名,不管它是什么,以及is-a-directory测试的
True
标志,以便
*/
将匹配它;前缀
将意味着不会忽略此目录,因此我们将递归搜索它

这一行完全放弃了Git试图在此处进行的优化,因此如果您有应该忽略的目录,则成本相对较高。但是,如果您不想使用更详细的方法,那么这是一种非常快速且肮脏的方法,可以使
.gitignore
表现良好。也就是说,而不是:

foo/*
!foo/one/
foo/one/*
!foo/one/is/
foo/one/is/*
!foo/one/is/important/
foo/one/is/important/*
!foo/one/is/important/this-file
你可以简单地写:

foo/**
!foo/one/is/important/this-file
!foo/**/
这将迫使Git费力地搜索整个
foo
目录及其所有子目录,以便第二条规则可以匹配
foo/one/is/important/This file
文件。这里我们需要双
*
,因为它们的前缀是
foo/
;如果我们将这个
.gitignore
文件放入
foo/.gitignore
中,我们可以使用更简单的
*
表单:

*
!one/is/important/this-file
!*/
在任何情况下,这是一般原则,也是
**
可能有用的原因

(请注意,您也可以在进行第一次提交之前将一个重要文件强制添加到Git的索引,或者在创建将忽略它的
.gitignore
规则之前添加它。我自己不喜欢这个特殊的技巧,因为这意味着您在Git的索引中携带了一个文件,如果它发生的话
*
!one/is/important/this-file
!*/