git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore
@ 2012-09-15 12:01 Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
                   ` (6 more replies)
  0 siblings, 7 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:01 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

A while back I posted a proof of concept using wildmatch() from rsync.
Back then the obstacle was case-insensitive matching support. I did not
realize that there was iwildmatch() that does exactly that.

So here again a series that is probably ready for consumption. Now
patterns that contain slashes will always go through (i)wildmatch().
fnmatch() is only used for basename matching.

Nguyễn Thái Ngọc Duy (5):
  Import wildmatch from rsync
  compat/wildmatch: remove static variable force_lower_case
  compat/wildmatch: fix case-insensitive matching
  Integrate wildmatch to git
  Support "**" in .gitignore and .gitattributes patterns using
    wildmatch()

 Documentation/gitignore.txt    |   3 +
 Makefile                       |   6 +
 attr.c                         |   4 +-
 compat/wildmatch.c             | 373 +++++++++++++++++++++++++++++++++++++++++
 compat/wildmatch.h             |   6 +
 dir.c                          |   5 +-
 t/t3070-wildmatch.sh           |  27 +++
 t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++
 test-wildmatch.c               | 228 +++++++++++++++++++++++++
 9 files changed, 815 insertions(+), 2 deletions(-)
 create mode 100644 compat/wildmatch.c
 create mode 100644 compat/wildmatch.h
 create mode 100755 t/t3070-wildmatch.sh
 create mode 100644 t/t3070-wildmatch/wildtest.txt
 create mode 100644 test-wildmatch.c

-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 1/5] Import wildmatch from rsync
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
@ 2012-09-15 12:02 ` Nguyễn Thái Ngọc Duy
  2012-09-16  6:49   ` Junio C Hamano
  2012-09-15 12:02 ` [PATCH 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 22884 bytes --]

These files are from rsync.git commit
d51a3adb4fca3e6b1b046c6e570828f3bca8fe36. The commit is GPL-3. However
wildmatch.[ch] have not changed since rsync turned to GPL-3.

rsync.git           ->  git.git
lib/wildmatch.[ch]      compat/wildmatch.[ch]
wildtest.c              test-wildmatch.c
wildtest.txt            t/t3070-wildmatch/wildtest.txt

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 compat/wildmatch.c             | 368 +++++++++++++++++++++++++++++++++++++++++
 compat/wildmatch.h             |   6 +
 t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++
 test-wildmatch.c               | 222 +++++++++++++++++++++++++
 4 files changed, 761 insertions(+)
 create mode 100644 compat/wildmatch.c
 create mode 100644 compat/wildmatch.h
 create mode 100644 t/t3070-wildmatch/wildtest.txt
 create mode 100644 test-wildmatch.c

