summaryrefslogtreecommitdiff
path: root/builtin/ls-files.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/ls-files.c')
-rw-r--r--builtin/ls-files.c285
1 files changed, 130 insertions, 155 deletions
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 7cff175745..f02e3d23bb 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -13,6 +13,7 @@
#include "parse-options.h"
#include "resolve-undo.h"
#include "string-list.h"
+#include "pathspec.h"
static int abbrev;
static int show_deleted;
@@ -26,15 +27,17 @@ static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
static int debug_mode;
+static int show_eol;
static const char *prefix;
static int max_prefix_len;
static int prefix_len;
-static const char **pathspec;
+static struct pathspec pathspec;
static int error_unmatch;
static char *ps_matched;
static const char *with_tree;
static int exc_given;
+static int exclude_args;
static const char *tag_cached = "";
static const char *tag_unmerged = "";
@@ -45,10 +48,31 @@ static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
-static void write_name(const char* name, size_t len)
+static void write_eolinfo(const struct cache_entry *ce, const char *path)
{
- write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
- line_terminator);
+ if (!show_eol)
+ return;
+ else {
+ struct stat st;
+ const char *i_txt = "";
+ const char *w_txt = "";
+ const char *a_txt = get_convert_attr_ascii(path);
+ if (ce && S_ISREG(ce->ce_mode))
+ i_txt = get_cached_convert_stats_ascii(ce->name);
+ if (!lstat(path, &st) && S_ISREG(st.st_mode))
+ w_txt = get_wt_convert_stats_ascii(path);
+ printf("i/%-5s w/%-5s attr/%-17s\t", i_txt, w_txt, a_txt);
+ }
+}
+
+static void write_name(const char *name)
+{
+ /*
+ * 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);
}
static void show_dir_entry(const char *tag, struct dir_entry *ent)
@@ -58,11 +82,12 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
- if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
+ if (!dir_path_match(ent, &pathspec, len, ps_matched))
return;
fputs(tag, stdout);
- write_name(ent->name, ent->len);
+ write_eolinfo(NULL, ent->name);
+ write_name(ent->name);
}
static void show_other_files(struct dir_struct *dir)
@@ -126,14 +151,16 @@ static void show_killed_files(struct dir_struct *dir)
}
}
-static void show_ce_entry(const char *tag, struct cache_entry *ce)
+static void show_ce_entry(const char *tag, const struct cache_entry *ce)
{
int len = max_prefix_len;
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))
+ 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 &&
@@ -162,13 +189,16 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
find_unique_abbrev(ce->sha1,abbrev),
ce_stage(ce));
}
- write_name(ce->name, ce_namelen(ce));
+ write_eolinfo(ce, ce->name);
+ write_name(ce->name);
if (debug_mode) {
- printf(" ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
- printf(" mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
- printf(" dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
- printf(" uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
- printf(" size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+ 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);
}
}
@@ -187,7 +217,8 @@ static void show_ru_info(void)
len = strlen(path);
if (len < max_prefix_len)
continue; /* outside of the prefix */
- if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+ if (!match_pathspec(&pathspec, path, len,
+ max_prefix_len, ps_matched, 0))
continue; /* uninterested */
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
@@ -195,29 +226,36 @@ static void show_ru_info(void)
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
find_unique_abbrev(ui->sha1[i], abbrev),
i + 1);
- write_name(path, len);
+ write_name(path);
}
}
}
+static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce)
+{
+ int dtype = ce_to_dtype(ce);
+ return is_excluded(dir, ce->name, &dtype);
+}
+
static void show_files(struct dir_struct *dir)
{
int i;
/* For cached/deleted files we don't need to even do the readdir */
if (show_others || show_killed) {
- fill_directory(dir, pathspec);
+ if (!show_others)
+ dir->flags |= DIR_COLLECT_KILLED_ONLY;
+ fill_directory(dir, &pathspec);
if (show_others)
show_other_files(dir);
if (show_killed)
show_killed_files(dir);
}
- if (show_cached | show_stage) {
+ if (show_cached || show_stage) {
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- int dtype = ce_to_dtype(ce);
- if (dir->flags & DIR_SHOW_IGNORED &&
- !excluded(dir, ce->name, &dtype))
+ const struct cache_entry *ce = active_cache[i];
+ if ((dir->flags & DIR_SHOW_IGNORED) &&
+ !ce_excluded(dir, ce))
continue;
if (show_unmerged && !ce_stage(ce))
continue;
@@ -227,14 +265,13 @@ static void show_files(struct dir_struct *dir)
(ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
}
}
- if (show_deleted | show_modified) {
+ if (show_deleted || show_modified) {
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
- int dtype = ce_to_dtype(ce);
- if (dir->flags & DIR_SHOW_IGNORED &&
- !excluded(dir, ce->name, &dtype))
+ if ((dir->flags & DIR_SHOW_IGNORED) &&
+ !ce_excluded(dir, ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
@@ -266,7 +303,7 @@ static void prune_cache(const char *prefix)
last = active_nr;
while (last > first) {
int next = (last + first) >> 1;
- struct cache_entry *ce = active_cache[next];
+ const struct cache_entry *ce = active_cache[next];
if (!strncmp(ce->name, prefix, max_prefix_len)) {
first = next+1;
continue;
@@ -276,21 +313,6 @@ static void prune_cache(const char *prefix)
active_nr = last;
}
-static void strip_trailing_slash_from_submodules(void)
-{
- const char **p;
-
- for (p = pathspec; *p != NULL; p++) {
- int len = strlen(*p), pos;
-
- if (len < 1 || (*p)[len - 1] != '/')
- continue;
- pos = cache_name_pos(*p, len - 1);
- if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
- *p = xstrndup(*p, len - 1);
- }
-}
-
/*
* Read the tree specified with --with-tree option
* (typically, HEAD) into stage #1 and then
@@ -322,13 +344,12 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
}
if (prefix) {
- static const char *(matchbuf[2]);
- matchbuf[0] = prefix;
- matchbuf[1] = NULL;
- init_pathspec(&pathspec, matchbuf);
- pathspec.items[0].use_wildcard = 0;
+ static const char *(matchbuf[1]);
+ matchbuf[0] = NULL;
+ parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
+ PATHSPEC_PREFER_CWD, prefix, matchbuf);
} else
- init_pathspec(&pathspec, NULL);
+ memset(&pathspec, 0, sizeof(pathspec));
if (read_tree(tree, 1, &pathspec))
die("unable to read tree entries %s", tree_name);
@@ -353,66 +374,18 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
}
}
-int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix)
-{
- /*
- * Make sure all pathspec matched; otherwise it is an error.
- */
- struct strbuf sb = STRBUF_INIT;
- const char *name;
- int num, errors = 0;
- for (num = 0; pathspec[num]; num++) {
- int other, found_dup;
-
- if (ps_matched[num])
- continue;
- /*
- * The caller might have fed identical pathspec
- * twice. Do not barf on such a mistake.
- */
- for (found_dup = other = 0;
- !found_dup && pathspec[other];
- other++) {
- if (other == num || !ps_matched[other])
- continue;
- if (!strcmp(pathspec[other], pathspec[num]))
- /*
- * Ok, we have a match already.
- */
- found_dup = 1;
- }
- if (found_dup)
- continue;
-
- name = quote_path_relative(pathspec[num], -1, &sb, prefix);
- error("pathspec '%s' did not match any file(s) known to git.",
- name);
- errors++;
- }
- strbuf_release(&sb);
- return errors;
-}
-
static const char * const ls_files_usage[] = {
- "git ls-files [options] [<file>...]",
+ N_("git ls-files [<options>] [<file>...]"),
NULL
};
-static int option_parse_z(const struct option *opt,
- const char *arg, int unset)
-{
- line_terminator = unset ? '\n' : '\0';
-
- return 0;
-}
-
static int option_parse_exclude(const struct option *opt,
const char *arg, int unset)
{
- struct exclude_list *list = opt->value;
+ struct string_list *exclude_list = opt->value;
exc_given = 1;
- add_exclude(arg, "", 0, list);
+ string_list_append(exclude_list, arg);
return 0;
}
@@ -441,62 +414,65 @@ static int option_parse_exclude_standard(const struct option *opt,
int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
{
- int require_work_tree = 0, show_tag = 0;
+ int require_work_tree = 0, show_tag = 0, i;
const char *max_prefix;
struct dir_struct dir;
+ struct exclude_list *el;
+ struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct option builtin_ls_files_options[] = {
- { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
- "paths are separated with NUL character",
- PARSE_OPT_NOARG, option_parse_z },
- OPT_BOOLEAN('t', NULL, &show_tag,
- "identify the file status with tags"),
- OPT_BOOLEAN('v', NULL, &show_valid_bit,
- "use lowercase letters for 'assume unchanged' files"),
- OPT_BOOLEAN('c', "cached", &show_cached,
- "show cached files in the output (default)"),
- OPT_BOOLEAN('d', "deleted", &show_deleted,
- "show deleted files in the output"),
- OPT_BOOLEAN('m', "modified", &show_modified,
- "show modified files in the output"),
- OPT_BOOLEAN('o', "others", &show_others,
- "show other files in the output"),
+ /* Think twice before adding "--nul" synonym to this */
+ OPT_SET_INT('z', NULL, &line_terminator,
+ N_("paths are separated with NUL character"), '\0'),
+ OPT_BOOL('t', NULL, &show_tag,
+ N_("identify the file status with tags")),
+ OPT_BOOL('v', NULL, &show_valid_bit,
+ N_("use lowercase letters for 'assume unchanged' files")),
+ OPT_BOOL('c', "cached", &show_cached,
+ N_("show cached files in the output (default)")),
+ OPT_BOOL('d', "deleted", &show_deleted,
+ N_("show deleted files in the output")),
+ OPT_BOOL('m', "modified", &show_modified,
+ N_("show modified files in the output")),
+ OPT_BOOL('o', "others", &show_others,
+ N_("show other files in the output")),
OPT_BIT('i', "ignored", &dir.flags,
- "show ignored files in the output",
+ N_("show ignored files in the output"),
DIR_SHOW_IGNORED),
- OPT_BOOLEAN('s', "stage", &show_stage,
- "show staged contents' object name in the output"),
- OPT_BOOLEAN('k', "killed", &show_killed,
- "show files on the filesystem that need to be removed"),
+ OPT_BOOL('s', "stage", &show_stage,
+ N_("show staged contents' object name in the output")),
+ OPT_BOOL('k', "killed", &show_killed,
+ N_("show files on the filesystem that need to be removed")),
OPT_BIT(0, "directory", &dir.flags,
- "show 'other' directories' name only",
+ N_("show 'other' directories' names only"),
DIR_SHOW_OTHER_DIRECTORIES),
+ OPT_BOOL(0, "eol", &show_eol, N_("show line endings of files")),
OPT_NEGBIT(0, "empty-directory", &dir.flags,
- "don't show empty directories",
+ N_("don't show empty directories"),
DIR_HIDE_EMPTY_DIRECTORIES),
- OPT_BOOLEAN('u', "unmerged", &show_unmerged,
- "show unmerged files in the output"),
- OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
- "show resolve-undo information"),
- { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
- "skip files matching pattern",
+ OPT_BOOL('u', "unmerged", &show_unmerged,
+ N_("show unmerged files in the output")),
+ OPT_BOOL(0, "resolve-undo", &show_resolve_undo,
+ N_("show resolve-undo information")),
+ { OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"),
+ N_("skip files matching pattern"),
0, option_parse_exclude },
- { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
- "exclude patterns are read from <file>",
+ { OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"),
+ N_("exclude patterns are read from <file>"),
0, option_parse_exclude_from },
- OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file",
- "read additional per-directory exclude patterns in <file>"),
+ OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
+ N_("read additional per-directory exclude patterns in <file>")),
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
- "add the standard git exclusions",
+ N_("add the standard git exclusions"),
PARSE_OPT_NOARG, option_parse_exclude_standard },
{ OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
- "make the output relative to the project top directory",
+ N_("make the output relative to the project top directory"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
- OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
- "if any <file> is not in the index, treat this as an error"),
- OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
- "pretend that paths removed since <tree-ish> are still present"),
+ 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"),
+ N_("pretend that paths removed since <tree-ish> are still present")),
OPT__ABBREV(&abbrev),
- OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
+ OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_END()
};
@@ -514,6 +490,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
ls_files_usage, 0);
+ el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+ for (i = 0; i < exclude_list.nr; i++) {
+ add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
+ }
if (show_tag || show_valid_bit) {
tag_cached = "H ";
tag_unmerged = "M ";
@@ -538,30 +518,25 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
- pathspec = get_pathspec(prefix, argv);
-
- /* be nice with submodule paths ending in a slash */
- if (pathspec)
- strip_trailing_slash_from_submodules();
+ parse_pathspec(&pathspec, 0,
+ PATHSPEC_PREFER_CWD |
+ PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ prefix, argv);
/* Find common prefix for all pathspec's */
- max_prefix = common_prefix(pathspec);
+ max_prefix = common_prefix(&pathspec);
max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
/* Treat unmatching pathspec elements as errors */
- if (pathspec && error_unmatch) {
- int num;
- for (num = 0; pathspec[num]; num++)
- ;
- ps_matched = xcalloc(1, num);
- }
+ if (pathspec.nr && error_unmatch)
+ ps_matched = xcalloc(pathspec.nr, 1);
if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
die("ls-files --ignored needs some exclude pattern");
/* With no flags, we default to showing the cached files */
- if (!(show_stage | show_deleted | show_others | show_unmerged |
- show_killed | show_modified | show_resolve_undo))
+ if (!(show_stage || show_deleted || show_others || show_unmerged ||
+ show_killed || show_modified || show_resolve_undo))
show_cached = 1;
if (max_prefix)
@@ -581,7 +556,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (ps_matched) {
int bad;
- bad = report_path_error(ps_matched, pathspec, prefix);
+ bad = report_path_error(ps_matched, &pathspec, prefix);
if (bad)
fprintf(stderr, "Did you forget to 'git add'?\n");