@matheusbsbs / matheus-bagliano / commits / 8a42c98501
Fork 0

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>

Junio C Hamano committed Apr 6, 2011 at 16:11 UTC 8a42c9850177cc91e9f38779e8aca89682a02975
2 files changed +126 -3
Documentation/glossary-content.txt
+30 -1
@@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a
277 Pattern used to specify paths.
278 +
279 Pathspecs are used on the command line of "git ls-files", "git
280 -ls-tree", "git grep", "git checkout", and many other commands to
280 +ls-tree", "git add", "git grep", "git diff", "git checkout",
281 +and many other commands to
282 limit the scope of operations to some subset of the tree or
283 worktree. See the documentation of each command for whether
284 paths are relative to the current directory or toplevel. The
@@ -296,6 +297,34 @@ For example, Documentation/*.jpg will match all .jpg files
297 in the Documentation subtree,
298 including Documentation/chapter_1/figure_1.jpg.
299
300 ++
301 +A pathspec that begins with a colon `:` has special meaning. In the
302 +short form, the leading colon `:` is followed by zero or more "magic
303 +signature" letters (which optionally is terminated by another colon `:`),
304 +and the remainder is the pattern to match against the path. The optional
305 +colon that terminates the "magic signature" can be omitted if the pattern
306 +begins with a character that cannot be a "magic signature" and is not a
307 +colon.
308 ++
309 +In the long form, the leading colon `:` is followed by a open
310 +parenthesis `(`, a comma-separated list of zero or more "magic words",
311 +and a close parentheses `)`, and the remainder is the pattern to match
312 +against the path.
313 ++
314 +The "magic signature" consists of an ASCII symbol that is not
315 +alphanumeric.
316 ++
317 +--
318 +top `/`;;
319 + The magic word `top` (mnemonic: `/`) makes the pattern match
320 + from the root of the working tree, even when you are running
321 + the command from inside a subdirectory.
322 +--
323 ++
324 +Currently only the slash `/` is recognized as the "magic signature",
325 +but it is envisioned that we will support more types of magic in later
326 +versions of git.
327 +
328 [[def_parent]]parent::
329 A <<def_commit_object,commit object>> contains a (possibly empty) list
330 of the logical predecessor(s) in the line of development, i.e. its
setup.c
+96 -2
@@ -126,6 +126,101 @@ void verify_non_filename(const char *prefix, const char *arg)
126 "Use '--' to separate filenames from revisions", arg);
127 }
128
129 +/*
130 + * Magic pathspec
131 + *
132 + * NEEDSWORK: These need to be moved to dir.h or even to a new
133 + * pathspec.h when we restructure get_pathspec() users to use the
134 + * "struct pathspec" interface.
135 + *
136 + * Possible future magic semantics include stuff like:
137 + *
138 + * { PATHSPEC_NOGLOB, '!', "noglob" },
139 + * { PATHSPEC_ICASE, '\0', "icase" },
140 + * { PATHSPEC_RECURSIVE, '*', "recursive" },
141 + * { PATHSPEC_REGEXP, '\0', "regexp" },
142 + *
143 + */
144 +#define PATHSPEC_FROMTOP (1<<0)
145 +
146 +struct pathspec_magic {
147 + unsigned bit;
148 + char mnemonic; /* this cannot be ':'! */
149 + const char *name;
150 +} pathspec_magic[] = {
151 + { PATHSPEC_FROMTOP, '/', "top" },
152 +};
153 +
154 +/*
155 + * Take an element of a pathspec and check for magic signatures.
156 + * Append the result to the prefix.
157 + *
158 + * For now, we only parse the syntax and throw out anything other than
159 + * "top" magic.
160 + *
161 + * NEEDSWORK: This needs to be rewritten when we start migrating
162 + * get_pathspec() users to use the "struct pathspec" interface. For
163 + * example, a pathspec element may be marked as case-insensitive, but
164 + * the prefix part must always match literally, and a single stupid
165 + * string cannot express such a case.
166 + */
167 +const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
168 +{
169 + unsigned magic = 0;
170 + const char *copyfrom = elt;
171 + int i;
172 +
173 + if (elt[0] != ':') {
174 + ; /* nothing to do */
175 + } else if (elt[1] == '(') {
176 + /* longhand */
177 + const char *nextat;
178 + for (copyfrom = elt + 2;
179 + *copyfrom && *copyfrom != ')';
180 + copyfrom = nextat) {
181 + size_t len = strcspn(copyfrom, ",)");
182 + if (copyfrom[len] == ')')
183 + nextat = copyfrom + len;
184 + else
185 + nextat = copyfrom + len + 1;
186 + if (!len)
187 + continue;
188 + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
189 + if (strlen(pathspec_magic[i].name) == len &&
190 + !strncmp(pathspec_magic[i].name, copyfrom, len)) {
191 + magic |= pathspec_magic[i].bit;
192 + break;
193 + }
194 + if (ARRAY_SIZE(pathspec_magic) <= i)
195 + die("Invalid pathspec magic '%.*s' in '%s'",
196 + (int) len, copyfrom, elt);
197 + }
198 + if (*copyfrom == ')')
199 + copyfrom++;
200 + } else {
201 + /* shorthand */
202 + for (copyfrom = elt + 1;
203 + *copyfrom && *copyfrom != ':';
204 + copyfrom++) {
205 + char ch = *copyfrom;
206 + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
207 + if (pathspec_magic[i].mnemonic == ch) {
208 + magic |= pathspec_magic[i].bit;
209 + break;
210 + }
211 + if (ARRAY_SIZE(pathspec_magic) <= i)
212 + break;
213 + }
214 + if (*copyfrom == ':')
215 + copyfrom++;
216 + }
217 +
218 + if (magic & PATHSPEC_FROMTOP)
219 + return xstrdup(copyfrom);
220 + else
221 + return prefix_path(prefix, prefixlen, copyfrom);
222 +}
223 +
224 const char **get_pathspec(const char *prefix, const char **pathspec)
225 {
226 const char *entry = *pathspec;
@@ -147,8 +242,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
242 dst = pathspec;
243 prefixlen = prefix ? strlen(prefix) : 0;
244 while (*src) {
150 - const char *p = prefix_path(prefix, prefixlen, *src);
151 - *(dst++) = p;
245 + *(dst++) = prefix_pathspec(prefix, prefixlen, *src);
246 src++;
247 }
248 *dst = NULL;