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