Code

magic pathspec: futureproof shorthand form
authorJunio C Hamano <gitster@pobox.com>
Fri, 8 Apr 2011 23:18:46 +0000 (16:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 8 Apr 2011 23:19:48 +0000 (16:19 -0700)
The earlier design was to take whatever non-alnum that the short format
parser happens to support, leaving the rest as part of the pattern, so a
version of git that knows '*' magic and a version that does not would have
behaved differently when given ":*Makefile".  The former would have
applied the '*' magic to the pattern "Makefile", while the latter would
used no magic to the pattern "*Makefile".

Instead, just reserve all non-alnum ASCII letters that are neither glob
nor regexp special as potential magic signature, and when we see a magic
that is not supported, die with an error message, just like the longhand
codepath does.

With this, ":%#!*Makefile" will always mean "%#!" magic applied to the
pattern "*Makefile", no matter what version of git is used (it is a
different matter if the version of git supports all of these three magic
matching rules).

Also make ':' without anything else to mean "there is no pathspec".  This
would allow differences between "git log" and "git log ." run from the top
level of the working tree (the latter simplifies no-op commits away from
the history) to be expressed from a subdirectory by saying "git log :".

Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
ctype.c
git-compat-util.h
setup.c

diff --git a/ctype.c b/ctype.c
index de600279eef4765db497599e6654c2bedd978129..b5d856fd26bd892a5f18202b054fc53e7c953429 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -10,17 +10,18 @@ enum {
        A = GIT_ALPHA,
        D = GIT_DIGIT,
        G = GIT_GLOB_SPECIAL,   /* *, ?, [, \\ */
-       R = GIT_REGEX_SPECIAL   /* $, (, ), +, ., ^, {, | */
+       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | */
+       P = GIT_PATHSPEC_MAGIC  /* other non-alnum, except for ] and } */
 };
 
 unsigned char sane_ctype[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,         /*   0.. 15 */
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         /*  16.. 31 */
-       S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0,         /*  32.. 47 */
-       D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,         /*  48.. 63 */
-       0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  64.. 79 */
-       A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0,         /*  80.. 95 */
-       0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  96..111 */
-       A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0,         /* 112..127 */
+       S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P,         /*  32.. 47 */
+       D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G,         /*  48.. 63 */
+       P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  64.. 79 */
+       A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P,         /*  80.. 95 */
+       P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  96..111 */
+       A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0,         /* 112..127 */
        /* Nothing in the 128.. range */
 };
index 49b50eec86828066c90b6165c35ec21cb51e85ce..d88cf8afb67f1140164a640d17a28b0a3e67599a 100644 (file)
@@ -462,6 +462,7 @@ extern unsigned char sane_ctype[256];
 #define GIT_ALPHA 0x04
 #define GIT_GLOB_SPECIAL 0x08
 #define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isascii(x) (((x) & ~0x7f) == 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
@@ -472,6 +473,7 @@ extern unsigned char sane_ctype[256];
 #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
+#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
 
 static inline int sane_case(int x, int high)
 {
diff --git a/setup.c b/setup.c
index 820ed05b27292dbb72ccce8cf8a1fb7f6b77a2e5..5048252d78638c2c6adcf8807442728ef4f37a5e 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -197,19 +197,26 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
                }
                if (*copyfrom == ')')
                        copyfrom++;
+       } else if (!elt[1]) {
+               /* Just ':' -- no element! */
+               return NULL;
        } else {
                /* shorthand */
                for (copyfrom = elt + 1;
                     *copyfrom && *copyfrom != ':';
                     copyfrom++) {
                        char ch = *copyfrom;
+
+                       if (!is_pathspec_magic(ch))
+                               break;
                        for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
                                if (pathspec_magic[i].mnemonic == ch) {
                                        magic |= pathspec_magic[i].bit;
                                        break;
                                }
                        if (ARRAY_SIZE(pathspec_magic) <= i)
-                               break;
+                               die("Unimplemented pathspec magic '%c' in '%s'",
+                                   ch, elt);
                }
                if (*copyfrom == ':')
                        copyfrom++;