diff --git a/compat/wildmatch.c b/compat/wildmatch.c
new file mode 100644
index 0000000..f3a1731
--- /dev/null
+++ b/compat/wildmatch.c
@@ -0,0 +1,368 @@
+/*
+**  Do shell-style pattern matching for ?, \, [], and * characters.
+**  It is 8bit clean.
+**
+**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+**  Rich $alz is now <rsalz@bbn.com>.
+**
+**  Modified by Wayne Davison to special-case '/' matching, to make '**'
+**  work differently than '*', and to fix the character-class code.
+*/
+
+#include "rsync.h"
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS	'!'
+#define NEGATE_CLASS2	'^'
+
+#define FALSE 0
+#define TRUE 1
+#define ABORT_ALL -1
+#define ABORT_TO_STARSTAR -2
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+				    && *(class) == *(litmatch) \
+				    && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifdef WILD_TEST_ITERATIONS
+int wildmatch_iteration_count;
+#endif
+
+static int force_lower_case = 0;
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+{
+    uchar p_ch;
+
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count++;
+#endif
+
+    for ( ; (p_ch = *p) != '\0'; text++, p++) {
+	int matched, special;
+	uchar t_ch, prev_ch;
+	while ((t_ch = *text) == '\0') {
+	    if (*a == NULL) {
+		if (p_ch != '*')
+		    return ABORT_ALL;
+		break;
+	    }
+	    text = *a++;
+	}
+	if (force_lower_case && ISUPPER(t_ch))
+	    t_ch = tolower(t_ch);
+	switch (p_ch) {
+	  case '\\':
+	    /* Literal match with following character.  Note that the test
+	     * in "default" handles the p[1] == '\0' failure case. */
+	    p_ch = *++p;
+	    /* FALLTHROUGH */
+	  default:
+	    if (t_ch != p_ch)
+		return FALSE;
+	    continue;
+	  case '?':
+	    /* Match anything but '/'. */
+	    if (t_ch == '/')
+		return FALSE;
+	    continue;
+	  case '*':
+	    if (*++p == '*') {
+		while (*++p == '*') {}
+		special = TRUE;
+	    } else
+		special = FALSE;
+	    if (*p == '\0') {
+		/* Trailing "**" matches everything.  Trailing "*" matches
+		 * only if there are no more slash characters. */
+		if (!special) {
+		    do {
+			if (strchr((char*)text, '/') != NULL)
+			    return FALSE;
+		    } while ((text = *a++) != NULL);
+		}
+		return TRUE;
+	    }
+	    while (1) {
+		if (t_ch == '\0') {
+		    if ((text = *a++) == NULL)
+			break;
+		    t_ch = *text;
+		    continue;
+		}
+		if ((matched = dowild(p, text, a)) != FALSE) {
+		    if (!special || matched != ABORT_TO_STARSTAR)
+			return matched;
+		} else if (!special && t_ch == '/')
+		    return ABORT_TO_STARSTAR;
+		t_ch = *++text;
+	    }
+	    return ABORT_ALL;
+	  case '[':
+	    p_ch = *++p;
+#ifdef NEGATE_CLASS2
+	    if (p_ch == NEGATE_CLASS2)
+		p_ch = NEGATE_CLASS;
+#endif
+	    /* Assign literal TRUE/FALSE because of "matched" comparison. */
+	    special = p_ch == NEGATE_CLASS? TRUE : FALSE;
+	    if (special) {
+		/* Inverted character class. */
+		p_ch = *++p;
+	    }
+	    prev_ch = 0;
+	    matched = FALSE;
+	    do {
+		if (!p_ch)
+		    return ABORT_ALL;
+		if (p_ch == '\\') {
+		    p_ch = *++p;
+		    if (!p_ch)
+			return ABORT_ALL;
+		    if (t_ch == p_ch)
+			matched = TRUE;
+		} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+		    p_ch = *++p;
+		    if (p_ch == '\\') {
+			p_ch = *++p;
+			if (!p_ch)
+			    return ABORT_ALL;
+		    }
+		    if (t_ch <= p_ch && t_ch >= prev_ch)
+			matched = TRUE;
+		    p_ch = 0; /* This makes "prev_ch" get set to 0. */
+		} else if (p_ch == '[' && p[1] == ':') {
+		    const uchar *s;
+		    int i;
+		    for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+		    if (!p_ch)
+			return ABORT_ALL;
+		    i = p - s - 1;
+		    if (i < 0 || p[-1] != ':') {
+			/* Didn't find ":]", so treat like a normal set. */
+			p = s - 2;
+			p_ch = '[';
+			if (t_ch == p_ch)
+			    matched = TRUE;
+			continue;
+		    }
+		    if (CC_EQ(s,i, "alnum")) {
+			if (ISALNUM(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "alpha")) {
+			if (ISALPHA(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "blank")) {
+			if (ISBLANK(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "cntrl")) {
+			if (ISCNTRL(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "digit")) {
+			if (ISDIGIT(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "graph")) {
+			if (ISGRAPH(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "lower")) {
+			if (ISLOWER(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "print")) {
+			if (ISPRINT(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "punct")) {
+			if (ISPUNCT(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "space")) {
+			if (ISSPACE(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "upper")) {
+			if (ISUPPER(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "xdigit")) {
+			if (ISXDIGIT(t_ch))
+			    matched = TRUE;
+		    } else /* malformed [:class:] string */
+			return ABORT_ALL;
+		    p_ch = 0; /* This makes "prev_ch" get set to 0. */
+		} else if (t_ch == p_ch)
+		    matched = TRUE;
+	    } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+	    if (matched == special || t_ch == '/')
+		return FALSE;
+	    continue;
+	}
+    }
+
+    do {
+	if (*text)
+	    return FALSE;
+    } while ((text = *a++) != NULL);
+
+    return TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
+{
+    for ( ; *s != '\0'; text++, s++) {
+	while (*text == '\0') {
+	    if ((text = *a++) == NULL)
+		return FALSE;
+	}
+	if (*text != *s)
+	    return FALSE;
+    }
+
+    do {
+	if (*text)
+	    return FALSE;
+    } while ((text = *a++) != NULL);
+
+    return TRUE;
+}
+
+/* Return the last "count" path elements from the concatenated string.
+ * We return a string pointer to the start of the string, and update the
+ * array pointer-pointer to point to any remaining string elements. */
+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
+{
+    const uchar*const *a = *a_ptr;
+    const uchar*const *first_a = a;
+
+    while (*a)
+	    a++;
+
+    while (a != first_a) {
+	const uchar *s = *--a;
+	s += strlen((char*)s);
+	while (--s >= *a) {
+	    if (*s == '/' && !--count) {
+		*a_ptr = a+1;
+		return s+1;
+	    }
+	}
+    }
+
+    if (count == 1) {
+	*a_ptr = a+1;
+	return *a;
+    }
+
+    return NULL;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text)
+{
+    static const uchar *nomore[1]; /* A NULL pointer. */
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count = 0;
+#endif
+    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+}
+
+/* Match the "pattern" against the forced-to-lower-case "text" string. */
+int iwildmatch(const char *pattern, const char *text)
+{
+    static const uchar *nomore[1]; /* A NULL pointer. */
+    int ret;
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count = 0;
+#endif
+    force_lower_case = 1;
+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+    force_lower_case = 0;
+    return ret;
+}
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end).  The int "where" can be 0 (normal matching), > 0 (match only
+ * the trailing N slash-separated filename components of "texts"), or < 0
+ * (match the "pattern" at the start or after any slash in "texts"). */
+int wildmatch_array(const char *pattern, const char*const *texts, int where)
+{
+    const uchar *p = (const uchar*)pattern;
+    const uchar*const *a = (const uchar*const*)texts;
+    const uchar *text;
+    int matched;
+
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count = 0;
+#endif
+
+    if (where > 0)
+	text = trailing_N_elements(&a, where);
+    else
+	text = *a++;
+    if (!text)
+	return FALSE;
+
+    if ((matched = dowild(p, text, a)) != TRUE && where < 0
+     && matched != ABORT_ALL) {
+	while (1) {
+	    if (*text == '\0') {
+		if ((text = (uchar*)*a++) == NULL)
+		    return FALSE;
+		continue;
+	    }
+	    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+	     && matched != ABORT_TO_STARSTAR)
+		break;
+	}
+    }
+    return matched == TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end).  The int "where" can be 0 (normal matching), or > 0 (match
+ * only the trailing N slash-separated filename components of "texts"). */
+int litmatch_array(const char *string, const char*const *texts, int where)
+{
+    const uchar *s = (const uchar*)string;
+    const uchar*const *a = (const uchar* const*)texts;
+    const uchar *text;
+
+    if (where > 0)
+	text = trailing_N_elements(&a, where);
+    else
+	text = *a++;
+    if (!text)
+	return FALSE;
+
+    return doliteral(s, text, a) == TRUE;
+}
diff --git a/compat/wildmatch.h b/compat/wildmatch.h
new file mode 100644
index 0000000..e7f1a35
--- /dev/null
+++ b/compat/wildmatch.h
@@ -0,0 +1,6 @@
+/* wildmatch.h */
+
+int wildmatch(const char *pattern, const char *text);
+int iwildmatch(const char *pattern, const char *text);
+int wildmatch_array(const char *pattern, const char*const *texts, int where);
+int litmatch_array(const char *string, const char*const *texts, int where);
diff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070-wildmatch/wildtest.txt
new file mode 100644
index 0000000..42c1678
--- /dev/null
+++ b/t/t3070-wildmatch/wildtest.txt
@@ -0,0 +1,165 @@
+# Input is in the following format (all items white-space separated):
+#
+# The first two items are 1 or 0 indicating if the wildmat call is expected to
+# succeed and if fnmatch works the same way as wildmat, respectively.  After
+# that is a text string for the match, and a pattern string.  Strings can be
+# quoted (if desired) in either double or single quotes, as well as backticks.
+#
+# MATCH FNMATCH_SAME "text to match" 'pattern to use'
+
+# Basic wildmat features
+1 1 foo			foo
+0 1 foo			bar
+1 1 ''			""
+1 1 foo			???
+0 1 foo			??
+1 1 foo			*
+1 1 foo			f*
+0 1 foo			*f
+1 1 foo			*foo*
+1 1 foobar		*ob*a*r*
+1 1 aaaaaaabababab	*ab
+1 1 foo*		foo\*
+0 1 foobar		foo\*bar
+1 1 f\oo		f\\oo
+1 1 ball		*[al]?
+0 1 ten			[ten]
+1 1 ten			**[!te]
+0 1 ten			**[!ten]
+1 1 ten			t[a-g]n
+0 1 ten			t[!a-g]n
+1 1 ton			t[!a-g]n
+1 1 ton			t[^a-g]n
+1 1 a]b			a[]]b
+1 1 a-b			a[]-]b
+1 1 a]b			a[]-]b
+0 1 aab			a[]-]b
+1 1 aab			a[]a-]b
+1 1 ]			]
+
+# Extended slash-matching features
+0 1 foo/baz/bar		foo*bar
+1 1 foo/baz/bar		foo**bar
+0 1 foo/bar		foo?bar
+0 1 foo/bar		foo[/]bar
+0 1 foo/bar		f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+1 1 foo-bar		f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+0 1 foo			**/foo
+1 1 /foo		**/foo
+1 1 bar/baz/foo		**/foo
+0 1 bar/baz/foo		*/foo
+0 0 foo/bar/baz		**/bar*
+1 1 deep/foo/bar/baz	**/bar/*
+0 1 deep/foo/bar/baz/	**/bar/*
+1 1 deep/foo/bar/baz/	**/bar/**
+0 1 deep/foo/bar	**/bar/*
+1 1 deep/foo/bar/	**/bar/**
+1 1 foo/bar/baz		**/bar**
+1 1 foo/bar/baz/x	*/bar/**
+0 0 deep/foo/bar/baz/x	*/bar/**
+1 1 deep/foo/bar/baz/x	**/bar/*/*
+
+# Various additional tests
+0 1 acrt		a[c-c]st
+1 1 acrt		a[c-c]rt
+0 1 ]			[!]-]
+1 1 a			[!]-]
+0 1 ''			\
+0 1 \			\
+0 1 /\			*/\
+1 1 /\			*/\\
+1 1 foo			foo
+1 1 @foo		@foo
+0 1 foo			@foo
+1 1 [ab]		\[ab]
+1 1 [ab]		[[]ab]
+1 1 [ab]		[[:]ab]
+0 1 [ab]		[[::]ab]
+1 1 [ab]		[[:digit]ab]
+1 1 [ab]		[\[:]ab]
+1 1 ?a?b		\??\?b
+1 1 abc			\a\b\c
+0 1 foo			''
+1 1 foo/bar/baz/to	**/t[o]
+
+# Character class tests
+1 1 a1B		[[:alpha:]][[:digit:]][[:upper:]]
+0 1 a		[[:digit:][:upper:][:space:]]
+1 1 A		[[:digit:][:upper:][:space:]]
+1 1 1		[[:digit:][:upper:][:space:]]
+0 1 1		[[:digit:][:upper:][:spaci:]]
+1 1 ' '		[[:digit:][:upper:][:space:]]
+0 1 .		[[:digit:][:upper:][:space:]]
+1 1 .		[[:digit:][:punct:][:space:]]
+1 1 5		[[:xdigit:]]
+1 1 f		[[:xdigit:]]
+1 1 D		[[:xdigit:]]
+1 1 _		[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+#1 1 …		[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 \x7f		[^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 .		[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]
+1 1 5		[a-c[:digit:]x-z]
+1 1 b		[a-c[:digit:]x-z]
+1 1 y		[a-c[:digit:]x-z]
+0 1 q		[a-c[:digit:]x-z]
+
+# Additional tests, including some malformed wildmats
+1 1 ]		[\\-^]
+0 1 [		[\\-^]
+1 1 -		[\-_]
+1 1 ]		[\]]
+0 1 \]		[\]]
+0 1 \		[\]]
+0 1 ab		a[]b
+0 1 a[]b	a[]b
+0 1 ab[		ab[
+0 1 ab		[!
+0 1 ab		[-
+1 1 -		[-]
+0 1 -		[a-
+0 1 -		[!a-
+1 1 -		[--A]
+1 1 5		[--A]
+1 1 ' '		'[ --]'
+1 1 $		'[ --]'
+1 1 -		'[ --]'
+0 1 0		'[ --]'
+1 1 -		[---]
+1 1 -		[------]
+0 1 j		[a-e-n]
+1 1 -		[a-e-n]
+1 1 a		[!------]
+0 1 [		[]-a]
+1 1 ^		[]-a]
+0 1 ^		[!]-a]
+1 1 [		[!]-a]
+1 1 ^		[a^bc]
+1 1 -b]		[a-]b]
+0 1 \		[\]
+1 1 \		[\\]
+0 1 \		[!\\]
+1 1 G		[A-\\]
+0 1 aaabbb	b*a
+0 1 aabcaa	*ba*
+1 1 ,		[,]
+1 1 ,		[\\,]
+1 1 \		[\\,]
+1 1 -		[,-.]
+0 1 +		[,-.]
+0 1 -.]		[,-.]
+1 1 2		[\1-\3]
+1 1 3		[\1-\3]
+0 1 4		[\1-\3]
+1 1 \		[[-\]]
+1 1 [		[[-\]]
+1 1 ]		[[-\]]
+0 1 -		[[-\]]
+
+# Test recursion and the abort code (use "wildtest -i" to see iteration counts)
+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1	-*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1	-*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1	-*-*-*-*-*-*-12-*-*-*-m-*-*-*
+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1	/*/*/*/*/*/*/12/*/*/*/m/*/*/*
+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1	/*/*/*/*/*/*/12/*/*/*/m/*/*/*
+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt		**/*a*b*g*n*t
+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz		**/*a*b*g*n*t
diff --git a/test-wildmatch.c b/test-wildmatch.c
new file mode 100644
index 0000000..88585c2
--- /dev/null
+++ b/test-wildmatch.c
@@ -0,0 +1,222 @@
+/*
+ * Test suite for the wildmatch code.
+ *
+ * Copyright (C) 2003-2009 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/*#define COMPARE_WITH_FNMATCH*/
+
+#define WILD_TEST_ITERATIONS
+#include "lib/wildmatch.c"
+
+#include <popt.h>
+
+#ifdef COMPARE_WITH_FNMATCH
+#include <fnmatch.h>
+
+int fnmatch_errors = 0;
+#endif
+
+int wildmatch_errors = 0;
+char number_separator = ',';
+
+typedef char bool;
+
+int output_iterations = 0;
+int explode_mod = 0;
+int empties_mod = 0;
+int empty_at_start = 0;
+int empty_at_end = 0;
+
+static struct poptOption long_options[] = {
+  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+  {"iterations",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},
+  {"empties",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},
+  {"explode",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},
+  {0,0,0,0, 0, 0, 0}
+};
+
+/* match just at the start of string (anchored tests) */
+static void
+run_test(int line, bool matches,
+#ifdef COMPARE_WITH_FNMATCH
+	 bool same_as_fnmatch,
+#endif
+	 const char *text, const char *pattern)
+{
+    bool matched;
+#ifdef COMPARE_WITH_FNMATCH
+    bool fn_matched;
+    int flags = strstr(pattern, "**")? 0 : FNM_PATHNAME;
+#endif
+
+    if (explode_mod) {
+	char buf[MAXPATHLEN*2], *texts[MAXPATHLEN];
+	int pos = 0, cnt = 0, ndx = 0, len = strlen(text);
+
+	if (empty_at_start)
+	    texts[ndx++] = "";
+	/* An empty string must turn into at least one empty array item. */
+	while (1) {
+	    texts[ndx] = buf + ndx * (explode_mod + 1);
+	    strlcpy(texts[ndx++], text + pos, explode_mod + 1);
+	    if (pos + explode_mod >= len)
+		break;
+	    pos += explode_mod;
+	    if (!(++cnt % empties_mod))
+		texts[ndx++] = "";
+	}
+	if (empty_at_end)
+	    texts[ndx++] = "";
+	texts[ndx] = NULL;
+	matched = wildmatch_array(pattern, (const char**)texts, 0);
+    } else
+	matched = wildmatch(pattern, text);
+#ifdef COMPARE_WITH_FNMATCH
+    fn_matched = !fnmatch(pattern, text, flags);
+#endif
+    if (matched != matches) {
+	printf("wildmatch failure on line %d:\n  %s\n  %s\n  expected %s match\n",
+	       line, text, pattern, matches? "a" : "NO");
+	wildmatch_errors++;
+    }
+#ifdef COMPARE_WITH_FNMATCH
+    if (fn_matched != (matches ^ !same_as_fnmatch)) {
+	printf("fnmatch disagreement on line %d:\n  %s\n  %s\n  expected %s match\n",
+	       line, text, pattern, matches ^ !same_as_fnmatch? "a" : "NO");
+	fnmatch_errors++;
+    }
+#endif
+    if (output_iterations) {
+	printf("%d: \"%s\" iterations = %d\n", line, pattern,
+	       wildmatch_iteration_count);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    char buf[2048], *s, *string[2], *end[2];
+    const char *arg;
+    FILE *fp;
+    int opt, line, i, flag[2];
+    poptContext pc = poptGetContext("wildtest", argc, (const char**)argv,
+				    long_options, 0);
+
+    while ((opt = poptGetNextOpt(pc)) != -1) {
+	switch (opt) {
+	  case 'e':
+	    arg = poptGetOptArg(pc);
+	    empties_mod = atoi(arg);
+	    if (strchr(arg, 's'))
+		empty_at_start = 1;
+	    if (strchr(arg, 'e'))
+		empty_at_end = 1;
+	    if (!explode_mod)
+		explode_mod = 1024;
+	    break;
+	  default:
+	    fprintf(stderr, "%s: %s\n",
+		    poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+		    poptStrerror(opt));
+	    exit(1);
+	}
+    }
+
+    if (explode_mod && !empties_mod)
+	empties_mod = 1024;
+
+    argv = (char**)poptGetArgs(pc);
+    if (!argv || argv[1]) {
+	fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n");
+	exit(1);
+    }
+
+    if ((fp = fopen(*argv, "r")) == NULL) {
+	fprintf(stderr, "Unable to open %s\n", *argv);
+	exit(1);
+    }
+
+    line = 0;
+    while (fgets(buf, sizeof buf, fp)) {
+	line++;
+	if (*buf == '#' || *buf == '\n')
+	    continue;
+	for (s = buf, i = 0; i <= 1; i++) {
+	    if (*s == '1')
+		flag[i] = 1;
+	    else if (*s == '0')
+		flag[i] = 0;
+	    else
+		flag[i] = -1;
+	    if (*++s != ' ' && *s != '\t')
+		flag[i] = -1;
+	    if (flag[i] < 0) {
+		fprintf(stderr, "Invalid flag syntax on line %d of %s:\n%s",
+			line, *argv, buf);
+		exit(1);
+	    }
+	    while (*++s == ' ' || *s == '\t') {}
+	}
+	for (i = 0; i <= 1; i++) {
+	    if (*s == '\'' || *s == '"' || *s == '`') {
+		char quote = *s++;
+		string[i] = s;
+		while (*s && *s != quote) s++;
+		if (!*s) {
+		    fprintf(stderr, "Unmatched quote on line %d of %s:\n%s",
+			    line, *argv, buf);
+		    exit(1);
+		}
+		end[i] = s;
+	    }
+	    else {
+		if (!*s || *s == '\n') {
+		    fprintf(stderr, "Not enough strings on line %d of %s:\n%s",
+			    line, *argv, buf);
+		    exit(1);
+		}
+		string[i] = s;
+		while (*++s && *s != ' ' && *s != '\t' && *s != '\n') {}
+		end[i] = s;
+	    }
+	    while (*++s == ' ' || *s == '\t') {}
+	}
+	*end[0] = *end[1] = '\0';
+	run_test(line, flag[0],
+#ifdef COMPARE_WITH_FNMATCH
+		 flag[1],
+#endif
+		 string[0], string[1]);
+    }
+
+    if (!wildmatch_errors)
+	fputs("No", stdout);
+    else
+	printf("%d", wildmatch_errors);
+    printf(" wildmatch error%s found.\n", wildmatch_errors == 1? "" : "s");
+
+#ifdef COMPARE_WITH_FNMATCH
+    if (!fnmatch_errors)
+	fputs("No", stdout);
+    else
+	printf("%d", fnmatch_errors);
+    printf(" fnmatch error%s found.\n", fnmatch_errors == 1? "" : "s");
+
+#endif
+
+    return 0;
+}
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 2/5] compat/wildmatch: remove static variable force_lower_case
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
@ 2012-09-15 12:02 ` Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

One place less to worry about thread safety

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 compat/wildmatch.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/compat/wildmatch.c b/compat/wildmatch.c
index f3a1731..e824eb2 100644
--- a/compat/wildmatch.c
+++ b/compat/wildmatch.c
@@ -57,11 +57,10 @@
 int wildmatch_iteration_count;
 #endif
 
-static int force_lower_case = 0;
-
 /* Match pattern "p" against the a virtually-joined string consisting
  * of "text" and any strings in array "a". */
-static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+static int dowild(const uchar *p, const uchar *text,
+		  const uchar*const *a, int force_lower_case)
 {
     uchar p_ch;
 
@@ -121,7 +120,7 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
 		    t_ch = *text;
 		    continue;
 		}
-		if ((matched = dowild(p, text, a)) != FALSE) {
+		if ((matched = dowild(p, text, a, force_lower_case)) != FALSE) {
 		    if (!special || matched != ABORT_TO_STARSTAR)
 			return matched;
 		} else if (!special && t_ch == '/')
@@ -291,7 +290,7 @@ int wildmatch(const char *pattern, const char *text)
 #ifdef WILD_TEST_ITERATIONS
     wildmatch_iteration_count = 0;
 #endif
-    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+    return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;
 }
 
 /* Match the "pattern" against the forced-to-lower-case "text" string. */
@@ -302,9 +301,7 @@ int iwildmatch(const char *pattern, const char *text)
 #ifdef WILD_TEST_ITERATIONS
     wildmatch_iteration_count = 0;
 #endif
-    force_lower_case = 1;
-    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
-    force_lower_case = 0;
+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;
     return ret;
 }
 
@@ -331,7 +328,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
     if (!text)
 	return FALSE;
 
-    if ((matched = dowild(p, text, a)) != TRUE && where < 0
+    if ((matched = dowild(p, text, a, 0)) != TRUE && where < 0
      && matched != ABORT_ALL) {
 	while (1) {
 	    if (*text == '\0') {
@@ -339,7 +336,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
 		    return FALSE;
 		continue;
 	    }
-	    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+	    if (*text++ == '/' && (matched = dowild(p, text, a, 0)) != FALSE
 	     && matched != ABORT_TO_STARSTAR)
 		break;
 	}
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 3/5] compat/wildmatch: fix case-insensitive matching
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
@ 2012-09-15 12:02 ` Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

dowild() does case insensitive matching by lower-casing the text. That
means lower case letters in patterns imply case-insensitive matching,
but upper case means exact matching.

We do not want that subtlety. Lower case pattern too so iwildmatch()
always does what we expect it to do.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 compat/wildmatch.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/compat/wildmatch.c b/compat/wildmatch.c
index e824eb2..c7f7f9f 100644
--- a/compat/wildmatch.c
+++ b/compat/wildmatch.c
@@ -81,6 +81,8 @@ static int dowild(const uchar *p, const uchar *text,
 	}
 	if (force_lower_case && ISUPPER(t_ch))
 	    t_ch = tolower(t_ch);
+	if (force_lower_case && ISUPPER(p_ch))
+	    p_ch = tolower(p_ch);
 	switch (p_ch) {
 	  case '\\':
 	    /* Literal match with following character.  Note that the test
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 4/5] Integrate wildmatch to git
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2012-09-15 12:02 ` [PATCH 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
@ 2012-09-15 12:02 ` Nguyễn Thái Ngọc Duy
  2012-09-15 12:02 ` [PATCH 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This makes wildmatch.c part of libgit.a and builds test-wildmatch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Makefile             |  6 ++++++
 compat/wildmatch.c   |  8 +++++++-
 t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++
 test-wildmatch.c     |  8 +++++++-
 4 files changed, 47 insertions(+), 2 deletions(-)
 create mode 100755 t/t3070-wildmatch.sh

diff --git a/Makefile b/Makefile
index 56301dc..c3608e6 100644
--- a/Makefile
+++ b/Makefile
@@ -511,6 +511,7 @@ TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-subprocess
 TEST_PROGRAMS_NEED_X += test-svn-fe
+TEST_PROGRAMS_NEED_X += test-wildmatch
 
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
@@ -605,6 +606,7 @@ LIB_H += compat/mingw.h
 LIB_H += compat/obstack.h
 LIB_H += compat/precompose_utf8.h
 LIB_H += compat/terminal.h
+LIB_H += compat/wildmatch.h
 LIB_H += compat/win32/dirent.h
 LIB_H += compat/win32/poll.h
 LIB_H += compat/win32/pthread.h
@@ -709,6 +711,7 @@ LIB_OBJS += combine-diff.o
 LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
 LIB_OBJS += compat/terminal.o
+LIB_OBJS += compat/wildmatch.o
 LIB_OBJS += config.o
 LIB_OBJS += connect.o
 LIB_OBJS += connected.o
@@ -2586,6 +2589,9 @@ test-svn-fe$X: vcs-svn/lib.a
 test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
+test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt
+
 check-sha1:: test-sha1$X
 	./test-sha1.sh
 
diff --git a/compat/wildmatch.c b/compat/wildmatch.c
index c7f7f9f..625cb0c 100644
--- a/compat/wildmatch.c
+++ b/compat/wildmatch.c
@@ -9,7 +9,13 @@
 **  work differently than '*', and to fix the character-class code.
 */
 
-#include "rsync.h"
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "wildmatch.h"
+
+typedef unsigned char uchar;
 
 /* What character marks an inverted character class? */
 #define NEGATE_CLASS	'!'
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
new file mode 100755
index 0000000..7fb63ff
--- /dev/null
+++ b/t/t3070-wildmatch.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='wildmatch tests'
+
+. ./test-lib.sh
+
+test_wildmatch() {
+    test_expect_success "wildmatch $*" "
+	test-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual &&
+	echo 'No wildmatch errors found.' >expected &&
+	test_cmp expected actual
+    "
+}
+
+test_wildmatch -x1
+test_wildmatch -x1 -e1
+test_wildmatch -x1 -else
+test_wildmatch -x2
+test_wildmatch -x2 -ese
+test_wildmatch -x3
+test_wildmatch -x3 -e1
+test_wildmatch -x4
+test_wildmatch -x4 -e2e
+test_wildmatch -x5
+test_wildmatch -x5 -es
+
+test_done
diff --git a/test-wildmatch.c b/test-wildmatch.c
index 88585c2..2c506a0 100644
--- a/test-wildmatch.c
+++ b/test-wildmatch.c
@@ -20,7 +20,13 @@
 /*#define COMPARE_WITH_FNMATCH*/
 
 #define WILD_TEST_ITERATIONS
-#include "lib/wildmatch.c"
+#include "compat/wildmatch.c"
+
+#define MAXPATHLEN 1024
+#ifdef NO_STRLCPY
+#include "compat/strlcpy.c"
+#define strlcpy gitstrlcpy
+#endif
 
 #include <popt.h>
 
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch()
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2012-09-15 12:02 ` [PATCH 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
@ 2012-09-15 12:02 ` Nguyễn Thái Ngọc Duy
  2012-09-15 14:27 ` How to create the " [PATCH 0/5]" first email? Philip Oakley
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
  6 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/gitignore.txt | 3 +++
 attr.c                      | 4 +++-
 dir.c                       | 5 ++++-
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index c1f692a..eb81d31 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -93,6 +93,9 @@ PATTERN FORMAT
    For example, "Documentation/{asterisk}.html" matches
    "Documentation/git.html" but not "Documentation/ppc/ppc.html"
    or "tools/perf/Documentation/perf.html".
++
+Contrary to fnmatch(3), git matches "**" to anything including
+slashes, similar to rsync(1).
 
  - A leading slash matches the beginning of the pathname.
    For example, "/{asterisk}.c" matches "cat-file.c" but not
diff --git a/attr.c b/attr.c
index 3430faf..f06ad95 100644
--- a/attr.c
+++ b/attr.c
@@ -12,6 +12,7 @@
 #include "exec_cmd.h"
 #include "attr.h"
 #include "dir.h"
+#include "compat/wildmatch.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen,
 		return 0;
 	if (baselen != 0)
 		baselen++;
-	return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+	return (ignore_case && iwildmatch(pattern, pathname + baselen)) ||
+		(!ignore_case && wildmatch(pattern, pathname + baselen));
 }
 
 static int macroexpand_one(int attr_nr, int rem);
diff --git a/dir.c b/dir.c
index 4868339..c17f9ff 100644
--- a/dir.c
+++ b/dir.c
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "refs.h"
+#include "compat/wildmatch.h"
 
 struct path_simplify {
 	int len;
@@ -575,7 +576,9 @@ int excluded_from_list(const char *pathname,
 			namelen -= prefix;
 		}
 
-		if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
+		if (!namelen ||
+		    ((ignore_case && iwildmatch(exclude, name)) ||
+		     (!ignore_case && wildmatch(exclude, name))))
 			return to_exclude;
 	}
 	return -1; /* undecided */
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* How to create the " [PATCH 0/5]" first email?
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                   ` (4 preceding siblings ...)
  2012-09-15 12:02 ` [PATCH 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
@ 2012-09-15 14:27 ` Philip Oakley
  2012-09-15 17:08   ` Junio C Hamano
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
  6 siblings, 1 reply; 34+ messages in thread
From: Philip Oakley @ 2012-09-15 14:27 UTC (permalink / raw)
  To: Git List

The git format-patch allows numbered patches to be created, however I'm
not sure how folk generate the initial "0/n" patch. Which of the various
options should I be using?

Also, being on windows, the various 'mbox' and 'MUA' discussions are new
to me, so the format-patch step where :-

"Typically it will be placed in a MUA’s drafts folder, edited to add
timely commentary that should not go in the changelog after the three
dashes, and then sent as a message whose body, in our example, starts
with "arch/arm config files were…". On the receiving end, readers can
save interesting patches in a UNIX mailbox and apply them with
git-am(1)."

hides a multitude of implicit knowledge steps. Is there an extended
description of what that would mean from a platform independent
viewpoint? e.g. if the patches are separte files and an mbox is one
consolidated file, how to get from one to the other so that 'it' can be
sent by 'git send-mail'.

I'm also missing an understanding of the preparation stage where one
tries to tidy up the various commit messages becaue they weren't
explicit, specfic nor concise enough, so I suspect that there is an
implicit `git format-patch` <-> `git am` loop of sharpening the mbox
patches before submission to the list that I'm missing. Has this 
described somewhere?

Philip Oakley

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-15 14:27 ` How to create the " [PATCH 0/5]" first email? Philip Oakley
@ 2012-09-15 17:08   ` Junio C Hamano
  2012-09-17 22:55     ` Philip Oakley
  2012-09-23 12:03     ` Jan Engelhardt
  0 siblings, 2 replies; 34+ messages in thread
From: Junio C Hamano @ 2012-09-15 17:08 UTC (permalink / raw)
  To: Philip Oakley; +Cc: Git List

"Philip Oakley" <philipoakley@iee.org> writes:

> "Typically it will be placed in a MUA’s drafts folder, edited to add
> timely commentary that should not go in the changelog after the three
> dashes, and then sent as a message whose body, in our example, starts
> with "arch/arm config files were…". On the receiving end, readers can
> save interesting patches in a UNIX mailbox and apply them with
> git-am(1)."
>
> hides a multitude of implicit knowledge steps. Is there an extended
> description of what that would mean from a platform independent
> viewpoint? e.g. if the patches are separte files and an mbox is one
> consolidated file, how to get from one to the other so that 'it' can be
> sent by 'git send-mail'.

If you plan to use "git send-email" to send the final results out,
you should consider "git send-email" as your "MUA" in the quoted
paragraph.  And that will be very platform independent viewpoint to
see things from.

"git format-patch -o my-series/ --cover-letter ..."  would treat
"my-series/" directory as "MUA's drafts folder" and prepares the
messages you would want to send out, and you can proof-read and edit
the files in there before telling your "MUA" to send them out, with
"git send-email ... my-series/*.patch" or something.

> I'm also missing an understanding of the preparation stage where one
> tries to tidy up the various commit messages becaue they weren't
> explicit, specfic nor concise enough,...

Many people usually do "rebase -i" until perfection and then a
single final invocation of "format-patch".  Of course, the "final"
can and should further be proof-read and it is fine to do typofixes
in the format-patch output files without going back to the commits
before sending them out.

> ...so I suspect that there is an
> implicit `git format-patch` <-> `git am` loop of sharpening the mbox
> patches before submission to the list that I'm missing. Has this 
> described somewhere?

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 1/5] Import wildmatch from rsync
  2012-09-15 12:02 ` [PATCH 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
@ 2012-09-16  6:49   ` Junio C Hamano
  0 siblings, 0 replies; 34+ messages in thread
From: Junio C Hamano @ 2012-09-16  6:49 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> These files are from rsync.git commit
> d51a3adb4fca3e6b1b046c6e570828f3bca8fe36. The commit is GPL-3. However
> wildmatch.[ch] have not changed since rsync turned to GPL-3.

I'd like see the last version of rsync that was GPLv2 hunted down
and the copy from that version lifted from it.  If you _know_ they
are identical, then you should be able to do that, no?

>  compat/wildmatch.c             | 368 +++++++++++++++++++++++++++++++++++++++++
>  compat/wildmatch.h             |   6 +

As this will be linked on all platforms, it shouldn't be in compat/
directory.  Unlike xdiff/ that has many files, this can live at the
top-level, just like kwset we borrowed from GNU grep does.

> diff --git a/compat/wildmatch.c b/compat/wildmatch.c
> new file mode 100644
> index 0000000..f3a1731
> --- /dev/null
> +++ b/compat/wildmatch.c
> @@ -0,0 +1,368 @@
> +/*
> +**  Do shell-style pattern matching for ?, \, [], and * characters.
> +**  It is 8bit clean.
> +**
> +**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
> +**  Rich $alz is now <rsalz@bbn.com>.
> +**
> +**  Modified by Wayne Davison to special-case '/' matching, to make '**'
> +**  work differently than '*', and to fix the character-class code.
> +*/
> +
> +#include "rsync.h"

Huh???

Ahh.  The approach you took is "This patch imports the pristine copy
to make it easier to verify the provenance, and the plan is to make
it usable with separate follow-up patches.", I agree that is a very
sensible thing to do.

But please say so in the commit log message.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore
  2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                   ` (5 preceding siblings ...)
  2012-09-15 14:27 ` How to create the " [PATCH 0/5]" first email? Philip Oakley
@ 2012-09-16 15:27 ` Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
                     ` (5 more replies)
  6 siblings, 6 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Changes in v2:

 - Correct commit sha-1 in the 1/5 to the last GPL-2 rsync commit.
   State it is pristine import. Move wildmatch.[ch] to top dir.
 - Add tests to 5/5 (I forgot to test that the integration should run
   well, in addition to wildmatch() working in general)

Note that there might be a regression because wildmatch() does not
care about LC_CTYPE when dealing with character classes, while libc's
fnmatch might. But it's probably not worth bothering about because we
match against path names, which do not necessarily follow LC_TYPE
but the file system's encoding.

Nguyễn Thái Ngọc Duy (5):
  Import wildmatch from rsync
  compat/wildmatch: remove static variable force_lower_case
  compat/wildmatch: fix case-insensitive matching
  Integrate wildmatch to git
  Support "**" in .gitignore and .gitattributes patterns using
    wildmatch()

 .gitignore                         |   1 +
 Documentation/gitignore.txt        |   3 +
 Makefile                           |   6 +
 attr.c                             |   4 +-
 dir.c                              |   5 +-
 t/t0003-attributes.sh              |  17 ++
 t/t3001-ls-files-others-exclude.sh |  11 ++
 t/t3070-wildmatch.sh               |  27 +++
 t/t3070-wildmatch/wildtest.txt     | 165 ++++++++++++++++
 test-wildmatch.c                   | 228 +++++++++++++++++++++++
 wildmatch.c                        | 373 +++++++++++++++++++++++++++++++++++++
 wildmatch.h                        |   6 +
 12 files changed, 844 insertions(+), 2 deletions(-)
 create mode 100755 t/t3070-wildmatch.sh
 create mode 100644 t/t3070-wildmatch/wildtest.txt
 create mode 100644 test-wildmatch.c
 create mode 100644 wildmatch.c
 create mode 100644 wildmatch.h

-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v2 1/5] Import wildmatch from rsync
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
@ 2012-09-16 15:27   ` Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 22865 bytes --]

These files are from rsync.git commit
f92f5b166e3019db42bc7fe1aa2f1a9178cd215d, which was the last commit
before rsync turned GPL-3. All files are imported as-is and
no-op. Adaptation is done in a separate patch.

rsync.git           ->  git.git
lib/wildmatch.[ch]      wildmatch.[ch]
wildtest.c              test-wildmatch.c
wildtest.txt            t/t3070-wildmatch/wildtest.txt

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++
 test-wildmatch.c               | 222 +++++++++++++++++++++++++
 wildmatch.c                    | 368 +++++++++++++++++++++++++++++++++++++++++
 wildmatch.h                    |   6 +
 4 files changed, 761 insertions(+)
 create mode 100644 t/t3070-wildmatch/wildtest.txt
 create mode 100644 test-wildmatch.c
 create mode 100644 wildmatch.c
 create mode 100644 wildmatch.h

diff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070-wildmatch/wildtest.txt
new file mode 100644
index 0000000..42c1678
--- /dev/null
+++ b/t/t3070-wildmatch/wildtest.txt
@@ -0,0 +1,165 @@
+# Input is in the following format (all items white-space separated):
+#
+# The first two items are 1 or 0 indicating if the wildmat call is expected to
+# succeed and if fnmatch works the same way as wildmat, respectively.  After
+# that is a text string for the match, and a pattern string.  Strings can be
+# quoted (if desired) in either double or single quotes, as well as backticks.
+#
+# MATCH FNMATCH_SAME "text to match" 'pattern to use'
+
+# Basic wildmat features
+1 1 foo			foo
+0 1 foo			bar
+1 1 ''			""
+1 1 foo			???
+0 1 foo			??
+1 1 foo			*
+1 1 foo			f*
+0 1 foo			*f
+1 1 foo			*foo*
+1 1 foobar		*ob*a*r*
+1 1 aaaaaaabababab	*ab
+1 1 foo*		foo\*
+0 1 foobar		foo\*bar
+1 1 f\oo		f\\oo
+1 1 ball		*[al]?
+0 1 ten			[ten]
+1 1 ten			**[!te]
+0 1 ten			**[!ten]
+1 1 ten			t[a-g]n
+0 1 ten			t[!a-g]n
+1 1 ton			t[!a-g]n
+1 1 ton			t[^a-g]n
+1 1 a]b			a[]]b
+1 1 a-b			a[]-]b
+1 1 a]b			a[]-]b
+0 1 aab			a[]-]b
+1 1 aab			a[]a-]b
+1 1 ]			]
+
+# Extended slash-matching features
+0 1 foo/baz/bar		foo*bar
+1 1 foo/baz/bar		foo**bar
+0 1 foo/bar		foo?bar
+0 1 foo/bar		foo[/]bar
+0 1 foo/bar		f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+1 1 foo-bar		f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+0 1 foo			**/foo
+1 1 /foo		**/foo
+1 1 bar/baz/foo		**/foo
+0 1 bar/baz/foo		*/foo
+0 0 foo/bar/baz		**/bar*
+1 1 deep/foo/bar/baz	**/bar/*
+0 1 deep/foo/bar/baz/	**/bar/*
+1 1 deep/foo/bar/baz/	**/bar/**
+0 1 deep/foo/bar	**/bar/*
+1 1 deep/foo/bar/	**/bar/**
+1 1 foo/bar/baz		**/bar**
+1 1 foo/bar/baz/x	*/bar/**
+0 0 deep/foo/bar/baz/x	*/bar/**
+1 1 deep/foo/bar/baz/x	**/bar/*/*
+
+# Various additional tests
+0 1 acrt		a[c-c]st
+1 1 acrt		a[c-c]rt
+0 1 ]			[!]-]
+1 1 a			[!]-]
+0 1 ''			\
+0 1 \			\
+0 1 /\			*/\
+1 1 /\			*/\\
+1 1 foo			foo
+1 1 @foo		@foo
+0 1 foo			@foo
+1 1 [ab]		\[ab]
+1 1 [ab]		[[]ab]
+1 1 [ab]		[[:]ab]
+0 1 [ab]		[[::]ab]
+1 1 [ab]		[[:digit]ab]
+1 1 [ab]		[\[:]ab]
+1 1 ?a?b		\??\?b
+1 1 abc			\a\b\c
+0 1 foo			''
+1 1 foo/bar/baz/to	**/t[o]
+
+# Character class tests
+1 1 a1B		[[:alpha:]][[:digit:]][[:upper:]]
+0 1 a		[[:digit:][:upper:][:space:]]
+1 1 A		[[:digit:][:upper:][:space:]]
+1 1 1		[[:digit:][:upper:][:space:]]
+0 1 1		[[:digit:][:upper:][:spaci:]]
+1 1 ' '		[[:digit:][:upper:][:space:]]
+0 1 .		[[:digit:][:upper:][:space:]]
+1 1 .		[[:digit:][:punct:][:space:]]
+1 1 5		[[:xdigit:]]
+1 1 f		[[:xdigit:]]
+1 1 D		[[:xdigit:]]
+1 1 _		[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+#1 1 …		[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 \x7f		[^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 .		[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]
+1 1 5		[a-c[:digit:]x-z]
+1 1 b		[a-c[:digit:]x-z]
+1 1 y		[a-c[:digit:]x-z]
+0 1 q		[a-c[:digit:]x-z]
+
+# Additional tests, including some malformed wildmats
+1 1 ]		[\\-^]
+0 1 [		[\\-^]
+1 1 -		[\-_]
+1 1 ]		[\]]
+0 1 \]		[\]]
+0 1 \		[\]]
+0 1 ab		a[]b
+0 1 a[]b	a[]b
+0 1 ab[		ab[
+0 1 ab		[!
+0 1 ab		[-
+1 1 -		[-]
+0 1 -		[a-
+0 1 -		[!a-
+1 1 -		[--A]
+1 1 5		[--A]
+1 1 ' '		'[ --]'
+1 1 $		'[ --]'
+1 1 -		'[ --]'
+0 1 0		'[ --]'
+1 1 -		[---]
+1 1 -		[------]
+0 1 j		[a-e-n]
+1 1 -		[a-e-n]
+1 1 a		[!------]
+0 1 [		[]-a]
+1 1 ^		[]-a]
+0 1 ^		[!]-a]
+1 1 [		[!]-a]
+1 1 ^		[a^bc]
+1 1 -b]		[a-]b]
+0 1 \		[\]
+1 1 \		[\\]
+0 1 \		[!\\]
+1 1 G		[A-\\]
+0 1 aaabbb	b*a
+0 1 aabcaa	*ba*
+1 1 ,		[,]
+1 1 ,		[\\,]
+1 1 \		[\\,]
+1 1 -		[,-.]
+0 1 +		[,-.]
+0 1 -.]		[,-.]
+1 1 2		[\1-\3]
+1 1 3		[\1-\3]
+0 1 4		[\1-\3]
+1 1 \		[[-\]]
+1 1 [		[[-\]]
+1 1 ]		[[-\]]
+0 1 -		[[-\]]
+
+# Test recursion and the abort code (use "wildtest -i" to see iteration counts)
+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1	-*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1	-*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1	-*-*-*-*-*-*-12-*-*-*-m-*-*-*
+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1	/*/*/*/*/*/*/12/*/*/*/m/*/*/*
+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1	/*/*/*/*/*/*/12/*/*/*/m/*/*/*
+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt		**/*a*b*g*n*t
+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz		**/*a*b*g*n*t
diff --git a/test-wildmatch.c b/test-wildmatch.c
new file mode 100644
index 0000000..88585c2
--- /dev/null
+++ b/test-wildmatch.c
@@ -0,0 +1,222 @@
+/*
+ * Test suite for the wildmatch code.
+ *
+ * Copyright (C) 2003-2009 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/*#define COMPARE_WITH_FNMATCH*/
+
+#define WILD_TEST_ITERATIONS
+#include "lib/wildmatch.c"
+
+#include <popt.h>
+
+#ifdef COMPARE_WITH_FNMATCH
+#include <fnmatch.h>
+
+int fnmatch_errors = 0;
+#endif
+
+int wildmatch_errors = 0;
+char number_separator = ',';
+
+typedef char bool;
+
+int output_iterations = 0;
+int explode_mod = 0;
+int empties_mod = 0;
+int empty_at_start = 0;
+int empty_at_end = 0;
+
+static struct poptOption long_options[] = {
+  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+  {"iterations",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},
+  {"empties",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},
+  {"explode",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},
+  {0,0,0,0, 0, 0, 0}
+};
+
+/* match just at the start of string (anchored tests) */
+static void
+run_test(int line, bool matches,
+#ifdef COMPARE_WITH_FNMATCH
+	 bool same_as_fnmatch,
+#endif
+	 const char *text, const char *pattern)
+{
+    bool matched;
+#ifdef COMPARE_WITH_FNMATCH
+    bool fn_matched;
+    int flags = strstr(pattern, "**")? 0 : FNM_PATHNAME;
+#endif
+
+    if (explode_mod) {
+	char buf[MAXPATHLEN*2], *texts[MAXPATHLEN];
+	int pos = 0, cnt = 0, ndx = 0, len = strlen(text);
+
+	if (empty_at_start)
+	    texts[ndx++] = "";
+	/* An empty string must turn into at least one empty array item. */
+	while (1) {
+	    texts[ndx] = buf + ndx * (explode_mod + 1);
+	    strlcpy(texts[ndx++], text + pos, explode_mod + 1);
+	    if (pos + explode_mod >= len)
+		break;
+	    pos += explode_mod;
+	    if (!(++cnt % empties_mod))
+		texts[ndx++] = "";
+	}
+	if (empty_at_end)
+	    texts[ndx++] = "";
+	texts[ndx] = NULL;
+	matched = wildmatch_array(pattern, (const char**)texts, 0);
+    } else
+	matched = wildmatch(pattern, text);
+#ifdef COMPARE_WITH_FNMATCH
+    fn_matched = !fnmatch(pattern, text, flags);
+#endif
+    if (matched != matches) {
+	printf("wildmatch failure on line %d:\n  %s\n  %s\n  expected %s match\n",
+	       line, text, pattern, matches? "a" : "NO");
+	wildmatch_errors++;
+    }
+#ifdef COMPARE_WITH_FNMATCH
+    if (fn_matched != (matches ^ !same_as_fnmatch)) {
+	printf("fnmatch disagreement on line %d:\n  %s\n  %s\n  expected %s match\n",
+	       line, text, pattern, matches ^ !same_as_fnmatch? "a" : "NO");
+	fnmatch_errors++;
+    }
+#endif
+    if (output_iterations) {
+	printf("%d: \"%s\" iterations = %d\n", line, pattern,
+	       wildmatch_iteration_count);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    char buf[2048], *s, *string[2], *end[2];
+    const char *arg;
+    FILE *fp;
+    int opt, line, i, flag[2];
+    poptContext pc = poptGetContext("wildtest", argc, (const char**)argv,
+				    long_options, 0);
+
+    while ((opt = poptGetNextOpt(pc)) != -1) {
+	switch (opt) {
+	  case 'e':
+	    arg = poptGetOptArg(pc);
+	    empties_mod = atoi(arg);
+	    if (strchr(arg, 's'))
+		empty_at_start = 1;
+	    if (strchr(arg, 'e'))
+		empty_at_end = 1;
+	    if (!explode_mod)
+		explode_mod = 1024;
+	    break;
+	  default:
+	    fprintf(stderr, "%s: %s\n",
+		    poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+		    poptStrerror(opt));
+	    exit(1);
+	}
+    }
+
+    if (explode_mod && !empties_mod)
+	empties_mod = 1024;
+
+    argv = (char**)poptGetArgs(pc);
+    if (!argv || argv[1]) {
+	fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n");
+	exit(1);
+    }
+
+    if ((fp = fopen(*argv, "r")) == NULL) {
+	fprintf(stderr, "Unable to open %s\n", *argv);
+	exit(1);
+    }
+
+    line = 0;
+    while (fgets(buf, sizeof buf, fp)) {
+	line++;
+	if (*buf == '#' || *buf == '\n')
+	    continue;
+	for (s = buf, i = 0; i <= 1; i++) {
+	    if (*s == '1')
+		flag[i] = 1;
+	    else if (*s == '0')
+		flag[i] = 0;
+	    else
+		flag[i] = -1;
+	    if (*++s != ' ' && *s != '\t')
+		flag[i] = -1;
+	    if (flag[i] < 0) {
+		fprintf(stderr, "Invalid flag syntax on line %d of %s:\n%s",
+			line, *argv, buf);
+		exit(1);
+	    }
+	    while (*++s == ' ' || *s == '\t') {}
+	}
+	for (i = 0; i <= 1; i++) {
+	    if (*s == '\'' || *s == '"' || *s == '`') {
+		char quote = *s++;
+		string[i] = s;
+		while (*s && *s != quote) s++;
+		if (!*s) {
+		    fprintf(stderr, "Unmatched quote on line %d of %s:\n%s",
+			    line, *argv, buf);
+		    exit(1);
+		}
+		end[i] = s;
+	    }
+	    else {
+		if (!*s || *s == '\n') {
+		    fprintf(stderr, "Not enough strings on line %d of %s:\n%s",
+			    line, *argv, buf);
+		    exit(1);
+		}
+		string[i] = s;
+		while (*++s && *s != ' ' && *s != '\t' && *s != '\n') {}
+		end[i] = s;
+	    }
+	    while (*++s == ' ' || *s == '\t') {}
+	}
+	*end[0] = *end[1] = '\0';
+	run_test(line, flag[0],
+#ifdef COMPARE_WITH_FNMATCH
+		 flag[1],
+#endif
+		 string[0], string[1]);
+    }
+
+    if (!wildmatch_errors)
+	fputs("No", stdout);
+    else
+	printf("%d", wildmatch_errors);
+    printf(" wildmatch error%s found.\n", wildmatch_errors == 1? "" : "s");
+
+#ifdef COMPARE_WITH_FNMATCH
+    if (!fnmatch_errors)
+	fputs("No", stdout);
+    else
+	printf("%d", fnmatch_errors);
+    printf(" fnmatch error%s found.\n", fnmatch_errors == 1? "" : "s");
+
+#endif
+
+    return 0;
+}
diff --git a/wildmatch.c b/wildmatch.c
new file mode 100644
index 0000000..f3a1731
--- /dev/null
+++ b/wildmatch.c
@@ -0,0 +1,368 @@
+/*
+**  Do shell-style pattern matching for ?, \, [], and * characters.
+**  It is 8bit clean.
+**
+**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+**  Rich $alz is now <rsalz@bbn.com>.
+**
+**  Modified by Wayne Davison to special-case '/' matching, to make '**'
+**  work differently than '*', and to fix the character-class code.
+*/
+
+#include "rsync.h"
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS	'!'
+#define NEGATE_CLASS2	'^'
+
+#define FALSE 0
+#define TRUE 1
+#define ABORT_ALL -1
+#define ABORT_TO_STARSTAR -2
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+				    && *(class) == *(litmatch) \
+				    && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifdef WILD_TEST_ITERATIONS
+int wildmatch_iteration_count;
+#endif
+
+static int force_lower_case = 0;
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+{
+    uchar p_ch;
+
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count++;
+#endif
+
+    for ( ; (p_ch = *p) != '\0'; text++, p++) {
+	int matched, special;
+	uchar t_ch, prev_ch;
+	while ((t_ch = *text) == '\0') {
+	    if (*a == NULL) {
+		if (p_ch != '*')
+		    return ABORT_ALL;
+		break;
+	    }
+	    text = *a++;
+	}
+	if (force_lower_case && ISUPPER(t_ch))
+	    t_ch = tolower(t_ch);
+	switch (p_ch) {
+	  case '\\':
+	    /* Literal match with following character.  Note that the test
+	     * in "default" handles the p[1] == '\0' failure case. */
+	    p_ch = *++p;
+	    /* FALLTHROUGH */
+	  default:
+	    if (t_ch != p_ch)
+		return FALSE;
+	    continue;
+	  case '?':
+	    /* Match anything but '/'. */
+	    if (t_ch == '/')
+		return FALSE;
+	    continue;
+	  case '*':
+	    if (*++p == '*') {
+		while (*++p == '*') {}
+		special = TRUE;
+	    } else
+		special = FALSE;
+	    if (*p == '\0') {
+		/* Trailing "**" matches everything.  Trailing "*" matches
+		 * only if there are no more slash characters. */
+		if (!special) {
+		    do {
+			if (strchr((char*)text, '/') != NULL)
+			    return FALSE;
+		    } while ((text = *a++) != NULL);
+		}
+		return TRUE;
+	    }
+	    while (1) {
+		if (t_ch == '\0') {
+		    if ((text = *a++) == NULL)
+			break;
+		    t_ch = *text;
+		    continue;
+		}
+		if ((matched = dowild(p, text, a)) != FALSE) {
+		    if (!special || matched != ABORT_TO_STARSTAR)
+			return matched;
+		} else if (!special && t_ch == '/')
+		    return ABORT_TO_STARSTAR;
+		t_ch = *++text;
+	    }
+	    return ABORT_ALL;
+	  case '[':
+	    p_ch = *++p;
+#ifdef NEGATE_CLASS2
+	    if (p_ch == NEGATE_CLASS2)
+		p_ch = NEGATE_CLASS;
+#endif
+	    /* Assign literal TRUE/FALSE because of "matched" comparison. */
+	    special = p_ch == NEGATE_CLASS? TRUE : FALSE;
+	    if (special) {
+		/* Inverted character class. */
+		p_ch = *++p;
+	    }
+	    prev_ch = 0;
+	    matched = FALSE;
+	    do {
+		if (!p_ch)
+		    return ABORT_ALL;
+		if (p_ch == '\\') {
+		    p_ch = *++p;
+		    if (!p_ch)
+			return ABORT_ALL;
+		    if (t_ch == p_ch)
+			matched = TRUE;
+		} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+		    p_ch = *++p;
+		    if (p_ch == '\\') {
+			p_ch = *++p;
+			if (!p_ch)
+			    return ABORT_ALL;
+		    }
+		    if (t_ch <= p_ch && t_ch >= prev_ch)
+			matched = TRUE;
+		    p_ch = 0; /* This makes "prev_ch" get set to 0. */
+		} else if (p_ch == '[' && p[1] == ':') {
+		    const uchar *s;
+		    int i;
+		    for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+		    if (!p_ch)
+			return ABORT_ALL;
+		    i = p - s - 1;
+		    if (i < 0 || p[-1] != ':') {
+			/* Didn't find ":]", so treat like a normal set. */
+			p = s - 2;
+			p_ch = '[';
+			if (t_ch == p_ch)
+			    matched = TRUE;
+			continue;
+		    }
+		    if (CC_EQ(s,i, "alnum")) {
+			if (ISALNUM(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "alpha")) {
+			if (ISALPHA(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "blank")) {
+			if (ISBLANK(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "cntrl")) {
+			if (ISCNTRL(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "digit")) {
+			if (ISDIGIT(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "graph")) {
+			if (ISGRAPH(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "lower")) {
+			if (ISLOWER(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "print")) {
+			if (ISPRINT(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "punct")) {
+			if (ISPUNCT(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "space")) {
+			if (ISSPACE(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "upper")) {
+			if (ISUPPER(t_ch))
+			    matched = TRUE;
+		    } else if (CC_EQ(s,i, "xdigit")) {
+			if (ISXDIGIT(t_ch))
+			    matched = TRUE;
+		    } else /* malformed [:class:] string */
+			return ABORT_ALL;
+		    p_ch = 0; /* This makes "prev_ch" get set to 0. */
+		} else if (t_ch == p_ch)
+		    matched = TRUE;
+	    } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+	    if (matched == special || t_ch == '/')
+		return FALSE;
+	    continue;
+	}
+    }
+
+    do {
+	if (*text)
+	    return FALSE;
+    } while ((text = *a++) != NULL);
+
+    return TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
+{
+    for ( ; *s != '\0'; text++, s++) {
+	while (*text == '\0') {
+	    if ((text = *a++) == NULL)
+		return FALSE;
+	}
+	if (*text != *s)
+	    return FALSE;
+    }
+
+    do {
+	if (*text)
+	    return FALSE;
+    } while ((text = *a++) != NULL);
+
+    return TRUE;
+}
+
+/* Return the last "count" path elements from the concatenated string.
+ * We return a string pointer to the start of the string, and update the
+ * array pointer-pointer to point to any remaining string elements. */
+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
+{
+    const uchar*const *a = *a_ptr;
+    const uchar*const *first_a = a;
+
+    while (*a)
+	    a++;
+
+    while (a != first_a) {
+	const uchar *s = *--a;
+	s += strlen((char*)s);
+	while (--s >= *a) {
+	    if (*s == '/' && !--count) {
+		*a_ptr = a+1;
+		return s+1;
+	    }
+	}
+    }
+
+    if (count == 1) {
+	*a_ptr = a+1;
+	return *a;
+    }
+
+    return NULL;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text)
+{
+    static const uchar *nomore[1]; /* A NULL pointer. */
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count = 0;
+#endif
+    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+}
+
+/* Match the "pattern" against the forced-to-lower-case "text" string. */
+int iwildmatch(const char *pattern, const char *text)
+{
+    static const uchar *nomore[1]; /* A NULL pointer. */
+    int ret;
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count = 0;
+#endif
+    force_lower_case = 1;
+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+    force_lower_case = 0;
+    return ret;
+}
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end).  The int "where" can be 0 (normal matching), > 0 (match only
+ * the trailing N slash-separated filename components of "texts"), or < 0
+ * (match the "pattern" at the start or after any slash in "texts"). */
+int wildmatch_array(const char *pattern, const char*const *texts, int where)
+{
+    const uchar *p = (const uchar*)pattern;
+    const uchar*const *a = (const uchar*const*)texts;
+    const uchar *text;
+    int matched;
+
+#ifdef WILD_TEST_ITERATIONS
+    wildmatch_iteration_count = 0;
+#endif
+
+    if (where > 0)
+	text = trailing_N_elements(&a, where);
+    else
+	text = *a++;
+    if (!text)
+	return FALSE;
+
+    if ((matched = dowild(p, text, a)) != TRUE && where < 0
+     && matched != ABORT_ALL) {
+	while (1) {
+	    if (*text == '\0') {
+		if ((text = (uchar*)*a++) == NULL)
+		    return FALSE;
+		continue;
+	    }
+	    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+	     && matched != ABORT_TO_STARSTAR)
+		break;
+	}
+    }
+    return matched == TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end).  The int "where" can be 0 (normal matching), or > 0 (match
+ * only the trailing N slash-separated filename components of "texts"). */
+int litmatch_array(const char *string, const char*const *texts, int where)
+{
+    const uchar *s = (const uchar*)string;
+    const uchar*const *a = (const uchar* const*)texts;
+    const uchar *text;
+
+    if (where > 0)
+	text = trailing_N_elements(&a, where);
+    else
+	text = *a++;
+    if (!text)
+	return FALSE;
+
+    return doliteral(s, text, a) == TRUE;
+}
diff --git a/wildmatch.h b/wildmatch.h
new file mode 100644
index 0000000..e7f1a35
--- /dev/null
+++ b/wildmatch.h
@@ -0,0 +1,6 @@
+/* wildmatch.h */
+
+int wildmatch(const char *pattern, const char *text);
+int iwildmatch(const char *pattern, const char *text);
+int wildmatch_array(const char *pattern, const char*const *texts, int where);
+int litmatch_array(const char *string, const char*const *texts, int where);
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
@ 2012-09-16 15:27   ` Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

One place less to worry about thread safety

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wildmatch.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/wildmatch.c b/wildmatch.c
index f3a1731..e824eb2 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -57,11 +57,10 @@
 int wildmatch_iteration_count;
 #endif
 
-static int force_lower_case = 0;
-
 /* Match pattern "p" against the a virtually-joined string consisting
  * of "text" and any strings in array "a". */
-static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+static int dowild(const uchar *p, const uchar *text,
+		  const uchar*const *a, int force_lower_case)
 {
     uchar p_ch;
 
@@ -121,7 +120,7 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
 		    t_ch = *text;
 		    continue;
 		}
-		if ((matched = dowild(p, text, a)) != FALSE) {
+		if ((matched = dowild(p, text, a, force_lower_case)) != FALSE) {
 		    if (!special || matched != ABORT_TO_STARSTAR)
 			return matched;
 		} else if (!special && t_ch == '/')
@@ -291,7 +290,7 @@ int wildmatch(const char *pattern, const char *text)
 #ifdef WILD_TEST_ITERATIONS
     wildmatch_iteration_count = 0;
 #endif
-    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+    return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;
 }
 
 /* Match the "pattern" against the forced-to-lower-case "text" string. */
@@ -302,9 +301,7 @@ int iwildmatch(const char *pattern, const char *text)
 #ifdef WILD_TEST_ITERATIONS
     wildmatch_iteration_count = 0;
 #endif
-    force_lower_case = 1;
-    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
-    force_lower_case = 0;
+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;
     return ret;
 }
 
@@ -331,7 +328,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
     if (!text)
 	return FALSE;
 
-    if ((matched = dowild(p, text, a)) != TRUE && where < 0
+    if ((matched = dowild(p, text, a, 0)) != TRUE && where < 0
      && matched != ABORT_ALL) {
 	while (1) {
 	    if (*text == '\0') {
@@ -339,7 +336,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
 		    return FALSE;
 		continue;
 	    }
-	    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+	    if (*text++ == '/' && (matched = dowild(p, text, a, 0)) != FALSE
 	     && matched != ABORT_TO_STARSTAR)
 		break;
 	}
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
@ 2012-09-16 15:27   ` Nguyễn Thái Ngọc Duy
  2012-09-16 15:27   ` [PATCH v2 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

dowild() does case insensitive matching by lower-casing the text. That
means lower case letters in patterns imply case-insensitive matching,
but upper case means exact matching.

We do not want that subtlety. Lower case pattern too so iwildmatch()
always does what we expect it to do.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wildmatch.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/wildmatch.c b/wildmatch.c
index e824eb2..c7f7f9f 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -81,6 +81,8 @@ static int dowild(const uchar *p, const uchar *text,
 	}
 	if (force_lower_case && ISUPPER(t_ch))
 	    t_ch = tolower(t_ch);
+	if (force_lower_case && ISUPPER(p_ch))
+	    p_ch = tolower(p_ch);
 	switch (p_ch) {
 	  case '\\':
 	    /* Literal match with following character.  Note that the test
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 4/5] Integrate wildmatch to git
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                     ` (2 preceding siblings ...)
  2012-09-16 15:27   ` [PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
@ 2012-09-16 15:27   ` Nguyễn Thái Ngọc Duy
  2012-09-17  5:31     ` Junio C Hamano
  2012-09-16 15:27   ` [PATCH v2 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
  2012-09-25  7:01   ` [PATCH 6/5] side-step a make rule that builds t3070-wildmatch Johannes Sixt
  5 siblings, 1 reply; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

This makes wildmatch.c part of libgit.a and builds test-wildmatch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore           |  1 +
 Makefile             |  6 ++++++
 t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++
 test-wildmatch.c     |  8 +++++++-
 wildmatch.c          |  8 +++++++-
 5 files changed, 48 insertions(+), 2 deletions(-)
 create mode 100755 t/t3070-wildmatch.sh

diff --git a/.gitignore b/.gitignore
index 68fe464..54b1b3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -196,6 +196,7 @@
 /test-sigchain
 /test-subprocess
 /test-svn-fe
+/test-wildmatch
 /common-cmds.h
 *.tar.gz
 *.dsc
diff --git a/Makefile b/Makefile
index 56301dc..745e88c 100644
--- a/Makefile
+++ b/Makefile
@@ -511,6 +511,7 @@ TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-subprocess
 TEST_PROGRAMS_NEED_X += test-svn-fe
+TEST_PROGRAMS_NEED_X += test-wildmatch
 
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
@@ -683,6 +684,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += varint.h
 LIB_H += walker.h
+LIB_H += wildmatch.h
 LIB_H += wt-status.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
@@ -814,6 +816,7 @@ LIB_OBJS += utf8.o
 LIB_OBJS += varint.o
 LIB_OBJS += version.o
 LIB_OBJS += walker.o
+LIB_OBJS += wildmatch.o
 LIB_OBJS += wrapper.o
 LIB_OBJS += write_or_die.o
 LIB_OBJS += ws.o
@@ -2586,6 +2589,9 @@ test-svn-fe$X: vcs-svn/lib.a
 test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
+test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt
+
 check-sha1:: test-sha1$X
 	./test-sha1.sh
 
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
new file mode 100755
index 0000000..7fb63ff
--- /dev/null
+++ b/t/t3070-wildmatch.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='wildmatch tests'
+
+. ./test-lib.sh
+
+test_wildmatch() {
+    test_expect_success "wildmatch $*" "
+	test-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual &&
+	echo 'No wildmatch errors found.' >expected &&
+	test_cmp expected actual
+    "
+}
+
+test_wildmatch -x1
+test_wildmatch -x1 -e1
+test_wildmatch -x1 -else
+test_wildmatch -x2
+test_wildmatch -x2 -ese
+test_wildmatch -x3
+test_wildmatch -x3 -e1
+test_wildmatch -x4
+test_wildmatch -x4 -e2e
+test_wildmatch -x5
+test_wildmatch -x5 -es
+
+test_done
diff --git a/test-wildmatch.c b/test-wildmatch.c
index 88585c2..828188a 100644
--- a/test-wildmatch.c
+++ b/test-wildmatch.c
@@ -20,7 +20,13 @@
 /*#define COMPARE_WITH_FNMATCH*/
 
 #define WILD_TEST_ITERATIONS
-#include "lib/wildmatch.c"
+#include "wildmatch.c"
+
+#define MAXPATHLEN 1024
+#ifdef NO_STRLCPY
+#include "compat/strlcpy.c"
+#define strlcpy gitstrlcpy
+#endif
 
 #include <popt.h>
 
diff --git a/wildmatch.c b/wildmatch.c
index c7f7f9f..625cb0c 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -9,7 +9,13 @@
 **  work differently than '*', and to fix the character-class code.
 */
 
-#include "rsync.h"
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "wildmatch.h"
+
+typedef unsigned char uchar;
 
 /* What character marks an inverted character class? */
 #define NEGATE_CLASS	'!'
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch()
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                     ` (3 preceding siblings ...)
  2012-09-16 15:27   ` [PATCH v2 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
@ 2012-09-16 15:27   ` Nguyễn Thái Ngọc Duy
  2012-09-25  7:01   ` [PATCH 6/5] side-step a make rule that builds t3070-wildmatch Johannes Sixt
  5 siblings, 0 replies; 34+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/gitignore.txt        |  3 +++
 attr.c                             |  4 +++-
 dir.c                              |  5 ++++-
 t/t0003-attributes.sh              | 17 +++++++++++++++++
 t/t3001-ls-files-others-exclude.sh | 11 +++++++++++
 5 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index c1f692a..eb81d31 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -93,6 +93,9 @@ PATTERN FORMAT
    For example, "Documentation/{asterisk}.html" matches
    "Documentation/git.html" but not "Documentation/ppc/ppc.html"
    or "tools/perf/Documentation/perf.html".
++
+Contrary to fnmatch(3), git matches "**" to anything including
+slashes, similar to rsync(1).
 
  - A leading slash matches the beginning of the pathname.
    For example, "/{asterisk}.c" matches "cat-file.c" but not
diff --git a/attr.c b/attr.c
index 3430faf..2cea18c 100644
--- a/attr.c
+++ b/attr.c
@@ -12,6 +12,7 @@
 #include "exec_cmd.h"
 #include "attr.h"
 #include "dir.h"
+#include "wildmatch.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen,
 		return 0;
 	if (baselen != 0)
 		baselen++;
-	return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+	return (ignore_case && iwildmatch(pattern, pathname + baselen)) ||
+		(!ignore_case && wildmatch(pattern, pathname + baselen));
 }
 
 static int macroexpand_one(int attr_nr, int rem);
diff --git a/dir.c b/dir.c
index 4868339..55ab2b3 100644
--- a/dir.c
+++ b/dir.c
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "refs.h"
+#include "wildmatch.h"
 
 struct path_simplify {
 	int len;
@@ -575,7 +576,9 @@ int excluded_from_list(const char *pathname,
 			namelen -= prefix;
 		}
 
-		if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
+		if (!namelen ||
+		    ((ignore_case && iwildmatch(exclude, name)) ||
+		     (!ignore_case && wildmatch(exclude, name))))
 			return to_exclude;
 	}
 	return -1; /* undecided */
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index febc45c..6c3c554 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -232,4 +232,21 @@ test_expect_success 'bare repository: test info/attributes' '
 	attr_check subdir/a/i unspecified
 '
 
+test_expect_success '"**" test' '
+	cd .. &&
+	echo "**/f foo=bar" >.gitattributes &&
+	cat <<\EOF >expect &&
+f: foo: unspecified
+a/f: foo: bar
+a/b/f: foo: bar
+a/b/c/f: foo: bar
+EOF
+	git check-attr foo -- "f" >actual 2>err &&
+	git check-attr foo -- "a/f" >>actual 2>>err &&
+	git check-attr foo -- "a/b/f" >>actual 2>>err &&
+	git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
+	test_cmp expect actual &&
+	test_line_count = 0 err
+'
+
 test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index c8fe978..67c8bcf 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -214,4 +214,15 @@ test_expect_success 'subdirectory ignore (l1)' '
 	test_cmp expect actual
 '
 
+
+test_expect_success 'ls-files with "**" patterns' '
+	cat <<\EOF >expect &&
+one/a.1
+one/two/a.1
+three/a.1
+EOF
+	git ls-files -o -i --exclude "**/a.1" >actual
+	test_cmp expect actual
+'
+
 test_done
-- 
1.7.12.403.gce5cf6f.dirty

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 4/5] Integrate wildmatch to git
  2012-09-16 15:27   ` [PATCH v2 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
@ 2012-09-17  5:31     ` Junio C Hamano
  2012-09-17  5:54       ` Junio C Hamano
  0 siblings, 1 reply; 34+ messages in thread
From: Junio C Hamano @ 2012-09-17  5:31 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Am I missing some includes?

test-wildmatch.c:50: error: array type has incomplete element type
test-wildmatch.c:52: error: 'POPT_ARG_NONE' undeclared here (not in a function)
test-wildmatch.c:53: error: 'POPT_ARG_STRING' undeclared here (not in a function)
test-wildmatch.c:54: error: 'POPT_ARG_INT' undeclared here (not in a function)
test-wildmatch.c: In function 'main':
test-wildmatch.c:122: error: 'poptContext' undeclared (first use in this function)
test-wildmatch.c:122: error: (Each undeclared identifier is reported only once
test-wildmatch.c:122: error: for each function it appears in.)
test-wildmatch.c:122: error: expected ';' before 'pc'
cc1: warnings being treated as errors
test-wildmatch.c:125: error: implicit declaration of function 'poptGetNextOpt'
test-wildmatch.c:125: error: 'pc' undeclared (first use in this function)
test-wildmatch.c:128: error: implicit declaration of function 'poptGetOptArg'
test-wildmatch.c:139: error: implicit declaration of function 'poptBadOption'
test-wildmatch.c:139: error: 'POPT_BADOPTION_NOALIAS' undeclared (first use in this function)
test-wildmatch.c:140: error: implicit declaration of function 'poptStrerror'
test-wildmatch.c:148: error: implicit declaration of function 'poptGetArgs'

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 4/5] Integrate wildmatch to git
  2012-09-17  5:31     ` Junio C Hamano
@ 2012-09-17  5:54       ` Junio C Hamano
  2012-09-17  5:57         ` Nguyen Thai Ngoc Duy
  2012-09-17 12:40         ` Nguyen Thai Ngoc Duy
  0 siblings, 2 replies; 34+ messages in thread
From: Junio C Hamano @ 2012-09-17  5:54 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

> Am I missing some includes?
>
> test-wildmatch.c:50: error: array type has incomplete element type
> test-wildmatch.c:52: error: 'POPT_ARG_NONE' undeclared here (not in a function)
> test-wildmatch.c:53: error: 'POPT_ARG_STRING' undeclared here (not in a function)
> test-wildmatch.c:54: error: 'POPT_ARG_INT' undeclared here (not in a function)
> test-wildmatch.c: In function 'main':
> test-wildmatch.c:122: error: 'poptContext' undeclared (first use in this function)
> test-wildmatch.c:122: error: (Each undeclared identifier is reported only once
> test-wildmatch.c:122: error: for each function it appears in.)
> test-wildmatch.c:122: error: expected ';' before 'pc'
> cc1: warnings being treated as errors
> test-wildmatch.c:125: error: implicit declaration of function 'poptGetNextOpt'
> test-wildmatch.c:125: error: 'pc' undeclared (first use in this function)
> test-wildmatch.c:128: error: implicit declaration of function 'poptGetOptArg'
> test-wildmatch.c:139: error: implicit declaration of function 'poptBadOption'
> test-wildmatch.c:139: error: 'POPT_BADOPTION_NOALIAS' undeclared (first use in this function)
> test-wildmatch.c:140: error: implicit declaration of function 'poptStrerror'
> test-wildmatch.c:148: error: implicit declaration of function 'poptGetArgs'

Yeah, popt.h it is.  It is a bit distasteful that we have a build
dependency only to build test-* helper on something that we do not
even have runtime dependency on.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 4/5] Integrate wildmatch to git
  2012-09-17  5:54       ` Junio C Hamano
@ 2012-09-17  5:57         ` Nguyen Thai Ngoc Duy
  2012-09-17 12:40         ` Nguyen Thai Ngoc Duy
  1 sibling, 0 replies; 34+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-09-17  5:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Mon, Sep 17, 2012 at 12:54 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Yeah, popt.h it is.  It is a bit distasteful that we have a build
> dependency only to build test-* helper on something that we do not
> even have runtime dependency on.

Yep. I don't know how popular libpopt is. But if it's undesired, we
could rewrite test-wildmatch.c to use parse-options.c instead. We fork
from rsync anyway due to license conflict so we don't have to keep it
close to upstream any more.
-- 
Duy

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 4/5] Integrate wildmatch to git
  2012-09-17  5:54       ` Junio C Hamano
  2012-09-17  5:57         ` Nguyen Thai Ngoc Duy
@ 2012-09-17 12:40         ` Nguyen Thai Ngoc Duy
  2012-09-17 17:20           ` Junio C Hamano
  1 sibling, 1 reply; 34+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-09-17 12:40 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Sun, Sep 16, 2012 at 10:54:57PM -0700, Junio C Hamano wrote:
> Yeah, popt.h it is.  It is a bit distasteful that we have a build
> dependency only to build test-* helper on something that we do not
> even have runtime dependency on.

Perhaps this squash-in? It kills libpopt and removes the "#include
"wildmatch.c"". Now we really do test what libgit.a carries.

-- 8< --
diff --git a/Makefile b/Makefile
index 745e88c..093ab9c 100644
--- a/Makefile
+++ b/Makefile
@@ -2589,9 +2589,6 @@ test-svn-fe$X: vcs-svn/lib.a
 test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
-test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS
-	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt
-
 check-sha1:: test-sha1$X
 	./test-sha1.sh
 
diff --git a/test-wildmatch.c b/test-wildmatch.c
index 828188a..b94921b 100644
--- a/test-wildmatch.c
+++ b/test-wildmatch.c
@@ -19,17 +19,18 @@
 
 /*#define COMPARE_WITH_FNMATCH*/
 
-#define WILD_TEST_ITERATIONS
-#include "wildmatch.c"
+#include "cache.h"
+#include "parse-options.h"
+#include "wildmatch.h"
 
+#ifndef MAXPATHLEN
 #define MAXPATHLEN 1024
+#endif
 #ifdef NO_STRLCPY
 #include "compat/strlcpy.c"
 #define strlcpy gitstrlcpy
 #endif
 
-#include <popt.h>
-
 #ifdef COMPARE_WITH_FNMATCH
 #include <fnmatch.h>
 
@@ -41,18 +42,16 @@ char number_separator = ',';
 
 typedef char bool;
 
-int output_iterations = 0;
 int explode_mod = 0;
 int empties_mod = 0;
 int empty_at_start = 0;
 int empty_at_end = 0;
+char *empties;
 
-static struct poptOption long_options[] = {
-  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
-  {"iterations",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},
-  {"empties",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},
-  {"explode",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},
-  {0,0,0,0, 0, 0, 0}
+static struct option options[] = {
+  OPT_STRING('e', "empties", &empties, "", ""),
+  OPT_INTEGER('x', "explode", &explode_mod, ""),
+  OPT_END(),
 };
 
 /* match just at the start of string (anchored tests) */
@@ -106,51 +105,33 @@ run_test(int line, bool matches,
 	fnmatch_errors++;
     }
 #endif
-    if (output_iterations) {
-	printf("%d: \"%s\" iterations = %d\n", line, pattern,
-	       wildmatch_iteration_count);
-    }
 }
 
 int
 main(int argc, char **argv)
 {
     char buf[2048], *s, *string[2], *end[2];
-    const char *arg;
     FILE *fp;
-    int opt, line, i, flag[2];
-    poptContext pc = poptGetContext("wildtest", argc, (const char**)argv,
-				    long_options, 0);
-
-    while ((opt = poptGetNextOpt(pc)) != -1) {
-	switch (opt) {
-	  case 'e':
-	    arg = poptGetOptArg(pc);
-	    empties_mod = atoi(arg);
-	    if (strchr(arg, 's'))
-		empty_at_start = 1;
-	    if (strchr(arg, 'e'))
-		empty_at_end = 1;
-	    if (!explode_mod)
-		explode_mod = 1024;
-	    break;
-	  default:
-	    fprintf(stderr, "%s: %s\n",
-		    poptBadOption(pc, POPT_BADOPTION_NOALIAS),
-		    poptStrerror(opt));
-	    exit(1);
-	}
+    int line, i, flag[2];
+    const char *help[] = { NULL };
+
+    argc = parse_options(argc, (const char **)argv, "", options, help, 0);
+    if (argc != 1)
+	    die("redundant options");
+    if (empties) {
+	const char *arg = empties;
+	empties_mod = atoi(arg);
+	if (strchr(empties, 's'))
+	    empty_at_start = 1;
+	if (strchr(arg, 'e'))
+	    empty_at_end = 1;
+	if (!explode_mod)
+	    explode_mod = 1024;
     }
 
     if (explode_mod && !empties_mod)
 	empties_mod = 1024;
 
-    argv = (char**)poptGetArgs(pc);
-    if (!argv || argv[1]) {
-	fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n");
-	exit(1);
-    }
-
     if ((fp = fopen(*argv, "r")) == NULL) {
 	fprintf(stderr, "Unable to open %s\n", *argv);
 	exit(1);
diff --git a/wildmatch.c b/wildmatch.c
index 625cb0c..f153f8a 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -59,10 +59,6 @@ typedef unsigned char uchar;
 #define ISUPPER(c) (ISASCII(c) && isupper(c))
 #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
 
-#ifdef WILD_TEST_ITERATIONS
-int wildmatch_iteration_count;
-#endif
-
 /* Match pattern "p" against the a virtually-joined string consisting
  * of "text" and any strings in array "a". */
 static int dowild(const uchar *p, const uchar *text,
@@ -70,10 +66,6 @@ static int dowild(const uchar *p, const uchar *text,
 {
     uchar p_ch;
 
-#ifdef WILD_TEST_ITERATIONS
-    wildmatch_iteration_count++;
-#endif
-
     for ( ; (p_ch = *p) != '\0'; text++, p++) {
 	int matched, special;
 	uchar t_ch, prev_ch;
@@ -295,9 +287,6 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
 int wildmatch(const char *pattern, const char *text)
 {
     static const uchar *nomore[1]; /* A NULL pointer. */
-#ifdef WILD_TEST_ITERATIONS
-    wildmatch_iteration_count = 0;
-#endif
     return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;
 }
 
@@ -306,9 +295,6 @@ int iwildmatch(const char *pattern, const char *text)
 {
     static const uchar *nomore[1]; /* A NULL pointer. */
     int ret;
-#ifdef WILD_TEST_ITERATIONS
-    wildmatch_iteration_count = 0;
-#endif
     ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;
     return ret;
 }
@@ -325,10 +311,6 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
     const uchar *text;
     int matched;
 
-#ifdef WILD_TEST_ITERATIONS
-    wildmatch_iteration_count = 0;
-#endif
-
     if (where > 0)
 	text = trailing_N_elements(&a, where);
     else
-- 8< --

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 4/5] Integrate wildmatch to git
  2012-09-17 12:40         ` Nguyen Thai Ngoc Duy
@ 2012-09-17 17:20           ` Junio C Hamano
  0 siblings, 0 replies; 34+ messages in thread
From: Junio C Hamano @ 2012-09-17 17:20 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git

Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:

> On Sun, Sep 16, 2012 at 10:54:57PM -0700, Junio C Hamano wrote:
>> Yeah, popt.h it is.  It is a bit distasteful that we have a build
>> dependency only to build test-* helper on something that we do not
>> even have runtime dependency on.
>
> Perhaps this squash-in? It kills libpopt and removes the "#include
> "wildmatch.c"". Now we really do test what libgit.a carries.

Sounds fine to me.  Thanks; will take a look.

>
> -- 8< --
> diff --git a/Makefile b/Makefile
> index 745e88c..093ab9c 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -2589,9 +2589,6 @@ test-svn-fe$X: vcs-svn/lib.a
>  test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
>  	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
>  
> -test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS
> -	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt
> -
>  check-sha1:: test-sha1$X
>  	./test-sha1.sh
>  
> diff --git a/test-wildmatch.c b/test-wildmatch.c
> index 828188a..b94921b 100644
> --- a/test-wildmatch.c
> +++ b/test-wildmatch.c
> @@ -19,17 +19,18 @@
>  
>  /*#define COMPARE_WITH_FNMATCH*/
>  
> -#define WILD_TEST_ITERATIONS
> -#include "wildmatch.c"
> +#include "cache.h"
> +#include "parse-options.h"
> +#include "wildmatch.h"
>  
> +#ifndef MAXPATHLEN
>  #define MAXPATHLEN 1024
> +#endif
>  #ifdef NO_STRLCPY
>  #include "compat/strlcpy.c"
>  #define strlcpy gitstrlcpy
>  #endif
>  
> -#include <popt.h>
> -
>  #ifdef COMPARE_WITH_FNMATCH
>  #include <fnmatch.h>
>  
> @@ -41,18 +42,16 @@ char number_separator = ',';
>  
>  typedef char bool;
>  
> -int output_iterations = 0;
>  int explode_mod = 0;
>  int empties_mod = 0;
>  int empty_at_start = 0;
>  int empty_at_end = 0;
> +char *empties;
>  
> -static struct poptOption long_options[] = {
> -  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
> -  {"iterations",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},
> -  {"empties",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},
> -  {"explode",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},
> -  {0,0,0,0, 0, 0, 0}
> +static struct option options[] = {
> +  OPT_STRING('e', "empties", &empties, "", ""),
> +  OPT_INTEGER('x', "explode", &explode_mod, ""),
> +  OPT_END(),
>  };
>  
>  /* match just at the start of string (anchored tests) */
> @@ -106,51 +105,33 @@ run_test(int line, bool matches,
>  	fnmatch_errors++;
>      }
>  #endif
> -    if (output_iterations) {
> -	printf("%d: \"%s\" iterations = %d\n", line, pattern,
> -	       wildmatch_iteration_count);
> -    }
>  }
>  
>  int
>  main(int argc, char **argv)
>  {
>      char buf[2048], *s, *string[2], *end[2];
> -    const char *arg;
>      FILE *fp;
> -    int opt, line, i, flag[2];
> -    poptContext pc = poptGetContext("wildtest", argc, (const char**)argv,
> -				    long_options, 0);
> -
> -    while ((opt = poptGetNextOpt(pc)) != -1) {
> -	switch (opt) {
> -	  case 'e':
> -	    arg = poptGetOptArg(pc);
> -	    empties_mod = atoi(arg);
> -	    if (strchr(arg, 's'))
> -		empty_at_start = 1;
> -	    if (strchr(arg, 'e'))
> -		empty_at_end = 1;
> -	    if (!explode_mod)
> -		explode_mod = 1024;
> -	    break;
> -	  default:
> -	    fprintf(stderr, "%s: %s\n",
> -		    poptBadOption(pc, POPT_BADOPTION_NOALIAS),
> -		    poptStrerror(opt));
> -	    exit(1);
> -	}
> +    int line, i, flag[2];
> +    const char *help[] = { NULL };
> +
> +    argc = parse_options(argc, (const char **)argv, "", options, help, 0);
> +    if (argc != 1)
> +	    die("redundant options");
> +    if (empties) {
> +	const char *arg = empties;
> +	empties_mod = atoi(arg);
> +	if (strchr(empties, 's'))
> +	    empty_at_start = 1;
> +	if (strchr(arg, 'e'))
> +	    empty_at_end = 1;
> +	if (!explode_mod)
> +	    explode_mod = 1024;
>      }
>  
>      if (explode_mod && !empties_mod)
>  	empties_mod = 1024;
>  
> -    argv = (char**)poptGetArgs(pc);
> -    if (!argv || argv[1]) {
> -	fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n");
> -	exit(1);
> -    }
> -
>      if ((fp = fopen(*argv, "r")) == NULL) {
>  	fprintf(stderr, "Unable to open %s\n", *argv);
>  	exit(1);
> diff --git a/wildmatch.c b/wildmatch.c
> index 625cb0c..f153f8a 100644
> --- a/wildmatch.c
> +++ b/wildmatch.c
> @@ -59,10 +59,6 @@ typedef unsigned char uchar;
>  #define ISUPPER(c) (ISASCII(c) && isupper(c))
>  #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
>  
> -#ifdef WILD_TEST_ITERATIONS
> -int wildmatch_iteration_count;
> -#endif
> -
>  /* Match pattern "p" against the a virtually-joined string consisting
>   * of "text" and any strings in array "a". */
>  static int dowild(const uchar *p, const uchar *text,
> @@ -70,10 +66,6 @@ static int dowild(const uchar *p, const uchar *text,
>  {
>      uchar p_ch;
>  
> -#ifdef WILD_TEST_ITERATIONS
> -    wildmatch_iteration_count++;
> -#endif
> -
>      for ( ; (p_ch = *p) != '\0'; text++, p++) {
>  	int matched, special;
>  	uchar t_ch, prev_ch;
> @@ -295,9 +287,6 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
>  int wildmatch(const char *pattern, const char *text)
>  {
>      static const uchar *nomore[1]; /* A NULL pointer. */
> -#ifdef WILD_TEST_ITERATIONS
> -    wildmatch_iteration_count = 0;
> -#endif
>      return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;
>  }
>  
> @@ -306,9 +295,6 @@ int iwildmatch(const char *pattern, const char *text)
>  {
>      static const uchar *nomore[1]; /* A NULL pointer. */
>      int ret;
> -#ifdef WILD_TEST_ITERATIONS
> -    wildmatch_iteration_count = 0;
> -#endif
>      ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;
>      return ret;
>  }
> @@ -325,10 +311,6 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
>      const uchar *text;
>      int matched;
>  
> -#ifdef WILD_TEST_ITERATIONS
> -    wildmatch_iteration_count = 0;
> -#endif
> -
>      if (where > 0)
>  	text = trailing_N_elements(&a, where);
>      else
> -- 8< --

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-15 17:08   ` Junio C Hamano
@ 2012-09-17 22:55     ` Philip Oakley
  2012-09-17 23:49       ` Junio C Hamano
  2012-09-23 12:03     ` Jan Engelhardt
  1 sibling, 1 reply; 34+ messages in thread
From: Philip Oakley @ 2012-09-17 22:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git List

From: "Junio C Hamano" <gitster@pobox.com>
> "Philip Oakley" <philipoakley@iee.org> writes:
>
>> "Typically it will be placed in a MUA’s drafts folder, edited to add
>> timely commentary that should not go in the changelog after the three
>> dashes, and then sent as a message whose body, in our example, starts
>> with "arch/arm config files were…". On the receiving end, readers 
>> can
>> save interesting patches in a UNIX mailbox and apply them with
>> git-am(1)."
>>
>> hides a multitude of implicit knowledge steps. Is there an extended
>> description of what that would mean from a platform independent
>> viewpoint? e.g. if the patches are separte files and an mbox is one
>> consolidated file, how to get from one to the other so that 'it' can 
>> be
>> sent by 'git send-mail'.
>
> If you plan to use "git send-email" to send the final results out,
> you should consider "git send-email" as your "MUA" in the quoted
> paragraph.  And that will be very platform independent viewpoint to
> see things from.

On git for windows (msysgit)n there were a couple of other steps I had 
to do https://git.wiki.kernel.org/index.php/MSysGit:UsingSendEmail as 
Outlooklook Express isn't really a suitable MUA ;-)  [noted for other 
list readers]

>
> "git format-patch -o my-series/ --cover-letter ..."  would treat
> "my-series/" directory as "MUA's drafts folder" and prepares the
> messages you would want to send out, and you can proof-read and edit
> the files in there before telling your "MUA" to send them out, with
> "git send-email ... my-series/*.patch" or something.
>

I hadn't picked up from the man page that the --cover-letter would do 
the [PATCH 0/n] - should it?

>> I'm also missing an understanding of the preparation stage where one
>> tries to tidy up the various commit messages becaue they weren't
>> explicit, specfic nor concise enough,...
>
> Many people usually do "rebase -i" until perfection and then a
> single final invocation of "format-patch".  Of course, the "final"
> can and should further be proof-read and it is fine to do typofixes
> in the format-patch output files without going back to the commits
> before sending them out.

I did an initial rebase to correct a few obvious mistakes (e.g. an extra 
file that had crept in), but then, after some false starts, used
    git format-patch pu --stdout > fix_Docs.patch
to get a single file I could inspect and refine for both the commit 
messages and content.

I then applied it (using git am) to a temp branch to see what it 
produced, and could repeat the cycle until the patches looked right.
>
>> ...so I suspect that there is an
>> implicit `git format-patch` <-> `git am` loop of sharpening the mbox
>> patches before submission to the list that I'm missing. Has this
>> described somewhere?
> --

However, when it came to creating the series, with comments, I couldn't 
see a way of having my comments within my local commits, but preparing a 
patch series that would properly include the '---' separator.

Is there a way of getting format-patch to change some line break 
sequence (within the commit message) to the '---' three dashes patch 
break suitable for submission to the list?

Philip

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-17 22:55     ` Philip Oakley
@ 2012-09-17 23:49       ` Junio C Hamano
  2012-09-18  0:15         ` Jeff King
  2012-09-18 20:42         ` Wesley J. Landaker
  0 siblings, 2 replies; 34+ messages in thread
From: Junio C Hamano @ 2012-09-17 23:49 UTC (permalink / raw)
  To: Philip Oakley; +Cc: Git List

"Philip Oakley" <philipoakley@iee.org> writes:

> I then applied it (using git am) to a temp branch to see what it
> produced, and could repeat the cycle until the patches looked right.

That's another obvious and valid way to prepare your series.  It all
depends on how comfortable you are to directly edit patches.  Some
people fear it.  Some don't.  Some can do it with their eyes closed ;-).

> However, when it came to creating the series, with comments, I
> couldn't see a way of having my comments within my local commits, but
> preparing a patch series that would properly include the '---'
> separator.

An unofficial trick that works is to write the

    ---

     * This is an additional comment


yourself when running "git commit".  That will be propagated to the
output from format-patch.  You will have another "---" in front of
the diffstat, but nobody is hurt by that.

But when doing a big series that deserves a cover letter [PATCH 0/n],
you will use editor on the output from format-patch anyway, and I
find it simpler to do the follow-on comments at that point myself.

Personal preferences vary, so whatever makes you feel comfortable
with and works well for you is good.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-17 23:49       ` Junio C Hamano
@ 2012-09-18  0:15         ` Jeff King
  2012-09-18  0:36           ` Junio C Hamano
  2012-09-18 20:42         ` Wesley J. Landaker
  1 sibling, 1 reply; 34+ messages in thread
From: Jeff King @ 2012-09-18  0:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Philip Oakley, Git List

On Mon, Sep 17, 2012 at 04:49:39PM -0700, Junio C Hamano wrote:

> > However, when it came to creating the series, with comments, I
> > couldn't see a way of having my comments within my local commits, but
> > preparing a patch series that would properly include the '---'
> > separator.
> 
> An unofficial trick that works is to write the
> 
>     ---
> 
>      * This is an additional comment
> 
> 
> yourself when running "git commit".  That will be propagated to the
> output from format-patch.  You will have another "---" in front of
> the diffstat, but nobody is hurt by that.

But note that using "format-patch -s" will break; it puts the sign-off
below the "---".

> But when doing a big series that deserves a cover letter [PATCH 0/n],
> you will use editor on the output from format-patch anyway, and I
> find it simpler to do the follow-on comments at that point myself.

Me too (actually, I load it all into mutt and then comment on each as I
send them out, but it amounts to the same thing, seeing as how my MUA
just invokes $EDITOR when I edit a mail).

-Peff

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18  0:15         ` Jeff King
@ 2012-09-18  0:36           ` Junio C Hamano
  2012-09-18 18:55             ` Jeff King
  0 siblings, 1 reply; 34+ messages in thread
From: Junio C Hamano @ 2012-09-18  0:36 UTC (permalink / raw)
  To: Jeff King; +Cc: Philip Oakley, Git List

Jeff King <peff@peff.net> writes:

> On Mon, Sep 17, 2012 at 04:49:39PM -0700, Junio C Hamano wrote:
>
>> > However, when it came to creating the series, with comments, I
>> > couldn't see a way of having my comments within my local commits, but
>> > preparing a patch series that would properly include the '---'
>> > separator.
>> 
>> An unofficial trick that works is to write the
>> 
>>     ---
>> 
>>      * This is an additional comment
>> 
>> 
>> yourself when running "git commit".  That will be propagated to the
>> output from format-patch.  You will have another "---" in front of
>> the diffstat, but nobody is hurt by that.
>
> But note that using "format-patch -s" will break; it puts the sign-off
> below the "---".

I think "format-patch -s" is a workflow mistake in the first place.
You should be doing the sign-off the commit when you commit in the
first place.  It is not like "I cannot sign off because I think it
is still iffy" or anything.

But your point still stands; "commit -s" will not see through that
official trick either ;-).

>> But when doing a big series that deserves a cover letter [PATCH 0/n],
>> you will use editor on the output from format-patch anyway, and I
>> find it simpler to do the follow-on comments at that point myself.
>
> Me too (actually, I load it all into mutt and then comment on each as I
> send them out, but it amounts to the same thing, seeing as how my MUA
> just invokes $EDITOR when I edit a mail).

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18  0:36           ` Junio C Hamano
@ 2012-09-18 18:55             ` Jeff King
  2012-09-18 19:11               ` Junio C Hamano
  0 siblings, 1 reply; 34+ messages in thread
From: Jeff King @ 2012-09-18 18:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Philip Oakley, Git List

On Mon, Sep 17, 2012 at 05:36:35PM -0700, Junio C Hamano wrote:

> > But note that using "format-patch -s" will break; it puts the sign-off
> > below the "---".
> 
> I think "format-patch -s" is a workflow mistake in the first place.
> You should be doing the sign-off the commit when you commit in the
> first place.  It is not like "I cannot sign off because I think it
> is still iffy" or anything.

It is mostly "I do not signoff because typing '-s' each time I commit is
a pain". I know it is supposed to be a conscious decision, but I think
it is a little silly. Since everything I do for git.git is going to be
signed-off, either I will automate it, or I will end up typing it
without thinking.  Whenever I make a conscious decision, it is to
_withhold_ signoff, since that is the exceptional case.

But even without that, I still think format-patch is a reasonable time
to do it. It is the time when I proof-read my commit message and patch
in its final form, and think "do I really want to send this?". That
seems to me like a reasonable time to make such a conscious decision to
signoff (or not).

> But your point still stands; "commit -s" will not see through that
> official trick either ;-).

Yes. :)

-Peff

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18 18:55             ` Jeff King
@ 2012-09-18 19:11               ` Junio C Hamano
  2012-09-18 19:16                 ` Jeff King
  0 siblings, 1 reply; 34+ messages in thread
From: Junio C Hamano @ 2012-09-18 19:11 UTC (permalink / raw)
  To: Jeff King; +Cc: Philip Oakley, Git List

Jeff King <peff@peff.net> writes:

> But even without that, I still think format-patch is a reasonable time
> to do it. It is the time when I proof-read my commit message and patch
> in its final form, and think "do I really want to send this?".

But it is not like "I cannot sign off because I think it is still
iffy".

> seems to me like a reasonable time to make such a conscious decision to
> signoff (or not).
>
>> But your point still stands; "commit -s" will not see through that
>> official trick either ;-).
>
> Yes. :)

Actually, no.  "commit -s" does not have any need to see through it.

	... hack hack hack ...
        $ git commit -a -s
        ... editor opens, you see your Sign-off at the end, with
        ... the cursor sitting on the first line
        ... edit the title, move to the line below the Sign-off,
        ... and do the "---\n\n * comment" thing.

And this survives "rebase -i" (but not "format-patch | am" for
obvious reasons).

So I take it back.  The time you do the "git commit" for the very
first time for this change that may need to be rerolled number of
times is the right time to say "-s".

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18 19:11               ` Junio C Hamano
@ 2012-09-18 19:16                 ` Jeff King
  2012-09-18 19:47                   ` Junio C Hamano
  0 siblings, 1 reply; 34+ messages in thread
From: Jeff King @ 2012-09-18 19:16 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Philip Oakley, Git List

On Tue, Sep 18, 2012 at 12:11:58PM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > But even without that, I still think format-patch is a reasonable time
> > to do it. It is the time when I proof-read my commit message and patch
> > in its final form, and think "do I really want to send this?".
> 
> But it is not like "I cannot sign off because I think it is still
> iffy".

No, that is not the particular reason in my case, but I think I
explained other reasons why "format-patch -s" is not a wrong workflow.

> >> But your point still stands; "commit -s" will not see through that
> >> official trick either ;-).
> >
> > Yes. :)
> 
> Actually, no.  "commit -s" does not have any need to see through it.
> 
> 	... hack hack hack ...
>         $ git commit -a -s
>         ... editor opens, you see your Sign-off at the end, with
>         ... the cursor sitting on the first line
>         ... edit the title, move to the line below the Sign-off,
>         ... and do the "---\n\n * comment" thing.
> 
> And this survives "rebase -i" (but not "format-patch | am" for
> obvious reasons).

Yes, if your particular workflow is to signoff the very first time you
commit. But it would not work for:

 ... hack hack hack ...
 $ git commit -a
   ... make a note after "---" ...

 ... hack hack hack ...
 ... OK, looks good, ready to signoff ...
 $ git commit --amend -s

So it can work, but it is workflow dependent, and in general is a little
flaky with the automagic signoff. You may want to signoff later for a
variety of reasons, not the least of which is that you forgot to type
"-s" the first time.

> So I take it back.  The time you do the "git commit" for the very
> first time for this change that may need to be rerolled number of
> times is the right time to say "-s".

If you remember to type it. :)

-Peff

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18 19:16                 ` Jeff King
@ 2012-09-18 19:47                   ` Junio C Hamano
  2012-09-18 20:10                     ` Philip Oakley
  2012-09-18 20:16                     ` Jeff King
  0 siblings, 2 replies; 34+ messages in thread
From: Junio C Hamano @ 2012-09-18 19:47 UTC (permalink / raw)
  To: Jeff King; +Cc: Philip Oakley, Git List

Jeff King <peff@peff.net> writes:

> On Tue, Sep 18, 2012 at 12:11:58PM -0700, Junio C Hamano wrote:
>
>> Jeff King <peff@peff.net> writes:
>> 
>> > But even without that, I still think format-patch is a reasonable time
>> > to do it. It is the time when I proof-read my commit message and patch
>> > in its final form, and think "do I really want to send this?".
>> 
>> But it is not like "I cannot sign off because I think it is still
>> iffy".
>
> No, that is not the particular reason in my case, but I think I
> explained other reasons why "format-patch -s" is not a wrong workflow.

Then I didn't read it.  What does "do I really want to send this?"
have anything to do with DCO in any case?

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18 19:47                   ` Junio C Hamano
@ 2012-09-18 20:10                     ` Philip Oakley
  2012-09-18 20:16                       ` Jeff King
  2012-09-18 20:16                     ` Jeff King
  1 sibling, 1 reply; 34+ messages in thread
From: Philip Oakley @ 2012-09-18 20:10 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: Git List

From: "Junio C Hamano" <gitster@pobox.com>
> have anything to do with DCO in any case?

Junio,
What's DCO an abbreviation of?
Philip

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18 19:47                   ` Junio C Hamano
  2012-09-18 20:10                     ` Philip Oakley
@ 2012-09-18 20:16                     ` Jeff King
  1 sibling, 0 replies; 34+ messages in thread
From: Jeff King @ 2012-09-18 20:16 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Philip Oakley, Git List

On Tue, Sep 18, 2012 at 12:47:36PM -0700, Junio C Hamano wrote:

> >> Jeff King <peff@peff.net> writes:
> >> 
> >> > But even without that, I still think format-patch is a reasonable time
> >> > to do it. It is the time when I proof-read my commit message and patch
> >> > in its final form, and think "do I really want to send this?".
> >> 
> >> But it is not like "I cannot sign off because I think it is still
> >> iffy".
> >
> > No, that is not the particular reason in my case, but I think I
> > explained other reasons why "format-patch -s" is not a wrong workflow.
> 
> Then I didn't read it.  What does "do I really want to send this?"
> have anything to do with DCO in any case?

Because it is an excellent time to think about "am I willing and able to
agree to the DCO?" As I said, for me personally working on git.git, that
is not generally an issue. But I think it is perfectly reasonable for
somebody to work and commit in isolation, and then only decide on the
DCO during the sending phase (perhaps because they need to clear it with
their company's legal department or some such). In other words, "it is
iffy at the time of commit" might be exactly the reason for some people.

If you are responding to my "that is not the particular reason in my
case", I will paraphrase the reason I gave earlier: I find it annoying
and pointless to type "-s" on every commit. We do not have
commit.signoff, but we do have format.signoff.

-Peff

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-18 20:10                     ` Philip Oakley
@ 2012-09-18 20:16                       ` Jeff King
  0 siblings, 0 replies; 34+ messages in thread
From: Jeff King @ 2012-09-18 20:16 UTC (permalink / raw)
  To: Philip Oakley; +Cc: Junio C Hamano, Git List

On Tue, Sep 18, 2012 at 09:10:26PM +0100, Philip Oakley wrote:

> From: "Junio C Hamano" <gitster@pobox.com>
> >have anything to do with DCO in any case?
> 
> What's DCO an abbreviation of?

Developer's Certificate of Origin. See SubmittingPatches.

-Peff

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-17 23:49       ` Junio C Hamano
  2012-09-18  0:15         ` Jeff King
@ 2012-09-18 20:42         ` Wesley J. Landaker
  1 sibling, 0 replies; 34+ messages in thread
From: Wesley J. Landaker @ 2012-09-18 20:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Philip Oakley, Git List

On Monday, September 17, 2012 17:49:39 Junio C Hamano wrote:
> "Philip Oakley" <philipoakley@iee.org> writes:
> > I then applied it (using git am) to a temp branch to see what it
> > produced, and could repeat the cycle until the patches looked right.
> 
> That's another obvious and valid way to prepare your series.  It all
> depends on how comfortable you are to directly edit patches.  Some
> people fear it.  Some don't.  Some can do it with their eyes closed ;-).
> 
> > However, when it came to creating the series, with comments, I
> > couldn't see a way of having my comments within my local commits, but
> > preparing a patch series that would properly include the '---'
> > separator.
> 
> An unofficial trick that works is to write the
> 
>     ---
> 
>      * This is an additional comment
> 
> 
> yourself when running "git commit".  That will be propagated to the
> output from format-patch.  You will have another "---" in front of
> the diffstat, but nobody is hurt by that.

One thing I have done is to add the additional comments I want with "git 
notes", then give the "--notes" option to format-patch or send-email.

Unfortunately, this sticks the notes right into the commit message section, 
because the "--notes" option is actually a diff option, not something 
format-patch knows about, so you have to make sure to manually move it.

But even so, I've found it a a nice way to track comments.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: How to create the " [PATCH 0/5]" first email?
  2012-09-15 17:08   ` Junio C Hamano
  2012-09-17 22:55     ` Philip Oakley
@ 2012-09-23 12:03     ` Jan Engelhardt
  1 sibling, 0 replies; 34+ messages in thread
From: Jan Engelhardt @ 2012-09-23 12:03 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Philip Oakley, Git List


On Saturday 2012-09-15 19:08, Junio C Hamano wrote:
>
>If you plan to use "git send-email" to send the final results out,
>you should consider "git send-email" as your "MUA" in the quoted
>paragraph.  And that will be very platform independent viewpoint to
>see things from.
>
>"git format-patch -o my-series/ --cover-letter ..."  would treat
>"my-series/" directory as "MUA's drafts folder" and prepares the
>messages you would want to send out, and you can proof-read and edit
>the files in there before telling your "MUA" to send them out, with
>"git send-email ... my-series/*.patch" or something.

One can also send [0/n] with a normal MUA, and then use

 git send-email --in-reply-to '<messageidof0@yourhost.no>' commitrange


It's not like 0/n has to be emitted at the same second 1/n is :)

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 6/5] side-step a make rule that builds t3070-wildmatch
  2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
                     ` (4 preceding siblings ...)
  2012-09-16 15:27   ` [PATCH v2 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
@ 2012-09-25  7:01   ` Johannes Sixt
  5 siblings, 0 replies; 34+ messages in thread
From: Johannes Sixt @ 2012-09-25  7:01 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

From: Johannes Sixt <j6t@kdbg.org>

Running "make t3070-wildmatch" aborts with this error:

   $ make t3070-wildmatch
   *** t3070-wildmatch.sh ***
   # passed all 11 test(s)
   1..11
   cat t3070-wildmatch.sh >t3070-wildmatch
   /bin/sh.exe: t3070-wildmatch: File exists
   make: *** [t3070-wildmatch] Error 1

It seems that there is a make rule that attempts to transform a *.sh file
into a file without the extension. The rule fails because t3070-wildmatch
is a directory. Rename it to t3070.

Of course, nobody would run exactly "make t3070-wildmatch". But it is
common (at least for me) to run "make t[3-9]*" or similar, which would
include t3070-wildmatch. With this patch, it includes t3070, but that does
not hurt because make just skips it with "Nothing to be done for `t3070'."

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
 Feel free to squash this patch if you re-roll the series.

 t/t3070-wildmatch.sh                      | 2 +-
 t/{t3070-wildmatch => t3070}/wildtest.txt | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename t/{t3070-wildmatch => t3070}/wildtest.txt (100%)

diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 7fb63ff..c4da26c 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -6,7 +6,7 @@ test_description='wildmatch tests'

 test_wildmatch() {
     test_expect_success "wildmatch $*" "
-	test-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual &&
+	test-wildmatch $* ../t3070/wildtest.txt >actual &&
 	echo 'No wildmatch errors found.' >expected &&
 	test_cmp expected actual
     "
diff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070/wildtest.txt
similarity index 100%
rename from t/t3070-wildmatch/wildtest.txt
rename to t/t3070/wildtest.txt
-- 
1.7.12.1.1626.gf25df0e

^ permalink raw reply related	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2012-09-25  7:02 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
2012-09-16  6:49   ` Junio C Hamano
2012-09-15 12:02 ` [PATCH 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
2012-09-15 14:27 ` How to create the " [PATCH 0/5]" first email? Philip Oakley
2012-09-15 17:08   ` Junio C Hamano
2012-09-17 22:55     ` Philip Oakley
2012-09-17 23:49       ` Junio C Hamano
2012-09-18  0:15         ` Jeff King
2012-09-18  0:36           ` Junio C Hamano
2012-09-18 18:55             ` Jeff King
2012-09-18 19:11               ` Junio C Hamano
2012-09-18 19:16                 ` Jeff King
2012-09-18 19:47                   ` Junio C Hamano
2012-09-18 20:10                     ` Philip Oakley
2012-09-18 20:16                       ` Jeff King
2012-09-18 20:16                     ` Jeff King
2012-09-18 20:42         ` Wesley J. Landaker
2012-09-23 12:03     ` Jan Engelhardt
2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
2012-09-16 15:27   ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
2012-09-16 15:27   ` [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
2012-09-16 15:27   ` [PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
2012-09-16 15:27   ` [PATCH v2 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
2012-09-17  5:31     ` Junio C Hamano
2012-09-17  5:54       ` Junio C Hamano
2012-09-17  5:57         ` Nguyen Thai Ngoc Duy
2012-09-17 12:40         ` Nguyen Thai Ngoc Duy
2012-09-17 17:20           ` Junio C Hamano
2012-09-16 15:27   ` [PATCH v2 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
2012-09-25  7:01   ` [PATCH 6/5] side-step a make rule that builds t3070-wildmatch Johannes Sixt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).