magic pathspec: add tentative ":/path/from/top/level" pathspec support
Support ":/" magic string that can be prefixed to a pathspec element to say "this names the path from the top-level of the working tree", when you are in the subdirectory. For example, you should be able to say: $ edit Makefile ;# top-level $ cd Documentation $ edit git.txt ;# in the subdirectory and then do one of three things, still inside the subdirectory: $ git add -u . ;# add only Documentation/git.txt $ git add -u :/ ;# add everything, including paths outside Documentation $ git add -u ;# whatever the default setting is. To truly support magic pathspec, the API needs to be restructured so that get_pathspec() and init_pathspec() are unified into one call. Currently, the former just prefixes the user supplied pathspec with the current subdirectory path, and the latter takes the output from the former and pre-parses them into a bit richer structure for easier handling. They should become a single API function that takes the current subdirectory path and the remainder of argv[] (after parsing --options and revision arguments from the command line) and returns an array of parsed pathspec elements, and "magic" should become attributes of struct pathspec_item. This patch implements only "top" magic because it can be hacked into the system without such a refactoring. The syntax for magic pathspec prefix is designed to be extensible yet simple to type to invoke a simple magic like "from the top". The parser for the magic prefix is hooked into get_pathspec() function in this patch, and it needs to be moved when we refactor the API. But we have to start from somewhere. Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
a91df69cbb
commit
8a42c98501
|
@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||||
Pattern used to specify paths.
|
Pattern used to specify paths.
|
||||||
+
|
+
|
||||||
Pathspecs are used on the command line of "git ls-files", "git
|
Pathspecs are used on the command line of "git ls-files", "git
|
||||||
ls-tree", "git grep", "git checkout", and many other commands to
|
ls-tree", "git add", "git grep", "git diff", "git checkout",
|
||||||
|
and many other commands to
|
||||||
limit the scope of operations to some subset of the tree or
|
limit the scope of operations to some subset of the tree or
|
||||||
worktree. See the documentation of each command for whether
|
worktree. See the documentation of each command for whether
|
||||||
paths are relative to the current directory or toplevel. The
|
paths are relative to the current directory or toplevel. The
|
||||||
|
@ -296,6 +297,34 @@ For example, Documentation/*.jpg will match all .jpg files
|
||||||
in the Documentation subtree,
|
in the Documentation subtree,
|
||||||
including Documentation/chapter_1/figure_1.jpg.
|
including Documentation/chapter_1/figure_1.jpg.
|
||||||
|
|
||||||
|
+
|
||||||
|
A pathspec that begins with a colon `:` has special meaning. In the
|
||||||
|
short form, the leading colon `:` is followed by zero or more "magic
|
||||||
|
signature" letters (which optionally is terminated by another colon `:`),
|
||||||
|
and the remainder is the pattern to match against the path. The optional
|
||||||
|
colon that terminates the "magic signature" can be omitted if the pattern
|
||||||
|
begins with a character that cannot be a "magic signature" and is not a
|
||||||
|
colon.
|
||||||
|
+
|
||||||
|
In the long form, the leading colon `:` is followed by a open
|
||||||
|
parenthesis `(`, a comma-separated list of zero or more "magic words",
|
||||||
|
and a close parentheses `)`, and the remainder is the pattern to match
|
||||||
|
against the path.
|
||||||
|
+
|
||||||
|
The "magic signature" consists of an ASCII symbol that is not
|
||||||
|
alphanumeric.
|
||||||
|
+
|
||||||
|
--
|
||||||
|
top `/`;;
|
||||||
|
The magic word `top` (mnemonic: `/`) makes the pattern match
|
||||||
|
from the root of the working tree, even when you are running
|
||||||
|
the command from inside a subdirectory.
|
||||||
|
--
|
||||||
|
+
|
||||||
|
Currently only the slash `/` is recognized as the "magic signature",
|
||||||
|
but it is envisioned that we will support more types of magic in later
|
||||||
|
versions of git.
|
||||||
|
|
||||||
[[def_parent]]parent::
|
[[def_parent]]parent::
|
||||||
A <<def_commit_object,commit object>> contains a (possibly empty) list
|
A <<def_commit_object,commit object>> contains a (possibly empty) list
|
||||||
of the logical predecessor(s) in the line of development, i.e. its
|
of the logical predecessor(s) in the line of development, i.e. its
|
||||||
|
|
98
setup.c
98
setup.c
|
@ -126,6 +126,101 @@ void verify_non_filename(const char *prefix, const char *arg)
|
||||||
"Use '--' to separate filenames from revisions", arg);
|
"Use '--' to separate filenames from revisions", arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Magic pathspec
|
||||||
|
*
|
||||||
|
* NEEDSWORK: These need to be moved to dir.h or even to a new
|
||||||
|
* pathspec.h when we restructure get_pathspec() users to use the
|
||||||
|
* "struct pathspec" interface.
|
||||||
|
*
|
||||||
|
* Possible future magic semantics include stuff like:
|
||||||
|
*
|
||||||
|
* { PATHSPEC_NOGLOB, '!', "noglob" },
|
||||||
|
* { PATHSPEC_ICASE, '\0', "icase" },
|
||||||
|
* { PATHSPEC_RECURSIVE, '*', "recursive" },
|
||||||
|
* { PATHSPEC_REGEXP, '\0', "regexp" },
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PATHSPEC_FROMTOP (1<<0)
|
||||||
|
|
||||||
|
struct pathspec_magic {
|
||||||
|
unsigned bit;
|
||||||
|
char mnemonic; /* this cannot be ':'! */
|
||||||
|
const char *name;
|
||||||
|
} pathspec_magic[] = {
|
||||||
|
{ PATHSPEC_FROMTOP, '/', "top" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take an element of a pathspec and check for magic signatures.
|
||||||
|
* Append the result to the prefix.
|
||||||
|
*
|
||||||
|
* For now, we only parse the syntax and throw out anything other than
|
||||||
|
* "top" magic.
|
||||||
|
*
|
||||||
|
* NEEDSWORK: This needs to be rewritten when we start migrating
|
||||||
|
* get_pathspec() users to use the "struct pathspec" interface. For
|
||||||
|
* example, a pathspec element may be marked as case-insensitive, but
|
||||||
|
* the prefix part must always match literally, and a single stupid
|
||||||
|
* string cannot express such a case.
|
||||||
|
*/
|
||||||
|
const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||||
|
{
|
||||||
|
unsigned magic = 0;
|
||||||
|
const char *copyfrom = elt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (elt[0] != ':') {
|
||||||
|
; /* nothing to do */
|
||||||
|
} else if (elt[1] == '(') {
|
||||||
|
/* longhand */
|
||||||
|
const char *nextat;
|
||||||
|
for (copyfrom = elt + 2;
|
||||||
|
*copyfrom && *copyfrom != ')';
|
||||||
|
copyfrom = nextat) {
|
||||||
|
size_t len = strcspn(copyfrom, ",)");
|
||||||
|
if (copyfrom[len] == ')')
|
||||||
|
nextat = copyfrom + len;
|
||||||
|
else
|
||||||
|
nextat = copyfrom + len + 1;
|
||||||
|
if (!len)
|
||||||
|
continue;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||||
|
if (strlen(pathspec_magic[i].name) == len &&
|
||||||
|
!strncmp(pathspec_magic[i].name, copyfrom, len)) {
|
||||||
|
magic |= pathspec_magic[i].bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||||
|
die("Invalid pathspec magic '%.*s' in '%s'",
|
||||||
|
(int) len, copyfrom, elt);
|
||||||
|
}
|
||||||
|
if (*copyfrom == ')')
|
||||||
|
copyfrom++;
|
||||||
|
} else {
|
||||||
|
/* shorthand */
|
||||||
|
for (copyfrom = elt + 1;
|
||||||
|
*copyfrom && *copyfrom != ':';
|
||||||
|
copyfrom++) {
|
||||||
|
char ch = *copyfrom;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||||
|
if (pathspec_magic[i].mnemonic == ch) {
|
||||||
|
magic |= pathspec_magic[i].bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*copyfrom == ':')
|
||||||
|
copyfrom++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magic & PATHSPEC_FROMTOP)
|
||||||
|
return xstrdup(copyfrom);
|
||||||
|
else
|
||||||
|
return prefix_path(prefix, prefixlen, copyfrom);
|
||||||
|
}
|
||||||
|
|
||||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||||
{
|
{
|
||||||
const char *entry = *pathspec;
|
const char *entry = *pathspec;
|
||||||
|
@ -147,8 +242,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||||
dst = pathspec;
|
dst = pathspec;
|
||||||
prefixlen = prefix ? strlen(prefix) : 0;
|
prefixlen = prefix ? strlen(prefix) : 0;
|
||||||
while (*src) {
|
while (*src) {
|
||||||
const char *p = prefix_path(prefix, prefixlen, *src);
|
*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
|
||||||
*(dst++) = p;
|
|
||||||
src++;
|
src++;
|
||||||
}
|
}
|
||||||
*dst = NULL;
|
*dst = NULL;
|
||||||
|
|
Loading…
Reference in New Issue