@matheusbsbs / matheus-bagliano / commits / efad1a5615
Fork 0

ls-files: allow relative pathspec

git ls-files used to error out if given paths which point outside the current working directory, such as '../'. We now allow such paths and the output is analogous to git grep -l. Signed-off-by: Clemens Buchacher <drizzd@aon.at> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Clemens Buchacher committed Jun 3, 2010 at 15:39 UTC efad1a5615e80637dcb38c8eaa1e35c03b24169f
2 files changed +45 -42
builtin/ls-files.c
+41 -34
@@ -26,8 +26,9 @@ static int show_killed;
26 static int show_valid_bit;
27 static int line_terminator = '\n';
28
29 +static const char *prefix;
30 +static int max_prefix_len;
31 static int prefix_len;
30 -static int prefix_offset;
32 static const char **pathspec;
33 static int error_unmatch;
34 static char *ps_matched;
@@ -43,10 +44,15 @@ static const char *tag_modified = "";
44 static const char *tag_skip_worktree = "";
45 static const char *tag_resolve_undo = "";
46
47 +static void write_name(const char* name, size_t len)
48 +{
49 + write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
50 + line_terminator);
51 +}
52 +
53 static void show_dir_entry(const char *tag, struct dir_entry *ent)
54 {
48 - int len = prefix_len;
49 - int offset = prefix_offset;
55 + int len = max_prefix_len;
56
57 if (len >= ent->len)
58 die("git ls-files: internal error - directory entry not superset of prefix");
@@ -55,7 +61,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
61 return;
62
63 fputs(tag, stdout);
58 - write_name_quoted(ent->name + offset, stdout, line_terminator);
64 + write_name(ent->name, ent->len);
65 }
66
67 static void show_other_files(struct dir_struct *dir)
@@ -121,8 +127,7 @@ static void show_killed_files(struct dir_struct *dir)
127
128 static void show_ce_entry(const char *tag, struct cache_entry *ce)
129 {
124 - int len = prefix_len;
125 - int offset = prefix_offset;
130 + int len = max_prefix_len;
131
132 if (len >= ce_namelen(ce))
133 die("git ls-files: internal error - cache entry not superset of prefix");
@@ -156,20 +161,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
161 find_unique_abbrev(ce->sha1,abbrev),
162 ce_stage(ce));
163 }
159 - write_name_quoted(ce->name + offset, stdout, line_terminator);
164 + write_name(ce->name, ce_namelen(ce));
165 }
166
167 static int show_one_ru(struct string_list_item *item, void *cbdata)
168 {
164 - int offset = prefix_offset;
169 const char *path = item->string;
170 struct resolve_undo_info *ui = item->util;
171 int i, len;
172
173 len = strlen(path);
170 - if (len < prefix_len)
174 + if (len < max_prefix_len)
175 return 0; /* outside of the prefix */
172 - if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
176 + if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
177 return 0; /* uninterested */
178 for (i = 0; i < 3; i++) {
179 if (!ui->mode[i])
@@ -177,19 +181,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)
181 printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
182 find_unique_abbrev(ui->sha1[i], abbrev),
183 i + 1);
180 - write_name_quoted(path + offset, stdout, line_terminator);
184 + write_name(path, len);
185 }
186 return 0;
187 }
188
185 -static void show_ru_info(const char *prefix)
189 +static void show_ru_info(void)
190 {
191 if (!the_index.resolve_undo)
192 return;
193 for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
194 }
195
192 -static void show_files(struct dir_struct *dir, const char *prefix)
196 +static void show_files(struct dir_struct *dir)
197 {
198 int i;
199
@@ -243,7 +247,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
247 */
248 static void prune_cache(const char *prefix)
249 {
246 - int pos = cache_name_pos(prefix, prefix_len);
250 + int pos = cache_name_pos(prefix, max_prefix_len);
251 unsigned int first, last;
252
253 if (pos < 0)
@@ -256,7 +260,7 @@ static void prune_cache(const char *prefix)
260 while (last > first) {
261 int next = (last + first) >> 1;
262 struct cache_entry *ce = active_cache[next];
259 - if (!strncmp(ce->name, prefix, prefix_len)) {
263 + if (!strncmp(ce->name, prefix, max_prefix_len)) {
264 first = next+1;
265 continue;
266 }
@@ -265,11 +269,16 @@ static void prune_cache(const char *prefix)
269 active_nr = last;
270 }
271
268 -static const char *verify_pathspec(const char *prefix)
272 +static const char *pathspec_prefix(const char *prefix)
273 {
274 const char **p, *n, *prev;
275 unsigned long max;
276
277 + if (!pathspec) {
278 + max_prefix_len = prefix ? strlen(prefix) : 0;
279 + return prefix;
280 + }
281 +
282 prev = NULL;
283 max = PATH_MAX;
284 for (p = pathspec; (n = *p) != NULL; p++) {
@@ -291,10 +300,7 @@ static const char *verify_pathspec(const char *prefix)
300 }
301 }
302
294 - if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
295 - die("git ls-files: cannot generate relative filenames containing '..'");
296 -
297 - prefix_len = max;
303 + max_prefix_len = max;
304 return max ? xmemdupz(prev, max) : NULL;
305 }
306
@@ -374,7 +380,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
380 }
381 }
382
377 -int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
383 +int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
384 {
385 /*
386 * Make sure all pathspec matched; otherwise it is an error.
@@ -404,7 +410,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
410 continue;
411
412 error("pathspec '%s' did not match any file(s) known to git.",
407 - pathspec[num] + prefix_offset);
413 + pathspec[num] + prefix_len);
414 errors++;
415 }
416 return errors;
@@ -456,9 +462,10 @@ static int option_parse_exclude_standard(const struct option *opt,
462 return 0;
463 }
464
459 -int cmd_ls_files(int argc, const char **argv, const char *prefix)
465 +int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
466 {
467 int require_work_tree = 0, show_tag = 0;
468 + const char *max_prefix;
469 struct dir_struct dir;
470 struct option builtin_ls_files_options[] = {
471 { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -504,7 +511,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
511 { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
512 "add the standard git exclusions",
513 PARSE_OPT_NOARG, option_parse_exclude_standard },
507 - { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
514 + { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
515 "make the output relative to the project top directory",
516 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
517 OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
@@ -516,8 +523,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
523 };
524
525 memset(&dir, 0, sizeof(dir));
526 + prefix = cmd_prefix;
527 if (prefix)
520 - prefix_offset = strlen(prefix);
528 + prefix_len = strlen(prefix);
529 git_config(git_default_config, NULL);
530
531 if (read_cache() < 0)
@@ -555,9 +563,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
563 if (pathspec)
564 strip_trailing_slash_from_submodules();
565
558 - /* Verify that the pathspec matches the prefix */
559 - if (pathspec)
560 - prefix = verify_pathspec(prefix);
566 + /* Find common prefix for all pathspec's */
567 + max_prefix = pathspec_prefix(prefix);
568
569 /* Treat unmatching pathspec elements as errors */
570 if (pathspec && error_unmatch) {
@@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
582 show_killed | show_modified | show_resolve_undo))
583 show_cached = 1;
584
578 - if (prefix)
579 - prune_cache(prefix);
585 + if (max_prefix)
586 + prune_cache(max_prefix);
587 if (with_tree) {
588 /*
589 * Basic sanity check; show-stages and show-unmerged
@@ -584,15 +591,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
591 */
592 if (show_stage || show_unmerged)
593 die("ls-files --with-tree is incompatible with -s or -u");
587 - overlay_tree_on_cache(with_tree, prefix);
594 + overlay_tree_on_cache(with_tree, max_prefix);
595 }
589 - show_files(&dir, prefix);
596 + show_files(&dir);
597 if (show_resolve_undo)
591 - show_ru_info(prefix);
598 + show_ru_info();
599
600 if (ps_matched) {
601 int bad;
595 - bad = report_path_error(ps_matched, pathspec, prefix_offset);
602 + bad = report_path_error(ps_matched, pathspec, prefix_len);
603 if (bad)
604 fprintf(stderr, "Did you forget to 'git add'?\n");
605
t/t7010-setup.sh
+4 -8
@@ -103,14 +103,10 @@ test_expect_success 'git ls-files (relative #3)' '
103 git add a &&
104 (
105 cd a/b &&
106 - if git ls-files "../e/f"
107 - then
108 - echo Gaah, should have failed
109 - exit 1
110 - else
111 - : happy
112 - fi
113 - )
106 + git ls-files "../e/f"
107 + ) >current &&
108 + echo ../e/f >expect &&
109 + test_cmp expect current
110
111 '
112