好的。我需要忽略版本控制中的文件列表,但三个特定文件夹中的文件除外(我们将其命名为Folder1,Folder2和Folder3)。我可以将所有需要忽略的文件夹列出为普通列表,但是我认为这不是一种好方法,因此我编写了以下正则表达式:
.*/(Bin|bin)/(?!Folder1/|Folder2/|Folder3/).*
我的想法从左至右如下:
当我在regex101.com上用几个文本字符串测试表示该文件的路径时,此表达式可以很好地工作,但是将其放入.hgignore文件中时,如下所示,则无用:
syntax: regexp
.*/(Bin|bin)/(?!Folder1/|Folder2/|Folder3/).*
由于某种原因,它会忽略所有“ Bin”和“ bin”文件夹中的所有文件和子文件夹。我该如何完成任务?
P.S。据我所知,Mercurial / TortoiseHG使用Python / Perl正则表达式。
非常感谢。
答案 0 :(得分:1)
要稍微调整一下问题以使其更清楚(至少对我来说),我们提供了任意数量的/bin/somename/...
和.../bin/anothername/...
名称,应将 忽略具有三组.../bin/folder1/...
,.../bin/2folder/...
和.../Bin/third/...
的名称集,应该不被忽略。
因此,我们需要一个正则表达式,该表达式(不使用锚定)将匹配要忽略的名称,而不匹配要保留的名称。 (此外,全局匹配不起作用,因为它不那么强大:我们要么匹配得太少,要么匹配得太多,Mercurial缺少Git的“重写功能,以后不再被忽略”。)
最短的正则表达式应为:
/[Bb]in/(?!(folder1|2folder|third)/)
(此正则表达式中实际上与诸如/bin/somename/...
之类的字符串匹配的部分只是/bin/
部分,但Mercurial不会查看匹配的 ,只有是否匹配。)
问题是,您的示例正则表达式应该有效,它只是同一内容的较长变体,并且不需要但无害(性能除外).*
然后回来。因此,如果您的计算机不起作用,则上述方法也可能不起作用。一个示例库,其中包含一些虚拟文件,可以克隆并进行试验,可以帮助诊断问题。
所需情况的最短正则表达式为:
/[Bb]in/Folder[123]/
但是,如果目录/文件夹名称实际上不符合这种模式,则我们需要:
/[Bb]in/(somedir|another|third)/
首先,请注意:默认语法为regexp,因此不需要syntax: regexp
开头的行。结果,您的.hgignore
文件可能未采用正确的UTF-8格式:请参见Mercurial gives "invalid pattern" error for simple GLOB syntax。 (但这会产生不同的行为,所以这可能是一个问题。在有关.hgignore
文件故障的任何答案中都值得一提。)
接下来,值得注意一些项目:
Mercurial仅跟踪文件,而不跟踪目录/文件夹。因此,真正的问题是,给定的文件名是否与.hgignore
中列出的模式匹配。如果它们确实匹配,和文件当前未跟踪 ,则不会通过“添加所有内容”操作自动添加文件,Mercurial不会抓取该文件未被跟踪。
如果已经跟踪了某个文件,则其名称与忽略模式匹配的事实无关紧要。如果未跟踪文件a/b/c.ext
并且确实匹配模式,则hg add a/b/c.ext
仍将其添加,而hg add a/b
将使{{1 }},但不会添加a/b
,因为它与模式匹配。因此,重要的是要知道文件是否已经被跟踪,并考虑您显式列出到c.ext
的内容。例如,另请参见How to check which files are being ignored because of .hgignore?。
Glob模式比正则表达式更容易正确地编写 。除非您出于学习或教学目的这样做,否则glob不够强大,请坚持使用glob模式。 (在非常老版本的Mercurial中,全局匹配明显比regexp匹配慢,但已修复了很长时间。)
Mercurial的正则表达式忽略条目不会自动锚定:如果需要锚定行为,请根据需要在前面使用hg add
,在末尾使用^
。在这里,您不要想要锚定行为,因此可以消除前导$
和后缀.*
。 (Mercurial将其称为 rooted 而不是 anchred ,并且需要注意的是,某些模式 是固定的,但.hgignore
不是。)
Python / Perl regexp (?!...)
语法是 negation 语法:(?!...)
匹配,如果括号表达式不与串。这是问题的一部分。
我们无需担心捕获组(请参阅capturing group in regex),因为Mercurial对正则表达式中的组不做任何处理。它只在乎我们是否匹配。
路径名实际上是用斜杠分隔的组件。前导组件是文件名上方的各种目录(文件夹),最后一个组件是文件名。 (也就是说,请不要将前一部分视为文件夹:,这并不是说错了,因为它不如“组件”通用,因为最后一部分也是组件。)
在这种情况下,我们要匹配的名称(因此“忽略”)名称具有一个与bin
或Bin
匹配的成分,紧随其后的是与{{1 }},Folder1
或Folder2
,后跟一个组件分隔符(例如,我们没有停止 at Folder3
,这是目录/bin/Folder1
中名为Folder1
的文件。
字符串/bin
和bin
都以Bin
的结尾结尾,因此可以识别为in
,但是更容易表示单字符交替作为字符类:(B|b)in
,从而不需要括号和竖线。
名称[Bb]
,Folder1
和Folder2
的用法相同,只是它们的公用字符串以前导而不是后缀开头,因此我们可以使用Folder3
。
假设我们锚定了比赛。也就是说,假设Mercurial要求我们匹配整个文件名,该文件名可能是Folder[123]
。 然后我们需要/foo/hello/bin/Folder2/bar/world.ext
,因为我们需要匹配任意数量的字符以在匹配.*/[Bb]in/Folder[123]/.*
之前跳过/foo/hello
,并再次跳过任何字符匹配/bin/Folder2/
的字符数,以匹配整个字符串。但是,由于我们没有具有锚定匹配,因此我们将在整个字符串中找到模式bar/world.ext
,因此可以使用更简单的模式(没有前导和尾随{{ 1}}。