ls-files: optionally recurse into submodules
Allow ls-files to recognize submodules in order to retrieve a list of files from a repository's submodules. This is done by forking off a process to recursively call ls-files on all submodules. Use top-level --super-prefix option to pass a path to the submodule which it can use to prepend to output or pathspec matching logic. Signed-off-by: Brandon Williams <bmwill@google.com> Reviewed-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
74866d7579
commit
e77aa336f1
@ -14,6 +14,7 @@
|
||||
#include "resolve-undo.h"
|
||||
#include "string-list.h"
|
||||
#include "pathspec.h"
|
||||
#include "run-command.h"
|
||||
|
||||
static int abbrev;
|
||||
static int show_deleted;
|
||||
@ -28,8 +29,10 @@ static int show_valid_bit;
|
||||
static int line_terminator = '\n';
|
||||
static int debug_mode;
|
||||
static int show_eol;
|
||||
static int recurse_submodules;
|
||||
|
||||
static const char *prefix;
|
||||
static const char *super_prefix;
|
||||
static int max_prefix_len;
|
||||
static int prefix_len;
|
||||
static struct pathspec pathspec;
|
||||
@ -67,12 +70,25 @@ static void write_eolinfo(const struct cache_entry *ce, const char *path)
|
||||
|
||||
static void write_name(const char *name)
|
||||
{
|
||||
/*
|
||||
* Prepend the super_prefix to name to construct the full_name to be
|
||||
* written.
|
||||
*/
|
||||
struct strbuf full_name = STRBUF_INIT;
|
||||
if (super_prefix) {
|
||||
strbuf_addstr(&full_name, super_prefix);
|
||||
strbuf_addstr(&full_name, name);
|
||||
name = full_name.buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* With "--full-name", prefix_len=0; this caller needs to pass
|
||||
* an empty string in that case (a NULL is good for "").
|
||||
*/
|
||||
write_name_quoted_relative(name, prefix_len ? prefix : NULL,
|
||||
stdout, line_terminator);
|
||||
|
||||
strbuf_release(&full_name);
|
||||
}
|
||||
|
||||
static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||
@ -152,55 +168,84 @@ static void show_killed_files(struct dir_struct *dir)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively call ls-files on a submodule
|
||||
*/
|
||||
static void show_gitlink(const struct cache_entry *ce)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
int status;
|
||||
|
||||
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
|
||||
super_prefix ? super_prefix : "",
|
||||
ce->name);
|
||||
argv_array_push(&cp.args, "ls-files");
|
||||
argv_array_push(&cp.args, "--recurse-submodules");
|
||||
|
||||
cp.git_cmd = 1;
|
||||
cp.dir = ce->name;
|
||||
status = run_command(&cp);
|
||||
if (status)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void show_ce_entry(const char *tag, const struct cache_entry *ce)
|
||||
{
|
||||
struct strbuf name = STRBUF_INIT;
|
||||
int len = max_prefix_len;
|
||||
if (super_prefix)
|
||||
strbuf_addstr(&name, super_prefix);
|
||||
strbuf_addstr(&name, ce->name);
|
||||
|
||||
if (len >= ce_namelen(ce))
|
||||
die("git ls-files: internal error - cache entry not superset of prefix");
|
||||
|
||||
if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
|
||||
len, ps_matched,
|
||||
S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
|
||||
return;
|
||||
|
||||
if (tag && *tag && show_valid_bit &&
|
||||
(ce->ce_flags & CE_VALID)) {
|
||||
static char alttag[4];
|
||||
memcpy(alttag, tag, 3);
|
||||
if (isalpha(tag[0]))
|
||||
alttag[0] = tolower(tag[0]);
|
||||
else if (tag[0] == '?')
|
||||
alttag[0] = '!';
|
||||
else {
|
||||
alttag[0] = 'v';
|
||||
alttag[1] = tag[0];
|
||||
alttag[2] = ' ';
|
||||
alttag[3] = 0;
|
||||
if (recurse_submodules && S_ISGITLINK(ce->ce_mode)) {
|
||||
show_gitlink(ce);
|
||||
} else if (match_pathspec(&pathspec, name.buf, name.len,
|
||||
len, ps_matched,
|
||||
S_ISDIR(ce->ce_mode) ||
|
||||
S_ISGITLINK(ce->ce_mode))) {
|
||||
if (tag && *tag && show_valid_bit &&
|
||||
(ce->ce_flags & CE_VALID)) {
|
||||
static char alttag[4];
|
||||
memcpy(alttag, tag, 3);
|
||||
if (isalpha(tag[0]))
|
||||
alttag[0] = tolower(tag[0]);
|
||||
else if (tag[0] == '?')
|
||||
alttag[0] = '!';
|
||||
else {
|
||||
alttag[0] = 'v';
|
||||
alttag[1] = tag[0];
|
||||
alttag[2] = ' ';
|
||||
alttag[3] = 0;
|
||||
}
|
||||
tag = alttag;
|
||||
}
|
||||
|
||||
if (!show_stage) {
|
||||
fputs(tag, stdout);
|
||||
} else {
|
||||
printf("%s%06o %s %d\t",
|
||||
tag,
|
||||
ce->ce_mode,
|
||||
find_unique_abbrev(ce->sha1,abbrev),
|
||||
ce_stage(ce));
|
||||
}
|
||||
write_eolinfo(ce, ce->name);
|
||||
write_name(ce->name);
|
||||
if (debug_mode) {
|
||||
const struct stat_data *sd = &ce->ce_stat_data;
|
||||
|
||||
printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
|
||||
printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
|
||||
printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
|
||||
printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
|
||||
printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
|
||||
}
|
||||
tag = alttag;
|
||||
}
|
||||
|
||||
if (!show_stage) {
|
||||
fputs(tag, stdout);
|
||||
} else {
|
||||
printf("%s%06o %s %d\t",
|
||||
tag,
|
||||
ce->ce_mode,
|
||||
find_unique_abbrev(ce->sha1,abbrev),
|
||||
ce_stage(ce));
|
||||
}
|
||||
write_eolinfo(ce, ce->name);
|
||||
write_name(ce->name);
|
||||
if (debug_mode) {
|
||||
const struct stat_data *sd = &ce->ce_stat_data;
|
||||
|
||||
printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
|
||||
printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
|
||||
printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
|
||||
printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
|
||||
printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
|
||||
}
|
||||
strbuf_release(&name);
|
||||
}
|
||||
|
||||
static void show_ru_info(void)
|
||||
@ -468,6 +513,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
{ OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
|
||||
N_("make the output relative to the project top directory"),
|
||||
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
|
||||
OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
|
||||
N_("recurse through submodules")),
|
||||
OPT_BOOL(0, "error-unmatch", &error_unmatch,
|
||||
N_("if any <file> is not in the index, treat this as an error")),
|
||||
OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
|
||||
@ -484,6 +531,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
prefix = cmd_prefix;
|
||||
if (prefix)
|
||||
prefix_len = strlen(prefix);
|
||||
super_prefix = get_super_prefix();
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
if (read_cache() < 0)
|
||||
@ -519,6 +567,20 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
if (require_work_tree && !is_inside_work_tree())
|
||||
setup_work_tree();
|
||||
|
||||
if (recurse_submodules &&
|
||||
(show_stage || show_deleted || show_others || show_unmerged ||
|
||||
show_killed || show_modified || show_resolve_undo ||
|
||||
show_valid_bit || show_tag || show_eol || with_tree ||
|
||||
(line_terminator == '\0')))
|
||||
die("ls-files --recurse-submodules unsupported mode");
|
||||
|
||||
if (recurse_submodules && error_unmatch)
|
||||
die("ls-files --recurse-submodules does not support "
|
||||
"--error-unmatch");
|
||||
|
||||
if (recurse_submodules && argc)
|
||||
die("ls-files --recurse-submodules does not support pathspec");
|
||||
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_CWD |
|
||||
PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
|
||||
|
||||
Reference in New Issue
Block a user