Merge branch 'jc/magic-pathspec' into early parts of jc/add-u-migration

* jc/magic-pathspec:
  magic pathspec: add tentative ":/path/from/top/level" pathspec support
This commit is contained in:
Junio C Hamano
2011-04-06 17:08:59 -07:00
2 changed files with 126 additions and 3 deletions

View File

@@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a
Pattern used to specify paths.
+
Pathspecs are used on the command line of "git ls-files", "git
ls-tree", "git grep", "git checkout", and many other commands to
ls-tree", "git add", "git grep", "git diff", "git checkout",
and many other commands to
limit the scope of operations to some subset of the tree or
worktree. See the documentation of each command for whether
paths are relative to the current directory or toplevel. The
@@ -296,6 +297,34 @@ For example, Documentation/*.jpg will match all .jpg files
in the Documentation subtree,
including Documentation/chapter_1/figure_1.jpg.
+
A pathspec that begins with a colon `:` has special meaning. In the
short form, the leading colon `:` is followed by zero or more "magic
signature" letters (which optionally is terminated by another colon `:`),
and the remainder is the pattern to match against the path. The optional
colon that terminates the "magic signature" can be omitted if the pattern
begins with a character that cannot be a "magic signature" and is not a
colon.
+
In the long form, the leading colon `:` is followed by a open
parenthesis `(`, a comma-separated list of zero or more "magic words",
and a close parentheses `)`, and the remainder is the pattern to match
against the path.
+
The "magic signature" consists of an ASCII symbol that is not
alphanumeric.
+
--
top `/`;;
The magic word `top` (mnemonic: `/`) makes the pattern match
from the root of the working tree, even when you are running
the command from inside a subdirectory.
--
+
Currently only the slash `/` is recognized as the "magic signature",
but it is envisioned that we will support more types of magic in later
versions of git.
[[def_parent]]parent::
A <<def_commit_object,commit object>> contains a (possibly empty) list
of the logical predecessor(s) in the line of development, i.e. its

98
setup.c
View File

@@ -126,6 +126,101 @@ void verify_non_filename(const char *prefix, const char *arg)
"Use '--' to separate filenames from revisions", arg);
}
/*
* Magic pathspec
*
* NEEDSWORK: These need to be moved to dir.h or even to a new
* pathspec.h when we restructure get_pathspec() users to use the
* "struct pathspec" interface.
*
* Possible future magic semantics include stuff like:
*
* { PATHSPEC_NOGLOB, '!', "noglob" },
* { PATHSPEC_ICASE, '\0', "icase" },
* { PATHSPEC_RECURSIVE, '*', "recursive" },
* { PATHSPEC_REGEXP, '\0', "regexp" },
*
*/
#define PATHSPEC_FROMTOP (1<<0)
struct pathspec_magic {
unsigned bit;
char mnemonic; /* this cannot be ':'! */
const char *name;
} pathspec_magic[] = {
{ PATHSPEC_FROMTOP, '/', "top" },
};
/*
* Take an element of a pathspec and check for magic signatures.
* Append the result to the prefix.
*
* For now, we only parse the syntax and throw out anything other than
* "top" magic.
*
* NEEDSWORK: This needs to be rewritten when we start migrating
* get_pathspec() users to use the "struct pathspec" interface. For
* example, a pathspec element may be marked as case-insensitive, but
* the prefix part must always match literally, and a single stupid
* string cannot express such a case.
*/
const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
{
unsigned magic = 0;
const char *copyfrom = elt;
int i;
if (elt[0] != ':') {
; /* nothing to do */
} else if (elt[1] == '(') {
/* longhand */
const char *nextat;
for (copyfrom = elt + 2;
*copyfrom && *copyfrom != ')';
copyfrom = nextat) {
size_t len = strcspn(copyfrom, ",)");
if (copyfrom[len] == ')')
nextat = copyfrom + len;
else
nextat = copyfrom + len + 1;
if (!len)
continue;
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
if (strlen(pathspec_magic[i].name) == len &&
!strncmp(pathspec_magic[i].name, copyfrom, len)) {
magic |= pathspec_magic[i].bit;
break;
}
if (ARRAY_SIZE(pathspec_magic) <= i)
die("Invalid pathspec magic '%.*s' in '%s'",
(int) len, copyfrom, elt);
}
if (*copyfrom == ')')
copyfrom++;
} else {
/* shorthand */
for (copyfrom = elt + 1;
*copyfrom && *copyfrom != ':';
copyfrom++) {
char ch = *copyfrom;
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
if (pathspec_magic[i].mnemonic == ch) {
magic |= pathspec_magic[i].bit;
break;
}
if (ARRAY_SIZE(pathspec_magic) <= i)
break;
}
if (*copyfrom == ':')
copyfrom++;
}
if (magic & PATHSPEC_FROMTOP)
return xstrdup(copyfrom);
else
return prefix_path(prefix, prefixlen, copyfrom);
}
const char **get_pathspec(const char *prefix, const char **pathspec)
{
const char *entry = *pathspec;
@@ -147,8 +242,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
dst = pathspec;
prefixlen = prefix ? strlen(prefix) : 0;
while (*src) {
const char *p = prefix_path(prefix, prefixlen, *src);
*(dst++) = p;
*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
src++;
}
*dst = NULL;