Merge branch 'lt/dirwalk' into jc/dirwalk-n-cache-tree
authorJunio C Hamano <junkio@cox.net>
Sat, 20 May 2006 08:28:49 +0000 (01:28 -0700)
committerJunio C Hamano <junkio@cox.net>
Sat, 20 May 2006 08:52:19 +0000 (01:52 -0700)
This commit is what this branch is all about. It records the
evil merge needed to adjust built-in git-add and git-rm for
the cache-tree extension.

* lt/dirwalk:
Add builtin "git rm" command
Move pathspec matching from builtin-add.c into dir.c
Prevent bogus paths from being added to the index.
builtin-add: fix unmatched pathspec warnings.
Remove old "git-add.sh" remnants
builtin-add: warn on unmatched pathspecs
Do "git add" as a builtin
Clean up git-ls-file directory walking library interface
libify git-ls-files directory traversal

Conflicts:

Makefile
builtin.h
git.c
update-index.c

1  2 
Makefile
builtin-add.c
builtin-rm.c
builtin.h
cache.h
git.c
read-cache.c
update-index.c
diff --cc Makefile
index 3127ab384a4e43dfd2a9cf7723e8e4e26520c105,d4a91135c33a49fcb2bd5da3f5653d72a32673a7..abfc07358256219c60d5aabdcbe6879d4c064e3d
+++ b/Makefile
@@@ -170,8 -170,8 +170,9 @@@ 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-grep$X git-add$X git-rm$X git-rev-list$X \
 -      git-check-ref-format$X
++      git-check-ref-format$X \
 +      git-init-db$X
  
  # what 'all' will build and 'install' will install, in gitexecdir
  ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@@ -208,10 -208,10 +209,10 @@@ DIFF_OBJS = 
        diffcore-delta.o log-tree.o
  
  LIB_OBJS = \
 -      blob.o commit.o connect.o csum-file.o base85.o \
 +      blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o \
-       quote.o read-cache.o refs.o run-command.o \
+       quote.o read-cache.o refs.o run-command.o dir.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
        fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
  
  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-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
 -      builtin-rm.o
++      builtin-rm.o builtin-init-db.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
  LIBS = $(GITLIBS) -lz
