From: Junio C Hamano Date: Wed, 24 May 2006 18:24:50 +0000 (-0700) Subject: Merge branch 'jc/builtin-n-tar-tree' X-Git-Tag: v1.4.0-rc1~77 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=f2054be4c4816d8c363dc867f462bc0af9031c02;p=git.git Merge branch 'jc/builtin-n-tar-tree' This pulls in "make many commands built-in" branches. * jc/builtin-n-tar-tree: built-in tar-tree and remote tar-tree Builtin git-diff-files, git-diff-index, git-diff-stages, and git-diff-tree. Builtin git-show-branch. Builtin git-apply. Builtin git-commit-tree. Builtin git-read-tree. Builtin git-tar-tree. Builtin git-ls-tree. Builtin git-ls-files. --- f2054be4c4816d8c363dc867f462bc0af9031c02 diff --cc Makefile index 46c83cb4,14873fa3..3bfe1c33 --- a/Makefile +++ b/Makefile @@@ -170,9 -168,12 +168,13 @@@ PROGRAMS = BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X git-ls-files$X git-ls-tree$X \ + git-grep$X git-add$X git-rm$X git-rev-list$X \ + git-check-ref-format$X \ - git-init-db$X ++ git-init-db$X git-tar-tree$X git-upload-tar$X \ ++ git-ls-files$X git-ls-tree$X \ + git-read-tree$X git-commit-tree$X \ + git-apply$X git-show-branch$X git-diff-files$X \ - git-diff-index$X git-diff-stages$X git-diff-tree$X \ - git-tar-tree$X git-upload-tar$X ++ git-diff-index$X git-diff-stages$X git-diff-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@@ -220,8 -221,12 +222,13 @@@ LIB_OBJS = BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ + builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-rm.o builtin-init-db.o ++ builtin-rm.o builtin-init-db.o \ ++ builtin-tar-tree.o builtin-upload-tar.o \ ++ builtin-ls-files.o builtin-ls-tree.o \ + builtin-read-tree.o builtin-commit-tree.o \ + builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ - builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ - builtin-tar-tree.o builtin-upload-tar.o ++ builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --cc builtin-ls-files.c index 00000000,3a0c5f21..8dae9f70 mode 000000,100644..100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@@ -1,0 -1,824 +1,499 @@@ + /* + * This merges the file listing in the directory cache index + * with the actual working directory list, and shows different + * combinations of the two. + * + * Copyright (C) Linus Torvalds, 2005 + */ -#include + #include + + #include "cache.h" + #include "quote.h" ++#include "dir.h" + #include "builtin.h" + + static int abbrev = 0; + static int show_deleted = 0; + static int show_cached = 0; + static int show_others = 0; -static int show_ignored = 0; + 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 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 = ""; + static const char *tag_removed = ""; + static const char *tag_other = ""; + static const char *tag_killed = ""; + static const char *tag_modified = ""; + -static const char *exclude_per_dir = NULL; - -/* We maintain three exclude pattern lists: - * EXC_CMDL lists patterns explicitly given on the command line. - * EXC_DIRS lists patterns obtained from per-directory ignore files. - * EXC_FILE lists patterns from fallback ignore files. - */ -#define EXC_CMDL 0 -#define EXC_DIRS 1 -#define EXC_FILE 2 -static struct exclude_list { - int nr; - int alloc; - struct exclude { - const char *pattern; - const char *base; - int baselen; - } **excludes; -} exclude_list[3]; - -static void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *which) -{ - struct exclude *x = xmalloc(sizeof (*x)); - - x->pattern = string; - x->base = base; - x->baselen = baselen; - if (which->nr == which->alloc) { - which->alloc = alloc_nr(which->alloc); - which->excludes = realloc(which->excludes, - which->alloc * sizeof(x)); - } - which->excludes[which->nr++] = x; -} - -static int add_excludes_from_file_1(const char *fname, - const char *base, - int baselen, - struct exclude_list *which) -{ - int fd, i; - long size; - char *buf, *entry; - - fd = open(fname, O_RDONLY); - if (fd < 0) - goto err; - size = lseek(fd, 0, SEEK_END); - if (size < 0) - goto err; - lseek(fd, 0, SEEK_SET); - if (size == 0) { - close(fd); - return 0; - } - 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') { - if (entry != buf + i && entry[0] != '#') { - buf[i - (i && buf[i-1] == '\r')] = 0; - add_exclude(entry, base, baselen, which); - } - entry = buf + i + 1; - } - } - return 0; - - err: - if (0 <= fd) - close(fd); - return -1; -} - -static void add_excludes_from_file(const char *fname) -{ - if (add_excludes_from_file_1(fname, "", 0, - &exclude_list[EXC_FILE]) < 0) - die("cannot use %s as an exclude file", fname); -} - -static int push_exclude_per_directory(const char *base, int baselen) -{ - char exclude_file[PATH_MAX]; - struct exclude_list *el = &exclude_list[EXC_DIRS]; - int current_nr = el->nr; - - if (exclude_per_dir) { - memcpy(exclude_file, base, baselen); - strcpy(exclude_file + baselen, exclude_per_dir); - add_excludes_from_file_1(exclude_file, base, baselen, el); - } - return current_nr; -} - -static void pop_exclude_per_directory(int stk) -{ - struct exclude_list *el = &exclude_list[EXC_DIRS]; - - while (stk < el->nr) - free(el->excludes[--el->nr]); -} - -/* Scan the list and let the last match determines the fate. - * Return 1 for exclude, 0 for include and -1 for undecided. - */ -static int excluded_1(const char *pathname, - int pathlen, - struct exclude_list *el) -{ - int i; - - if (el->nr) { - for (i = el->nr - 1; 0 <= i; i--) { - struct exclude *x = el->excludes[i]; - const char *exclude = x->pattern; - int to_exclude = 1; - - if (*exclude == '!') { - to_exclude = 0; - exclude++; - } - - if (!strchr(exclude, '/')) { - /* match basename */ - const char *basename = strrchr(pathname, '/'); - basename = (basename) ? basename+1 : pathname; - if (fnmatch(exclude, basename, 0) == 0) - return to_exclude; - } - else { - /* match with FNM_PATHNAME: - * exclude has base (baselen long) implicitly - * in front of it. - */ - int baselen = x->baselen; - if (*exclude == '/') - exclude++; - - if (pathlen < baselen || - (baselen && pathname[baselen-1] != '/') || - strncmp(pathname, x->base, baselen)) - continue; - - if (fnmatch(exclude, pathname+baselen, - FNM_PATHNAME) == 0) - return to_exclude; - } - } - } - return -1; /* undecided */ -} - -static int excluded(const char *pathname) -{ - int pathlen = strlen(pathname); - int st; - - for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_1(pathname, pathlen, &exclude_list[st])) { - case 0: - return 0; - case 1: - return 1; - } - } - return 0; -} - -struct nond_on_fs { - int len; - char name[FLEX_ARRAY]; /* more */ -}; - -static struct nond_on_fs **dir; -static int nr_dir; -static int dir_alloc; - -static void add_name(const char *pathname, int len) -{ - struct nond_on_fs *ent; - - if (cache_name_pos(pathname, len) >= 0) - return; - - if (nr_dir == dir_alloc) { - dir_alloc = alloc_nr(dir_alloc); - dir = xrealloc(dir, dir_alloc*sizeof(ent)); - } - ent = xmalloc(sizeof(*ent) + len + 1); - ent->len = len; - memcpy(ent->name, pathname, len); - ent->name[len] = 0; - 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 - * doesn't handle them at all yet. Maybe that will change some - * day. - * - * Also, we ignore the name ".git" (even if it is not a directory). - * That likely will not change. - */ -static int read_directory(const char *path, const char *base, int baselen) -{ - DIR *fdir = opendir(path); - int contents = 0; - - if (fdir) { - int exclude_stk; - struct dirent *de; - char fullname[MAXPATHLEN + 1]; - memcpy(fullname, base, baselen); - - exclude_stk = push_exclude_per_directory(base, baselen); - - while ((de = readdir(fdir)) != NULL) { - int len; - - if ((de->d_name[0] == '.') && - (de->d_name[1] == 0 || - !strcmp(de->d_name + 1, ".") || - !strcmp(de->d_name + 1, "git"))) - continue; - len = strlen(de->d_name); - memcpy(fullname + baselen, de->d_name, len+1); - 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: - if (lstat(fullname, &st)) - continue; - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) - break; - if (!S_ISDIR(st.st_mode)) - continue; - /* fallthrough */ - case DT_DIR: - memcpy(fullname + baselen + len, "/", 2); - len++; - rewind_base = nr_dir; - subdir = read_directory(fullname, fullname, - baselen + len); - if (show_other_directories && - (subdir || !hide_empty_directories) && - !dir_exists(fullname, baselen + len)) { - // Rewind the read subdirectory - while (nr_dir > rewind_base) - free(dir[--nr_dir]); - break; - } - contents += subdir; - continue; - case DT_REG: - case DT_LNK: - break; - } - add_name(fullname, baselen + len); - contents++; - } - closedir(fdir); - - pop_exclude_per_directory(exclude_stk); - } - - return contents; -} - -static int cmp_name(const void *p1, const void *p2) -{ - const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1; - const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2; - - return cache_name_compare(e1->name, e1->len, - e2->name, e2->len); -} + + /* + * Match a pathspec against a filename. The first "len" characters + * are the common prefix + */ + static int match(const char **spec, char *ps_matched, + const char *filename, int len) + { + const char *m; + + while ((m = *spec++) != NULL) { + int matchlen = strlen(m + len); + + if (!matchlen) + goto matched; + if (!strncmp(m + len, filename + len, matchlen)) { + if (m[len + matchlen - 1] == '/') + goto matched; + switch (filename[len + matchlen]) { + case '/': case '\0': + goto matched; + } + } + if (!fnmatch(m + len, filename + len, 0)) + goto matched; + if (ps_matched) + ps_matched++; + continue; + matched: + if (ps_matched) + *ps_matched = 1; + return 1; + } + return 0; + } + -static void show_dir_entry(const char *tag, struct nond_on_fs *ent) ++static void show_dir_entry(const char *tag, struct dir_entry *ent) + { + int len = prefix_len; + int offset = prefix_offset; + + if (len >= ent->len) + die("git-ls-files: internal error - directory entry not superset of prefix"); + + if (pathspec && !match(pathspec, ps_matched, ent->name, len)) + return; + + fputs(tag, stdout); + write_name_quoted("", 0, ent->name + offset, line_terminator, stdout); + putchar(line_terminator); + } + -static void show_other_files(void) ++static void show_other_files(struct dir_struct *dir) + { + int i; - for (i = 0; i < nr_dir; i++) { ++ for (i = 0; i < dir->nr; i++) { + /* We should not have a matching entry, but we + * may have an unmerged entry for this path. + */ - struct nond_on_fs *ent = dir[i]; ++ struct dir_entry *ent = dir->entries[i]; + int pos = cache_name_pos(ent->name, ent->len); + struct cache_entry *ce; + if (0 <= pos) + die("bug in show-other-files"); + pos = -pos - 1; + if (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) == ent->len && + !memcmp(ce->name, ent->name, ent->len)) + continue; /* Yup, this one exists unmerged */ + } + show_dir_entry(tag_other, ent); + } + } + -static void show_killed_files(void) ++static void show_killed_files(struct dir_struct *dir) + { + int i; - for (i = 0; i < nr_dir; i++) { - struct nond_on_fs *ent = dir[i]; ++ for (i = 0; i < dir->nr; i++) { ++ struct dir_entry *ent = dir->entries[i]; + char *cp, *sp; + int pos, len, killed = 0; + + for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) { + sp = strchr(cp, '/'); + if (!sp) { + /* If ent->name is prefix of an entry in the + * cache, it will be killed. + */ + pos = cache_name_pos(ent->name, ent->len); + if (0 <= pos) + die("bug in show-killed-files"); + pos = -pos - 1; + while (pos < active_nr && + ce_stage(active_cache[pos])) + pos++; /* skip unmerged */ + if (active_nr <= pos) + break; + /* pos points at a name immediately after + * ent->name in the cache. Does it expect + * ent->name to be a directory? + */ + len = ce_namelen(active_cache[pos]); + if ((ent->len < len) && + !strncmp(active_cache[pos]->name, + ent->name, ent->len) && + active_cache[pos]->name[ent->len] == '/') + killed = 1; + break; + } + if (0 <= cache_name_pos(ent->name, sp - ent->name)) { + /* If any of the leading directories in + * ent->name is registered in the cache, + * ent->name will be killed. + */ + killed = 1; + break; + } + } + if (killed) - show_dir_entry(tag_killed, dir[i]); ++ show_dir_entry(tag_killed, dir->entries[i]); + } + } + + static void show_ce_entry(const char *tag, struct cache_entry *ce) + { + int len = prefix_len; + int offset = prefix_offset; + + if (len >= ce_namelen(ce)) + die("git-ls-files: internal error - cache entry not superset of prefix"); + + 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, + line_terminator, stdout); + putchar(line_terminator); + } + else { + printf("%s%06o %s %d\t", + tag, + ntohl(ce->ce_mode), + 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); + putchar(line_terminator); + } + } + -static void show_files(void) ++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) { + 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); ++ read_directory(dir, path, base, baselen); + if (show_others) - show_other_files(); ++ show_other_files(dir); + if (show_killed) - show_killed_files(); ++ show_killed_files(dir); + } + if (show_cached | show_stage) { + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; - if (excluded(ce->name) != show_ignored) ++ if (excluded(dir, ce->name) != dir->show_ignored) + continue; + if (show_unmerged && !ce_stage(ce)) + continue; + show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce); + } + } + if (show_deleted | show_modified) { + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + struct stat st; + int err; - if (excluded(ce->name) != show_ignored) ++ if (excluded(dir, ce->name) != dir->show_ignored) + continue; + err = lstat(ce->name, &st); + if (show_deleted && err) + show_ce_entry(tag_removed, ce); + if (show_modified && ce_modified(ce, &st, 0)) + show_ce_entry(tag_modified, ce); + } + } + } + + /* + * Prune the index to only contain stuff starting with "prefix" + */ + static void prune_cache(void) + { + int pos = cache_name_pos(prefix, prefix_len); + unsigned int first, last; + + if (pos < 0) + pos = -pos-1; + active_cache += pos; + active_nr -= pos; + first = 0; + last = active_nr; + while (last > first) { + int next = (last + first) >> 1; + struct cache_entry *ce = active_cache[next]; + if (!strncmp(ce->name, prefix, prefix_len)) { + first = next+1; + continue; + } + last = next; + } + active_nr = last; + } + + static void verify_pathspec(void) + { + const char **p, *n, *prev; + char *real_prefix; + unsigned long max; + + prev = NULL; + max = PATH_MAX; + for (p = pathspec; (n = *p) != NULL; p++) { + int i, len = 0; + for (i = 0; i < max; i++) { + char c = n[i]; + if (prev && prev[i] != c) + break; + if (!c || c == '*' || c == '?') + break; + if (c == '/') + len = i+1; + } + prev = n; + if (len < max) { + max = len; + if (!max) + break; + } + } + + if (prefix_offset > max || memcmp(prev, prefix, prefix_offset)) + die("git-ls-files: cannot generate relative filenames containing '..'"); + + real_prefix = NULL; + prefix_len = max; + if (max) { + real_prefix = xmalloc(max + 1); + memcpy(real_prefix, prev, max); + real_prefix[max] = 0; + } + prefix = real_prefix; + } + + 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] [--abbrev] " + "[--] []*"; + + int cmd_ls_files(int argc, const char **argv, char** envp) + { + int i; + int exc_given = 0; ++ struct dir_struct dir; + ++ memset(&dir, 0, sizeof(dir)); + prefix = setup_git_directory(); + if (prefix) + prefix_offset = strlen(prefix); + git_config(git_default_config); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-z")) { + line_terminator = 0; + continue; + } + 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")) { + show_cached = 1; + continue; + } + if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { + show_deleted = 1; + continue; + } + if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { + show_modified = 1; + continue; + } + if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) { + show_others = 1; + continue; + } + if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { - show_ignored = 1; ++ dir.show_ignored = 1; + continue; + } + if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { + show_stage = 1; + continue; + } + if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) { + show_killed = 1; + continue; + } + if (!strcmp(arg, "--directory")) { - show_other_directories = 1; ++ dir.show_other_directories = 1; + continue; + } + if (!strcmp(arg, "--no-empty-directory")) { - hide_empty_directories = 1; ++ dir.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. + */ + show_stage = 1; + show_unmerged = 1; + continue; + } + if (!strcmp(arg, "-x") && i+1 < argc) { + exc_given = 1; - add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]); ++ add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); + continue; + } + if (!strncmp(arg, "--exclude=", 10)) { + exc_given = 1; - add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]); ++ add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); + continue; + } + if (!strcmp(arg, "-X") && i+1 < argc) { + exc_given = 1; - add_excludes_from_file(argv[++i]); ++ add_excludes_from_file(&dir, argv[++i]); + continue; + } + if (!strncmp(arg, "--exclude-from=", 15)) { + exc_given = 1; - add_excludes_from_file(arg+15); ++ add_excludes_from_file(&dir, arg+15); + continue; + } + if (!strncmp(arg, "--exclude-per-directory=", 24)) { + exc_given = 1; - exclude_per_dir = arg + 24; ++ dir.exclude_per_dir = arg + 24; + continue; + } + if (!strcmp(arg, "--full-name")) { + 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; + } + + pathspec = get_pathspec(prefix, argv + i); + + /* Verify that the pathspec matches the prefix */ + 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) { ++ if (dir.show_ignored && !exc_given) { + fprintf(stderr, "%s: --ignored needs some exclude pattern\n", + argv[0]); + exit(1); + } + + /* With no flags, we default to showing the cached files */ + if (!(show_stage | show_deleted | show_others | show_unmerged | + show_killed | show_modified)) + show_cached = 1; + + read_cache(); + if (prefix) + prune_cache(); - show_files(); ++ show_files(&dir); + + 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; + } diff --cc git.c index 4a2c4bab,38e84618..9ce458e6 --- a/git.c +++ b/git.c @@@ -50,11 -50,22 +50,24 @@@ static void handle_internal_command(in { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, + { "rm", cmd_rm }, + { "add", cmd_add }, { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, - { "check-ref-format", cmd_check_ref_format } + { "tar-tree", cmd_tar_tree }, + { "upload-tar", cmd_upload_tar }, + { "check-ref-format", cmd_check_ref_format }, + { "ls-files", cmd_ls_files }, + { "ls-tree", cmd_ls_tree }, + { "tar-tree", cmd_tar_tree }, + { "read-tree", cmd_read_tree }, + { "commit-tree", cmd_commit_tree }, + { "apply", cmd_apply }, + { "show-branch", cmd_show_branch }, + { "diff-files", cmd_diff_files }, + { "diff-index", cmd_diff_index }, + { "diff-stages", cmd_diff_stages }, + { "diff-tree", cmd_diff_tree } }; int i;