Patchwork [stable] match: make glob '**/' match the empty string

login
register
mail settings
Submitter Siddharth Agarwal
Date June 25, 2014, 10:24 p.m.
Message ID <1412c4ef8064ff6fe3c1.1403735049@dev1738.prn1.facebook.com>
Download mbox | patch
Permalink /patch/5064/
State Accepted
Headers show

Comments

Siddharth Agarwal - June 25, 2014, 10:24 p.m.
# HG changeset patch
# User Siddharth Agarwal <sid0@fb.com>
# Date 1403733048 25200
#      Wed Jun 25 14:50:48 2014 -0700
# Branch stable
# Node ID 1412c4ef8064ff6fe3c184870a5c3c094af40a7b
# Parent  b2dc026a9bd2620a0b8c1f28caf5a608352960ab
match: make glob '**/' match the empty string

Previously, a glob pattern of the form 'foo/**/bar' would match 'foo/a/bar' but
not 'foo/bar'. That was because the '**' in 'foo/**/bar' would be translated to
'.*', making the final regex pattern 'foo/.*/bar'. That pattern doesn't match
the string 'foo/bar'.

This is a bug because the '**/' glob matches the empty string in standard Unix
shells like bash and zsh.

Fix that by making the ending '/' optional if an empty string can be matched.
Matt Mackall - June 26, 2014, 11:38 p.m.
On Wed, 2014-06-25 at 15:24 -0700, Siddharth Agarwal wrote:
> # HG changeset patch
> # User Siddharth Agarwal <sid0@fb.com>
> # Date 1403733048 25200
> #      Wed Jun 25 14:50:48 2014 -0700
> # Branch stable
> # Node ID 1412c4ef8064ff6fe3c184870a5c3c094af40a7b
> # Parent  b2dc026a9bd2620a0b8c1f28caf5a608352960ab
> match: make glob '**/' match the empty string

Queued for stable, thanks.

Patch

diff --git a/mercurial/match.py b/mercurial/match.py
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -233,6 +233,10 @@ 
     [^/]*
     >>> print _globre(r'**')
     .*
+    >>> print _globre(r'**/a')
+    (?:.*/)?a
+    >>> print _globre(r'a/**/b')
+    a\/(?:.*/)?b
     >>> print _globre(r'[a*?!^][^b][!c]')
     [a*?!^][\^b][^c]
     >>> print _globre(r'{a,b}')
@@ -254,7 +258,11 @@ 
         elif c == '*':
             if peek() == '*':
                 i += 1
-                res += '.*'
+                if peek() == '/':
+                    i += 1
+                    res += '(?:.*/)?'
+                else:
+                    res += '.*'
             else:
                 res += '[^/]*'
         elif c == '?':
diff --git a/tests/test-hgignore.t b/tests/test-hgignore.t
--- a/tests/test-hgignore.t
+++ b/tests/test-hgignore.t
@@ -134,3 +134,17 @@ 
   ? a.c
   ? a.o
   ? syntax
+
+Check recursive glob pattern matches no directories (dir/**/c.o matches dir/c.o)
+
+  $ echo "syntax: glob" > .hgignore
+  $ echo "dir/**/c.o" >> .hgignore
+  $ touch dir/c.o
+  $ mkdir dir/subdir
+  $ touch dir/subdir/c.o
+  $ hg status
+  A dir/b.o
+  ? .hgignore
+  ? a.c
+  ? a.o
+  ? syntax