diff --cc builtin-add.c
index 0000000000000000000000000000000000000000,6166f66bceb006b5ffc3834d4d77e253c7b7992e..02fe38b0c4c7c61bcc51544d1fd54c0b1641e2e8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,187 +1,189 @@@
+ /*
+  * "git add" builtin command
+  *
+  * Copyright (C) 2006 Linus Torvalds
+  */
+ #include <fnmatch.h>
+ #include "cache.h"
+ #include "builtin.h"
+ #include "dir.h"
++#include "cache-tree.h"
+ static const char builtin_add_usage[] =
+ "git-add [-n] [-v] <filepattern>...";
+ static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
+ {
+       char *seen;
+       int i, specs;
+       struct dir_entry **src, **dst;
+       for (specs = 0; pathspec[specs];  specs++)
+               /* nothing */;
+       seen = xmalloc(specs);
+       memset(seen, 0, specs);
+       src = dst = dir->entries;
+       i = dir->nr;
+       while (--i >= 0) {
+               struct dir_entry *entry = *src++;
+               if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
+                       free(entry);
+                       continue;
+               }
+               *dst++ = entry;
+       }
+       dir->nr = dst - dir->entries;
+       for (i = 0; i < specs; i++) {
+               struct stat st;
+               const char *match;
+               if (seen[i])
+                       continue;
+               /* Existing file? We must have ignored it */
+               match = pathspec[i];
+               if (!match[0] || !lstat(match, &st))
+                       continue;
+               die("pathspec '%s' did not match any files", match);
+       }
+ }
+ static void fill_directory(struct dir_struct *dir, const char **pathspec)
+ {
+       const char *path, *base;
+       int baselen;
+       /* Set up the default git porcelain excludes */
+       memset(dir, 0, sizeof(*dir));
+       dir->exclude_per_dir = ".gitignore";
+       path = git_path("info/exclude");
+       if (!access(path, R_OK))
+               add_excludes_from_file(dir, path);
+       /*
+        * Calculate common prefix for the pathspec, and
+        * use that to optimize the directory walk
+        */
+       baselen = common_prefix(pathspec);
+       path = ".";
+       base = "";
+       if (baselen) {
+               char *common = xmalloc(baselen + 1);
+               common = xmalloc(baselen + 1);
+               memcpy(common, *pathspec, baselen);
+               common[baselen] = 0;
+               path = base = common;
+       }
+       /* Read the directory and prune it */
+       read_directory(dir, path, base, baselen);
+       if (pathspec)
+               prune_directory(dir, pathspec, baselen);
+ }
+ static int add_file_to_index(const char *path, int verbose)
+ {
+       int size, namelen;
+       struct stat st;
+       struct cache_entry *ce;
+       if (lstat(path, &st))
+               die("%s: unable to stat (%s)", path, strerror(errno));
+       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+               die("%s: can only add regular files or symbolic links", path);
+       namelen = strlen(path);
+       size = cache_entry_size(namelen);
+       ce = xcalloc(1, size);
+       memcpy(ce->name, path, namelen);
+       ce->ce_flags = htons(namelen);
+       fill_stat_cache_info(ce, &st);
+       ce->ce_mode = create_ce_mode(st.st_mode);
+       if (!trust_executable_bit) {
+               /* If there is an existing entry, pick the mode bits
+                * from it.
+                */
+               int pos = cache_name_pos(path, namelen);
+               if (pos >= 0)
+                       ce->ce_mode = active_cache[pos]->ce_mode;
+       }
+       if (index_path(ce->sha1, path, &st, 1))
+               die("unable to index file %s", path);
+       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+               die("unable to add %s to index",path);
+       if (verbose)
+               printf("add '%s'\n", path);
++      cache_tree_invalidate_path(active_cache_tree, path);
+       return 0;
+ }
+ static struct cache_file cache_file;
+ int cmd_add(int argc, const char **argv, char **envp)
+ {
+       int i, newfd;
+       int verbose = 0, show_only = 0;
+       const char *prefix = setup_git_directory();
+       const char **pathspec;
+       struct dir_struct dir;
+       git_config(git_default_config);
+       newfd = hold_index_file_for_update(&cache_file, get_index_file());
+       if (newfd < 0)
+               die("unable to create new cachefile");
+       if (read_cache() < 0)
+               die("index file corrupt");
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (arg[0] != '-')
+                       break;
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(arg, "-n")) {
+                       show_only = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-v")) {
+                       verbose = 1;
+                       continue;
+               }
+               die(builtin_add_usage);
+       }
+       git_config(git_default_config);
+       pathspec = get_pathspec(prefix, argv + i);
+       fill_directory(&dir, pathspec);
+       if (show_only) {
+               const char *sep = "", *eof = "";
+               for (i = 0; i < dir.nr; i++) {
+                       printf("%s%s", sep, dir.entries[i]->name);
+                       sep = " ";
+                       eof = "\n";
+               }
+               fputs(eof, stdout);
+               return 0;
+       }
+       for (i = 0; i < dir.nr; i++)
+               add_file_to_index(dir.entries[i]->name, verbose);
+       if (active_cache_changed) {
+               if (write_cache(newfd, active_cache, active_nr) ||
+                   commit_index_file(&cache_file))
+                       die("Unable to write new index file");
+       }
+       return 0;
+ }
diff --cc builtin-rm.c
index 0000000000000000000000000000000000000000,9014c61556c80d2d37d7f59e9e9419d3b3ba9884..0beb86dc3dd13610da14afe77c96a2a1ea34573b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,150 +1,151 @@@
+ /*
+  * "git rm" builtin command
+  *
+  * Copyright (C) Linus Torvalds 2006
+  */
+ #include "cache.h"
+ #include "builtin.h"
+ #include "dir.h"
+ static const char builtin_rm_usage[] =
+ "git-rm [-n] [-v] [-f] <filepattern>...";
+ static struct {
+       int nr, alloc;
+       const char **name;
+ } list;
+ static void add_list(const char *name)
+ {
+       if (list.nr >= list.alloc) {
+               list.alloc = alloc_nr(list.alloc);
+               list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
+       }
+       list.name[list.nr++] = name;
+ }
+ static int remove_file(const char *name)
+ {
+       int ret;
+       char *slash;
+       ret = unlink(name);
+       if (!ret && (slash = strrchr(name, '/'))) {
+               char *n = strdup(name);
+               do {
+                       n[slash - name] = 0;
+                       name = n;
+               } while (!rmdir(name) && (slash = strrchr(name, '/')));
+       }
+       return ret;
+ }
+ static struct cache_file cache_file;
+ int cmd_rm(int argc, const char **argv, char **envp)
+ {
+       int i, newfd;
+       int verbose = 0, show_only = 0, force = 0;
+       const char *prefix = setup_git_directory();
+       const char **pathspec;
+       char *seen;
+       git_config(git_default_config);
+       newfd = hold_index_file_for_update(&cache_file, get_index_file());
+       if (newfd < 0)
+               die("unable to create new index file");
+       if (read_cache() < 0)
+               die("index file corrupt");
+       for (i = 1 ; i < argc ; i++) {
+               const char *arg = argv[i];
+               if (*arg != '-')
+                       break;
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(arg, "-n")) {
+                       show_only = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-v")) {
+                       verbose = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-f")) {
+                       force = 1;
+                       continue;
+               }
+               die(builtin_rm_usage);
+       }
+       pathspec = get_pathspec(prefix, argv + i);
+       seen = NULL;
+       if (pathspec) {
+               for (i = 0; pathspec[i] ; i++)
+                       /* nothing */;
+               seen = xmalloc(i);
+               memset(seen, 0, i);
+       }
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
+                       continue;
+               add_list(ce->name);
+       }
+       if (pathspec) {
+               const char *match;
+               for (i = 0; (match = pathspec[i]) != NULL ; i++) {
+                       if (*match && !seen[i])
+                               die("pathspec '%s' did not match any files", match);
+               }
+       }
+       /*
+        * First remove the names from the index: we won't commit
+        * the index unless all of them succeed
+        */
+       for (i = 0; i < list.nr; i++) {
+               const char *path = list.name[i];
+               printf("rm '%s'\n", path);
+               if (remove_file_from_cache(path))
+                       die("git rm: unable to remove %s", path);
++              cache_tree_invalidate_path(active_cache_tree, path);
+       }
+       /*
+        * Then, if we used "-f", remove the filenames from the
+        * workspace. If we fail to remove the first one, we
+        * abort the "git rm" (but once we've successfully removed
+        * any file at all, we'll go ahead and commit to it all:
+        * by then we've already committed ourself and can't fail
+        * in the middle)
+        */
+       if (force) {
+               int removed = 0;
+               for (i = 0; i < list.nr; i++) {
+                       const char *path = list.name[i];
+                       if (!remove_file(path)) {
+                               removed = 1;
+                               continue;
+                       }
+                       if (!removed)
+                               die("git rm: %s: %s", path, strerror(errno));
+               }
+       }
+       if (active_cache_changed) {
+               if (write_cache(newfd, active_cache, active_nr) ||
+                   commit_index_file(&cache_file))
+                       die("Unable to write new index file");
+       }
+       return 0;
+ }
diff --cc builtin.h
index 60541262c462a257cd80d6edb8525b0fbbac0f71,c1cb765deac0b83735a053cfaeb7fd41044d7ae1..1a41d4753f789860a4714cf4a0449d1247315f83
+++ b/builtin.h
@@@ -24,8 -24,7 +24,10 @@@ extern int cmd_count_objects(int argc, 
  
  extern int cmd_push(int argc, const char **argv, char **envp);
  extern int cmd_grep(int argc, const char **argv, char **envp);
+ extern int cmd_rm(int argc, const char **argv, char **envp);
+ extern int cmd_add(int argc, const char **argv, char **envp);
 +extern int cmd_rev_list(int argc, const char **argv, char **envp);
 +extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
 +extern int cmd_init_db(int argc, const char **argv, char **envp);
  
  #endif
diff --cc cache.h
Simple merge
diff --cc git.c
index 3216d311b2efdd238f0eb023d6a918e0a43b54ab,20c0f197a355299600898a1e9ca3e20dceb14645..4a2c4bab7aeca670a999fd5f2da5440ac9ca1b96
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -50,9 -50,8 +50,11 @@@ 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 }
        };
        int i;
  
