From 8a42c9850177cc91e9f38779e8aca89682a02975 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 6 Apr 2011 16:11:56 -0700 Subject: magic pathspec: add tentative ":/path/from/top/level" pathspec support Support ":/" magic string that can be prefixed to a pathspec element to say "this names the path from the top-level of the working tree", when you are in the subdirectory. For example, you should be able to say: $ edit Makefile ;# top-level $ cd Documentation $ edit git.txt ;# in the subdirectory and then do one of three things, still inside the subdirectory: $ git add -u . ;# add only Documentation/git.txt $ git add -u :/ ;# add everything, including paths outside Documentation $ git add -u ;# whatever the default setting is. To truly support magic pathspec, the API needs to be restructured so that get_pathspec() and init_pathspec() are unified into one call. Currently, the former just prefixes the user supplied pathspec with the current subdirectory path, and the latter takes the output from the former and pre-parses them into a bit richer structure for easier handling. They should become a single API function that takes the current subdirectory path and the remainder of argv[] (after parsing --options and revision arguments from the command line) and returns an array of parsed pathspec elements, and "magic" should become attributes of struct pathspec_item. This patch implements only "top" magic because it can be hacked into the system without such a refactoring. The syntax for magic pathspec prefix is designed to be extensible yet simple to type to invoke a simple magic like "from the top". The parser for the magic prefix is hooked into get_pathspec() function in this patch, and it needs to be moved when we refactor the API. But we have to start from somewhere. Signed-off-by: Junio C Hamano diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 33716a3..e51d7e6 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a Pattern used to specify paths. + Pathspecs are used on the command line of "git ls-files", "git -ls-tree", "git grep", "git checkout", and many other commands to +ls-tree", "git add", "git grep", "git diff", "git checkout", +and many other commands to limit the scope of operations to some subset of the tree or worktree. See the documentation of each command for whether paths are relative to the current directory or toplevel. The @@ -296,6 +297,34 @@ For example, Documentation/*.jpg will match all .jpg files in the Documentation subtree, including Documentation/chapter_1/figure_1.jpg. ++ +A pathspec that begins with a colon `:` has special meaning. In the +short form, the leading colon `:` is followed by zero or more "magic +signature" letters (which optionally is terminated by another colon `:`), +and the remainder is the pattern to match against the path. The optional +colon that terminates the "magic signature" can be omitted if the pattern +begins with a character that cannot be a "magic signature" and is not a +colon. ++ +In the long form, the leading colon `:` is followed by a open +parenthesis `(`, a comma-separated list of zero or more "magic words", +and a close parentheses `)`, and the remainder is the pattern to match +against the path. ++ +The "magic signature" consists of an ASCII symbol that is not +alphanumeric. ++ +-- +top `/`;; + The magic word `top` (mnemonic: `/`) makes the pattern match + from the root of the working tree, even when you are running + the command from inside a subdirectory. +-- ++ +Currently only the slash `/` is recognized as the "magic signature", +but it is envisioned that we will support more types of magic in later +versions of git. + [[def_parent]]parent:: A <> contains a (possibly empty) list of the logical predecessor(s) in the line of development, i.e. its diff --git a/setup.c b/setup.c index 03cd84f..820ed05 100644 --- a/setup.c +++ b/setup.c @@ -126,6 +126,101 @@ void verify_non_filename(const char *prefix, const char *arg) "Use '--' to separate filenames from revisions", arg); } +/* + * Magic pathspec + * + * NEEDSWORK: These need to be moved to dir.h or even to a new + * pathspec.h when we restructure get_pathspec() users to use the + * "struct pathspec" interface. + * + * Possible future magic semantics include stuff like: + * + * { PATHSPEC_NOGLOB, '!', "noglob" }, + * { PATHSPEC_ICASE, '\0', "icase" }, + * { PATHSPEC_RECURSIVE, '*', "recursive" }, + * { PATHSPEC_REGEXP, '\0', "regexp" }, + * + */ +#define PATHSPEC_FROMTOP (1<<0) + +struct pathspec_magic { + unsigned bit; + char mnemonic; /* this cannot be ':'! */ + const char *name; +} pathspec_magic[] = { + { PATHSPEC_FROMTOP, '/', "top" }, +}; + +/* + * Take an element of a pathspec and check for magic signatures. + * Append the result to the prefix. + * + * For now, we only parse the syntax and throw out anything other than + * "top" magic. + * + * NEEDSWORK: This needs to be rewritten when we start migrating + * get_pathspec() users to use the "struct pathspec" interface. For + * example, a pathspec element may be marked as case-insensitive, but + * the prefix part must always match literally, and a single stupid + * string cannot express such a case. + */ +const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) +{ + unsigned magic = 0; + const char *copyfrom = elt; + int i; + + if (elt[0] != ':') { + ; /* nothing to do */ + } else if (elt[1] == '(') { + /* longhand */ + const char *nextat; + for (copyfrom = elt + 2; + *copyfrom && *copyfrom != ')'; + copyfrom = nextat) { + size_t len = strcspn(copyfrom, ",)"); + if (copyfrom[len] == ')') + nextat = copyfrom + len; + else + nextat = copyfrom + len + 1; + if (!len) + continue; + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) + if (strlen(pathspec_magic[i].name) == len && + !strncmp(pathspec_magic[i].name, copyfrom, len)) { + magic |= pathspec_magic[i].bit; + break; + } + if (ARRAY_SIZE(pathspec_magic) <= i) + die("Invalid pathspec magic '%.*s' in '%s'", + (int) len, copyfrom, elt); + } + if (*copyfrom == ')') + copyfrom++; + } else { + /* shorthand */ + for (copyfrom = elt + 1; + *copyfrom && *copyfrom != ':'; + copyfrom++) { + char ch = *copyfrom; + 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; + } + if (*copyfrom == ':') + copyfrom++; + } + + if (magic & PATHSPEC_FROMTOP) + return xstrdup(copyfrom); + else + return prefix_path(prefix, prefixlen, copyfrom); +} + const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; @@ -147,8 +242,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec) dst = pathspec; prefixlen = prefix ? strlen(prefix) : 0; while (*src) { - const char *p = prefix_path(prefix, prefixlen, *src); - *(dst++) = p; + *(dst++) = prefix_pathspec(prefix, prefixlen, *src); src++; } *dst = NULL; -- cgit v0.10.2-6-g49f6 From 2f6c9760debfb4705f6efb5862e2b3a23b2b951c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 8 Apr 2011 16:18:46 -0700 Subject: magic pathspec: futureproof shorthand form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano diff --git a/ctype.c b/ctype.c index de60027..b5d856f 100644 --- 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 */ }; diff --git a/git-compat-util.h b/git-compat-util.h index 49b50ee..d88cf8a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -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 820ed05..5048252 100644 --- 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++; -- cgit v0.10.2-6-g49f6 From d0546e2d488b1ba185c430b638619ab1d91af509 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 6 Apr 2011 20:56:19 -0700 Subject: magic pathspec: add ":(icase)path" to match case insensitively Signed-off-by: Junio C Hamano diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index e51d7e6..0ca029b 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -319,10 +319,13 @@ top `/`;; The magic word `top` (mnemonic: `/`) makes the pattern match from the root of the working tree, even when you are running the command from inside a subdirectory. +icase;; + The magic word `icase` (there is no mnemonic for it) makes the + pattern match case insensitively. E.g. `:(icase)makefile` matches + both `Makefile` and `makefile`. -- + -Currently only the slash `/` is recognized as the "magic signature", -but it is envisioned that we will support more types of magic in later +It is envisioned that we will support more types of magic in later versions of git. [[def_parent]]parent:: diff --git a/setup.c b/setup.c index 5048252..51e354c 100644 --- a/setup.c +++ b/setup.c @@ -136,12 +136,12 @@ void verify_non_filename(const char *prefix, const char *arg) * Possible future magic semantics include stuff like: * * { PATHSPEC_NOGLOB, '!', "noglob" }, - * { PATHSPEC_ICASE, '\0', "icase" }, * { PATHSPEC_RECURSIVE, '*', "recursive" }, * { PATHSPEC_REGEXP, '\0', "regexp" }, * */ #define PATHSPEC_FROMTOP (1<<0) +#define PATHSPEC_ICASE (1<<1) struct pathspec_magic { unsigned bit; @@ -149,6 +149,7 @@ struct pathspec_magic { const char *name; } pathspec_magic[] = { { PATHSPEC_FROMTOP, '/', "top" }, + { PATHSPEC_ICASE, '\0', "icase" }, }; /* @@ -168,7 +169,8 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) { unsigned magic = 0; const char *copyfrom = elt; - int i; + const char *retval; + int i, free_source = 0; if (elt[0] != ':') { ; /* nothing to do */ @@ -222,10 +224,31 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) copyfrom++; } + if (magic & PATHSPEC_ICASE) { + struct strbuf sb = STRBUF_INIT; + for (i = 0; copyfrom[i]; i++) { + int ch = copyfrom[i]; + if (('a' <= ch && ch <= 'z') || + ('A' <= ch && ch <= 'Z')) { + strbuf_addf(&sb, "[%c%c]", + tolower(ch), toupper(ch)); + } else { + strbuf_addch(&sb, ch); + } + } + if (sb.len) { + free_source = 1; + copyfrom = strbuf_detach(&sb, NULL); + } + } + if (magic & PATHSPEC_FROMTOP) - return xstrdup(copyfrom); + retval = xstrdup(copyfrom); else - return prefix_path(prefix, prefixlen, copyfrom); + retval = prefix_path(prefix, prefixlen, copyfrom); + if (free_source) + free((char *)copyfrom); + return retval; } const char **get_pathspec(const char *prefix, const char **pathspec) -- cgit v0.10.2-6-g49f6 From 6d9429271013898df103f7e77ed0736cdfab01b8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 May 2011 10:23:41 -0700 Subject: Revert "magic pathspec: add ":(icase)path" to match case insensitively" This reverts commit d0546e2d488b1ba185c430b638619ab1d91af509, which was only meant to be a Proof-of-concept used during the discussion. The real implementation of the feature needs to wait until we migrate all the code to use "struct pathspec", not "char **", to represent richer semantics given to pathspec. diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 0ca029b..e51d7e6 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -319,13 +319,10 @@ top `/`;; The magic word `top` (mnemonic: `/`) makes the pattern match from the root of the working tree, even when you are running the command from inside a subdirectory. -icase;; - The magic word `icase` (there is no mnemonic for it) makes the - pattern match case insensitively. E.g. `:(icase)makefile` matches - both `Makefile` and `makefile`. -- + -It is envisioned that we will support more types of magic in later +Currently only the slash `/` is recognized as the "magic signature", +but it is envisioned that we will support more types of magic in later versions of git. [[def_parent]]parent:: diff --git a/setup.c b/setup.c index 51e354c..5048252 100644 --- a/setup.c +++ b/setup.c @@ -136,12 +136,12 @@ void verify_non_filename(const char *prefix, const char *arg) * Possible future magic semantics include stuff like: * * { PATHSPEC_NOGLOB, '!', "noglob" }, + * { PATHSPEC_ICASE, '\0', "icase" }, * { PATHSPEC_RECURSIVE, '*', "recursive" }, * { PATHSPEC_REGEXP, '\0', "regexp" }, * */ #define PATHSPEC_FROMTOP (1<<0) -#define PATHSPEC_ICASE (1<<1) struct pathspec_magic { unsigned bit; @@ -149,7 +149,6 @@ struct pathspec_magic { const char *name; } pathspec_magic[] = { { PATHSPEC_FROMTOP, '/', "top" }, - { PATHSPEC_ICASE, '\0', "icase" }, }; /* @@ -169,8 +168,7 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) { unsigned magic = 0; const char *copyfrom = elt; - const char *retval; - int i, free_source = 0; + int i; if (elt[0] != ':') { ; /* nothing to do */ @@ -224,31 +222,10 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) copyfrom++; } - if (magic & PATHSPEC_ICASE) { - struct strbuf sb = STRBUF_INIT; - for (i = 0; copyfrom[i]; i++) { - int ch = copyfrom[i]; - if (('a' <= ch && ch <= 'z') || - ('A' <= ch && ch <= 'Z')) { - strbuf_addf(&sb, "[%c%c]", - tolower(ch), toupper(ch)); - } else { - strbuf_addch(&sb, ch); - } - } - if (sb.len) { - free_source = 1; - copyfrom = strbuf_detach(&sb, NULL); - } - } - if (magic & PATHSPEC_FROMTOP) - retval = xstrdup(copyfrom); + return xstrdup(copyfrom); else - retval = prefix_path(prefix, prefixlen, copyfrom); - if (free_source) - free((char *)copyfrom); - return retval; + return prefix_path(prefix, prefixlen, copyfrom); } const char **get_pathspec(const char *prefix, const char **pathspec) -- cgit v0.10.2-6-g49f6 From b060ce7de42b357af013909039da3f08a68f3c0b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 May 2011 12:07:12 -0700 Subject: pathspec: drop "lone : means no pathspec" from get_pathspec() We may want to give the pathspec subsystem such a feature, but not while we are still using get_pathspec() that returns a stupid "char **" that loses subtle nuances that existed in the input string. In the meantime, the callers of get_pathspec() that want to support it could do an equivalent before feeding their argv[] to the function themselves quite easily. Signed-off-by: Junio C Hamano diff --git a/setup.c b/setup.c index 5048252..84f71d5 100644 --- a/setup.c +++ b/setup.c @@ -197,9 +197,6 @@ 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; -- cgit v0.10.2-6-g49f6 From 7c5f3cc4a5680e23b8aa378ed9b655a1779ee881 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 May 2011 21:34:04 -0700 Subject: grep: use get_pathspec() correctly When there is no remaining string in argv, get_pathspec(prefix, argv) will return a two-element array that has prefix as the first element, so there is no need to re-roll that logic in the code that uses get_pathspec(). Signed-off-by: Junio C Hamano diff --git a/builtin/grep.c b/builtin/grep.c index 0bf8c01..222dd6d 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -956,13 +956,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) verify_filename(prefix, argv[j]); } - if (i < argc) - paths = get_pathspec(prefix, argv + i); - else if (prefix) { - paths = xcalloc(2, sizeof(const char *)); - paths[0] = prefix; - paths[1] = NULL; - } + paths = get_pathspec(prefix, argv + i); init_pathspec(&pathspec, paths); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; -- cgit v0.10.2-6-g49f6 From 9619617d33c83ad873539384b3cbfd564053be76 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 May 2011 11:10:30 -0700 Subject: fix overstrict : diagnosis Given "git log :", we get a disambiguation message that tries to be helpful and yet totally misses the point, i.e. $ git log : fatal: Path '' does not exist (neither on disk nor in the index). $ git log :/ fatal: Path '/' exists on disk, but not in the index. An empty path nor anything that begins with '/' cannot possibly in the index, and it is wrong to guess that the user might have meant to access such an index entry. It should yield the same error message as "git log '*.c'", i.e. $ git log '*.c' fatal: ambiguous argument '*.c': unknown revision or path not in the working tree. Use '--' to separate paths from revisions Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index faea58d..90d8bfa 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1173,7 +1173,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1, } pos++; } - if (!gently) + if (!gently && name[1] && name[1] != '/') diagnose_invalid_index_path(stage, prefix, cp); free(new_path); return -1; -- cgit v0.10.2-6-g49f6 From 2e83b66c32c1d482575fd8caed80680a2f69c5f1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 May 2011 12:02:54 -0700 Subject: fix overslow :/no-such-string-ever-existed diagnostics "git cmd :/no-such-string-ever-existed" runs an extra round of get_sha1() since 009fee4 (Detailed diagnosis when parsing an object name fails., 2009-12-07). Once without error diagnosis to see there is no commit with such a string in the log message (hence "it cannot be a ref"), and after seeing that :/no-such-string-ever-existed is not a filename (hence "it cannot be a path, either"), another time to give "better diagnosis". The thing is, the second time it runs, we already know that traversing the history all the way down to the root will _not_ find any matching commit. Rename misguided "gently" parameter, which is turned off _only_ when the "detailed diagnosis" codepath knows that it cannot be a ref and making the call only for the caller to die with a message. Flip its meaning (and adjust the callers) and call it "only_to_die", which is not a great name, but it describes far more clearly what the codepaths that switches their behaviour based on this variable do. On my box, the command spends ~1.8 seconds without the patch to make the report; with the patch it spends ~1.12 seconds. Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index be6ce72..a9e6419 100644 --- a/cache.h +++ b/cache.h @@ -785,15 +785,15 @@ struct object_context { }; extern int get_sha1(const char *str, unsigned char *sha1); -extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix); +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix); static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode) { - return get_sha1_with_mode_1(str, sha1, mode, 1, NULL); + return get_sha1_with_mode_1(str, sha1, mode, 0, NULL); } -extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix); +extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix); static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc) { - return get_sha1_with_context_1(str, sha1, orc, 1, NULL); + return get_sha1_with_context_1(str, sha1, orc, 0, NULL); } extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ diff --git a/setup.c b/setup.c index 84f71d5..7fde4fa 100644 --- a/setup.c +++ b/setup.c @@ -86,7 +86,7 @@ static void NORETURN die_verify_filename(const char *prefix, const char *arg) unsigned char sha1[20]; unsigned mode; /* try a detailed diagnostic ... */ - get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix); + get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix); /* ... or fall back the most general message. */ die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" "Use '--' to separate paths from revisions", arg); diff --git a/sha1_name.c b/sha1_name.c index 90d8bfa..ec83611 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1080,11 +1080,12 @@ static void diagnose_invalid_index_path(int stage, } -int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix) +int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, + int only_to_die, const char *prefix) { struct object_context oc; int ret; - ret = get_sha1_with_context_1(name, sha1, &oc, gently, prefix); + ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix); *mode = oc.mode; return ret; } @@ -1108,7 +1109,7 @@ static char *resolve_relative_path(const char *rel) int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *oc, - int gently, const char *prefix) + int only_to_die, const char *prefix) { int ret, bracket_depth; int namelen = strlen(name); @@ -1130,7 +1131,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct cache_entry *ce; char *new_path = NULL; int pos; - if (namelen > 2 && name[1] == '/') { + if (!only_to_die && namelen > 2 && name[1] == '/') { struct commit_list *list = NULL; for_each_ref(handle_one_ref, &list); return get_sha1_oneline(name + 2, sha1, list); @@ -1173,7 +1174,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1, } pos++; } - if (!gently && name[1] && name[1] != '/') + if (only_to_die && name[1] && name[1] != '/') diagnose_invalid_index_path(stage, prefix, cp); free(new_path); return -1; @@ -1189,7 +1190,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1, if (*cp == ':') { unsigned char tree_sha1[20]; char *object_name = NULL; - if (!gently) { + if (only_to_die) { object_name = xmalloc(cp-name+1); strncpy(object_name, name, cp-name); object_name[cp-name] = '\0'; @@ -1202,7 +1203,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1, if (new_filename) filename = new_filename; ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode); - if (!gently) { + if (only_to_die) { diagnose_invalid_sha1_path(prefix, filename, tree_sha1, object_name); free(object_name); @@ -1215,7 +1216,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1, free(new_filename); return ret; } else { - if (!gently) + if (only_to_die) die("Invalid object name '%s'.", object_name); } } -- cgit v0.10.2-6-g49f6 From 0e539dca51c298ca2ee102e0ca118797f2da99eb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 May 2011 12:05:01 -0700 Subject: rev/path disambiguation: further restrict "misspelled index entry" diag A colon followed by anything !isalnum() (e.g. ":/heh") at this point is known not to be an existing rev. Just give a generic "neither a rev nor a path" error message. Signed-off-by: Junio C Hamano diff --git a/setup.c b/setup.c index 7fde4fa..fd4ce59 100644 --- a/setup.c +++ b/setup.c @@ -85,8 +85,17 @@ static void NORETURN die_verify_filename(const char *prefix, const char *arg) { unsigned char sha1[20]; unsigned mode; - /* try a detailed diagnostic ... */ - get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix); + + /* + * Saying "'(icase)foo' does not exist in the index" when the + * user gave us ":(icase)foo" is just stupid. A magic pathspec + * begins with a colon and is followed by a non-alnum; do not + * let get_sha1_with_mode_1(only_to_die=1) to even trigger. + */ + if (!(arg[0] == ':' && !isalnum(arg[1]))) + /* try a detailed diagnostic ... */ + get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix); + /* ... or fall back the most general message. */ die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" "Use '--' to separate paths from revisions", arg); -- cgit v0.10.2-6-g49f6 From 6fd09f537c7a1b0a92587aa6f00ef7302a546a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 8 May 2011 18:08:26 +0700 Subject: t3703, t4208: add test cases for magic pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index e51d7e6..8f62d1a 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -324,6 +324,9 @@ top `/`;; Currently only the slash `/` is recognized as the "magic signature", but it is envisioned that we will support more types of magic in later versions of git. ++ +A pathspec with only a colon means "there is no pathspec". This form +should not be combined with other pathspec. [[def_parent]]parent:: A <> contains a (possibly empty) list diff --git a/t/t3703-add-magic-pathspec.sh b/t/t3703-add-magic-pathspec.sh new file mode 100755 index 0000000..ce5585e --- /dev/null +++ b/t/t3703-add-magic-pathspec.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +test_description='magic pathspec tests using git-add' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir sub anothersub && + : >sub/foo && + : >anothersub/foo +' + +test_expect_success 'add :/' " + cat >expected <<-EOF && + add 'anothersub/foo' + add 'expected' + add 'sub/actual' + add 'sub/foo' + EOF + (cd sub && git add -n :/ >actual) && + test_cmp expected sub/actual +" + +cat >expected <actual) && + test_cmp expected sub/actual +' + +test_expect_success 'add :/non-existent' ' + (cd sub && test_must_fail git add -n :/non-existent) +' + +cat >expected <":(icase)ha" && + test_must_fail git add -n ":(icase)ha" && + git add -n "./:(icase)ha" +' + +test_expect_success 'a file with the same (short) magic name exists' ' + mkdir ":" && + : >":/bar" && + test_must_fail git add -n :/bar && + git add -n "./:/bar" +' + +test_done diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh new file mode 100755 index 0000000..2c482b6 --- /dev/null +++ b/t/t4208-log-magic-pathspec.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='magic pathspec tests using git-log' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit initial && + test_tick && + git commit --allow-empty -m empty && + mkdir sub +' + +test_expect_success '"git log :/" should be ambiguous' ' + test_must_fail git log :/ 2>error && + grep ambiguous error +' + +test_expect_success '"git log :" should be ambiguous' ' + test_must_fail git log : 2>error && + grep ambiguous error +' + +test_expect_success 'git log -- :' ' + git log -- : +' + +test_expect_success 'git log HEAD -- :/' ' + cat >expected <<-EOF && + 24b24cf initial + EOF + (cd sub && git log --oneline HEAD -- :/ >../actual) && + test_cmp expected actual +' + +test_done -- cgit v0.10.2-6-g49f6 From 93e7d672fcac8bdc16ae7276bc5942889aa3f179 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 11 May 2011 15:23:25 -0700 Subject: revision.c: leave a note for "a lone :" enhancement If we later add a command in the log family that by default limit its operation to the current subdirectory, we would need to resurrect the "a lone ':' on the command line means no pathspec whatsoever". Now the codepath was cleaned up, we can do so in one place. Leave a note to mark where it is for later generations. Signed-off-by: Junio C Hamano diff --git a/revision.c b/revision.c index e571a3f..17f9fcb 100644 --- a/revision.c +++ b/revision.c @@ -1589,6 +1589,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s } if (prune_data.nr) { + /* + * If we need to introduce the magic "a lone ':' means no + * pathspec whatsoever", here is the place to do so. + * + * if (prune_data.nr == 1 && !strcmp(prune_data[0], ":")) { + * prune_data.nr = 0; + * prune_data.alloc = 0; + * free(prune_data.path); + * prune_data.path = NULL; + * } else { + * terminate prune_data.alloc with NULL and + * call init_pathspec() to set revs->prune_data here. + * } + */ ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc); prune_data.path[prune_data.nr++] = NULL; init_pathspec(&revs->prune_data, -- cgit v0.10.2-6-g49f6 From 650af7ae8bdf92bd92df20152b6d1ad7eb014169 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 12 May 2011 10:21:33 +0200 Subject: t3703: Skip tests using directory name ":" on Windows ":" is not allowed in file names on Windows. Detect this case and skip a test if necessary. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano diff --git a/t/t3703-add-magic-pathspec.sh b/t/t3703-add-magic-pathspec.sh index ce5585e..e508246 100755 --- a/t/t3703-add-magic-pathspec.sh +++ b/t/t3703-add-magic-pathspec.sh @@ -44,8 +44,12 @@ test_expect_success 'a file with the same (long) magic name exists' ' git add -n "./:(icase)ha" ' -test_expect_success 'a file with the same (short) magic name exists' ' - mkdir ":" && +if mkdir ":" 2>/dev/null +then + test_set_prereq COLON_DIR +fi + +test_expect_success COLON_DIR 'a file with the same (short) magic name exists' ' : >":/bar" && test_must_fail git add -n :/bar && git add -n "./:/bar" -- cgit v0.10.2-6-g49f6 From 488201c87e284ae06323b534c31e354811fb0d51 Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Tue, 17 May 2011 18:43:10 +0100 Subject: setup.c: Fix some "symbol not declared" sparse warnings In particular, sparse issues the "symbol 'a_symbol' was not declared. Should it be static?" warnings for the following symbols: setup.c:159:3: 'pathspec_magic' setup.c:176:12: 'prefix_pathspec' These symbols only require file scope, so we add the static modifier to their declarations. Signed-off-by: Ramsay Jones Signed-off-by: Junio C Hamano diff --git a/setup.c b/setup.c index fd4ce59..d7d8e3e 100644 --- a/setup.c +++ b/setup.c @@ -152,7 +152,7 @@ void verify_non_filename(const char *prefix, const char *arg) */ #define PATHSPEC_FROMTOP (1<<0) -struct pathspec_magic { +static struct pathspec_magic { unsigned bit; char mnemonic; /* this cannot be ':'! */ const char *name; @@ -173,7 +173,7 @@ struct pathspec_magic { * the prefix part must always match literally, and a single stupid * string cannot express such a case. */ -const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) +static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) { unsigned magic = 0; const char *copyfrom = elt; -- cgit v0.10.2-6-g49f6