X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=ls-files.c;h=4a4af1ca3b3cc11e2996c2889cb8ea16bbb0dded;hb=65b5e41e24dd76e9cc272399f458857d5b13d63e;hp=5539fd85492e6e76c5a3ebc6651ff9f750e4e8c6;hpb=77abf6db91b44fff6d338ab7bbdd627366c135eb;p=git.git diff --git a/ls-files.c b/ls-files.c index 5539fd85..4a4af1ca 100644 --- a/ls-files.c +++ b/ls-files.c @@ -11,6 +11,7 @@ #include "cache.h" #include "quote.h" +static int abbrev = 0; static int show_deleted = 0; static int show_cached = 0; static int show_others = 0; @@ -20,12 +21,15 @@ static int show_unmerged = 0; static int show_modified = 0; static int show_killed = 0; static int show_other_directories = 0; +static int hide_empty_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 = ""; @@ -90,11 +94,12 @@ static int add_excludes_from_file_1(const char *fname, close(fd); return 0; } - buf = xmalloc(size); + buf = xmalloc(size+1); if (read(fd, buf, size) != size) goto err; close(fd); + buf[size++] = '\n'; entry = buf; for (i = 0; i < size; i++) { if (buf[i] == '\n') { @@ -255,11 +260,12 @@ static int dir_exists(const char *dirname, int len) * Also, we ignore the name ".git" (even if it is not a directory). * That likely will not change. */ -static void read_directory(const char *path, const char *base, int baselen) +static int read_directory(const char *path, const char *base, int baselen) { - DIR *dir = opendir(path); + DIR *fdir = opendir(path); + int contents = 0; - if (dir) { + if (fdir) { int exclude_stk; struct dirent *de; char fullname[MAXPATHLEN + 1]; @@ -267,7 +273,7 @@ static void read_directory(const char *path, const char *base, int baselen) exclude_stk = push_exclude_per_directory(base, baselen); - while ((de = readdir(dir)) != NULL) { + while ((de = readdir(fdir)) != NULL) { int len; if ((de->d_name[0] == '.') && @@ -277,11 +283,15 @@ static void read_directory(const char *path, const char *base, int baselen) continue; len = strlen(de->d_name); memcpy(fullname + baselen, de->d_name, len+1); - if (excluded(fullname) != show_ignored) - continue; + if (excluded(fullname) != show_ignored) { + if (!show_ignored || DTYPE(de) != DT_DIR) { + continue; + } + } switch (DTYPE(de)) { struct stat st; + int subdir, rewind_base; default: continue; case DT_UNKNOWN: @@ -295,22 +305,32 @@ static void read_directory(const char *path, const char *base, int baselen) case DT_DIR: memcpy(fullname + baselen + len, "/", 2); len++; + rewind_base = nr_dir; + subdir = read_directory(fullname, fullname, + baselen + len); if (show_other_directories && - !dir_exists(fullname, baselen + len)) + (subdir || !hide_empty_directories) && + !dir_exists(fullname, baselen + len)) { + // Rewind the read subdirectory + while (nr_dir > rewind_base) + free(dir[--nr_dir]); break; - read_directory(fullname, fullname, - baselen + len); + } + contents += subdir; continue; case DT_REG: case DT_LNK: break; } add_name(fullname, baselen + len); + contents++; } - closedir(dir); + closedir(fdir); pop_exclude_per_directory(exclude_stk); } + + return contents; } static int cmp_name(const void *p1, const void *p2) @@ -326,7 +346,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; @@ -334,17 +355,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; } @@ -357,7 +385,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); @@ -445,7 +473,7 @@ 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 && @@ -475,7 +503,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) printf("%s%06o %s %d\t", tag, ntohl(ce->ce_mode), - sha1_to_hex(ce->sha1), + abbrev ? find_unique_abbrev(ce->sha1,abbrev) + : sha1_to_hex(ce->sha1), ce_stage(ce)); write_name_quoted("", 0, ce->name + offset, line_terminator, stdout); @@ -616,7 +645,8 @@ static void verify_pathspec(void) static const char ls_files_usage[] = "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " "[ --ignored ] [--exclude=] [--exclude-from=] " - "[ --exclude-per-directory= ] [--full-name] [--] []*"; + "[ --exclude-per-directory= ] [--full-name] [--abbrev] " + "[--] []*"; int main(int argc, const char **argv) { @@ -682,6 +712,10 @@ int main(int argc, const char **argv) show_other_directories = 1; continue; } + if (!strcmp(arg, "--no-empty-directory")) { + hide_empty_directories = 1; + continue; + } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { /* There's no point in showing unmerged unless * you also show the stage information. @@ -719,6 +753,22 @@ int main(int argc, const char **argv) prefix_offset = 0; continue; } + if (!strcmp(arg, "--error-unmatch")) { + error_unmatch = 1; + continue; + } + if (!strncmp(arg, "--abbrev=", 9)) { + abbrev = strtoul(arg+9, NULL, 10); + if (abbrev && abbrev < MINIMUM_ABBREV) + abbrev = MINIMUM_ABBREV; + else if (abbrev > 40) + abbrev = 40; + continue; + } + if (!strcmp(arg, "--abbrev")) { + abbrev = DEFAULT_ABBREV; + continue; + } if (*arg == '-') usage(ls_files_usage); break; @@ -730,6 +780,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]); @@ -745,5 +803,21 @@ 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_offset); + errors++; + } + return errors ? 1 : 0; + } + return 0; }