git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 8/8] Support "**" wildcard in .gitignore and .gitattributes

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Oct 9, 2012, 07:57 UTC
Message-ID
<5073D8FF.8080503@alum.mit.edu>
In-Reply-To
<1349752147-13314-9-git-send-email-pclouds@gmail.com>
I like how this series is going and it's going to be a nice new feature.
 Some comments...
It would be helpful if you would use
    --subject-prefix='PATCH v3'

etc. to help spectators keep track of the different versions of your patch series.

On 10/09/2012 05:09 AM, Nguyễn Thái Ngọc Duy wrote:
Show 29 quoted lines
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  Documentation/gitignore.txt        | 19 +++++++++++++++++++
>  attr.c                             |  4 +++-
>  dir.c                              |  4 +++-
>  t/t0003-attributes.sh              | 38 ++++++++++++++++++++++++++++++++++++++
>  t/t3001-ls-files-others-exclude.sh | 19 +++++++++++++++++++
>  5 files changed, 82 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
> index 96639e0..5a9c9f7 100644
> --- a/Documentation/gitignore.txt
> +++ b/Documentation/gitignore.txt
> @@ -104,6 +104,25 @@ PATTERN FORMAT
>     For example, "/{asterisk}.c" matches "cat-file.c" but not
>     "mozilla-sha1/sha1.c".
>  
> +Two consecutive asterisks ("`**`") in patterns matched against
> +full pathname may have special meaning:
> +
> + - A leading "`**`" followed by a slash means match in all
> +   directories. For example, "`**/foo`" matches file or directory
> +   "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
> +   matches file or directory "`bar`" anywhere that is directly
> +   under directory "`foo`".
> +
> + - A trailing "/**" matches everything inside. For example,
> +   "abc/**" is equivalent to "`/abc/`".

It seems odd that you add a leading slash in this example. I assume that is because of the rule that a pattern containing a slash is considered anchored at the current directory. But I find it confusing because the addition of the leading slash is not part of the rule you are trying to illustrate here, and is therefore a distraction. I suggest that you write either

- A trailing "/**" matches everything inside. For example,
  "/abc/**" is equivalent to "`/abc/`".
or
- A trailing "/**" matches everything inside. For example,
  "abc/**" is equivalent to "`abc/`" (which is also equivalent
  to "`/abc/`").
Show 8 quoted lines
> +
> + - A slash followed by two consecutive asterisks then a slash
> +   matches zero or more directories. For example, "`a/**/b`"
> +   matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.
> +
> + - Consecutive asterisks otherwise are treated like normal
> +   asterisk wildcards.
> +

I don't like the last rule. (1) This construct is superfluous; why wouldn't the user just use a single asterisk? (2) Allowing this construct means that it could appear in .gitignore files, creating unnecessary confusion: extrapolating from the other meanings of "**" users would expect that it would somehow match slashes. (3) It is conceivable (though admittedly unlikely) that we might want to assign a distinct meaning to this construct in the future, and accepting it now as a different way to spell "*" would prevent such a change.

Perhaps this rule was intended for backwards compatibility?

I think it would be preferable to say that other uses of consecutive asterisks are undefined, and probably make them trigger a warning.

