From 5fee4df7f43e52eed19da3529bc50735c6dcd58e 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, 14 Jul 2013 15:35:24 +0700 Subject: clean: remove unused variable "seen" 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/builtin/clean.c b/builtin/clean.c index 04e396b..f955a40 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -155,7 +155,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct exclude_list *el; const char *qname; - char *seen = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("do not print names of files removed")), OPT__DRY_RUN(&dry_run, N_("dry run")), @@ -214,9 +213,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) fill_directory(&dir, pathspec); - if (pathspec) - seen = xmalloc(argc > 0 ? argc : 1); - for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; int len, pos; @@ -250,11 +246,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (lstat(ent->name, &st)) continue; - if (pathspec) { - memset(seen, 0, argc > 0 ? argc : 1); + if (pathspec) matches = match_pathspec(pathspec, ent->name, len, - 0, seen); - } + 0, NULL); if (S_ISDIR(st.st_mode)) { strbuf_addstr(&directory, ent->name); @@ -281,7 +275,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) } } } - free(seen); strbuf_release(&directory); string_list_clear(&exclude_list, 0); -- cgit v0.10.2-6-g49f6 From 64acde94efd2906c3e20560c31c2957ac0b242c4 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, 14 Jul 2013 15:35:25 +0700 Subject: move struct pathspec and related functions to pathspec.[ch] 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/archive.c b/archive.c index d254fa5..c699a2d 100644 --- a/archive.c +++ b/archive.c @@ -5,6 +5,7 @@ #include "archive.h" #include "parse-options.h" #include "unpack-trees.h" +#include "pathspec.h" static char const * const archive_usage[] = { N_("git archive [options] [...]"), diff --git a/builtin/grep.c b/builtin/grep.c index 159e65d..4de49df 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -17,6 +17,7 @@ #include "grep.h" #include "quote.h" #include "dir.h" +#include "pathspec.h" static char const * const grep_usage[] = { N_("git grep [options] [-e] [...] [[--] ...]"), diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 3a410c3..30357df 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -13,6 +13,7 @@ #include "parse-options.h" #include "resolve-undo.h" #include "string-list.h" +#include "pathspec.h" static int abbrev; static int show_deleted; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index fb76e38..93fc3a0 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -10,6 +10,7 @@ #include "quote.h" #include "builtin.h" #include "parse-options.h" +#include "pathspec.h" static int line_termination = '\n'; #define LS_RECURSIVE 1 diff --git a/builtin/update-index.c b/builtin/update-index.c index 5c7762e..b9c2bd0 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -11,6 +11,7 @@ #include "refs.h" #include "resolve-undo.h" #include "parse-options.h" +#include "pathspec.h" /* * Default to not allowing changes to the list of files. The diff --git a/cache.h b/cache.h index dd0fb33..fd0a6f8 100644 --- a/cache.h +++ b/cache.h @@ -189,6 +189,8 @@ struct cache_entry { #error "CE_EXTENDED_FLAGS out of range" #endif +struct pathspec; + /* * Copy the sha1 and stat state of a cache entry from one to * another. But we never change the name, or the hash state! @@ -489,28 +491,8 @@ extern void *read_blob_data_from_index(struct index_state *, const char *, unsig extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int); -#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ - -struct pathspec { - const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ - int nr; - unsigned int has_wildcard:1; - unsigned int recursive:1; - int max_depth; - struct pathspec_item { - const char *match; - int len; - int nowildcard_len; - int flags; - } *items; -}; - -extern int init_pathspec(struct pathspec *, const char **); -extern void free_pathspec(struct pathspec *); extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); -extern int limit_pathspec_to_literal(void); - #define HASH_WRITE_OBJECT 1 #define HASH_FORMAT_CHECK 2 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); diff --git a/diff.h b/diff.h index 78b4091..d1bc914 100644 --- a/diff.h +++ b/diff.h @@ -5,6 +5,7 @@ #define DIFF_H #include "tree-walk.h" +#include "pathspec.h" struct rev_info; struct diff_options; diff --git a/dir.c b/dir.c index 0480419..b0599dd 100644 --- a/dir.c +++ b/dir.c @@ -11,6 +11,7 @@ #include "dir.h" #include "refs.h" #include "wildmatch.h" +#include "pathspec.h" struct path_simplify { int len; diff --git a/pathspec.c b/pathspec.c index 284f397..133f8be 100644 --- a/pathspec.c +++ b/pathspec.c @@ -99,3 +99,153 @@ void die_if_path_beyond_symlink(const char *path, const char *prefix) die(_("'%s' is beyond a symbolic link"), path + len); } } + +/* + * 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) + +static 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. + */ +static 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 + 1; + else + /* handle ')' and '\0' */ + nextat = copyfrom + len; + 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 != ')') + die("Missing ')' at the end of pathspec magic in '%s'", elt); + copyfrom++; + } 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) + die("Unimplemented pathspec magic '%c' in '%s'", + ch, elt); + } + if (*copyfrom == ':') + copyfrom++; + } + + if (magic & PATHSPEC_FROMTOP) + return xstrdup(copyfrom); + else + return prefix_path(prefix, prefixlen, copyfrom); +} + +/* + * N.B. get_pathspec() is deprecated in favor of the "struct pathspec" + * based interface - see pathspec_magic above. + * + * Arguments: + * - prefix - a path relative to the root of the working tree + * - pathspec - a list of paths underneath the prefix path + * + * Iterates over pathspec, prepending each path with prefix, + * and return the resulting list. + * + * If pathspec is empty, return a singleton list containing prefix. + * + * If pathspec and prefix are both empty, return an empty list. + * + * This is typically used by built-in commands such as add.c, in order + * to normalize argv arguments provided to the built-in into a list of + * paths to process, all relative to the root of the working tree. + */ +const char **get_pathspec(const char *prefix, const char **pathspec) +{ + const char *entry = *pathspec; + const char **src, **dst; + int prefixlen; + + if (!prefix && !entry) + return NULL; + + if (!entry) { + static const char *spec[2]; + spec[0] = prefix; + spec[1] = NULL; + return spec; + } + + /* Otherwise we have to re-write the entries.. */ + src = pathspec; + dst = pathspec; + prefixlen = prefix ? strlen(prefix) : 0; + while (*src) { + *(dst++) = prefix_pathspec(prefix, prefixlen, *src); + src++; + } + *dst = NULL; + if (!*pathspec) + return NULL; + return pathspec; +} diff --git a/pathspec.h b/pathspec.h index db0184a..7884068 100644 --- a/pathspec.h +++ b/pathspec.h @@ -1,6 +1,27 @@ #ifndef PATHSPEC_H #define PATHSPEC_H +#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ + +struct pathspec { + const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ + int nr; + unsigned int has_wildcard:1; + unsigned int recursive:1; + int max_depth; + struct pathspec_item { + const char *match; + int len; + int nowildcard_len; + int flags; + } *items; +}; + +extern int init_pathspec(struct pathspec *, const char **); +extern void free_pathspec(struct pathspec *); + +extern int limit_pathspec_to_literal(void); + extern char *find_pathspecs_matching_against_index(const char **pathspec); extern void add_pathspec_matches_against_index(const char **pathspec, char *seen, int specs); extern const char *check_path_for_gitlink(const char *path); diff --git a/preload-index.c b/preload-index.c index 49cb08d..cddfffa 100644 --- a/preload-index.c +++ b/preload-index.c @@ -2,6 +2,7 @@ * Copyright (C) 2008 Linus Torvalds */ #include "cache.h" +#include "pathspec.h" #ifdef NO_PTHREADS static void preload_index(struct index_state *index, const char **pathspec) diff --git a/setup.c b/setup.c index 94c1e61..d1ece5d 100644 --- a/setup.c +++ b/setup.c @@ -154,155 +154,6 @@ void verify_non_filename(const char *prefix, const char *arg) "'git [...] -- [...]'", 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) - -static 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. - */ -static 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 + 1; - else - /* handle ')' and '\0' */ - nextat = copyfrom + len; - 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 != ')') - die("Missing ')' at the end of pathspec magic in '%s'", elt); - copyfrom++; - } 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) - die("Unimplemented pathspec magic '%c' in '%s'", - ch, elt); - } - if (*copyfrom == ':') - copyfrom++; - } - - if (magic & PATHSPEC_FROMTOP) - return xstrdup(copyfrom); - else - return prefix_path(prefix, prefixlen, copyfrom); -} - -/* - * N.B. get_pathspec() is deprecated in favor of the "struct pathspec" - * based interface - see pathspec_magic above. - * - * Arguments: - * - prefix - a path relative to the root of the working tree - * - pathspec - a list of paths underneath the prefix path - * - * Iterates over pathspec, prepending each path with prefix, - * and return the resulting list. - * - * If pathspec is empty, return a singleton list containing prefix. - * - * If pathspec and prefix are both empty, return an empty list. - * - * This is typically used by built-in commands such as add.c, in order - * to normalize argv arguments provided to the built-in into a list of - * paths to process, all relative to the root of the working tree. - */ -const char **get_pathspec(const char *prefix, const char **pathspec) -{ - const char *entry = *pathspec; - const char **src, **dst; - int prefixlen; - - if (!prefix && !entry) - return NULL; - - if (!entry) { - static const char *spec[2]; - spec[0] = prefix; - spec[1] = NULL; - return spec; - } - - /* Otherwise we have to re-write the entries.. */ - src = pathspec; - dst = pathspec; - prefixlen = prefix ? strlen(prefix) : 0; - while (*src) { - *(dst++) = prefix_pathspec(prefix, prefixlen, *src); - src++; - } - *dst = NULL; - if (!*pathspec) - return NULL; - return pathspec; -} /* * Test if it looks like we're at a git directory. diff --git a/tree-walk.c b/tree-walk.c index 6e30ef9..72a9613 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -3,6 +3,7 @@ #include "unpack-trees.h" #include "dir.h" #include "tree.h" +#include "pathspec.h" static const char *get_mode(const char *str, unsigned int *modep) { -- cgit v0.10.2-6-g49f6 From f01d9820e768e7eb9eab4ad9a55a62317e0b23ad 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, 14 Jul 2013 15:35:26 +0700 Subject: pathspec: i18n-ize error strings in pathspec parsing code 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/pathspec.c b/pathspec.c index 133f8be..403095b 100644 --- a/pathspec.c +++ b/pathspec.c @@ -167,11 +167,11 @@ static const char *prefix_pathspec(const char *prefix, int prefixlen, const char break; } if (ARRAY_SIZE(pathspec_magic) <= i) - die("Invalid pathspec magic '%.*s' in '%s'", + die(_("Invalid pathspec magic '%.*s' in '%s'"), (int) len, copyfrom, elt); } if (*copyfrom != ')') - die("Missing ')' at the end of pathspec magic in '%s'", elt); + die(_("Missing ')' at the end of pathspec magic in '%s'"), elt); copyfrom++; } else { /* shorthand */ @@ -188,7 +188,7 @@ static const char *prefix_pathspec(const char *prefix, int prefixlen, const char break; } if (ARRAY_SIZE(pathspec_magic) <= i) - die("Unimplemented pathspec magic '%c' in '%s'", + die(_("Unimplemented pathspec magic '%c' in '%s'"), ch, elt); } if (*copyfrom == ':') -- cgit v0.10.2-6-g49f6 From e4d92cdcd9450af7ae4c40de2b7c5c27a1faa138 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, 14 Jul 2013 15:35:27 +0700 Subject: pathspec: add copy_pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because free_pathspec wants to free "items" pointer in the pathspec structure, a simple structure assignment is not enough if you want to copy an existing pathspec into another. Freeing the original will damage the copy unless a deep copy is made. Note that the strings in pathspec->items->match and the array pathspec->raw[] are still shared between the original and the copy. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/mv.c b/builtin/mv.c index 034fec9..16ce99b 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -15,8 +15,9 @@ static const char * const builtin_mv_usage[] = { NULL }; -static const char **copy_pathspec(const char *prefix, const char **pathspec, - int count, int base_name) +static const char **internal_copy_pathspec(const char *prefix, + const char **pathspec, + int count, int base_name) { int i; const char **result = xmalloc((count + 1) * sizeof(const char *)); @@ -81,17 +82,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die(_("index file corrupt")); - source = copy_pathspec(prefix, argv, argc, 0); + source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); - dest_path = copy_pathspec(prefix, argv + argc, 1, 0); + dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = copy_pathspec(dest_path[0], argv, argc, 1); + destination = internal_copy_pathspec(dest_path[0], argv, argc, 1); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); - destination = copy_pathspec(dest_path[0], argv, argc, 1); + destination = internal_copy_pathspec(dest_path[0], argv, argc, 1); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); diff --git a/pathspec.c b/pathspec.c index 403095b..8fe56cd 100644 --- a/pathspec.c +++ b/pathspec.c @@ -249,3 +249,11 @@ const char **get_pathspec(const char *prefix, const char **pathspec) return NULL; return pathspec; } + +void copy_pathspec(struct pathspec *dst, const struct pathspec *src) +{ + *dst = *src; + dst->items = xmalloc(sizeof(struct pathspec_item) * dst->nr); + memcpy(dst->items, src->items, + sizeof(struct pathspec_item) * dst->nr); +} diff --git a/pathspec.h b/pathspec.h index 7884068..a621676 100644 --- a/pathspec.h +++ b/pathspec.h @@ -18,6 +18,7 @@ struct pathspec { }; extern int init_pathspec(struct pathspec *, const char **); +extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src); extern void free_pathspec(struct pathspec *); extern int limit_pathspec_to_literal(void); -- cgit v0.10.2-6-g49f6 From 87323bdace47c3d464a783f48b16617720e8eeee 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, 14 Jul 2013 15:35:28 +0700 Subject: add parse_pathspec() that converts cmdline args to struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently to fill a struct pathspec, we do: const char **paths; paths = get_pathspec(prefix, argv); ... init_pathspec(&pathspec, paths); "paths" can only carry bare strings, which loses information from command line arguments such as pathspec magic or the prefix part's length for each argument. parse_pathspec() is introduced to combine the two calls into one. The plan is gradually replace all get_pathspec() and init_pathspec() with parse_pathspec(). get_pathspec() now becomes a thin wrapper of parse_pathspec(). parse_pathspec() allows the caller to reject the pathspec magics that it does not support. When a new pathspec magic is introduced, we can enable it per command after making sure that all underlying code has no problem with the new magic. "flags" parameter is currently unused. But it would allow callers to pass certain instructions to parse_pathspec, for example forcing literal pathspec when no magic is used. With the introduction of parse_pathspec, there are now two functions that can initialize struct pathspec: init_pathspec and parse_pathspec. Any semantic changes in struct pathspec must be reflected in both functions. init_pathspec() will be phased out in favor of parse_pathspec(). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt index 4f63a04..90d1aff 100644 --- a/Documentation/technical/api-setup.txt +++ b/Documentation/technical/api-setup.txt @@ -8,6 +8,23 @@ Talk about * is_inside_git_dir() * is_inside_work_tree() * setup_work_tree() -* get_pathspec() (Dscho) + +Pathspec +-------- + +See glossary-context.txt for the syntax of pathspec. In memory, a +pathspec set is represented by "struct pathspec" and is prepared by +parse_pathspec(). This function takes several arguments: + +- magic_mask specifies what features that are NOT supported by the + following code. If a user attempts to use such a feature, + parse_pathspec() can reject it early. + +- flags specifies other things that the caller wants parse_pathspec to + perform. + +- prefix and args come from cmd_* functions + +get_pathspec() is obsolete and should never be used in new code. diff --git a/dir.c b/dir.c index b0599dd..5f86e46 100644 --- a/dir.c +++ b/dir.c @@ -381,7 +381,7 @@ int match_pathspec_depth(const struct pathspec *ps, /* * Return the length of the "simple" part of a path match limiter. */ -static int simple_length(const char *match) +int simple_length(const char *match) { int len = -1; @@ -393,7 +393,7 @@ static int simple_length(const char *match) } } -static int no_wildcard(const char *string) +int no_wildcard(const char *string) { return string[simple_length(string)] == '\0'; } diff --git a/dir.h b/dir.h index 3d6b80c..229ccc8 100644 --- a/dir.h +++ b/dir.h @@ -128,6 +128,8 @@ struct dir_struct { #define MATCHED_RECURSIVELY 1 #define MATCHED_FNMATCH 2 #define MATCHED_EXACTLY 3 +extern int simple_length(const char *match); +extern int no_wildcard(const char *string); extern char *common_prefix(const char **pathspec); extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); extern int match_pathspec_depth(const struct pathspec *pathspec, diff --git a/pathspec.c b/pathspec.c index 8fe56cd..ce942db 100644 --- a/pathspec.c +++ b/pathspec.c @@ -103,10 +103,6 @@ void die_if_path_beyond_symlink(const char *path, const char *prefix) /* * 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" }, @@ -115,7 +111,6 @@ void die_if_path_beyond_symlink(const char *path, const char *prefix) * { PATHSPEC_REGEXP, '\0', "regexp" }, * */ -#define PATHSPEC_FROMTOP (1<<0) static struct pathspec_magic { unsigned bit; @@ -127,7 +122,7 @@ static struct pathspec_magic { /* * Take an element of a pathspec and check for magic signatures. - * Append the result to the prefix. + * Append the result to the prefix. Return the magic bitmap. * * For now, we only parse the syntax and throw out anything other than * "top" magic. @@ -138,10 +133,15 @@ static struct pathspec_magic { * the prefix part must always match literally, and a single stupid * string cannot express such a case. */ -static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) +static unsigned prefix_pathspec(struct pathspec_item *item, + unsigned *p_short_magic, + const char **raw, unsigned flags, + const char *prefix, int prefixlen, + const char *elt) { - unsigned magic = 0; + unsigned magic = 0, short_magic = 0; const char *copyfrom = elt; + char *match; int i; if (elt[0] != ':') { @@ -184,7 +184,7 @@ static const char *prefix_pathspec(const char *prefix, int prefixlen, const char break; for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) if (pathspec_magic[i].mnemonic == ch) { - magic |= pathspec_magic[i].bit; + short_magic |= pathspec_magic[i].bit; break; } if (ARRAY_SIZE(pathspec_magic) <= i) @@ -195,15 +195,128 @@ static const char *prefix_pathspec(const char *prefix, int prefixlen, const char copyfrom++; } + magic |= short_magic; + *p_short_magic = short_magic; + if (magic & PATHSPEC_FROMTOP) - return xstrdup(copyfrom); + match = xstrdup(copyfrom); else - return prefix_path(prefix, prefixlen, copyfrom); + match = prefix_path(prefix, prefixlen, copyfrom); + *raw = item->match = match; + item->len = strlen(item->match); + if (limit_pathspec_to_literal()) + item->nowildcard_len = item->len; + else + item->nowildcard_len = simple_length(item->match); + item->flags = 0; + if (item->nowildcard_len < item->len && + item->match[item->nowildcard_len] == '*' && + no_wildcard(item->match + item->nowildcard_len + 1)) + item->flags |= PATHSPEC_ONESTAR; + return magic; +} + +static int pathspec_item_cmp(const void *a_, const void *b_) +{ + struct pathspec_item *a, *b; + + a = (struct pathspec_item *)a_; + b = (struct pathspec_item *)b_; + return strcmp(a->match, b->match); +} + +static void NORETURN unsupported_magic(const char *pattern, + unsigned magic, + unsigned short_magic) +{ + struct strbuf sb = STRBUF_INIT; + int i, n; + for (n = i = 0; i < ARRAY_SIZE(pathspec_magic); i++) { + const struct pathspec_magic *m = pathspec_magic + i; + if (!(magic & m->bit)) + continue; + if (sb.len) + strbuf_addstr(&sb, " "); + if (short_magic & m->bit) + strbuf_addf(&sb, "'%c'", m->mnemonic); + else + strbuf_addf(&sb, "'%s'", m->name); + n++; + } + /* + * We may want to substitute "this command" with a command + * name. E.g. when add--interactive dies when running + * "checkout -p" + */ + die(_("%s: pathspec magic not supported by this command: %s"), + pattern, sb.buf); +} + +/* + * Given command line arguments and a prefix, convert the input to + * pathspec. die() if any magic in magic_mask is used. + */ +void parse_pathspec(struct pathspec *pathspec, + unsigned magic_mask, unsigned flags, + const char *prefix, const char **argv) +{ + struct pathspec_item *item; + const char *entry = argv ? *argv : NULL; + int i, n, prefixlen; + + memset(pathspec, 0, sizeof(*pathspec)); + + /* No arguments, no prefix -> no pathspec */ + if (!entry && !prefix) + return; + + /* No arguments with prefix -> prefix pathspec */ + if (!entry) { + static const char *raw[2]; + + pathspec->items = item = xmalloc(sizeof(*item)); + memset(item, 0, sizeof(*item)); + item->match = prefix; + item->nowildcard_len = item->len = strlen(prefix); + raw[0] = prefix; + raw[1] = NULL; + pathspec->nr = 1; + pathspec->raw = raw; + return; + } + + n = 0; + while (argv[n]) + n++; + + pathspec->nr = n; + pathspec->items = item = xmalloc(sizeof(*item) * n); + pathspec->raw = argv; + prefixlen = prefix ? strlen(prefix) : 0; + + for (i = 0; i < n; i++) { + unsigned short_magic; + entry = argv[i]; + + item[i].magic = prefix_pathspec(item + i, &short_magic, + argv + i, flags, + prefix, prefixlen, entry); + if (item[i].magic & magic_mask) + unsupported_magic(entry, + item[i].magic & magic_mask, + short_magic); + if (item[i].nowildcard_len < item[i].len) + pathspec->has_wildcard = 1; + pathspec->magic |= item[i].magic; + } + + qsort(pathspec->items, pathspec->nr, + sizeof(struct pathspec_item), pathspec_item_cmp); } /* * N.B. get_pathspec() is deprecated in favor of the "struct pathspec" - * based interface - see pathspec_magic above. + * based interface - see pathspec.c:parse_pathspec(). * * Arguments: * - prefix - a path relative to the root of the working tree @@ -222,32 +335,11 @@ static const char *prefix_pathspec(const char *prefix, int prefixlen, const char */ const char **get_pathspec(const char *prefix, const char **pathspec) { - const char *entry = *pathspec; - const char **src, **dst; - int prefixlen; - - if (!prefix && !entry) - return NULL; - - if (!entry) { - static const char *spec[2]; - spec[0] = prefix; - spec[1] = NULL; - return spec; - } - - /* Otherwise we have to re-write the entries.. */ - src = pathspec; - dst = pathspec; - prefixlen = prefix ? strlen(prefix) : 0; - while (*src) { - *(dst++) = prefix_pathspec(prefix, prefixlen, *src); - src++; - } - *dst = NULL; - if (!*pathspec) - return NULL; - return pathspec; + struct pathspec ps; + parse_pathspec(&ps, + PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, + 0, prefix, pathspec); + return ps.raw; } void copy_pathspec(struct pathspec *dst, const struct pathspec *src) diff --git a/pathspec.h b/pathspec.h index a621676..937ec91 100644 --- a/pathspec.h +++ b/pathspec.h @@ -1,6 +1,10 @@ #ifndef PATHSPEC_H #define PATHSPEC_H +/* Pathspec magic */ +#define PATHSPEC_FROMTOP (1<<0) +#define PATHSPEC_ALL_MAGIC PATHSPEC_FROMTOP + #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ struct pathspec { @@ -8,9 +12,11 @@ struct pathspec { int nr; unsigned int has_wildcard:1; unsigned int recursive:1; + unsigned magic; int max_depth; struct pathspec_item { const char *match; + unsigned magic; int len; int nowildcard_len; int flags; @@ -18,6 +24,11 @@ struct pathspec { }; extern int init_pathspec(struct pathspec *, const char **); +extern void parse_pathspec(struct pathspec *pathspec, + unsigned magic_mask, + unsigned flags, + const char *prefix, + const char **args); extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src); extern void free_pathspec(struct pathspec *); -- cgit v0.10.2-6-g49f6 From d2ce1331953e9f4306e00f5edf3a6ce6e3330e9e 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, 14 Jul 2013 15:35:29 +0700 Subject: parse_pathspec: save original pathspec for reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We usually use pathspec_item's match field for pathspec error reporting. However "match" (or "raw") does not show the magic part, which will play more important role later on. Preserve exact user input for reporting. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/dir.c b/dir.c index 5f86e46..308028e 100644 --- a/dir.c +++ b/dir.c @@ -1599,6 +1599,7 @@ int init_pathspec(struct pathspec *pathspec, const char **paths) const char *path = paths[i]; item->match = path; + item->original = path; item->len = strlen(path); item->flags = 0; if (limit_pathspec_to_literal()) { diff --git a/pathspec.c b/pathspec.c index ce942db..f94beb6 100644 --- a/pathspec.c +++ b/pathspec.c @@ -203,6 +203,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, else match = prefix_path(prefix, prefixlen, copyfrom); *raw = item->match = match; + item->original = elt; item->len = strlen(item->match); if (limit_pathspec_to_literal()) item->nowildcard_len = item->len; @@ -277,6 +278,7 @@ void parse_pathspec(struct pathspec *pathspec, pathspec->items = item = xmalloc(sizeof(*item)); memset(item, 0, sizeof(*item)); item->match = prefix; + item->original = prefix; item->nowildcard_len = item->len = strlen(prefix); raw[0] = prefix; raw[1] = NULL; diff --git a/pathspec.h b/pathspec.h index 937ec91..cc5841b 100644 --- a/pathspec.h +++ b/pathspec.h @@ -16,6 +16,7 @@ struct pathspec { int max_depth; struct pathspec_item { const char *match; + const char *original; unsigned magic; int len; int nowildcard_len; -- cgit v0.10.2-6-g49f6 From fc12261fea778cabd08dec707169cb88ecdbc647 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, 14 Jul 2013 15:35:30 +0700 Subject: parse_pathspec: add PATHSPEC_PREFER_{CWD,FULL} flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have two ways of dealing with empty pathspec: 1. limit it to current prefix 2. match the entire working directory Some commands go with #1, some #2. get_pathspec() and parse_pathspec() only support #1. Make parse_pathspec() reject empty pathspec by default. #1 and #2 can be specified via new flags. This makes it more expressive about default behavior at command level. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/pathspec.c b/pathspec.c index f94beb6..6d99a3d 100644 --- a/pathspec.c +++ b/pathspec.c @@ -271,10 +271,20 @@ void parse_pathspec(struct pathspec *pathspec, if (!entry && !prefix) return; + if ((flags & PATHSPEC_PREFER_CWD) && + (flags & PATHSPEC_PREFER_FULL)) + die("BUG: PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible"); + /* No arguments with prefix -> prefix pathspec */ if (!entry) { static const char *raw[2]; + if (flags & PATHSPEC_PREFER_FULL) + return; + + if (!(flags & PATHSPEC_PREFER_CWD)) + die("BUG: PATHSPEC_PREFER_CWD requires arguments"); + pathspec->items = item = xmalloc(sizeof(*item)); memset(item, 0, sizeof(*item)); item->match = prefix; @@ -340,7 +350,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec) struct pathspec ps; parse_pathspec(&ps, PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, - 0, prefix, pathspec); + PATHSPEC_PREFER_CWD, + prefix, pathspec); return ps.raw; } diff --git a/pathspec.h b/pathspec.h index cc5841b..d630e8b 100644 --- a/pathspec.h +++ b/pathspec.h @@ -24,6 +24,10 @@ struct pathspec { } *items; }; +/* parse_pathspec flags */ +#define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */ +#define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */ + extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, unsigned magic_mask, -- cgit v0.10.2-6-g49f6 From 0fdc2ae5125b61e5cfe0cd49b064d8367994e61b 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, 14 Jul 2013 15:35:31 +0700 Subject: convert some get_pathspec() calls to parse_pathspec() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These call sites follow the pattern: paths = get_pathspec(prefix, argv); init_pathspec(&pathspec, paths); which can be converted into a single parse_pathspec() call. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/grep.c b/builtin/grep.c index 4de49df..1a6c028 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -631,7 +631,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) const char *show_in_pager = NULL, *default_pager = "dummy"; struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; - const char **paths = NULL; struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; @@ -858,8 +857,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) verify_filename(prefix, argv[j], j == i); } - paths = get_pathspec(prefix, argv + i); - init_pathspec(&pathspec, paths); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, argv + i); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 93fc3a0..bdb03f3 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -167,7 +167,15 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) if (get_sha1(argv[0], sha1)) die("Not a valid object name %s", argv[0]); - init_pathspec(&pathspec, get_pathspec(prefix, argv + 1)); + /* + * show_recursive() rolls its own matching code and is + * generally ignorant of 'struct pathspec'. The magic mask + * cannot be lifted until it is converted to use + * match_pathspec_depth() or tree_entry_interesting() + */ + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, argv + 1); for (i = 0; i < pathspec.nr; i++) pathspec.items[i].nowildcard_len = pathspec.items[i].len; pathspec.has_wildcard = 0; diff --git a/builtin/update-index.c b/builtin/update-index.c index b9c2bd0..e795818 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -547,10 +547,11 @@ static int do_reupdate(int ac, const char **av, */ int pos; int has_head = 1; - const char **paths = get_pathspec(prefix, av + 1); struct pathspec pathspec; - init_pathspec(&pathspec, paths); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, av + 1); if (read_ref("HEAD", head_sha1)) /* If there is no HEAD, that means it is an initial diff --git a/revision.c b/revision.c index 2f0142f..cdc7f45 100644 --- a/revision.c +++ b/revision.c @@ -2120,8 +2120,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s */ ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc); prune_data.path[prune_data.nr++] = NULL; - init_pathspec(&revs->prune_data, - get_pathspec(revs->prefix, prune_data.path)); + parse_pathspec(&revs->prune_data, 0, 0, + revs->prefix, prune_data.path); } if (revs->def == NULL) -- cgit v0.10.2-6-g49f6 From 6330a171996abbec7dac48788de851aea7d0a18f 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, 14 Jul 2013 15:35:32 +0700 Subject: parse_pathspec: add special flag for max_depth feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit match_pathspec_depth() and tree_entry_interesting() check max_depth field in order to support "git grep --max-depth". The feature activation is tied to "recursive" field, which led to some unwanted activation, e.g. 5c8eeb8 (diff-index: enable recursive pathspec matching in unpack_trees - 2012-01-15). This patch decouples the activation from "recursive" field, puts it in "magic" field instead. This makes sure that only "git grep" can activate this feature. And because parse_pathspec knows when the feature is not used, it does not need to sort pathspec (required for max_depth to work correctly). A small win for non-grep cases. Even though a new magic flag is introduced, no magic syntax is. The magic can be only enabled by parse_pathspec() caller. We might someday want to support ":(maxdepth:10)src." It all depends on actual use cases. max_depth feature cannot be enabled via init_pathspec() anymore. But that's ok because init_pathspec() is on its way to /dev/null. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/grep.c b/builtin/grep.c index 1a6c028..4bc0754 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -858,7 +858,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD, + PATHSPEC_PREFER_CWD | + (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), prefix, argv + i); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; diff --git a/diff-lib.c b/diff-lib.c index b6f4b21..d4e17f7 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -476,7 +476,6 @@ static int diff_cache(struct rev_info *revs, opts.dst_index = NULL; opts.pathspec = &revs->diffopt.pathspec; opts.pathspec->recursive = 1; - opts.pathspec->max_depth = -1; init_tree_desc(&t, tree->buffer, tree->size); return unpack_trees(1, &t, &opts); diff --git a/dir.c b/dir.c index 308028e..e28bc0d 100644 --- a/dir.c +++ b/dir.c @@ -341,7 +341,9 @@ int match_pathspec_depth(const struct pathspec *ps, int i, retval = 0; if (!ps->nr) { - if (!ps->recursive || ps->max_depth == -1) + if (!ps->recursive || + !(ps->magic & PATHSPEC_MAXDEPTH) || + ps->max_depth == -1) return MATCHED_RECURSIVELY; if (within_depth(name, namelen, 0, ps->max_depth)) @@ -358,7 +360,9 @@ int match_pathspec_depth(const struct pathspec *ps, if (seen && seen[i] == MATCHED_EXACTLY) continue; how = match_pathspec_item(ps->items+i, prefix, name, namelen); - if (ps->recursive && ps->max_depth != -1 && + if (ps->recursive && + (ps->magic & PATHSPEC_MAXDEPTH) && + ps->max_depth != -1 && how && how != MATCHED_FNMATCH) { int len = ps->items[i].len; if (name[len] == '/') diff --git a/pathspec.c b/pathspec.c index 6d99a3d..06778fc 100644 --- a/pathspec.c +++ b/pathspec.c @@ -267,6 +267,9 @@ void parse_pathspec(struct pathspec *pathspec, memset(pathspec, 0, sizeof(*pathspec)); + if (flags & PATHSPEC_MAXDEPTH_VALID) + pathspec->magic |= PATHSPEC_MAXDEPTH; + /* No arguments, no prefix -> no pathspec */ if (!entry && !prefix) return; @@ -322,8 +325,9 @@ void parse_pathspec(struct pathspec *pathspec, pathspec->magic |= item[i].magic; } - qsort(pathspec->items, pathspec->nr, - sizeof(struct pathspec_item), pathspec_item_cmp); + if (pathspec->magic & PATHSPEC_MAXDEPTH) + qsort(pathspec->items, pathspec->nr, + sizeof(struct pathspec_item), pathspec_item_cmp); } /* diff --git a/pathspec.h b/pathspec.h index d630e8b..aa98597 100644 --- a/pathspec.h +++ b/pathspec.h @@ -3,7 +3,10 @@ /* Pathspec magic */ #define PATHSPEC_FROMTOP (1<<0) -#define PATHSPEC_ALL_MAGIC PATHSPEC_FROMTOP +#define PATHSPEC_MAXDEPTH (1<<1) +#define PATHSPEC_ALL_MAGIC \ + (PATHSPEC_FROMTOP | \ + PATHSPEC_MAXDEPTH) #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ @@ -27,6 +30,7 @@ struct pathspec { /* parse_pathspec flags */ #define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */ #define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */ +#define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */ extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, diff --git a/tree-diff.c b/tree-diff.c index ba01563..826512e 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -138,7 +138,6 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, /* Enable recursion indefinitely */ opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE); - opt->pathspec.max_depth = -1; strbuf_init(&base, PATH_MAX); strbuf_add(&base, base_str, baselen); diff --git a/tree-walk.c b/tree-walk.c index 72a9613..d399ca9 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -637,7 +637,9 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, entry_not_interesting : all_entries_not_interesting; if (!ps->nr) { - if (!ps->recursive || ps->max_depth == -1) + if (!ps->recursive || + !(ps->magic & PATHSPEC_MAXDEPTH) || + ps->max_depth == -1) return all_entries_interesting; return within_depth(base->buf + base_offset, baselen, !!S_ISDIR(entry->mode), @@ -658,7 +660,9 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, if (!match_dir_prefix(base_str, match, matchlen)) goto match_wildcards; - if (!ps->recursive || ps->max_depth == -1) + if (!ps->recursive || + !(ps->magic & PATHSPEC_MAXDEPTH) || + ps->max_depth == -1) return all_entries_interesting; return within_depth(base_str + matchlen + 1, -- cgit v0.10.2-6-g49f6 From b69bb3fc271911317c12fd6ecce6fe1e0301a2b3 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, 14 Jul 2013 15:35:33 +0700 Subject: parse_pathspec: support stripping submodule trailing slashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag is equivalent to builtin/ls-files.c:strip_trailing_slashes() and is intended to replace that function when ls-files is converted to use parse_pathspec. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/pathspec.c b/pathspec.c index 06778fc..1c07c23 100644 --- a/pathspec.c +++ b/pathspec.c @@ -205,6 +205,15 @@ static unsigned prefix_pathspec(struct pathspec_item *item, *raw = item->match = match; item->original = elt; item->len = strlen(item->match); + + if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) && + (item->len >= 1 && item->match[item->len - 1] == '/') && + (i = cache_name_pos(item->match, item->len - 1)) >= 0 && + S_ISGITLINK(active_cache[i]->ce_mode)) { + item->len--; + match[item->len] = '\0'; + } + if (limit_pathspec_to_literal()) item->nowildcard_len = item->len; else diff --git a/pathspec.h b/pathspec.h index aa98597..5144851 100644 --- a/pathspec.h +++ b/pathspec.h @@ -31,6 +31,8 @@ struct pathspec { #define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */ #define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */ #define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */ +/* strip the trailing slash if the given path is a gitlink */ +#define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3) extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, -- cgit v0.10.2-6-g49f6 From 8745024422d4b38d8bf93d342b7b173f57cbceaa 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, 14 Jul 2013 15:35:34 +0700 Subject: parse_pathspec: support stripping/checking submodule paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PATHSPEC_SYMLINK_LEADING_PATH and _STRIP_SUBMODULE_SLASH_EXPENSIVE are respectively the alternate implementation of pathspec.c:die_if_path_beyond_symlink() and pathspec.c:check_path_for_gitlink(). They are intended to replace those functions when builtin/add.c and builtin/check-ignore.c are converted to use parse_pathspec. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/pathspec.c b/pathspec.c index 1c07c23..e2a4f91 100644 --- a/pathspec.c +++ b/pathspec.c @@ -214,6 +214,26 @@ static unsigned prefix_pathspec(struct pathspec_item *item, match[item->len] = '\0'; } + if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE) + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + int ce_len = ce_namelen(ce); + + if (!S_ISGITLINK(ce->ce_mode)) + continue; + + if (item->len <= ce_len || match[ce_len] != '/' || + memcmp(ce->name, match, ce_len)) + continue; + if (item->len == ce_len + 1) { + /* strip trailing slash */ + item->len--; + match[item->len] = '\0'; + } else + die (_("Pathspec '%s' is in submodule '%.*s'"), + elt, ce_len, ce->name); + } + if (limit_pathspec_to_literal()) item->nowildcard_len = item->len; else @@ -329,6 +349,12 @@ void parse_pathspec(struct pathspec *pathspec, unsupported_magic(entry, item[i].magic & magic_mask, short_magic); + + if ((flags & PATHSPEC_SYMLINK_LEADING_PATH) && + has_symlink_leading_path(item[i].match, item[i].len)) { + die(_("pathspec '%s' is beyond a symbolic link"), entry); + } + if (item[i].nowildcard_len < item[i].len) pathspec->has_wildcard = 1; pathspec->magic |= item[i].magic; diff --git a/pathspec.h b/pathspec.h index 5144851..450fc03 100644 --- a/pathspec.h +++ b/pathspec.h @@ -33,6 +33,16 @@ struct pathspec { #define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */ /* strip the trailing slash if the given path is a gitlink */ #define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3) +/* die if a symlink is part of the given path's directory */ +#define PATHSPEC_SYMLINK_LEADING_PATH (1<<4) +/* + * This is like a combination of ..LEADING_PATH and .._SLASH_CHEAP + * (but not the same): it strips the trailing slash if the given path + * is a gitlink but also checks and dies if gitlink is part of the + * leading path (i.e. the given path goes beyond a submodule). It's + * safer than _SLASH_CHEAP and also more expensive. + */ +#define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5) extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, -- cgit v0.10.2-6-g49f6 From dad2586a6b30da902d5f1979221eccc474425a7a 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, 14 Jul 2013 15:35:35 +0700 Subject: parse_pathspec: support prefixing original patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes 'original' suitable for passing to an external command because all pathspec magic is left in place, provided that the external command understands pathspec. The prefixing is needed because we usually launch a subcommand at worktree's top directory and the subcommand can no longer calculate the prefix itself. This slightly affects the original purpose of 'original' (i.e. reporting). We should report without prefixing. So only turn this flag on when you know you are about to pass the result straight away to an external command. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/pathspec.c b/pathspec.c index e2a4f91..ba6408a 100644 --- a/pathspec.c +++ b/pathspec.c @@ -203,7 +203,17 @@ static unsigned prefix_pathspec(struct pathspec_item *item, else match = prefix_path(prefix, prefixlen, copyfrom); *raw = item->match = match; - item->original = elt; + /* + * Prefix the pathspec (keep all magic) and assign to + * original. Useful for passing to another command. + */ + if (flags & PATHSPEC_PREFIX_ORIGIN) { + struct strbuf sb = STRBUF_INIT; + strbuf_add(&sb, elt, copyfrom - elt); + strbuf_addstr(&sb, match); + item->original = strbuf_detach(&sb, NULL); + } else + item->original = elt; item->len = strlen(item->match); if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) && diff --git a/pathspec.h b/pathspec.h index 450fc03..2e427d7 100644 --- a/pathspec.h +++ b/pathspec.h @@ -43,6 +43,7 @@ struct pathspec { * safer than _SLASH_CHEAP and also more expensive. */ #define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5) +#define PATHSPEC_PREFIX_ORIGIN (1<<6) extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, -- cgit v0.10.2-6-g49f6 From 8f4f8f4579fe8cc630e118cc736de2b8d5cf8e34 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, 14 Jul 2013 15:35:36 +0700 Subject: guard against new pathspec magic in pathspec matching code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GUARD_PATHSPEC() marks pathspec-sensitive code, basically all those that touch anything in 'struct pathspec' except fields "nr" and "original". GUARD_PATHSPEC() is not supposed to fail. It's mainly to help the designers catch unsupported codepaths. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt index 90d1aff..540e455 100644 --- a/Documentation/technical/api-setup.txt +++ b/Documentation/technical/api-setup.txt @@ -28,3 +28,22 @@ parse_pathspec(). This function takes several arguments: - prefix and args come from cmd_* functions get_pathspec() is obsolete and should never be used in new code. + +parse_pathspec() helps catch unsupported features and reject them +politely. At a lower level, different pathspec-related functions may +not support the same set of features. Such pathspec-sensitive +functions are guarded with GUARD_PATHSPEC(), which will die in an +unfriendly way when an unsupported feature is requested. + +The command designers are supposed to make sure that GUARD_PATHSPEC() +never dies. They have to make sure all unsupported features are caught +by parse_pathspec(), not by GUARD_PATHSPEC. grepping GUARD_PATHSPEC() +should give the designers all pathspec-sensitive codepaths and what +features they support. + +A similar process is applied when a new pathspec magic is added. The +designer lifts the GUARD_PATHSPEC restriction in the functions that +support the new magic. At the same time (s)he has to make sure this +new feature will be caught at parse_pathspec() in commands that cannot +handle the new magic in some cases. grepping parse_pathspec() should +help. diff --git a/builtin/diff.c b/builtin/diff.c index 9fc273d..6bb41af 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -367,6 +367,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } } if (rev.prune_data.nr) { + /* builtin_diff_b_f() */ + GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP); if (!path) path = rev.prune_data.items[0].match; paths += rev.prune_data.nr; diff --git a/dir.c b/dir.c index e28bc0d..19978d3 100644 --- a/dir.c +++ b/dir.c @@ -340,6 +340,8 @@ int match_pathspec_depth(const struct pathspec *ps, { int i, retval = 0; + GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + if (!ps->nr) { if (!ps->recursive || !(ps->magic & PATHSPEC_MAXDEPTH) || diff --git a/pathspec.h b/pathspec.h index 2e427d7..6baf205 100644 --- a/pathspec.h +++ b/pathspec.h @@ -27,6 +27,13 @@ struct pathspec { } *items; }; +#define GUARD_PATHSPEC(ps, mask) \ + do { \ + if ((ps)->magic & ~(mask)) \ + die("BUG:%s:%d: unsupported magic %x", \ + __FILE__, __LINE__, (ps)->magic & ~(mask)); \ + } while (0) + /* parse_pathspec flags */ #define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */ #define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */ diff --git a/tree-diff.c b/tree-diff.c index 826512e..5a87614 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -198,6 +198,25 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co const char *paths[1]; int i; + /* + * follow-rename code is very specific, we need exactly one + * path. Magic that matches more than one path is not + * supported. + */ + GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP); +#if 0 + /* + * We should reject wildcards as well. Unfortunately we + * haven't got a reliable way to detect that 'foo\*bar' in + * fact has no wildcards. nowildcard_len is merely a hint for + * optimization. Let it slip for now until wildmatch is taught + * about dry-run mode and returns wildcard info. + */ + if (opt->pathspec.has_wildcard) + die("BUG:%s:%d: wildcards are not supported", + __FILE__, __LINE__); +#endif + /* Remove the file creation entry from the diff queue, and remember it */ choice = q->queue[0]; q->nr = 0; diff --git a/tree-walk.c b/tree-walk.c index d399ca9..37b157e 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -636,6 +636,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, enum interesting never_interesting = ps->has_wildcard ? entry_not_interesting : all_entries_not_interesting; + GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + if (!ps->nr) { if (!ps->recursive || !(ps->magic & PATHSPEC_MAXDEPTH) || -- cgit v0.10.2-6-g49f6 From 893d839970c0f966bee17cdf63895a62e670e0ca 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, 14 Jul 2013 15:35:37 +0700 Subject: clean: convert to use parse_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/builtin/clean.c b/builtin/clean.c index f955a40..fdd4980 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -13,6 +13,7 @@ #include "refs.h" #include "string-list.h" #include "quote.h" +#include "pathspec.h" static int force = -1; /* unset */ @@ -150,7 +151,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf directory = STRBUF_INIT; struct dir_struct dir; - static const char **pathspec; + struct pathspec pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct exclude_list *el; @@ -209,9 +210,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix) for (i = 0; i < exclude_list.nr; i++) add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1)); - pathspec = get_pathspec(prefix, argv); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, argv); - fill_directory(&dir, pathspec); + fill_directory(&dir, pathspec.raw); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; @@ -246,9 +249,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (lstat(ent->name, &st)) continue; - if (pathspec) - matches = match_pathspec(pathspec, ent->name, len, - 0, NULL); + if (pathspec.nr) + matches = match_pathspec_depth(&pathspec, ent->name, + len, 0, NULL); if (S_ISDIR(st.st_mode)) { strbuf_addstr(&directory, ent->name); @@ -262,7 +265,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) } strbuf_reset(&directory); } else { - if (pathspec && !matches) + if (pathspec.nr && !matches) continue; res = dry_run ? 0 : unlink(ent->name); if (res) { -- cgit v0.10.2-6-g49f6 From 6654c8894effaf12ae73a2a6e5bc72433f4a3479 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, 14 Jul 2013 15:35:38 +0700 Subject: commit: convert to use parse_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/builtin/commit.c b/builtin/commit.c index 790e5ab..530f0ed 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -284,17 +284,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, { int fd; struct string_list partial; - const char **pathspec = NULL; + struct pathspec pathspec; char *old_index_env = NULL; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, argv); - if (*argv) - pathspec = get_pathspec(prefix, argv); - - if (read_cache_preload(pathspec) < 0) + if (read_cache_preload(pathspec.raw) < 0) die(_("index file corrupt")); if (interactive) { @@ -336,9 +336,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ - if (all || (also && pathspec && *pathspec)) { + if (all || (also && pathspec.nr)) { fd = hold_locked_index(&index_lock, 1); - add_files_to_cache(also ? prefix : NULL, pathspec, 0); + add_files_to_cache(also ? prefix : NULL, pathspec.raw, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || @@ -357,7 +357,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, * and create commit from the_index. * We still need to refresh the index here. */ - if (!only && (!pathspec || !*pathspec)) { + if (!only && !pathspec.nr) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { @@ -402,7 +402,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; - if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec)) + if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec.raw)) exit(1); discard_cache(); -- cgit v0.10.2-6-g49f6 From 15b55ae06a208dea4d10125d9655a84f5b62d781 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, 14 Jul 2013 15:35:39 +0700 Subject: status: convert to use parse_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/builtin/commit.c b/builtin/commit.c index 530f0ed..64d1a3d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1241,11 +1241,12 @@ int cmd_status(int argc, const char **argv, const char *prefix) handle_untracked_files_arg(&s); if (show_ignored_in_status) s.show_ignored_files = 1; - if (*argv) - s.pathspec = get_pathspec(prefix, argv); + parse_pathspec(&s.pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, argv); - read_cache_preload(s.pathspec); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL); + read_cache_preload(s.pathspec.raw); + refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec.raw, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) diff --git a/wt-status.c b/wt-status.c index 9c4d086..fae0c27 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "pathspec.h" #include "wt-status.h" #include "object.h" #include "dir.h" @@ -438,7 +439,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s) } rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; - init_pathspec(&rev.prune_data, s->pathspec); + copy_pathspec(&rev.prune_data, &s->pathspec); run_diff_files(&rev, 0); } @@ -463,22 +464,20 @@ static void wt_status_collect_changes_index(struct wt_status *s) rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; - init_pathspec(&rev.prune_data, s->pathspec); + copy_pathspec(&rev.prune_data, &s->pathspec); run_diff_index(&rev, 1); } static void wt_status_collect_changes_initial(struct wt_status *s) { - struct pathspec pathspec; int i; - init_pathspec(&pathspec, s->pathspec); for (i = 0; i < active_nr; i++) { struct string_list_item *it; struct wt_status_change_data *d; struct cache_entry *ce = active_cache[i]; - if (!ce_path_match(ce, &pathspec)) + if (!ce_path_match(ce, &s->pathspec)) continue; it = string_list_insert(&s->change, ce->name); d = it->util; @@ -493,7 +492,6 @@ static void wt_status_collect_changes_initial(struct wt_status *s) else d->index_status = DIFF_STATUS_ADDED; } - free_pathspec(&pathspec); } static void wt_status_collect_untracked(struct wt_status *s) @@ -516,12 +514,12 @@ static void wt_status_collect_untracked(struct wt_status *s) dir.flags |= DIR_SHOW_IGNORED_TOO; setup_standard_excludes(&dir); - fill_directory(&dir, s->pathspec); + fill_directory(&dir, s->pathspec.raw); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (cache_name_is_other(ent->name, ent->len) && - match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL)) string_list_insert(&s->untracked, ent->name); free(ent); } @@ -529,7 +527,7 @@ static void wt_status_collect_untracked(struct wt_status *s) for (i = 0; i < dir.ignored_nr; i++) { struct dir_entry *ent = dir.ignored[i]; if (cache_name_is_other(ent->name, ent->len) && - match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL)) string_list_insert(&s->ignored, ent->name); free(ent); } diff --git a/wt-status.h b/wt-status.h index 4121bc2..8463672 100644 --- a/wt-status.h +++ b/wt-status.h @@ -44,7 +44,7 @@ struct wt_status { int is_initial; char *branch; const char *reference; - const char **pathspec; + struct pathspec pathspec; int verbose; int amend; enum commit_whence whence; -- cgit v0.10.2-6-g49f6 From 01a10b0af9f84069f7c96cb4c94e1417cb7139c1 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, 14 Jul 2013 15:35:40 +0700 Subject: rerere: convert to use parse_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/builtin/rerere.c b/builtin/rerere.c index dc1708e..4e51add 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -6,6 +6,7 @@ #include "rerere.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" +#include "pathspec.h" static const char * const rerere_usage[] = { N_("git rerere [clear | forget path... | status | remaining | diff | gc]"), @@ -68,11 +69,12 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) return rerere(flags); if (!strcmp(argv[0], "forget")) { - const char **pathspec; + struct pathspec pathspec; if (argc < 2) warning("'git rerere forget' without paths is deprecated"); - pathspec = get_pathspec(prefix, argv + 1); - return rerere_forget(pathspec); + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, + prefix, argv + 1); + return rerere_forget(&pathspec); } fd = setup_rerere(&merge_rr, flags); diff --git a/rerere.c b/rerere.c index 98e3e29..27afbfe 100644 --- a/rerere.c +++ b/rerere.c @@ -6,6 +6,7 @@ #include "resolve-undo.h" #include "ll-merge.h" #include "attr.h" +#include "pathspec.h" #define RESOLVED 0 #define PUNTED 1 @@ -656,7 +657,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr) return 0; } -int rerere_forget(const char **pathspec) +int rerere_forget(struct pathspec *pathspec) { int i, fd; struct string_list conflict = STRING_LIST_INIT_DUP; @@ -667,12 +668,12 @@ int rerere_forget(const char **pathspec) fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE); - unmerge_cache(pathspec); + unmerge_cache(pathspec->raw); find_conflict(&conflict); for (i = 0; i < conflict.nr; i++) { struct string_list_item *it = &conflict.items[i]; - if (!match_pathspec(pathspec, it->string, strlen(it->string), - 0, NULL)) + if (!match_pathspec_depth(pathspec, it->string, strlen(it->string), + 0, NULL)) continue; rerere_forget_one_path(it->string, &merge_rr); } diff --git a/rerere.h b/rerere.h index 156d2aa..4aa06c9 100644 --- a/rerere.h +++ b/rerere.h @@ -3,6 +3,8 @@ #include "string-list.h" +struct pathspec; + #define RERERE_AUTOUPDATE 01 #define RERERE_NOAUTOUPDATE 02 @@ -16,7 +18,7 @@ extern void *RERERE_RESOLVED; extern int setup_rerere(struct string_list *, int); extern int rerere(int); extern const char *rerere_path(const char *hex, const char *file); -extern int rerere_forget(const char **); +extern int rerere_forget(struct pathspec *); extern int rerere_remaining(struct string_list *); extern void rerere_clear(struct string_list *); extern void rerere_gc(struct string_list *); -- cgit v0.10.2-6-g49f6 From 817b345aeb47eb2dfeccbdbf99442359283434c2 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, 14 Jul 2013 15:35:41 +0700 Subject: checkout: convert to use parse_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/builtin/checkout.c b/builtin/checkout.c index 7fe0bff..6721de2 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -46,7 +46,7 @@ struct checkout_opts { int branch_exists; const char *prefix; - const char **pathspec; + struct pathspec pathspec; struct tree *source_tree; }; @@ -257,20 +257,18 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) return run_add_interactive(revision, "--patch=checkout", - opts->pathspec); + opts->pathspec.raw); lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 1); - if (read_cache_preload(opts->pathspec) < 0) + if (read_cache_preload(opts->pathspec.raw) < 0) return error(_("corrupt index file")); if (opts->source_tree) - read_tree_some(opts->source_tree, opts->pathspec); + read_tree_some(opts->source_tree, opts->pathspec.raw); - for (pos = 0; opts->pathspec[pos]; pos++) - ; - ps_matched = xcalloc(1, pos); + ps_matched = xcalloc(1, opts->pathspec.nr); /* * Make sure all pathspecs participated in locating the paths @@ -304,12 +302,12 @@ static int checkout_paths(const struct checkout_opts *opts, * match_pathspec() for _all_ entries when * opts->source_tree != NULL. */ - if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), + if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched)) ce->ce_flags |= CE_MATCHED; } - if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) { + if (report_path_error(ps_matched, opts->pathspec.raw, opts->prefix)) { free(ps_matched); return 1; } @@ -1002,7 +1000,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) static int checkout_branch(struct checkout_opts *opts, struct branch_info *new) { - if (opts->pathspec) + if (opts->pathspec.nr) die(_("paths cannot be used with switching branches")); if (opts->patch_mode) @@ -1154,9 +1152,19 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) } if (argc) { - opts.pathspec = get_pathspec(prefix, argv); + /* + * In patch mode (opts.patch_mode != 0), we pass the + * pathspec to an external program, git-add--interactive. + * Do not accept any kind of magic that that program + * cannot handle. Magic mask is pretty safe to be + * lifted for new magic when opts.patch_mode == 0. + */ + parse_pathspec(&opts.pathspec, + opts.patch_mode == 0 ? 0 : + (PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP), + 0, prefix, argv); - if (!opts.pathspec) + if (!opts.pathspec.nr) die(_("invalid path specification")); /* @@ -1188,7 +1196,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) strbuf_release(&buf); } - if (opts.patch_mode || opts.pathspec) + if (opts.patch_mode || opts.pathspec.nr) return checkout_paths(&opts, new.name); else return checkout_branch(&opts, &new); -- cgit v0.10.2-6-g49f6 From 29211a93c14011f3eff2cf2706a809f1b8160fb5 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, 14 Jul 2013 15:35:42 +0700 Subject: rm: convert to use parse_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/builtin/rm.c b/builtin/rm.c index 06025a2..ee0ae4c 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -11,6 +11,7 @@ #include "parse-options.h" #include "string-list.h" #include "submodule.h" +#include "pathspec.h" static const char * const builtin_rm_usage[] = { N_("git rm [options] [--] ..."), @@ -279,7 +280,7 @@ static struct option builtin_rm_options[] = { int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; - const char **pathspec; + struct pathspec pathspec; char *seen; git_config(git_default_config, NULL); @@ -312,31 +313,30 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } } - pathspec = get_pathspec(prefix, argv); - refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv); + refresh_index(&the_index, REFRESH_QUIET, pathspec.raw, NULL, NULL); seen = NULL; - for (i = 0; pathspec[i] ; i++) - /* nothing */; - seen = xcalloc(i, 1); + seen = xcalloc(pathspec.nr, 1); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) + if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); } - if (pathspec) { - const char *match; + if (pathspec.nr) { + const char *original; int seen_any = 0; - for (i = 0; (match = pathspec[i]) != NULL ; i++) { + for (i = 0; i < pathspec.nr; i++) { + original = pathspec.items[i].original; if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), - match); + original); } } else { @@ -344,7 +344,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), - *match ? match : "."); + *original ? original : "."); } if (! seen_any) -- cgit v0.10.2-6-g49f6 From 9e06d6ed7667b9564702f3bcda954748cb8afe0e 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, 14 Jul 2013 15:35:43 +0700 Subject: ls-files: convert to use parse_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/builtin/ls-files.c b/builtin/ls-files.c index 30357df..f1be425 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -31,7 +31,7 @@ static int debug_mode; static const char *prefix; static int max_prefix_len; static int prefix_len; -static const char **pathspec; +static struct pathspec pathspec; static int error_unmatch; static char *ps_matched; static const char *with_tree; @@ -60,7 +60,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (len >= ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); - if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched)) + if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched)) return; fputs(tag, stdout); @@ -135,7 +135,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git ls-files: internal error - cache entry not superset of prefix"); - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched)) + if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched)) return; if (tag && *tag && show_valid_bit && @@ -191,7 +191,7 @@ static void show_ru_info(void) len = strlen(path); if (len < max_prefix_len) continue; /* outside of the prefix */ - if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched)) + if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched)) continue; /* uninterested */ for (i = 0; i < 3; i++) { if (!ui->mode[i]) @@ -216,7 +216,7 @@ static void show_files(struct dir_struct *dir) /* For cached/deleted files we don't need to even do the readdir */ if (show_others || show_killed) { - fill_directory(dir, pathspec); + fill_directory(dir, pathspec.raw); if (show_others) show_other_files(dir); if (show_killed) @@ -284,21 +284,6 @@ static void prune_cache(const char *prefix) active_nr = last; } -static void strip_trailing_slash_from_submodules(void) -{ - const char **p; - - for (p = pathspec; *p != NULL; p++) { - int len = strlen(*p), pos; - - if (len < 1 || (*p)[len - 1] != '/') - continue; - pos = cache_name_pos(*p, len - 1); - if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode)) - *p = xstrndup(*p, len - 1); - } -} - /* * Read the tree specified with --with-tree option * (typically, HEAD) into stage #1 and then @@ -552,23 +537,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); - pathspec = get_pathspec(prefix, argv); - - /* be nice with submodule paths ending in a slash */ - if (pathspec) - strip_trailing_slash_from_submodules(); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD | + PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, + prefix, argv); /* Find common prefix for all pathspec's */ - max_prefix = common_prefix(pathspec); + max_prefix = common_prefix(pathspec.raw); max_prefix_len = max_prefix ? strlen(max_prefix) : 0; /* Treat unmatching pathspec elements as errors */ - if (pathspec && error_unmatch) { - int num; - for (num = 0; pathspec[num]; num++) - ; - ps_matched = xcalloc(1, num); - } + if (pathspec.nr && error_unmatch) + ps_matched = xcalloc(1, pathspec.nr); if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) die("ls-files --ignored needs some exclude pattern"); @@ -595,7 +575,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (ps_matched) { int bad; - bad = report_path_error(ps_matched, pathspec, prefix); + bad = report_path_error(ps_matched, pathspec.raw, prefix); if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); -- cgit v0.10.2-6-g49f6 From f3e743a0d972e8ed3367d74c1152ff66f7cde416 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, 14 Jul 2013 15:35:44 +0700 Subject: archive: convert to use parse_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/archive.c b/archive.c index c699a2d..99fadc8 100644 --- a/archive.c +++ b/archive.c @@ -5,7 +5,6 @@ #include "archive.h" #include "parse-options.h" #include "unpack-trees.h" -#include "pathspec.h" static char const * const archive_usage[] = { N_("git archive [options] [...]"), @@ -152,7 +151,6 @@ int write_archive_entries(struct archiver_args *args, struct archiver_context context; struct unpack_trees_options opts; struct tree_desc t; - struct pathspec pathspec; int err; if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { @@ -187,10 +185,8 @@ int write_archive_entries(struct archiver_args *args, git_attr_set_direction(GIT_ATTR_INDEX, &the_index); } - init_pathspec(&pathspec, args->pathspec); - err = read_tree_recursive(args->tree, "", 0, 0, &pathspec, + err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec, write_archive_entry, &context); - free_pathspec(&pathspec); if (err == READ_TREE_RECURSIVE) err = 0; return err; @@ -223,7 +219,7 @@ static int path_exists(struct tree *tree, const char *path) struct pathspec pathspec; int ret; - init_pathspec(&pathspec, paths); + parse_pathspec(&pathspec, 0, 0, "", paths); ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL); free_pathspec(&pathspec); return ret != 0; @@ -232,11 +228,18 @@ static int path_exists(struct tree *tree, const char *path) static void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) { - ar_args->pathspec = pathspec = get_pathspec("", pathspec); + /* + * must be consistent with parse_pathspec in path_exists() + * Also if pathspec patterns are dependent, we're in big + * trouble as we test each one separately + */ + parse_pathspec(&ar_args->pathspec, 0, + PATHSPEC_PREFER_FULL, + "", pathspec); if (pathspec) { while (*pathspec) { if (**pathspec && !path_exists(ar_args->tree, *pathspec)) - die("path not found: %s", *pathspec); + die(_("pathspec '%s' did not match any files"), *pathspec); pathspec++; } } diff --git a/archive.h b/archive.h index 895afcd..4a791e1 100644 --- a/archive.h +++ b/archive.h @@ -1,6 +1,8 @@ #ifndef ARCHIVE_H #define ARCHIVE_H +#include "pathspec.h" + struct archiver_args { const char *base; size_t baselen; @@ -8,7 +10,7 @@ struct archiver_args { const unsigned char *commit_sha1; const struct commit *commit; time_t time; - const char **pathspec; + struct pathspec pathspec; unsigned int verbose : 1; unsigned int worktree_attributes : 1; unsigned int convert : 1; -- cgit v0.10.2-6-g49f6 From 931eab64ad24a742182c4e946a1138e677a8bd4e 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, 14 Jul 2013 15:35:45 +0700 Subject: check-ignore: convert to use parse_pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check-ignore (at least the test suite) seems to rely on the pattern order. PATHSPEC_KEEP_ORDER is introduced to explictly express this. The lack of PATHSPEC_MAXDEPTH_VALID is sufficient because it's the only flag that reorders pathspecs, but it's less obvious that way. Cc: Adam Spiers Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 4a8fc70..d49c083 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -64,37 +64,45 @@ static void output_exclude(const char *path, struct exclude *exclude) } static int check_ignore(struct dir_struct *dir, - const char *prefix, const char **pathspec) + const char *prefix, int argc, const char **argv) { - const char *path, *full_path; + const char *full_path; char *seen; int num_ignored = 0, dtype = DT_UNKNOWN, i; struct exclude *exclude; + struct pathspec pathspec; - if (!pathspec || !*pathspec) { + if (!argc) { if (!quiet) fprintf(stderr, "no pathspec given.\n"); return 0; } /* + * check-ignore just needs paths. Magic beyond :/ is really + * irrelevant. + */ + parse_pathspec(&pathspec, + PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE | + PATHSPEC_KEEP_ORDER, + prefix, argv); + + /* * look for pathspecs matching entries in the index, since these * should not be ignored, in order to be consistent with * 'git status', 'git add' etc. */ - seen = find_pathspecs_matching_against_index(pathspec); - for (i = 0; pathspec[i]; i++) { - path = pathspec[i]; - full_path = prefix_path(prefix, prefix - ? strlen(prefix) : 0, path); - full_path = check_path_for_gitlink(full_path); - die_if_path_beyond_symlink(full_path, prefix); + seen = find_pathspecs_matching_against_index(pathspec.raw); + for (i = 0; i < pathspec.nr; i++) { + full_path = pathspec.raw[i]; exclude = NULL; if (!seen[i]) { exclude = last_exclude_matching(dir, full_path, &dtype); } if (!quiet && (exclude || show_non_matching)) - output_exclude(path, exclude); + output_exclude(pathspec.items[i].original, exclude); if (exclude) num_ignored++; } @@ -120,7 +128,8 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix) strbuf_swap(&buf, &nbuf); } pathspec[0] = buf.buf; - num_ignored += check_ignore(dir, prefix, (const char **)pathspec); + num_ignored += check_ignore(dir, prefix, + 1, (const char **)pathspec); maybe_flush_or_die(stdout, "check-ignore to stdout"); } strbuf_release(&buf); @@ -166,7 +175,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) if (stdin_paths) { num_ignored = check_ignore_stdin_paths(&dir, prefix); } else { - num_ignored = check_ignore(&dir, prefix, argv); + num_ignored = check_ignore(&dir, prefix, argc, argv); maybe_flush_or_die(stdout, "ignore to stdout"); } diff --git a/pathspec.c b/pathspec.c index ba6408a..5c9631a 100644 --- a/pathspec.c +++ b/pathspec.c @@ -370,9 +370,13 @@ void parse_pathspec(struct pathspec *pathspec, pathspec->magic |= item[i].magic; } - if (pathspec->magic & PATHSPEC_MAXDEPTH) + + if (pathspec->magic & PATHSPEC_MAXDEPTH) { + if (flags & PATHSPEC_KEEP_ORDER) + die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible"); qsort(pathspec->items, pathspec->nr, sizeof(struct pathspec_item), pathspec_item_cmp); + } } /* diff --git a/pathspec.h b/pathspec.h index 6baf205..02dded3 100644 --- a/pathspec.h +++ b/pathspec.h @@ -51,6 +51,7 @@ struct pathspec { */ #define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5) #define PATHSPEC_PREFIX_ORIGIN (1<<6) +#define PATHSPEC_KEEP_ORDER (1<<7) extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index a56db80..2ced8e9 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -432,7 +432,7 @@ test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' ' test_expect_success_multi SYMLINKS 'beyond a symlink' '' ' test_check_ignore "a/symlink/foo" 128 && - test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link" + test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link" ' test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' @@ -440,7 +440,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' cd a && test_check_ignore "symlink/foo" 128 ) && - test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link" + test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link" ' ############################################################################ @@ -449,7 +449,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' test_expect_success_multi 'submodule' '' ' test_check_ignore "a/submodule/one" 128 && - test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" + test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" ' test_expect_success_multi 'submodule from subdirectory' '' ' @@ -457,7 +457,7 @@ test_expect_success_multi 'submodule from subdirectory' '' ' cd a && test_check_ignore "submodule/one" 128 ) && - test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" + test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''" ' ############################################################################ -- cgit v0.10.2-6-g49f6 From 5a76aff1a6f112cc2888b63ee97ade679e6e3e01 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, 14 Jul 2013 15:35:46 +0700 Subject: add: convert to use parse_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/builtin/add.c b/builtin/add.c index f45d9d4..9a7235e 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -226,21 +226,6 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, return seen; } -/* - * Checks the index to see whether any path in pathspec refers to - * something inside a submodule. If so, dies with an error message. - */ -static void treat_gitlinks(const char **pathspec) -{ - int i; - - if (!pathspec || !*pathspec) - return; - - for (i = 0; pathspec[i]; i++) - pathspec[i] = check_path_for_gitlink(pathspec[i]); -} - static void refresh(int verbose, const char **pathspec) { char *seen; @@ -258,25 +243,6 @@ static void refresh(int verbose, const char **pathspec) free(seen); } -/* - * Normalizes argv relative to prefix, via get_pathspec(), and then - * runs die_if_path_beyond_symlink() on each path in the normalized - * list. - */ -static const char **validate_pathspec(const char **argv, const char *prefix) -{ - const char **pathspec = get_pathspec(prefix, argv); - - if (pathspec) { - const char **p; - for (p = pathspec; *p; p++) { - die_if_path_beyond_symlink(*p, prefix); - } - } - - return pathspec; -} - int run_add_interactive(const char *revision, const char *patch_mode, const char **pathspec) { @@ -308,17 +274,23 @@ int run_add_interactive(const char *revision, const char *patch_mode, int interactive_add(int argc, const char **argv, const char *prefix, int patch) { - const char **pathspec = NULL; + struct pathspec pathspec; - if (argc) { - pathspec = validate_pathspec(argv, prefix); - if (!pathspec) - return -1; - } + /* + * git-add--interactive itself does not parse pathspec. It + * simply passes the pathspec to other builtin commands. Let's + * hope all of them support all magic, or we'll need to limit + * the magic here. There is still a problem with prefix. But + * that'll be worked on later on. + */ + parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH, + prefix, argv); return run_add_interactive(NULL, patch ? "--patch" : NULL, - pathspec); + pathspec.raw); } static int edit_patch(int argc, const char **argv, const char *prefix) @@ -445,7 +417,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; int newfd; - const char **pathspec; + struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; @@ -526,11 +498,19 @@ int cmd_add(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } - pathspec = validate_pathspec(argv, prefix); if (read_cache() < 0) die(_("index file corrupt")); - treat_gitlinks(pathspec); + + /* + * Check the "pathspec '%s' did not match any files" block + * below before enabling new magic. + */ + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, + prefix, argv); if (add_new_files) { int baselen; @@ -543,34 +523,40 @@ int cmd_add(int argc, const char **argv, const char *prefix) } /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec); - if (pathspec) - seen = prune_directory(&dir, pathspec, baselen, + baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec.raw); + if (pathspec.nr) + seen = prune_directory(&dir, pathspec.raw, baselen, implicit_dot ? WARN_IMPLICIT_DOT : 0); } if (refresh_only) { - refresh(verbose, pathspec); + refresh(verbose, pathspec.raw); goto finish; } if (implicit_dot && prefix) refresh_cache(REFRESH_QUIET); - if (pathspec) { + if (pathspec.nr) { int i; if (!seen) - seen = find_pathspecs_matching_against_index(pathspec); - for (i = 0; pathspec[i]; i++) { - if (!seen[i] && pathspec[i][0] - && !file_exists(pathspec[i])) { + seen = find_pathspecs_matching_against_index(pathspec.raw); + + /* + * file_exists() assumes exact match + */ + GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP); + + for (i = 0; pathspec.raw[i]; i++) { + if (!seen[i] && pathspec.raw[i][0] + && !file_exists(pathspec.raw[i])) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, pathspec[i], &dtype)) - dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); + if (is_excluded(&dir, pathspec.raw[i], &dtype)) + dir_add_ignored(&dir, pathspec.raw[i], strlen(pathspec.raw[i])); } else die(_("pathspec '%s' did not match any files"), - pathspec[i]); + pathspec.raw[i]); } } free(seen); @@ -586,10 +572,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) */ update_data.implicit_dot = prefix; update_data.implicit_dot_len = strlen(prefix); - pathspec = NULL; + free_pathspec(&pathspec); + memset(&pathspec, 0, sizeof(pathspec)); } update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; - update_files_in_cache(prefix, pathspec, &update_data); + update_files_in_cache(prefix, pathspec.raw, &update_data); exit_status |= !!update_data.add_errors; if (add_new_files) diff --git a/pathspec.c b/pathspec.c index 5c9631a..2774bda 100644 --- a/pathspec.c +++ b/pathspec.c @@ -58,49 +58,6 @@ char *find_pathspecs_matching_against_index(const char **pathspec) } /* - * Check the index to see whether path refers to a submodule, or - * something inside a submodule. If the former, returns the path with - * any trailing slash stripped. If the latter, dies with an error - * message. - */ -const char *check_path_for_gitlink(const char *path) -{ - int i, path_len = strlen(path); - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - if (S_ISGITLINK(ce->ce_mode)) { - int ce_len = ce_namelen(ce); - if (path_len <= ce_len || path[ce_len] != '/' || - memcmp(ce->name, path, ce_len)) - /* path does not refer to this - * submodule or anything inside it */ - continue; - if (path_len == ce_len + 1) { - /* path refers to submodule; - * strip trailing slash */ - return xstrndup(ce->name, ce_len); - } else { - die (_("Path '%s' is in submodule '%.*s'"), - path, ce_len, ce->name); - } - } - } - return path; -} - -/* - * Dies if the given path refers to a file inside a symlinked - * directory in the index. - */ -void die_if_path_beyond_symlink(const char *path, const char *prefix) -{ - if (has_symlink_leading_path(path, strlen(path))) { - int len = prefix ? strlen(prefix) : 0; - die(_("'%s' is beyond a symbolic link"), path + len); - } -} - -/* * Magic pathspec * * Possible future magic semantics include stuff like: -- cgit v0.10.2-6-g49f6 From f8144c9fcff09597670ac7bf69d5cde4af5f206c 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, 14 Jul 2013 15:35:47 +0700 Subject: reset: convert to use parse_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/builtin/reset.c b/builtin/reset.c index afa6e02..313b296 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -174,7 +174,10 @@ static void die_if_unmerged_cache(int reset_type) } -static const char **parse_args(const char **argv, const char *prefix, const char **rev_ret) +static void parse_args(struct pathspec *pathspec, + const char **argv, const char *prefix, + int patch_mode, + const char **rev_ret) { const char *rev = "HEAD"; unsigned char unused[20]; @@ -216,7 +219,10 @@ static const char **parse_args(const char **argv, const char *prefix, const char } } *rev_ret = rev; - return argv[0] ? get_pathspec(prefix, argv) : NULL; + parse_pathspec(pathspec, + patch_mode ? PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP : 0, + PATHSPEC_PREFER_FULL, + prefix, argv); } static int update_refs(const char *rev, const unsigned char *sha1) @@ -246,7 +252,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) int patch_mode = 0, unborn; const char *rev; unsigned char sha1[20]; - const char **pathspec = NULL; + struct pathspec pathspec; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, @@ -266,13 +272,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); - pathspec = parse_args(argv, prefix, &rev); + parse_args(&pathspec, argv, prefix, patch_mode, &rev); unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ hashcpy(sha1, EMPTY_TREE_SHA1_BIN); - } else if (!pathspec) { + } else if (!pathspec.nr) { struct commit *commit; if (get_sha1_committish(rev, sha1)) die(_("Failed to resolve '%s' as a valid revision."), rev); @@ -293,13 +299,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); - return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec); + return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec.raw); } /* git reset tree [--] paths... can be used to * load chosen paths from the tree into the index without * affecting the working tree nor HEAD. */ - if (pathspec) { + if (pathspec.nr) { if (reset_type == MIXED) warning(_("--mixed with paths is deprecated; use 'git reset -- ' instead.")); else if (reset_type != NONE) @@ -326,7 +332,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { - if (read_from_tree(pathspec, sha1)) + if (read_from_tree(pathspec.raw, sha1)) return 1; } else { int err = reset_index(sha1, reset_type, quiet); @@ -347,7 +353,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) die(_("Could not write new index file.")); } - if (!pathspec && !unborn) { + if (!pathspec.nr && !unborn) { /* Any resets without paths update HEAD to the head being * switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status = update_refs(rev, sha1); @@ -355,7 +361,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type == HARD && !update_ref_status && !quiet) print_new_head_line(lookup_commit_reference(sha1)); } - if (!pathspec) + if (!pathspec.nr) remove_branch_state(); return update_ref_status; -- cgit v0.10.2-6-g49f6 From 78a951432d0f2e16d8b7bd09cb9665315b7a3d16 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, 14 Jul 2013 15:35:48 +0700 Subject: line-log: convert to use parse_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/line-log.c b/line-log.c index 4bbb09b..843a334 100644 --- a/line-log.c +++ b/line-log.c @@ -747,7 +747,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list r = r->next; } paths[count] = NULL; - init_pathspec(&rev->diffopt.pathspec, paths); + parse_pathspec(&rev->diffopt.pathspec, 0, 0, "", paths); free(paths); } } -- cgit v0.10.2-6-g49f6 From 5ab2a2dabd6e13cd6830ff4f12f232dc79278e29 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, 14 Jul 2013 15:35:49 +0700 Subject: convert read_cache_preload() to take struct 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/builtin/checkout.c b/builtin/checkout.c index 6721de2..2f0fb8d 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -262,7 +262,7 @@ static int checkout_paths(const struct checkout_opts *opts, lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 1); - if (read_cache_preload(opts->pathspec.raw) < 0) + if (read_cache_preload(&opts->pathspec) < 0) return error(_("corrupt index file")); if (opts->source_tree) diff --git a/builtin/commit.c b/builtin/commit.c index 64d1a3d..0344ec7 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -294,7 +294,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, PATHSPEC_PREFER_FULL, prefix, argv); - if (read_cache_preload(pathspec.raw) < 0) + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); if (interactive) { @@ -1245,7 +1245,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) PATHSPEC_PREFER_FULL, prefix, argv); - read_cache_preload(s.pathspec.raw); + read_cache_preload(&s.pathspec); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec.raw, NULL, NULL); fd = hold_locked_index(&index_lock, 0); diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 46085f8..9200069 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; - if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 1c737f7..ce15b23 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -43,7 +43,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) usage(diff_cache_usage); if (!cached) { setup_work_tree(); - if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/diff.c b/builtin/diff.c index 6bb41af..bb84ba0 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -140,7 +140,7 @@ static int builtin_diff_index(struct rev_info *revs, usage(builtin_diff_usage); if (!cached) { setup_work_tree(); - if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&revs->diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } @@ -242,7 +242,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); - if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&revs->diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } diff --git a/cache.h b/cache.h index fd0a6f8..0370546 100644 --- a/cache.h +++ b/cache.h @@ -449,7 +449,7 @@ extern int init_db(const char *template_dir, unsigned int flags); /* Initialize and use the cache information */ extern int read_index(struct index_state *); -extern int read_index_preload(struct index_state *, const char **pathspec); +extern int read_index_preload(struct index_state *, const struct pathspec *pathspec); extern int read_index_from(struct index_state *, const char *path); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); diff --git a/preload-index.c b/preload-index.c index cddfffa..8c44ceb 100644 --- a/preload-index.c +++ b/preload-index.c @@ -5,7 +5,8 @@ #include "pathspec.h" #ifdef NO_PTHREADS -static void preload_index(struct index_state *index, const char **pathspec) +static void preload_index(struct index_state *index, + const struct pathspec *pathspec) { ; /* nothing */ } @@ -25,7 +26,7 @@ static void preload_index(struct index_state *index, const char **pathspec) struct thread_data { pthread_t pthread; struct index_state *index; - const char **pathspec; + struct pathspec pathspec; int offset, nr; }; @@ -36,9 +37,7 @@ static void *preload_thread(void *_data) struct index_state *index = p->index; struct cache_entry **cep = index->cache + p->offset; struct cache_def cache; - struct pathspec pathspec; - init_pathspec(&pathspec, p->pathspec); memset(&cache, 0, sizeof(cache)); nr = p->nr; if (nr + p->offset > index->cache_nr) @@ -54,7 +53,7 @@ static void *preload_thread(void *_data) continue; if (ce_uptodate(ce)) continue; - if (!ce_path_match(ce, &pathspec)) + if (!ce_path_match(ce, &p->pathspec)) continue; if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce))) continue; @@ -64,11 +63,11 @@ static void *preload_thread(void *_data) continue; ce_mark_uptodate(ce); } while (--nr > 0); - free_pathspec(&pathspec); return NULL; } -static void preload_index(struct index_state *index, const char **pathspec) +static void preload_index(struct index_state *index, + const struct pathspec *pathspec) { int threads, i, work, offset; struct thread_data data[MAX_PARALLEL]; @@ -83,10 +82,12 @@ static void preload_index(struct index_state *index, const char **pathspec) threads = MAX_PARALLEL; offset = 0; work = DIV_ROUND_UP(index->cache_nr, threads); + memset(&data, 0, sizeof(data)); for (i = 0; i < threads; i++) { struct thread_data *p = data+i; p->index = index; - p->pathspec = pathspec; + if (pathspec) + copy_pathspec(&p->pathspec, pathspec); p->offset = offset; p->nr = work; offset += work; @@ -101,7 +102,8 @@ static void preload_index(struct index_state *index, const char **pathspec) } #endif -int read_index_preload(struct index_state *index, const char **pathspec) +int read_index_preload(struct index_state *index, + const struct pathspec *pathspec) { int retval = read_index(index); -- cgit v0.10.2-6-g49f6 From 480ca6449ed0981311b8aef2fbaa89d024b881ee 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, 14 Jul 2013 15:35:50 +0700 Subject: convert run_add_interactive to use struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This passes the pathspec, more or less unmodified, to git-add--interactive. The command itself does not process pathspec. It simply passes the pathspec to other builtin commands. So if all those commands support pathspec, we're good. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/add.c b/builtin/add.c index 9a7235e..d039fc9 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -244,16 +244,12 @@ static void refresh(int verbose, const char **pathspec) } int run_add_interactive(const char *revision, const char *patch_mode, - const char **pathspec) + const struct pathspec *pathspec) { - int status, ac, pc = 0; + int status, ac, i; const char **args; - if (pathspec) - while (pathspec[pc]) - pc++; - - args = xcalloc(sizeof(const char *), (pc + 5)); + args = xcalloc(sizeof(const char *), (pathspec->nr + 6)); ac = 0; args[ac++] = "add--interactive"; if (patch_mode) @@ -261,11 +257,9 @@ int run_add_interactive(const char *revision, const char *patch_mode, if (revision) args[ac++] = revision; args[ac++] = "--"; - if (pc) { - memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc); - ac += pc; - } - args[ac] = NULL; + for (i = 0; i < pathspec->nr; i++) + /* pass original pathspec, to be re-parsed */ + args[ac++] = pathspec->items[i].original; status = run_command_v_opt(args, RUN_GIT_CMD); free(args); @@ -280,17 +274,17 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch) * git-add--interactive itself does not parse pathspec. It * simply passes the pathspec to other builtin commands. Let's * hope all of them support all magic, or we'll need to limit - * the magic here. There is still a problem with prefix. But - * that'll be worked on later on. + * the magic here. */ parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, PATHSPEC_PREFER_FULL | - PATHSPEC_SYMLINK_LEADING_PATH, + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_PREFIX_ORIGIN, prefix, argv); return run_add_interactive(NULL, patch ? "--patch" : NULL, - pathspec.raw); + &pathspec); } static int edit_patch(int argc, const char **argv, const char *prefix) diff --git a/builtin/checkout.c b/builtin/checkout.c index 2f0fb8d..5d31767 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -257,7 +257,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) return run_add_interactive(revision, "--patch=checkout", - opts->pathspec.raw); + &opts->pathspec); lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -1159,10 +1159,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) * cannot handle. Magic mask is pretty safe to be * lifted for new magic when opts.patch_mode == 0. */ - parse_pathspec(&opts.pathspec, - opts.patch_mode == 0 ? 0 : - (PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP), - 0, prefix, argv); + parse_pathspec(&opts.pathspec, 0, + opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0, + prefix, argv); if (!opts.pathspec.nr) die(_("invalid path specification")); diff --git a/builtin/reset.c b/builtin/reset.c index 313b296..9bef869 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -219,9 +219,9 @@ static void parse_args(struct pathspec *pathspec, } } *rev_ret = rev; - parse_pathspec(pathspec, - patch_mode ? PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP : 0, - PATHSPEC_PREFER_FULL, + parse_pathspec(pathspec, 0, + PATHSPEC_PREFER_FULL | + (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0), prefix, argv); } @@ -299,7 +299,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); - return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec.raw); + return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", &pathspec); } /* git reset tree [--] paths... can be used to diff --git a/commit.h b/commit.h index 4d452dc..1f1350c 100644 --- a/commit.h +++ b/commit.h @@ -194,7 +194,7 @@ int in_merge_bases_many(struct commit *, int, struct commit **); extern int interactive_add(int argc, const char **argv, const char *prefix, int patch); extern int run_add_interactive(const char *revision, const char *patch_mode, - const char **pathspec); + const struct pathspec *pathspec); static inline int single_parent(struct commit *commit) { -- cgit v0.10.2-6-g49f6 From 5ab06518a71a8bd128a2aea38ddb537870d3ea2b 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, 14 Jul 2013 15:35:51 +0700 Subject: convert unmerge_cache to take struct 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/rerere.c b/rerere.c index 27afbfe..4105bca 100644 --- a/rerere.c +++ b/rerere.c @@ -668,7 +668,7 @@ int rerere_forget(struct pathspec *pathspec) fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE); - unmerge_cache(pathspec->raw); + unmerge_cache(pathspec); find_conflict(&conflict); for (i = 0; i < conflict.nr; i++) { struct string_list_item *it = &conflict.items[i]; diff --git a/resolve-undo.c b/resolve-undo.c index 639eb9c..4b78e6f 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -173,7 +173,7 @@ void unmerge_marked_index(struct index_state *istate) } } -void unmerge_index(struct index_state *istate, const char **pathspec) +void unmerge_index(struct index_state *istate, const struct pathspec *pathspec) { int i; @@ -182,7 +182,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec) for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) + if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) continue; i = unmerge_index_entry_at(istate, i); } diff --git a/resolve-undo.h b/resolve-undo.h index 7a30206..4630645 100644 --- a/resolve-undo.h +++ b/resolve-undo.h @@ -11,7 +11,7 @@ extern void resolve_undo_write(struct strbuf *, struct string_list *); extern struct string_list *resolve_undo_read(const char *, unsigned long); extern void resolve_undo_clear_index(struct index_state *); extern int unmerge_index_entry_at(struct index_state *, int); -extern void unmerge_index(struct index_state *, const char **); +extern void unmerge_index(struct index_state *, const struct pathspec *); extern void unmerge_marked_index(struct index_state *); #endif -- cgit v0.10.2-6-g49f6 From 18e4f4059936dd35dfe929f502101ca371af856c 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, 14 Jul 2013 15:35:52 +0700 Subject: checkout: convert read_tree_some to take struct 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/builtin/checkout.c b/builtin/checkout.c index 5d31767..c2f3571 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -83,12 +83,9 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, return 0; } -static int read_tree_some(struct tree *tree, const char **pathspec) +static int read_tree_some(struct tree *tree, const struct pathspec *pathspec) { - struct pathspec ps; - init_pathspec(&ps, pathspec); - read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL); - free_pathspec(&ps); + read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL); /* update the index with the given tree's info * for all args, expanding wildcards, and exit @@ -266,7 +263,7 @@ static int checkout_paths(const struct checkout_opts *opts, return error(_("corrupt index file")); if (opts->source_tree) - read_tree_some(opts->source_tree, opts->pathspec.raw); + read_tree_some(opts->source_tree, &opts->pathspec); ps_matched = xcalloc(1, opts->pathspec.nr); diff --git a/tree.c b/tree.c index 62fed63..ff72f67 100644 --- a/tree.c +++ b/tree.c @@ -47,7 +47,7 @@ static int read_one_entry_quick(const unsigned char *sha1, const char *base, int } static int read_tree_1(struct tree *tree, struct strbuf *base, - int stage, struct pathspec *pathspec, + int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context) { struct tree_desc desc; @@ -116,7 +116,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, int read_tree_recursive(struct tree *tree, const char *base, int baselen, - int stage, struct pathspec *pathspec, + int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context) { struct strbuf sb = STRBUF_INIT; diff --git a/tree.h b/tree.h index 69bcb5e..9dc90ba 100644 --- a/tree.h +++ b/tree.h @@ -25,7 +25,7 @@ typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const ch extern int read_tree_recursive(struct tree *tree, const char *base, int baselen, - int stage, struct pathspec *pathspec, + int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context); extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec); -- cgit v0.10.2-6-g49f6 From 17ddc66e702a82671744d1dc5ee59272bd220da6 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, 14 Jul 2013 15:35:53 +0700 Subject: convert report_path_error to take struct 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/builtin/checkout.c b/builtin/checkout.c index c2f3571..7ea1100 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -304,7 +304,7 @@ static int checkout_paths(const struct checkout_opts *opts, ce->ce_flags |= CE_MATCHED; } - if (report_path_error(ps_matched, opts->pathspec.raw, opts->prefix)) { + if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) { free(ps_matched); return 1; } diff --git a/builtin/commit.c b/builtin/commit.c index 0344ec7..eaecf7c 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -188,20 +188,18 @@ static int commit_index_files(void) * and return the paths that match the given pattern in list. */ static int list_paths(struct string_list *list, const char *with_tree, - const char *prefix, const char **pattern) + const char *prefix, const struct pathspec *pattern) { int i; char *m; - if (!pattern) + if (!pattern->nr) return 0; - for (i = 0; pattern[i]; i++) - ; - m = xcalloc(1, i); + m = xcalloc(1, pattern->nr); if (with_tree) { - char *max_prefix = common_prefix(pattern); + char *max_prefix = common_prefix(pattern->raw); overlay_tree_on_cache(with_tree, max_prefix ? max_prefix : prefix); free(max_prefix); } @@ -212,7 +210,7 @@ static int list_paths(struct string_list *list, const char *with_tree, if (ce->ce_flags & CE_UPDATE) continue; - if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m)) + if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m)) continue; item = string_list_insert(list, ce->name); if (ce_skip_worktree(ce)) @@ -402,7 +400,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; - if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec.raw)) + if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec)) exit(1); discard_cache(); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index f1be425..50e6edf 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -346,15 +346,16 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) } } -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix) +int report_path_error(const char *ps_matched, + const struct pathspec *pathspec, + const char *prefix) { /* * Make sure all pathspec matched; otherwise it is an error. */ struct strbuf sb = STRBUF_INIT; - const char *name; int num, errors = 0; - for (num = 0; pathspec[num]; num++) { + for (num = 0; num < pathspec->nr; num++) { int other, found_dup; if (ps_matched[num]) @@ -362,13 +363,16 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char /* * The caller might have fed identical pathspec * twice. Do not barf on such a mistake. + * FIXME: parse_pathspec should have eliminated + * duplicate pathspec. */ for (found_dup = other = 0; - !found_dup && pathspec[other]; + !found_dup && other < pathspec->nr; other++) { if (other == num || !ps_matched[other]) continue; - if (!strcmp(pathspec[other], pathspec[num])) + if (!strcmp(pathspec->items[other].original, + pathspec->items[num].original)) /* * Ok, we have a match already. */ @@ -377,9 +381,8 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char if (found_dup) continue; - name = quote_path_relative(pathspec[num], -1, &sb, prefix); error("pathspec '%s' did not match any file(s) known to git.", - name); + pathspec->items[num].original); errors++; } strbuf_release(&sb); @@ -575,7 +578,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (ps_matched) { int bad; - bad = report_path_error(ps_matched, pathspec.raw, prefix); + bad = report_path_error(ps_matched, &pathspec, prefix); if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); diff --git a/cache.h b/cache.h index 0370546..42c9920 100644 --- a/cache.h +++ b/cache.h @@ -1312,7 +1312,7 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule); #define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK) /* ls-files */ -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix); +int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix); void overlay_tree_on_cache(const char *tree_name, const char *prefix); char *alias_lookup(const char *alias); -- cgit v0.10.2-6-g49f6 From 9b2d61499b4ff9ded3e1f3d535912ce04c21d72e 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, 14 Jul 2013 15:35:54 +0700 Subject: convert refresh_index to take struct 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/builtin/add.c b/builtin/add.c index d039fc9..f5d6a33 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -226,19 +226,18 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, return seen; } -static void refresh(int verbose, const char **pathspec) +static void refresh(int verbose, const struct pathspec *pathspec) { char *seen; - int i, specs; + int i; - for (specs = 0; pathspec[specs]; specs++) - /* nothing */; - seen = xcalloc(specs, 1); + seen = xcalloc(pathspec->nr, 1); refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET, pathspec, seen, _("Unstaged changes after refreshing the index:")); - for (i = 0; i < specs; i++) { + for (i = 0; i < pathspec->nr; i++) { if (!seen[i]) - die(_("pathspec '%s' did not match any files"), pathspec[i]); + die(_("pathspec '%s' did not match any files"), + pathspec->items[i].match); } free(seen); } @@ -524,7 +523,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } if (refresh_only) { - refresh(verbose, pathspec.raw); + refresh(verbose, &pathspec); goto finish; } if (implicit_dot && prefix) diff --git a/builtin/commit.c b/builtin/commit.c index eaecf7c..d34baab 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1244,7 +1244,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) prefix, argv); read_cache_preload(&s.pathspec); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec.raw, NULL, NULL); + refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) diff --git a/builtin/rm.c b/builtin/rm.c index ee0ae4c..f08561d 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -314,7 +314,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv); - refresh_index(&the_index, REFRESH_QUIET, pathspec.raw, NULL, NULL); + refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); seen = NULL; seen = xcalloc(pathspec.nr, 1); diff --git a/cache.h b/cache.h index 42c9920..b294277 100644 --- a/cache.h +++ b/cache.h @@ -520,7 +520,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ #define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */ #define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg); +extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg); struct lock_file { struct lock_file *next; diff --git a/read-cache.c b/read-cache.c index d5201f9..6ad2ff6 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1114,7 +1114,8 @@ static void show_file(const char * fmt, const char * name, int in_porcelain, printf(fmt, name); } -int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, +int refresh_index(struct index_state *istate, unsigned int flags, + const struct pathspec *pathspec, char *seen, const char *header_msg) { int i; @@ -1149,7 +1150,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p continue; if (pathspec && - !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) + !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen)) filtered = 1; if (ce_stage(ce)) { -- cgit v0.10.2-6-g49f6 From 7327d3d1b70bfde70840aa8970fe70de6ef1be16 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, 14 Jul 2013 15:35:55 +0700 Subject: convert {read,fill}_directory to take struct 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/builtin/add.c b/builtin/add.c index f5d6a33..34c9358 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -507,6 +507,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (add_new_files) { int baselen; + struct pathspec empty_pathspec; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); @@ -515,8 +516,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) setup_standard_excludes(&dir); } + memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec.raw); + baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); if (pathspec.nr) seen = prune_directory(&dir, pathspec.raw, baselen, implicit_dot ? WARN_IMPLICIT_DOT : 0); diff --git a/builtin/clean.c b/builtin/clean.c index fdd4980..d540ca4 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -214,7 +214,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) PATHSPEC_PREFER_CWD, prefix, argv); - fill_directory(&dir, pathspec.raw); + fill_directory(&dir, &pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; diff --git a/builtin/grep.c b/builtin/grep.c index 4bc0754..76a6a60 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -523,7 +523,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, if (exc_std) setup_standard_excludes(&dir); - fill_directory(&dir, pathspec->raw); + fill_directory(&dir, pathspec); for (i = 0; i < dir.nr; i++) { const char *name = dir.entries[i]->name; int namelen = strlen(name); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 50e6edf..fa1a6be 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -216,7 +216,7 @@ static void show_files(struct dir_struct *dir) /* For cached/deleted files we don't need to even do the readdir */ if (show_others || show_killed) { - fill_directory(dir, pathspec.raw); + fill_directory(dir, &pathspec); if (show_others) show_other_files(dir); if (show_killed) diff --git a/dir.c b/dir.c index 19978d3..290c7a3 100644 --- a/dir.c +++ b/dir.c @@ -142,7 +142,7 @@ char *common_prefix(const char **pathspec) return len ? xmemdupz(*pathspec, len) : NULL; } -int fill_directory(struct dir_struct *dir, const char **pathspec) +int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) { size_t len; @@ -150,10 +150,10 @@ int fill_directory(struct dir_struct *dir, const char **pathspec) * Calculate common prefix for the pathspec, and * use that to optimize the directory walk */ - len = common_prefix_len(pathspec); + len = common_prefix_len(pathspec->raw); /* Read the directory and prune it */ - read_directory(dir, pathspec ? *pathspec : "", len, pathspec); + read_directory(dir, pathspec->nr ? pathspec->raw[0] : "", len, pathspec); return len; } @@ -1388,14 +1388,20 @@ static int treat_leading_path(struct dir_struct *dir, return rc; } -int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec) +int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec) { struct path_simplify *simplify; + /* + * Check out create_simplify() + */ + if (pathspec) + GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + if (has_symlink_leading_path(path, len)) return dir->nr; - simplify = create_simplify(pathspec); + simplify = create_simplify(pathspec ? pathspec->raw : NULL); if (!len || treat_leading_path(dir, path, len, simplify)) read_directory_recursive(dir, path, len, 0, simplify); free_simplify(simplify); diff --git a/dir.h b/dir.h index 229ccc8..076dd96 100644 --- a/dir.h +++ b/dir.h @@ -137,8 +137,8 @@ extern int match_pathspec_depth(const struct pathspec *pathspec, int prefix, char *seen); extern int within_depth(const char *name, int namelen, int depth, int max_depth); -extern int fill_directory(struct dir_struct *dir, const char **pathspec); -extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec); +extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec); +extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec); extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, struct exclude_list *el); diff --git a/wt-status.c b/wt-status.c index fae0c27..33baf0a 100644 --- a/wt-status.c +++ b/wt-status.c @@ -514,7 +514,7 @@ static void wt_status_collect_untracked(struct wt_status *s) dir.flags |= DIR_SHOW_IGNORED_TOO; setup_standard_excludes(&dir); - fill_directory(&dir, s->pathspec.raw); + fill_directory(&dir, &s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; -- cgit v0.10.2-6-g49f6 From 3efe8e43813c859b78796b985a4fc06c2c153fb4 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, 14 Jul 2013 15:35:56 +0700 Subject: convert add_files_to_cache to take struct 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/builtin/add.c b/builtin/add.c index 34c9358..a47aeb4 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -166,14 +166,16 @@ static void update_callback(struct diff_queue_struct *q, } } -static void update_files_in_cache(const char *prefix, const char **pathspec, +static void update_files_in_cache(const char *prefix, + const struct pathspec *pathspec, struct update_callback_data *data) { struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - init_pathspec(&rev.prune_data, pathspec); + if (pathspec) + copy_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = data; @@ -181,7 +183,8 @@ static void update_files_in_cache(const char *prefix, const char **pathspec, run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } -int add_files_to_cache(const char *prefix, const char **pathspec, int flags) +int add_files_to_cache(const char *prefix, + const struct pathspec *pathspec, int flags) { struct update_callback_data data; @@ -571,7 +574,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) memset(&pathspec, 0, sizeof(pathspec)); } update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; - update_files_in_cache(prefix, pathspec.raw, &update_data); + update_files_in_cache(prefix, &pathspec, &update_data); exit_status |= !!update_data.add_errors; if (add_new_files) diff --git a/builtin/commit.c b/builtin/commit.c index d34baab..3b4dd60 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -336,7 +336,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, */ if (all || (also && pathspec.nr)) { fd = hold_locked_index(&index_lock, 1); - add_files_to_cache(also ? prefix : NULL, pathspec.raw, 0); + add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || diff --git a/cache.h b/cache.h index b294277..b0ed117 100644 --- a/cache.h +++ b/cache.h @@ -1278,7 +1278,7 @@ void packet_trace_identity(const char *prog); * return 0 if success, 1 - if addition of a file failed and * ADD_FILES_IGNORE_ERRORS was specified in flags */ -int add_files_to_cache(const char *prefix, const char **pathspec, int flags); +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags); /* diff.c */ extern int diff_auto_refresh_index; -- cgit v0.10.2-6-g49f6 From 827f4d6c218448d58f934c0980b6366407261b74 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, 14 Jul 2013 15:35:57 +0700 Subject: convert common_prefix() to use struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code now takes advantage of nowildcard_len field. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/commit.c b/builtin/commit.c index 3b4dd60..4ee9ba6 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -199,7 +199,7 @@ static int list_paths(struct string_list *list, const char *with_tree, m = xcalloc(1, pattern->nr); if (with_tree) { - char *max_prefix = common_prefix(pattern->raw); + char *max_prefix = common_prefix(pattern); overlay_tree_on_cache(with_tree, max_prefix ? max_prefix : prefix); free(max_prefix); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index fa1a6be..c074e6f 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -546,7 +546,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) prefix, argv); /* Find common prefix for all pathspec's */ - max_prefix = common_prefix(pathspec.raw); + max_prefix = common_prefix(&pathspec); max_prefix_len = max_prefix ? strlen(max_prefix) : 0; /* Treat unmatching pathspec elements as errors */ diff --git a/dir.c b/dir.c index 290c7a3..019ad09 100644 --- a/dir.c +++ b/dir.c @@ -103,26 +103,25 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen, return match_status; } -static size_t common_prefix_len(const char **pathspec) +static size_t common_prefix_len(const struct pathspec *pathspec) { - const char *n, *first; + int n; size_t max = 0; - int literal = limit_pathspec_to_literal(); - if (!pathspec) - return max; - - first = *pathspec; - while ((n = *pathspec++)) { - size_t i, len = 0; - for (i = 0; first == n || i < max; i++) { - char c = n[i]; - if (!c || c != first[i] || (!literal && is_glob_special(c))) + GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + + for (n = 0; n < pathspec->nr; n++) { + size_t i = 0, len = 0; + while (i < pathspec->items[n].nowildcard_len && + (n == 0 || i < max)) { + char c = pathspec->items[n].match[i]; + if (c != pathspec->items[0].match[i]) break; if (c == '/') len = i + 1; + i++; } - if (first == n || len < max) { + if (n == 0 || len < max) { max = len; if (!max) break; @@ -135,11 +134,11 @@ static size_t common_prefix_len(const char **pathspec) * Returns a copy of the longest leading path common among all * pathspecs. */ -char *common_prefix(const char **pathspec) +char *common_prefix(const struct pathspec *pathspec) { unsigned long len = common_prefix_len(pathspec); - return len ? xmemdupz(*pathspec, len) : NULL; + return len ? xmemdupz(pathspec->items[0].match, len) : NULL; } int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) @@ -150,7 +149,7 @@ int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) * Calculate common prefix for the pathspec, and * use that to optimize the directory walk */ - len = common_prefix_len(pathspec->raw); + len = common_prefix_len(pathspec); /* Read the directory and prune it */ read_directory(dir, pathspec->nr ? pathspec->raw[0] : "", len, pathspec); diff --git a/dir.h b/dir.h index 076dd96..ba40e69 100644 --- a/dir.h +++ b/dir.h @@ -130,7 +130,7 @@ struct dir_struct { #define MATCHED_EXACTLY 3 extern int simple_length(const char *match); extern int no_wildcard(const char *string); -extern char *common_prefix(const char **pathspec); +extern char *common_prefix(const struct pathspec *pathspec); extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); extern int match_pathspec_depth(const struct pathspec *pathspec, const char *name, int namelen, -- cgit v0.10.2-6-g49f6 From bd1928df1d1cc6a10a7b275694d3504d9a041fae 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, 14 Jul 2013 15:35:58 +0700 Subject: remove diff_tree_{setup,release}_paths 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/builtin/blame.c b/builtin/blame.c index 079dcd3..5bd721d 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -408,7 +408,7 @@ static struct origin *find_origin(struct scoreboard *sb, paths[0] = origin->path; paths[1] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) @@ -458,7 +458,7 @@ static struct origin *find_origin(struct scoreboard *sb, } } diff_flush(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); if (porigin) { /* * Create a freestanding copy that is not part of @@ -494,7 +494,7 @@ static struct origin *find_rename(struct scoreboard *sb, diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) @@ -516,7 +516,7 @@ static struct origin *find_rename(struct scoreboard *sb, } } diff_flush(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); return porigin; } @@ -1079,7 +1079,7 @@ static int find_copy_in_parent(struct scoreboard *sb, diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; @@ -1162,7 +1162,7 @@ static int find_copy_in_parent(struct scoreboard *sb, } reset_scanned_flag(sb); diff_flush(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); return retval; } diff --git a/builtin/reset.c b/builtin/reset.c index 9bef869..86150d1 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -133,12 +133,13 @@ static void update_index_from_diff(struct diff_queue_struct *q, } } -static int read_from_tree(const char **pathspec, unsigned char *tree_sha1) +static int read_from_tree(const struct pathspec *pathspec, + unsigned char *tree_sha1) { struct diff_options opt; memset(&opt, 0, sizeof(opt)); - diff_tree_setup_paths(pathspec, &opt); + copy_pathspec(&opt.pathspec, pathspec); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; @@ -147,7 +148,7 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1) return 1; diffcore_std(&opt); diff_flush(&opt); - diff_tree_release_paths(&opt); + free_pathspec(&opt.pathspec); return 0; } @@ -332,7 +333,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { - if (read_from_tree(pathspec.raw, sha1)) + if (read_from_tree(&pathspec, sha1)) return 1; } else { int err = reset_index(sha1, reset_type, quiet); diff --git a/combine-diff.c b/combine-diff.c index 6dc0609..c973f5d 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1305,7 +1305,7 @@ void diff_tree_combined(const unsigned char *sha1, int i, num_paths, needsep, show_log_first, num_parent = parents->nr; diffopts = *opt; - diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts); + copy_pathspec(&diffopts.pathspec, &opt->pathspec); diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; DIFF_OPT_SET(&diffopts, RECURSIVE); DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL); @@ -1377,7 +1377,7 @@ void diff_tree_combined(const unsigned char *sha1, free(tmp); } - diff_tree_release_paths(&diffopts); + free_pathspec(&diffopts.pathspec); } void diff_tree_combined_merge(const struct commit *commit, int dense, diff --git a/diff.h b/diff.h index d1bc914..b8df245 100644 --- a/diff.h +++ b/diff.h @@ -180,8 +180,6 @@ const char *diff_line_prefix(struct diff_options *); extern const char mime_boundary_leader[]; -extern void diff_tree_setup_paths(const char **paths, struct diff_options *); -extern void diff_tree_release_paths(struct diff_options *); extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt); extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, diff --git a/notes-merge.c b/notes-merge.c index ab18857..94a1a8a 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -170,7 +170,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, sha1_to_hex(mp->remote)); } diff_flush(&opt); - diff_tree_release_paths(&opt); + free_pathspec(&opt.pathspec); *num_changes = len; return changes; @@ -256,7 +256,7 @@ static void diff_tree_local(struct notes_merge_options *o, sha1_to_hex(mp->local)); } diff_flush(&opt); - diff_tree_release_paths(&opt); + free_pathspec(&opt.pathspec); } static void check_notes_merge_worktree(struct notes_merge_options *o) diff --git a/revision.c b/revision.c index cdc7f45..c2d44b7 100644 --- a/revision.c +++ b/revision.c @@ -2154,12 +2154,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s revs->limited = 1; if (revs->prune_data.nr) { - diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning); + copy_pathspec(&revs->pruning.pathspec, &revs->prune_data); /* Can't prune commits with rename following: the paths change.. */ if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->prune = 1; if (!revs->full_diff) - diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt); + copy_pathspec(&revs->diffopt.pathspec, + &revs->prune_data); } if (revs->combine_merges) revs->ignore_merges = 0; diff --git a/tree-diff.c b/tree-diff.c index 5a87614..f4c92f6 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -229,11 +229,11 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); diff_tree(t1, t2, base, &diff_opts); diffcore_std(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); /* Go through the new set of filepairing, and see if we find a more interesting one */ opt->found_follow = 0; @@ -252,9 +252,9 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co choice = p; /* Update the path we use from now on.. */ - diff_tree_release_paths(opt); + free_pathspec(&opt->pathspec); opt->pathspec.raw[0] = xstrdup(p->one->path); - diff_tree_setup_paths(opt->pathspec.raw, opt); + init_pathspec(&opt->pathspec, opt->pathspec.raw); /* * The caller expects us to return a set of vanilla @@ -328,13 +328,3 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_ free(tree); return retval; } - -void diff_tree_release_paths(struct diff_options *opt) -{ - free_pathspec(&opt->pathspec); -} - -void diff_tree_setup_paths(const char **p, struct diff_options *opt) -{ - init_pathspec(&opt->pathspec, p); -} -- cgit v0.10.2-6-g49f6 From 9a0872744315da67db3c81eb9270751e31fcc8f5 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, 14 Jul 2013 15:35:59 +0700 Subject: remove init_pathspec() in favor of parse_pathspec() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at there, move free_pathspec() to pathspec.c Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/blame.c b/builtin/blame.c index 5bd721d..56e3d6b 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -408,7 +408,7 @@ static struct origin *find_origin(struct scoreboard *sb, paths[0] = origin->path; paths[1] = NULL; - init_pathspec(&diff_opts.pathspec, paths); + parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC, 0, "", paths); diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) @@ -486,15 +486,12 @@ static struct origin *find_rename(struct scoreboard *sb, struct origin *porigin = NULL; struct diff_options diff_opts; int i; - const char *paths[2]; diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; - paths[0] = NULL; - init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) @@ -1064,7 +1061,6 @@ static int find_copy_in_parent(struct scoreboard *sb, int opt) { struct diff_options diff_opts; - const char *paths[1]; int i, j; int retval; struct blame_list *blame_list; @@ -1078,8 +1074,6 @@ static int find_copy_in_parent(struct scoreboard *sb, DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - paths[0] = NULL; - init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; diff --git a/builtin/log.c b/builtin/log.c index e3222ed..873af69 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -503,7 +503,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) init_grep_defaults(); git_config(git_log_config, NULL); - init_pathspec(&match_all, NULL); + memset(&match_all, 0, sizeof(match_all)); init_revisions(&rev, prefix); rev.diff = 1; rev.always_show_header = 1; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index c074e6f..d3a0495 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -315,13 +315,12 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) } if (prefix) { - static const char *(matchbuf[2]); - matchbuf[0] = prefix; - matchbuf[1] = NULL; - init_pathspec(&pathspec, matchbuf); - pathspec.items[0].nowildcard_len = pathspec.items[0].len; + static const char *(matchbuf[1]); + matchbuf[0] = NULL; + parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, + PATHSPEC_PREFER_CWD, prefix, matchbuf); } else - init_pathspec(&pathspec, NULL); + memset(&pathspec, 0, sizeof(pathspec)); if (read_tree(tree, 1, &pathspec)) die("unable to read tree entries %s", tree_name); diff --git a/diff-lib.c b/diff-lib.c index d4e17f7..0a95763 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) struct rev_info revs; init_revisions(&revs, NULL); - init_pathspec(&revs.prune_data, opt->pathspec.raw); + copy_pathspec(&revs.prune_data, &opt->pathspec); revs.diffopt = *opt; if (diff_cache(&revs, tree_sha1, NULL, 1)) diff --git a/dir.c b/dir.c index 019ad09..959e694 100644 --- a/dir.c +++ b/dir.c @@ -1580,64 +1580,6 @@ int remove_path(const char *name) return 0; } -static int pathspec_item_cmp(const void *a_, const void *b_) -{ - struct pathspec_item *a, *b; - - a = (struct pathspec_item *)a_; - b = (struct pathspec_item *)b_; - return strcmp(a->match, b->match); -} - -int init_pathspec(struct pathspec *pathspec, const char **paths) -{ - const char **p = paths; - int i; - - memset(pathspec, 0, sizeof(*pathspec)); - if (!p) - return 0; - while (*p) - p++; - pathspec->raw = paths; - pathspec->nr = p - paths; - if (!pathspec->nr) - return 0; - - pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr); - for (i = 0; i < pathspec->nr; i++) { - struct pathspec_item *item = pathspec->items+i; - const char *path = paths[i]; - - item->match = path; - item->original = path; - item->len = strlen(path); - item->flags = 0; - if (limit_pathspec_to_literal()) { - item->nowildcard_len = item->len; - } else { - item->nowildcard_len = simple_length(path); - if (item->nowildcard_len < item->len) { - pathspec->has_wildcard = 1; - if (path[item->nowildcard_len] == '*' && - no_wildcard(path + item->nowildcard_len + 1)) - item->flags |= PATHSPEC_ONESTAR; - } - } - } - - qsort(pathspec->items, pathspec->nr, - sizeof(struct pathspec_item), pathspec_item_cmp); - - return 0; -} - -void free_pathspec(struct pathspec *pathspec) -{ - free(pathspec->items); - pathspec->items = NULL; -} - int limit_pathspec_to_literal(void) { static int flag = -1; diff --git a/merge-recursive.c b/merge-recursive.c index ea9dbd3..8395b9b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -297,7 +297,7 @@ static int get_files_dirs(struct merge_options *o, struct tree *tree) { int n; struct pathspec match_all; - init_pathspec(&match_all, NULL); + memset(&match_all, 0, sizeof(match_all)); if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o)) return 0; n = o->current_file_set.nr + o->current_directory_set.nr; diff --git a/pathspec.c b/pathspec.c index 2774bda..74f0203 100644 --- a/pathspec.c +++ b/pathspec.c @@ -372,3 +372,9 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src) memcpy(dst->items, src->items, sizeof(struct pathspec_item) * dst->nr); } + +void free_pathspec(struct pathspec *pathspec) +{ + free(pathspec->items); + pathspec->items = NULL; +} diff --git a/pathspec.h b/pathspec.h index 02dded3..cadd46c 100644 --- a/pathspec.h +++ b/pathspec.h @@ -53,7 +53,6 @@ struct pathspec { #define PATHSPEC_PREFIX_ORIGIN (1<<6) #define PATHSPEC_KEEP_ORDER (1<<7) -extern int init_pathspec(struct pathspec *, const char **); extern void parse_pathspec(struct pathspec *pathspec, unsigned magic_mask, unsigned flags, diff --git a/revision.c b/revision.c index c2d44b7..001623a 100644 --- a/revision.c +++ b/revision.c @@ -1372,7 +1372,7 @@ static void prepare_show_merge(struct rev_info *revs) i++; } free_pathspec(&revs->prune_data); - init_pathspec(&revs->prune_data, prune); + parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, 0, "", prune); revs->limited = 1; } diff --git a/tree-diff.c b/tree-diff.c index f4c92f6..e1145c6 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -195,7 +195,6 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co struct diff_options diff_opts; struct diff_queue_struct *q = &diff_queued_diff; struct diff_filepair *choice; - const char *paths[1]; int i; /* @@ -228,8 +227,6 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co diff_opts.single_follow = opt->pathspec.raw[0]; diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; - paths[0] = NULL; - init_pathspec(&diff_opts.pathspec, paths); diff_setup_done(&diff_opts); diff_tree(t1, t2, base, &diff_opts); diffcore_std(&diff_opts); @@ -247,14 +244,17 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co */ if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->pathspec.raw[0])) { + const char *path[2]; + /* Switch the file-pairs around */ q->queue[i] = choice; choice = p; /* Update the path we use from now on.. */ + path[0] = p->one->path; + path[1] = NULL; free_pathspec(&opt->pathspec); - opt->pathspec.raw[0] = xstrdup(p->one->path); - init_pathspec(&opt->pathspec, opt->pathspec.raw); + parse_pathspec(&opt->pathspec, PATHSPEC_ALL_MAGIC, 0, "", path); /* * The caller expects us to return a set of vanilla -- cgit v0.10.2-6-g49f6 From 84b8b5d1fa244bc591291a3cf18bf0fa9976ad17 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, 14 Jul 2013 15:36:00 +0700 Subject: remove match_pathspec() in favor of match_pathspec_depth() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit match_pathspec_depth was created to replace match_pathspec (see 61cf282 (pathspec: add match_pathspec_depth() - 2010-12-15). It took more than two years, but the replacement finally happens :-) Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/add.c b/builtin/add.c index a47aeb4..0b80fa8 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -195,23 +195,21 @@ int add_files_to_cache(const char *prefix, } #define WARN_IMPLICIT_DOT (1u << 0) -static char *prune_directory(struct dir_struct *dir, const char **pathspec, +static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix, unsigned flag) { char *seen; - int i, specs; + int i; struct dir_entry **src, **dst; - for (specs = 0; pathspec[specs]; specs++) - /* nothing */; - seen = xcalloc(specs, 1); + seen = xcalloc(pathspec->nr, 1); src = dst = dir->entries; i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (match_pathspec(pathspec, entry->name, entry->len, - prefix, seen)) + if (match_pathspec_depth(pathspec, entry->name, entry->len, + prefix, seen)) *dst++ = entry; else if (flag & WARN_IMPLICIT_DOT) /* @@ -225,7 +223,7 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, warn_pathless_add(); } dir->nr = dst - dir->entries; - add_pathspec_matches_against_index(pathspec, seen, specs); + add_pathspec_matches_against_index(pathspec, seen); return seen; } @@ -523,7 +521,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); if (pathspec.nr) - seen = prune_directory(&dir, pathspec.raw, baselen, + seen = prune_directory(&dir, &pathspec, baselen, implicit_dot ? WARN_IMPLICIT_DOT : 0); } @@ -538,23 +536,23 @@ int cmd_add(int argc, const char **argv, const char *prefix) int i; if (!seen) - seen = find_pathspecs_matching_against_index(pathspec.raw); + seen = find_pathspecs_matching_against_index(&pathspec); /* * file_exists() assumes exact match */ GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP); - for (i = 0; pathspec.raw[i]; i++) { - if (!seen[i] && pathspec.raw[i][0] - && !file_exists(pathspec.raw[i])) { + for (i = 0; i < pathspec.nr; i++) { + const char *path = pathspec.items[i].match; + if (!seen[i] && !file_exists(path)) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, pathspec.raw[i], &dtype)) - dir_add_ignored(&dir, pathspec.raw[i], strlen(pathspec.raw[i])); + if (is_excluded(&dir, path, &dtype)) + dir_add_ignored(&dir, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), - pathspec.raw[i]); + pathspec.items[i].original); } } free(seen); diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index d49c083..70537c8 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -94,9 +94,9 @@ static int check_ignore(struct dir_struct *dir, * should not be ignored, in order to be consistent with * 'git status', 'git add' etc. */ - seen = find_pathspecs_matching_against_index(pathspec.raw); + seen = find_pathspecs_matching_against_index(&pathspec); for (i = 0; i < pathspec.nr; i++) { - full_path = pathspec.raw[i]; + full_path = pathspec.items[i].match; exclude = NULL; if (!seen[i]) { exclude = last_exclude_matching(dir, full_path, &dtype); diff --git a/dir.c b/dir.c index 959e694..9423fbb 100644 --- a/dir.c +++ b/dir.c @@ -183,113 +183,6 @@ int within_depth(const char *name, int namelen, * * It returns 0 when there is no match. */ -static int match_one(const char *match, const char *name, int namelen) -{ - int matchlen; - int literal = limit_pathspec_to_literal(); - - /* If the match was just the prefix, we matched */ - if (!*match) - return MATCHED_RECURSIVELY; - - if (ignore_case) { - for (;;) { - unsigned char c1 = tolower(*match); - unsigned char c2 = tolower(*name); - if (c1 == '\0' || (!literal && is_glob_special(c1))) - break; - if (c1 != c2) - return 0; - match++; - name++; - namelen--; - } - } else { - for (;;) { - unsigned char c1 = *match; - unsigned char c2 = *name; - if (c1 == '\0' || (!literal && is_glob_special(c1))) - break; - if (c1 != c2) - return 0; - match++; - name++; - namelen--; - } - } - - /* - * If we don't match the matchstring exactly, - * we need to match by fnmatch - */ - matchlen = strlen(match); - if (strncmp_icase(match, name, matchlen)) { - if (literal) - return 0; - return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0; - } - - if (namelen == matchlen) - return MATCHED_EXACTLY; - if (match[matchlen-1] == '/' || name[matchlen] == '/') - return MATCHED_RECURSIVELY; - return 0; -} - -/* - * Given a name and a list of pathspecs, returns the nature of the - * closest (i.e. most specific) match of the name to any of the - * pathspecs. - * - * The caller typically calls this multiple times with the same - * pathspec and seen[] array but with different name/namelen - * (e.g. entries from the index) and is interested in seeing if and - * how each pathspec matches all the names it calls this function - * with. A mark is left in the seen[] array for each pathspec element - * indicating the closest type of match that element achieved, so if - * seen[n] remains zero after multiple invocations, that means the nth - * pathspec did not match any names, which could indicate that the - * user mistyped the nth pathspec. - */ -int match_pathspec(const char **pathspec, const char *name, int namelen, - int prefix, char *seen) -{ - int i, retval = 0; - - if (!pathspec) - return 1; - - name += prefix; - namelen -= prefix; - - for (i = 0; pathspec[i] != NULL; i++) { - int how; - const char *match = pathspec[i] + prefix; - if (seen && seen[i] == MATCHED_EXACTLY) - continue; - how = match_one(match, name, namelen); - if (how) { - if (retval < how) - retval = how; - if (seen && seen[i] < how) - seen[i] = how; - } - } - return retval; -} - -/* - * Does 'match' match the given name? - * A match is found if - * - * (1) the 'match' string is leading directory of 'name', or - * (2) the 'match' string is a wildcard and matches 'name', or - * (3) the 'match' string is exactly the same as 'name'. - * - * and the return value tells which case it was. - * - * It returns 0 when there is no match. - */ static int match_pathspec_item(const struct pathspec_item *item, int prefix, const char *name, int namelen) { diff --git a/dir.h b/dir.h index ba40e69..7d051e3 100644 --- a/dir.h +++ b/dir.h @@ -131,7 +131,6 @@ struct dir_struct { extern int simple_length(const char *match); extern int no_wildcard(const char *string); extern char *common_prefix(const struct pathspec *pathspec); -extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); extern int match_pathspec_depth(const struct pathspec *pathspec, const char *name, int namelen, int prefix, char *seen); diff --git a/pathspec.c b/pathspec.c index 74f0203..3d1386d 100644 --- a/pathspec.c +++ b/pathspec.c @@ -15,8 +15,8 @@ * If seen[] has not already been written to, it may make sense * to use find_pathspecs_matching_against_index() instead. */ -void add_pathspec_matches_against_index(const char **pathspec, - char *seen, int specs) +void add_pathspec_matches_against_index(const struct pathspec *pathspec, + char *seen) { int num_unmatched = 0, i; @@ -26,14 +26,14 @@ void add_pathspec_matches_against_index(const char **pathspec, * mistakenly think that the user gave a pathspec that did not match * anything. */ - for (i = 0; i < specs; i++) + for (i = 0; i < pathspec->nr; i++) if (!seen[i]) num_unmatched++; if (!num_unmatched) return; for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen); + match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen); } } @@ -45,15 +45,10 @@ void add_pathspec_matches_against_index(const char **pathspec, * nature of the "closest" (i.e. most specific) matches which each of the * given pathspecs achieves against all items in the index. */ -char *find_pathspecs_matching_against_index(const char **pathspec) +char *find_pathspecs_matching_against_index(const struct pathspec *pathspec) { - char *seen; - int i; - - for (i = 0; pathspec[i]; i++) - ; /* just counting */ - seen = xcalloc(i, 1); - add_pathspec_matches_against_index(pathspec, seen, i); + char *seen = xcalloc(pathspec->nr, 1); + add_pathspec_matches_against_index(pathspec, seen); return seen; } diff --git a/pathspec.h b/pathspec.h index cadd46c..31a9c96 100644 --- a/pathspec.h +++ b/pathspec.h @@ -63,8 +63,8 @@ extern void free_pathspec(struct pathspec *); extern int limit_pathspec_to_literal(void); -extern char *find_pathspecs_matching_against_index(const char **pathspec); -extern void add_pathspec_matches_against_index(const char **pathspec, char *seen, int specs); +extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec); +extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen); extern const char *check_path_for_gitlink(const char *path); extern void die_if_path_beyond_symlink(const char *path, const char *prefix); -- cgit v0.10.2-6-g49f6 From 61588ccf78373f5211ecdc56d53f0e17d04283fe 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, 14 Jul 2013 15:36:01 +0700 Subject: tree-diff: remove the use of pathspec's raw[] in follow-rename codepath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put a checkpoint to guard unsupported pathspec features in future. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/tree-diff.c b/tree-diff.c index e1145c6..21a50d8 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -224,7 +224,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co DIFF_OPT_SET(&diff_opts, RECURSIVE); DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_opts.single_follow = opt->pathspec.raw[0]; + diff_opts.single_follow = opt->pathspec.items[0].match; diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; diff_setup_done(&diff_opts); @@ -243,7 +243,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co * the future! */ if ((p->status == 'R' || p->status == 'C') && - !strcmp(p->two->path, opt->pathspec.raw[0])) { + !strcmp(p->two->path, opt->pathspec.items[0].match)) { const char *path[2]; /* Switch the file-pairs around */ -- cgit v0.10.2-6-g49f6 From b3920bbdc51fc360bde70e7c19088acfe44b9c4b 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, 14 Jul 2013 15:36:02 +0700 Subject: rename field "raw" to "_raw" in struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is essentially no-op. It helps catching new use of this field though. This field is introduced as an intermediate step for the pathspec conversion and will be removed eventually. At this stage no more access sites should be introduced. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index bdb03f3..1c4f48e 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -36,7 +36,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname) if (ls_options & LS_RECURSIVE) return 1; - s = pathspec.raw; + s = pathspec._raw; if (!s) return 0; diff --git a/dir.c b/dir.c index 9423fbb..bf22498 100644 --- a/dir.c +++ b/dir.c @@ -152,7 +152,7 @@ int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) len = common_prefix_len(pathspec); /* Read the directory and prune it */ - read_directory(dir, pathspec->nr ? pathspec->raw[0] : "", len, pathspec); + read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec); return len; } @@ -1293,7 +1293,7 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru if (has_symlink_leading_path(path, len)) return dir->nr; - simplify = create_simplify(pathspec ? pathspec->raw : NULL); + simplify = create_simplify(pathspec ? pathspec->_raw : NULL); if (!len || treat_leading_path(dir, path, len, simplify)) read_directory_recursive(dir, path, len, 0, simplify); free_simplify(simplify); diff --git a/pathspec.c b/pathspec.c index 3d1386d..da802e2 100644 --- a/pathspec.c +++ b/pathspec.c @@ -287,7 +287,7 @@ void parse_pathspec(struct pathspec *pathspec, raw[0] = prefix; raw[1] = NULL; pathspec->nr = 1; - pathspec->raw = raw; + pathspec->_raw = raw; return; } @@ -297,7 +297,7 @@ void parse_pathspec(struct pathspec *pathspec, pathspec->nr = n; pathspec->items = item = xmalloc(sizeof(*item) * n); - pathspec->raw = argv; + pathspec->_raw = argv; prefixlen = prefix ? strlen(prefix) : 0; for (i = 0; i < n; i++) { @@ -357,7 +357,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec) PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, PATHSPEC_PREFER_CWD, prefix, pathspec); - return ps.raw; + return ps._raw; } void copy_pathspec(struct pathspec *dst, const struct pathspec *src) diff --git a/pathspec.h b/pathspec.h index 31a9c96..0f6739d 100644 --- a/pathspec.h +++ b/pathspec.h @@ -11,7 +11,7 @@ #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ struct pathspec { - const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ + const char **_raw; /* get_pathspec() result, not freed by free_pathspec() */ int nr; unsigned int has_wildcard:1; unsigned int recursive:1; -- cgit v0.10.2-6-g49f6 From 645a29c40a12a4ade1b041dce246ae241c502a64 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, 14 Jul 2013 15:36:03 +0700 Subject: parse_pathspec: make sure the prefix part is wildcard-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepending prefix to pathspec is a trick to workaround the fact that commands can be executed in a subdirectory, but all git commands run at worktree's root. The prefix part should always be treated as literal string. Make it so. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index b0ed117..13e3c94 100644 --- a/cache.h +++ b/cache.h @@ -414,6 +414,7 @@ extern void setup_work_tree(void); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern char *prefix_path(const char *prefix, int len, const char *path); +extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path); extern const char *prefix_filename(const char *prefix, int len, const char *path); extern int check_filename(const char *prefix, const char *name); extern void verify_filename(const char *prefix, @@ -741,6 +742,7 @@ const char *real_path(const char *path); const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); const char *relative_path(const char *abs, const char *base); +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); diff --git a/path.c b/path.c index 04ff148..f4b49d6 100644 --- a/path.c +++ b/path.c @@ -492,8 +492,14 @@ const char *relative_path(const char *abs, const char *base) * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. + * + * prefix_len != NULL is for a specific case of prefix_pathspec(): + * assume that src == dst and src[0..prefix_len-1] is already + * normalized, any time "../" eats up to the prefix_len part, + * prefix_len is reduced. In the end prefix_len is the remaining + * prefix that has not been overridden by user pathspec. */ -int normalize_path_copy(char *dst, const char *src) +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; @@ -568,11 +574,18 @@ int normalize_path_copy(char *dst, const char *src) /* Windows: dst[-1] cannot be backslash anymore */ while (dst0 < dst && dst[-1] != '/') dst--; + if (prefix_len && *prefix_len > dst - dst0) + *prefix_len = dst - dst0; } *dst = '\0'; return 0; } +int normalize_path_copy(char *dst, const char *src) +{ + return normalize_path_copy_len(dst, src, NULL); +} + /* * path = Canonical absolute path * prefixes = string_list containing normalized, absolute paths without diff --git a/pathspec.c b/pathspec.c index da802e2..71e5eaf 100644 --- a/pathspec.c +++ b/pathspec.c @@ -150,10 +150,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= short_magic; *p_short_magic = short_magic; - if (magic & PATHSPEC_FROMTOP) + if (magic & PATHSPEC_FROMTOP) { match = xstrdup(copyfrom); - else - match = prefix_path(prefix, prefixlen, copyfrom); + prefixlen = 0; + } else { + match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom); + if (!match) + die(_("%s: '%s' is outside repository"), elt, copyfrom); + } *raw = item->match = match; /* * Prefix the pathspec (keep all magic) and assign to @@ -167,6 +171,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, } else item->original = elt; item->len = strlen(item->match); + item->prefix = prefixlen; if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) && (item->len >= 1 && item->match[item->len - 1] == '/') && @@ -198,13 +203,20 @@ static unsigned prefix_pathspec(struct pathspec_item *item, if (limit_pathspec_to_literal()) item->nowildcard_len = item->len; - else + else { item->nowildcard_len = simple_length(item->match); + if (item->nowildcard_len < prefixlen) + item->nowildcard_len = prefixlen; + } item->flags = 0; if (item->nowildcard_len < item->len && item->match[item->nowildcard_len] == '*' && no_wildcard(item->match + item->nowildcard_len + 1)) item->flags |= PATHSPEC_ONESTAR; + + /* sanity checks, pathspec matchers assume these are sane */ + assert(item->nowildcard_len <= item->len && + item->prefix <= item->len); return magic; } @@ -284,6 +296,7 @@ void parse_pathspec(struct pathspec *pathspec, item->match = prefix; item->original = prefix; item->nowildcard_len = item->len = strlen(prefix); + item->prefix = item->len; raw[0] = prefix; raw[1] = NULL; pathspec->nr = 1; diff --git a/pathspec.h b/pathspec.h index 0f6739d..2f3532e 100644 --- a/pathspec.h +++ b/pathspec.h @@ -21,7 +21,7 @@ struct pathspec { const char *match; const char *original; unsigned magic; - int len; + int len, prefix; int nowildcard_len; int flags; } *items; diff --git a/setup.c b/setup.c index d1ece5d..ff4499e 100644 --- a/setup.c +++ b/setup.c @@ -5,7 +5,19 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; -static char *prefix_path_gently(const char *prefix, int len, const char *path) +/* + * Normalize "path", prepending the "prefix" for relative paths. If + * remaining_prefix is not NULL, return the actual prefix still + * remains in the path. For example, prefix = sub1/sub2/ and path is + * + * foo -> sub1/sub2/foo (full prefix) + * ../foo -> sub1/foo (remaining prefix is sub1/) + * ../../bar -> bar (no remaining prefix) + * ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix) + * `pwd`/../bar -> sub1/bar (no remaining prefix) + */ +char *prefix_path_gently(const char *prefix, int len, + int *remaining_prefix, const char *path) { const char *orig = path; char *sanitized; @@ -13,13 +25,17 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path) const char *temp = real_path(path); sanitized = xmalloc(len + strlen(temp) + 1); strcpy(sanitized, temp); + if (remaining_prefix) + *remaining_prefix = 0; } else { sanitized = xmalloc(len + strlen(path) + 1); if (len) memcpy(sanitized, prefix, len); strcpy(sanitized + len, path); + if (remaining_prefix) + *remaining_prefix = len; } - if (normalize_path_copy(sanitized, sanitized)) + if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) goto error_out; if (is_absolute_path(orig)) { size_t root_len, len, total; @@ -44,7 +60,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path) char *prefix_path(const char *prefix, int len, const char *path) { - char *r = prefix_path_gently(prefix, len, path); + char *r = prefix_path_gently(prefix, len, NULL, path); if (!r) die("'%s' is outside repository", path); return r; @@ -53,7 +69,7 @@ char *prefix_path(const char *prefix, int len, const char *path) int path_inside_repo(const char *prefix, const char *path) { int len = prefix ? strlen(prefix) : 0; - char *r = prefix_path_gently(prefix, len, path); + char *r = prefix_path_gently(prefix, len, NULL, path); if (r) { free(r); return 1; -- cgit v0.10.2-6-g49f6 From 233c3e6c59945650e5919893a4f090824f06e889 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, 14 Jul 2013 15:36:04 +0700 Subject: parse_pathspec: preserve prefix length via PATHSPEC_PREFIX_ORIGIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prefix length is passed from one command to another via the new magic 'prefix'. The magic is for parse_pathspec's internal use only, not visible to parse_pathspec's callers. Prefix length is not preserved across commands when --literal-pathspecs is specified (no magic is allowed, including 'prefix'). That's OK because we know all paths are literal. No magic, no special treatment regarding prefix. (This may be no longer true if we make :(glob) default) Other options to preserve the prefix include saving it to env variable or quoting. Env var way (at least _one_ env var) is not suitable because the prefix is not the same for all pathspecs. Pathspecs starting with "../" will eat into the prefix part. We could also preserve 'prefix' across commands by quoting the prefix part, then dequoting on receiving. But it may not be 100% accurate, we may dequote longer than the original prefix part, for example. That may be good or not, but it's not the purpose. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/pathspec.c b/pathspec.c index 71e5eaf..82ede57 100644 --- a/pathspec.c +++ b/pathspec.c @@ -92,9 +92,9 @@ static unsigned prefix_pathspec(struct pathspec_item *item, const char *elt) { unsigned magic = 0, short_magic = 0; - const char *copyfrom = elt; + const char *copyfrom = elt, *long_magic_end = NULL; char *match; - int i; + int i, pathspec_prefix = -1; if (elt[0] != ':') { ; /* nothing to do */ @@ -112,18 +112,29 @@ static unsigned prefix_pathspec(struct pathspec_item *item, nextat = copyfrom + len; if (!len) continue; - for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) + 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 (!prefixcmp(copyfrom, "prefix:")) { + char *endptr; + pathspec_prefix = strtol(copyfrom + 7, + &endptr, 10); + if (endptr - copyfrom != len) + die(_("invalid parameter for pathspec magic 'prefix'")); + /* "i" would be wrong, but it does not matter */ + break; + } + } if (ARRAY_SIZE(pathspec_magic) <= i) die(_("Invalid pathspec magic '%.*s' in '%s'"), (int) len, copyfrom, elt); } if (*copyfrom != ')') die(_("Missing ')' at the end of pathspec magic in '%s'"), elt); + long_magic_end = copyfrom; copyfrom++; } else { /* shorthand */ @@ -150,7 +161,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= short_magic; *p_short_magic = short_magic; - if (magic & PATHSPEC_FROMTOP) { + if (pathspec_prefix >= 0 && + (prefixlen || (prefix && *prefix))) + die("BUG: 'prefix' magic is supposed to be used at worktree's root"); + + if (pathspec_prefix >= 0) { + match = xstrdup(copyfrom); + prefixlen = pathspec_prefix; + } else if (magic & PATHSPEC_FROMTOP) { match = xstrdup(copyfrom); prefixlen = 0; } else { @@ -165,7 +183,20 @@ static unsigned prefix_pathspec(struct pathspec_item *item, */ if (flags & PATHSPEC_PREFIX_ORIGIN) { struct strbuf sb = STRBUF_INIT; - strbuf_add(&sb, elt, copyfrom - elt); + const char *start = elt; + if (prefixlen && !limit_pathspec_to_literal()) { + /* Preserve the actual prefix length of each pattern */ + if (long_magic_end) { + strbuf_add(&sb, start, long_magic_end - start); + strbuf_addf(&sb, ",prefix:%d", prefixlen); + start = long_magic_end; + } else { + if (*start == ':') + start++; + strbuf_addf(&sb, ":(prefix:%d)", prefixlen); + } + } + strbuf_add(&sb, start, copyfrom - start); strbuf_addstr(&sb, match); item->original = strbuf_detach(&sb, NULL); } else -- cgit v0.10.2-6-g49f6 From 341003e715c9a7c0332d3f8f08c2f3696a057565 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, 14 Jul 2013 15:36:05 +0700 Subject: kill limit_pathspec_to_literal() as it's only used by parse_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/dir.c b/dir.c index bf22498..79465e7 100644 --- a/dir.c +++ b/dir.c @@ -1473,14 +1473,6 @@ int remove_path(const char *name) return 0; } -int limit_pathspec_to_literal(void) -{ - static int flag = -1; - if (flag < 0) - flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0); - return flag; -} - /* * Frees memory within dir which was allocated for exclude lists and * the exclude_stack. Does not free dir itself. diff --git a/pathspec.c b/pathspec.c index 82ede57..b2e3a87 100644 --- a/pathspec.c +++ b/pathspec.c @@ -91,11 +91,15 @@ static unsigned prefix_pathspec(struct pathspec_item *item, const char *prefix, int prefixlen, const char *elt) { + static int literal_global = -1; unsigned magic = 0, short_magic = 0; const char *copyfrom = elt, *long_magic_end = NULL; char *match; int i, pathspec_prefix = -1; + if (literal_global < 0) + literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0); + if (elt[0] != ':') { ; /* nothing to do */ } else if (elt[1] == '(') { @@ -184,7 +188,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, if (flags & PATHSPEC_PREFIX_ORIGIN) { struct strbuf sb = STRBUF_INIT; const char *start = elt; - if (prefixlen && !limit_pathspec_to_literal()) { + if (prefixlen && !literal_global) { /* Preserve the actual prefix length of each pattern */ if (long_magic_end) { strbuf_add(&sb, start, long_magic_end - start); @@ -232,7 +236,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, elt, ce_len, ce->name); } - if (limit_pathspec_to_literal()) + if (literal_global) item->nowildcard_len = item->len; else { item->nowildcard_len = simple_length(item->match); diff --git a/pathspec.h b/pathspec.h index 2f3532e..7ef9896 100644 --- a/pathspec.h +++ b/pathspec.h @@ -61,8 +61,6 @@ extern void parse_pathspec(struct pathspec *pathspec, extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src); extern void free_pathspec(struct pathspec *); -extern int limit_pathspec_to_literal(void); - extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec); extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen); extern const char *check_path_for_gitlink(const char *path); -- cgit v0.10.2-6-g49f6 From 5c6933d201fab183a9779dca0fe43bf2f1eca098 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, 14 Jul 2013 15:36:06 +0700 Subject: pathspec: support :(literal) syntax for noglob 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 dba5062..ca9f20f 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -322,10 +322,22 @@ 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. Currently only the slash `/` is recognized as a -"magic signature": it makes the pattern match from the root of -the working tree, even when you are running the command from -inside a subdirectory. +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. + +literal;; + Wildcards in the pattern such as `*` or `?` are treated + as literal characters. +-- ++ +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. diff --git a/builtin/add.c b/builtin/add.c index 0b80fa8..663ddd1 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -541,7 +541,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) /* * file_exists() assumes exact match */ - GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP); + GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; diff --git a/builtin/diff.c b/builtin/diff.c index bb84ba0..2fb8c5d 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -368,7 +368,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } if (rev.prune_data.nr) { /* builtin_diff_b_f() */ - GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP); + GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); if (!path) path = rev.prune_data.items[0].match; paths += rev.prune_data.nr; diff --git a/dir.c b/dir.c index 79465e7..50ec2f5 100644 --- a/dir.c +++ b/dir.c @@ -108,7 +108,10 @@ static size_t common_prefix_len(const struct pathspec *pathspec) int n; size_t max = 0; - GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + GUARD_PATHSPEC(pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL); for (n = 0; n < pathspec->nr; n++) { size_t i = 0, len = 0; @@ -232,7 +235,10 @@ int match_pathspec_depth(const struct pathspec *ps, { int i, retval = 0; - GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + GUARD_PATHSPEC(ps, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL); if (!ps->nr) { if (!ps->recursive || @@ -1288,7 +1294,10 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru * Check out create_simplify() */ if (pathspec) - GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + GUARD_PATHSPEC(pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL); if (has_symlink_leading_path(path, len)) return dir->nr; diff --git a/pathspec.c b/pathspec.c index b2e3a87..6a16938 100644 --- a/pathspec.c +++ b/pathspec.c @@ -70,6 +70,7 @@ static struct pathspec_magic { const char *name; } pathspec_magic[] = { { PATHSPEC_FROMTOP, '/', "top" }, + { PATHSPEC_LITERAL, 0, "literal" }, }; /* @@ -92,13 +93,15 @@ static unsigned prefix_pathspec(struct pathspec_item *item, const char *elt) { static int literal_global = -1; - unsigned magic = 0, short_magic = 0; + unsigned magic = 0, short_magic = 0, global_magic = 0; const char *copyfrom = elt, *long_magic_end = NULL; char *match; int i, pathspec_prefix = -1; if (literal_global < 0) literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0); + if (literal_global) + global_magic |= PATHSPEC_LITERAL; if (elt[0] != ':') { ; /* nothing to do */ @@ -164,6 +167,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= short_magic; *p_short_magic = short_magic; + magic |= global_magic; if (pathspec_prefix >= 0 && (prefixlen || (prefix && *prefix))) @@ -236,7 +240,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, elt, ce_len, ce->name); } - if (literal_global) + if (magic & PATHSPEC_LITERAL) item->nowildcard_len = item->len; else { item->nowildcard_len = simple_length(item->match); @@ -402,7 +406,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec) { struct pathspec ps; parse_pathspec(&ps, - PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, + PATHSPEC_ALL_MAGIC & + ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL), PATHSPEC_PREFER_CWD, prefix, pathspec); return ps._raw; diff --git a/pathspec.h b/pathspec.h index 7ef9896..987d70c 100644 --- a/pathspec.h +++ b/pathspec.h @@ -4,9 +4,11 @@ /* Pathspec magic */ #define PATHSPEC_FROMTOP (1<<0) #define PATHSPEC_MAXDEPTH (1<<1) +#define PATHSPEC_LITERAL (1<<2) #define PATHSPEC_ALL_MAGIC \ (PATHSPEC_FROMTOP | \ - PATHSPEC_MAXDEPTH) + PATHSPEC_MAXDEPTH | \ + PATHSPEC_LITERAL) #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh index 39ef619..49c148e 100755 --- a/t/t6130-pathspec-noglob.sh +++ b/t/t6130-pathspec-noglob.sh @@ -47,18 +47,36 @@ test_expect_success 'no-glob option matches literally (vanilla)' ' test_cmp expect actual ' +test_expect_success 'no-glob option matches literally (vanilla)' ' + echo vanilla >expect && + git log --format=%s -- ":(literal)foo" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob option matches literally (star)' ' echo star >expect && git --literal-pathspecs log --format=%s -- "f*" >actual && test_cmp expect actual ' +test_expect_success 'no-glob option matches literally (star)' ' + echo star >expect && + git log --format=%s -- ":(literal)f*" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob option matches literally (bracket)' ' echo bracket >expect && git --literal-pathspecs log --format=%s -- "f[o][o]" >actual && test_cmp expect actual ' +test_expect_success 'no-glob option matches literally (bracket)' ' + echo bracket >expect && + git log --format=%s -- ":(literal)f[o][o]" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob environment variable works' ' echo star >expect && GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual && diff --git a/tree-diff.c b/tree-diff.c index 21a50d8..ccf9d7c 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -202,7 +202,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co * path. Magic that matches more than one path is not * supported. */ - GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP); + GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); #if 0 /* * We should reject wildcards as well. Unfortunately we diff --git a/tree-walk.c b/tree-walk.c index 37b157e..676bd7f 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -636,7 +636,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, enum interesting never_interesting = ps->has_wildcard ? entry_not_interesting : all_entries_not_interesting; - GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH); + GUARD_PATHSPEC(ps, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL); if (!ps->nr) { if (!ps->recursive || -- cgit v0.10.2-6-g49f6 From a16bf9dd745a9e43e46d745d850db49358430e46 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, 14 Jul 2013 15:36:07 +0700 Subject: pathspec: make --literal-pathspecs disable pathspec magic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --literal-pathspecs and its equivalent environment variable are probably used for scripting. In that setting, pathspec magic may be unwanted. Disabling globbing in individual pathspec can be done via :(literal) magic. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/git.txt b/Documentation/git.txt index b738a40..80139ae 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -450,8 +450,8 @@ help ...`. linkgit:git-replace[1] for more information. --literal-pathspecs:: - Treat pathspecs literally, rather than as glob patterns. This is - equivalent to setting the `GIT_LITERAL_PATHSPECS` environment + Treat pathspecs literally (i.e. no globbing, no pathspec magic). + This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment variable to `1`. diff --git a/pathspec.c b/pathspec.c index 6a16938..b6d8e74 100644 --- a/pathspec.c +++ b/pathspec.c @@ -103,7 +103,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, if (literal_global) global_magic |= PATHSPEC_LITERAL; - if (elt[0] != ':') { + if (elt[0] != ':' || literal_global) { ; /* nothing to do */ } else if (elt[1] == '(') { /* longhand */ diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh index 49c148e..8551b02 100755 --- a/t/t6130-pathspec-noglob.sh +++ b/t/t6130-pathspec-noglob.sh @@ -77,6 +77,12 @@ test_expect_success 'no-glob option matches literally (bracket)' ' test_cmp expect actual ' +test_expect_success 'no-glob option disables :(literal)' ' + : >expect && + git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob environment variable works' ' echo star >expect && GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual && -- cgit v0.10.2-6-g49f6 From bd30c2e48432c692f9e77d3529c9cf25117066bb 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, 14 Jul 2013 15:36:08 +0700 Subject: pathspec: support :(glob) syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :(glob)path differs from plain pathspec that it uses wildmatch with WM_PATHNAME while the other uses fnmatch without FNM_PATHNAME. The difference lies in how '*' (and '**') is processed. With the introduction of :(glob) and :(literal) and their global options --[no]glob-pathspecs, the user can: - make everything literal by default via --noglob-pathspecs --literal-pathspecs cannot be used for this purpose as it disables _all_ pathspec magic. - individually turn on globbing with :(glob) - make everything globbing by default via --glob-pathspecs - individually turn off globbing with :(literal) The implication behind this is, there is no way to gain the default matching behavior (i.e. fnmatch without FNM_PATHNAME). You either get new globbing or literal. The old fnmatch behavior is considered deprecated and discouraged to use. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/git.txt b/Documentation/git.txt index 80139ae..3571a1b 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -454,6 +454,17 @@ help ...`. This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment variable to `1`. +--glob-pathspecs: + Add "glob" magic to all pathspec. This is equivalent to setting + the `GIT_GLOB_PATHSPECS` environment variable to `1`. Disabling + globbing on individual pathspecs can be done using pathspec + magic ":(literal)" + +--noglob-pathspecs: + Add "literal" magic to all pathspec. This is equivalent to setting + the `GIT_NOGLOB_PATHSPECS` environment variable to `1`. Enabling + globbing on individual pathspecs can be done using pathspec + magic ":(glob)" GIT COMMANDS ------------ @@ -860,6 +871,14 @@ GIT_LITERAL_PATHSPECS:: literal paths to Git (e.g., paths previously given to you by `git ls-tree`, `--raw` diff output, etc). +GIT_GLOB_PATHSPECS:: + Setting this variable to `1` will cause Git to treat all + pathspecs as glob patterns (aka "glob" magic). + +GIT_NOGLOB_PATHSPECS:: + Setting this variable to `1` will cause Git to treat all + pathspecs as literal (aka "literal" magic). + Discussion[[Discussion]] ------------------------ diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index ca9f20f..a3d9029 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -333,6 +333,35 @@ top `/`;; literal;; Wildcards in the pattern such as `*` or `?` are treated as literal characters. + +glob;; + Git treats the pattern as a shell glob suitable for + consumption by fnmatch(3) with the FNM_PATHNAME flag: + wildcards in the pattern will not match a / in the pathname. + For example, "Documentation/{asterisk}.html" matches + "Documentation/git.html" but not "Documentation/ppc/ppc.html" + or "tools/perf/Documentation/perf.html". ++ +Two consecutive asterisks ("`**`") in patterns matched against +full pathname may have special meaning: + + - A leading "`**`" followed by a slash means match in all + directories. For example, "`**/foo`" matches file or directory + "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar" + matches file or directory "`bar`" anywhere that is directly + under directory "`foo`". + + - A trailing "/**" matches everything inside. For example, + "abc/**" matches all files inside directory "abc", relative + to the location of the `.gitignore` file, with infinite depth. + + - A slash followed by two consecutive asterisks then a slash + matches zero or more directories. For example, "`a/**/b`" + matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on. + + - Other consecutive asterisks are considered invalid. ++ +Glob magic is incompatible with literal magic. -- + Currently only the slash `/` is recognized as the "magic signature", diff --git a/builtin/add.c b/builtin/add.c index 663ddd1..1dab246 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -541,11 +541,16 @@ int cmd_add(int argc, const char **argv, const char *prefix) /* * file_exists() assumes exact match */ - GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); + GUARD_PATHSPEC(&pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_LITERAL | + PATHSPEC_GLOB); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; - if (!seen[i] && !file_exists(path)) { + if (!seen[i] && + ((pathspec.items[i].magic & PATHSPEC_GLOB) || + !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, path, &dtype)) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 1c4f48e..7882352 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -173,7 +173,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * cannot be lifted until it is converted to use * match_pathspec_depth() or tree_entry_interesting() */ - parse_pathspec(&pathspec, 0, + parse_pathspec(&pathspec, PATHSPEC_GLOB, PATHSPEC_PREFER_CWD, prefix, argv + 1); for (i = 0; i < pathspec.nr; i++) diff --git a/cache.h b/cache.h index 13e3c94..dc4d2ee 100644 --- a/cache.h +++ b/cache.h @@ -367,6 +367,8 @@ static inline enum object_type object_type(unsigned int mode) #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF" #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE" #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS" +#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS" +#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS" /* * This environment variable is expected to contain a boolean indicating diff --git a/dir.c b/dir.c index 50ec2f5..076bd46 100644 --- a/dir.c +++ b/dir.c @@ -52,26 +52,28 @@ int fnmatch_icase(const char *pattern, const char *string, int flags) return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0)); } -inline int git_fnmatch(const char *pattern, const char *string, - int flags, int prefix) +inline int git_fnmatch(const struct pathspec_item *item, + const char *pattern, const char *string, + int prefix) { - int fnm_flags = 0; - if (flags & GFNM_PATHNAME) - fnm_flags |= FNM_PATHNAME; if (prefix > 0) { if (strncmp(pattern, string, prefix)) return FNM_NOMATCH; pattern += prefix; string += prefix; } - if (flags & GFNM_ONESTAR) { + if (item->flags & PATHSPEC_ONESTAR) { int pattern_len = strlen(++pattern); int string_len = strlen(string); return string_len < pattern_len || strcmp(pattern, string + string_len - pattern_len); } - return fnmatch(pattern, string, fnm_flags); + if (item->magic & PATHSPEC_GLOB) + return wildmatch(pattern, string, WM_PATHNAME, NULL); + else + /* wildmatch has not learned no FNM_PATHNAME mode yet */ + return fnmatch(pattern, string, 0); } static int fnmatch_icase_mem(const char *pattern, int patternlen, @@ -111,7 +113,8 @@ static size_t common_prefix_len(const struct pathspec *pathspec) GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | - PATHSPEC_LITERAL); + PATHSPEC_LITERAL | + PATHSPEC_GLOB); for (n = 0; n < pathspec->nr; n++) { size_t i = 0, len = 0; @@ -206,8 +209,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, } if (item->nowildcard_len < item->len && - !git_fnmatch(match, name, - item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + !git_fnmatch(item, match, name, item->nowildcard_len - prefix)) return MATCHED_FNMATCH; @@ -238,7 +240,8 @@ int match_pathspec_depth(const struct pathspec *ps, GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | - PATHSPEC_LITERAL); + PATHSPEC_LITERAL | + PATHSPEC_GLOB); if (!ps->nr) { if (!ps->recursive || @@ -1297,7 +1300,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | - PATHSPEC_LITERAL); + PATHSPEC_LITERAL | + PATHSPEC_GLOB); if (has_symlink_leading_path(path, len)) return dir->nr; diff --git a/dir.h b/dir.h index 7d051e3..343ec7a 100644 --- a/dir.h +++ b/dir.h @@ -199,10 +199,9 @@ extern int fnmatch_icase(const char *pattern, const char *string, int flags); /* * The prefix part of pattern must not contains wildcards. */ -#define GFNM_PATHNAME 1 /* similar to FNM_PATHNAME */ -#define GFNM_ONESTAR 2 /* there is only _one_ wildcard, a star */ - -extern int git_fnmatch(const char *pattern, const char *string, - int flags, int prefix); +struct pathspec_item; +extern int git_fnmatch(const struct pathspec_item *item, + const char *pattern, const char *string, + int prefix); #endif diff --git a/git.c b/git.c index 4359086..2509675 100644 --- a/git.c +++ b/git.c @@ -147,6 +147,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1); if (envchanged) *envchanged = 1; + } else if (!strcmp(cmd, "--glob-pathspecs")) { + setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; + } else if (!strcmp(cmd, "--noglob-pathspecs")) { + setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; } else if (!strcmp(cmd, "--shallow-file")) { (*argv)++; (*argc)--; diff --git a/pathspec.c b/pathspec.c index b6d8e74..c1e6917 100644 --- a/pathspec.c +++ b/pathspec.c @@ -57,7 +57,6 @@ char *find_pathspecs_matching_against_index(const struct pathspec *pathspec) * * Possible future magic semantics include stuff like: * - * { PATHSPEC_NOGLOB, '!', "noglob" }, * { PATHSPEC_ICASE, '\0', "icase" }, * { PATHSPEC_RECURSIVE, '*', "recursive" }, * { PATHSPEC_REGEXP, '\0', "regexp" }, @@ -71,6 +70,7 @@ static struct pathspec_magic { } pathspec_magic[] = { { PATHSPEC_FROMTOP, '/', "top" }, { PATHSPEC_LITERAL, 0, "literal" }, + { PATHSPEC_GLOB, '\0', "glob" }, }; /* @@ -93,6 +93,8 @@ static unsigned prefix_pathspec(struct pathspec_item *item, const char *elt) { static int literal_global = -1; + static int glob_global = -1; + static int noglob_global = -1; unsigned magic = 0, short_magic = 0, global_magic = 0; const char *copyfrom = elt, *long_magic_end = NULL; char *match; @@ -103,6 +105,22 @@ static unsigned prefix_pathspec(struct pathspec_item *item, if (literal_global) global_magic |= PATHSPEC_LITERAL; + if (glob_global < 0) + glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0); + if (glob_global) + global_magic |= PATHSPEC_GLOB; + + if (noglob_global < 0) + noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0); + + if (glob_global && noglob_global) + die(_("global 'glob' and 'noglob' pathspec settings are incompatible")); + + if ((global_magic & PATHSPEC_LITERAL) && + (global_magic & ~PATHSPEC_LITERAL)) + die(_("global 'literal' pathspec setting is incompatible " + "with all other global pathspec settings")); + if (elt[0] != ':' || literal_global) { ; /* nothing to do */ } else if (elt[1] == '(') { @@ -167,12 +185,24 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= short_magic; *p_short_magic = short_magic; + + /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specifed */ + if (noglob_global && !(magic & PATHSPEC_GLOB)) + global_magic |= PATHSPEC_LITERAL; + + /* --glob-pathspec is overriden by :(literal) */ + if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL)) + global_magic &= ~PATHSPEC_GLOB; + magic |= global_magic; if (pathspec_prefix >= 0 && (prefixlen || (prefix && *prefix))) die("BUG: 'prefix' magic is supposed to be used at worktree's root"); + if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB)) + die(_("%s: 'literal' and 'glob' are incompatible"), elt); + if (pathspec_prefix >= 0) { match = xstrdup(copyfrom); prefixlen = pathspec_prefix; @@ -248,10 +278,17 @@ static unsigned prefix_pathspec(struct pathspec_item *item, item->nowildcard_len = prefixlen; } item->flags = 0; - if (item->nowildcard_len < item->len && - item->match[item->nowildcard_len] == '*' && - no_wildcard(item->match + item->nowildcard_len + 1)) - item->flags |= PATHSPEC_ONESTAR; + if (magic & PATHSPEC_GLOB) { + /* + * FIXME: should we enable ONESTAR in _GLOB for + * pattern "* * / * . c"? + */ + } else { + if (item->nowildcard_len < item->len && + item->match[item->nowildcard_len] == '*' && + no_wildcard(item->match + item->nowildcard_len + 1)) + item->flags |= PATHSPEC_ONESTAR; + } /* sanity checks, pathspec matchers assume these are sane */ assert(item->nowildcard_len <= item->len && diff --git a/pathspec.h b/pathspec.h index 987d70c..cdf2fa3 100644 --- a/pathspec.h +++ b/pathspec.h @@ -5,10 +5,12 @@ #define PATHSPEC_FROMTOP (1<<0) #define PATHSPEC_MAXDEPTH (1<<1) #define PATHSPEC_LITERAL (1<<2) +#define PATHSPEC_GLOB (1<<3) #define PATHSPEC_ALL_MAGIC \ (PATHSPEC_FROMTOP | \ PATHSPEC_MAXDEPTH | \ - PATHSPEC_LITERAL) + PATHSPEC_LITERAL | \ + PATHSPEC_GLOB) #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh index 8551b02..ea00d71 100755 --- a/t/t6130-pathspec-noglob.sh +++ b/t/t6130-pathspec-noglob.sh @@ -32,6 +32,16 @@ test_expect_success 'star pathspec globs' ' test_cmp expect actual ' +test_expect_success 'star pathspec globs' ' + cat >expect <<-\EOF && + bracket + star + vanilla + EOF + git log --format=%s -- ":(glob)f*" >actual && + test_cmp expect actual +' + test_expect_success 'bracket pathspec globs and matches literal brackets' ' cat >expect <<-\EOF && bracket @@ -41,6 +51,15 @@ test_expect_success 'bracket pathspec globs and matches literal brackets' ' test_cmp expect actual ' +test_expect_success 'bracket pathspec globs and matches literal brackets' ' + cat >expect <<-\EOF && + bracket + vanilla + EOF + git log --format=%s -- ":(glob)f[o][o]" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob option matches literally (vanilla)' ' echo vanilla >expect && git --literal-pathspecs log --format=%s -- foo >actual && @@ -89,4 +108,48 @@ test_expect_success 'no-glob environment variable works' ' test_cmp expect actual ' +test_expect_success 'setup xxx/bar' ' + mkdir xxx && + test_commit xxx xxx/bar +' + +test_expect_success '**/ works with :(glob)' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git log --format=%s -- ":(glob)**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ does not work with --noglob-pathspecs' ' + : >expect && + git --noglob-pathspecs log --format=%s -- "**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ works with :(glob) and --noglob-pathspecs' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git --noglob-pathspecs log --format=%s -- ":(glob)**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ works with --glob-pathspecs' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git --glob-pathspecs log --format=%s -- "**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' ' + : >expect && + git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual && + test_cmp expect actual +' + test_done diff --git a/tree-walk.c b/tree-walk.c index 676bd7f..a44f528 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -639,7 +639,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | - PATHSPEC_LITERAL); + PATHSPEC_LITERAL | + PATHSPEC_GLOB); if (!ps->nr) { if (!ps->recursive || @@ -685,8 +686,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, return entry_interesting; if (item->nowildcard_len < item->len) { - if (!git_fnmatch(match + baselen, entry->path, - item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + if (!git_fnmatch(item, match + baselen, entry->path, item->nowildcard_len - baselen)) return entry_interesting; @@ -727,8 +727,7 @@ match_wildcards: strbuf_add(base, entry->path, pathlen); - if (!git_fnmatch(match, base->buf + base_offset, - item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + if (!git_fnmatch(item, match, base->buf + base_offset, item->nowildcard_len)) { strbuf_setlen(base, base_offset + baselen); return entry_interesting; -- cgit v0.10.2-6-g49f6 From 93d935371654faf2956a4c37c1ca46f3195ee832 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, 14 Jul 2013 15:36:09 +0700 Subject: parse_pathspec: accept :(icase)path syntax 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/git.txt b/Documentation/git.txt index 3571a1b..2c1f6f5 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -466,6 +466,10 @@ help ...`. globbing on individual pathspecs can be done using pathspec magic ":(glob)" +--icase-pathspecs: + Add "icase" magic to all pathspec. This is equivalent to setting + the `GIT_ICASE_PATHSPECS` environment variable to `1`. + GIT COMMANDS ------------ @@ -879,6 +883,10 @@ GIT_NOGLOB_PATHSPECS:: Setting this variable to `1` will cause Git to treat all pathspecs as literal (aka "literal" magic). +GIT_ICASE_PATHSPECS:: + Setting this variable to `1` will cause Git to treat all + pathspecs as case-insensitive. + Discussion[[Discussion]] ------------------------ diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index a3d9029..13a64d3 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -334,6 +334,9 @@ literal;; Wildcards in the pattern such as `*` or `?` are treated as literal characters. +icase;; + Case insensitive match. + glob;; Git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: diff --git a/builtin/add.c b/builtin/add.c index 1dab246..9d52fc7 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -544,12 +544,14 @@ int cmd_add(int argc, const char **argv, const char *prefix) GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL | - PATHSPEC_GLOB); + PATHSPEC_GLOB | + PATHSPEC_ICASE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; if (!seen[i] && - ((pathspec.items[i].magic & PATHSPEC_GLOB) || + ((pathspec.items[i].magic & + (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 7882352..f6d8215 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -173,7 +173,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * cannot be lifted until it is converted to use * match_pathspec_depth() or tree_entry_interesting() */ - parse_pathspec(&pathspec, PATHSPEC_GLOB, + parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE, PATHSPEC_PREFER_CWD, prefix, argv + 1); for (i = 0; i < pathspec.nr; i++) diff --git a/cache.h b/cache.h index dc4d2ee..3cff825 100644 --- a/cache.h +++ b/cache.h @@ -369,6 +369,7 @@ static inline enum object_type object_type(unsigned int mode) #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS" #define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS" #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS" +#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS" /* * This environment variable is expected to contain a boolean indicating diff --git a/dir.c b/dir.c index 076bd46..8543736 100644 --- a/dir.c +++ b/dir.c @@ -57,7 +57,7 @@ inline int git_fnmatch(const struct pathspec_item *item, int prefix) { if (prefix > 0) { - if (strncmp(pattern, string, prefix)) + if (ps_strncmp(item, pattern, string, prefix)) return FNM_NOMATCH; pattern += prefix; string += prefix; @@ -66,14 +66,18 @@ inline int git_fnmatch(const struct pathspec_item *item, int pattern_len = strlen(++pattern); int string_len = strlen(string); return string_len < pattern_len || - strcmp(pattern, - string + string_len - pattern_len); + ps_strcmp(item, pattern, + string + string_len - pattern_len); } if (item->magic & PATHSPEC_GLOB) - return wildmatch(pattern, string, WM_PATHNAME, NULL); + return wildmatch(pattern, string, + WM_PATHNAME | + (item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0), + NULL); else /* wildmatch has not learned no FNM_PATHNAME mode yet */ - return fnmatch(pattern, string, 0); + return fnmatch(pattern, string, + item->magic & PATHSPEC_ICASE ? FNM_CASEFOLD : 0); } static int fnmatch_icase_mem(const char *pattern, int patternlen, @@ -110,16 +114,27 @@ static size_t common_prefix_len(const struct pathspec *pathspec) int n; size_t max = 0; + /* + * ":(icase)path" is treated as a pathspec full of + * wildcard. In other words, only prefix is considered common + * prefix. If the pathspec is abc/foo abc/bar, running in + * subdir xyz, the common prefix is still xyz, not xuz/abc as + * in non-:(icase). + */ GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | - PATHSPEC_GLOB); + PATHSPEC_GLOB | + PATHSPEC_ICASE); for (n = 0; n < pathspec->nr; n++) { - size_t i = 0, len = 0; - while (i < pathspec->items[n].nowildcard_len && - (n == 0 || i < max)) { + size_t i = 0, len = 0, item_len; + if (pathspec->items[n].magic & PATHSPEC_ICASE) + item_len = pathspec->items[n].prefix; + else + item_len = pathspec->items[n].nowildcard_len; + while (i < item_len && (n == 0 || i < max)) { char c = pathspec->items[n].match[i]; if (c != pathspec->items[0].match[i]) break; @@ -196,11 +211,44 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, const char *match = item->match + prefix; int matchlen = item->len - prefix; + /* + * The normal call pattern is: + * 1. prefix = common_prefix_len(ps); + * 2. prune something, or fill_directory + * 3. match_pathspec_depth() + * + * 'prefix' at #1 may be shorter than the command's prefix and + * it's ok for #2 to match extra files. Those extras will be + * trimmed at #3. + * + * Suppose the pathspec is 'foo' and '../bar' running from + * subdir 'xyz'. The common prefix at #1 will be empty, thanks + * to "../". We may have xyz/foo _and_ XYZ/foo after #2. The + * user does not want XYZ/foo, only the "foo" part should be + * case-insensitive. We need to filter out XYZ/foo here. In + * other words, we do not trust the caller on comparing the + * prefix part when :(icase) is involved. We do exact + * comparison ourselves. + * + * Normally the caller (common_prefix_len() in fact) does + * _exact_ matching on name[-prefix+1..-1] and we do not need + * to check that part. Be defensive and check it anyway, in + * case common_prefix_len is changed, or a new caller is + * introduced that does not use common_prefix_len. + * + * If the penalty turns out too high when prefix is really + * long, maybe change it to + * strncmp(match, name, item->prefix - prefix) + */ + if (item->prefix && (item->magic & PATHSPEC_ICASE) && + strncmp(item->match, name - prefix, item->prefix)) + return 0; + /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; - if (matchlen <= namelen && !strncmp(match, name, matchlen)) { + if (matchlen <= namelen && !ps_strncmp(item, match, name, matchlen)) { if (matchlen == namelen) return MATCHED_EXACTLY; @@ -241,7 +289,8 @@ int match_pathspec_depth(const struct pathspec *ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | - PATHSPEC_GLOB); + PATHSPEC_GLOB | + PATHSPEC_ICASE); if (!ps->nr) { if (!ps->recursive || @@ -1301,7 +1350,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | - PATHSPEC_GLOB); + PATHSPEC_GLOB | + PATHSPEC_ICASE); if (has_symlink_leading_path(path, len)) return dir->nr; diff --git a/git.c b/git.c index 2509675..cebf882 100644 --- a/git.c +++ b/git.c @@ -155,6 +155,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; + } else if (!strcmp(cmd, "--icase-pathspecs")) { + setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; } else if (!strcmp(cmd, "--shallow-file")) { (*argv)++; (*argc)--; diff --git a/pathspec.c b/pathspec.c index c1e6917..d9f4143 100644 --- a/pathspec.c +++ b/pathspec.c @@ -57,7 +57,6 @@ char *find_pathspecs_matching_against_index(const struct pathspec *pathspec) * * Possible future magic semantics include stuff like: * - * { PATHSPEC_ICASE, '\0', "icase" }, * { PATHSPEC_RECURSIVE, '*', "recursive" }, * { PATHSPEC_REGEXP, '\0', "regexp" }, * @@ -71,6 +70,7 @@ static struct pathspec_magic { { PATHSPEC_FROMTOP, '/', "top" }, { PATHSPEC_LITERAL, 0, "literal" }, { PATHSPEC_GLOB, '\0', "glob" }, + { PATHSPEC_ICASE, '\0', "icase" }, }; /* @@ -95,6 +95,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, static int literal_global = -1; static int glob_global = -1; static int noglob_global = -1; + static int icase_global = -1; unsigned magic = 0, short_magic = 0, global_magic = 0; const char *copyfrom = elt, *long_magic_end = NULL; char *match; @@ -116,6 +117,12 @@ static unsigned prefix_pathspec(struct pathspec_item *item, if (glob_global && noglob_global) die(_("global 'glob' and 'noglob' pathspec settings are incompatible")); + + if (icase_global < 0) + icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0); + if (icase_global) + global_magic |= PATHSPEC_ICASE; + if ((global_magic & PATHSPEC_LITERAL) && (global_magic & ~PATHSPEC_LITERAL)) die(_("global 'literal' pathspec setting is incompatible " diff --git a/pathspec.h b/pathspec.h index cdf2fa3..04b632f 100644 --- a/pathspec.h +++ b/pathspec.h @@ -6,11 +6,13 @@ #define PATHSPEC_MAXDEPTH (1<<1) #define PATHSPEC_LITERAL (1<<2) #define PATHSPEC_GLOB (1<<3) +#define PATHSPEC_ICASE (1<<4) #define PATHSPEC_ALL_MAGIC \ (PATHSPEC_FROMTOP | \ PATHSPEC_MAXDEPTH | \ PATHSPEC_LITERAL | \ - PATHSPEC_GLOB) + PATHSPEC_GLOB | \ + PATHSPEC_ICASE) #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ @@ -65,6 +67,24 @@ extern void parse_pathspec(struct pathspec *pathspec, extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src); extern void free_pathspec(struct pathspec *); +static inline int ps_strncmp(const struct pathspec_item *item, + const char *s1, const char *s2, size_t n) +{ + if (item->magic & PATHSPEC_ICASE) + return strncasecmp(s1, s2, n); + else + return strncmp(s1, s2, n); +} + +static inline int ps_strcmp(const struct pathspec_item *item, + const char *s1, const char *s2) +{ + if (item->magic & PATHSPEC_ICASE) + return strcasecmp(s1, s2); + else + return strcmp(s1, s2); +} + extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec); extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen); extern const char *check_path_for_gitlink(const char *path); diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh new file mode 100755 index 0000000..3215eef --- /dev/null +++ b/t/t6131-pathspec-icase.sh @@ -0,0 +1,97 @@ +#!/bin/sh + +test_description='test case insensitive pathspec limiting' +. ./test-lib.sh + +test_expect_success 'create commits with glob characters' ' + test_commit bar bar && + test_commit bAr bAr && + test_commit BAR BAR && + mkdir foo && + test_commit foo/bar foo/bar && + test_commit foo/bAr foo/bAr && + test_commit foo/BAR foo/BAR && + mkdir fOo && + test_commit fOo/bar fOo/bar && + test_commit fOo/bAr fOo/bAr && + test_commit fOo/BAR fOo/BAR && + mkdir FOO && + test_commit FOO/bar FOO/bar && + test_commit FOO/bAr FOO/bAr && + test_commit FOO/BAR FOO/BAR +' + +test_expect_success 'tree_entry_interesting matches bar' ' + echo bar >expect && + git log --format=%s -- "bar" >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar' ' + cat <<-EOF >expect && + BAR + bAr + bar + EOF + git log --format=%s -- ":(icase)bar" >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar with prefix' ' + cat <<-EOF >expect && + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git log --format=%s -- ":(icase)bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefix' ' + cat <<-EOF >expect && + FOO/BAR + FOO/bAr + FOO/bar + fOo/BAR + fOo/bAr + fOo/bar + foo/BAR + foo/bAr + foo/bar + EOF + ( cd fOo && git log --format=%s -- ":(icase)../foo/bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar' ' + cat <<-EOF >expect && + BAR + bAr + bar + EOF + git ls-files ":(icase)bar" >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' ' + cat <<-EOF >expect && + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git ls-files --full-name ":(icase)bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' ' + cat <<-EOF >expect && + bar + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git ls-files --full-name ":(icase)bar" ../bar ) >actual && + test_cmp expect actual +' + +test_done diff --git a/tree-walk.c b/tree-walk.c index a44f528..c366852 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -489,13 +489,25 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch return retval; } -static int match_entry(const struct name_entry *entry, int pathlen, +static int match_entry(const struct pathspec_item *item, + const struct name_entry *entry, int pathlen, const char *match, int matchlen, enum interesting *never_interesting) { int m = -1; /* signals that we haven't called strncmp() */ - if (*never_interesting != entry_not_interesting) { + if (item->magic & PATHSPEC_ICASE) + /* + * "Never interesting" trick requires exact + * matching. We could do something clever with inexact + * matching, but it's trickier (and not to forget that + * strcasecmp is locale-dependent, at least in + * glibc). Just disable it for now. It can't be worse + * than the wildcard's codepath of '[Tt][Hi][Is][Ss]' + * pattern. + */ + *never_interesting = entry_not_interesting; + else if (*never_interesting != entry_not_interesting) { /* * We have not seen any match that sorts later * than the current path. @@ -541,7 +553,7 @@ static int match_entry(const struct name_entry *entry, int pathlen, * we cheated and did not do strncmp(), so we do * that here. */ - m = strncmp(match, entry->path, pathlen); + m = ps_strncmp(item, match, entry->path, pathlen); /* * If common part matched earlier then it is a hit, @@ -549,15 +561,39 @@ static int match_entry(const struct name_entry *entry, int pathlen, * leading directory and is shorter than match. */ if (!m) + /* + * match_entry does not check if the prefix part is + * matched case-sensitively. If the entry is a + * directory and part of prefix, it'll be rematched + * eventually by basecmp with special treatment for + * the prefix. + */ return 1; return 0; } -static int match_dir_prefix(const char *base, +/* :(icase)-aware string compare */ +static int basecmp(const struct pathspec_item *item, + const char *base, const char *match, int len) +{ + if (item->magic & PATHSPEC_ICASE) { + int ret, n = len > item->prefix ? item->prefix : len; + ret = strncmp(base, match, n); + if (ret) + return ret; + base += n; + match += n; + len -= n; + } + return ps_strncmp(item, base, match, len); +} + +static int match_dir_prefix(const struct pathspec_item *item, + const char *base, const char *match, int matchlen) { - if (strncmp(base, match, matchlen)) + if (basecmp(item, base, match, matchlen)) return 0; /* @@ -594,7 +630,7 @@ static int match_wildcard_base(const struct pathspec_item *item, */ if (baselen >= matchlen) { *matched = matchlen; - return !strncmp(base, match, matchlen); + return !basecmp(item, base, match, matchlen); } dirlen = matchlen; @@ -607,7 +643,7 @@ static int match_wildcard_base(const struct pathspec_item *item, * base ends with '/' so we are sure it really matches * directory */ - if (strncmp(base, match, baselen)) + if (basecmp(item, base, match, baselen)) return 0; *matched = baselen; } else @@ -640,7 +676,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | - PATHSPEC_GLOB); + PATHSPEC_GLOB | + PATHSPEC_ICASE); if (!ps->nr) { if (!ps->recursive || @@ -663,7 +700,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, if (baselen >= matchlen) { /* If it doesn't match, move along... */ - if (!match_dir_prefix(base_str, match, matchlen)) + if (!match_dir_prefix(item, base_str, match, matchlen)) goto match_wildcards; if (!ps->recursive || @@ -679,8 +716,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, } /* Either there must be no base, or the base must match. */ - if (baselen == 0 || !strncmp(base_str, match, baselen)) { - if (match_entry(entry, pathlen, + if (baselen == 0 || !basecmp(item, base_str, match, baselen)) { + if (match_entry(item, entry, pathlen, match + baselen, matchlen - baselen, &never_interesting)) return entry_interesting; -- cgit v0.10.2-6-g49f6 From db6a6adabfb57d8a4dcd0491a7b7e2168dac60c1 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Wed, 17 Jul 2013 09:22:16 -0400 Subject: t6131 - skip tests if on case-insensitive file system This test fails on Cygwin where the default system configuration does not support case sensitivity (only case retention), so don't run the test on such systems. Signed-off-by: Mark Levedahl Signed-off-by: Junio C Hamano diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh index 3215eef..8d4a7fc 100755 --- a/t/t6131-pathspec-icase.sh +++ b/t/t6131-pathspec-icase.sh @@ -3,6 +3,12 @@ test_description='test case insensitive pathspec limiting' . ./test-lib.sh +if test_have_prereq CASE_INSENSITIVE_FS +then + skip_all='skipping case sensitive tests - case insensitive file system' + test_done +fi + test_expect_success 'create commits with glob characters' ' test_commit bar bar && test_commit bAr bAr && -- cgit v0.10.2-6-g49f6 From 4838c81fabc2bab7ff5cd95135060d9a580ad742 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 23 Jul 2013 11:19:22 +0200 Subject: rm: do not set a variable twice without intermediate reading. Just the next line assigns a non-null value to seen. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano diff --git a/builtin/rm.c b/builtin/rm.c index f08561d..fe3faad 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -316,7 +316,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix) parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv); refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); - seen = NULL; seen = xcalloc(pathspec.nr, 1); for (i = 0; i < active_nr; i++) { -- cgit v0.10.2-6-g49f6 From 11502468287fdd62a22c43766881d21ab4fcf31c Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 30 Jul 2013 21:49:25 +0200 Subject: mv: move submodules together with their work trees Currently the attempt to use "git mv" on a submodule errors out with: fatal: source directory is empty, source=, destination= The reason is that mv searches for the submodule with a trailing slash in the index, which it doesn't find (because it is stored without a trailing slash). As it doesn't find any index entries inside the submodule it claims the directory would be empty even though it isn't. Fix that by searching for the name without a trailing slash and continue if it is a submodule. Then rename() will move the submodule work tree just like it moves a file. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano diff --git a/builtin/mv.c b/builtin/mv.c index 16ce99b..1d3ef63 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -118,55 +118,60 @@ int cmd_mv(int argc, const char **argv, const char *prefix) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { - const char *src_w_slash = add_slash(src); - int len_w_slash = length + 1; - int first, last; - - modes[i] = WORKING_DIRECTORY; - - first = cache_name_pos(src_w_slash, len_w_slash); - if (first >= 0) - die (_("Huh? %.*s is in index?"), - len_w_slash, src_w_slash); - - first = -1 - first; - for (last = first; last < active_nr; last++) { - const char *path = active_cache[last]->name; - if (strncmp(path, src_w_slash, len_w_slash)) - break; - } - free((char *)src_w_slash); - - if (last - first < 1) - bad = _("source directory is empty"); - else { - int j, dst_len; - - if (last - first > 0) { - source = xrealloc(source, - (argc + last - first) - * sizeof(char *)); - destination = xrealloc(destination, - (argc + last - first) - * sizeof(char *)); - modes = xrealloc(modes, - (argc + last - first) - * sizeof(enum update_mode)); + int first = cache_name_pos(src, length); + if (first >= 0) { + if (!S_ISGITLINK(active_cache[first]->ce_mode)) + die (_("Huh? Directory %s is in index and no submodule?"), src); + } else { + const char *src_w_slash = add_slash(src); + int last, len_w_slash = length + 1; + + modes[i] = WORKING_DIRECTORY; + + first = cache_name_pos(src_w_slash, len_w_slash); + if (first >= 0) + die (_("Huh? %.*s is in index?"), + len_w_slash, src_w_slash); + + first = -1 - first; + for (last = first; last < active_nr; last++) { + const char *path = active_cache[last]->name; + if (strncmp(path, src_w_slash, len_w_slash)) + break; } - - dst = add_slash(dst); - dst_len = strlen(dst); - - for (j = 0; j < last - first; j++) { - const char *path = - active_cache[first + j]->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, - path + length + 1); - modes[argc + j] = INDEX; + free((char *)src_w_slash); + + if (last - first < 1) + bad = _("source directory is empty"); + else { + int j, dst_len; + + if (last - first > 0) { + source = xrealloc(source, + (argc + last - first) + * sizeof(char *)); + destination = xrealloc(destination, + (argc + last - first) + * sizeof(char *)); + modes = xrealloc(modes, + (argc + last - first) + * sizeof(enum update_mode)); + } + + dst = add_slash(dst); + dst_len = strlen(dst); + + for (j = 0; j < last - first; j++) { + const char *path = + active_cache[first + j]->name; + source[argc + j] = path; + destination[argc + j] = + prefix_path(dst, dst_len, + path + length + 1); + modes[argc + j] = INDEX; + } + argc += last - first; } - argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 101816e..15c18b6 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -259,4 +259,38 @@ test_expect_success SYMLINKS 'check moved symlink' ' rm -f moved symlink +test_expect_success 'setup submodule' ' + git commit -m initial && + git reset --hard && + git submodule add ./. sub && + echo content >file && + git add file && + git commit -m "added sub and file" +' + +test_expect_success 'git mv cannot move a submodule in a file' ' + test_must_fail git mv sub file +' + +test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' ' + entry="$(git ls-files --stage sub | cut -f 1)" && + git rm .gitmodules && + ( + cd sub && + rm -f .git && + cp -a ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + mkdir mod && + git mv sub mod/sub && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + test_done -- cgit v0.10.2-6-g49f6 From a88c915de9886fe17005a5daff4900ced0ea76ad Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 30 Jul 2013 21:50:03 +0200 Subject: mv: move submodules using a gitfile When moving a submodule which uses a gitfile to point to the git directory stored in .git/modules/ of the superproject two changes must be made to make the submodule work: the .git file and the core.worktree setting must be adjusted to point from work tree to git directory and back. Achieve that by remembering which submodule uses a gitfile by storing the result of read_gitfile() of each submodule. If that is not NULL the new function connect_work_tree_and_git_dir() is called after renaming the submodule's work tree which updates the two settings to the new values. Extend the man page to inform the user about that feature (and while at it change the description to not talk about a script anymore, as mv is a builtin for quite some time now). Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index e93fcb4..1f6fce0 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -13,7 +13,7 @@ SYNOPSIS DESCRIPTION ----------- -This script is used to move or rename a file, directory or symlink. +Move or rename a file, directory or symlink. git mv [-v] [-f] [-n] [-k] git mv [-v] [-f] [-n] [-k] ... @@ -44,6 +44,12 @@ OPTIONS --verbose:: Report the names of files as they are moved. +SUBMODULES +---------- +Moving a submodule using a gitfile (which means they were cloned +with a Git version 1.7.8 or newer) will update the gitfile and +core.worktree setting to make the submodule work in the new location. + GIT --- Part of the linkgit:git[1] suite diff --git a/builtin/mv.c b/builtin/mv.c index 1d3ef63..68b7060 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -9,6 +9,7 @@ #include "cache-tree.h" #include "string-list.h" #include "parse-options.h" +#include "submodule.h" static const char * const builtin_mv_usage[] = { N_("git mv [options] ... "), @@ -66,7 +67,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; - const char **source, **destination, **dest_path; + const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; @@ -85,6 +86,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0); + submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ @@ -120,8 +122,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix) else if (src_is_dir) { int first = cache_name_pos(src, length); if (first >= 0) { + struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die (_("Huh? Directory %s is in index and no submodule?"), src); + strbuf_addf(&submodule_dotgit, "%s/.git", src); + submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); + if (submodule_gitfile[i]) + submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); + strbuf_release(&submodule_dotgit); } else { const char *src_w_slash = add_slash(src); int last, len_w_slash = length + 1; @@ -216,9 +224,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); - if (!show_only && mode != INDEX && - rename(src, dst) < 0 && !ignore_errors) - die_errno (_("renaming '%s' failed"), src); + if (!show_only && mode != INDEX) { + if (rename(src, dst) < 0 && !ignore_errors) + die_errno (_("renaming '%s' failed"), src); + if (submodule_gitfile[i]) + connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); + } if (mode == WORKING_DIRECTORY) continue; diff --git a/submodule.c b/submodule.c index 85415d0..392e83e 100644 --- a/submodule.c +++ b/submodule.c @@ -1004,3 +1004,34 @@ int merge_submodule(unsigned char result[20], const char *path, free(merges.objects); return 0; } + +/* Update gitfile and core.worktree setting to connect work tree and git dir */ +void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir) +{ + struct strbuf file_name = STRBUF_INIT; + struct strbuf rel_path = STRBUF_INIT; + const char *real_work_tree = xstrdup(real_path(work_tree)); + FILE *fp; + + /* Update gitfile */ + strbuf_addf(&file_name, "%s/.git", work_tree); + fp = fopen(file_name.buf, "w"); + if (!fp) + die(_("Could not create git link %s"), file_name.buf); + fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree, + &rel_path)); + fclose(fp); + + /* Update core.worktree setting */ + strbuf_reset(&file_name); + strbuf_addf(&file_name, "%s/config", git_dir); + if (git_config_set_in_file(file_name.buf, "core.worktree", + relative_path(real_work_tree, git_dir, + &rel_path))) + die(_("Could not set core.worktree in %s"), + file_name.buf); + + strbuf_release(&file_name); + strbuf_release(&rel_path); + free((void *)real_work_tree); +} diff --git a/submodule.h b/submodule.h index c7ffc7c..29e9658 100644 --- a/submodule.h +++ b/submodule.h @@ -36,5 +36,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name, struct string_list *needs_pushing); int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name); +void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir); #endif diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 15c18b6..b99177f 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -293,4 +293,23 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm git diff-files --quiet ' +test_expect_success 'git mv moves a submodule with gitfile' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + entry="$(git ls-files --stage sub | cut -f 1)" && + ( + cd mod && + git mv ../sub/ . + ) && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + test_done -- cgit v0.10.2-6-g49f6 From 5fee995244e13fd59500425d49038b00499396f8 Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 30 Jul 2013 21:50:34 +0200 Subject: submodule.c: add .gitmodules staging helper functions Add the new is_staging_gitmodules_ok() and stage_updated_gitmodules() functions to submodule.c. The first makes it possible for call sites to see if the .gitmodules file did contain any unstaged modifications they would accidentally stage in addition to those they intend to stage themselves. The second function stages all modifications to the .gitmodules file, both will be used by subsequent patches for the mv and rm commands. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano diff --git a/submodule.c b/submodule.c index 392e83e..fb5134a 100644 --- a/submodule.c +++ b/submodule.c @@ -10,6 +10,7 @@ #include "string-list.h" #include "sha1-array.h" #include "argv-array.h" +#include "blob.h" static struct string_list config_name_for_path; static struct string_list config_fetch_recurse_submodules_for_name; @@ -30,6 +31,51 @@ static struct sha1_array ref_tips_after_fetch; */ static int gitmodules_is_unmerged; +/* + * This flag is set if the .gitmodules file had unstaged modifications on + * startup. This must be checked before allowing modifications to the + * .gitmodules file with the intention to stage them later, because when + * continuing we would stage the modifications the user didn't stage herself + * too. That might change in a future version when we learn to stage the + * changes we do ourselves without staging any previous modifications. + */ +static int gitmodules_is_modified; + + +int is_staging_gitmodules_ok(void) +{ + return !gitmodules_is_modified; +} + +void stage_updated_gitmodules(void) +{ + struct strbuf buf = STRBUF_INIT; + struct stat st; + int pos; + struct cache_entry *ce; + int namelen = strlen(".gitmodules"); + + pos = cache_name_pos(".gitmodules", namelen); + if (pos < 0) { + warning(_("could not find .gitmodules in index")); + return; + } + ce = active_cache[pos]; + ce->ce_flags = namelen; + if (strbuf_read_file(&buf, ".gitmodules", 0) < 0) + die(_("reading updated .gitmodules failed")); + if (lstat(".gitmodules", &st) < 0) + die_errno(_("unable to stat updated .gitmodules")); + fill_stat_cache_info(ce, &st); + ce->ce_mode = ce_mode_from_stat(ce, st.st_mode); + if (remove_cache_entry_at(pos) < 0) + die(_("unable to remove .gitmodules from index")); + if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1)) + die(_("adding updated .gitmodules failed")); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) + die(_("staging updated .gitmodules failed")); +} + static int add_submodule_odb(const char *path) { struct strbuf objects_directory = STRBUF_INIT; @@ -116,6 +162,11 @@ void gitmodules_config(void) !memcmp(ce->name, ".gitmodules", 11)) gitmodules_is_unmerged = 1; } + } else if (pos < active_nr) { + struct stat st; + if (lstat(".gitmodules", &st) == 0 && + ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED) + gitmodules_is_modified = 1; } if (!gitmodules_is_unmerged) diff --git a/submodule.h b/submodule.h index 29e9658..5501354 100644 --- a/submodule.h +++ b/submodule.h @@ -11,6 +11,8 @@ enum { RECURSE_SUBMODULES_ON = 2 }; +int is_staging_gitmodules_ok(void); +void stage_updated_gitmodules(void); void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, const char *path); int submodule_config(const char *var, const char *value, void *cb); -- cgit v0.10.2-6-g49f6 From 0656781fadca17cc51fb1c30f265c251ebe12819 Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 6 Aug 2013 21:15:11 +0200 Subject: mv: update the path entry in .gitmodules for moved submodules Currently using "git mv" on a submodule moves the submodule's work tree in that of the superproject. But the submodule's path setting in .gitmodules is left untouched, which is now inconsistent with the work tree and makes git commands that rely on the proper path -> name mapping (like status and diff) behave strangely. Let "git mv" help here by not only moving the submodule's work tree but also updating the "submodule..path" setting from the .gitmodules file and stage both. This doesn't happen when no .gitmodules file is found and only issues a warning when it doesn't have a section for this submodule. This is because the user might just use plain gitlinks without the .gitmodules file or has already updated the path setting by hand before issuing the "git mv" command (in which case the warning reminds him that mv would have done that for him). Only when .gitmodules is found and contains merge conflicts the mv command will fail and tell the user to resolve the conflict before trying again. Also extend the man page to inform the user about this new feature. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index 1f6fce0..b1f7988 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -49,6 +49,8 @@ SUBMODULES Moving a submodule using a gitfile (which means they were cloned with a Git version 1.7.8 or newer) will update the gitfile and core.worktree setting to make the submodule work in the new location. +It also will attempt to update the submodule..path setting in +the linkgit:gitmodules[5] file and stage that file (unless -n is used). GIT --- diff --git a/builtin/mv.c b/builtin/mv.c index 68b7060..7dd6bb4 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -58,7 +58,7 @@ static struct lock_file lock_file; int cmd_mv(int argc, const char **argv, const char *prefix) { - int i, newfd; + int i, newfd, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), @@ -72,6 +72,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; + gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, @@ -125,6 +126,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die (_("Huh? Directory %s is in index and no submodule?"), src); + if (!is_staging_gitmodules_ok()) + die (_("Please, stage your changes to .gitmodules or stash them to proceed")); strbuf_addf(&submodule_dotgit, "%s/.git", src); submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); if (submodule_gitfile[i]) @@ -229,6 +232,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) die_errno (_("renaming '%s' failed"), src); if (submodule_gitfile[i]) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); + if (!update_path_in_gitmodules(src, dst)) + gitmodules_modified = 1; } if (mode == WORKING_DIRECTORY) @@ -240,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix) rename_cache_entry_at(pos, dst); } + if (gitmodules_modified) + stage_updated_gitmodules(); + if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) diff --git a/submodule.c b/submodule.c index fb5134a..d4e8276 100644 --- a/submodule.c +++ b/submodule.c @@ -47,6 +47,40 @@ int is_staging_gitmodules_ok(void) return !gitmodules_is_modified; } +/* + * Try to update the "path" entry in the "submodule." section of the + * .gitmodules file. Return 0 only if a .gitmodules file was found, a section + * with the correct path= setting was found and we could update it. + */ +int update_path_in_gitmodules(const char *oldpath, const char *newpath) +{ + struct strbuf entry = STRBUF_INIT; + struct string_list_item *path_option; + + if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */ + return -1; + + if (gitmodules_is_unmerged) + die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first")); + + path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath); + if (!path_option) { + warning(_("Could not find section in .gitmodules where path=%s"), oldpath); + return -1; + } + strbuf_addstr(&entry, "submodule."); + strbuf_addstr(&entry, path_option->util); + strbuf_addstr(&entry, ".path"); + if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) { + /* Maybe the user already did that, don't error out here */ + warning(_("Could not update .gitmodules entry %s"), entry.buf); + strbuf_release(&entry); + return -1; + } + strbuf_release(&entry); + return 0; +} + void stage_updated_gitmodules(void) { struct strbuf buf = STRBUF_INIT; diff --git a/submodule.h b/submodule.h index 5501354..e339891 100644 --- a/submodule.h +++ b/submodule.h @@ -12,6 +12,7 @@ enum { }; int is_staging_gitmodules_ok(void); +int update_path_in_gitmodules(const char *oldpath, const char *newpath); void stage_updated_gitmodules(void); void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, const char *path); diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index b99177f..d432f42 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -308,8 +308,83 @@ test_expect_success 'git mv moves a submodule with gitfile' ' cd mod/sub && git status ) && + echo mod/sub >expected && + git config -f .gitmodules submodule.sub.path >actual && + test_cmp expected actual && git update-index --refresh && git diff-files --quiet ' +test_expect_success 'mv does not complain when no .gitmodules file is found' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git rm .gitmodules && + entry="$(git ls-files --stage sub | cut -f 1)" && + git mv sub mod/sub 2>actual.err && + ! test -s actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv will error out on a modified .gitmodules file unless staged' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git config -f .gitmodules foo.bar true && + entry="$(git ls-files --stage sub | cut -f 1)" && + test_must_fail git mv sub mod/sub 2>actual.err && + test -s actual.err && + test -e sub && + git diff-files --quiet -- sub && + git add .gitmodules && + git mv sub mod/sub 2>actual.err && + ! test -s actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv issues a warning when section is not found in .gitmodules' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git config -f .gitmodules --remove-section submodule.sub && + git add .gitmodules && + entry="$(git ls-files --stage sub | cut -f 1)" && + echo "warning: Could not find section in .gitmodules where path=sub" >expect.err && + git mv sub mod/sub 2>actual.err && + test_i18ncmp expect.err actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git mv -n sub mod/sub 2>actual.err && + test -f sub/.git && + git diff-index --exit-code HEAD && + git update-index --refresh && + git diff-files --quiet -- sub .gitmodules +' + test_done -- cgit v0.10.2-6-g49f6 From 95c16418f0375e2fc325f32c3d7578fba9cfd7ef Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 6 Aug 2013 21:15:25 +0200 Subject: rm: delete .gitmodules entry of submodules removed from the work tree Currently using "git rm" on a submodule removes the submodule's work tree from that of the superproject and the gitlink from the index. But the submodule's section in .gitmodules is left untouched, which is a leftover of the now removed submodule and might irritate users (as opposed to the setting in .git/config, this must stay as a reminder that the user showed interest in this submodule so it will be repopulated later when an older commit is checked out). Let "git rm" help the user by not only removing the submodule from the work tree but by also removing the "submodule." section from the .gitmodules file and stage both. This doesn't happen when the "--cached" option is used, as it would modify the work tree. This also silently does nothing when no .gitmodules file is found and only issues a warning when it doesn't have a section for this submodule. This is because the user might just use plain gitlinks without the .gitmodules file or has already removed the section by hand before issuing the "git rm" command (in which case the warning reminds him that rm would have done that for him). Only when .gitmodules is found and contains merge conflicts the rm command will fail and tell the user to resolve the conflict before trying again. Also extend the man page to inform the user about this new feature. While at it promote the submodule sub-section to a chapter as it made not much sense under "REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM". In t7610 three uses of "git rm submod" had to be replaced with "git rm --cached submod" because that test expects .gitmodules and the work tree to stay untouched. Also in t7400 the tests for the remaining settings in the .gitmodules file had to be changed to assert that these settings are missing. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index 1d876c2..9d731b4 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -134,14 +134,16 @@ use the following command: git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached ---------------- -Submodules -~~~~~~~~~~ +SUBMODULES +---------- Only submodules using a gitfile (which means they were cloned with a Git version 1.7.8 or newer) will be removed from the work tree, as their repository lives inside the .git directory of the superproject. If a submodule (or one of those nested inside it) still uses a .git directory, `git rm` will fail - no matter if forced -or not - to protect the submodule's history. +or not - to protect the submodule's history. If it exists the +submodule. section in the linkgit:gitmodules[5] file will also +be removed and that file will be staged (unless --cached or -n are used). A submodule is considered up-to-date when the HEAD is the same as recorded in the index, no tracked files are modified and no untracked diff --git a/builtin/rm.c b/builtin/rm.c index fe3faad..c848dad 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -283,6 +283,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) struct pathspec pathspec; char *seen; + gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, @@ -324,7 +325,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; - list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); + list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); + if (list.entry[list.nr++].is_submodule && + !is_staging_gitmodules_ok()) + die (_("Please, stage your changes to .gitmodules or stash them to proceed")); } if (pathspec.nr) { @@ -396,13 +400,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix) * in the middle) */ if (!index_only) { - int removed = 0; + int removed = 0, gitmodules_modified = 0; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { if (is_empty_dir(path)) { if (!rmdir(path)) { removed = 1; + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; continue; } } else { @@ -410,9 +416,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix) strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; strbuf_release(&buf); continue; - } + } else if (!file_exists(path)) + /* Submodule was removed by user */ + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } @@ -424,6 +435,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!removed) die_errno("git rm: '%s'", path); } + if (gitmodules_modified) + stage_updated_gitmodules(); } if (active_cache_changed) { diff --git a/submodule.c b/submodule.c index d4e8276..0494492 100644 --- a/submodule.c +++ b/submodule.c @@ -81,6 +81,39 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath) return 0; } +/* + * Try to remove the "submodule." section from .gitmodules where the given + * path is configured. Return 0 only if a .gitmodules file was found, a section + * with the correct path= setting was found and we could remove it. + */ +int remove_path_from_gitmodules(const char *path) +{ + struct strbuf sect = STRBUF_INIT; + struct string_list_item *path_option; + + if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */ + return -1; + + if (gitmodules_is_unmerged) + die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first")); + + path_option = unsorted_string_list_lookup(&config_name_for_path, path); + if (!path_option) { + warning(_("Could not find section in .gitmodules where path=%s"), path); + return -1; + } + strbuf_addstr(§, "submodule."); + strbuf_addstr(§, path_option->util); + if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) { + /* Maybe the user already did that, don't error out here */ + warning(_("Could not remove .gitmodules entry for %s"), path); + strbuf_release(§); + return -1; + } + strbuf_release(§); + return 0; +} + void stage_updated_gitmodules(void) { struct strbuf buf = STRBUF_INIT; diff --git a/submodule.h b/submodule.h index e339891..7beec48 100644 --- a/submodule.h +++ b/submodule.h @@ -13,6 +13,7 @@ enum { int is_staging_gitmodules_ok(void); int update_path_in_gitmodules(const char *oldpath, const char *newpath); +int remove_path_from_gitmodules(const char *path); void stage_updated_gitmodules(void); void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, const char *path); diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 5c87b55..639cb70 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -263,6 +263,7 @@ test_expect_success 'rm removes subdirectories recursively' ' ' cat >expect <expect.modified <expect.cached <expect.both_deleted< actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' -test_expect_success 'rm removes removed submodule from index' ' +test_expect_success 'rm removes removed submodule from index and .gitmodules' ' git reset --hard && git submodule update && rm -rf submod && git rm submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm removes work tree of unmodified submodules' ' @@ -299,7 +313,9 @@ test_expect_success 'rm removes work tree of unmodified submodules' ' git rm submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm removes a submodule with a trailing /' ' @@ -333,6 +349,72 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' ' + git reset --hard && + git submodule update && + git rm --cached submod && + test -d submod && + test -f submod/.git && + git status -s -uno >actual && + test_cmp expect.cached actual && + git config -f .gitmodules submodule.sub.url && + git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' ' + git reset --hard && + git submodule update && + git rm -n submod && + test -f submod/.git && + git diff-index --exit-code HEAD +' + +test_expect_success 'rm does not complain when no .gitmodules file is found' ' + git reset --hard && + git submodule update && + git rm .gitmodules && + git rm submod >actual 2>actual.err && + ! test -s actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect.both_deleted actual +' + +test_expect_success 'rm will error out on a modified .gitmodules file unless staged' ' + git reset --hard && + git submodule update && + git config -f .gitmodules foo.bar true && + test_must_fail git rm submod >actual 2>actual.err && + test -s actual.err && + test -d submod && + test -f submod/.git && + git diff-files --quiet -- submod && + git add .gitmodules && + git rm submod >actual 2>actual.err && + ! test -s actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect actual +' + +test_expect_success 'rm issues a warning when section is not found in .gitmodules' ' + git reset --hard && + git submodule update && + git config -f .gitmodules --remove-section submodule.sub && + git add .gitmodules && + echo "warning: Could not find section in .gitmodules where path=submod" >expect.err && + git rm submod >actual 2>actual.err && + test_i18ncmp expect.err actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && test_cmp expect actual ' @@ -427,7 +509,9 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' ' @@ -446,7 +530,9 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' ' diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 50e6ad7..a9dd730 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -773,13 +773,11 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano test_cmp expect .git ) && echo "repo" >expect && - git config -f .gitmodules submodule.repo.path >actual && - test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.repo.path && git config -f .gitmodules submodule.repo_new.path >actual && test_cmp expect actual&& echo "$submodurl/repo" >expect && - git config -f .gitmodules submodule.repo.url >actual && - test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.repo.url && echo "$submodurl/bare.git" >expect && git config -f .gitmodules submodule.repo_new.url >actual && test_cmp expect actual && @@ -799,12 +797,8 @@ test_expect_success 'submodule add with an existing name fails unless forced' ' git rm repo && test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo && test ! -d repo && - echo "repo" >expect && - git config -f .gitmodules submodule.repo_new.path >actual && - test_cmp expect actual&& - echo "$submodurl/bare.git" >expect && - git config -f .gitmodules submodule.repo_new.url >actual && - test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.repo_new.path && + test_must_fail git config -f .gitmodules submodule.repo_new.url && echo "$submodurl/bare.git" >expect && git config submodule.repo_new.url >actual && test_cmp expect actual && diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index d526b1d..05d9db0 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -253,7 +253,7 @@ test_expect_success 'deleted vs modified submodule' ' git checkout -b test6 branch1 && git submodule update -N && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && git commit -m "Submodule deleted from branch" && git checkout -b test6.a test6 && test_must_fail git merge master && @@ -322,7 +322,7 @@ test_expect_success 'file vs modified submodule' ' git checkout -b test7 branch1 && git submodule update -N && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && echo not a submodule >submod && git add submod && git commit -m "Submodule path becomes file" && @@ -453,7 +453,7 @@ test_expect_success 'submodule in subdirectory' ' test_expect_success 'directory vs modified submodule' ' git checkout -b test11 branch1 && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && mkdir submod && echo not a submodule >submod/file16 && git add submod/file16 && -- cgit v0.10.2-6-g49f6