{"thread":{"id":"31819","subject":"nd/attr-match-more-optim, nd/wildmatch and as/check-ignore","startedAt":"2012-10-15T06:23:32Z","lastAt":"2012-12-17T00:10:13Z","messageCount":55,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Nguyen Thai Ngoc Duy","Adam Spiers","Johannes Sixt","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"201201","messageId":"1350282212-4270-1-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":null,"subject":"nd/attr-match-more-optim, nd/wildmatch and as/check-ignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:23:32Z","receivedAt":"2012-10-15T06:23:32Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I promise I won't send anything dir.c-related till the end of this\nmonth :) These three series all touch the same code in dir.c and cause\na bunch of conflicts. So I rebase nd/wildmatch and as/check-ignore\non top of nd/attr-match-more-optim and resolve all conflicts.\n\nnd/attr-match-more-optim\n------------------------\n\nA lot of refactoring in dir.c leads to a cleaner last patch to port\nmany exclude optimizations to attr. We can extend EXC_FLAGS_ENDSWITH\noptimization further, from \"^*.[ch]\" to \"path/to/*.[ch]\", but not in\nthis series.\n\nNguyễn Thái Ngọc Duy (6):\n  exclude: stricten a length check in EXC_FLAG_ENDSWITH case\n  exclude: split basename matching code into a separate function\n  exclude: fix a bug in prefix compare optimization\n  exclude: split pathname matching code into a separate function\n  gitignore: make pattern parsing code a separate function\n  attr: more matching optimizations from .gitignore\n\n Documentation/gitattributes.txt    |   1 +\n attr.c                             |  52 ++++++----\n dir.c                              | 192 ++++++++++++++++++++++++-------------\n dir.h                              |  13 ++-\n t/t0003-attributes.sh              |  10 ++\n t/t3001-ls-files-others-exclude.sh |   6 ++\n 6 files changed, 186 insertions(+), 88 deletions(-)\n\nnd/wildmatch\n------------\n\nnew ctype patches that no longer introduce sane_ctype2[]. I also\nre-indent wildmatch.c to follow Git's coding style with the intention\nof making more changes in future (e.g. case insensitive support in\npathspec means we cannot rely on GNU extension FNM_CASEFOLD). Thanks\nto nd/attr-match-more-optim we don't need to make any changes to\nattr.c.\n\nDepends on nd/attr-match-more-optim.\n\nNguyễn Thái Ngọc Duy (13):\n  ctype: make sane_ctype[] const array\n  ctype: support iscntrl, ispunct, isxdigit and isprint\n  Import wildmatch from rsync\n  wildmatch: remove unnecessary functions\n  wildmatch: follow Git's coding convention\n  Integrate wildmatch to git\n  t3070: disable unreliable fnmatch tests\n  wildmatch: make wildmatch's return value compatible with fnmatch\n  wildmatch: remove static variable force_lower_case\n  wildmatch: fix case-insensitive matching\n  wildmatch: adjust \"**\" behavior\n  wildmatch: make /**/ match zero or more directories\n  Support \"**\" wildcard in .gitignore and .gitattributes\n\n .gitignore                         |   1 +\n Documentation/gitignore.txt        |  19 +++\n Makefile                           |   3 +\n ctype.c                            |  15 ++-\n dir.c                              |   4 +-\n git-compat-util.h                  |  15 ++-\n t/t0003-attributes.sh              |  37 ++++++\n t/t3001-ls-files-others-exclude.sh |  18 +++\n t/t3070-wildmatch.sh               | 195 +++++++++++++++++++++++++++++++\n test-wildmatch.c                   |  14 +++\n wildmatch.c                        | 234 +++++++++++++++++++++++++++++++++++++\n wildmatch.h                        |   9 ++\n 12 files changed, 556 insertions(+), 8 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n create mode 100644 test-wildmatch.c\n create mode 100644 wildmatch.c\n create mode 100644 wildmatch.h\n\nas/check-ignore\n---------------\n\nConflict resolution and cleanups. A lot of matching code sharing\nbetween exclude and attr means we might be able to bring check-ignore\nfunctionality to check-attr. But let's leave it for now.\n\nDepends on nd/attr-match-more-optim.\n\nAdam Spiers (12):\n  dir.c: rename cryptic 'which' variable to more consistent name\n  dir.c: rename path_excluded() to is_path_excluded()\n  dir.c: rename excluded_from_list() to is_excluded_from_list()\n  dir.c: rename excluded() to is_excluded()\n  dir.c: refactor is_excluded_from_list()\n  dir.c: refactor is_excluded()\n  dir.c: refactor is_path_excluded()\n  dir.c: keep track of where patterns came from\n  dir.c: refactor treat_gitlinks()\n  pathspec.c: move reusable code from builtin/add.c\n  dir.c: provide free_directory() for reclaiming dir_struct memory\n  Add git-check-ignore sub-command\n\n .gitignore                                        |   1 +\n Documentation/git-check-ignore.txt                |  85 ++++\n Documentation/gitignore.txt                       |   6 +-\n Documentation/technical/api-directory-listing.txt |   2 +\n Makefile                                          |   3 +\n attr.c                                            |   2 +-\n builtin.h                                         |   1 +\n builtin/add.c                                     |  84 +---\n builtin/check-ignore.c                            | 170 +++++++\n builtin/clean.c                                   |   2 +-\n builtin/ls-files.c                                |   5 +-\n command-list.txt                                  |   1 +\n contrib/completion/git-completion.bash            |   1 +\n dir.c                                             | 185 +++++--\n dir.h                                             |  21 +-\n git.c                                             |   1 +\n pathspec.c                                        |  98 ++++\n pathspec.h                                        |   6 +\n t/t0007-ignores.sh                                | 587 ++++++++++++++++++++++\n unpack-trees.c                                    |  10 +-\n 20 files changed, 1135 insertions(+), 136 deletions(-)\n create mode 100644 Documentation/git-check-ignore.txt\n create mode 100644 builtin/check-ignore.c\n create mode 100644 pathspec.c\n create mode 100644 pathspec.h\n create mode 100755 t/t0007-ignores.sh\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201202","messageId":"1350282279-4377-1-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282212-4270-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/6] exclude: stricten a length check in EXC_FLAG_ENDSWITH case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:24:34Z","receivedAt":"2012-10-15T06:24:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This block of code deals with the \"basename\" part only, which has the\nlength of \"pathlen - (basename - pathname)\". Stricten the length check\nand remove \"pathname\" from the main expression to avoid confusion.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 0015cc5..b0ae417 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -534,8 +534,9 @@ int excluded_from_list(const char *pathname,\n \t\t\t\tif (!strcmp_icase(exclude, basename))\n \t\t\t\t\treturn to_exclude;\n \t\t\t} else if (x->flags & EXC_FLAG_ENDSWITH) {\n-\t\t\t\tif (x->patternlen - 1 <= pathlen &&\n-\t\t\t\t    !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))\n+\t\t\t\tint len = pathlen - (basename - pathname);\n+\t\t\t\tif (x->patternlen - 1 <= len &&\n+\t\t\t\t    !strcmp_icase(exclude + 1, basename + len - x->patternlen + 1))\n \t\t\t\t\treturn to_exclude;\n \t\t\t} else {\n \t\t\t\tif (fnmatch_icase(exclude, basename, 0) == 0)\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201203","messageId":"1350282279-4377-2-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282279-4377-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/6] exclude: split basename matching code into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:24:35Z","receivedAt":"2012-10-15T06:24:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 37 ++++++++++++++++++++++++-------------\n 1 file changed, 24 insertions(+), 13 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b0ae417..d9b5561 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -503,6 +503,25 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \tdir->basebuf[baselen] = '\\0';\n }\n \n+static int match_basename(const char *basename, int basenamelen,\n+\t\t\t  const char *pattern, int prefix, int patternlen,\n+\t\t\t  int flags)\n+{\n+\tif (prefix == patternlen) {\n+\t\tif (!strcmp_icase(pattern, basename))\n+\t\t\treturn 1;\n+\t} else if (flags & EXC_FLAG_ENDSWITH) {\n+\t\tif (patternlen - 1 <= basenamelen &&\n+\t\t    !strcmp_icase(pattern + 1,\n+\t\t\t\t  basename + basenamelen - patternlen + 1))\n+\t\t\treturn 1;\n+\t} else {\n+\t\tif (fnmatch_icase(pattern, basename, 0) == 0)\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n /* Scan the list and let the last match determine the fate.\n  * Return 1 for exclude, 0 for include and -1 for undecided.\n  */\n@@ -529,19 +548,11 @@ int excluded_from_list(const char *pathname,\n \t\t}\n \n \t\tif (x->flags & EXC_FLAG_NODIR) {\n-\t\t\t/* match basename */\n-\t\t\tif (prefix == x->patternlen) {\n-\t\t\t\tif (!strcmp_icase(exclude, basename))\n-\t\t\t\t\treturn to_exclude;\n-\t\t\t} else if (x->flags & EXC_FLAG_ENDSWITH) {\n-\t\t\t\tint len = pathlen - (basename - pathname);\n-\t\t\t\tif (x->patternlen - 1 <= len &&\n-\t\t\t\t    !strcmp_icase(exclude + 1, basename + len - x->patternlen + 1))\n-\t\t\t\t\treturn to_exclude;\n-\t\t\t} else {\n-\t\t\t\tif (fnmatch_icase(exclude, basename, 0) == 0)\n-\t\t\t\t\treturn to_exclude;\n-\t\t\t}\n+\t\t\tif (match_basename(basename,\n+\t\t\t\t\t   pathlen - (basename - pathname),\n+\t\t\t\t\t   exclude, prefix, x->patternlen,\n+\t\t\t\t\t   x->flags))\n+\t\t\t\treturn to_exclude;\n \t\t\tcontinue;\n \t\t}\n \n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201204","messageId":"1350282279-4377-3-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282279-4377-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/6] exclude: fix a bug in prefix compare optimization","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:24:36Z","receivedAt":"2012-10-15T06:24:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When \"namelen\" becomes zero at this stage, we have matched the fixed\npart, but whether it actually matches the pattern still depends on the\npattern in \"exclude\". As demonstrated in t3001, path \"three/a.3\"\nexists and it matches the \"three/a.3\" part in pattern \"three/a.3[abc]\",\nbut that does not mean a true match.\n\nDon't be too optimistic and let fnmatch() do the job.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c                              | 2 +-\n t/t3001-ls-files-others-exclude.sh | 6 ++++++\n 2 files changed, 7 insertions(+), 1 deletion(-)\n\ndiff --git a/dir.c b/dir.c\nindex d9b5561..22d0b7b 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -585,7 +585,7 @@ int excluded_from_list(const char *pathname,\n \t\t\tnamelen -= prefix;\n \t\t}\n \n-\t\tif (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))\n+\t\tif (!fnmatch_icase(exclude, name, FNM_PATHNAME))\n \t\t\treturn to_exclude;\n \t}\n \treturn -1; /* undecided */\ndiff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh\nindex c8fe978..dc2f045 100755\n--- a/t/t3001-ls-files-others-exclude.sh\n+++ b/t/t3001-ls-files-others-exclude.sh\n@@ -214,4 +214,10 @@ test_expect_success 'subdirectory ignore (l1)' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'pattern matches prefix completely' '\n+\t: >expect &&\n+\tgit ls-files -i -o --exclude \"/three/a.3[abc]\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201205","messageId":"1350282279-4377-4-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282279-4377-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/6] exclude: split pathname matching code into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:24:37Z","receivedAt":"2012-10-15T06:24:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 85 ++++++++++++++++++++++++++++++++++++++++++-------------------------\n 1 file changed, 53 insertions(+), 32 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 22d0b7b..32d1c90 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -522,6 +522,53 @@ static int match_basename(const char *basename, int basenamelen,\n \treturn 0;\n }\n \n+static int match_pathname(const char *pathname, int pathlen,\n+\t\t\t  const char *base, int baselen,\n+\t\t\t  const char *pattern, int prefix, int patternlen,\n+\t\t\t  int flags)\n+{\n+\tconst char *name;\n+\tint namelen;\n+\n+\t/*\n+\t * match with FNM_PATHNAME; the pattern has base implicitly\n+\t * in front of it.\n+\t */\n+\tif (*pattern == '/') {\n+\t\tpattern++;\n+\t\tprefix--;\n+\t}\n+\n+\t/*\n+\t * baselen does not count the trailing slash. base[] may or\n+\t * may not end with a trailing slash though.\n+\t */\n+\tif (pathlen < baselen + 1 ||\n+\t    (baselen && pathname[baselen] != '/') ||\n+\t    strncmp_icase(pathname, base, baselen))\n+\t\treturn 0;\n+\n+\tnamelen = baselen ? pathlen - baselen - 1 : pathlen;\n+\tname = pathname + pathlen - namelen;\n+\n+\tif (prefix) {\n+\t\t/*\n+\t\t * if the non-wildcard part is longer than the\n+\t\t * remaining pathname, surely it cannot match.\n+\t\t */\n+\t\tif (prefix > namelen)\n+\t\t\treturn 0;\n+\n+\t\tif (strncmp_icase(pattern, name, prefix))\n+\t\t\treturn 0;\n+\t\tpattern += prefix;\n+\t\tname    += prefix;\n+\t\tnamelen -= prefix;\n+\t}\n+\n+\treturn fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;\n+}\n+\n /* Scan the list and let the last match determine the fate.\n  * Return 1 for exclude, 0 for include and -1 for undecided.\n  */\n@@ -536,9 +583,9 @@ int excluded_from_list(const char *pathname,\n \n \tfor (i = el->nr - 1; 0 <= i; i--) {\n \t\tstruct exclude *x = el->excludes[i];\n-\t\tconst char *name, *exclude = x->pattern;\n+\t\tconst char *exclude = x->pattern;\n \t\tint to_exclude = x->to_exclude;\n-\t\tint namelen, prefix = x->nowildcardlen;\n+\t\tint prefix = x->nowildcardlen;\n \n \t\tif (x->flags & EXC_FLAG_MUSTBEDIR) {\n \t\t\tif (*dtype == DT_UNKNOWN)\n@@ -556,36 +603,10 @@ int excluded_from_list(const char *pathname,\n \t\t\tcontinue;\n \t\t}\n \n-\t\t/* match with FNM_PATHNAME:\n-\t\t * exclude has base (baselen long) implicitly in front of it.\n-\t\t */\n-\t\tif (*exclude == '/') {\n-\t\t\texclude++;\n-\t\t\tprefix--;\n-\t\t}\n-\n-\t\tif (pathlen < x->baselen ||\n-\t\t    (x->baselen && pathname[x->baselen-1] != '/') ||\n-\t\t    strncmp_icase(pathname, x->base, x->baselen))\n-\t\t\tcontinue;\n-\n-\t\tnamelen = x->baselen ? pathlen - x->baselen : pathlen;\n-\t\tname = pathname + pathlen  - namelen;\n-\n-\t\t/* if the non-wildcard part is longer than the\n-\t\t   remaining pathname, surely it cannot match */\n-\t\tif (prefix > namelen)\n-\t\t\tcontinue;\n-\n-\t\tif (prefix) {\n-\t\t\tif (strncmp_icase(exclude, name, prefix))\n-\t\t\t\tcontinue;\n-\t\t\texclude += prefix;\n-\t\t\tname    += prefix;\n-\t\t\tnamelen -= prefix;\n-\t\t}\n-\n-\t\tif (!fnmatch_icase(exclude, name, FNM_PATHNAME))\n+\t\tassert(x->baselen == 0 || x->base[x->baselen - 1] == '/');\n+\t\tif (match_pathname(pathname, pathlen,\n+\t\t\t\t   x->base, x->baselen ? x->baselen - 1 : 0,\n+\t\t\t\t   exclude, prefix, x->patternlen, x->flags))\n \t\t\treturn to_exclude;\n \t}\n \treturn -1; /* undecided */\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201206","messageId":"1350282279-4377-5-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282279-4377-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/6] gitignore: make pattern parsing code a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:24:38Z","receivedAt":"2012-10-15T06:24:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function can later be reused by attr.c. Also turn to_exclude\nfield into a flag.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++---------------------\n dir.h |  2 +-\n 2 files changed, 50 insertions(+), 23 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 32d1c90..c4e64a5 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -308,42 +308,69 @@ static int no_wildcard(const char *string)\n \treturn string[simple_length(string)] == '\\0';\n }\n \n+static void parse_exclude_pattern(const char **pattern,\n+\t\t\t\t  int *patternlen,\n+\t\t\t\t  int *flags,\n+\t\t\t\t  int *nowildcardlen)\n+{\n+\tconst char *p = *pattern;\n+\tsize_t i, len;\n+\n+\t*flags = 0;\n+\tif (*p == '!') {\n+\t\t*flags |= EXC_FLAG_NEGATIVE;\n+\t\tp++;\n+\t}\n+\tlen = strlen(p);\n+\tif (len && p[len - 1] == '/') {\n+\t\tlen--;\n+\t\t*flags |= EXC_FLAG_MUSTBEDIR;\n+\t}\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (p[i] == '/')\n+\t\t\tbreak;\n+\t}\n+\tif (i == len)\n+\t\t*flags |= EXC_FLAG_NODIR;\n+\t*nowildcardlen = simple_length(p);\n+\t/*\n+\t * we should have excluded the trailing slash from 'p' too,\n+\t * but that's one more allocation. Instead just make sure\n+\t * nowildcardlen does not exceed real patternlen\n+\t */\n+\tif (*nowildcardlen > len)\n+\t\t*nowildcardlen = len;\n+\tif (*p == '*' && no_wildcard(p + 1))\n+\t\t*flags |= EXC_FLAG_ENDSWITH;\n+\t*pattern = p;\n+\t*patternlen = len;\n+}\n+\n void add_exclude(const char *string, const char *base,\n \t\t int baselen, struct exclude_list *which)\n {\n \tstruct exclude *x;\n-\tsize_t len;\n-\tint to_exclude = 1;\n-\tint flags = 0;\n+\tint patternlen;\n+\tint flags;\n+\tint nowildcardlen;\n \n-\tif (*string == '!') {\n-\t\tto_exclude = 0;\n-\t\tstring++;\n-\t}\n-\tlen = strlen(string);\n-\tif (len && string[len - 1] == '/') {\n+\tparse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);\n+\tif (flags & EXC_FLAG_MUSTBEDIR) {\n \t\tchar *s;\n-\t\tx = xmalloc(sizeof(*x) + len);\n+\t\tx = xmalloc(sizeof(*x) + patternlen + 1);\n \t\ts = (char *)(x+1);\n-\t\tmemcpy(s, string, len - 1);\n-\t\ts[len - 1] = '\\0';\n-\t\tstring = s;\n+\t\tmemcpy(s, string, patternlen);\n+\t\ts[patternlen] = '\\0';\n \t\tx->pattern = s;\n-\t\tflags = EXC_FLAG_MUSTBEDIR;\n \t} else {\n \t\tx = xmalloc(sizeof(*x));\n \t\tx->pattern = string;\n \t}\n-\tx->to_exclude = to_exclude;\n-\tx->patternlen = strlen(string);\n+\tx->patternlen = patternlen;\n+\tx->nowildcardlen = nowildcardlen;\n \tx->base = base;\n \tx->baselen = baselen;\n \tx->flags = flags;\n-\tif (!strchr(string, '/'))\n-\t\tx->flags |= EXC_FLAG_NODIR;\n-\tx->nowildcardlen = simple_length(string);\n-\tif (*string == '*' && no_wildcard(string+1))\n-\t\tx->flags |= EXC_FLAG_ENDSWITH;\n \tALLOC_GROW(which->excludes, which->nr + 1, which->alloc);\n \twhich->excludes[which->nr++] = x;\n }\n@@ -584,7 +611,7 @@ int excluded_from_list(const char *pathname,\n \tfor (i = el->nr - 1; 0 <= i; i--) {\n \t\tstruct exclude *x = el->excludes[i];\n \t\tconst char *exclude = x->pattern;\n-\t\tint to_exclude = x->to_exclude;\n+\t\tint to_exclude = x->flags & EXC_FLAG_NEGATIVE ? 0 : 1;\n \t\tint prefix = x->nowildcardlen;\n \n \t\tif (x->flags & EXC_FLAG_MUSTBEDIR) {\ndiff --git a/dir.h b/dir.h\nindex 893465a..41ea32d 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -11,6 +11,7 @@ struct dir_entry {\n #define EXC_FLAG_NODIR 1\n #define EXC_FLAG_ENDSWITH 4\n #define EXC_FLAG_MUSTBEDIR 8\n+#define EXC_FLAG_NEGATIVE 16\n \n struct exclude_list {\n \tint nr;\n@@ -21,7 +22,6 @@ struct exclude_list {\n \t\tint nowildcardlen;\n \t\tconst char *base;\n \t\tint baselen;\n-\t\tint to_exclude;\n \t\tint flags;\n \t} **excludes;\n };\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201207","messageId":"1350282279-4377-6-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282279-4377-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/6] attr: more matching optimizations from .gitignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:24:39Z","receivedAt":"2012-10-15T06:24:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":".gitattributes and .gitignore share the same pattern syntax but has\nseparate matching implementation. Over the years, ignore's\nimplementation accumulates more optimizations while attr's stays the\nsame.\n\nThis patch reuses the core matching functions that are also used by\nexcluded_from_list. excluded_from_list and path_matches can't be\nmerged due to differences in exclude and attr, for example:\n\n* \"!pattern\" syntax is forbidden in .gitattributes.  As an attribute\n  can be unset (i.e. set to a special value \"false\") or made back to\n  unspecified (i.e. not even set to \"false\"), \"!pattern attr\" is unclear\n  which one it means.\n\n* we support attaching attributes to directories, but git-core\n  internally does not currently make use of attributes on\n  directories.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitattributes.txt |  1 +\n attr.c                          | 52 ++++++++++++++++++++++++-----------------\n dir.c                           | 22 ++++++++---------\n dir.h                           | 11 +++++++++\n t/t0003-attributes.sh           | 10 ++++++++\n 5 files changed, 64 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 80120ea..b7c0e65 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -56,6 +56,7 @@ When more than one pattern matches the path, a later line\n overrides an earlier line.  This overriding is done per\n attribute.  The rules how the pattern matches paths are the\n same as in `.gitignore` files; see linkgit:gitignore[5].\n+Unlike `.gitignore`, negative patterns are forbidden.\n \n When deciding what attributes are assigned to a path, git\n consults `$GIT_DIR/info/attributes` file (which has the highest\ndiff --git a/attr.c b/attr.c\nindex e7caee4..2fc6353 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -115,6 +115,13 @@ struct attr_state {\n \tconst char *setto;\n };\n \n+struct pattern {\n+\tconst char *pattern;\n+\tint patternlen;\n+\tint nowildcardlen;\n+\tint flags;\t\t/* EXC_FLAG_* */\n+};\n+\n /*\n  * One rule, as from a .gitattributes file.\n  *\n@@ -131,7 +138,7 @@ struct attr_state {\n  */\n struct match_attr {\n \tunion {\n-\t\tchar *pattern;\n+\t\tstruct pattern pat;\n \t\tstruct git_attr *attr;\n \t} u;\n \tchar is_macro;\n@@ -241,9 +248,16 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,\n \tif (is_macro)\n \t\tres->u.attr = git_attr_internal(name, namelen);\n \telse {\n-\t\tres->u.pattern = (char *)&(res->state[num_attr]);\n-\t\tmemcpy(res->u.pattern, name, namelen);\n-\t\tres->u.pattern[namelen] = 0;\n+\t\tchar *p = (char *)&(res->state[num_attr]);\n+\t\tmemcpy(p, name, namelen);\n+\t\tres->u.pat.pattern = p;\n+\t\tparse_exclude_pattern(&res->u.pat.pattern,\n+\t\t\t\t      &res->u.pat.patternlen,\n+\t\t\t\t      &res->u.pat.flags,\n+\t\t\t\t      &res->u.pat.nowildcardlen);\n+\t\tif (res->u.pat.flags & EXC_FLAG_NEGATIVE)\n+\t\t\tdie(_(\"Negative patterns are forbidden in git attributes\\n\"\n+\t\t\t      \"Use '\\\\!' for literal leading exclamation.\"));\n \t}\n \tres->is_macro = is_macro;\n \tres->num_attr = num_attr;\n@@ -640,25 +654,21 @@ static void prepare_attr_stack(const char *path)\n \n static int path_matches(const char *pathname, int pathlen,\n \t\t\tconst char *basename,\n-\t\t\tconst char *pattern,\n+\t\t\tconst struct pattern *pat,\n \t\t\tconst char *base, int baselen)\n {\n-\tif (!strchr(pattern, '/')) {\n-\t\treturn (fnmatch_icase(pattern, basename, 0) == 0);\n+\tconst char *pattern = pat->pattern;\n+\tint prefix = pat->nowildcardlen;\n+\n+\tif (pat->flags & EXC_FLAG_NODIR) {\n+\t\treturn match_basename(basename,\n+\t\t\t\t      pathlen - (basename - pathname),\n+\t\t\t\t      pattern, prefix,\n+\t\t\t\t      pat->patternlen, pat->flags);\n \t}\n-\t/*\n-\t * match with FNM_PATHNAME; the pattern has base implicitly\n-\t * in front of it.\n-\t */\n-\tif (*pattern == '/')\n-\t\tpattern++;\n-\tif (pathlen < baselen ||\n-\t    (baselen && pathname[baselen] != '/') ||\n-\t    strncmp(pathname, base, baselen))\n-\t\treturn 0;\n-\tif (baselen != 0)\n-\t\tbaselen++;\n-\treturn fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;\n+\treturn match_pathname(pathname, pathlen,\n+\t\t\t      base, baselen,\n+\t\t\t      pattern, prefix, pat->patternlen, pat->flags);\n }\n \n static int macroexpand_one(int attr_nr, int rem);\n@@ -696,7 +706,7 @@ static int fill(const char *path, int pathlen, const char *basename,\n \t\tif (a->is_macro)\n \t\t\tcontinue;\n \t\tif (path_matches(path, pathlen, basename,\n-\t\t\t\t a->u.pattern, base, stk->originlen))\n+\t\t\t\t &a->u.pat, base, stk->originlen))\n \t\t\trem = fill_one(\"fill\", a, rem);\n \t}\n \treturn rem;\ndiff --git a/dir.c b/dir.c\nindex c4e64a5..ee8e711 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -308,10 +308,10 @@ static int no_wildcard(const char *string)\n \treturn string[simple_length(string)] == '\\0';\n }\n \n-static void parse_exclude_pattern(const char **pattern,\n-\t\t\t\t  int *patternlen,\n-\t\t\t\t  int *flags,\n-\t\t\t\t  int *nowildcardlen)\n+void parse_exclude_pattern(const char **pattern,\n+\t\t\t   int *patternlen,\n+\t\t\t   int *flags,\n+\t\t\t   int *nowildcardlen)\n {\n \tconst char *p = *pattern;\n \tsize_t i, len;\n@@ -530,9 +530,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \tdir->basebuf[baselen] = '\\0';\n }\n \n-static int match_basename(const char *basename, int basenamelen,\n-\t\t\t  const char *pattern, int prefix, int patternlen,\n-\t\t\t  int flags)\n+int match_basename(const char *basename, int basenamelen,\n+\t\t   const char *pattern, int prefix, int patternlen,\n+\t\t   int flags)\n {\n \tif (prefix == patternlen) {\n \t\tif (!strcmp_icase(pattern, basename))\n@@ -549,10 +549,10 @@ static int match_basename(const char *basename, int basenamelen,\n \treturn 0;\n }\n \n-static int match_pathname(const char *pathname, int pathlen,\n-\t\t\t  const char *base, int baselen,\n-\t\t\t  const char *pattern, int prefix, int patternlen,\n-\t\t\t  int flags)\n+int match_pathname(const char *pathname, int pathlen,\n+\t\t   const char *base, int baselen,\n+\t\t   const char *pattern, int prefix, int patternlen,\n+\t\t   int flags)\n {\n \tconst char *name;\n \tint namelen;\ndiff --git a/dir.h b/dir.h\nindex 41ea32d..f5c89e3 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -81,6 +81,16 @@ extern int excluded_from_list(const char *pathname, int pathlen, const char *bas\n struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);\n \n /*\n+ * these implement the matching logic for dir.c:excluded_from_list and\n+ * attr.c:path_matches()\n+ */\n+extern int match_basename(const char *, int,\n+\t\t\t  const char *, int, int, int);\n+extern int match_pathname(const char *, int,\n+\t\t\t  const char *, int,\n+\t\t\t  const char *, int, int, int);\n+\n+/*\n  * The excluded() API is meant for callers that check each level of leading\n  * directory hierarchies with excluded() to avoid recursing into excluded\n  * directories.  Callers that do not do so should use this API instead.\n@@ -97,6 +107,7 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,\n extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\n \t\t\t\t\t  char **buf_p, struct exclude_list *which, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n+extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);\n extern void add_exclude(const char *string, const char *base,\n \t\t\tint baselen, struct exclude_list *which);\n extern void free_excludes(struct exclude_list *el);\ndiff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh\nindex 51f3045..f6c21ea 100755\n--- a/t/t0003-attributes.sh\n+++ b/t/t0003-attributes.sh\n@@ -206,6 +206,16 @@ test_expect_success 'root subdir attribute test' '\n \tattr_check subdir/a/i unspecified\n '\n \n+test_expect_success 'negative patterns' '\n+\techo \"!f test=bar\" >.gitattributes &&\n+\ttest_must_fail git check-attr test -- f\n+'\n+\n+test_expect_success 'patterns starting with exclamation' '\n+\techo \"\\!f test=foo\" >.gitattributes &&\n+\tattr_check \"!f\" foo\n+'\n+\n test_expect_success 'setup bare' '\n \tgit clone --bare . bare.git &&\n \tcd bare.git\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201208","messageId":"1350282362-4505-1-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282212-4270-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/13] ctype: make sane_ctype[] const array","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:50Z","receivedAt":"2012-10-15T06:25:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n ctype.c           | 2 +-\n git-compat-util.h | 2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/ctype.c b/ctype.c\nindex 9353271..faeaf34 100644\n--- a/ctype.c\n+++ b/ctype.c\n@@ -14,7 +14,7 @@ enum {\n \tP = GIT_PATHSPEC_MAGIC  /* other non-alnum, except for ] and } */\n };\n \n-unsigned char sane_ctype[256] = {\n+const unsigned char sane_ctype[256] = {\n \t0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,\t\t/*   0.. 15 */\n \t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\t\t/*  16.. 31 */\n \tS, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P,\t\t/*  32.. 47 */\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 5bd9ad7..80767ff 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -470,7 +470,7 @@ extern const char tolower_trans_tbl[256];\n #undef isupper\n #undef tolower\n #undef toupper\n-extern unsigned char sane_ctype[256];\n+extern const unsigned char sane_ctype[256];\n #define GIT_SPACE 0x01\n #define GIT_DIGIT 0x02\n #define GIT_ALPHA 0x04\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201209","messageId":"1350282362-4505-2-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/13] ctype: support iscntrl, ispunct, isxdigit and isprint","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:51Z","receivedAt":"2012-10-15T06:25:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n ctype.c           | 13 ++++++++-----\n git-compat-util.h | 13 +++++++++++++\n 2 files changed, 21 insertions(+), 5 deletions(-)\n\ndiff --git a/ctype.c b/ctype.c\nindex faeaf34..0bfebb4 100644\n--- a/ctype.c\n+++ b/ctype.c\n@@ -11,18 +11,21 @@ enum {\n \tD = GIT_DIGIT,\n \tG = GIT_GLOB_SPECIAL,\t/* *, ?, [, \\\\ */\n \tR = GIT_REGEX_SPECIAL,\t/* $, (, ), +, ., ^, {, | */\n-\tP = GIT_PATHSPEC_MAGIC  /* other non-alnum, except for ] and } */\n+\tP = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */\n+\tX = GIT_CNTRL,\n+\tU = GIT_PUNCT,\n+\tZ = GIT_CNTRL | GIT_SPACE\n };\n \n const unsigned char sane_ctype[256] = {\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,\t\t/*   0.. 15 */\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\t\t/*  16.. 31 */\n+\tX, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X,\t\t/*   0.. 15 */\n+\tX, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,\t\t/*  16.. 31 */\n \tS, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P,\t\t/*  32.. 47 */\n \tD, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G,\t\t/*  48.. 63 */\n \tP, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,\t\t/*  64.. 79 */\n-\tA, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P,\t\t/*  80.. 95 */\n+\tA, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P,\t\t/*  80.. 95 */\n \tP, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,\t\t/*  96..111 */\n-\tA, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0,\t\t/* 112..127 */\n+\tA, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X,\t\t/* 112..127 */\n \t/* Nothing in the 128.. range */\n };\n \ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 80767ff..02f48f6 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -470,6 +470,10 @@ extern const char tolower_trans_tbl[256];\n #undef isupper\n #undef tolower\n #undef toupper\n+#undef iscntrl\n+#undef ispunct\n+#undef isxdigit\n+#undef isprint\n extern const unsigned char sane_ctype[256];\n #define GIT_SPACE 0x01\n #define GIT_DIGIT 0x02\n@@ -477,6 +481,8 @@ extern const unsigned char sane_ctype[256];\n #define GIT_GLOB_SPECIAL 0x08\n #define GIT_REGEX_SPECIAL 0x10\n #define GIT_PATHSPEC_MAGIC 0x20\n+#define GIT_CNTRL 0x40\n+#define GIT_PUNCT 0x80\n #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)\n #define isascii(x) (((x) & ~0x7f) == 0)\n #define isspace(x) sane_istest(x,GIT_SPACE)\n@@ -487,6 +493,13 @@ extern const unsigned char sane_ctype[256];\n #define isupper(x) sane_iscase(x, 0)\n #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)\n #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)\n+#define iscntrl(x) (sane_istest(x,GIT_CNTRL))\n+#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \\\n+\t\tGIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)\n+#define isxdigit(x) (hexval_table[x] != -1)\n+#define isprint(x) (sane_istest(x, GIT_ALPHA | GIT_DIGIT | GIT_SPACE | \\\n+\t\tGIT_PUNCT | GIT_REGEX_SPECIAL | GIT_GLOB_SPECIAL | \\\n+\t\tGIT_PATHSPEC_MAGIC))\n #define tolower(x) sane_case((unsigned char)(x), 0x20)\n #define toupper(x) sane_case((unsigned char)(x), 0)\n #define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201210","messageId":"1350282362-4505-3-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/13] Import wildmatch from rsync","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:52Z","receivedAt":"2012-10-15T06:25:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These files are from rsync.git commit\nf92f5b166e3019db42bc7fe1aa2f1a9178cd215d, which was the last commit\nbefore rsync turned GPL-3. All files are imported as-is and\nno-op. Adaptation is done in a separate patch.\n\nrsync.git           ->  git.git\nlib/wildmatch.[ch]      wildmatch.[ch]\nwildtest.txt            t/t3070/wildtest.txt\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070/wildtest.txt | 165 +++++++++++++++++++++++\n wildmatch.c          | 368 +++++++++++++++++++++++++++++++++++++++++++++++++++\n wildmatch.h          |   6 +\n 3 files changed, 539 insertions(+)\n create mode 100644 t/t3070/wildtest.txt\n create mode 100644 wildmatch.c\n create mode 100644 wildmatch.h\n\ndiff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt\nnew file mode 100644\nindex 0000000..42c1678\n--- /dev/null\n+++ b/t/t3070/wildtest.txt\n@@ -0,0 +1,165 @@\n+# Input is in the following format (all items white-space separated):\n+#\n+# The first two items are 1 or 0 indicating if the wildmat call is expected to\n+# succeed and if fnmatch works the same way as wildmat, respectively.  After\n+# that is a text string for the match, and a pattern string.  Strings can be\n+# quoted (if desired) in either double or single quotes, as well as backticks.\n+#\n+# MATCH FNMATCH_SAME \"text to match\" 'pattern to use'\n+\n+# Basic wildmat features\n+1 1 foo\t\t\tfoo\n+0 1 foo\t\t\tbar\n+1 1 ''\t\t\t\"\"\n+1 1 foo\t\t\t???\n+0 1 foo\t\t\t??\n+1 1 foo\t\t\t*\n+1 1 foo\t\t\tf*\n+0 1 foo\t\t\t*f\n+1 1 foo\t\t\t*foo*\n+1 1 foobar\t\t*ob*a*r*\n+1 1 aaaaaaabababab\t*ab\n+1 1 foo*\t\tfoo\\*\n+0 1 foobar\t\tfoo\\*bar\n+1 1 f\\oo\t\tf\\\\oo\n+1 1 ball\t\t*[al]?\n+0 1 ten\t\t\t[ten]\n+1 1 ten\t\t\t**[!te]\n+0 1 ten\t\t\t**[!ten]\n+1 1 ten\t\t\tt[a-g]n\n+0 1 ten\t\t\tt[!a-g]n\n+1 1 ton\t\t\tt[!a-g]n\n+1 1 ton\t\t\tt[^a-g]n\n+1 1 a]b\t\t\ta[]]b\n+1 1 a-b\t\t\ta[]-]b\n+1 1 a]b\t\t\ta[]-]b\n+0 1 aab\t\t\ta[]-]b\n+1 1 aab\t\t\ta[]a-]b\n+1 1 ]\t\t\t]\n+\n+# Extended slash-matching features\n+0 1 foo/baz/bar\t\tfoo*bar\n+1 1 foo/baz/bar\t\tfoo**bar\n+0 1 foo/bar\t\tfoo?bar\n+0 1 foo/bar\t\tfoo[/]bar\n+0 1 foo/bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n+1 1 foo-bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n+0 1 foo\t\t\t**/foo\n+1 1 /foo\t\t**/foo\n+1 1 bar/baz/foo\t\t**/foo\n+0 1 bar/baz/foo\t\t*/foo\n+0 0 foo/bar/baz\t\t**/bar*\n+1 1 deep/foo/bar/baz\t**/bar/*\n+0 1 deep/foo/bar/baz/\t**/bar/*\n+1 1 deep/foo/bar/baz/\t**/bar/**\n+0 1 deep/foo/bar\t**/bar/*\n+1 1 deep/foo/bar/\t**/bar/**\n+1 1 foo/bar/baz\t\t**/bar**\n+1 1 foo/bar/baz/x\t*/bar/**\n+0 0 deep/foo/bar/baz/x\t*/bar/**\n+1 1 deep/foo/bar/baz/x\t**/bar/*/*\n+\n+# Various additional tests\n+0 1 acrt\t\ta[c-c]st\n+1 1 acrt\t\ta[c-c]rt\n+0 1 ]\t\t\t[!]-]\n+1 1 a\t\t\t[!]-]\n+0 1 ''\t\t\t\\\n+0 1 \\\t\t\t\\\n+0 1 /\\\t\t\t*/\\\n+1 1 /\\\t\t\t*/\\\\\n+1 1 foo\t\t\tfoo\n+1 1 @foo\t\t@foo\n+0 1 foo\t\t\t@foo\n+1 1 [ab]\t\t\\[ab]\n+1 1 [ab]\t\t[[]ab]\n+1 1 [ab]\t\t[[:]ab]\n+0 1 [ab]\t\t[[::]ab]\n+1 1 [ab]\t\t[[:digit]ab]\n+1 1 [ab]\t\t[\\[:]ab]\n+1 1 ?a?b\t\t\\??\\?b\n+1 1 abc\t\t\t\\a\\b\\c\n+0 1 foo\t\t\t''\n+1 1 foo/bar/baz/to\t**/t[o]\n+\n+# Character class tests\n+1 1 a1B\t\t[[:alpha:]][[:digit:]][[:upper:]]\n+0 1 a\t\t[[:digit:][:upper:][:space:]]\n+1 1 A\t\t[[:digit:][:upper:][:space:]]\n+1 1 1\t\t[[:digit:][:upper:][:space:]]\n+0 1 1\t\t[[:digit:][:upper:][:spaci:]]\n+1 1 ' '\t\t[[:digit:][:upper:][:space:]]\n+0 1 .\t\t[[:digit:][:upper:][:space:]]\n+1 1 .\t\t[[:digit:][:punct:][:space:]]\n+1 1 5\t\t[[:xdigit:]]\n+1 1 f\t\t[[:xdigit:]]\n+1 1 D\t\t[[:xdigit:]]\n+1 1 _\t\t[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+#1 1 �\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+1 1 \t\t[^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+1 1 .\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]\n+1 1 5\t\t[a-c[:digit:]x-z]\n+1 1 b\t\t[a-c[:digit:]x-z]\n+1 1 y\t\t[a-c[:digit:]x-z]\n+0 1 q\t\t[a-c[:digit:]x-z]\n+\n+# Additional tests, including some malformed wildmats\n+1 1 ]\t\t[\\\\-^]\n+0 1 [\t\t[\\\\-^]\n+1 1 -\t\t[\\-_]\n+1 1 ]\t\t[\\]]\n+0 1 \\]\t\t[\\]]\n+0 1 \\\t\t[\\]]\n+0 1 ab\t\ta[]b\n+0 1 a[]b\ta[]b\n+0 1 ab[\t\tab[\n+0 1 ab\t\t[!\n+0 1 ab\t\t[-\n+1 1 -\t\t[-]\n+0 1 -\t\t[a-\n+0 1 -\t\t[!a-\n+1 1 -\t\t[--A]\n+1 1 5\t\t[--A]\n+1 1 ' '\t\t'[ --]'\n+1 1 $\t\t'[ --]'\n+1 1 -\t\t'[ --]'\n+0 1 0\t\t'[ --]'\n+1 1 -\t\t[---]\n+1 1 -\t\t[------]\n+0 1 j\t\t[a-e-n]\n+1 1 -\t\t[a-e-n]\n+1 1 a\t\t[!------]\n+0 1 [\t\t[]-a]\n+1 1 ^\t\t[]-a]\n+0 1 ^\t\t[!]-a]\n+1 1 [\t\t[!]-a]\n+1 1 ^\t\t[a^bc]\n+1 1 -b]\t\t[a-]b]\n+0 1 \\\t\t[\\]\n+1 1 \\\t\t[\\\\]\n+0 1 \\\t\t[!\\\\]\n+1 1 G\t\t[A-\\\\]\n+0 1 aaabbb\tb*a\n+0 1 aabcaa\t*ba*\n+1 1 ,\t\t[,]\n+1 1 ,\t\t[\\\\,]\n+1 1 \\\t\t[\\\\,]\n+1 1 -\t\t[,-.]\n+0 1 +\t\t[,-.]\n+0 1 -.]\t\t[,-.]\n+1 1 2\t\t[\\1-\\3]\n+1 1 3\t\t[\\1-\\3]\n+0 1 4\t\t[\\1-\\3]\n+1 1 \\\t\t[[-\\]]\n+1 1 [\t\t[[-\\]]\n+1 1 ]\t\t[[-\\]]\n+0 1 -\t\t[[-\\]]\n+\n+# Test recursion and the abort code (use \"wildtest -i\" to see iteration counts)\n+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt\t\t**/*a*b*g*n*t\n+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz\t\t**/*a*b*g*n*t\ndiff --git a/wildmatch.c b/wildmatch.c\nnew file mode 100644\nindex 0000000..f3a1731\n--- /dev/null\n+++ b/wildmatch.c\n@@ -0,0 +1,368 @@\n+/*\n+**  Do shell-style pattern matching for ?, \\, [], and * characters.\n+**  It is 8bit clean.\n+**\n+**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.\n+**  Rich $alz is now <rsalz@bbn.com>.\n+**\n+**  Modified by Wayne Davison to special-case '/' matching, to make '**'\n+**  work differently than '*', and to fix the character-class code.\n+*/\n+\n+#include \"rsync.h\"\n+\n+/* What character marks an inverted character class? */\n+#define NEGATE_CLASS\t'!'\n+#define NEGATE_CLASS2\t'^'\n+\n+#define FALSE 0\n+#define TRUE 1\n+#define ABORT_ALL -1\n+#define ABORT_TO_STARSTAR -2\n+\n+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \\\n+\t\t\t\t    && *(class) == *(litmatch) \\\n+\t\t\t\t    && strncmp((char*)class, litmatch, len) == 0)\n+\n+#if defined STDC_HEADERS || !defined isascii\n+# define ISASCII(c) 1\n+#else\n+# define ISASCII(c) isascii(c)\n+#endif\n+\n+#ifdef isblank\n+# define ISBLANK(c) (ISASCII(c) && isblank(c))\n+#else\n+# define ISBLANK(c) ((c) == ' ' || (c) == '\\t')\n+#endif\n+\n+#ifdef isgraph\n+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))\n+#else\n+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))\n+#endif\n+\n+#define ISPRINT(c) (ISASCII(c) && isprint(c))\n+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))\n+#define ISALNUM(c) (ISASCII(c) && isalnum(c))\n+#define ISALPHA(c) (ISASCII(c) && isalpha(c))\n+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))\n+#define ISLOWER(c) (ISASCII(c) && islower(c))\n+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))\n+#define ISSPACE(c) (ISASCII(c) && isspace(c))\n+#define ISUPPER(c) (ISASCII(c) && isupper(c))\n+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n+\n+#ifdef WILD_TEST_ITERATIONS\n+int wildmatch_iteration_count;\n+#endif\n+\n+static int force_lower_case = 0;\n+\n+/* Match pattern \"p\" against the a virtually-joined string consisting\n+ * of \"text\" and any strings in array \"a\". */\n+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n+{\n+    uchar p_ch;\n+\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count++;\n+#endif\n+\n+    for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n+\tint matched, special;\n+\tuchar t_ch, prev_ch;\n+\twhile ((t_ch = *text) == '\\0') {\n+\t    if (*a == NULL) {\n+\t\tif (p_ch != '*')\n+\t\t    return ABORT_ALL;\n+\t\tbreak;\n+\t    }\n+\t    text = *a++;\n+\t}\n+\tif (force_lower_case && ISUPPER(t_ch))\n+\t    t_ch = tolower(t_ch);\n+\tswitch (p_ch) {\n+\t  case '\\\\':\n+\t    /* Literal match with following character.  Note that the test\n+\t     * in \"default\" handles the p[1] == '\\0' failure case. */\n+\t    p_ch = *++p;\n+\t    /* FALLTHROUGH */\n+\t  default:\n+\t    if (t_ch != p_ch)\n+\t\treturn FALSE;\n+\t    continue;\n+\t  case '?':\n+\t    /* Match anything but '/'. */\n+\t    if (t_ch == '/')\n+\t\treturn FALSE;\n+\t    continue;\n+\t  case '*':\n+\t    if (*++p == '*') {\n+\t\twhile (*++p == '*') {}\n+\t\tspecial = TRUE;\n+\t    } else\n+\t\tspecial = FALSE;\n+\t    if (*p == '\\0') {\n+\t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n+\t\t * only if there are no more slash characters. */\n+\t\tif (!special) {\n+\t\t    do {\n+\t\t\tif (strchr((char*)text, '/') != NULL)\n+\t\t\t    return FALSE;\n+\t\t    } while ((text = *a++) != NULL);\n+\t\t}\n+\t\treturn TRUE;\n+\t    }\n+\t    while (1) {\n+\t\tif (t_ch == '\\0') {\n+\t\t    if ((text = *a++) == NULL)\n+\t\t\tbreak;\n+\t\t    t_ch = *text;\n+\t\t    continue;\n+\t\t}\n+\t\tif ((matched = dowild(p, text, a)) != FALSE) {\n+\t\t    if (!special || matched != ABORT_TO_STARSTAR)\n+\t\t\treturn matched;\n+\t\t} else if (!special && t_ch == '/')\n+\t\t    return ABORT_TO_STARSTAR;\n+\t\tt_ch = *++text;\n+\t    }\n+\t    return ABORT_ALL;\n+\t  case '[':\n+\t    p_ch = *++p;\n+#ifdef NEGATE_CLASS2\n+\t    if (p_ch == NEGATE_CLASS2)\n+\t\tp_ch = NEGATE_CLASS;\n+#endif\n+\t    /* Assign literal TRUE/FALSE because of \"matched\" comparison. */\n+\t    special = p_ch == NEGATE_CLASS? TRUE : FALSE;\n+\t    if (special) {\n+\t\t/* Inverted character class. */\n+\t\tp_ch = *++p;\n+\t    }\n+\t    prev_ch = 0;\n+\t    matched = FALSE;\n+\t    do {\n+\t\tif (!p_ch)\n+\t\t    return ABORT_ALL;\n+\t\tif (p_ch == '\\\\') {\n+\t\t    p_ch = *++p;\n+\t\t    if (!p_ch)\n+\t\t\treturn ABORT_ALL;\n+\t\t    if (t_ch == p_ch)\n+\t\t\tmatched = TRUE;\n+\t\t} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {\n+\t\t    p_ch = *++p;\n+\t\t    if (p_ch == '\\\\') {\n+\t\t\tp_ch = *++p;\n+\t\t\tif (!p_ch)\n+\t\t\t    return ABORT_ALL;\n+\t\t    }\n+\t\t    if (t_ch <= p_ch && t_ch >= prev_ch)\n+\t\t\tmatched = TRUE;\n+\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t} else if (p_ch == '[' && p[1] == ':') {\n+\t\t    const uchar *s;\n+\t\t    int i;\n+\t\t    for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/\n+\t\t    if (!p_ch)\n+\t\t\treturn ABORT_ALL;\n+\t\t    i = p - s - 1;\n+\t\t    if (i < 0 || p[-1] != ':') {\n+\t\t\t/* Didn't find \":]\", so treat like a normal set. */\n+\t\t\tp = s - 2;\n+\t\t\tp_ch = '[';\n+\t\t\tif (t_ch == p_ch)\n+\t\t\t    matched = TRUE;\n+\t\t\tcontinue;\n+\t\t    }\n+\t\t    if (CC_EQ(s,i, \"alnum\")) {\n+\t\t\tif (ISALNUM(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"alpha\")) {\n+\t\t\tif (ISALPHA(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"blank\")) {\n+\t\t\tif (ISBLANK(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"cntrl\")) {\n+\t\t\tif (ISCNTRL(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"digit\")) {\n+\t\t\tif (ISDIGIT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"graph\")) {\n+\t\t\tif (ISGRAPH(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"lower\")) {\n+\t\t\tif (ISLOWER(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"print\")) {\n+\t\t\tif (ISPRINT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"punct\")) {\n+\t\t\tif (ISPUNCT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"space\")) {\n+\t\t\tif (ISSPACE(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"upper\")) {\n+\t\t\tif (ISUPPER(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"xdigit\")) {\n+\t\t\tif (ISXDIGIT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else /* malformed [:class:] string */\n+\t\t\treturn ABORT_ALL;\n+\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t} else if (t_ch == p_ch)\n+\t\t    matched = TRUE;\n+\t    } while (prev_ch = p_ch, (p_ch = *++p) != ']');\n+\t    if (matched == special || t_ch == '/')\n+\t\treturn FALSE;\n+\t    continue;\n+\t}\n+    }\n+\n+    do {\n+\tif (*text)\n+\t    return FALSE;\n+    } while ((text = *a++) != NULL);\n+\n+    return TRUE;\n+}\n+\n+/* Match literal string \"s\" against the a virtually-joined string consisting\n+ * of \"text\" and any strings in array \"a\". */\n+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)\n+{\n+    for ( ; *s != '\\0'; text++, s++) {\n+\twhile (*text == '\\0') {\n+\t    if ((text = *a++) == NULL)\n+\t\treturn FALSE;\n+\t}\n+\tif (*text != *s)\n+\t    return FALSE;\n+    }\n+\n+    do {\n+\tif (*text)\n+\t    return FALSE;\n+    } while ((text = *a++) != NULL);\n+\n+    return TRUE;\n+}\n+\n+/* Return the last \"count\" path elements from the concatenated string.\n+ * We return a string pointer to the start of the string, and update the\n+ * array pointer-pointer to point to any remaining string elements. */\n+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)\n+{\n+    const uchar*const *a = *a_ptr;\n+    const uchar*const *first_a = a;\n+\n+    while (*a)\n+\t    a++;\n+\n+    while (a != first_a) {\n+\tconst uchar *s = *--a;\n+\ts += strlen((char*)s);\n+\twhile (--s >= *a) {\n+\t    if (*s == '/' && !--count) {\n+\t\t*a_ptr = a+1;\n+\t\treturn s+1;\n+\t    }\n+\t}\n+    }\n+\n+    if (count == 1) {\n+\t*a_ptr = a+1;\n+\treturn *a;\n+    }\n+\n+    return NULL;\n+}\n+\n+/* Match the \"pattern\" against the \"text\" string. */\n+int wildmatch(const char *pattern, const char *text)\n+{\n+    static const uchar *nomore[1]; /* A NULL pointer. */\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+}\n+\n+/* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n+int iwildmatch(const char *pattern, const char *text)\n+{\n+    static const uchar *nomore[1]; /* A NULL pointer. */\n+    int ret;\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+    force_lower_case = 1;\n+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    force_lower_case = 0;\n+    return ret;\n+}\n+\n+/* Match pattern \"p\" against the a virtually-joined string consisting\n+ * of all the pointers in array \"texts\" (which has a NULL pointer at the\n+ * end).  The int \"where\" can be 0 (normal matching), > 0 (match only\n+ * the trailing N slash-separated filename components of \"texts\"), or < 0\n+ * (match the \"pattern\" at the start or after any slash in \"texts\"). */\n+int wildmatch_array(const char *pattern, const char*const *texts, int where)\n+{\n+    const uchar *p = (const uchar*)pattern;\n+    const uchar*const *a = (const uchar*const*)texts;\n+    const uchar *text;\n+    int matched;\n+\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+\n+    if (where > 0)\n+\ttext = trailing_N_elements(&a, where);\n+    else\n+\ttext = *a++;\n+    if (!text)\n+\treturn FALSE;\n+\n+    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n+     && matched != ABORT_ALL) {\n+\twhile (1) {\n+\t    if (*text == '\\0') {\n+\t\tif ((text = (uchar*)*a++) == NULL)\n+\t\t    return FALSE;\n+\t\tcontinue;\n+\t    }\n+\t    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n+\t     && matched != ABORT_TO_STARSTAR)\n+\t\tbreak;\n+\t}\n+    }\n+    return matched == TRUE;\n+}\n+\n+/* Match literal string \"s\" against the a virtually-joined string consisting\n+ * of all the pointers in array \"texts\" (which has a NULL pointer at the\n+ * end).  The int \"where\" can be 0 (normal matching), or > 0 (match\n+ * only the trailing N slash-separated filename components of \"texts\"). */\n+int litmatch_array(const char *string, const char*const *texts, int where)\n+{\n+    const uchar *s = (const uchar*)string;\n+    const uchar*const *a = (const uchar* const*)texts;\n+    const uchar *text;\n+\n+    if (where > 0)\n+\ttext = trailing_N_elements(&a, where);\n+    else\n+\ttext = *a++;\n+    if (!text)\n+\treturn FALSE;\n+\n+    return doliteral(s, text, a) == TRUE;\n+}\ndiff --git a/wildmatch.h b/wildmatch.h\nnew file mode 100644\nindex 0000000..e7f1a35\n--- /dev/null\n+++ b/wildmatch.h\n@@ -0,0 +1,6 @@\n+/* wildmatch.h */\n+\n+int wildmatch(const char *pattern, const char *text);\n+int iwildmatch(const char *pattern, const char *text);\n+int wildmatch_array(const char *pattern, const char*const *texts, int where);\n+int litmatch_array(const char *string, const char*const *texts, int where);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201211","messageId":"1350282362-4505-4-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/13] wildmatch: remove unnecessary functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:53Z","receivedAt":"2012-10-15T06:25:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wildmatch.c | 164 ++++--------------------------------------------------------\n wildmatch.h |   2 -\n 2 files changed, 10 insertions(+), 156 deletions(-)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex f3a1731..fae7397 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -53,33 +53,18 @@\n #define ISUPPER(c) (ISASCII(c) && isupper(c))\n #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n \n-#ifdef WILD_TEST_ITERATIONS\n-int wildmatch_iteration_count;\n-#endif\n-\n static int force_lower_case = 0;\n \n-/* Match pattern \"p\" against the a virtually-joined string consisting\n- * of \"text\" and any strings in array \"a\". */\n-static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n+/* Match pattern \"p\" against \"text\" */\n+static int dowild(const uchar *p, const uchar *text)\n {\n     uchar p_ch;\n \n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count++;\n-#endif\n-\n     for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n \tint matched, special;\n \tuchar t_ch, prev_ch;\n-\twhile ((t_ch = *text) == '\\0') {\n-\t    if (*a == NULL) {\n-\t\tif (p_ch != '*')\n-\t\t    return ABORT_ALL;\n-\t\tbreak;\n-\t    }\n-\t    text = *a++;\n-\t}\n+\tif ((t_ch = *text) == '\\0' && p_ch != '*')\n+\t\treturn ABORT_ALL;\n \tif (force_lower_case && ISUPPER(t_ch))\n \t    t_ch = tolower(t_ch);\n \tswitch (p_ch) {\n@@ -107,21 +92,15 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n \t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n \t\t * only if there are no more slash characters. */\n \t\tif (!special) {\n-\t\t    do {\n \t\t\tif (strchr((char*)text, '/') != NULL)\n \t\t\t    return FALSE;\n-\t\t    } while ((text = *a++) != NULL);\n \t\t}\n \t\treturn TRUE;\n \t    }\n \t    while (1) {\n-\t\tif (t_ch == '\\0') {\n-\t\t    if ((text = *a++) == NULL)\n-\t\t\tbreak;\n-\t\t    t_ch = *text;\n-\t\t    continue;\n-\t\t}\n-\t\tif ((matched = dowild(p, text, a)) != FALSE) {\n+\t\tif (t_ch == '\\0')\n+\t\t    break;\n+\t\tif ((matched = dowild(p, text)) != FALSE) {\n \t\t    if (!special || matched != ABORT_TO_STARSTAR)\n \t\t\treturn matched;\n \t\t} else if (!special && t_ch == '/')\n@@ -225,144 +204,21 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n \t}\n     }\n \n-    do {\n-\tif (*text)\n-\t    return FALSE;\n-    } while ((text = *a++) != NULL);\n-\n-    return TRUE;\n-}\n-\n-/* Match literal string \"s\" against the a virtually-joined string consisting\n- * of \"text\" and any strings in array \"a\". */\n-static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)\n-{\n-    for ( ; *s != '\\0'; text++, s++) {\n-\twhile (*text == '\\0') {\n-\t    if ((text = *a++) == NULL)\n-\t\treturn FALSE;\n-\t}\n-\tif (*text != *s)\n-\t    return FALSE;\n-    }\n-\n-    do {\n-\tif (*text)\n-\t    return FALSE;\n-    } while ((text = *a++) != NULL);\n-\n-    return TRUE;\n-}\n-\n-/* Return the last \"count\" path elements from the concatenated string.\n- * We return a string pointer to the start of the string, and update the\n- * array pointer-pointer to point to any remaining string elements. */\n-static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)\n-{\n-    const uchar*const *a = *a_ptr;\n-    const uchar*const *first_a = a;\n-\n-    while (*a)\n-\t    a++;\n-\n-    while (a != first_a) {\n-\tconst uchar *s = *--a;\n-\ts += strlen((char*)s);\n-\twhile (--s >= *a) {\n-\t    if (*s == '/' && !--count) {\n-\t\t*a_ptr = a+1;\n-\t\treturn s+1;\n-\t    }\n-\t}\n-    }\n-\n-    if (count == 1) {\n-\t*a_ptr = a+1;\n-\treturn *a;\n-    }\n-\n-    return NULL;\n+    return *text ? FALSE : TRUE;\n }\n \n /* Match the \"pattern\" against the \"text\" string. */\n int wildmatch(const char *pattern, const char *text)\n {\n-    static const uchar *nomore[1]; /* A NULL pointer. */\n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count = 0;\n-#endif\n-    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    return dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n }\n \n /* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n int iwildmatch(const char *pattern, const char *text)\n {\n-    static const uchar *nomore[1]; /* A NULL pointer. */\n     int ret;\n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count = 0;\n-#endif\n     force_lower_case = 1;\n-    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n     force_lower_case = 0;\n     return ret;\n }\n-\n-/* Match pattern \"p\" against the a virtually-joined string consisting\n- * of all the pointers in array \"texts\" (which has a NULL pointer at the\n- * end).  The int \"where\" can be 0 (normal matching), > 0 (match only\n- * the trailing N slash-separated filename components of \"texts\"), or < 0\n- * (match the \"pattern\" at the start or after any slash in \"texts\"). */\n-int wildmatch_array(const char *pattern, const char*const *texts, int where)\n-{\n-    const uchar *p = (const uchar*)pattern;\n-    const uchar*const *a = (const uchar*const*)texts;\n-    const uchar *text;\n-    int matched;\n-\n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count = 0;\n-#endif\n-\n-    if (where > 0)\n-\ttext = trailing_N_elements(&a, where);\n-    else\n-\ttext = *a++;\n-    if (!text)\n-\treturn FALSE;\n-\n-    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n-     && matched != ABORT_ALL) {\n-\twhile (1) {\n-\t    if (*text == '\\0') {\n-\t\tif ((text = (uchar*)*a++) == NULL)\n-\t\t    return FALSE;\n-\t\tcontinue;\n-\t    }\n-\t    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n-\t     && matched != ABORT_TO_STARSTAR)\n-\t\tbreak;\n-\t}\n-    }\n-    return matched == TRUE;\n-}\n-\n-/* Match literal string \"s\" against the a virtually-joined string consisting\n- * of all the pointers in array \"texts\" (which has a NULL pointer at the\n- * end).  The int \"where\" can be 0 (normal matching), or > 0 (match\n- * only the trailing N slash-separated filename components of \"texts\"). */\n-int litmatch_array(const char *string, const char*const *texts, int where)\n-{\n-    const uchar *s = (const uchar*)string;\n-    const uchar*const *a = (const uchar* const*)texts;\n-    const uchar *text;\n-\n-    if (where > 0)\n-\ttext = trailing_N_elements(&a, where);\n-    else\n-\ttext = *a++;\n-    if (!text)\n-\treturn FALSE;\n-\n-    return doliteral(s, text, a) == TRUE;\n-}\ndiff --git a/wildmatch.h b/wildmatch.h\nindex e7f1a35..562faa3 100644\n--- a/wildmatch.h\n+++ b/wildmatch.h\n@@ -2,5 +2,3 @@\n \n int wildmatch(const char *pattern, const char *text);\n int iwildmatch(const char *pattern, const char *text);\n-int wildmatch_array(const char *pattern, const char*const *texts, int where);\n-int litmatch_array(const char *string, const char*const *texts, int where);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201212","messageId":"1350282362-4505-5-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/13] wildmatch: follow Git's coding convention","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:54Z","receivedAt":"2012-10-15T06:25:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"wildmatch's coding style is pretty close to Git's except the use of 4\nspace indentation instead of 8. This patch should produce empty diff\nwith \"git diff -b\"\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wildmatch.c | 292 ++++++++++++++++++++++++++++++------------------------------\n 1 file changed, 146 insertions(+), 146 deletions(-)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex fae7397..4653dd6 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -58,167 +58,167 @@ static int force_lower_case = 0;\n /* Match pattern \"p\" against \"text\" */\n static int dowild(const uchar *p, const uchar *text)\n {\n-    uchar p_ch;\n+\tuchar p_ch;\n \n-    for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n-\tint matched, special;\n-\tuchar t_ch, prev_ch;\n-\tif ((t_ch = *text) == '\\0' && p_ch != '*')\n-\t\treturn ABORT_ALL;\n-\tif (force_lower_case && ISUPPER(t_ch))\n-\t    t_ch = tolower(t_ch);\n-\tswitch (p_ch) {\n-\t  case '\\\\':\n-\t    /* Literal match with following character.  Note that the test\n-\t     * in \"default\" handles the p[1] == '\\0' failure case. */\n-\t    p_ch = *++p;\n-\t    /* FALLTHROUGH */\n-\t  default:\n-\t    if (t_ch != p_ch)\n-\t\treturn FALSE;\n-\t    continue;\n-\t  case '?':\n-\t    /* Match anything but '/'. */\n-\t    if (t_ch == '/')\n-\t\treturn FALSE;\n-\t    continue;\n-\t  case '*':\n-\t    if (*++p == '*') {\n-\t\twhile (*++p == '*') {}\n-\t\tspecial = TRUE;\n-\t    } else\n-\t\tspecial = FALSE;\n-\t    if (*p == '\\0') {\n-\t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n-\t\t * only if there are no more slash characters. */\n-\t\tif (!special) {\n-\t\t\tif (strchr((char*)text, '/') != NULL)\n-\t\t\t    return FALSE;\n-\t\t}\n-\t\treturn TRUE;\n-\t    }\n-\t    while (1) {\n-\t\tif (t_ch == '\\0')\n-\t\t    break;\n-\t\tif ((matched = dowild(p, text)) != FALSE) {\n-\t\t    if (!special || matched != ABORT_TO_STARSTAR)\n-\t\t\treturn matched;\n-\t\t} else if (!special && t_ch == '/')\n-\t\t    return ABORT_TO_STARSTAR;\n-\t\tt_ch = *++text;\n-\t    }\n-\t    return ABORT_ALL;\n-\t  case '[':\n-\t    p_ch = *++p;\n-#ifdef NEGATE_CLASS2\n-\t    if (p_ch == NEGATE_CLASS2)\n-\t\tp_ch = NEGATE_CLASS;\n-#endif\n-\t    /* Assign literal TRUE/FALSE because of \"matched\" comparison. */\n-\t    special = p_ch == NEGATE_CLASS? TRUE : FALSE;\n-\t    if (special) {\n-\t\t/* Inverted character class. */\n-\t\tp_ch = *++p;\n-\t    }\n-\t    prev_ch = 0;\n-\t    matched = FALSE;\n-\t    do {\n-\t\tif (!p_ch)\n-\t\t    return ABORT_ALL;\n-\t\tif (p_ch == '\\\\') {\n-\t\t    p_ch = *++p;\n-\t\t    if (!p_ch)\n+\tfor ( ; (p_ch = *p) != '\\0'; text++, p++) {\n+\t\tint matched, special;\n+\t\tuchar t_ch, prev_ch;\n+\t\tif ((t_ch = *text) == '\\0' && p_ch != '*')\n \t\t\treturn ABORT_ALL;\n-\t\t    if (t_ch == p_ch)\n-\t\t\tmatched = TRUE;\n-\t\t} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {\n-\t\t    p_ch = *++p;\n-\t\t    if (p_ch == '\\\\') {\n+\t\tif (force_lower_case && ISUPPER(t_ch))\n+\t\t\tt_ch = tolower(t_ch);\n+\t\tswitch (p_ch) {\n+\t\tcase '\\\\':\n+\t\t\t/* Literal match with following character.  Note that the test\n+\t\t\t * in \"default\" handles the p[1] == '\\0' failure case. */\n \t\t\tp_ch = *++p;\n-\t\t\tif (!p_ch)\n-\t\t\t    return ABORT_ALL;\n-\t\t    }\n-\t\t    if (t_ch <= p_ch && t_ch >= prev_ch)\n-\t\t\tmatched = TRUE;\n-\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n-\t\t} else if (p_ch == '[' && p[1] == ':') {\n-\t\t    const uchar *s;\n-\t\t    int i;\n-\t\t    for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/\n-\t\t    if (!p_ch)\n-\t\t\treturn ABORT_ALL;\n-\t\t    i = p - s - 1;\n-\t\t    if (i < 0 || p[-1] != ':') {\n-\t\t\t/* Didn't find \":]\", so treat like a normal set. */\n-\t\t\tp = s - 2;\n-\t\t\tp_ch = '[';\n-\t\t\tif (t_ch == p_ch)\n-\t\t\t    matched = TRUE;\n+\t\t\t/* FALLTHROUGH */\n+\t\tdefault:\n+\t\t\tif (t_ch != p_ch)\n+\t\t\t\treturn FALSE;\n+\t\t\tcontinue;\n+\t\tcase '?':\n+\t\t\t/* Match anything but '/'. */\n+\t\t\tif (t_ch == '/')\n+\t\t\t\treturn FALSE;\n \t\t\tcontinue;\n-\t\t    }\n-\t\t    if (CC_EQ(s,i, \"alnum\")) {\n-\t\t\tif (ISALNUM(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"alpha\")) {\n-\t\t\tif (ISALPHA(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"blank\")) {\n-\t\t\tif (ISBLANK(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"cntrl\")) {\n-\t\t\tif (ISCNTRL(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"digit\")) {\n-\t\t\tif (ISDIGIT(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"graph\")) {\n-\t\t\tif (ISGRAPH(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"lower\")) {\n-\t\t\tif (ISLOWER(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"print\")) {\n-\t\t\tif (ISPRINT(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"punct\")) {\n-\t\t\tif (ISPUNCT(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"space\")) {\n-\t\t\tif (ISSPACE(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"upper\")) {\n-\t\t\tif (ISUPPER(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else if (CC_EQ(s,i, \"xdigit\")) {\n-\t\t\tif (ISXDIGIT(t_ch))\n-\t\t\t    matched = TRUE;\n-\t\t    } else /* malformed [:class:] string */\n+\t\tcase '*':\n+\t\t\tif (*++p == '*') {\n+\t\t\t\twhile (*++p == '*') {}\n+\t\t\t\tspecial = TRUE;\n+\t\t\t} else\n+\t\t\t\tspecial = FALSE;\n+\t\t\tif (*p == '\\0') {\n+\t\t\t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n+\t\t\t\t * only if there are no more slash characters. */\n+\t\t\t\tif (!special) {\n+\t\t\t\t\tif (strchr((char*)text, '/') != NULL)\n+\t\t\t\t\t\treturn FALSE;\n+\t\t\t\t}\n+\t\t\t\treturn TRUE;\n+\t\t\t}\n+\t\t\twhile (1) {\n+\t\t\t\tif (t_ch == '\\0')\n+\t\t\t\t\tbreak;\n+\t\t\t\tif ((matched = dowild(p, text)) != FALSE) {\n+\t\t\t\t\tif (!special || matched != ABORT_TO_STARSTAR)\n+\t\t\t\t\t\treturn matched;\n+\t\t\t\t} else if (!special && t_ch == '/')\n+\t\t\t\t\treturn ABORT_TO_STARSTAR;\n+\t\t\t\tt_ch = *++text;\n+\t\t\t}\n \t\t\treturn ABORT_ALL;\n-\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n-\t\t} else if (t_ch == p_ch)\n-\t\t    matched = TRUE;\n-\t    } while (prev_ch = p_ch, (p_ch = *++p) != ']');\n-\t    if (matched == special || t_ch == '/')\n-\t\treturn FALSE;\n-\t    continue;\n+\t\tcase '[':\n+\t\t\tp_ch = *++p;\n+#ifdef NEGATE_CLASS2\n+\t\t\tif (p_ch == NEGATE_CLASS2)\n+\t\t\t\tp_ch = NEGATE_CLASS;\n+#endif\n+\t\t\t/* Assign literal TRUE/FALSE because of \"matched\" comparison. */\n+\t\t\tspecial = p_ch == NEGATE_CLASS? TRUE : FALSE;\n+\t\t\tif (special) {\n+\t\t\t\t/* Inverted character class. */\n+\t\t\t\tp_ch = *++p;\n+\t\t\t}\n+\t\t\tprev_ch = 0;\n+\t\t\tmatched = FALSE;\n+\t\t\tdo {\n+\t\t\t\tif (!p_ch)\n+\t\t\t\t\treturn ABORT_ALL;\n+\t\t\t\tif (p_ch == '\\\\') {\n+\t\t\t\t\tp_ch = *++p;\n+\t\t\t\t\tif (!p_ch)\n+\t\t\t\t\t\treturn ABORT_ALL;\n+\t\t\t\t\tif (t_ch == p_ch)\n+\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {\n+\t\t\t\t\tp_ch = *++p;\n+\t\t\t\t\tif (p_ch == '\\\\') {\n+\t\t\t\t\t\tp_ch = *++p;\n+\t\t\t\t\t\tif (!p_ch)\n+\t\t\t\t\t\t\treturn ABORT_ALL;\n+\t\t\t\t\t}\n+\t\t\t\t\tif (t_ch <= p_ch && t_ch >= prev_ch)\n+\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\tp_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t\t\t} else if (p_ch == '[' && p[1] == ':') {\n+\t\t\t\t\tconst uchar *s;\n+\t\t\t\t\tint i;\n+\t\t\t\t\tfor (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/\n+\t\t\t\t\tif (!p_ch)\n+\t\t\t\t\t\treturn ABORT_ALL;\n+\t\t\t\t\ti = p - s - 1;\n+\t\t\t\t\tif (i < 0 || p[-1] != ':') {\n+\t\t\t\t\t\t/* Didn't find \":]\", so treat like a normal set. */\n+\t\t\t\t\t\tp = s - 2;\n+\t\t\t\t\t\tp_ch = '[';\n+\t\t\t\t\t\tif (t_ch == p_ch)\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n+\t\t\t\t\tif (CC_EQ(s,i, \"alnum\")) {\n+\t\t\t\t\t\tif (ISALNUM(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"alpha\")) {\n+\t\t\t\t\t\tif (ISALPHA(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"blank\")) {\n+\t\t\t\t\t\tif (ISBLANK(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"cntrl\")) {\n+\t\t\t\t\t\tif (ISCNTRL(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"digit\")) {\n+\t\t\t\t\t\tif (ISDIGIT(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"graph\")) {\n+\t\t\t\t\t\tif (ISGRAPH(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"lower\")) {\n+\t\t\t\t\t\tif (ISLOWER(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"print\")) {\n+\t\t\t\t\t\tif (ISPRINT(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"punct\")) {\n+\t\t\t\t\t\tif (ISPUNCT(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"space\")) {\n+\t\t\t\t\t\tif (ISSPACE(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"upper\")) {\n+\t\t\t\t\t\tif (ISUPPER(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else if (CC_EQ(s,i, \"xdigit\")) {\n+\t\t\t\t\t\tif (ISXDIGIT(t_ch))\n+\t\t\t\t\t\t\tmatched = TRUE;\n+\t\t\t\t\t} else /* malformed [:class:] string */\n+\t\t\t\t\t\treturn ABORT_ALL;\n+\t\t\t\t\tp_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t\t\t} else if (t_ch == p_ch)\n+\t\t\t\t\tmatched = TRUE;\n+\t\t\t} while (prev_ch = p_ch, (p_ch = *++p) != ']');\n+\t\t\tif (matched == special || t_ch == '/')\n+\t\t\t\treturn FALSE;\n+\t\t\tcontinue;\n+\t\t}\n \t}\n-    }\n \n-    return *text ? FALSE : TRUE;\n+\treturn *text ? FALSE : TRUE;\n }\n \n /* Match the \"pattern\" against the \"text\" string. */\n int wildmatch(const char *pattern, const char *text)\n {\n-    return dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n+\treturn dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n }\n \n /* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n int iwildmatch(const char *pattern, const char *text)\n {\n-    int ret;\n-    force_lower_case = 1;\n-    ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n-    force_lower_case = 0;\n-    return ret;\n+\tint ret;\n+\tforce_lower_case = 1;\n+\tret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n+\tforce_lower_case = 0;\n+\treturn ret;\n }\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201213","messageId":"1350282362-4505-6-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/13] Integrate wildmatch to git","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:55Z","receivedAt":"2012-10-15T06:25:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore           |   1 +\n Makefile             |   3 +\n t/t3070-wildmatch.sh | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t3070/wildtest.txt | 165 --------------------------------------------\n test-wildmatch.c     |  14 ++++\n wildmatch.c          |   5 +-\n 6 files changed, 210 insertions(+), 166 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n delete mode 100644 t/t3070/wildtest.txt\n create mode 100644 test-wildmatch.c\n\ndiff --git a/.gitignore b/.gitignore\nindex f1acd3e..1153a4b 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -193,6 +193,7 @@\n /test-sigchain\n /test-subprocess\n /test-svn-fe\n+/test-wildmatch\n /common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 13293d3..bc868d1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -503,6 +503,7 @@ TEST_PROGRAMS_NEED_X += test-sha1\n TEST_PROGRAMS_NEED_X += test-sigchain\n TEST_PROGRAMS_NEED_X += test-subprocess\n TEST_PROGRAMS_NEED_X += test-svn-fe\n+TEST_PROGRAMS_NEED_X += test-wildmatch\n \n TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\n \n@@ -674,6 +675,7 @@ LIB_H += unpack-trees.h\n LIB_H += userdiff.h\n LIB_H += utf8.h\n LIB_H += varint.h\n+LIB_H += wildmatch.h\n LIB_H += xdiff-interface.h\n LIB_H += xdiff/xdiff.h\n \n@@ -803,6 +805,7 @@ LIB_OBJS += userdiff.o\n LIB_OBJS += utf8.o\n LIB_OBJS += varint.o\n LIB_OBJS += walker.o\n+LIB_OBJS += wildmatch.o\n LIB_OBJS += wrapper.o\n LIB_OBJS += write_or_die.o\n LIB_OBJS += ws.o\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nnew file mode 100755\nindex 0000000..dbd3c8b\n--- /dev/null\n+++ b/t/t3070-wildmatch.sh\n@@ -0,0 +1,188 @@\n+#!/bin/sh\n+\n+test_description='wildmatch tests'\n+\n+. ./test-lib.sh\n+\n+match() {\n+    if [ $1 = 1 ]; then\n+\ttest_expect_success \"wildmatch:    match '$3' '$4'\" \"\n+\t    test-wildmatch wildmatch '$3' '$4'\n+\t\"\n+    else\n+\ttest_expect_success \"wildmatch: no match '$3' '$4'\" \"\n+\t    ! test-wildmatch wildmatch '$3' '$4'\n+\t\"\n+    fi\n+    if [ $2 = 1 ]; then\n+\ttest_expect_success \"fnmatch:      match '$3' '$4'\" \"\n+\t    test-wildmatch fnmatch '$3' '$4'\n+\t\"\n+    elif [ $2 = 0 ]; then\n+\ttest_expect_success \"fnmatch:   no match '$3' '$4'\" \"\n+\t    ! test-wildmatch fnmatch '$3' '$4'\n+\t\"\n+#    else\n+#\ttest_expect_success BROKEN_FNMATCH \"fnmatch:       '$3' '$4'\" \"\n+#\t    ! test-wildmatch fnmatch '$3' '$4'\n+#\t\"\n+    fi\n+}\n+\n+# Basic wildmat features\n+match 1 1 foo foo\n+match 0 0 foo bar\n+match 1 1 '' \"\"\n+match 1 1 foo '???'\n+match 0 0 foo '??'\n+match 1 1 foo '*'\n+match 1 1 foo 'f*'\n+match 0 0 foo '*f'\n+match 1 1 foo '*foo*'\n+match 1 1 foobar '*ob*a*r*'\n+match 1 1 aaaaaaabababab '*ab'\n+match 1 1 'foo*' 'foo\\*'\n+match 0 0 foobar 'foo\\*bar'\n+match 1 1 'f\\oo' 'f\\\\oo'\n+match 1 1 ball '*[al]?'\n+match 0 0 ten '[ten]'\n+match 1 1 ten '**[!te]'\n+match 0 0 ten '**[!ten]'\n+match 1 1 ten 't[a-g]n'\n+match 0 0 ten 't[!a-g]n'\n+match 1 1 ton 't[!a-g]n'\n+match 1 1 ton 't[^a-g]n'\n+match 1 1 'a]b' 'a[]]b'\n+match 1 1 a-b 'a[]-]b'\n+match 1 1 'a]b' 'a[]-]b'\n+match 0 0 aab 'a[]-]b'\n+match 1 1 aab 'a[]a-]b'\n+match 1 1 ']' ']'\n+\n+# Extended slash-matching features\n+match 0 0 'foo/baz/bar' 'foo*bar'\n+match 1 0 'foo/baz/bar' 'foo**bar'\n+match 0 0 'foo/bar' 'foo?bar'\n+match 0 0 'foo/bar' 'foo[/]bar'\n+match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n+match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n+match 0 0 'foo' '**/foo'\n+match 1 1 '/foo' '**/foo'\n+match 1 0 'bar/baz/foo' '**/foo'\n+match 0 0 'bar/baz/foo' '*/foo'\n+match 0 0 'foo/bar/baz' '**/bar*'\n+match 1 0 'deep/foo/bar/baz' '**/bar/*'\n+match 0 0 'deep/foo/bar/baz/' '**/bar/*'\n+match 1 0 'deep/foo/bar/baz/' '**/bar/**'\n+match 0 0 'deep/foo/bar' '**/bar/*'\n+match 1 0 'deep/foo/bar/' '**/bar/**'\n+match 1 0 'foo/bar/baz' '**/bar**'\n+match 1 0 'foo/bar/baz/x' '*/bar/**'\n+match 0 0 'deep/foo/bar/baz/x' '*/bar/**'\n+match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*'\n+\n+# Various additional tests\n+match 0 0 'acrt' 'a[c-c]st'\n+match 1 1 'acrt' 'a[c-c]rt'\n+match 0 0 ']' '[!]-]'\n+match 1 1 'a' '[!]-]'\n+match 0 0 '' '\\'\n+match 0 0 '\\' '\\'\n+match 0 0 '/\\' '*/\\'\n+match 1 1 '/\\' '*/\\\\'\n+match 1 1 'foo' 'foo'\n+match 1 1 '@foo' '@foo'\n+match 0 0 'foo' '@foo'\n+match 1 1 '[ab]' '\\[ab]'\n+match 1 1 '[ab]' '[[]ab]'\n+match 1 1 '[ab]' '[[:]ab]'\n+match 0 0 '[ab]' '[[::]ab]'\n+match 1 1 '[ab]' '[[:digit]ab]'\n+match 1 1 '[ab]' '[\\[:]ab]'\n+match 1 1 '?a?b' '\\??\\?b'\n+match 1 1 'abc' '\\a\\b\\c'\n+match 0 0 'foo' ''\n+match 1 0 'foo/bar/baz/to' '**/t[o]'\n+\n+# Character class tests\n+match 1 1 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]'\n+match 0 0 'a' '[[:digit:][:upper:][:space:]]'\n+match 1 1 'A' '[[:digit:][:upper:][:space:]]'\n+match 1 0 '1' '[[:digit:][:upper:][:space:]]'\n+match 0 0 '1' '[[:digit:][:upper:][:spaci:]]'\n+match 1 1 ' ' '[[:digit:][:upper:][:space:]]'\n+match 0 0 '.' '[[:digit:][:upper:][:space:]]'\n+match 1 1 '.' '[[:digit:][:punct:][:space:]]'\n+match 1 1 '5' '[[:xdigit:]]'\n+match 1 1 'f' '[[:xdigit:]]'\n+match 1 1 'D' '[[:xdigit:]]'\n+match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n+match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n+match 1 1 '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'\n+match 1 1 '5' '[a-c[:digit:]x-z]'\n+match 1 1 'b' '[a-c[:digit:]x-z]'\n+match 1 1 'y' '[a-c[:digit:]x-z]'\n+match 0 0 'q' '[a-c[:digit:]x-z]'\n+\n+# Additional tests, including some malformed wildmats\n+match 1 1 ']' '[\\\\-^]'\n+match 0 0 '[' '[\\\\-^]'\n+match 1 1 '-' '[\\-_]'\n+match 1 1 ']' '[\\]]'\n+match 0 0 '\\]' '[\\]]'\n+match 0 0 '\\' '[\\]]'\n+match 0 0 'ab' 'a[]b'\n+match 0 1 'a[]b' 'a[]b'\n+match 0 1 'ab[' 'ab['\n+match 0 0 'ab' '[!'\n+match 0 0 'ab' '[-'\n+match 1 1 '-' '[-]'\n+match 0 0 '-' '[a-'\n+match 0 0 '-' '[!a-'\n+match 1 1 '-' '[--A]'\n+match 1 1 '5' '[--A]'\n+match 1 1 ' ' '[ --]'\n+match 1 1 '$' '[ --]'\n+match 1 1 '-' '[ --]'\n+match 0 0 '0' '[ --]'\n+match 1 1 '-' '[---]'\n+match 1 1 '-' '[------]'\n+match 0 0 'j' '[a-e-n]'\n+match 1 1 '-' '[a-e-n]'\n+match 1 1 'a' '[!------]'\n+match 0 0 '[' '[]-a]'\n+match 1 1 '^' '[]-a]'\n+match 0 0 '^' '[!]-a]'\n+match 1 1 '[' '[!]-a]'\n+match 1 1 '^' '[a^bc]'\n+match 1 1 '-b]' '[a-]b]'\n+match 0 0 '\\' '[\\]'\n+match 1 1 '\\' '[\\\\]'\n+match 0 0 '\\' '[!\\\\]'\n+match 1 1 'G' '[A-\\\\]'\n+match 0 0 'aaabbb' 'b*a'\n+match 0 0 'aabcaa' '*ba*'\n+match 1 1 ',' '[,]'\n+match 1 1 ',' '[\\\\,]'\n+match 1 1 '\\' '[\\\\,]'\n+match 1 1 '-' '[,-.]'\n+match 0 0 '+' '[,-.]'\n+match 0 0 '-.]' '[,-.]'\n+match 1 1 '2' '[\\1-\\3]'\n+match 1 1 '3' '[\\1-\\3]'\n+match 0 0 '4' '[\\1-\\3]'\n+match 1 1 '\\' '[[-\\]]'\n+match 1 1 '[' '[[-\\]]'\n+match 1 1 ']' '[[-\\]]'\n+match 0 0 '-' '[[-\\]]'\n+\n+# Test recursion and the abort code (use \"wildtest -i\" to see iteration counts)\n+match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n+match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n+match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n+match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n+match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n+\n+test_done\ndiff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt\ndeleted file mode 100644\nindex 42c1678..0000000\n--- a/t/t3070/wildtest.txt\n+++ /dev/null\n@@ -1,165 +0,0 @@\n-# Input is in the following format (all items white-space separated):\n-#\n-# The first two items are 1 or 0 indicating if the wildmat call is expected to\n-# succeed and if fnmatch works the same way as wildmat, respectively.  After\n-# that is a text string for the match, and a pattern string.  Strings can be\n-# quoted (if desired) in either double or single quotes, as well as backticks.\n-#\n-# MATCH FNMATCH_SAME \"text to match\" 'pattern to use'\n-\n-# Basic wildmat features\n-1 1 foo\t\t\tfoo\n-0 1 foo\t\t\tbar\n-1 1 ''\t\t\t\"\"\n-1 1 foo\t\t\t???\n-0 1 foo\t\t\t??\n-1 1 foo\t\t\t*\n-1 1 foo\t\t\tf*\n-0 1 foo\t\t\t*f\n-1 1 foo\t\t\t*foo*\n-1 1 foobar\t\t*ob*a*r*\n-1 1 aaaaaaabababab\t*ab\n-1 1 foo*\t\tfoo\\*\n-0 1 foobar\t\tfoo\\*bar\n-1 1 f\\oo\t\tf\\\\oo\n-1 1 ball\t\t*[al]?\n-0 1 ten\t\t\t[ten]\n-1 1 ten\t\t\t**[!te]\n-0 1 ten\t\t\t**[!ten]\n-1 1 ten\t\t\tt[a-g]n\n-0 1 ten\t\t\tt[!a-g]n\n-1 1 ton\t\t\tt[!a-g]n\n-1 1 ton\t\t\tt[^a-g]n\n-1 1 a]b\t\t\ta[]]b\n-1 1 a-b\t\t\ta[]-]b\n-1 1 a]b\t\t\ta[]-]b\n-0 1 aab\t\t\ta[]-]b\n-1 1 aab\t\t\ta[]a-]b\n-1 1 ]\t\t\t]\n-\n-# Extended slash-matching features\n-0 1 foo/baz/bar\t\tfoo*bar\n-1 1 foo/baz/bar\t\tfoo**bar\n-0 1 foo/bar\t\tfoo?bar\n-0 1 foo/bar\t\tfoo[/]bar\n-0 1 foo/bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n-1 1 foo-bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n-0 1 foo\t\t\t**/foo\n-1 1 /foo\t\t**/foo\n-1 1 bar/baz/foo\t\t**/foo\n-0 1 bar/baz/foo\t\t*/foo\n-0 0 foo/bar/baz\t\t**/bar*\n-1 1 deep/foo/bar/baz\t**/bar/*\n-0 1 deep/foo/bar/baz/\t**/bar/*\n-1 1 deep/foo/bar/baz/\t**/bar/**\n-0 1 deep/foo/bar\t**/bar/*\n-1 1 deep/foo/bar/\t**/bar/**\n-1 1 foo/bar/baz\t\t**/bar**\n-1 1 foo/bar/baz/x\t*/bar/**\n-0 0 deep/foo/bar/baz/x\t*/bar/**\n-1 1 deep/foo/bar/baz/x\t**/bar/*/*\n-\n-# Various additional tests\n-0 1 acrt\t\ta[c-c]st\n-1 1 acrt\t\ta[c-c]rt\n-0 1 ]\t\t\t[!]-]\n-1 1 a\t\t\t[!]-]\n-0 1 ''\t\t\t\\\n-0 1 \\\t\t\t\\\n-0 1 /\\\t\t\t*/\\\n-1 1 /\\\t\t\t*/\\\\\n-1 1 foo\t\t\tfoo\n-1 1 @foo\t\t@foo\n-0 1 foo\t\t\t@foo\n-1 1 [ab]\t\t\\[ab]\n-1 1 [ab]\t\t[[]ab]\n-1 1 [ab]\t\t[[:]ab]\n-0 1 [ab]\t\t[[::]ab]\n-1 1 [ab]\t\t[[:digit]ab]\n-1 1 [ab]\t\t[\\[:]ab]\n-1 1 ?a?b\t\t\\??\\?b\n-1 1 abc\t\t\t\\a\\b\\c\n-0 1 foo\t\t\t''\n-1 1 foo/bar/baz/to\t**/t[o]\n-\n-# Character class tests\n-1 1 a1B\t\t[[:alpha:]][[:digit:]][[:upper:]]\n-0 1 a\t\t[[:digit:][:upper:][:space:]]\n-1 1 A\t\t[[:digit:][:upper:][:space:]]\n-1 1 1\t\t[[:digit:][:upper:][:space:]]\n-0 1 1\t\t[[:digit:][:upper:][:spaci:]]\n-1 1 ' '\t\t[[:digit:][:upper:][:space:]]\n-0 1 .\t\t[[:digit:][:upper:][:space:]]\n-1 1 .\t\t[[:digit:][:punct:][:space:]]\n-1 1 5\t\t[[:xdigit:]]\n-1 1 f\t\t[[:xdigit:]]\n-1 1 D\t\t[[:xdigit:]]\n-1 1 _\t\t[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n-#1 1 �\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n-1 1 \t\t[^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n-1 1 .\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]\n-1 1 5\t\t[a-c[:digit:]x-z]\n-1 1 b\t\t[a-c[:digit:]x-z]\n-1 1 y\t\t[a-c[:digit:]x-z]\n-0 1 q\t\t[a-c[:digit:]x-z]\n-\n-# Additional tests, including some malformed wildmats\n-1 1 ]\t\t[\\\\-^]\n-0 1 [\t\t[\\\\-^]\n-1 1 -\t\t[\\-_]\n-1 1 ]\t\t[\\]]\n-0 1 \\]\t\t[\\]]\n-0 1 \\\t\t[\\]]\n-0 1 ab\t\ta[]b\n-0 1 a[]b\ta[]b\n-0 1 ab[\t\tab[\n-0 1 ab\t\t[!\n-0 1 ab\t\t[-\n-1 1 -\t\t[-]\n-0 1 -\t\t[a-\n-0 1 -\t\t[!a-\n-1 1 -\t\t[--A]\n-1 1 5\t\t[--A]\n-1 1 ' '\t\t'[ --]'\n-1 1 $\t\t'[ --]'\n-1 1 -\t\t'[ --]'\n-0 1 0\t\t'[ --]'\n-1 1 -\t\t[---]\n-1 1 -\t\t[------]\n-0 1 j\t\t[a-e-n]\n-1 1 -\t\t[a-e-n]\n-1 1 a\t\t[!------]\n-0 1 [\t\t[]-a]\n-1 1 ^\t\t[]-a]\n-0 1 ^\t\t[!]-a]\n-1 1 [\t\t[!]-a]\n-1 1 ^\t\t[a^bc]\n-1 1 -b]\t\t[a-]b]\n-0 1 \\\t\t[\\]\n-1 1 \\\t\t[\\\\]\n-0 1 \\\t\t[!\\\\]\n-1 1 G\t\t[A-\\\\]\n-0 1 aaabbb\tb*a\n-0 1 aabcaa\t*ba*\n-1 1 ,\t\t[,]\n-1 1 ,\t\t[\\\\,]\n-1 1 \\\t\t[\\\\,]\n-1 1 -\t\t[,-.]\n-0 1 +\t\t[,-.]\n-0 1 -.]\t\t[,-.]\n-1 1 2\t\t[\\1-\\3]\n-1 1 3\t\t[\\1-\\3]\n-0 1 4\t\t[\\1-\\3]\n-1 1 \\\t\t[[-\\]]\n-1 1 [\t\t[[-\\]]\n-1 1 ]\t\t[[-\\]]\n-0 1 -\t\t[[-\\]]\n-\n-# Test recursion and the abort code (use \"wildtest -i\" to see iteration counts)\n-1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n-0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n-0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n-1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n-0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n-1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt\t\t**/*a*b*g*n*t\n-0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz\t\t**/*a*b*g*n*t\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nnew file mode 100644\nindex 0000000..ac56420\n--- /dev/null\n+++ b/test-wildmatch.c\n@@ -0,0 +1,14 @@\n+#include \"cache.h\"\n+#include \"wildmatch.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\tif (!strcmp(argv[1], \"wildmatch\"))\n+\t\treturn wildmatch(argv[3], argv[2]) ? 0 : 1;\n+\telse if (!strcmp(argv[1], \"iwildmatch\"))\n+\t\treturn iwildmatch(argv[3], argv[2]) ? 0 : 1;\n+\telse if (!strcmp(argv[1], \"fnmatch\"))\n+\t\treturn !!fnmatch(argv[3], argv[2], FNM_PATHNAME);\n+\telse\n+\t\treturn 1;\n+}\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 4653dd6..ac29471 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -9,7 +9,10 @@\n **  work differently than '*', and to fix the character-class code.\n */\n \n-#include \"rsync.h\"\n+#include \"cache.h\"\n+#include \"wildmatch.h\"\n+\n+typedef unsigned char uchar;\n \n /* What character marks an inverted character class? */\n #define NEGATE_CLASS\t'!'\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201214","messageId":"1350282362-4505-7-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/13] t3070: disable unreliable fnmatch tests","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:56Z","receivedAt":"2012-10-15T06:25:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These tests show different results on different fnmatch() versions. We\ndon't want to test fnmatch here. We want to make sure wildmatch\nbehavior matches fnmatch and that only makes sense in cases when\nfnmatch() behaves consistently.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070-wildmatch.sh | 86 ++++++++++++++++++++++++++--------------------------\n 1 file changed, 43 insertions(+), 43 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex dbd3c8b..dd95b00 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -52,11 +52,11 @@ match 1 1 ten 't[a-g]n'\n match 0 0 ten 't[!a-g]n'\n match 1 1 ton 't[!a-g]n'\n match 1 1 ton 't[^a-g]n'\n-match 1 1 'a]b' 'a[]]b'\n-match 1 1 a-b 'a[]-]b'\n-match 1 1 'a]b' 'a[]-]b'\n-match 0 0 aab 'a[]-]b'\n-match 1 1 aab 'a[]a-]b'\n+match 1 x 'a]b' 'a[]]b'\n+match 1 x a-b 'a[]-]b'\n+match 1 x 'a]b' 'a[]-]b'\n+match 0 x aab 'a[]-]b'\n+match 1 x aab 'a[]a-]b'\n match 1 1 ']' ']'\n \n # Extended slash-matching features\n@@ -67,7 +67,7 @@ match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 0 0 'foo' '**/foo'\n-match 1 1 '/foo' '**/foo'\n+match 1 x '/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\n match 0 0 'foo/bar/baz' '**/bar*'\n@@ -85,77 +85,77 @@ match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*'\n match 0 0 'acrt' 'a[c-c]st'\n match 1 1 'acrt' 'a[c-c]rt'\n match 0 0 ']' '[!]-]'\n-match 1 1 'a' '[!]-]'\n+match 1 x 'a' '[!]-]'\n match 0 0 '' '\\'\n-match 0 0 '\\' '\\'\n-match 0 0 '/\\' '*/\\'\n-match 1 1 '/\\' '*/\\\\'\n+match 0 x '\\' '\\'\n+match 0 x '/\\' '*/\\'\n+match 1 x '/\\' '*/\\\\'\n match 1 1 'foo' 'foo'\n match 1 1 '@foo' '@foo'\n match 0 0 'foo' '@foo'\n match 1 1 '[ab]' '\\[ab]'\n match 1 1 '[ab]' '[[]ab]'\n-match 1 1 '[ab]' '[[:]ab]'\n-match 0 0 '[ab]' '[[::]ab]'\n-match 1 1 '[ab]' '[[:digit]ab]'\n-match 1 1 '[ab]' '[\\[:]ab]'\n+match 1 x '[ab]' '[[:]ab]'\n+match 0 x '[ab]' '[[::]ab]'\n+match 1 x '[ab]' '[[:digit]ab]'\n+match 1 x '[ab]' '[\\[:]ab]'\n match 1 1 '?a?b' '\\??\\?b'\n match 1 1 'abc' '\\a\\b\\c'\n match 0 0 'foo' ''\n match 1 0 'foo/bar/baz/to' '**/t[o]'\n \n # Character class tests\n-match 1 1 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]'\n-match 0 0 'a' '[[:digit:][:upper:][:space:]]'\n-match 1 1 'A' '[[:digit:][:upper:][:space:]]'\n-match 1 0 '1' '[[:digit:][:upper:][:space:]]'\n-match 0 0 '1' '[[:digit:][:upper:][:spaci:]]'\n-match 1 1 ' ' '[[:digit:][:upper:][:space:]]'\n-match 0 0 '.' '[[:digit:][:upper:][:space:]]'\n-match 1 1 '.' '[[:digit:][:punct:][:space:]]'\n+match 1 x 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]'\n+match 0 x 'a' '[[:digit:][:upper:][:space:]]'\n+match 1 x 'A' '[[:digit:][:upper:][:space:]]'\n+match 1 x '1' '[[:digit:][:upper:][:space:]]'\n+match 0 x '1' '[[:digit:][:upper:][:spaci:]]'\n+match 1 x ' ' '[[:digit:][:upper:][:space:]]'\n+match 0 x '.' '[[:digit:][:upper:][:space:]]'\n+match 1 x '.' '[[:digit:][:punct:][:space:]]'\n match 1 1 '5' '[[:xdigit:]]'\n match 1 1 'f' '[[:xdigit:]]'\n match 1 1 'D' '[[:xdigit:]]'\n-match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n-match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n-match 1 1 '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'\n-match 1 1 '5' '[a-c[:digit:]x-z]'\n-match 1 1 'b' '[a-c[:digit:]x-z]'\n-match 1 1 'y' '[a-c[:digit:]x-z]'\n-match 0 0 'q' '[a-c[:digit:]x-z]'\n+match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n+match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n+match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'\n+match 1 x '5' '[a-c[:digit:]x-z]'\n+match 1 x 'b' '[a-c[:digit:]x-z]'\n+match 1 x 'y' '[a-c[:digit:]x-z]'\n+match 0 x 'q' '[a-c[:digit:]x-z]'\n \n # Additional tests, including some malformed wildmats\n-match 1 1 ']' '[\\\\-^]'\n+match 1 x ']' '[\\\\-^]'\n match 0 0 '[' '[\\\\-^]'\n-match 1 1 '-' '[\\-_]'\n-match 1 1 ']' '[\\]]'\n+match 1 x '-' '[\\-_]'\n+match 1 x ']' '[\\]]'\n match 0 0 '\\]' '[\\]]'\n match 0 0 '\\' '[\\]]'\n match 0 0 'ab' 'a[]b'\n-match 0 1 'a[]b' 'a[]b'\n-match 0 1 'ab[' 'ab['\n+match 0 x 'a[]b' 'a[]b'\n+match 0 x 'ab[' 'ab['\n match 0 0 'ab' '[!'\n match 0 0 'ab' '[-'\n match 1 1 '-' '[-]'\n match 0 0 '-' '[a-'\n match 0 0 '-' '[!a-'\n-match 1 1 '-' '[--A]'\n-match 1 1 '5' '[--A]'\n+match 1 x '-' '[--A]'\n+match 1 x '5' '[--A]'\n match 1 1 ' ' '[ --]'\n match 1 1 '$' '[ --]'\n match 1 1 '-' '[ --]'\n match 0 0 '0' '[ --]'\n-match 1 1 '-' '[---]'\n-match 1 1 '-' '[------]'\n+match 1 x '-' '[---]'\n+match 1 x '-' '[------]'\n match 0 0 'j' '[a-e-n]'\n-match 1 1 '-' '[a-e-n]'\n-match 1 1 'a' '[!------]'\n+match 1 x '-' '[a-e-n]'\n+match 1 x 'a' '[!------]'\n match 0 0 '[' '[]-a]'\n-match 1 1 '^' '[]-a]'\n+match 1 x '^' '[]-a]'\n match 0 0 '^' '[!]-a]'\n-match 1 1 '[' '[!]-a]'\n+match 1 x '[' '[!]-a]'\n match 1 1 '^' '[a^bc]'\n-match 1 1 '-b]' '[a-]b]'\n+match 1 x '-b]' '[a-]b]'\n match 0 0 '\\' '[\\]'\n match 1 1 '\\' '[\\\\]'\n match 0 0 '\\' '[!\\\\]'\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201215","messageId":"1350282362-4505-8-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/13] wildmatch: make wildmatch's return value compatible with fnmatch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:57Z","receivedAt":"2012-10-15T06:25:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"wildmatch returns non-zero if matched, zero otherwise. This patch\nmakes it return zero if matches, non-zero otherwise, like fnmatch().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n test-wildmatch.c |  4 ++--\n wildmatch.c      | 21 ++++++++++++---------\n 2 files changed, 14 insertions(+), 11 deletions(-)\n\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex ac56420..77014e9 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -4,9 +4,9 @@\n int main(int argc, char **argv)\n {\n \tif (!strcmp(argv[1], \"wildmatch\"))\n-\t\treturn wildmatch(argv[3], argv[2]) ? 0 : 1;\n+\t\treturn !!wildmatch(argv[3], argv[2]);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-\t\treturn iwildmatch(argv[3], argv[2]) ? 0 : 1;\n+\t\treturn !!iwildmatch(argv[3], argv[2]);\n \telse if (!strcmp(argv[1], \"fnmatch\"))\n \t\treturn !!fnmatch(argv[3], argv[2], FNM_PATHNAME);\n \telse\ndiff --git a/wildmatch.c b/wildmatch.c\nindex ac29471..6d992d3 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -20,6 +20,9 @@ typedef unsigned char uchar;\n \n #define FALSE 0\n #define TRUE 1\n+\n+#define NOMATCH 1\n+#define MATCH 0\n #define ABORT_ALL -1\n #define ABORT_TO_STARSTAR -2\n \n@@ -78,12 +81,12 @@ static int dowild(const uchar *p, const uchar *text)\n \t\t\t/* FALLTHROUGH */\n \t\tdefault:\n \t\t\tif (t_ch != p_ch)\n-\t\t\t\treturn FALSE;\n+\t\t\t\treturn NOMATCH;\n \t\t\tcontinue;\n \t\tcase '?':\n \t\t\t/* Match anything but '/'. */\n \t\t\tif (t_ch == '/')\n-\t\t\t\treturn FALSE;\n+\t\t\t\treturn NOMATCH;\n \t\t\tcontinue;\n \t\tcase '*':\n \t\t\tif (*++p == '*') {\n@@ -96,14 +99,14 @@ static int dowild(const uchar *p, const uchar *text)\n \t\t\t\t * only if there are no more slash characters. */\n \t\t\t\tif (!special) {\n \t\t\t\t\tif (strchr((char*)text, '/') != NULL)\n-\t\t\t\t\t\treturn FALSE;\n+\t\t\t\t\t\treturn NOMATCH;\n \t\t\t\t}\n-\t\t\t\treturn TRUE;\n+\t\t\t\treturn MATCH;\n \t\t\t}\n \t\t\twhile (1) {\n \t\t\t\tif (t_ch == '\\0')\n \t\t\t\t\tbreak;\n-\t\t\t\tif ((matched = dowild(p, text)) != FALSE) {\n+\t\t\t\tif ((matched = dowild(p, text)) != NOMATCH) {\n \t\t\t\t\tif (!special || matched != ABORT_TO_STARSTAR)\n \t\t\t\t\t\treturn matched;\n \t\t\t\t} else if (!special && t_ch == '/')\n@@ -202,18 +205,18 @@ static int dowild(const uchar *p, const uchar *text)\n \t\t\t\t\tmatched = TRUE;\n \t\t\t} while (prev_ch = p_ch, (p_ch = *++p) != ']');\n \t\t\tif (matched == special || t_ch == '/')\n-\t\t\t\treturn FALSE;\n+\t\t\t\treturn NOMATCH;\n \t\t\tcontinue;\n \t\t}\n \t}\n \n-\treturn *text ? FALSE : TRUE;\n+\treturn *text ? NOMATCH : MATCH;\n }\n \n /* Match the \"pattern\" against the \"text\" string. */\n int wildmatch(const char *pattern, const char *text)\n {\n-\treturn dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n+\treturn dowild((const uchar*)pattern, (const uchar*)text);\n }\n \n /* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n@@ -221,7 +224,7 @@ int iwildmatch(const char *pattern, const char *text)\n {\n \tint ret;\n \tforce_lower_case = 1;\n-\tret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n+\tret = dowild((const uchar*)pattern, (const uchar*)text);\n \tforce_lower_case = 0;\n \treturn ret;\n }\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201216","messageId":"1350282362-4505-9-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/13] wildmatch: remove static variable force_lower_case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:58Z","receivedAt":"2012-10-15T06:25:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One place less to worry about thread safety. Also combine wildmatch\nand iwildmatch into one.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n test-wildmatch.c |  4 ++--\n wildmatch.c      | 21 +++++----------------\n wildmatch.h      |  3 +--\n 3 files changed, 8 insertions(+), 20 deletions(-)\n\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 77014e9..74c0864 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -4,9 +4,9 @@\n int main(int argc, char **argv)\n {\n \tif (!strcmp(argv[1], \"wildmatch\"))\n-\t\treturn !!wildmatch(argv[3], argv[2]);\n+\t\treturn !!wildmatch(argv[3], argv[2], 0);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-\t\treturn !!iwildmatch(argv[3], argv[2]);\n+\t\treturn !!wildmatch(argv[3], argv[2], FNM_CASEFOLD);\n \telse if (!strcmp(argv[1], \"fnmatch\"))\n \t\treturn !!fnmatch(argv[3], argv[2], FNM_PATHNAME);\n \telse\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 6d992d3..eef7b13 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -59,10 +59,8 @@ typedef unsigned char uchar;\n #define ISUPPER(c) (ISASCII(c) && isupper(c))\n #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n \n-static int force_lower_case = 0;\n-\n /* Match pattern \"p\" against \"text\" */\n-static int dowild(const uchar *p, const uchar *text)\n+static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n {\n \tuchar p_ch;\n \n@@ -106,7 +104,7 @@ static int dowild(const uchar *p, const uchar *text)\n \t\t\twhile (1) {\n \t\t\t\tif (t_ch == '\\0')\n \t\t\t\t\tbreak;\n-\t\t\t\tif ((matched = dowild(p, text)) != NOMATCH) {\n+\t\t\t\tif ((matched = dowild(p, text,  force_lower_case)) != NOMATCH) {\n \t\t\t\t\tif (!special || matched != ABORT_TO_STARSTAR)\n \t\t\t\t\t\treturn matched;\n \t\t\t\t} else if (!special && t_ch == '/')\n@@ -214,17 +212,8 @@ static int dowild(const uchar *p, const uchar *text)\n }\n \n /* Match the \"pattern\" against the \"text\" string. */\n-int wildmatch(const char *pattern, const char *text)\n-{\n-\treturn dowild((const uchar*)pattern, (const uchar*)text);\n-}\n-\n-/* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n-int iwildmatch(const char *pattern, const char *text)\n+int wildmatch(const char *pattern, const char *text, int flags)\n {\n-\tint ret;\n-\tforce_lower_case = 1;\n-\tret = dowild((const uchar*)pattern, (const uchar*)text);\n-\tforce_lower_case = 0;\n-\treturn ret;\n+\treturn dowild((const uchar*)pattern, (const uchar*)text,\n+\t\t      flags & FNM_CASEFOLD ? 1 :0);\n }\ndiff --git a/wildmatch.h b/wildmatch.h\nindex 562faa3..e974f9a 100644\n--- a/wildmatch.h\n+++ b/wildmatch.h\n@@ -1,4 +1,3 @@\n /* wildmatch.h */\n \n-int wildmatch(const char *pattern, const char *text);\n-int iwildmatch(const char *pattern, const char *text);\n+int wildmatch(const char *pattern, const char *text, int flags);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201217","messageId":"1350282362-4505-10-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/13] wildmatch: fix case-insensitive matching","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:25:59Z","receivedAt":"2012-10-15T06:25:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"dowild() does case insensitive matching by lower-casing the text. That\nmeans lower case letters in patterns imply case-insensitive matching,\nbut upper case means exact matching.\n\nWe do not want that subtlety. Lower case pattern too so iwildmatch()\nalways does what we expect it to do.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wildmatch.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex eef7b13..5469866 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -71,6 +71,8 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\t\treturn ABORT_ALL;\n \t\tif (force_lower_case && ISUPPER(t_ch))\n \t\t\tt_ch = tolower(t_ch);\n+\t\tif (force_lower_case && ISUPPER(p_ch))\n+\t\t\tp_ch = tolower(p_ch);\n \t\tswitch (p_ch) {\n \t\tcase '\\\\':\n \t\t\t/* Literal match with following character.  Note that the test\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201218","messageId":"1350282362-4505-11-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/13] wildmatch: adjust \"**\" behavior","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:26:00Z","receivedAt":"2012-10-15T06:26:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Standard wildmatch() sees consecutive asterisks as \"*\" that can also\nmatch slashes. But that may be hard to explain to users as\n\"abc/**/def\" can match \"abcdef\", \"abcxyzdef\", \"abc/def\", \"abc/x/def\",\n\"abc/x/y/def\"...\n\nThis patch changes wildmatch so that users can do\n\n- \"**/def\" -> all paths ending with file/directory 'def'\n- \"abc/**\" - equivalent to \"/abc/\"\n- \"abc/**/def\" -> \"abc/x/def\", \"abc/x/y/def\"...\n- otherwise consider the pattern malformed if \"**\" is found\n\nBasically the magic of \"**\" only remains if it's wrapped around by\nslashes.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070-wildmatch.sh |  5 +++--\n wildmatch.c          | 13 +++++++------\n wildmatch.h          |  6 ++++++\n 3 files changed, 16 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex dd95b00..15848d5 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -46,7 +46,7 @@ match 0 0 foobar 'foo\\*bar'\n match 1 1 'f\\oo' 'f\\\\oo'\n match 1 1 ball '*[al]?'\n match 0 0 ten '[ten]'\n-match 1 1 ten '**[!te]'\n+match 0 1 ten '**[!te]'\n match 0 0 ten '**[!ten]'\n match 1 1 ten 't[a-g]n'\n match 0 0 ten 't[!a-g]n'\n@@ -61,7 +61,8 @@ match 1 1 ']' ']'\n \n # Extended slash-matching features\n match 0 0 'foo/baz/bar' 'foo*bar'\n-match 1 0 'foo/baz/bar' 'foo**bar'\n+match 0 0 'foo/baz/bar' 'foo**bar'\n+match 0 1 'foobazbar' 'foo**bar'\n match 0 0 'foo/bar' 'foo?bar'\n match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 5469866..85bc0df 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -21,11 +21,6 @@ typedef unsigned char uchar;\n #define FALSE 0\n #define TRUE 1\n \n-#define NOMATCH 1\n-#define MATCH 0\n-#define ABORT_ALL -1\n-#define ABORT_TO_STARSTAR -2\n-\n #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \\\n \t\t\t\t    && *(class) == *(litmatch) \\\n \t\t\t\t    && strncmp((char*)class, litmatch, len) == 0)\n@@ -90,8 +85,14 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\t\tcontinue;\n \t\tcase '*':\n \t\t\tif (*++p == '*') {\n+\t\t\t\tconst uchar *prev_p = p - 2;\n \t\t\t\twhile (*++p == '*') {}\n-\t\t\t\tspecial = TRUE;\n+\t\t\t\tif ((prev_p == text || *prev_p == '/') ||\n+\t\t\t\t    (*p == '\\0' || *p == '/' ||\n+\t\t\t\t     (p[0] == '\\\\' && p[1] == '/'))) {\n+\t\t\t\t\tspecial = TRUE;\n+\t\t\t\t} else\n+\t\t\t\t\treturn ABORT_MALFORMED;\n \t\t\t} else\n \t\t\t\tspecial = FALSE;\n \t\t\tif (*p == '\\0') {\ndiff --git a/wildmatch.h b/wildmatch.h\nindex e974f9a..984a38c 100644\n--- a/wildmatch.h\n+++ b/wildmatch.h\n@@ -1,3 +1,9 @@\n /* wildmatch.h */\n \n+#define ABORT_MALFORMED 2\n+#define NOMATCH 1\n+#define MATCH 0\n+#define ABORT_ALL -1\n+#define ABORT_TO_STARSTAR -2\n+\n int wildmatch(const char *pattern, const char *text, int flags);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201219","messageId":"1350282362-4505-12-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/13] wildmatch: make /**/ match zero or more directories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:26:01Z","receivedAt":"2012-10-15T06:26:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"foo/**/bar\" matches \"foo/x/bar\", \"foo/x/y/bar\"... but not\n\"foo/bar\". We make a special case, when foo/**/ is detected (and\n\"foo/\" part is already matched), try matching \"bar\" with the rest of\nthe string.\n\n\"Match one or more directories\" semantics can be easily achieved using\n\"foo/*/**/bar\".\n\nThis also makes \"**/foo\" match \"foo\" in addition to \"x/foo\",\n\"x/y/foo\"..\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070-wildmatch.sh |  8 +++++++-\n wildmatch.c          | 12 ++++++++++++\n 2 files changed, 19 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex 15848d5..e6ad6f4 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -63,11 +63,17 @@ match 1 1 ']' ']'\n match 0 0 'foo/baz/bar' 'foo*bar'\n match 0 0 'foo/baz/bar' 'foo**bar'\n match 0 1 'foobazbar' 'foo**bar'\n+match 1 1 'foo/baz/bar' 'foo/**/bar'\n+match 1 0 'foo/baz/bar' 'foo/**/**/bar'\n+match 1 0 'foo/b/a/z/bar' 'foo/**/bar'\n+match 1 0 'foo/b/a/z/bar' 'foo/**/**/bar'\n+match 1 0 'foo/bar' 'foo/**/bar'\n+match 1 0 'foo/bar' 'foo/**/**/bar'\n match 0 0 'foo/bar' 'foo?bar'\n match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n-match 0 0 'foo' '**/foo'\n+match 1 0 'foo' '**/foo'\n match 1 x '/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 85bc0df..3972e26 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -90,6 +90,18 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\t\t\tif ((prev_p == text || *prev_p == '/') ||\n \t\t\t\t    (*p == '\\0' || *p == '/' ||\n \t\t\t\t     (p[0] == '\\\\' && p[1] == '/'))) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Assuming we already match 'foo/' and are at\n+\t\t\t\t\t * <star star slash>, just assume it matches\n+\t\t\t\t\t * nothing and go ahead match the rest of the\n+\t\t\t\t\t * pattern with the remaining string. This\n+\t\t\t\t\t * helps make foo/<*><*>/bar (<> because\n+\t\t\t\t\t * otherwise it breaks C comment syntax) match\n+\t\t\t\t\t * both foo/bar and foo/a/bar.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (p[0] == '/' &&\n+\t\t\t\t\t    dowild(p + 1, text, force_lower_case) == MATCH)\n+\t\t\t\t\t\treturn MATCH;\n \t\t\t\t\tspecial = TRUE;\n \t\t\t\t} else\n \t\t\t\t\treturn ABORT_MALFORMED;\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201220","messageId":"1350282362-4505-13-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282362-4505-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/13] Support \"**\" wildcard in .gitignore and .gitattributes","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:26:02Z","receivedAt":"2012-10-15T06:26:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitignore.txt        | 19 +++++++++++++++++++\n dir.c                              |  4 +++-\n t/t0003-attributes.sh              | 37 +++++++++++++++++++++++++++++++++++++\n t/t3001-ls-files-others-exclude.sh | 18 ++++++++++++++++++\n 4 files changed, 77 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 2e7328b..d4747ce 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -96,6 +96,25 @@ PATTERN FORMAT\n    For example, \"/{asterisk}.c\" matches \"cat-file.c\" but not\n    \"mozilla-sha1/sha1.c\".\n \n+Two consecutive asterisks (\"`**`\") in patterns matched against\n+full pathname may have special meaning:\n+\n+ - A leading \"`**`\" followed by a slash means match in all\n+   directories. For example, \"`**/foo`\" matches file or directory\n+   \"`foo`\" anywhere, the same as pattern \"`foo`\". \"**/foo/bar\"\n+   matches file or directory \"`bar`\" anywhere that is directly\n+   under directory \"`foo`\".\n+\n+ - A trailing \"/**\" matches everything inside. For example,\n+   \"abc/**\" matches all files inside directory \"abc\", relative\n+   to the location of the `.gitignore` file, with infinite depth.\n+\n+ - A slash followed by two consecutive asterisks then a slash\n+   matches zero or more directories. For example, \"`a/**/b`\"\n+   matches \"`a/b`\", \"`a/x/b`\", \"`a/x/y/b`\" and so on.\n+\n+ - Other consecutive asterisks are considered invalid.\n+\n NOTES\n -----\n \ndiff --git a/dir.c b/dir.c\nindex ee8e711..cb7328b 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"wildmatch.h\"\n \n struct path_simplify {\n \tint len;\n@@ -593,7 +594,8 @@ int match_pathname(const char *pathname, int pathlen,\n \t\tnamelen -= prefix;\n \t}\n \n-\treturn fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;\n+\treturn wildmatch(pattern, name,\n+\t\t\t ignore_case ? FNM_CASEFOLD : 0) == 0;\n }\n \n /* Scan the list and let the last match determine the fate.\ndiff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh\nindex f6c21ea..c962403 100755\n--- a/t/t0003-attributes.sh\n+++ b/t/t0003-attributes.sh\n@@ -216,6 +216,43 @@ test_expect_success 'patterns starting with exclamation' '\n \tattr_check \"!f\" foo\n '\n \n+test_expect_success '\"**\" test' '\n+\techo \"**/f foo=bar\" >.gitattributes &&\n+\tcat <<\\EOF >expect &&\n+f: foo: bar\n+a/f: foo: bar\n+a/b/f: foo: bar\n+a/b/c/f: foo: bar\n+EOF\n+\tgit check-attr foo -- \"f\" >actual 2>err &&\n+\tgit check-attr foo -- \"a/f\" >>actual 2>>err &&\n+\tgit check-attr foo -- \"a/b/f\" >>actual 2>>err &&\n+\tgit check-attr foo -- \"a/b/c/f\" >>actual 2>>err &&\n+\ttest_cmp expect actual &&\n+\ttest_line_count = 0 err\n+'\n+\n+test_expect_success '\"**\" with no slashes test' '\n+\techo \"a**f foo=bar\" >.gitattributes &&\n+\tgit check-attr foo -- \"f\" >actual &&\n+\tcat <<\\EOF >expect &&\n+f: foo: unspecified\n+af: foo: bar\n+axf: foo: bar\n+a/f: foo: unspecified\n+a/b/f: foo: unspecified\n+a/b/c/f: foo: unspecified\n+EOF\n+\tgit check-attr foo -- \"f\" >actual 2>err &&\n+\tgit check-attr foo -- \"af\" >>actual 2>err &&\n+\tgit check-attr foo -- \"axf\" >>actual 2>err &&\n+\tgit check-attr foo -- \"a/f\" >>actual 2>>err &&\n+\tgit check-attr foo -- \"a/b/f\" >>actual 2>>err &&\n+\tgit check-attr foo -- \"a/b/c/f\" >>actual 2>>err &&\n+\ttest_cmp expect actual &&\n+\ttest_line_count = 0 err\n+'\n+\n test_expect_success 'setup bare' '\n \tgit clone --bare . bare.git &&\n \tcd bare.git\ndiff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh\nindex dc2f045..efb7ebc 100755\n--- a/t/t3001-ls-files-others-exclude.sh\n+++ b/t/t3001-ls-files-others-exclude.sh\n@@ -220,4 +220,22 @@ test_expect_success 'pattern matches prefix completely' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'ls-files with \"**\" patterns' '\n+\tcat <<\\EOF >expect &&\n+a.1\n+one/a.1\n+one/two/a.1\n+three/a.1\n+EOF\n+\tgit ls-files -o -i --exclude \"**/a.1\" >actual\n+\ttest_cmp expect actual\n+'\n+\n+\n+test_expect_success 'ls-files with \"**\" patterns and no slashes' '\n+\t: >expect &&\n+\tgit ls-files -o -i --exclude \"one**a.1\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201221","messageId":"1350282486-4646-1-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282212-4270-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/12] dir.c: rename cryptic 'which' variable to more consistent name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:27:55Z","receivedAt":"2012-10-15T06:27:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\n'el' is only *slightly* less cryptic, but is already used as the\nvariable name for a struct exclude_list pointer in numerous other\nplaces, so this reduces the number of cryptic variable names in use by\none :-)\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 10 +++++-----\n dir.h |  4 ++--\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex ee8e711..f7219b5 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -347,7 +347,7 @@ void parse_exclude_pattern(const char **pattern,\n }\n \n void add_exclude(const char *string, const char *base,\n-\t\t int baselen, struct exclude_list *which)\n+\t\t int baselen, struct exclude_list *el)\n {\n \tstruct exclude *x;\n \tint patternlen;\n@@ -371,8 +371,8 @@ void add_exclude(const char *string, const char *base,\n \tx->base = base;\n \tx->baselen = baselen;\n \tx->flags = flags;\n-\tALLOC_GROW(which->excludes, which->nr + 1, which->alloc);\n-\twhich->excludes[which->nr++] = x;\n+\tALLOC_GROW(el->excludes, el->nr + 1, el->alloc);\n+\tel->excludes[el->nr++] = x;\n }\n \n static void *read_skip_worktree_file_from_index(const char *path, size_t *size)\n@@ -414,7 +414,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\t\t\t   const char *base,\n \t\t\t\t   int baselen,\n \t\t\t\t   char **buf_p,\n-\t\t\t\t   struct exclude_list *which,\n+\t\t\t\t   struct exclude_list *el,\n \t\t\t\t   int check_index)\n {\n \tstruct stat st;\n@@ -461,7 +461,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\tif (buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n-\t\t\t\tadd_exclude(entry, base, baselen, which);\n+\t\t\t\tadd_exclude(entry, base, baselen, el);\n \t\t\t}\n \t\t\tentry = buf + i + 1;\n \t\t}\ndiff --git a/dir.h b/dir.h\nindex f5c89e3..69cc7d2 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -105,11 +105,11 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,\n \n \n extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\n-\t\t\t\t\t  char **buf_p, struct exclude_list *which, int check_index);\n+\t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);\n extern void add_exclude(const char *string, const char *base,\n-\t\t\tint baselen, struct exclude_list *which);\n+\t\t\tint baselen, struct exclude_list *el);\n extern void free_excludes(struct exclude_list *el);\n extern int file_exists(const char *);\n \n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201222","messageId":"1350282486-4646-2-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/12] dir.c: rename path_excluded() to is_path_excluded()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:27:56Z","receivedAt":"2012-10-15T06:27:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nStart adopting clearer names for exclude functions.  This 'is_*'\nnaming pattern for functions returning booleans was agreed here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204924\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/add.c      | 2 +-\n builtin/ls-files.c | 2 +-\n dir.c              | 4 ++--\n dir.h              | 2 +-\n unpack-trees.c     | 2 +-\n 5 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 89dce56..c689f37 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -453,7 +453,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\t\t    && !file_exists(pathspec[i])) {\n \t\t\t\tif (ignore_missing) {\n \t\t\t\t\tint dtype = DT_UNKNOWN;\n-\t\t\t\t\tif (path_excluded(&check, pathspec[i], -1, &dtype))\n+\t\t\t\t\tif (is_path_excluded(&check, pathspec[i], -1, &dtype))\n \t\t\t\t\t\tdir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));\n \t\t\t\t} else\n \t\t\t\t\tdie(_(\"pathspec '%s' did not match any files\"),\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 31b3f2d..ef7f99a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -203,7 +203,7 @@ static void show_ru_info(void)\n static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)\n {\n \tint dtype = ce_to_dtype(ce);\n-\treturn path_excluded(check, ce->name, ce_namelen(ce), &dtype);\n+\treturn is_path_excluded(check, ce->name, ce_namelen(ce), &dtype);\n }\n \n static void show_files(struct dir_struct *dir)\ndiff --git a/dir.c b/dir.c\nindex f7219b5..6dfb815 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -679,8 +679,8 @@ void path_exclude_check_clear(struct path_exclude_check *check)\n  * A path to a directory known to be excluded is left in check->path to\n  * optimize for repeated checks for files in the same excluded directory.\n  */\n-int path_excluded(struct path_exclude_check *check,\n-\t\t  const char *name, int namelen, int *dtype)\n+int is_path_excluded(struct path_exclude_check *check,\n+\t\t     const char *name, int namelen, int *dtype)\n {\n \tint i;\n \tstruct strbuf *path = &check->path;\ndiff --git a/dir.h b/dir.h\nindex 69cc7d2..d40aeea 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -101,7 +101,7 @@ struct path_exclude_check {\n };\n extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n extern void path_exclude_check_clear(struct path_exclude_check *);\n-extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n+extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n \n \n extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 33a5819..3ac6370 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1372,7 +1372,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t\treturn 0;\n \n \tif (o->dir &&\n-\t    path_excluded(o->path_exclude_check, name, -1, &dtype))\n+\t    is_path_excluded(o->path_exclude_check, name, -1, &dtype))\n \t\t/*\n \t\t * ce->name is explicitly excluded, so it is Ok to\n \t\t * overwrite it.\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201223","messageId":"1350282486-4646-3-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/12] dir.c: rename excluded_from_list() to is_excluded_from_list()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:27:57Z","receivedAt":"2012-10-15T06:27:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nContinue adopting clearer names for exclude functions.  This 'is_*'\nnaming pattern for functions returning booleans was discussed here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204924\n\nAlso adjust their callers as necessary.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c          | 11 ++++++-----\n dir.h          |  4 ++--\n unpack-trees.c |  8 +++++---\n 3 files changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 6dfb815..7e1a23b 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -599,9 +599,9 @@ int match_pathname(const char *pathname, int pathlen,\n /* Scan the list and let the last match determine the fate.\n  * Return 1 for exclude, 0 for include and -1 for undecided.\n  */\n-int excluded_from_list(const char *pathname,\n-\t\t       int pathlen, const char *basename, int *dtype,\n-\t\t       struct exclude_list *el)\n+int is_excluded_from_list(const char *pathname,\n+\t\t\t  int pathlen, const char *basename, int *dtype,\n+\t\t\t  struct exclude_list *el)\n {\n \tint i;\n \n@@ -648,8 +648,9 @@ static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n \n \tprep_exclude(dir, pathname, basename-pathname);\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++) {\n-\t\tswitch (excluded_from_list(pathname, pathlen, basename,\n-\t\t\t\t\t   dtype_p, &dir->exclude_list[st])) {\n+\t\tswitch (is_excluded_from_list(pathname, pathlen,\n+\t\t\t\t\t      basename, dtype_p,\n+\t\t\t\t\t      &dir->exclude_list[st])) {\n \t\tcase 0:\n \t\t\treturn 0;\n \t\tcase 1:\ndiff --git a/dir.h b/dir.h\nindex d40aeea..2fce4bf 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -76,8 +76,8 @@ extern int within_depth(const char *name, int namelen, int depth, int max_depth)\n extern int fill_directory(struct dir_struct *dir, const char **pathspec);\n extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);\n \n-extern int excluded_from_list(const char *pathname, int pathlen, const char *basename,\n-\t\t\t      int *dtype, struct exclude_list *el);\n+extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename,\n+\t\t\t\t int *dtype, struct exclude_list *el);\n struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);\n \n /*\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 3ac6370..c0da094 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -836,7 +836,8 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,\n {\n \tstruct cache_entry **cache_end;\n \tint dtype = DT_DIR;\n-\tint ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);\n+\tint ret = is_excluded_from_list(prefix, prefix_len,\n+\t\t\t\t\tbasename, &dtype, el);\n \n \tprefix[prefix_len++] = '/';\n \n@@ -855,7 +856,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,\n \t * with ret (iow, we know in advance the incl/excl\n \t * decision for the entire directory), clear flag here without\n \t * calling clear_ce_flags_1(). That function will call\n-\t * the expensive excluded_from_list() on every entry.\n+\t * the expensive is_excluded_from_list() on every entry.\n \t */\n \treturn clear_ce_flags_1(cache, cache_end - cache,\n \t\t\t\tprefix, prefix_len,\n@@ -938,7 +939,8 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,\n \n \t\t/* Non-directory */\n \t\tdtype = ce_to_dtype(ce);\n-\t\tret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el);\n+\t\tret = is_excluded_from_list(ce->name, ce_namelen(ce),\n+\t\t\t\t\t    name, &dtype, el);\n \t\tif (ret < 0)\n \t\t\tret = defval;\n \t\tif (ret > 0)\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201224","messageId":"1350282486-4646-4-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/12] dir.c: rename excluded() to is_excluded()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:27:58Z","receivedAt":"2012-10-15T06:27:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nContinue adopting clearer names for exclude functions.  This is_*\nnaming pattern for functions returning booleans was discussed here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204924\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n attr.c |  2 +-\n dir.c  | 10 +++++-----\n dir.h  |  4 ++--\n 3 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 2fc6353..5362563 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -284,7 +284,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,\n  * (reading the file from top to bottom), .gitattribute of the root\n  * directory (again, reading the file from top to bottom) down to the\n  * current directory, and then scan the list backwards to find the first match.\n- * This is exactly the same as what excluded() does in dir.c to deal with\n+ * This is exactly the same as what is_excluded() does in dir.c to deal with\n  * .gitignore\n  */\n \ndiff --git a/dir.c b/dir.c\nindex 7e1a23b..50381f8 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -639,7 +639,7 @@ int is_excluded_from_list(const char *pathname,\n \treturn -1; /* undecided */\n }\n \n-static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n {\n \tint pathlen = strlen(pathname);\n \tint st;\n@@ -689,7 +689,7 @@ int is_path_excluded(struct path_exclude_check *check,\n \t/*\n \t * we allow the caller to pass namelen as an optimization; it\n \t * must match the length of the name, as we eventually call\n-\t * excluded() on the whole name string.\n+\t * is_excluded() on the whole name string.\n \t */\n \tif (namelen < 0)\n \t\tnamelen = strlen(name);\n@@ -706,7 +706,7 @@ int is_path_excluded(struct path_exclude_check *check,\n \n \t\tif (ch == '/') {\n \t\t\tint dt = DT_DIR;\n-\t\t\tif (excluded(check->dir, path->buf, &dt))\n+\t\t\tif (is_excluded(check->dir, path->buf, &dt))\n \t\t\t\treturn 1;\n \t\t}\n \t\tstrbuf_addch(path, ch);\n@@ -715,7 +715,7 @@ int is_path_excluded(struct path_exclude_check *check,\n \t/* An entry in the index; cannot be a directory with subentries */\n \tstrbuf_setlen(path, 0);\n \n-\treturn excluded(check->dir, name, dtype);\n+\treturn is_excluded(check->dir, name, dtype);\n }\n \n static struct dir_entry *dir_entry_new(const char *pathname, int len)\n@@ -1015,7 +1015,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\t\t\t\t  const struct path_simplify *simplify,\n \t\t\t\t\t  int dtype, struct dirent *de)\n {\n-\tint exclude = excluded(dir, path->buf, &dtype);\n+\tint exclude = is_excluded(dir, path->buf, &dtype);\n \tif (exclude && (dir->flags & DIR_COLLECT_IGNORED)\n \t    && exclude_matches_pathspec(path->buf, path->len, simplify))\n \t\tdir_add_ignored(dir, path->buf, path->len);\ndiff --git a/dir.h b/dir.h\nindex 2fce4bf..4b887cc 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -91,8 +91,8 @@ extern int match_pathname(const char *, int,\n \t\t\t  const char *, int, int, int);\n \n /*\n- * The excluded() API is meant for callers that check each level of leading\n- * directory hierarchies with excluded() to avoid recursing into excluded\n+ * The is_excluded() API is meant for callers that check each level of leading\n+ * directory hierarchies with is_excluded() to avoid recursing into excluded\n  * directories.  Callers that do not do so should use this API instead.\n  */\n struct path_exclude_check {\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201225","messageId":"1350282486-4646-5-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/12] dir.c: refactor is_excluded_from_list()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:27:59Z","receivedAt":"2012-10-15T06:27:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nThe excluded function uses a new helper function called\nlast_exclude_matching_from_list() to perform the inner loop over all of\nthe exclude patterns.  The helper just tells us whether the path is\nincluded, excluded, or undecided.\n\nHowever, it may be useful to know _which_ pattern was triggered.  So\nlet's pass out the entire exclude match, which contains the status\ninformation we were already passing out.\n\nFurther patches can make use of this.\n\nThis is a modified forward port of a patch from 2009 by Jeff King:\nhttp://article.gmane.org/gmane.comp.version-control.git/108815\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 37 ++++++++++++++++++++++++++++---------\n 1 file changed, 28 insertions(+), 9 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 50381f8..859e0f9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -596,22 +596,26 @@ int match_pathname(const char *pathname, int pathlen,\n \treturn fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;\n }\n \n-/* Scan the list and let the last match determine the fate.\n- * Return 1 for exclude, 0 for include and -1 for undecided.\n+/*\n+ * Scan the given exclude list in reverse to see whether pathname\n+ * should be ignored.  The first match (i.e. the last on the list), if\n+ * any, determines the fate.  Returns the exclude_list element which\n+ * matched, or NULL for undecided.\n  */\n-int is_excluded_from_list(const char *pathname,\n-\t\t\t  int pathlen, const char *basename, int *dtype,\n-\t\t\t  struct exclude_list *el)\n+static struct exclude *last_exclude_matching_from_list(const char *pathname,\n+\t\t\t\t\t\t       int pathlen,\n+\t\t\t\t\t\t       const char *basename,\n+\t\t\t\t\t\t       int *dtype,\n+\t\t\t\t\t\t       struct exclude_list *el)\n {\n \tint i;\n \n \tif (!el->nr)\n-\t\treturn -1;\t/* undefined */\n+\t\treturn NULL;\t/* undefined */\n \n \tfor (i = el->nr - 1; 0 <= i; i--) {\n \t\tstruct exclude *x = el->excludes[i];\n \t\tconst char *exclude = x->pattern;\n-\t\tint to_exclude = x->flags & EXC_FLAG_NEGATIVE ? 0 : 1;\n \t\tint prefix = x->nowildcardlen;\n \n \t\tif (x->flags & EXC_FLAG_MUSTBEDIR) {\n@@ -626,7 +630,7 @@ int is_excluded_from_list(const char *pathname,\n \t\t\t\t\t   pathlen - (basename - pathname),\n \t\t\t\t\t   exclude, prefix, x->patternlen,\n \t\t\t\t\t   x->flags))\n-\t\t\t\treturn to_exclude;\n+\t\t\t\treturn x;\n \t\t\tcontinue;\n \t\t}\n \n@@ -634,8 +638,23 @@ int is_excluded_from_list(const char *pathname,\n \t\tif (match_pathname(pathname, pathlen,\n \t\t\t\t   x->base, x->baselen ? x->baselen - 1 : 0,\n \t\t\t\t   exclude, prefix, x->patternlen, x->flags))\n-\t\t\treturn to_exclude;\n+\t\t\treturn x;\n \t}\n+\treturn NULL; /* undecided */\n+}\n+\n+/*\n+ * Scan the list and let the last match determine the fate.\n+ * Return 1 for exclude, 0 for include and -1 for undecided.\n+ */\n+int is_excluded_from_list(const char *pathname,\n+\t\t\t  int pathlen, const char *basename, int *dtype,\n+\t\t\t  struct exclude_list *el)\n+{\n+\tstruct exclude *exclude;\n+\texclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el);\n+\tif (exclude)\n+\t\treturn exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;\n \treturn -1; /* undecided */\n }\n \n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201226","messageId":"1350282486-4646-6-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/12] dir.c: refactor is_excluded()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:00Z","receivedAt":"2012-10-15T06:28:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nIn a similar way to the previous commit, this extracts a new helper\nfunction last_exclude_matching() which returns the last exclude_list\nelement which matched, or NULL if no match was found.  is_excluded()\nbecomes a wrapper around this, and just returns 0 or 1 depending on\nwhether any matching exclude_list element was found.\n\nThis allows callers to find out _why_ a given path was excluded,\nrather than just whether it was or not, paving the way for a new git\nsub-command which allows users to test their exclude lists from the\ncommand line.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 38 +++++++++++++++++++++++++++++---------\n 1 file changed, 29 insertions(+), 9 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 859e0f9..c91a2f6 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -658,24 +658,44 @@ int is_excluded_from_list(const char *pathname,\n \treturn -1; /* undecided */\n }\n \n-static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+/*\n+ * Loads the exclude lists for the directory containing pathname, then\n+ * scans all exclude lists to determine whether pathname is excluded.\n+ * Returns the exclude_list element which matched, or NULL for\n+ * undecided.\n+ */\n+static struct exclude *last_exclude_matching(struct dir_struct *dir,\n+\t\t\t\t\t     const char *pathname,\n+\t\t\t\t\t     int *dtype_p)\n {\n \tint pathlen = strlen(pathname);\n \tint st;\n+\tstruct exclude *exclude;\n \tconst char *basename = strrchr(pathname, '/');\n \tbasename = (basename) ? basename+1 : pathname;\n \n \tprep_exclude(dir, pathname, basename-pathname);\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++) {\n-\t\tswitch (is_excluded_from_list(pathname, pathlen,\n-\t\t\t\t\t      basename, dtype_p,\n-\t\t\t\t\t      &dir->exclude_list[st])) {\n-\t\tcase 0:\n-\t\t\treturn 0;\n-\t\tcase 1:\n-\t\t\treturn 1;\n-\t\t}\n+\t\texclude = last_exclude_matching_from_list(\n+\t\t\tpathname, pathlen, basename, dtype_p,\n+\t\t\t&dir->exclude_list[st]);\n+\t\tif (exclude)\n+\t\t\treturn exclude;\n \t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Loads the exclude lists for the directory containing pathname, then\n+ * scans all exclude lists to determine whether pathname is excluded.\n+ * Returns 1 if true, otherwise 0.\n+ */\n+static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+{\n+\tstruct exclude *exclude =\n+\t\tlast_exclude_matching(dir, pathname, dtype_p);\n+\tif (exclude)\n+\t\treturn exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;\n \treturn 0;\n }\n \n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201227","messageId":"1350282486-4646-7-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/12] dir.c: refactor is_path_excluded()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:01Z","receivedAt":"2012-10-15T06:28:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nIn a similar way to the previous commit, this extracts a new helper\nfunction last_exclude_matching_path() which return the last\nexclude_list element which matched, or NULL if no match was found.\nis_path_excluded() becomes a wrapper around this, and just returns 0\nor 1 depending on whether any matching exclude_list element was found.\n\nThis allows callers to find out _why_ a given path was excluded,\nrather than just whether it was or not, paving the way for a new git\nsub-command which allows users to test their exclude lists from the\ncommand line.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c | 47 ++++++++++++++++++++++++++++++++++++++---------\n dir.h |  3 +++\n 2 files changed, 41 insertions(+), 9 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex c91a2f6..84ab826 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -703,6 +703,7 @@ void path_exclude_check_init(struct path_exclude_check *check,\n \t\t\t     struct dir_struct *dir)\n {\n \tcheck->dir = dir;\n+\tcheck->exclude = NULL;\n \tstrbuf_init(&check->path, 256);\n }\n \n@@ -712,18 +713,21 @@ void path_exclude_check_clear(struct path_exclude_check *check)\n }\n \n /*\n- * Is this name excluded?  This is for a caller like show_files() that\n- * do not honor directory hierarchy and iterate through paths that are\n- * possibly in an ignored directory.\n+ * For each subdirectory in name, starting with the top-most, checks\n+ * to see if that subdirectory is excluded, and if so, returns the\n+ * corresponding exclude structure.  Otherwise, checks whether name\n+ * itself (which is presumably a file) is excluded.\n  *\n  * A path to a directory known to be excluded is left in check->path to\n  * optimize for repeated checks for files in the same excluded directory.\n  */\n-int is_path_excluded(struct path_exclude_check *check,\n-\t\t     const char *name, int namelen, int *dtype)\n+struct exclude *last_exclude_matching_path(struct path_exclude_check *check,\n+\t\t\t\t\t   const char *name, int namelen,\n+\t\t\t\t\t   int *dtype)\n {\n \tint i;\n \tstruct strbuf *path = &check->path;\n+\tstruct exclude *exclude;\n \n \t/*\n \t * we allow the caller to pass namelen as an optimization; it\n@@ -733,11 +737,17 @@ int is_path_excluded(struct path_exclude_check *check,\n \tif (namelen < 0)\n \t\tnamelen = strlen(name);\n \n+\t/*\n+\t * If path is non-empty, and name is equal to path or a\n+\t * subdirectory of path, name should be excluded, because\n+\t * it's inside a directory which is already known to be\n+\t * excluded and was previously left in check->path.\n+\t */\n \tif (path->len &&\n \t    path->len <= namelen &&\n \t    !memcmp(name, path->buf, path->len) &&\n \t    (!name[path->len] || name[path->len] == '/'))\n-\t\treturn 1;\n+\t\treturn check->exclude;\n \n \tstrbuf_setlen(path, 0);\n \tfor (i = 0; name[i]; i++) {\n@@ -745,8 +755,12 @@ int is_path_excluded(struct path_exclude_check *check,\n \n \t\tif (ch == '/') {\n \t\t\tint dt = DT_DIR;\n-\t\t\tif (is_excluded(check->dir, path->buf, &dt))\n-\t\t\t\treturn 1;\n+\t\t\texclude = last_exclude_matching(check->dir,\n+\t\t\t\t\t\t\tpath->buf, &dt);\n+\t\t\tif (exclude) {\n+\t\t\t\tcheck->exclude = exclude;\n+\t\t\t\treturn exclude;\n+\t\t\t}\n \t\t}\n \t\tstrbuf_addch(path, ch);\n \t}\n@@ -754,7 +768,22 @@ int is_path_excluded(struct path_exclude_check *check,\n \t/* An entry in the index; cannot be a directory with subentries */\n \tstrbuf_setlen(path, 0);\n \n-\treturn is_excluded(check->dir, name, dtype);\n+\treturn last_exclude_matching(check->dir, name, dtype);\n+}\n+\n+/*\n+ * Is this name excluded?  This is for a caller like show_files() that\n+ * do not honor directory hierarchy and iterate through paths that are\n+ * possibly in an ignored directory.\n+ */\n+int is_path_excluded(struct path_exclude_check *check,\n+\t\t  const char *name, int namelen, int *dtype)\n+{\n+\tstruct exclude *exclude =\n+\t\tlast_exclude_matching_path(check, name, namelen, dtype);\n+\tif (exclude)\n+\t\treturn exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;\n+\treturn 0;\n }\n \n static struct dir_entry *dir_entry_new(const char *pathname, int len)\ndiff --git a/dir.h b/dir.h\nindex 4b887cc..02ac0bf 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -97,10 +97,13 @@ extern int match_pathname(const char *, int,\n  */\n struct path_exclude_check {\n \tstruct dir_struct *dir;\n+\tstruct exclude *exclude;\n \tstruct strbuf path;\n };\n extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n extern void path_exclude_check_clear(struct path_exclude_check *);\n+extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, const char *,\n+\t\t\t\t\t\t  int namelen, int *dtype);\n extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n \n \n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201228","messageId":"1350282486-4646-8-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/12] dir.c: keep track of where patterns came from","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:02Z","receivedAt":"2012-10-15T06:28:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nFor exclude patterns read in from files, the filename is stored together\nwith the corresponding line number (counting starting at 1).\n\nFor exclude patterns provided on the command line, the sequence number\nis negative, with counting starting at -1, so for example the 2nd\npattern provided via --exclude would be numbered -2.  This allows any\nfuture consumers of that data to easily distinguish between exclude\npatterns from files vs. from the CLI.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clean.c    |  2 +-\n builtin/ls-files.c |  3 ++-\n dir.c              | 26 ++++++++++++++++++++------\n dir.h              |  5 ++++-\n 4 files changed, 27 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex 0c7b3d0..f618231 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -99,7 +99,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)\n \n \tfor (i = 0; i < exclude_list.nr; i++)\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0,\n-\t\t\t    &dir.exclude_list[EXC_CMDL]);\n+\t\t\t    &dir.exclude_list[EXC_CMDL], \"--exclude option\", -(i+1));\n \n \tpathspec = get_pathspec(prefix, argv);\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex ef7f99a..db3c081 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -35,6 +35,7 @@ static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n+static int exclude_args;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -423,7 +424,7 @@ static int option_parse_exclude(const struct option *opt,\n \tstruct exclude_list *list = opt->value;\n \n \texc_given = 1;\n-\tadd_exclude(arg, \"\", 0, list);\n+\tadd_exclude(arg, \"\", 0, list, \"--exclude option\", --exclude_args);\n \n \treturn 0;\n }\ndiff --git a/dir.c b/dir.c\nindex 84ab826..81a5dd5 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -347,7 +347,8 @@ void parse_exclude_pattern(const char **pattern,\n }\n \n void add_exclude(const char *string, const char *base,\n-\t\t int baselen, struct exclude_list *el)\n+\t\t int baselen, struct exclude_list *el,\n+\t\t const char *src, int srcpos)\n {\n \tstruct exclude *x;\n \tint patternlen;\n@@ -371,6 +372,8 @@ void add_exclude(const char *string, const char *base,\n \tx->base = base;\n \tx->baselen = baselen;\n \tx->flags = flags;\n+\tx->src = src;\n+\tx->srcpos = srcpos;\n \tALLOC_GROW(el->excludes, el->nr + 1, el->alloc);\n \tel->excludes[el->nr++] = x;\n }\n@@ -418,7 +421,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\t\t\t   int check_index)\n {\n \tstruct stat st;\n-\tint fd, i;\n+\tint fd, i, lineno = 1;\n \tsize_t size = 0;\n \tchar *buf, *entry;\n \n@@ -461,8 +464,10 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\tif (buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n-\t\t\t\tadd_exclude(entry, base, baselen, el);\n+\t\t\t\tadd_exclude(entry, base, baselen, el,\n+\t\t\t\t\t    fname, lineno);\n \t\t\t}\n+\t\t\tlineno++;\n \t\t\tentry = buf + i + 1;\n \t\t}\n \t}\n@@ -493,8 +498,10 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t    !strncmp(dir->basebuf, base, stk->baselen))\n \t\t\tbreak;\n \t\tdir->exclude_stack = stk->prev;\n-\t\twhile (stk->exclude_ix < el->nr)\n-\t\t\tfree(el->excludes[--el->nr]);\n+\t\twhile (stk->exclude_ix < el->nr) {\n+\t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n+\t\t\tfree(exclude);\n+\t\t}\n \t\tfree(stk->filebuf);\n \t\tfree(stk);\n \t}\n@@ -521,7 +528,14 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\tmemcpy(dir->basebuf + current, base + current,\n \t\t       stk->baselen - current);\n \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n-\t\tadd_excludes_from_file_to_list(dir->basebuf,\n+\n+\t\t/*\n+\t\t * dir->basebuf gets reused by the traversal, but we\n+\t\t * need fname to remain unchanged to ensure the src\n+\t\t * member of each struct exclude correctly back-references\n+\t\t * its source file.\n+\t\t */\n+\t\tadd_excludes_from_file_to_list(strdup(dir->basebuf),\n \t\t\t\t\t       dir->basebuf, stk->baselen,\n \t\t\t\t\t       &stk->filebuf, el, 1);\n \t\tdir->exclude_stack = stk;\ndiff --git a/dir.h b/dir.h\nindex 02ac0bf..3921aa9 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -23,6 +23,9 @@ struct exclude_list {\n \t\tconst char *base;\n \t\tint baselen;\n \t\tint flags;\n+\t\tconst char *src;\n+\t\tint srcpos; /* counting starts from 1 for line numbers in ignore files,\n+\t\t\t       and from -1 decrementing for patterns from CLI (--exclude) */\n \t} **excludes;\n };\n \n@@ -112,7 +115,7 @@ extern int add_excludes_from_file_to_list(const char *fname, const char *base, i\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);\n extern void add_exclude(const char *string, const char *base,\n-\t\t\tint baselen, struct exclude_list *el);\n+\t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n extern void free_excludes(struct exclude_list *el);\n extern int file_exists(const char *);\n \n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201229","messageId":"1350282486-4646-9-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/12] dir.c: refactor treat_gitlinks()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:03Z","receivedAt":"2012-10-15T06:28:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nExtract the body of the for loop in treat_gitlinks() into a separate\ntreat_gitlink() function so that it can be reused elsewhere.  This\npaves the way for a new check-ignore sub-command.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/add.c | 49 +++++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex c689f37..6d2fb0c 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -153,31 +153,44 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int\n \treturn seen;\n }\n \n-static void treat_gitlinks(const char **pathspec)\n+/*\n+ * Check whether path refers to a submodule, or something inside a\n+ * submodule.  If the former, returns the path with any trailing slash\n+ * stripped.  If the latter, dies with an error message.\n+ */\n+const char *treat_gitlink(const char *path)\n {\n-\tint i;\n-\n-\tif (!pathspec || !*pathspec)\n-\t\treturn;\n-\n+\tint i, path_len = strlen(path);\n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tif (S_ISGITLINK(ce->ce_mode)) {\n-\t\t\tint len = ce_namelen(ce), j;\n-\t\t\tfor (j = 0; pathspec[j]; j++) {\n-\t\t\t\tint len2 = strlen(pathspec[j]);\n-\t\t\t\tif (len2 <= len || pathspec[j][len] != '/' ||\n-\t\t\t\t    memcmp(ce->name, pathspec[j], len))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tif (len2 == len + 1)\n-\t\t\t\t\t/* strip trailing slash */\n-\t\t\t\t\tpathspec[j] = xstrndup(ce->name, len);\n-\t\t\t\telse\n-\t\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n-\t\t\t\t\t\tpathspec[j], len, ce->name);\n+\t\t\tint ce_len = ce_namelen(ce);\n+\t\t\tif (path_len <= ce_len || path[ce_len] != '/' ||\n+\t\t\t    memcmp(ce->name, path, ce_len))\n+\t\t\t\t/* path does not refer to this\n+\t\t\t\t * submodule or anything inside it */\n+\t\t\t\tcontinue;\n+\t\t\tif (path_len == ce_len + 1) {\n+\t\t\t\t/* path refers to submodule;\n+\t\t\t\t * strip trailing slash */\n+\t\t\t\treturn xstrndup(ce->name, ce_len);\n+\t\t\t} else {\n+\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n+\t\t\t\t     path, ce_len, ce->name);\n \t\t\t}\n \t\t}\n \t}\n+\treturn path;\n+}\n+\n+void treat_gitlinks(const char **pathspec)\n+{\n+\tif (!pathspec || !*pathspec)\n+\t\treturn;\n+\n+\tint i;\n+\tfor (i = 0; pathspec[i]; i++)\n+\t\tpathspec[i] = treat_gitlink(pathspec[i]);\n }\n \n static void refresh(int verbose, const char **pathspec)\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201230","messageId":"1350282486-4646-10-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/12] pathspec.c: move reusable code from builtin/add.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:04Z","receivedAt":"2012-10-15T06:28:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nThis is in preparation for reuse by a new git check-ignore command.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile      |  2 ++\n builtin/add.c | 95 ++-------------------------------------------------------\n pathspec.c    | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n pathspec.h    |  6 ++++\n 4 files changed, 109 insertions(+), 92 deletions(-)\n create mode 100644 pathspec.c\n create mode 100644 pathspec.h\n\ndiff --git a/Makefile b/Makefile\nindex 13293d3..48facad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -645,6 +645,7 @@ LIB_H += pack-refs.h\n LIB_H += pack-revindex.h\n LIB_H += parse-options.h\n LIB_H += patch-ids.h\n+LIB_H += pathspec.h\n LIB_H += pkt-line.h\n LIB_H += progress.h\n LIB_H += prompt.h\n@@ -758,6 +759,7 @@ LIB_OBJS += parse-options-cb.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n+LIB_OBJS += pathspec.o\n LIB_OBJS += pkt-line.o\n LIB_OBJS += preload-index.o\n LIB_OBJS += pretty.o\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 6d2fb0c..927b0f2 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -6,6 +6,7 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n #include \"dir.h\"\n+#include \"pathspec.h\"\n #include \"exec_cmd.h\"\n #include \"cache-tree.h\"\n #include \"run-command.h\"\n@@ -97,39 +98,6 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)\n \treturn !!data.add_errors;\n }\n \n-static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)\n-{\n-\tint num_unmatched = 0, i;\n-\n-\t/*\n-\t * Since we are walking the index as if we were walking the directory,\n-\t * we have to mark the matched pathspec as seen; otherwise we will\n-\t * mistakenly think that the user gave a pathspec that did not match\n-\t * anything.\n-\t */\n-\tfor (i = 0; i < specs; i++)\n-\t\tif (!seen[i])\n-\t\t\tnum_unmatched++;\n-\tif (!num_unmatched)\n-\t\treturn;\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tmatch_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);\n-\t}\n-}\n-\n-static char *find_used_pathspec(const char **pathspec)\n-{\n-\tchar *seen;\n-\tint i;\n-\n-\tfor (i = 0; pathspec[i];  i++)\n-\t\t; /* just counting */\n-\tseen = xcalloc(i, 1);\n-\tfill_pathspec_matches(pathspec, seen, i);\n-\treturn seen;\n-}\n-\n static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)\n {\n \tchar *seen;\n@@ -153,46 +121,6 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int\n \treturn seen;\n }\n \n-/*\n- * Check whether path refers to a submodule, or something inside a\n- * submodule.  If the former, returns the path with any trailing slash\n- * stripped.  If the latter, dies with an error message.\n- */\n-const char *treat_gitlink(const char *path)\n-{\n-\tint i, path_len = strlen(path);\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tif (S_ISGITLINK(ce->ce_mode)) {\n-\t\t\tint ce_len = ce_namelen(ce);\n-\t\t\tif (path_len <= ce_len || path[ce_len] != '/' ||\n-\t\t\t    memcmp(ce->name, path, ce_len))\n-\t\t\t\t/* path does not refer to this\n-\t\t\t\t * submodule or anything inside it */\n-\t\t\t\tcontinue;\n-\t\t\tif (path_len == ce_len + 1) {\n-\t\t\t\t/* path refers to submodule;\n-\t\t\t\t * strip trailing slash */\n-\t\t\t\treturn xstrndup(ce->name, ce_len);\n-\t\t\t} else {\n-\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n-\t\t\t\t     path, ce_len, ce->name);\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn path;\n-}\n-\n-void treat_gitlinks(const char **pathspec)\n-{\n-\tif (!pathspec || !*pathspec)\n-\t\treturn;\n-\n-\tint i;\n-\tfor (i = 0; pathspec[i]; i++)\n-\t\tpathspec[i] = treat_gitlink(pathspec[i]);\n-}\n-\n static void refresh(int verbose, const char **pathspec)\n {\n \tchar *seen;\n@@ -210,23 +138,6 @@ static void refresh(int verbose, const char **pathspec)\n         free(seen);\n }\n \n-static const char **validate_pathspec(int argc, const char **argv, const char *prefix)\n-{\n-\tconst char **pathspec = get_pathspec(prefix, argv);\n-\n-\tif (pathspec) {\n-\t\tconst char **p;\n-\t\tfor (p = pathspec; *p; p++) {\n-\t\t\tif (has_symlink_leading_path(*p, strlen(*p))) {\n-\t\t\t\tint len = prefix ? strlen(prefix) : 0;\n-\t\t\t\tdie(_(\"'%s' is beyond a symbolic link\"), *p + len);\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn pathspec;\n-}\n-\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst char **pathspec)\n {\n@@ -261,7 +172,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)\n \tconst char **pathspec = NULL;\n \n \tif (argc) {\n-\t\tpathspec = validate_pathspec(argc, argv, prefix);\n+\t\tpathspec = validate_pathspec(prefix, argv);\n \t\tif (!pathspec)\n \t\t\treturn -1;\n \t}\n@@ -427,7 +338,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\tfprintf(stderr, _(\"Maybe you wanted to say 'git add .'?\\n\"));\n \t\treturn 0;\n \t}\n-\tpathspec = validate_pathspec(argc, argv, prefix);\n+\tpathspec = validate_pathspec(prefix, argv);\n \n \tif (read_cache() < 0)\n \t\tdie(_(\"index file corrupt\"));\ndiff --git a/pathspec.c b/pathspec.c\nnew file mode 100644\nindex 0000000..a9c5b5b\n--- /dev/null\n+++ b/pathspec.c\n@@ -0,0 +1,98 @@\n+#include \"cache.h\"\n+#include \"dir.h\"\n+\n+void validate_path(const char *prefix, const char *path)\n+{\n+\tif (has_symlink_leading_path(path, strlen(path))) {\n+\t\tint len = prefix ? strlen(prefix) : 0;\n+\t\tdie(_(\"'%s' is beyond a symbolic link\"), path + len);\n+\t}\n+}\n+\n+const char **validate_pathspec(const char *prefix, const char **files)\n+{\n+\tconst char **pathspec = get_pathspec(prefix, files);\n+\n+\tif (pathspec) {\n+\t\tconst char **p;\n+\t\tfor (p = pathspec; *p; p++) {\n+\t\t\tvalidate_path(prefix, *p);\n+\t\t}\n+\t}\n+\n+\treturn pathspec;\n+}\n+\n+void fill_pathspec_matches(const char **pathspec, char *seen, int specs)\n+{\n+\tint num_unmatched = 0, i;\n+\n+\t/*\n+\t * Since we are walking the index as if we were walking the directory,\n+\t * we have to mark the matched pathspec as seen; otherwise we will\n+\t * mistakenly think that the user gave a pathspec that did not match\n+\t * anything.\n+\t */\n+\tfor (i = 0; i < specs; i++)\n+\t\tif (!seen[i])\n+\t\t\tnum_unmatched++;\n+\tif (!num_unmatched)\n+\t\treturn;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tmatch_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);\n+\t}\n+}\n+\n+char *find_used_pathspec(const char **pathspec)\n+{\n+\tchar *seen;\n+\tint i;\n+\n+\tfor (i = 0; pathspec[i];  i++)\n+\t\t; /* just counting */\n+\tseen = xcalloc(i, 1);\n+\tfill_pathspec_matches(pathspec, seen, i);\n+\treturn seen;\n+}\n+\n+/*\n+ * Check whether path refers to a submodule, or something inside a\n+ * submodule.  If the former, returns the path with any trailing slash\n+ * stripped.  If the latter, dies with an error message.\n+ */\n+const char *treat_gitlink(const char *path)\n+{\n+\tint i, path_len = strlen(path);\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tif (S_ISGITLINK(ce->ce_mode)) {\n+\t\t\tint ce_len = ce_namelen(ce);\n+\t\t\tif (path_len <= ce_len || path[ce_len] != '/' ||\n+\t\t\t    memcmp(ce->name, path, ce_len))\n+\t\t\t\t/* path does not refer to this\n+\t\t\t\t * submodule or anything inside it */\n+\t\t\t\tcontinue;\n+\t\t\tif (path_len == ce_len + 1) {\n+\t\t\t\t/* path refers to submodule;\n+\t\t\t\t * strip trailing slash */\n+\t\t\t\treturn xstrndup(ce->name, ce_len);\n+\t\t\t} else {\n+\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n+\t\t\t\t     path, ce_len, ce->name);\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn path;\n+}\n+\n+void treat_gitlinks(const char **pathspec)\n+{\n+\tint i;\n+\n+\tif (!pathspec || !*pathspec)\n+\t\treturn;\n+\n+\tfor (i = 0; pathspec[i]; i++)\n+\t\tpathspec[i] = treat_gitlink(pathspec[i]);\n+}\ndiff --git a/pathspec.h b/pathspec.h\nnew file mode 100644\nindex 0000000..b7c053a\n--- /dev/null\n+++ b/pathspec.h\n@@ -0,0 +1,6 @@\n+extern void validate_path(const char *prefix, const char *path);\n+extern const char **validate_pathspec(const char *prefix, const char **files);\n+extern char *find_used_pathspec(const char **pathspec);\n+extern void fill_pathspec_matches(const char **pathspec, char *seen, int specs);\n+extern const char *treat_gitlink(const char *path);\n+extern void treat_gitlinks(const char **pathspec);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201231","messageId":"1350282486-4646-11-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/12] dir.c: provide free_directory() for reclaiming dir_struct memory","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:05Z","receivedAt":"2012-10-15T06:28:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/technical/api-directory-listing.txt |  2 ++\n dir.c                                             | 28 +++++++++++++++++++++--\n dir.h                                             |  1 +\n 3 files changed, 29 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex add6f43..ca571a8 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -76,4 +76,6 @@ marked. If you to exclude files, make sure you have loaded index first.\n \n * Use `dir.entries[]`.\n \n+* Call `free_directory()` when none of the contained elements are no longer in use.\n+\n (JC)\ndiff --git a/dir.c b/dir.c\nindex 81a5dd5..d50634d 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -481,6 +481,16 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n+static void free_exclude_stack(struct exclude_stack *stk)\n+{\n+\tfree(stk->filebuf);\n+\tfree(stk);\n+}\n+\n+/*\n+ * Loads the per-directory exclude list for the substring of base\n+ * which has a char length of baselen.\n+ */\n static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n {\n \tstruct exclude_list *el;\n@@ -502,8 +512,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n \t\t\tfree(exclude);\n \t\t}\n-\t\tfree(stk->filebuf);\n-\t\tfree(stk);\n+\t\tfree_exclude_stack(stk);\n \t}\n \n \t/* Read from the parent directories and push them down. */\n@@ -1521,3 +1530,18 @@ void free_pathspec(struct pathspec *pathspec)\n \tfree(pathspec->items);\n \tpathspec->items = NULL;\n }\n+\n+void free_directory(struct dir_struct *dir)\n+{\n+\tstruct exclude_stack *stk;\n+\tint st;\n+\tfor (st = EXC_CMDL; st <= EXC_FILE; st++)\n+\t\tfree_excludes(&dir->exclude_list[st]);\n+\n+\tstk = dir->exclude_stack;\n+\twhile (stk) {\n+\t\tstruct exclude_stack *prev = stk->prev;\n+\t\tfree_exclude_stack(stk);\n+\t\tstk = prev;\n+\t}\n+}\ndiff --git a/dir.h b/dir.h\nindex 3921aa9..fb57c66 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -117,6 +117,7 @@ extern void parse_exclude_pattern(const char **string, int *patternlen, int *fla\n extern void add_exclude(const char *string, const char *base,\n \t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n extern void free_excludes(struct exclude_list *el);\n+extern void free_directory(struct dir_struct *dir);\n extern int file_exists(const char *);\n \n extern int is_inside_dir(const char *dir);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201232","messageId":"1350282486-4646-12-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1350282486-4646-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/12] Add git-check-ignore sub-command","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-15T06:28:06Z","receivedAt":"2012-10-15T06:28:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Adam Spiers <git@adamspiers.org>\n\nThis works in a similar manner to git-check-attr.  Some code\nwas reused from add.c by refactoring out into pathspec.c.\n\nThanks to Jeff King and Junio C Hamano for the idea:\nhttp://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                             |   1 +\n Documentation/git-check-ignore.txt     |  85 +++++\n Documentation/gitignore.txt            |   6 +-\n Makefile                               |   1 +\n builtin.h                              |   1 +\n builtin/check-ignore.c                 | 170 ++++++++++\n command-list.txt                       |   1 +\n contrib/completion/git-completion.bash |   1 +\n git.c                                  |   1 +\n t/t0007-ignores.sh                     | 587 +++++++++++++++++++++++++++++++++\n 10 files changed, 852 insertions(+), 2 deletions(-)\n create mode 100644 Documentation/git-check-ignore.txt\n create mode 100644 builtin/check-ignore.c\n create mode 100755 t/t0007-ignores.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex f1acd3e..20ef4e8 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -19,6 +19,7 @@\n /git-bundle\n /git-cat-file\n /git-check-attr\n+/git-check-ignore\n /git-check-ref-format\n /git-checkout\n /git-checkout-index\ndiff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt\nnew file mode 100644\nindex 0000000..96fa7bc\n--- /dev/null\n+++ b/Documentation/git-check-ignore.txt\n@@ -0,0 +1,85 @@\n+git-check-ignore(1)\n+===================\n+\n+NAME\n+----\n+git-check-ignore - Debug gitignore / exclude files\n+\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git check-ignore' [options] pathname...\n+'git check-ignore' [options] --stdin < <list-of-paths>\n+\n+DESCRIPTION\n+-----------\n+\n+For each pathname given via the command-line or from a file via\n+`--stdin`, this command will list the first exclude pattern found (if\n+any) which explicitly excludes or includes that pathname.  Note that\n+within any given exclude file, later patterns take precedence over\n+earlier ones, so any matching pattern which this command outputs may\n+not be the one you would immediately expect.\n+\n+OPTIONS\n+-------\n+-q, --quiet::\n+\tDon't output anything, just set exit status.  This is only\n+\tvalid with a single pathname.\n+\n+-v, --verbose::\n+\tAlso output details about the matching pattern (if any)\n+\tfor each given pathname.\n+\n+--stdin::\n+\tRead file names from stdin instead of from the command-line.\n+\n+-z::\n+\tThe output format is modified to be machine-parseable (see\n+\tbelow).  If `--stdin` is also given, input paths are separated\n+\twith a NUL character instead of a linefeed character.\n+\n+OUTPUT\n+------\n+\n+By default, any of the given pathnames which match an ignore pattern\n+will be output, one per line.  If no pattern matches a given path,\n+nothing will be output for that path; this means that path will not be\n+ignored.\n+\n+If `--verbose` is specified, the output is a series of lines of the form:\n+\n+<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>\n+\n+<pathname> is the path of a file being queried, <pattern> is the\n+matching pattern, <source> is the pattern's source file, and <linenum>\n+is the line number of the pattern within that source.  If the pattern\n+contained a `!` prefix or `/` suffix, it will be preserved in the\n+output.  <source> will be an absolute path when referring to the file\n+configured by `core.excludesfile`, or relative to the repository root\n+when referring to `.git/info/exclude` or a per-directory exclude file.\n+\n+If `-z` is specified, the output is a series of lines of the form:\n+\n+EXIT STATUS\n+-----------\n+\n+0::\n+\tOne or more of the provided paths is ignored.\n+\n+1::\n+\tNone of the provided paths are ignored.\n+\n+128::\n+\tA fatal error was encountered.\n+\n+SEE ALSO\n+--------\n+linkgit:gitignore[5]\n+linkgit:gitconfig[5]\n+linkgit:git-ls-files[5]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 2e7328b..f401b8c 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -153,8 +153,10 @@ The second .gitignore prevents git from ignoring\n \n SEE ALSO\n --------\n-linkgit:git-rm[1], linkgit:git-update-index[1],\n-linkgit:gitrepository-layout[5]\n+linkgit:git-rm[1],\n+linkgit:git-update-index[1],\n+linkgit:gitrepository-layout[5],\n+linkgit:git-check-ignore[1]\n \n GIT\n ---\ndiff --git a/Makefile b/Makefile\nindex 48facad..8476fc8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -822,6 +822,7 @@ BUILTIN_OBJS += builtin/branch.o\n BUILTIN_OBJS += builtin/bundle.o\n BUILTIN_OBJS += builtin/cat-file.o\n BUILTIN_OBJS += builtin/check-attr.o\n+BUILTIN_OBJS += builtin/check-ignore.o\n BUILTIN_OBJS += builtin/check-ref-format.o\n BUILTIN_OBJS += builtin/checkout-index.o\n BUILTIN_OBJS += builtin/checkout.o\ndiff --git a/builtin.h b/builtin.h\nindex dffb34e..d57faf4 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -58,6 +58,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_attr(int argc, const char **argv, const char *prefix);\n+extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/check-ignore.c b/builtin/check-ignore.c\nnew file mode 100644\nindex 0000000..d446ade\n--- /dev/null\n+++ b/builtin/check-ignore.c\n@@ -0,0 +1,170 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"dir.h\"\n+#include \"quote.h\"\n+#include \"pathspec.h\"\n+#include \"parse-options.h\"\n+\n+static int quiet, verbose, stdin_paths;\n+static const char * const check_ignore_usage[] = {\n+\"git check-ignore [options] pathname...\",\n+\"git check-ignore [options] --stdin < <list-of-paths>\",\n+NULL\n+};\n+\n+static int null_term_line;\n+\n+static const struct option check_ignore_options[] = {\n+\tOPT__QUIET(&quiet, N_(\"suppress progress reporting\")),\n+\tOPT__VERBOSE(&verbose, N_(\"be verbose\")),\n+\tOPT_GROUP(\"\"),\n+\tOPT_BOOLEAN(0, \"stdin\", &stdin_paths,\n+\t\t    N_(\"read file names from stdin\")),\n+\tOPT_BOOLEAN('z', NULL, &null_term_line,\n+\t\t    N_(\"input paths are terminated by a null character\")),\n+\tOPT_END()\n+};\n+\n+static void output_exclude(const char *path, struct exclude *exclude)\n+{\n+\tchar *bang = exclude->flags & EXC_FLAG_NEGATIVE ? \"!\" : \"\";\n+\tchar *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n+\tif (!null_term_line) {\n+\t\tif (!verbose) {\n+\t\t\twrite_name_quoted(path, stdout, '\\n');\n+\t\t} else {\n+\t\t\tquote_c_style(exclude->src, NULL, stdout, 0);\n+\t\t\tprintf(\":%d:%s%s%s\\t\",\n+\t\t\t       exclude->srcpos,\n+\t\t\t       bang, exclude->pattern, dir);\n+\t\t\tquote_c_style(path, NULL, stdout, 0);\n+\t\t\tfputc('\\n', stdout);\n+\t\t}\n+\t} else {\n+\t\tif (!verbose) {\n+\t\t\tprintf(\"%s%c\", path, '\\0');\n+\t\t} else {\n+\t\t\tprintf(\"%s%c%d%c%s%s%s%c%s%c\",\n+\t\t\t       exclude->src, '\\0',\n+\t\t\t       exclude->srcpos, '\\0',\n+\t\t\t       bang, exclude->pattern, dir, '\\0',\n+\t\t\t       path, '\\0');\n+\t\t}\n+\t}\n+}\n+\n+static int check_ignore(const char *prefix, const char **pathspec)\n+{\n+\tstruct dir_struct dir;\n+\tconst char *path;\n+\tchar *seen = NULL;\n+\tint num_ignored = 0;\n+\n+\t/* read_cache() is only necessary so we can watch out for submodules. */\n+\tif (read_cache() < 0)\n+\t\tdie(_(\"index file corrupt\"));\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tdir.flags |= DIR_COLLECT_IGNORED;\n+\tsetup_standard_excludes(&dir);\n+\n+\tif (pathspec) {\n+\t\tint i;\n+\t\tstruct path_exclude_check check;\n+\t\tstruct exclude *exclude;\n+\n+\t\tpath_exclude_check_init(&check, &dir);\n+\t\tif (!seen)\n+\t\t\tseen = find_used_pathspec(pathspec);\n+\t\tfor (i = 0; pathspec[i]; i++) {\n+\t\t\tconst char *full_path;\n+\t\t\tpath = pathspec[i];\n+\t\t\tfull_path = prefix_path(prefix, prefix\n+\t\t\t\t\t\t? strlen(prefix) : 0, path);\n+\t\t\tfull_path = treat_gitlink(full_path);\n+\t\t\tvalidate_path(prefix, full_path);\n+\t\t\tif (!seen[i] && path[0]) {\n+\t\t\t\tint dtype = DT_UNKNOWN;\n+\t\t\t\texclude = last_exclude_matching_path(&check, full_path,\n+\t\t\t\t\t\t\t\t     -1, &dtype);\n+\t\t\t\tif (exclude) {\n+\t\t\t\t\tif (!quiet)\n+\t\t\t\t\t\toutput_exclude(path, exclude);\n+\t\t\t\t\tnum_ignored++;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tfree(seen);\n+\t\tfree_directory(&dir);\n+\t\tpath_exclude_check_clear(&check);\n+\t} else {\n+\t\tprintf(\"no pathspec\\n\");\n+\t}\n+\treturn num_ignored;\n+}\n+\n+static int check_ignore_stdin_paths(const char *prefix)\n+{\n+\tstruct strbuf buf, nbuf;\n+\tchar **pathspec = NULL;\n+\tsize_t nr = 0, alloc = 0;\n+\tint line_termination = null_term_line ? 0 : '\\n';\n+\tint num_ignored;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_init(&nbuf, 0);\n+\twhile (strbuf_getline(&buf, stdin, line_termination) != EOF) {\n+\t\tif (line_termination && buf.buf[0] == '\"') {\n+\t\t\tstrbuf_reset(&nbuf);\n+\t\t\tif (unquote_c_style(&nbuf, buf.buf, NULL))\n+\t\t\t\tdie(\"line is badly quoted\");\n+\t\t\tstrbuf_swap(&buf, &nbuf);\n+\t\t}\n+\t\tALLOC_GROW(pathspec, nr + 1, alloc);\n+\t\tpathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));\n+\t\tstrcpy(pathspec[nr++], buf.buf);\n+\t}\n+\tALLOC_GROW(pathspec, nr + 1, alloc);\n+\tpathspec[nr] = NULL;\n+\tnum_ignored = check_ignore(prefix, (const char **)pathspec);\n+\tmaybe_flush_or_die(stdout, \"attribute to stdout\");\n+\tstrbuf_release(&buf);\n+\tstrbuf_release(&nbuf);\n+\tfree(pathspec);\n+\treturn num_ignored;\n+}\n+\n+int cmd_check_ignore(int argc, const char **argv, const char *prefix)\n+{\n+\tint num_ignored = 0;\n+\n+\tgit_config(git_default_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, check_ignore_options,\n+\t\t\t     check_ignore_usage, 0);\n+\n+\tif (stdin_paths) {\n+\t\tif (0 < argc)\n+\t\t\tdie(_(\"cannot specify pathnames with --stdin\"));\n+\t} else {\n+\t\tif (null_term_line)\n+\t\t\tdie(_(\"-z only makes sense with --stdin\"));\n+\t\tif (argc == 0)\n+\t\t\tdie(_(\"no path specified\"));\n+\t}\n+\tif (quiet) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"--quiet is only valid with a single pathname\"));\n+\t\tif (verbose)\n+\t\t\tdie(_(\"cannot have both --quiet and --verbose\"));\n+\t}\n+\n+\tif (stdin_paths) {\n+\t\tnum_ignored = check_ignore_stdin_paths(prefix);\n+\t} else {\n+\t\tnum_ignored = check_ignore(prefix, argv);\n+\t\tmaybe_flush_or_die(stdout, \"ignore to stdout\");\n+\t}\n+\n+\treturn num_ignored > 0 ? 0 : 1;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 14ea67a..ef7f39c 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -12,6 +12,7 @@ git-branch                              mainporcelain common\n git-bundle                              mainporcelain\n git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n+git-check-ignore                        purehelpers\n git-checkout                            mainporcelain common\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 2e1b5e1..1fb896b 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -842,6 +842,7 @@ __git_list_porcelain_commands ()\n \t\tarchimport)       : import;;\n \t\tcat-file)         : plumbing;;\n \t\tcheck-attr)       : plumbing;;\n+\t\tcheck-ignore)     : plumbing;;\n \t\tcheck-ref-format) : plumbing;;\n \t\tcheckout-index)   : plumbing;;\n \t\tcommit-tree)      : plumbing;;\ndiff --git a/git.c b/git.c\nindex d232de9..0b31e66 100644\n--- a/git.c\n+++ b/git.c\n@@ -340,6 +340,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"bundle\", cmd_bundle, RUN_SETUP_GENTLY },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP },\n+\t\t{ \"check-ignore\", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n \t\t{ \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"checkout-index\", cmd_checkout_index,\ndiff --git a/t/t0007-ignores.sh b/t/t0007-ignores.sh\nnew file mode 100755\nindex 0000000..7fd7e53\n--- /dev/null\n+++ b/t/t0007-ignores.sh\n@@ -0,0 +1,587 @@\n+#!/bin/sh\n+\n+test_description=check-ignore\n+\n+. ./test-lib.sh\n+\n+init_vars () {\n+\tglobal_excludes=\"$HOME/global-excludes\"\n+}\n+\n+enable_global_excludes () {\n+\tinit_vars\n+\tgit config core.excludesfile \"$global_excludes\"\n+}\n+\n+expect_in () {\n+\tdest=\"$HOME/expected-$1\" text=\"$2\"\n+\tif test -z \"$text\"\n+\tthen\n+\t\t>\"$dest\" # avoid newline\n+\telse\n+\t\techo \"$text\" >\"$dest\"\n+\tfi\n+}\n+\n+expect () {\n+\texpect_in stdout \"$1\"\n+}\n+\n+expect_from_stdin () {\n+\tcat >\"$HOME/expected-stdout\"\n+}\n+\n+test_stderr () {\n+\texpected=\"$1\"\n+\texpect_in stderr \"$1\" &&\n+\ttest_cmp \"$HOME/expected-stderr\" \"$HOME/stderr\"\n+}\n+\n+stderr_contains () {\n+\tregexp=\"$1\"\n+\tif grep -q \"$regexp\" \"$HOME/stderr\"\n+\tthen\n+\t\treturn 0\n+\telse\n+\t\techo \"didn't find /$regexp/ in $HOME/stderr\"\n+\t\tcat \"$HOME/stderr\"\n+\t\treturn 1\n+\tfi\n+}\n+\n+stderr_empty_on_success () {\n+\texpect_code=\"$1\"\n+\tif test $expect_code = 0\n+\tthen\n+\t\ttest_stderr \"\"\n+\telse\n+\t\t# If we expect failure then stderr might or might not be empty\n+\t\t# due to --quiet - the caller can check its contents\n+\t\treturn 0\n+\tfi\n+}\n+\n+test_check_ignore () {\n+\targs=\"$1\" expect_code=\"${2:-0}\" global_args=\"$3\"\n+\n+\tinit_vars &&\n+\trm -f \"$HOME/stdout\" \"$HOME/stderr\" \"$HOME/cmd\" &&\n+\techo $(which git) $global_args check-ignore $quiet_opt $verbose_opt $args \\\n+\t\t>\"$HOME/cmd\" &&\n+\tpwd >\"$HOME/pwd\" &&\n+\ttest_expect_code \"$expect_code\" \\\n+\t\tgit $global_args check-ignore $quiet_opt $verbose_opt $args \\\n+\t\t>\"$HOME/stdout\" 2>\"$HOME/stderr\" &&\n+\ttest_cmp \"$HOME/expected-stdout\" \"$HOME/stdout\" &&\n+\tstderr_empty_on_success \"$expect_code\"\n+}\n+\n+test_expect_success_multi () {\n+\ttestname=\"$1\" expect_verbose=\"$2\" code=\"$3\"\n+\n+\texpect=$( echo \"$expect_verbose\" | sed -e 's/.*\t//' )\n+\n+\ttest_expect_success \"$testname\" \"\n+\t\texpect '$expect' &&\n+\t\t$code\n+\t\"\n+\n+\tfor quiet_opt in '-q' '--quiet'\n+\tdo\n+\t\ttest_expect_success \"$testname${quiet_opt:+ with $quiet_opt}\" \"\n+\t\t\texpect '' &&\n+\t\t\t$code\n+\t\t\"\n+\tdone\n+\tquiet_opt=\n+\n+\tfor verbose_opt in '-v' '--verbose'\n+\tdo\n+\t\ttest_expect_success \"$testname${verbose_opt:+ with $verbose_opt}\" \"\n+\t\t\texpect '$expect_verbose' &&\n+\t\t\t$code\n+\t\t\"\n+\tdone\n+\tverbose_opt=\n+}\n+\n+test_expect_success 'setup' '\n+\tinit_vars\n+\tmkdir -p a/b/ignored-dir a/submodule b &&\n+\tln -s b a/symlink &&\n+\t(\n+\t\tcd a/submodule &&\n+\t\tgit init &&\n+\t\techo a > a &&\n+\t\tgit add a &&\n+\t\tgit commit -m\"commit in submodule\"\n+\t) &&\n+\tgit add a/submodule &&\n+\tcat <<-\\EOF >.gitignore &&\n+\t\tone\n+\tEOF\n+\tcat <<-\\EOF >a/.gitignore &&\n+\t\ttwo*\n+\t\t*three\n+\tEOF\n+\tcat <<-\\EOF >a/b/.gitignore &&\n+\t\tfour\n+\t\tfive\n+\t\t# this comment should affect the line numbers\n+\t\tsix\n+\t\tignored-dir/\n+\t\t# and so should this blank line:\n+\n+\t\t!on*\n+\t\t!two\n+\tEOF\n+\techo \"seven\" >a/b/ignored-dir/.gitignore &&\n+\ttest -n \"$HOME\" &&\n+\tcat <<-\\EOF >\"$global_excludes\" &&\n+\t\tglobalone\n+\t\t!globaltwo\n+\t\tglobalthree\n+\tEOF\n+\tcat <<-\\EOF >>.git/info/exclude\n+\t\tper-repo\n+\tEOF\n+'\n+\n+############################################################################\n+#\n+# test invalid inputs\n+\n+test_expect_success_multi 'empty command line' '' '\n+\ttest_check_ignore \"\" 128 &&\n+\tstderr_contains \"fatal: no path specified\"\n+'\n+\n+test_expect_success '-q with multiple args' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"-q one two\" 128 &&\n+\tstderr_contains \"fatal: --quiet is only valid with a single pathname\"\n+'\n+\n+test_expect_success '--quiet with multiple args' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"--quiet one two\" 128 &&\n+\tstderr_contains \"fatal: --quiet is only valid with a single pathname\"\n+'\n+\n+for verbose_opt in '-v' '--verbose'\n+do\n+\tfor quiet_opt in '-q' '--quiet'\n+\tdo\n+\t\ttest_expect_success \"$quiet_opt $verbose_opt\" \"\n+\t\t\texpect '' &&\n+\t\t\ttest_check_ignore '$quiet_opt $verbose_opt foo' 128 &&\n+\t\t\tstderr_contains 'fatal: cannot have both --quiet and --verbose'\n+\t\t\"\n+\tdone\n+done\n+\n+test_expect_success '--quiet with multiple args' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"--quiet one two\" 128 &&\n+\tstderr_contains \"fatal: --quiet is only valid with a single pathname\"\n+'\n+\n+test_expect_success_multi 'erroneous use of --' '' '\n+\ttest_check_ignore \"--\" 128 &&\n+\tstderr_contains \"fatal: no path specified\"\n+'\n+\n+test_expect_success_multi '--stdin with superfluous arg' '' '\n+\ttest_check_ignore \"--stdin foo\" 128 &&\n+\tstderr_contains \"fatal: cannot specify pathnames with --stdin\"\n+'\n+\n+test_expect_success_multi '--stdin -z with superfluous arg' '' '\n+\ttest_check_ignore \"--stdin -z foo\" 128 &&\n+\tstderr_contains \"fatal: cannot specify pathnames with --stdin\"\n+'\n+\n+test_expect_success_multi '-z without --stdin' '' '\n+\ttest_check_ignore \"-z\" 128 &&\n+\tstderr_contains \"fatal: -z only makes sense with --stdin\"\n+'\n+\n+test_expect_success_multi '-z without --stdin and superfluous arg' '' '\n+\ttest_check_ignore \"-z foo\" 128 &&\n+\tstderr_contains \"fatal: -z only makes sense with --stdin\"\n+'\n+\n+test_expect_success_multi 'needs work tree' '' '\n+\t(\n+\t\tcd .git &&\n+\t\ttest_check_ignore \"foo\" 128\n+\t) &&\n+\tstderr_contains \"fatal: This operation must be run in a work tree\"\n+'\n+\n+############################################################################\n+#\n+# test standard ignores\n+\n+test_expect_success_multi \"top-level not ignored\" '' '\n+\ttest_check_ignore \"foo\" 1\n+'\n+\n+test_expect_success_multi \"top-level ignored\" \\\n+\t'.gitignore:1:one\tone' '\n+\ttest_check_ignore \"one\"\n+'\n+\n+test_expect_success_multi 'sub-directory ignore from top' \\\n+\t'.gitignore:1:one\ta/one' '\n+\ttest_check_ignore \"a/one\"\n+'\n+\n+test_expect_success 'sub-directory local ignore' '\n+\texpect \"a/3-three\" &&\n+\ttest_check_ignore \"a/3-three a/three-not-this-one\"\n+'\n+\n+test_expect_success 'sub-directory local ignore with --verbose'  '\n+\texpect \"a/.gitignore:2:*three\ta/3-three\" &&\n+\ttest_check_ignore \"--verbose a/3-three a/three-not-this-one\"\n+'\n+\n+test_expect_success 'local ignore inside a sub-directory' '\n+\texpect \"3-three\" &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"3-three three-not-this-one\"\n+\t)\n+'\n+test_expect_success 'local ignore inside a sub-directory with --verbose' '\n+\texpect \"a/.gitignore:2:*three\t3-three\" &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"--verbose 3-three three-not-this-one\"\n+\t)\n+'\n+\n+test_expect_success_multi 'nested include' \\\n+\t'a/b/.gitignore:8:!on*\ta/b/one' '\n+\ttest_check_ignore \"a/b/one\"\n+'\n+\n+############################################################################\n+#\n+# test ignored sub-directories\n+\n+test_expect_success_multi 'ignored sub-directory' \\\n+\t'a/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir' '\n+\ttest_check_ignore \"a/b/ignored-dir\"\n+'\n+\n+test_expect_success 'multiple files inside ignored sub-directory' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\ta/b/ignored-dir/foo\n+\t\ta/b/ignored-dir/twoooo\n+\t\ta/b/ignored-dir/seven\n+\tEOF\n+\ttest_check_ignore \"a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven\"\n+'\n+\n+test_expect_success 'multiple files inside ignored sub-directory with -v' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\ta/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir/foo\n+\t\ta/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir/twoooo\n+\t\ta/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir/seven\n+\tEOF\n+\ttest_check_ignore \"-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven\"\n+'\n+\n+test_expect_success 'cd to ignored sub-directory' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\tfoo\n+\t\ttwoooo\n+\t\t../one\n+\t\tseven\n+\t\t../../one\n+\tEOF\n+\t(\n+\t\tcd a/b/ignored-dir &&\n+\t\ttest_check_ignore \"foo twoooo ../one seven ../../one\"\n+\t)\n+'\n+\n+test_expect_success 'cd to ignored sub-directory with -v' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\ta/b/.gitignore:5:ignored-dir/\tfoo\n+\t\ta/b/.gitignore:5:ignored-dir/\ttwoooo\n+\t\ta/b/.gitignore:8:!on*\t../one\n+\t\ta/b/.gitignore:5:ignored-dir/\tseven\n+\t\t.gitignore:1:one\t../../one\n+\tEOF\n+\t(\n+\t\tcd a/b/ignored-dir &&\n+\t\ttest_check_ignore \"-v foo twoooo ../one seven ../../one\"\n+\t)\n+'\n+\n+############################################################################\n+#\n+# test handling of symlinks\n+\n+test_expect_success_multi 'symlink' '' '\n+\ttest_check_ignore \"a/symlink\" 1\n+'\n+\n+test_expect_success_multi 'beyond a symlink' '' '\n+\ttest_check_ignore \"a/symlink/foo\" 128 &&\n+\ttest_stderr \"fatal: '\\''a/symlink/foo'\\'' is beyond a symbolic link\"\n+'\n+\n+test_expect_success_multi 'beyond a symlink from subdirectory' '' '\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"symlink/foo\" 128\n+\t) &&\n+\ttest_stderr \"fatal: '\\''symlink/foo'\\'' is beyond a symbolic link\"\n+'\n+\n+############################################################################\n+#\n+# test handling of submodules\n+\n+test_expect_success_multi 'submodule' '' '\n+\ttest_check_ignore \"a/submodule/one\" 128 &&\n+\ttest_stderr \"fatal: Path '\\''a/submodule/one'\\'' is in submodule '\\''a/submodule'\\''\"\n+'\n+\n+test_expect_success_multi 'submodule from subdirectory' '' '\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"submodule/one\" 128\n+\t) &&\n+\ttest_stderr \"fatal: Path '\\''a/submodule/one'\\'' is in submodule '\\''a/submodule'\\''\"\n+'\n+\n+############################################################################\n+#\n+# test handling of global ignore files\n+\n+test_expect_success 'global ignore not yet enabled' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\t.git/info/exclude:7:per-repo\tper-repo\n+\t\ta/.gitignore:2:*three\ta/globalthree\n+\t\t.git/info/exclude:7:per-repo\ta/per-repo\n+\tEOF\n+\ttest_check_ignore \"-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo\"\n+'\n+\n+test_expect_success 'global ignore' '\n+\tenable_global_excludes &&\n+\texpect_from_stdin <<-\\EOF &&\n+\t\tglobalone\n+\t\tper-repo\n+\t\tglobalthree\n+\t\ta/globalthree\n+\t\ta/per-repo\n+\t\tglobaltwo\n+\tEOF\n+\ttest_check_ignore \"globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo\"\n+'\n+\n+test_expect_success 'global ignore with -v' '\n+\tenable_global_excludes &&\n+\texpect_from_stdin <<-EOF &&\n+\t\t$global_excludes:1:globalone\tglobalone\n+\t\t.git/info/exclude:7:per-repo\tper-repo\n+\t\t$global_excludes:3:globalthree\tglobalthree\n+\t\ta/.gitignore:2:*three\ta/globalthree\n+\t\t.git/info/exclude:7:per-repo\ta/per-repo\n+\t\t$global_excludes:2:!globaltwo\tglobaltwo\n+\tEOF\n+\ttest_check_ignore \"-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo\"\n+'\n+\n+############################################################################\n+#\n+# test --stdin\n+\n+cat <<-\\EOF >stdin\n+\tone\n+\tnot-ignored\n+\ta/one\n+\ta/not-ignored\n+\ta/b/on\n+\ta/b/one\n+\ta/b/one one\n+\t\"a/b/one two\"\n+\t\"a/b/one\\\"three\"\n+\ta/b/not-ignored\n+\ta/b/two\n+\ta/b/twooo\n+\tglobaltwo\n+\ta/globaltwo\n+\ta/b/globaltwo\n+\tb/globaltwo\n+EOF\n+cat <<-\\EOF >expected-default\n+\tone\n+\ta/one\n+\ta/b/on\n+\ta/b/one\n+\ta/b/one one\n+\ta/b/one two\n+\t\"a/b/one\\\"three\"\n+\ta/b/two\n+\ta/b/twooo\n+\tglobaltwo\n+\ta/globaltwo\n+\ta/b/globaltwo\n+\tb/globaltwo\n+EOF\n+cat <<-EOF >expected-verbose\n+\t.gitignore:1:one\tone\n+\t.gitignore:1:one\ta/one\n+\ta/b/.gitignore:8:!on*\ta/b/on\n+\ta/b/.gitignore:8:!on*\ta/b/one\n+\ta/b/.gitignore:8:!on*\ta/b/one one\n+\ta/b/.gitignore:8:!on*\ta/b/one two\n+\ta/b/.gitignore:8:!on*\t\"a/b/one\\\"three\"\n+\ta/b/.gitignore:9:!two\ta/b/two\n+\ta/.gitignore:1:two*\ta/b/twooo\n+\t$global_excludes:2:!globaltwo\tglobaltwo\n+\t$global_excludes:2:!globaltwo\ta/globaltwo\n+\t$global_excludes:2:!globaltwo\ta/b/globaltwo\n+\t$global_excludes:2:!globaltwo\tb/globaltwo\n+EOF\n+\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' stdin | \\\n+\ttr \"\\n\" \"\\0\" >stdin0\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' expected-default | \\\n+\ttr \"\\n\" \"\\0\" >expected-default0\n+sed -e 's/\t\"/\t/' -e 's/\\\\//' -e 's/\"$//' expected-verbose | \\\n+\ttr \":\\t\\n\" \"\\0\" >expected-verbose0\n+\n+test_expect_success '--stdin' '\n+\texpect_from_stdin <expected-default &&\n+\ttest_check_ignore \"--stdin\" <stdin\n+'\n+\n+test_expect_success '--stdin -q' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"-q --stdin\" <stdin\n+'\n+\n+test_expect_success '--stdin -v' '\n+\texpect_from_stdin <expected-verbose &&\n+\ttest_check_ignore \"-v --stdin\" <stdin\n+'\n+\n+for opts in '--stdin -z' '-z --stdin'\n+do\n+\ttest_expect_success \"$opts\" \"\n+\t\texpect_from_stdin <expected-default0 &&\n+\t\ttest_check_ignore '$opts' <stdin0\n+\t\"\n+\n+\ttest_expect_success \"$opts -q\" \"\n+\t\texpect \"\" &&\n+\t\ttest_check_ignore '-q $opts' <stdin0\n+\t\"\n+\n+\ttest_expect_success \"$opts -v\" \"\n+\t\texpect_from_stdin <expected-verbose0 &&\n+\t\ttest_check_ignore '-v $opts' <stdin0\n+\t\"\n+done\n+\n+cat <<-\\EOF >stdin\n+\t../one\n+\t../not-ignored\n+\tone\n+\tnot-ignored\n+\tb/on\n+\tb/one\n+\tb/one one\n+\t\"b/one two\"\n+\t\"b/one\\\"three\"\n+\tb/two\n+\tb/not-ignored\n+\tb/twooo\n+\t../globaltwo\n+\tglobaltwo\n+\tb/globaltwo\n+\t../b/globaltwo\n+EOF\n+cat <<-\\EOF >expected-default\n+\t../one\n+\tone\n+\tb/on\n+\tb/one\n+\tb/one one\n+\tb/one two\n+\t\"b/one\\\"three\"\n+\tb/two\n+\tb/twooo\n+\t../globaltwo\n+\tglobaltwo\n+\tb/globaltwo\n+\t../b/globaltwo\n+EOF\n+cat <<-EOF >expected-verbose\n+\t.gitignore:1:one\t../one\n+\t.gitignore:1:one\tone\n+\ta/b/.gitignore:8:!on*\tb/on\n+\ta/b/.gitignore:8:!on*\tb/one\n+\ta/b/.gitignore:8:!on*\tb/one one\n+\ta/b/.gitignore:8:!on*\tb/one two\n+\ta/b/.gitignore:8:!on*\t\"b/one\\\"three\"\n+\ta/b/.gitignore:9:!two\tb/two\n+\ta/.gitignore:1:two*\tb/twooo\n+\t$global_excludes:2:!globaltwo\t../globaltwo\n+\t$global_excludes:2:!globaltwo\tglobaltwo\n+\t$global_excludes:2:!globaltwo\tb/globaltwo\n+\t$global_excludes:2:!globaltwo\t../b/globaltwo\n+EOF\n+\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' stdin | \\\n+\ttr \"\\n\" \"\\0\" >stdin0\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' expected-default | \\\n+\ttr \"\\n\" \"\\0\" >expected-default0\n+sed -e 's/\t\"/\t/' -e 's/\\\\//' -e 's/\"$//' expected-verbose | \\\n+\ttr \":\\t\\n\" \"\\0\" >expected-verbose0\n+\n+test_expect_success '--stdin from subdirectory' '\n+\texpect_from_stdin <expected-default &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"--stdin\" <../stdin\n+\t)\n+'\n+\n+test_expect_success '--stdin from subdirectory with -v' '\n+\texpect_from_stdin <expected-verbose &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"--stdin -v\" <../stdin\n+\t)\n+'\n+\n+for opts in '--stdin -z' '-z --stdin'\n+do\n+\ttest_expect_success \"$opts from subdirectory\" '\n+\t\texpect_from_stdin <expected-default0 &&\n+\t\t(\n+\t\t\tcd a &&\n+\t\t\ttest_check_ignore \"'\"$opts\"'\" <../stdin0\n+\t\t)\n+\t'\n+\n+\ttest_expect_success \"$opts from subdirectory with -v\" '\n+\t\texpect_from_stdin <expected-verbose0 &&\n+\t\t(\n+\t\t\tcd a &&\n+\t\t\ttest_check_ignore \"'\"$opts\"' -v\" <../stdin0\n+\t\t)\n+\t'\n+done\n+\n+\n+test_done\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"201291","messageId":"7vr4ozv0wr.fsf@alter.siamese.dyndns.org","threadId":"31819","inReplyTo":"1350282212-4270-1-git-send-email-pclouds@gmail.com","subject":"Re: nd/attr-match-more-optim, nd/wildmatch and as/check-ignore","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-10-15T22:13:56Z","receivedAt":"2012-10-15T22:13:56Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> These three series all touch the same code in dir.c and cause\n> a bunch of conflicts. So I rebase nd/wildmatch and as/check-ignore\n> on top of nd/attr-match-more-optim and resolve all conflicts.\n\nThanks for working on this.  Makes my life much easier.\n\nIf not \"my life\", it certainly will make it easier to shift blame to\nothers when things go wrong (it is called \"easier to bisect\" ;-)\n"},{"id":"201292","messageId":"7vlif7v03r.fsf@alter.siamese.dyndns.org","threadId":"31819","inReplyTo":"1350282486-4646-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-10-15T22:31:20Z","receivedAt":"2012-10-15T22:31:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +For each pathname given via the command-line or from a file via\n> +`--stdin`, this command will list the first exclude pattern found (if\n> +any) which explicitly excludes or includes that pathname.  Note that\n> +within any given exclude file, later patterns take precedence over\n> +earlier ones, so any matching pattern which this command outputs may\n> +not be the one you would immediately expect.\n\n\"The first exclude pattern\" is very misleading, isn't it?  For\nexample, with these in $GIT_DIR/info/exclude, I would get:\n\n\t$ cat -n .git/info/exclude\n\t  1 *~\n          2 Makefile~\n\t$ git check-ignore -v Makefile~\n\t.git/info/exclude:2:Makefile~\tMakefile~\n\nwhich is the correct result (the last one in a single source decides\nthe fate of the path), but it hardly is \"first one found\" and the\nmatching pattern in the output would not be something unexpected for\nthe users, either.\n\nThe reason it is \"the first one found\" is because the implementation\narranges the loop in such a way that it can stop early when it finds\na match---it simply checks matches from the end of the source.\n\nBut that is not visible to end-users, and they will find the above\ndescription just wrong, no?\n\n\n> +OUTPUT\n> +------\n> +\n> +By default, any of the given pathnames which match an ignore pattern\n> +will be output, one per line.  If no pattern matches a given path,\n> +nothing will be output for that path; this means that path will not be\n> +ignored.\n> +\n> +If `--verbose` is specified, the output is a series of lines of the form:\n> +\n> +<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>\n> +\n> +<pathname> is the path of a file being queried, <pattern> is the\n> +matching pattern, <source> is the pattern's source file, and <linenum>\n> +is the line number of the pattern within that source.  If the pattern\n> +contained a `!` prefix or `/` suffix, it will be preserved in the\n> +output.  <source> will be an absolute path when referring to the file\n> +configured by `core.excludesfile`, or relative to the repository root\n> +when referring to `.git/info/exclude` or a per-directory exclude file.\n> +\n> +If `-z` is specified, the output is a series of lines of the form:\n> +\n\nHmph... the remainder of the paragraph seems to have been chopped off.\n\n> +EXIT STATUS\n> +-----------\n> +\n> +0::\n> +\tOne or more of the provided paths is ignored.\n> +\n> +1::\n> +\tNone of the provided paths are ignored.\n> +\n> +128::\n> +\tA fatal error was encountered.\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:gitignore[5]\n> +linkgit:gitconfig[5]\n> +linkgit:git-ls-files[5]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n"},{"id":"201321","messageId":"CACsJy8Du1G-=pBbHW841V-61L9_HGmNkOE7EB2W2f1RJe9WuXg@mail.gmail.com","threadId":"31819","inReplyTo":"7vlif7v03r.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-16T11:08:10Z","receivedAt":"2012-10-16T11:08:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Adam, do you have time to continue this series? I can help polish it\nfor inclusion, but I don't want to step in your way if you are quietly\nupdating it.\n\nOn Tue, Oct 16, 2012 at 5:31 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +\n>> +If `-z` is specified, the output is a series of lines of the form:\n>> +\n>\n> Hmph... the remainder of the paragraph seems to have been chopped off.\n\nNo, the last version [1] looks the same. I was worried there was a bug\nsomewhere in the toolchain that chopped it off.\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/204661/focus=206085\n\n>> +EXIT STATUS\n>> +-----------\n>> +\n>> +0::\n>> +     One or more of the provided paths is ignored.\n-- \nDuy\n"},{"id":"201339","messageId":"CAOkDyE_HCxNYpWVc0WCwhcAivEm+RLiFj-TwSsEbi+U5mZ3cww@mail.gmail.com","threadId":"31819","inReplyTo":"CACsJy8Du1G-=pBbHW841V-61L9_HGmNkOE7EB2W2f1RJe9WuXg@mail.gmail.com","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-10-16T14:09:51Z","receivedAt":"2012-10-16T14:09:51Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Hi again,\n\nFirstly thanks very much for your recent work on this series!\n\nOn Tue, Oct 16, 2012 at 4:08 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> Adam, do you have time to continue this series? I can help polish it\n> for inclusion, but I don't want to step in your way if you are quietly\n> updating it.\n\nI was *intending* to finish it off soon, but I have been really busy\nwith work and other commitments recently, which has prevented this.  I\ndon't currently have any unpublished changes which would conflict with\nyour recent work, and I'm at a conference this week, so feel free to\ncarry on polishing if you want.  However I will probably have some\nresponses on the discussion about current issues, so it would be good\nif I was given a chance to catch up on this discussion before the\nseries makes its way to master.\n\nThanks again!\n\nAdam\n"},{"id":"201340","messageId":"CAOkDyE9g6rNiv7nnTu2i34dbn_z7r5SmhDuxief7iEQLVxtO5g@mail.gmail.com","threadId":"31819","inReplyTo":"7vlif7v03r.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-10-16T14:13:46Z","receivedAt":"2012-10-16T14:13:46Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Mon, Oct 15, 2012 at 3:31 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> +For each pathname given via the command-line or from a file via\n>> +`--stdin`, this command will list the first exclude pattern found (if\n>> +any) which explicitly excludes or includes that pathname.  Note that\n>> +within any given exclude file, later patterns take precedence over\n>> +earlier ones, so any matching pattern which this command outputs may\n>> +not be the one you would immediately expect.\n>\n> \"The first exclude pattern\" is very misleading, isn't it?\n\nI don't think so, because of the second sentence.\n\n> For example, with these in $GIT_DIR/info/exclude, I would get:\n>\n>         $ cat -n .git/info/exclude\n>           1 *~\n>           2 Makefile~\n>         $ git check-ignore -v Makefile~\n>         .git/info/exclude:2:Makefile~   Makefile~\n>\n> which is the correct result (the last one in a single source decides\n> the fate of the path), but it hardly is \"first one found\" and the\n> matching pattern in the output would not be something unexpected for\n> the users, either.\n>\n> The reason it is \"the first one found\" is because the implementation\n> arranges the loop in such a way that it can stop early when it finds\n> a match---it simply checks matches from the end of the source.\n>\n> But that is not visible to end-users,\n\nCorrect; that's precisely why I wrote the second sentence which\nexplicitly explains this.\n\n> and they will find the above description just wrong, no?\n\nIt's not wrong AFAICS, but suggestions for rewording this more clearly\nare of course welcome.  Maybe s/immediately/intuitively/ ?\n\n>> +OUTPUT\n>> +------\n>> +\n>> +By default, any of the given pathnames which match an ignore pattern\n>> +will be output, one per line.  If no pattern matches a given path,\n>> +nothing will be output for that path; this means that path will not be\n>> +ignored.\n>> +\n>> +If `--verbose` is specified, the output is a series of lines of the form:\n>> +\n>> +<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>\n>> +\n>> +<pathname> is the path of a file being queried, <pattern> is the\n>> +matching pattern, <source> is the pattern's source file, and <linenum>\n>> +is the line number of the pattern within that source.  If the pattern\n>> +contained a `!` prefix or `/` suffix, it will be preserved in the\n>> +output.  <source> will be an absolute path when referring to the file\n>> +configured by `core.excludesfile`, or relative to the repository root\n>> +when referring to `.git/info/exclude` or a per-directory exclude file.\n>> +\n>> +If `-z` is specified, the output is a series of lines of the form:\n>> +\n>\n> Hmph... the remainder of the paragraph seems to have been chopped off.\n\nYes, an earlier review also caught this but I have not had time to fix\nit yet, sorry :-/\n"},{"id":"201344","messageId":"CACsJy8BewSctFSoth=pzYd+5fNZOUZ2AwUcrLd9VKA2OduJV+A@mail.gmail.com","threadId":"31819","inReplyTo":"CAOkDyE_HCxNYpWVc0WCwhcAivEm+RLiFj-TwSsEbi+U5mZ3cww@mail.gmail.com","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-16T15:07:23Z","receivedAt":"2012-10-16T15:07:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Oct 16, 2012 at 9:09 PM, Adam Spiers <git@adamspiers.org> wrote:\n> I was *intending* to finish it off soon, but I have been really busy\n> with work and other commitments recently, which has prevented this.  I\n> don't currently have any unpublished changes which would conflict with\n> your recent work, and I'm at a conference this week, so feel free to\n> carry on polishing if you want.  However I will probably have some\n> responses on the discussion about current issues, so it would be good\n> if I was given a chance to catch up on this discussion before the\n> series makes its way to master.\n\nNo hurry. And thanks for your contribution.\n-- \nDuy\n"},{"id":"201350","messageId":"7vmwzmtmyd.fsf@alter.siamese.dyndns.org","threadId":"31819","inReplyTo":"CAOkDyE9g6rNiv7nnTu2i34dbn_z7r5SmhDuxief7iEQLVxtO5g@mail.gmail.com","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-10-16T16:12:58Z","receivedAt":"2012-10-16T16:12:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> On Mon, Oct 15, 2012 at 3:31 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>>\n>>> +For each pathname given via the command-line or from a file via\n>>> +`--stdin`, this command will list the first exclude pattern found (if\n>>> +any) which explicitly excludes or includes that pathname.  Note that\n>>> +within any given exclude file, later patterns take precedence over\n>>> +earlier ones, so any matching pattern which this command outputs may\n>>> +not be the one you would immediately expect.\n>>\n>> \"The first exclude pattern\" is very misleading, isn't it?\n>\n> I don't think so, because of the second sentence.\n>\n>> For example, with these in $GIT_DIR/info/exclude, I would get:\n>>\n>>         $ cat -n .git/info/exclude\n>>           1 *~\n>>           2 Makefile~\n>>         $ git check-ignore -v Makefile~\n>>         .git/info/exclude:2:Makefile~   Makefile~\n>>\n>> which is the correct result (the last one in a single source decides\n>> the fate of the path), but it hardly is \"first one found\" and the\n>> matching pattern in the output would not be something unexpected for\n>> the users, either.\n>>\n>> The reason it is \"the first one found\" is because the implementation\n>> arranges the loop in such a way that it can stop early when it finds\n>> a match---it simply checks matches from the end of the source.\n>>\n>> But that is not visible to end-users,\n>\n> Correct; that's precisely why I wrote the second sentence which\n> explicitly explains this.\n>\n>> and they will find the above description just wrong, no?\n>\n> It's not wrong AFAICS, but suggestions for rewording this more clearly\n> are of course welcome.  Maybe s/immediately/intuitively/ ?\n\nI think this is sufficient:\n\n\tFor each pathname given via the command-line or from a file\n\tvia `--stdin`, show the pattern from .gitignore (or other\n\tinput files to the exclude mechanism) that decides if the\n\tpathname is excluded.\n\nand without \"Note that\" at all.\n"},{"id":"202523","messageId":"5096D76F.5090907@kdbg.org","threadId":"31819","inReplyTo":"1350282362-4505-13-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/13] wildmatch: fix tests that fail on Windows due to path mangling","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-11-04T21:00:31Z","receivedAt":"2012-11-04T21:00:31Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Patterns beginning with a slash are converted to Windows paths before\ntest-wildmatch gets to see them. Use a different first character. This\ndoes change the meaning of the test because the slash is special. But:\n\n- The first pair of changed lines the test is about '*' matching an empty\n  string, which does not need the slash.\n\n- The second pair of changed lines the test is about a sequence of '*/',\n  and I think we can afford to test without the leading slash.\n\nOne case is unchanged:\n\n    match 1 x '/foo' '**/foo'\n\nEven though the test is about ** matching zero levels at the beginning of\na path, on Windows, '**' actually matches something because /foo is\nconverted to c:\\path\\to\\msys\\foo. Let's be lazy and let this pass.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n After this change, there are still 3 failing tests that are in connection\n with [[:xdigit:]]. Don't know, yet, what's going on there.\n\n t/t3070-wildmatch.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex e6ad6f4..74c0f7c 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -95,8 +95,8 @@ match 0 0 ']' '[!]-]'\n match 1 x 'a' '[!]-]'\n match 0 0 '' '\\'\n match 0 x '\\' '\\'\n-match 0 x '/\\' '*/\\'\n-match 1 x '/\\' '*/\\\\'\n+match 0 x '-\\' '*-\\'\n+match 1 x '-\\' '*-\\\\'\n match 1 1 'foo' 'foo'\n match 1 1 '@foo' '@foo'\n match 0 0 'foo' '@foo'\n@@ -187,8 +187,8 @@ match 0 0 '-' '[[-\\]]'\n match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n-match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n-match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 1 'adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 'adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n \n-- \n1.8.0.rc0.45.g6c9d890\n"},{"id":"202524","messageId":"5096D92A.7090600@kdbg.org","threadId":"31819","inReplyTo":"1350282486-4646-12-git-send-email-pclouds@gmail.com","subject":"[PATCH as/check-ignore] t0007: fix tests on Windows","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-11-04T21:07:54Z","receivedAt":"2012-11-04T21:07:54Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"The value of $global_excludes is sometimes part of the output\nthat is tested for. Since git on Windows only sees DOS style paths,\nwe have to ensure that the \"expected\" values are constructed in\nthe same manner. To account for this, use $(pwd) to set the value\nof global_excludes.\n\nAdditionally, add a SYMLINKS prerequisite to the tests involving\nsymbolic links.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n t/t0007-ignores.sh | 25 +++++++++++++++++--------\n 1 file changed, 17 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t0007-ignores.sh b/t/t0007-ignores.sh\nindex 7fd7e53..5d2b8f2 100755\n--- a/t/t0007-ignores.sh\n+++ b/t/t0007-ignores.sh\n@@ -5,7 +5,7 @@ test_description=check-ignore\n . ./test-lib.sh\n \n init_vars () {\n-\tglobal_excludes=\"$HOME/global-excludes\"\n+\tglobal_excludes=\"$(pwd)/global-excludes\"\n }\n \n enable_global_excludes () {\n@@ -77,18 +77,24 @@ test_check_ignore () {\n }\n \n test_expect_success_multi () {\n+\tprereq=\n+\tif test $# -eq 4\n+\tthen\n+\t\tprereq=$1\n+\t\tshift\n+\tfi\n \ttestname=\"$1\" expect_verbose=\"$2\" code=\"$3\"\n \n \texpect=$( echo \"$expect_verbose\" | sed -e 's/.*\t//' )\n \n-\ttest_expect_success \"$testname\" \"\n+\ttest_expect_success $prereq \"$testname\" \"\n \t\texpect '$expect' &&\n \t\t$code\n \t\"\n \n \tfor quiet_opt in '-q' '--quiet'\n \tdo\n-\t\ttest_expect_success \"$testname${quiet_opt:+ with $quiet_opt}\" \"\n+\t\ttest_expect_success $prereq \"$testname${quiet_opt:+ with $quiet_opt}\" \"\n \t\t\texpect '' &&\n \t\t\t$code\n \t\t\"\n@@ -97,7 +103,7 @@ test_expect_success_multi () {\n \n \tfor verbose_opt in '-v' '--verbose'\n \tdo\n-\t\ttest_expect_success \"$testname${verbose_opt:+ with $verbose_opt}\" \"\n+\t\ttest_expect_success $prereq \"$testname${verbose_opt:+ with $verbose_opt}\" \"\n \t\t\texpect '$expect_verbose' &&\n \t\t\t$code\n \t\t\"\n@@ -108,7 +114,10 @@ test_expect_success_multi () {\n test_expect_success 'setup' '\n \tinit_vars\n \tmkdir -p a/b/ignored-dir a/submodule b &&\n-\tln -s b a/symlink &&\n+\tif test_have_prereq SYMLINKS\n+\tthen\n+\t\tln -s b a/symlink\n+\tfi &&\n \t(\n \t\tcd a/submodule &&\n \t\tgit init &&\n@@ -326,16 +335,16 @@ test_expect_success 'cd to ignored sub-directory with -v' '\n #\n # test handling of symlinks\n \n-test_expect_success_multi 'symlink' '' '\n+test_expect_success_multi SYMLINKS 'symlink' '' '\n \ttest_check_ignore \"a/symlink\" 1\n '\n \n-test_expect_success_multi 'beyond a symlink' '' '\n+test_expect_success_multi SYMLINKS 'beyond a symlink' '' '\n \ttest_check_ignore \"a/symlink/foo\" 128 &&\n \ttest_stderr \"fatal: '\\''a/symlink/foo'\\'' is beyond a symbolic link\"\n '\n \n-test_expect_success_multi 'beyond a symlink from subdirectory' '' '\n+test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '\n \t(\n \t\tcd a &&\n \t\ttest_check_ignore \"symlink/foo\" 128\n-- \n1.8.0.rc0.45.g6c9d890\n"},{"id":"202589","messageId":"CACsJy8DEH53mA4fDyZwbVsbyGcNY8b54Sn1p5-rto6r_h48dkA@mail.gmail.com","threadId":"31819","inReplyTo":"5096D76F.5090907@kdbg.org","subject":"Re: [PATCH 14/13] wildmatch: fix tests that fail on Windows due to path mangling","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-06T12:47:13Z","receivedAt":"2012-11-06T12:47:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Nov 5, 2012 at 4:00 AM, Johannes Sixt <j6t@kdbg.org> wrote:\n> Patterns beginning with a slash are converted to Windows paths before\n> test-wildmatch gets to see them. Use a different first character.\n\nOr we could prepend the paths with something, which is then cut out by\ntest-wildmatch. Not sure if it's intuitive to look at the tests\nthough.\n\n>  After this change, there are still 3 failing tests that are in connection\n>  with [[:xdigit:]]. Don't know, yet, what's going on there.\n\nthe wildmatch tests or fnmatch ones?\n-- \nDuy\n"},{"id":"202619","messageId":"509AB76A.3070700@kdbg.org","threadId":"31819","inReplyTo":"CACsJy8DEH53mA4fDyZwbVsbyGcNY8b54Sn1p5-rto6r_h48dkA@mail.gmail.com","subject":"Re: [PATCH 14/13] wildmatch: fix tests that fail on Windows due to path mangling","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-11-07T19:32:58Z","receivedAt":"2012-11-07T19:32:58Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 06.11.2012 13:47, schrieb Nguyen Thai Ngoc Duy:\n> On Mon, Nov 5, 2012 at 4:00 AM, Johannes Sixt <j6t@kdbg.org> wrote:\n>> Patterns beginning with a slash are converted to Windows paths before\n>> test-wildmatch gets to see them. Use a different first character.\n> \n> Or we could prepend the paths with something, which is then cut out by\n> test-wildmatch. Not sure if it's intuitive to look at the tests\n> though.\n\nThat would be a possibility, too.\n\n>>  After this change, there are still 3 failing tests that are in connection\n>>  with [[:xdigit:]]. Don't know, yet, what's going on there.\n> \n> the wildmatch tests or fnmatch ones?\n\nfnmatch ones:\n\nok 147 - wildmatch:    match '5' '[[:xdigit:]]'\nnot ok 148 - fnmatch:      match '5' '[[:xdigit:]]'\n#\n#                   test-wildmatch fnmatch '5' '[[:xdigit:]]'\n#\nok 149 - wildmatch:    match 'f' '[[:xdigit:]]'\nnot ok 150 - fnmatch:      match 'f' '[[:xdigit:]]'\n#\n#                   test-wildmatch fnmatch 'f' '[[:xdigit:]]'\n#\nok 151 - wildmatch:    match 'D' '[[:xdigit:]]'\nnot ok 152 - fnmatch:      match 'D' '[[:xdigit:]]'\n#\n#                   test-wildmatch fnmatch 'D' '[[:xdigit:]]'\n#\n\nShe same tests fail on Linux, BTW, when built with NO_FNMATCH=1 and the\n\"#if defined _LIBC || !defined __GNU_LIBRARY__\" brackets removed.\n\n-- Hannes\n"},{"id":"202660","messageId":"20121108180445.GL15560@sigill.intra.peff.net","threadId":"31819","inReplyTo":"5096D92A.7090600@kdbg.org","subject":"Re: [PATCH as/check-ignore] t0007: fix tests on Windows","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-11-08T18:04:45Z","receivedAt":"2012-11-08T18:04:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Nov 04, 2012 at 10:07:54PM +0100, Johannes Sixt wrote:\n\n> The value of $global_excludes is sometimes part of the output\n> that is tested for. Since git on Windows only sees DOS style paths,\n> we have to ensure that the \"expected\" values are constructed in\n> the same manner. To account for this, use $(pwd) to set the value\n> of global_excludes.\n> \n> Additionally, add a SYMLINKS prerequisite to the tests involving\n> symbolic links.\n> \n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n\nThanks. I put this on top of as/check-ignore in 'pu' for reference, but\nI am still expecting a re-roll from Adam or Duy at some point.\n\n-Peff\n"},{"id":"202805","messageId":"1352628837-5784-1-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"5096D76F.5090907@kdbg.org","subject":"[PATCH 14/13] test-wildmatch: fix tests that fail on Windows due to path mangling","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-11T10:13:56Z","receivedAt":"2012-11-11T10:13:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Patterns beginning with a slash are converted to Windows paths before\ntest-wildmatch gets to see them. Avoid this case by always writing\nXXX/abc instead of /abc. The leading XXX will be removed by\ntest-wildmatch itself before processing.\n\nAny patterns beginning with a forward slash is rejected by\ntest-wildmatch to avoid the same fault in future.\n\nReported-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n On top of nd/wildmatch, apparently.\n\n t/t3070-wildmatch.sh | 10 +++++-----\n test-wildmatch.c     |  8 ++++++++\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex e6ad6f4..3155eab 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -74,7 +74,7 @@ match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 0 'foo' '**/foo'\n-match 1 x '/foo' '**/foo'\n+match 1 x 'XXX/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\n match 0 0 'foo/bar/baz' '**/bar*'\n@@ -95,8 +95,8 @@ match 0 0 ']' '[!]-]'\n match 1 x 'a' '[!]-]'\n match 0 0 '' '\\'\n match 0 x '\\' '\\'\n-match 0 x '/\\' '*/\\'\n-match 1 x '/\\' '*/\\\\'\n+match 0 x 'XXX/\\' '*/\\'\n+match 1 x 'XXX/\\' '*/\\\\'\n match 1 1 'foo' 'foo'\n match 1 1 '@foo' '@foo'\n match 0 0 'foo' '@foo'\n@@ -187,8 +187,8 @@ match 0 0 '-' '[[-\\]]'\n match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n-match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n-match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n \ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 74c0864..e384c8e 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -3,6 +3,14 @@\n \n int main(int argc, char **argv)\n {\n+\tint i;\n+\tfor (i = 2; i < argc; i++) {\n+\t\tif (argv[i][0] == '/')\n+\t\t\tdie(\"Forward slash is not allowed at the beginning of the\\n\"\n+\t\t\t    \"pattern because Windows does not like it. Use `XXX/' instead.\");\n+\t\telse if (!strncmp(argv[i], \"XXX/\", 4))\n+\t\t\targv[i] += 3;\n+\t}\n \tif (!strcmp(argv[1], \"wildmatch\"))\n \t\treturn !!wildmatch(argv[3], argv[2], 0);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"202806","messageId":"1352628837-5784-2-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"1352628837-5784-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/13] compat/fnmatch: fix off-by-one character class's length check","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-11T10:13:57Z","receivedAt":"2012-11-11T10:13:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Character class \"xdigit\" is the only one that hits 6 character limit\ndefined by CHAR_CLASS_MAX_LENGTH. All other character classes are 5\ncharacter long and therefore never caught by this.\n\nThis should make xdigit tests in t3070 pass on Windows.\n\nReported-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n I tested with Linux (removing the ifdef __LIBC in fnmatch.c) but I\n think this should get an ACK from someone who actually uses it on\n Windows.\n\n We may want to tell upstream (who?) about this if they haven't fixed\n it already.\n\n compat/fnmatch/fnmatch.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c\nindex 9473aed..0ff1d27 100644\n--- a/compat/fnmatch/fnmatch.c\n+++ b/compat/fnmatch/fnmatch.c\n@@ -345,7 +345,7 @@ internal_fnmatch (pattern, string, no_leading_period, flags)\n \n \t\t    for (;;)\n \t\t      {\n-\t\t\tif (c1 == CHAR_CLASS_MAX_LENGTH)\n+\t\t\tif (c1 > CHAR_CLASS_MAX_LENGTH)\n \t\t\t  /* The name is too long and therefore the pattern\n \t\t\t     is ill-formed.  */\n \t\t\t  return FNM_NOMATCH;\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"202811","messageId":"7vvcdco1pf.fsf@alter.siamese.dyndns.org","threadId":"31819","inReplyTo":"1352628837-5784-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 14/13] test-wildmatch: fix tests that fail on Windows due to path mangling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-11T10:47:56Z","receivedAt":"2012-11-11T10:47:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Patterns beginning with a slash are converted to Windows paths before\n> test-wildmatch gets to see them. Avoid this case by always writing\n> XXX/abc instead of /abc. The leading XXX will be removed by\n> test-wildmatch itself before processing.\n>\n> Any patterns beginning with a forward slash is rejected by\n> test-wildmatch to avoid the same fault in future.\n\nThe title taken together with the above explanation makes it sound\nas if wildmatch code does not work with the pattern /foo on Windows\nat all and to avoid the issue (instead of fixing the breakage) this\npatch removes such tests.\n\nBut I suspect that is not what is going on. Perhaps the title of the\nchange is more like \"test-wildmatch: avoid Windows path mangling\"\nand explain with \"On Windows, any command line argument that begins\nwith a slash is mangled as if it were a full pathname.  This causes\npatterns beginning with a slash not to be passed to test-wildmatch\ncorrectly\" or something?\n"},{"id":"203065","messageId":"1352801169-10501-1-git-send-email-pclouds@gmail.com","threadId":"31819","inReplyTo":"7vvcdco1pf.fsf@alter.siamese.dyndns.org","subject":"[PATCH 14/13] test-wildmatch: avoid Windows path mangling","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-13T10:06:09Z","receivedAt":"2012-11-13T10:06:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Windows, arguments starting with a forward slash is mangled as if\nit were full pathname. This causes the patterns beginning with a slash\nnot to be passed to test-wildmatch correctly. Avoid mangling by never\naccepting patterns starting with a slash. Those arguments must be\nrewritten with a leading \"XXX\" (e.g. \"/abc\" becomes \"XXX/abc\"), which\nwill be removed by test-wildmatch itself before feeding the patterns\nto wildmatch() or fnmatch().\n\nReported-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n On Sun, Nov 11, 2012 at 5:47 PM, Junio C Hamano <gitster@pobox.com> wrote:\n > The title taken together with the above explanation makes it sound\n > as if wildmatch code does not work with the pattern /foo on Windows\n > at all and to avoid the issue (instead of fixing the breakage) this\n > patch removes such tests....\n\n OK how about this?\n\n t/t3070-wildmatch.sh | 10 +++++-----\n test-wildmatch.c     |  8 ++++++++\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex e6ad6f4..3155eab 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -74,7 +74,7 @@ match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 0 'foo' '**/foo'\n-match 1 x '/foo' '**/foo'\n+match 1 x 'XXX/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\n match 0 0 'foo/bar/baz' '**/bar*'\n@@ -95,8 +95,8 @@ match 0 0 ']' '[!]-]'\n match 1 x 'a' '[!]-]'\n match 0 0 '' '\\'\n match 0 x '\\' '\\'\n-match 0 x '/\\' '*/\\'\n-match 1 x '/\\' '*/\\\\'\n+match 0 x 'XXX/\\' '*/\\'\n+match 1 x 'XXX/\\' '*/\\\\'\n match 1 1 'foo' 'foo'\n match 1 1 '@foo' '@foo'\n match 0 0 'foo' '@foo'\n@@ -187,8 +187,8 @@ match 0 0 '-' '[[-\\]]'\n match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n-match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n-match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n \ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 74c0864..e384c8e 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -3,6 +3,14 @@\n \n int main(int argc, char **argv)\n {\n+\tint i;\n+\tfor (i = 2; i < argc; i++) {\n+\t\tif (argv[i][0] == '/')\n+\t\t\tdie(\"Forward slash is not allowed at the beginning of the\\n\"\n+\t\t\t    \"pattern because Windows does not like it. Use `XXX/' instead.\");\n+\t\telse if (!strncmp(argv[i], \"XXX/\", 4))\n+\t\t\targv[i] += 3;\n+\t}\n \tif (!strcmp(argv[1], \"wildmatch\"))\n \t\treturn !!wildmatch(argv[3], argv[2], 0);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"203123","messageId":"50A28C17.9020506@kdbg.org","threadId":"31819","inReplyTo":"1352801169-10501-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 14/13] test-wildmatch: avoid Windows path mangling","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-11-13T18:06:15Z","receivedAt":"2012-11-13T18:06:15Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 13.11.2012 11:06, schrieb Nguyễn Thái Ngọc Duy:\n> On Windows, arguments starting with a forward slash is mangled as if\n> it were full pathname. This causes the patterns beginning with a slash\n> not to be passed to test-wildmatch correctly. Avoid mangling by never\n> accepting patterns starting with a slash. Those arguments must be\n> rewritten with a leading \"XXX\" (e.g. \"/abc\" becomes \"XXX/abc\"), which\n> will be removed by test-wildmatch itself before feeding the patterns\n> to wildmatch() or fnmatch().\n> \n> Reported-by: Johannes Sixt <j6t@kdbg.org>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  On Sun, Nov 11, 2012 at 5:47 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>  > The title taken together with the above explanation makes it sound\n>  > as if wildmatch code does not work with the pattern /foo on Windows\n>  > at all and to avoid the issue (instead of fixing the breakage) this\n>  > patch removes such tests....\n> \n>  OK how about this?\n\nThanks, the patch lets the tests pass on Windows.\n\n-- Hannes\n"},{"id":"203124","messageId":"50A28C5E.3090508@kdbg.org","threadId":"31819","inReplyTo":"1352628837-5784-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 15/13] compat/fnmatch: fix off-by-one character class's length check","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-11-13T18:07:26Z","receivedAt":"2012-11-13T18:07:26Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 11.11.2012 11:13, schrieb Nguyễn Thái Ngọc Duy:\n> -\t\t\tif (c1 == CHAR_CLASS_MAX_LENGTH)\n> +\t\t\tif (c1 > CHAR_CLASS_MAX_LENGTH)\n\nNice catch! With this one and 14/13, all tests in t3070 pass on Windows.\n\n-- Hannes\n"},{"id":"203539","messageId":"50AB2B0F.8090808@viscovery.net","threadId":"31819","inReplyTo":"7vvcdco1pf.fsf@alter.siamese.dyndns.org","subject":"[PATCH 14/13] test-wildmatch: avoid Windows path mangling","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-11-20T07:02:39Z","receivedAt":"2012-11-20T07:02:39Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThe MSYS bash mangles arguments that begin with a forward slash\nwhen they are passed to test-wildmatch. This causes tests to fail.\nAvoid mangling by prepending \"XXX\", which is removed by\ntest-wildmatch before further processing.\n\n[J6t: reworded commit message]\n\nReported-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n Works well, and I'm fine with this work-around.\n\n -- Hannes\n\n t/t3070-wildmatch.sh | 10 +++++-----\n test-wildmatch.c     |  8 ++++++++\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex e6ad6f4..3155eab 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -74,7 +74,7 @@ match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 0 'foo' '**/foo'\n-match 1 x '/foo' '**/foo'\n+match 1 x 'XXX/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\n match 0 0 'foo/bar/baz' '**/bar*'\n@@ -95,8 +95,8 @@ match 0 0 ']' '[!]-]'\n match 1 x 'a' '[!]-]'\n match 0 0 '' '\\'\n match 0 x '\\' '\\'\n-match 0 x '/\\' '*/\\'\n-match 1 x '/\\' '*/\\\\'\n+match 0 x 'XXX/\\' '*/\\'\n+match 1 x 'XXX/\\' '*/\\\\'\n match 1 1 'foo' 'foo'\n match 1 1 '@foo' '@foo'\n match 0 0 'foo' '@foo'\n@@ -187,8 +187,8 @@ match 0 0 '-' '[[-\\]]'\n match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n-match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n-match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n diff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 74c0864..e384c8e 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -3,6 +3,14 @@\n  int main(int argc, char **argv)\n {\n+\tint i;\n+\tfor (i = 2; i < argc; i++) {\n+\t\tif (argv[i][0] == '/')\n+\t\t\tdie(\"Forward slash is not allowed at the beginning of the\\n\"\n+\t\t\t    \"pattern because Windows does not like it. Use `XXX/' instead.\");\n+\t\telse if (!strncmp(argv[i], \"XXX/\", 4))\n+\t\t\targv[i] += 3;\n+\t}\n \tif (!strcmp(argv[1], \"wildmatch\"))\n \t\treturn !!wildmatch(argv[3], argv[2], 0);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n"},{"id":"203540","messageId":"50AB2BDD.5030707@viscovery.net","threadId":"31819","inReplyTo":"1352628837-5784-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 15/13] compat/fnmatch: fix off-by-one character class's length check","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-11-20T07:06:05Z","receivedAt":"2012-11-20T07:06:05Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 11/11/2012 11:13, schrieb Nguyễn Thái Ngọc Duy:\n> Character class \"xdigit\" is the only one that hits 6 character limit\n> defined by CHAR_CLASS_MAX_LENGTH. All other character classes are 5\n> character long and therefore never caught by this.\n> \n> This should make xdigit tests in t3070 pass on Windows.\n> \n> Reported-by: Johannes Sixt <j6t@kdbg.org>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  I tested with Linux (removing the ifdef __LIBC in fnmatch.c) but I\n>  think this should get an ACK from someone who actually uses it on\n>  Windows.\n\nWorks well here on Windows.\n\nThis does not affect Windows alone, but all platforms that fall back to\ncompat/fnmatch. It's perhaps worth its own topic branch.\n\n>  We may want to tell upstream (who?) about this if they haven't fixed\n>  it already.\n> \n>  compat/fnmatch/fnmatch.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n> \n> diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c\n> index 9473aed..0ff1d27 100644\n> --- a/compat/fnmatch/fnmatch.c\n> +++ b/compat/fnmatch/fnmatch.c\n> @@ -345,7 +345,7 @@ internal_fnmatch (pattern, string, no_leading_period, flags)\n>  \n>  \t\t    for (;;)\n>  \t\t      {\n> -\t\t\tif (c1 == CHAR_CLASS_MAX_LENGTH)\n> +\t\t\tif (c1 > CHAR_CLASS_MAX_LENGTH)\n>  \t\t\t  /* The name is too long and therefore the pattern\n>  \t\t\t     is ill-formed.  */\n>  \t\t\t  return FNM_NOMATCH;\n> \n\n-- Hannes\n"},{"id":"203568","messageId":"7vd2z8rq4y.fsf@alter.siamese.dyndns.org","threadId":"31819","inReplyTo":"50AB2B0F.8090808@viscovery.net","subject":"Re: [PATCH 14/13] test-wildmatch: avoid Windows path mangling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-20T20:11:25Z","receivedAt":"2012-11-20T20:11:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n> From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>\n> The MSYS bash mangles arguments that begin with a forward slash\n> when they are passed to test-wildmatch. This causes tests to fail.\n> Avoid mangling by prepending \"XXX\", which is removed by\n> test-wildmatch before further processing.\n>\n> [J6t: reworded commit message]\n>\n> Reported-by: Johannes Sixt <j6t@kdbg.org>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  Works well, and I'm fine with this work-around.\n>\n>  -- Hannes\n\nThanks, but you need to fix your format-patch somehow.\n\n> @@ -187,8 +187,8 @@ match 0 0 '-' '[[-\\]]'\n>  match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n>  match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n>  match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n> -match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n> -match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n> +match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n> +match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n>  match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n>  match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n>  diff --git a/test-wildmatch.c b/test-wildmatch.c\n\nThis hunk claims that there are 8 lines in preimage and postimage,\nbut it is not the case.  It has only 7 lines each.\n\nYou also have the first line of the next patch \"diff --git\" somehow\nindented.  How did this happen?\n\n\n> index 74c0864..e384c8e 100644\n> --- a/test-wildmatch.c\n> +++ b/test-wildmatch.c\n> @@ -3,6 +3,14 @@\n>   int main(int argc, char **argv)\n>  {\n> +\tint i;\n> +\tfor (i = 2; i < argc; i++) {\n> +\t\tif (argv[i][0] == '/')\n> +\t\t\tdie(\"Forward slash is not allowed at the beginning of the\\n\"\n> +\t\t\t    \"pattern because Windows does not like it. Use `XXX/' instead.\");\n> +\t\telse if (!strncmp(argv[i], \"XXX/\", 4))\n> +\t\t\targv[i] += 3;\n> +\t}\n>  \tif (!strcmp(argv[1], \"wildmatch\"))\n>  \t\treturn !!wildmatch(argv[3], argv[2], 0);\n>  \telse if (!strcmp(argv[1], \"iwildmatch\"))\n\nAnd again this claims that the preimage has 6 lines while the\npostimage has 14.  Somebody is overcounting, or perhaps you removed\nthe first pre-context by hand without adjusting the line number?\n"},{"id":"203596","messageId":"50AC77A9.90601@viscovery.net","threadId":"31819","inReplyTo":"7vd2z8rq4y.fsf@alter.siamese.dyndns.org","subject":"[PATCH 14/13] test-wildmatch: avoid Windows path mangling","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-11-21T06:41:45Z","receivedAt":"2012-11-21T06:41:45Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThe MSYS bash mangles arguments that begin with a forward slash\nwhen they are passed to test-wildmatch. This causes tests to fail.\nAvoid mangling by prepending \"XXX\", which is removed by\ntest-wildmatch before further processing.\n\n[J6t: reworded commit message]\n\nReported-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\nAm 11/20/2012 21:11, schrieb Junio C Hamano:\n> Thanks, but you need to fix your format-patch somehow.\n\nNo, it was Thunderbird. It ate the blank lines when I said \"Edit as new\"\nto forward the patch directly from the mailbox.\n\nSorry for the inconvenience.\n\n t/t3070-wildmatch.sh | 10 +++++-----\n test-wildmatch.c     |  8 ++++++++\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex e6ad6f4..3155eab 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -74,7 +74,7 @@ match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 0 'foo' '**/foo'\n-match 1 x '/foo' '**/foo'\n+match 1 x 'XXX/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\n match 0 0 'foo/bar/baz' '**/bar*'\n@@ -95,8 +95,8 @@ match 0 0 ']' '[!]-]'\n match 1 x 'a' '[!]-]'\n match 0 0 '' '\\'\n match 0 x '\\' '\\'\n-match 0 x '/\\' '*/\\'\n-match 1 x '/\\' '*/\\\\'\n+match 0 x 'XXX/\\' '*/\\'\n+match 1 x 'XXX/\\' '*/\\\\'\n match 1 1 'foo' 'foo'\n match 1 1 '@foo' '@foo'\n match 0 0 'foo' '@foo'\n@@ -187,8 +187,8 @@ match 0 0 '-' '[[-\\]]'\n match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n-match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n-match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n \ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 74c0864..e384c8e 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -3,6 +3,14 @@\n \n int main(int argc, char **argv)\n {\n+\tint i;\n+\tfor (i = 2; i < argc; i++) {\n+\t\tif (argv[i][0] == '/')\n+\t\t\tdie(\"Forward slash is not allowed at the beginning of the\\n\"\n+\t\t\t    \"pattern because Windows does not like it. Use `XXX/' instead.\");\n+\t\telse if (!strncmp(argv[i], \"XXX/\", 4))\n+\t\t\targv[i] += 3;\n+\t}\n \tif (!strcmp(argv[1], \"wildmatch\"))\n \t\treturn !!wildmatch(argv[3], argv[2], 0);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-- \n1.8.0.1417.gf6038d8\n"},{"id":"204994","messageId":"20121217001013.GB3673@gmail.com","threadId":"31819","inReplyTo":"7vmwzmtmyd.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 12/12] Add git-check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-12-17T00:10:13Z","receivedAt":"2012-12-17T00:10:13Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Tue, Oct 16, 2012 at 09:12:58AM -0700, Junio C Hamano wrote:\n> Adam Spiers <git@adamspiers.org> writes:\n> > On Mon, Oct 15, 2012 at 3:31 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> >> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> >>> +For each pathname given via the command-line or from a file via\n> >>> +`--stdin`, this command will list the first exclude pattern found (if\n> >>> +any) which explicitly excludes or includes that pathname.  Note that\n> >>> +within any given exclude file, later patterns take precedence over\n> >>> +earlier ones, so any matching pattern which this command outputs may\n> >>> +not be the one you would immediately expect.\n> >>\n> >> \"The first exclude pattern\" is very misleading, isn't it?\n> >\n> > I don't think so, because of the second sentence.\n> >\n> >> For example, with these in $GIT_DIR/info/exclude, I would get:\n> >>\n> >>         $ cat -n .git/info/exclude\n> >>           1 *~\n> >>           2 Makefile~\n> >>         $ git check-ignore -v Makefile~\n> >>         .git/info/exclude:2:Makefile~   Makefile~\n> >>\n> >> which is the correct result (the last one in a single source decides\n> >> the fate of the path), but it hardly is \"first one found\" and the\n> >> matching pattern in the output would not be something unexpected for\n> >> the users, either.\n> >>\n> >> The reason it is \"the first one found\" is because the implementation\n> >> arranges the loop in such a way that it can stop early when it finds\n> >> a match---it simply checks matches from the end of the source.\n> >>\n> >> But that is not visible to end-users,\n> >\n> > Correct; that's precisely why I wrote the second sentence which\n> > explicitly explains this.\n> >\n> >> and they will find the above description just wrong, no?\n> >\n> > It's not wrong AFAICS, but suggestions for rewording this more clearly\n> > are of course welcome.  Maybe s/immediately/intuitively/ ?\n> \n> I think this is sufficient:\n> \n> \tFor each pathname given via the command-line or from a file\n> \tvia `--stdin`, show the pattern from .gitignore (or other\n> \tinput files to the exclude mechanism) that decides if the\n> \tpathname is excluded.\n> \n> and without \"Note that\" at all.\n\nI don't think this is quite sufficient.  Firstly, it does not cover\nnegated patterns (my original text contained \"or includes\").\n\nSecondly, I think there is still potential for this rewording to\nresult in confused users.  If the \"backwards-ness\" of the internal\nalgorithm is kept hidden from them, then in your example above, most\nusers would be more likely to intuitively expect check-ignore to\nreturn the first line of .git/info/exclude (\"*~\").  When they saw it\nreturning the second, they might draw the conclusion that the first\nline failed to match (e.g. by mistakenly thinking that the file format\nrequires regular expressions rather than globs), rather than that git\nstarts at the end of the file.\n\nThis is precisely why I chose not to hide this aspect of the\nimplementation when initially writing this documentation.\nUnfortunately my wording managed to confuse several of you, so clearly\nit was not adequate.  Therefore I propose an extension of your\nversion:\n\n\tFor each pathname given via the command-line or from a file\n\tvia `--stdin`, show the pattern from .gitignore (or other\n\tinput files to the exclude mechanism) that decides if the\n\tpathname is excluded or included.  Later patterns within a\n\tfile take precedence over earlier ones.\n"}]}