Show 123 quoted lines
>  NOTES
>  -----
>  
> diff --git a/attr.c b/attr.c
> index 887a9ae..e85e5ed 100644
> --- a/attr.c
> +++ b/attr.c
> @@ -12,6 +12,7 @@
>  #include "exec_cmd.h"
>  #include "attr.h"
>  #include "dir.h"
> +#include "wildmatch.h"
>  
>  const char git_attr__true[] = "(builtin)true";
>  const char git_attr__false[] = "\0(builtin)false";
> @@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen,
>  		return 0;
>  	if (baselen != 0)
>  		baselen++;
> -	return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
> +	return wildmatch(pattern, pathname + baselen,
> +			 ignore_case ? FNM_CASEFOLD : 0);
>  }
>  
>  static int macroexpand_one(int attr_nr, int rem);
> diff --git a/dir.c b/dir.c
> index 4868339..dc721c0 100644
> --- a/dir.c
> +++ b/dir.c
> @@ -8,6 +8,7 @@
>  #include "cache.h"
>  #include "dir.h"
>  #include "refs.h"
> +#include "wildmatch.h"
>  
>  struct path_simplify {
>  	int len;
> @@ -575,7 +576,8 @@ int excluded_from_list(const char *pathname,
>  			namelen -= prefix;
>  		}
>  
> -		if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
> +		if (!namelen ||
> +		    wildmatch(exclude, name, ignore_case ? FNM_CASEFOLD : 0))
>  			return to_exclude;
>  	}
>  	return -1; /* undecided */
> diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
> index febc45c..67a5694 100755
> --- a/t/t0003-attributes.sh
> +++ b/t/t0003-attributes.sh
> @@ -232,4 +232,42 @@ test_expect_success 'bare repository: test info/attributes' '
>  	attr_check subdir/a/i unspecified
>  '
>  
> +test_expect_success '"**" test' '
> +	cd .. &&
> +	echo "**/f foo=bar" >.gitattributes &&
> +	cat <<\EOF >expect &&
> +f: foo: bar
> +a/f: foo: bar
> +a/b/f: foo: bar
> +a/b/c/f: foo: bar
> +EOF
> +	git check-attr foo -- "f" >actual 2>err &&
> +	git check-attr foo -- "a/f" >>actual 2>>err &&
> +	git check-attr foo -- "a/b/f" >>actual 2>>err &&
> +	git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
> +	test_cmp expect actual &&
> +	test_line_count = 0 err
> +'
> +
> +test_expect_success '"**" with no slashes test' '
> +	echo "a**f foo=bar" >.gitattributes &&
> +	git check-attr foo -- "f" >actual &&
> +	cat <<\EOF >expect &&
> +f: foo: unspecified
> +af: foo: bar
> +axf: foo: bar
> +a/f: foo: unspecified
> +a/b/f: foo: unspecified
> +a/b/c/f: foo: unspecified
> +EOF
> +	git check-attr foo -- "f" >actual 2>err &&
> +	git check-attr foo -- "af" >>actual 2>err &&
> +	git check-attr foo -- "axf" >>actual 2>err &&
> +	git check-attr foo -- "a/f" >>actual 2>>err &&
> +	git check-attr foo -- "a/b/f" >>actual 2>>err &&
> +	git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
> +	test_cmp expect actual &&
> +	test_line_count = 0 err
> +'
> +
>  test_done
> diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
> index c8fe978..278315d 100755
> --- a/t/t3001-ls-files-others-exclude.sh
> +++ b/t/t3001-ls-files-others-exclude.sh
> @@ -214,4 +214,23 @@ test_expect_success 'subdirectory ignore (l1)' '
>  	test_cmp expect actual
>  '
>  
> +
> +test_expect_success 'ls-files with "**" patterns' '
> +	cat <<\EOF >expect &&
> +a.1
> +one/a.1
> +one/two/a.1
> +three/a.1
> +EOF
> +	git ls-files -o -i --exclude "**/a.1" >actual
> +	test_cmp expect actual
> +'
> +
> +
> +test_expect_success 'ls-files with "**" patterns and no slashes' '
> +	: >expect &&
> +	git ls-files -o -i --exclude "one**a.1" >actual &&
> +	test_cmp expect actual
> +'
> +
>  test_done
> 
Michael
-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
Previous: Nguyễn Thái Ngọc DuyNext: Nguyen Thai Ngoc Duy
Message 14 of 15 in “wildmatch take 3”
  1. 0/8 wildmatch take 3Nguyễn Thái Ngọc Duy, Oct 9, 2012
  2. 1/8 Import wildmatch from rsyncNguyễn Thái Ngọc Duy, Oct 9, 2012
  3. 2/8 wildmatch: remove unnecessary functionsNguyễn Thái Ngọc Duy, Oct 9, 2012
  4. 3/8 Integrate wildmatch to gitNguyễn Thái Ngọc Duy, Oct 9, 2012
  5. 4/8 wildmatch: remove static variable force_lower_caseNguyễn Thái Ngọc Duy, Oct 9, 2012
  6. Junio C HamanoOct 9, 2012
  7. Nguyen Thai Ngoc DuyOct 10, 2012
  8. Junio C HamanoOct 10, 2012
  9. Nguyen Thai Ngoc DuyOct 10, 2012
  10. 5/8 wildmatch: fix case-insensitive matchingNguyễn Thái Ngọc Duy, Oct 9, 2012
  11. 6/8 wildmatch: adjust "**" behaviorNguyễn Thái Ngọc Duy, Oct 9, 2012
  12. 7/8 wildmatch: make /**/ match zero or more directoriesNguyễn Thái Ngọc Duy, Oct 9, 2012
  13. 8/8 Support "**" wildcard in .gitignore and .gitattributesNguyễn Thái Ngọc Duy, Oct 9, 2012
  14. Michael HaggertyOct 9, 2012
  15. Nguyen Thai Ngoc DuyOct 10, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.