diff --cc read-cache.c
Simple merge
diff --cc update-index.c
index 8def9bcd9fc05f205d278c3d8317f1af748f00d8,859efc79164cb86f5bdd65ba72ba087ccb8f0ae9..956b6b34f99a447f55fe48ef3ce1eb652524c985
@@@ -128,70 -140,103 +128,6 @@@ static int add_file_to_cache(const cha
        return 0;
  }
  
--/*
-  * We fundamentally don't like some paths: we don't want
-  * dot or dot-dot anywhere, and for obvious reasons don't
-  * want to recurse into ".git" either.
 - * "refresh" does not calculate a new sha1 file or bring the
 - * cache up-to-date for mode/content changes. But what it
 - * _does_ do is to "re-match" the stat information of a file
 - * with the cache, so that you can refresh the cache for a
 - * file that hasn't been changed but where the stat entry is
 - * out of date.
-- *
-  * Also, we don't want double slashes or slashes at the
-  * end that can make pathnames ambiguous.
 - * For example, you'd want to do this after doing a "git-read-tree",
 - * to link up the stat cache details with the proper files.
-- */
- static int verify_dotfile(const char *rest)
 -static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
--{
-       /*
-        * The first character was '.', but that
-        * has already been discarded, we now test
-        * the rest.
-        */
-       switch (*rest) {
-       /* "." is not allowed */
-       case '\0': case '/':
-               return 0;
 -      struct stat st;
 -      struct cache_entry *updated;
 -      int changed, size;
--
-       /*
-        * ".git" followed by  NUL or slash is bad. This
-        * shares the path end test with the ".." case.
-        */
-       case 'g':
-               if (rest[1] != 'i')
-                       break;
-               if (rest[2] != 't')
-                       break;
-               rest += 2;
-       /* fallthrough */
-       case '.':
-               if (rest[1] == '\0' || rest[1] == '/')
-                       return 0;
 -      if (lstat(ce->name, &st) < 0)
 -              return ERR_PTR(-errno);
 -
 -      changed = ce_match_stat(ce, &st, really);
 -      if (!changed) {
 -              if (really && assume_unchanged &&
 -                  !(ce->ce_flags & htons(CE_VALID)))
 -                      ; /* mark this one VALID again */
 -              else
 -                      return NULL;
--      }
-       return 1;
 -
 -      if (ce_modified(ce, &st, really))
 -              return ERR_PTR(-EINVAL);
 -
 -      size = ce_size(ce);
 -      updated = xmalloc(size);
 -      memcpy(updated, ce, size);
 -      fill_stat_cache_info(updated, &st);
 -
 -      /* In this case, if really is not set, we should leave
 -       * CE_VALID bit alone.  Otherwise, paths marked with
 -       * --no-assume-unchanged (i.e. things to be edited) will
 -       * reacquire CE_VALID bit automatically, which is not
 -       * really what we want.
 -       */
 -      if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))
 -              updated->ce_flags &= ~htons(CE_VALID);
 -
 -      return updated;
