ls-files: use repository object

Convert ls-files to use a repository struct and recurse submodules inprocess. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Brandon Williams committed Jun 22, 2017 at 11:43 UTC 188dce131fa95d85ddc024a1bc7d2b7fc5da4424
3 files changed +118 -115
builtin/ls-files.c
+78 -114
@@ -5,7 +5,9 @@
5 *
6 * Copyright (C) Linus Torvalds, 2005
7 */
8 +#define NO_THE_INDEX_COMPATIBILITY_MACROS
9 #include "cache.h"
10 +#include "repository.h"
11 #include "config.h"
12 #include "quote.h"
13 #include "dir.h"
@@ -32,10 +34,8 @@ static int line_terminator = '\n';
34 static int debug_mode;
35 static int show_eol;
36 static int recurse_submodules;
35 -static struct argv_array submodule_options = ARGV_ARRAY_INIT;
37
38 static const char *prefix;
38 -static const char *super_prefix;
39 static int max_prefix_len;
40 static int prefix_len;
41 static struct pathspec pathspec;
@@ -73,25 +73,12 @@ static void write_eolinfo(const struct index_state *istate,
73
74 static void write_name(const char *name)
75 {
76 - /*
77 - * Prepend the super_prefix to name to construct the full_name to be
78 - * written.
79 - */
80 - struct strbuf full_name = STRBUF_INIT;
81 - if (super_prefix) {
82 - strbuf_addstr(&full_name, super_prefix);
83 - strbuf_addstr(&full_name, name);
84 - name = full_name.buf;
85 - }
86 -
76 /*
77 * With "--full-name", prefix_len=0; this caller needs to pass
78 * an empty string in that case (a NULL is good for "").
79 */
80 write_name_quoted_relative(name, prefix_len ? prefix : NULL,
81 stdout, line_terminator);
93 -
94 - strbuf_release(&full_name);
82 }
83
84 static const char *get_tag(const struct cache_entry *ce, const char *tag)
@@ -210,83 +197,38 @@ static void show_killed_files(const struct index_state *istate,
197 }
198 }
199
213 -/*
214 - * Compile an argv_array with all of the options supported by --recurse_submodules
215 - */
216 -static void compile_submodule_options(const char **argv,
217 - const struct dir_struct *dir,
218 - int show_tag)
219 -{
220 - if (line_terminator == '\0')
221 - argv_array_push(&submodule_options, "-z");
222 - if (show_tag)
223 - argv_array_push(&submodule_options, "-t");
224 - if (show_valid_bit)
225 - argv_array_push(&submodule_options, "-v");
226 - if (show_cached)
227 - argv_array_push(&submodule_options, "--cached");
228 - if (show_eol)
229 - argv_array_push(&submodule_options, "--eol");
230 - if (debug_mode)
231 - argv_array_push(&submodule_options, "--debug");
232 -
233 - /* Add Pathspecs */
234 - argv_array_push(&submodule_options, "--");
235 - for (; *argv; argv++)
236 - argv_array_push(&submodule_options, *argv);
237 -}
200 +static void show_files(struct repository *repo, struct dir_struct *dir);
201
239 -/**
240 - * Recursively call ls-files on a submodule
241 - */
242 -static void show_gitlink(const struct cache_entry *ce)
202 +static void show_submodule(struct repository *superproject,
203 + struct dir_struct *dir, const char *path)
204 {
244 - struct child_process cp = CHILD_PROCESS_INIT;
245 - int status;
246 - char *dir;
247 -
248 - prepare_submodule_repo_env(&cp.env_array);
249 - argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
250 -
251 - if (prefix_len)
252 - argv_array_pushf(&cp.env_array, "%s=%s",
253 - GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
254 - prefix);
255 - argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
256 - super_prefix ? super_prefix : "",
257 - ce->name);
258 - argv_array_push(&cp.args, "ls-files");
259 - argv_array_push(&cp.args, "--recurse-submodules");
260 -
261 - /* add supported options */
262 - argv_array_pushv(&cp.args, submodule_options.argv);
263 -
264 - cp.git_cmd = 1;
265 - dir = mkpathdup("%s/%s", get_git_work_tree(), ce->name);
266 - cp.dir = dir;
267 - status = run_command(&cp);
268 - free(dir);
269 - if (status)
270 - exit(status);
205 + struct repository submodule;
206 +
207 + if (repo_submodule_init(&submodule, superproject, path))
208 + return;
209 +
210 + if (repo_read_index(&submodule) < 0)
211 + die("index file corrupt");
212 +
213 + repo_read_gitmodules(&submodule);
214 +
215 + show_files(&submodule, dir);
216 +
217 + repo_clear(&submodule);
218 }
219
273 -static void show_ce_entry(const struct index_state *istate,
274 - const char *tag, const struct cache_entry *ce)
220 +static void show_ce(struct repository *repo, struct dir_struct *dir,
221 + const struct cache_entry *ce, const char *fullname,
222 + const char *tag)
223 {
276 - struct strbuf name = STRBUF_INIT;
277 - int len = max_prefix_len;
278 - if (super_prefix)
279 - strbuf_addstr(&name, super_prefix);
280 - strbuf_addstr(&name, ce->name);
281 -
282 - if (len > ce_namelen(ce))
224 + if (max_prefix_len > strlen(fullname))
225 die("git ls-files: internal error - cache entry not superset of prefix");
226
227 if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
286 - submodule_path_match(&pathspec, name.buf, ps_matched)) {
287 - show_gitlink(ce);
288 - } else if (match_pathspec(&pathspec, name.buf, name.len,
289 - len, ps_matched,
228 + is_submodule_active(repo, ce->name)) {
229 + show_submodule(repo, dir, ce->name);
230 + } else if (match_pathspec(&pathspec, fullname, strlen(fullname),
231 + max_prefix_len, ps_matched,
232 S_ISDIR(ce->ce_mode) ||
233 S_ISGITLINK(ce->ce_mode))) {
234 tag = get_tag(ce, tag);
@@ -300,12 +242,10 @@ static void show_ce_entry(const struct index_state *istate,
242 find_unique_abbrev(ce->oid.hash, abbrev),
243 ce_stage(ce));
244 }
303 - write_eolinfo(istate, ce, ce->name);
304 - write_name(ce->name);
245 + write_eolinfo(repo->index, ce, fullname);
246 + write_name(fullname);
247 print_debug(ce);
248 }
307 -
308 - strbuf_release(&name);
249 }
250
251 static void show_ru_info(const struct index_state *istate)
@@ -338,59 +278,79 @@ static void show_ru_info(const struct index_state *istate)
278 }
279
280 static int ce_excluded(struct dir_struct *dir, struct index_state *istate,
341 - const struct cache_entry *ce)
281 + const char *fullname, const struct cache_entry *ce)
282 {
283 int dtype = ce_to_dtype(ce);
344 - return is_excluded(dir, istate, ce->name, &dtype);
284 + return is_excluded(dir, istate, fullname, &dtype);
285 +}
286 +
287 +static void construct_fullname(struct strbuf *out, const struct repository *repo,
288 + const struct cache_entry *ce)
289 +{
290 + strbuf_reset(out);
291 + if (repo->submodule_prefix)
292 + strbuf_addstr(out, repo->submodule_prefix);
293 + strbuf_addstr(out, ce->name);
294 }
295
347 -static void show_files(struct index_state *istate, struct dir_struct *dir)
296 +static void show_files(struct repository *repo, struct dir_struct *dir)
297 {
298 int i;
299 + struct strbuf fullname = STRBUF_INIT;
300
301 /* For cached/deleted files we don't need to even do the readdir */
302 if (show_others || show_killed) {
303 if (!show_others)
304 dir->flags |= DIR_COLLECT_KILLED_ONLY;
355 - fill_directory(dir, istate, &pathspec);
305 + fill_directory(dir, repo->index, &pathspec);
306 if (show_others)
357 - show_other_files(istate, dir);
307 + show_other_files(repo->index, dir);
308 if (show_killed)
359 - show_killed_files(istate, dir);
309 + show_killed_files(repo->index, dir);
310 }
311 if (show_cached || show_stage) {
362 - for (i = 0; i < istate->cache_nr; i++) {
363 - const struct cache_entry *ce = istate->cache[i];
312 + for (i = 0; i < repo->index->cache_nr; i++) {
313 + const struct cache_entry *ce = repo->index->cache[i];
314 +
315 + construct_fullname(&fullname, repo, ce);
316 +
317 if ((dir->flags & DIR_SHOW_IGNORED) &&
365 - !ce_excluded(dir, istate, ce))
318 + !ce_excluded(dir, repo->index, fullname.buf, ce))
319 continue;
320 if (show_unmerged && !ce_stage(ce))
321 continue;
322 if (ce->ce_flags & CE_UPDATE)
323 continue;
371 - show_ce_entry(istate, ce_stage(ce) ? tag_unmerged :
372 - (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
324 + show_ce(repo, dir, ce, fullname.buf,
325 + ce_stage(ce) ? tag_unmerged :
326 + (ce_skip_worktree(ce) ? tag_skip_worktree :
327 + tag_cached));
328 }
329 }
330 if (show_deleted || show_modified) {
376 - for (i = 0; i < istate->cache_nr; i++) {
377 - const struct cache_entry *ce = istate->cache[i];
331 + for (i = 0; i < repo->index->cache_nr; i++) {
332 + const struct cache_entry *ce = repo->index->cache[i];
333 struct stat st;
334 int err;
335 +
336 + construct_fullname(&fullname, repo, ce);
337 +
338 if ((dir->flags & DIR_SHOW_IGNORED) &&
381 - !ce_excluded(dir, istate, ce))
339 + !ce_excluded(dir, repo->index, fullname.buf, ce))
340 continue;
341 if (ce->ce_flags & CE_UPDATE)
342 continue;
343 if (ce_skip_worktree(ce))
344 continue;
387 - err = lstat(ce->name, &st);
345 + err = lstat(fullname.buf, &st);
346 if (show_deleted && err)
389 - show_ce_entry(istate, tag_removed, ce);
390 - if (show_modified && ie_modified(istate, ce, &st, 0))
391 - show_ce_entry(istate, tag_modified, ce);
347 + show_ce(repo, dir, ce, fullname.buf, tag_removed);
348 + if (show_modified && ie_modified(repo->index, ce, &st, 0))
349 + show_ce(repo, dir, ce, fullname.buf, tag_modified);
350 }
351 }
352 +
353 + strbuf_release(&fullname);
354 }
355
356 /*
@@ -615,10 +575,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
575 prefix = cmd_prefix;
576 if (prefix)
577 prefix_len = strlen(prefix);
618 - super_prefix = get_super_prefix();
578 git_config(git_default_config, NULL);
579
621 - if (read_cache() < 0)
580 + if (repo_read_index(the_repository) < 0)
581 die("index file corrupt");
582
583 argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
@@ -652,7 +611,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
611 setup_work_tree();
612
613 if (recurse_submodules)
655 - compile_submodule_options(argv, &dir, show_tag);
614 + repo_read_gitmodules(the_repository);
615
616 if (recurse_submodules &&
617 (show_stage || show_deleted || show_others || show_unmerged ||
@@ -670,7 +629,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
629 /*
630 * Find common prefix for all pathspec's
631 * This is used as a performance optimization which unfortunately cannot
673 - * be done when recursing into submodules
632 + * be done when recursing into submodules because when a pathspec is
633 + * given which spans repository boundaries you can't simply remove the
634 + * submodule entry because the pathspec may match something inside the
635 + * submodule.
636 */
637 if (recurse_submodules)
638 max_prefix = NULL;
@@ -678,7 +640,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
640 max_prefix = common_prefix(&pathspec);
641 max_prefix_len = get_common_prefix_len(max_prefix);
642
681 - prune_index(&the_index, max_prefix, max_prefix_len);
643 + prune_index(the_repository->index, max_prefix, max_prefix_len);
644
645 /* Treat unmatching pathspec elements as errors */
646 if (pathspec.nr && error_unmatch)
@@ -699,11 +661,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
661 */
662 if (show_stage || show_unmerged)
663 die("ls-files --with-tree is incompatible with -s or -u");
702 - overlay_tree_on_index(&the_index, with_tree, max_prefix);
664 + overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
665 }
704 - show_files(&the_index, &dir);
666 +
667 + show_files(the_repository, &dir);
668 +
669 if (show_resolve_undo)
706 - show_ru_info(&the_index);
670 + show_ru_info(the_repository->index);
671
672 if (ps_matched) {
673 int bad;
git.c
+1 -1
@@ -400,7 +400,7 @@ static struct cmd_struct commands[] = {
400 { "init-db", cmd_init_db },
401 { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
402 { "log", cmd_log, RUN_SETUP },
403 - { "ls-files", cmd_ls_files, RUN_SETUP | SUPPORT_SUPER_PREFIX },
403 + { "ls-files", cmd_ls_files, RUN_SETUP },
404 { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
405 { "ls-tree", cmd_ls_tree, RUN_SETUP },
406 { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
t/t3007-ls-files-recurse-submodules.sh
+39
@@ -135,6 +135,45 @@ test_expect_success '--recurse-submodules and pathspecs setup' '
135 test_cmp expect actual
136 '
137
138 +test_expect_success 'inactive submodule' '
139 + test_when_finished "git config --bool submodule.submodule.active true" &&
140 + test_when_finished "git -C submodule config --bool submodule.subsub.active true" &&
141 + git config --bool submodule.submodule.active "false" &&
142 +
143 + cat >expect <<-\EOF &&
144 + .gitmodules
145 + a
146 + b/b
147 + h.txt
148 + sib/file
149 + sub/file
150 + submodule
151 + EOF
152 +
153 + git ls-files --recurse-submodules >actual &&
154 + test_cmp expect actual &&
155 +
156 + git config --bool submodule.submodule.active "true" &&
157 + git -C submodule config --bool submodule.subsub.active "false" &&
158 +
159 + cat >expect <<-\EOF &&
160 + .gitmodules
161 + a
162 + b/b
163 + h.txt
164 + sib/file
165 + sub/file
166 + submodule/.gitmodules
167 + submodule/c
168 + submodule/f.TXT
169 + submodule/g.txt
170 + submodule/subsub
171 + EOF
172 +
173 + git ls-files --recurse-submodules >actual &&
174 + test_cmp expect actual
175 +'
176 +
177 test_expect_success '--recurse-submodules and pathspecs' '
178 cat >expect <<-\EOF &&
179 h.txt