parse_pathspec: preserve prefix length via PATHSPEC_PREFIX_ORIGIN

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 <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Jul 14, 2013 at 15:36 UTC 233c3e6c59945650e5919893a4f090824f06e889
1 file changed +36 -5
pathspec.c
+36 -5
@@ -92,9 +92,9 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
92 const char *elt)
93 {
94 unsigned magic = 0, short_magic = 0;
95 - const char *copyfrom = elt;
95 + const char *copyfrom = elt, *long_magic_end = NULL;
96 char *match;
97 - int i;
97 + int i, pathspec_prefix = -1;
98
99 if (elt[0] != ':') {
100 ; /* nothing to do */
@@ -112,18 +112,29 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
112 nextat = copyfrom + len;
113 if (!len)
114 continue;
115 - for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
115 + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
116 if (strlen(pathspec_magic[i].name) == len &&
117 !strncmp(pathspec_magic[i].name, copyfrom, len)) {
118 magic |= pathspec_magic[i].bit;
119 break;
120 }
121 + if (!prefixcmp(copyfrom, "prefix:")) {
122 + char *endptr;
123 + pathspec_prefix = strtol(copyfrom + 7,
124 + &endptr, 10);
125 + if (endptr - copyfrom != len)
126 + die(_("invalid parameter for pathspec magic 'prefix'"));
127 + /* "i" would be wrong, but it does not matter */
128 + break;
129 + }
130 + }
131 if (ARRAY_SIZE(pathspec_magic) <= i)
132 die(_("Invalid pathspec magic '%.*s' in '%s'"),
133 (int) len, copyfrom, elt);
134 }
135 if (*copyfrom != ')')
136 die(_("Missing ')' at the end of pathspec magic in '%s'"), elt);
137 + long_magic_end = copyfrom;
138 copyfrom++;
139 } else {
140 /* shorthand */
@@ -150,7 +161,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
161 magic |= short_magic;
162 *p_short_magic = short_magic;
163
153 - if (magic & PATHSPEC_FROMTOP) {
164 + if (pathspec_prefix >= 0 &&
165 + (prefixlen || (prefix && *prefix)))
166 + die("BUG: 'prefix' magic is supposed to be used at worktree's root");
167 +
168 + if (pathspec_prefix >= 0) {
169 + match = xstrdup(copyfrom);
170 + prefixlen = pathspec_prefix;
171 + } else if (magic & PATHSPEC_FROMTOP) {
172 match = xstrdup(copyfrom);
173 prefixlen = 0;
174 } else {
@@ -165,7 +183,20 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
183 */
184 if (flags & PATHSPEC_PREFIX_ORIGIN) {
185 struct strbuf sb = STRBUF_INIT;
168 - strbuf_add(&sb, elt, copyfrom - elt);
186 + const char *start = elt;
187 + if (prefixlen && !limit_pathspec_to_literal()) {
188 + /* Preserve the actual prefix length of each pattern */
189 + if (long_magic_end) {
190 + strbuf_add(&sb, start, long_magic_end - start);
191 + strbuf_addf(&sb, ",prefix:%d", prefixlen);
192 + start = long_magic_end;
193 + } else {
194 + if (*start == ':')
195 + start++;
196 + strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
197 + }
198 + }
199 + strbuf_add(&sb, start, copyfrom - start);
200 strbuf_addstr(&sb, match);
201 item->original = strbuf_detach(&sb, NULL);
202 } else