--}
--
- static int verify_path(const char *path)
 -static int refresh_cache(int really)
--{
-       char c;
 -      int i;
 -      int has_errors = 0;
--
-       goto inside;
-       for (;;) {
-               if (!c)
-                       return 1;
-               if (c == '/') {
- inside:
-                       c = *path++;
-                       switch (c) {
-                       default:
 -      for (i = 0; i < active_nr; i++) {
 -              struct cache_entry *ce, *new;
 -              ce = active_cache[i];
 -              if (ce_stage(ce)) {
 -                      while ((i < active_nr) &&
 -                             ! strcmp(active_cache[i]->name, ce->name))
 -                              i++;
 -                      i--;
 -                      if (allow_unmerged)
--                              continue;
-                       case '/': case '\0':
-                               break;
-                       case '.':
-                               if (verify_dotfile(path))
-                                       continue;
 -                      printf("%s: needs merge\n", ce->name);
 -                      has_errors = 1;
 -                      continue;
 -              }
 -
 -              new = refresh_entry(ce, really);
 -              if (!new)
 -                      continue;
 -              if (IS_ERR(new)) {
 -                      if (not_new && PTR_ERR(new) == -ENOENT)
 -                              continue;
 -                      if (really && PTR_ERR(new) == -EINVAL) {
 -                              /* If we are doing --really-refresh that
 -                               * means the index is not valid anymore.
 -                               */
 -                              ce->ce_flags &= ~htons(CE_VALID);
 -                              active_cache_changed = 1;
--                      }
-                       return 0;
 -                      if (quiet)
 -                              continue;
 -                      printf("%s: needs update\n", ce->name);
 -                      has_errors = 1;
 -                      continue;
--              }
-               c = *path++;
 -              active_cache_changed = 1;
 -              /* You can NOT just free active_cache[i] here, since it
 -               * might not be necessarily malloc()ed but can also come
 -               * from mmap(). */
 -              active_cache[i] = new;
--      }
 -      return has_errors;
--}
--
  static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
                         const char *path, int stage)
  {