summaryrefslogtreecommitdiff
path: root/ls-files.c
diff options
context:
space:
mode:
Diffstat (limited to 'ls-files.c')
-rw-r--r--ls-files.c140
1 files changed, 117 insertions, 23 deletions
diff --git a/ls-files.c b/ls-files.c
index 7024cf18e1..4a4af1ca3b 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,11 +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 = "";
@@ -89,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') {
@@ -254,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];
@@ -266,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] == '.') &&
@@ -276,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:
@@ -294,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)
@@ -325,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;
@@ -333,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;
}
@@ -356,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);
@@ -444,9 +473,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,
@@ -457,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);
@@ -523,7 +570,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);
}
}
@@ -596,9 +643,10 @@ 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=<pattern>] [--exclude-from=<file>] "
- "[ --exclude-per-directory=<filename> ] [--full-name] [--] [<file>]*";
+ "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
+ "[--] [<file>]*";
int main(int argc, const char **argv)
{
@@ -621,13 +669,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")) {
@@ -662,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.
@@ -699,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;
@@ -710,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]);
@@ -725,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;
}