X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=ls-files.c;h=52bbfdc805aecaa9c463adbb4ef94d1af9c8511f;hb=becb6a658c43633850a2417935c108266056b765;hp=f3f1a6a6633881bf43887731fdcbe08af9ad2f4a;hpb=5e80092f7e6db09a40a62e837ca3f74f0bc5ad73;p=git.git diff --git a/ls-files.c b/ls-files.c index f3f1a6a6..52bbfdc8 100644 --- a/ls-files.c +++ b/ls-files.c @@ -19,11 +19,15 @@ static int show_stage = 0; static int show_unmerged = 0; static int show_modified = 0; static int show_killed = 0; +static int show_other_directories = 0; +static int show_valid_bit = 0; static int line_terminator = '\n'; static int prefix_len = 0, prefix_offset = 0; static const char *prefix = NULL; static const char **pathspec = NULL; +static int error_unmatch = 0; +static char *ps_matched = NULL; static const char *tag_cached = ""; static const char *tag_unmerged = ""; @@ -169,7 +173,7 @@ static int excluded_1(const char *pathname, } else { /* match with FNM_PATHNAME: - * exclude has base (baselen long) inplicitly + * exclude has base (baselen long) implicitly * in front of it. */ int baselen = x->baselen; @@ -208,7 +212,7 @@ static int excluded(const char *pathname) struct nond_on_fs { int len; - char name[0]; + char name[FLEX_ARRAY]; /* more */ }; static struct nond_on_fs **dir; @@ -233,6 +237,17 @@ static void add_name(const char *pathname, int len) dir[nr_dir++] = ent; } +static int dir_exists(const char *dirname, int len) +{ + int pos = cache_name_pos(dirname, len); + if (pos >= 0) + return 1; + pos = -pos-1; + if (pos >= active_nr) /* can't */ + return 0; + return !strncmp(active_cache[pos]->name, dirname, len); +} + /* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git @@ -281,8 +296,12 @@ static void read_directory(const char *path, const char *base, int baselen) /* fallthrough */ case DT_DIR: memcpy(fullname + baselen + len, "/", 2); + len++; + if (show_other_directories && + !dir_exists(fullname, baselen + len)) + break; read_directory(fullname, fullname, - baselen + len + 1); + baselen + len); continue; case DT_REG: case DT_LNK: @@ -309,7 +328,8 @@ static int cmp_name(const void *p1, const void *p2) * Match a pathspec against a filename. The first "len" characters * are the common prefix */ -static int match(const char **spec, const char *filename, int len) +static int match(const char **spec, char *ps_matched, + const char *filename, int len) { const char *m; @@ -317,17 +337,24 @@ static int match(const char **spec, const char *filename, int len) int matchlen = strlen(m + len); if (!matchlen) - return 1; + goto matched; if (!strncmp(m + len, filename + len, matchlen)) { if (m[len + matchlen - 1] == '/') - return 1; + goto matched; switch (filename[len + matchlen]) { case '/': case '\0': - return 1; + goto matched; } } if (!fnmatch(m + len, filename + len, 0)) - return 1; + goto matched; + if (ps_matched) + ps_matched++; + continue; + matched: + if (ps_matched) + *ps_matched = 1; + return 1; } return 0; } @@ -340,7 +367,7 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent) if (len >= ent->len) die("git-ls-files: internal error - directory entry not superset of prefix"); - if (pathspec && !match(pathspec, ent->name, len)) + if (pathspec && !match(pathspec, ps_matched, ent->name, len)) return; fputs(tag, stdout); @@ -428,9 +455,26 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git-ls-files: internal error - cache entry not superset of prefix"); - if (pathspec && !match(pathspec, ce->name, len)) + if (pathspec && !match(pathspec, ps_matched, ce->name, len)) return; + if (tag && *tag && show_valid_bit && + (ce->ce_flags & htons(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); write_name_quoted("", 0, ce->name + offset, @@ -458,8 +502,28 @@ static void show_files(void) const char *path = ".", *base = ""; int baselen = prefix_len; - if (baselen) + if (baselen) { path = base = prefix; + if (exclude_per_dir) { + char *p, *pp = xmalloc(baselen+1); + memcpy(pp, prefix, baselen+1); + p = pp; + while (1) { + char save = *p; + *p = 0; + push_exclude_per_directory(pp, p-pp); + *p++ = save; + if (!save) + break; + p = strchr(p, '/'); + if (p) + p++; + else + p = pp + baselen; + } + free(pp); + } + } read_directory(path, base, baselen); qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name); if (show_others) @@ -487,7 +551,7 @@ static void show_files(void) err = lstat(ce->name, &st); if (show_deleted && err) show_ce_entry(tag_removed, ce); - if (show_modified && ce_modified(ce, &st)) + if (show_modified && ce_modified(ce, &st, 0)) show_ce_entry(tag_modified, ce); } } @@ -560,9 +624,9 @@ static void verify_pathspec(void) } static const char ls_files_usage[] = - "git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed|modified])* " + "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " "[ --ignored ] [--exclude=] [--exclude-from=] " - "[ --exclude-per-directory= ] [--] []*"; + "[ --exclude-per-directory= ] [--full-name] [--] []*"; int main(int argc, const char **argv) { @@ -585,13 +649,15 @@ int main(int argc, const char **argv) line_terminator = 0; continue; } - if (!strcmp(arg, "-t")) { + if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) { tag_cached = "H "; tag_unmerged = "M "; tag_removed = "R "; tag_modified = "C "; tag_other = "? "; tag_killed = "K "; + if (arg[1] == 'v') + show_valid_bit = 1; continue; } if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) { @@ -622,6 +688,10 @@ int main(int argc, const char **argv) show_killed = 1; continue; } + if (!strcmp(arg, "--directory")) { + show_other_directories = 1; + continue; + } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { /* There's no point in showing unmerged unless * you also show the stage information. @@ -659,6 +729,10 @@ int main(int argc, const char **argv) prefix_offset = 0; continue; } + if (!strcmp(arg, "--error-unmatch")) { + error_unmatch = 1; + continue; + } if (*arg == '-') usage(ls_files_usage); break; @@ -670,6 +744,14 @@ int main(int argc, const char **argv) if (pathspec) verify_pathspec(); + /* Treat unmatching pathspec elements as errors */ + if (pathspec && error_unmatch) { + int num; + for (num = 0; pathspec[num]; num++) + ; + ps_matched = xcalloc(1, num); + } + if (show_ignored && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); @@ -685,5 +767,20 @@ int main(int argc, const char **argv) if (prefix) prune_cache(); show_files(); + + if (ps_matched) { + /* We need to make sure all pathspec matched otherwise + * it is an error. + */ + int num, errors = 0; + for (num = 0; pathspec[num]; num++) { + if (ps_matched[num]) + continue; + error("pathspec '%s' did not match any.", + pathspec[num] + prefix_len); + } + return errors ? 1 : 0; + } + return 0; }