]> asedeno.scripts.mit.edu Git - git.git/commitdiff
Merge branch 'master' into next
authorJunio C Hamano <junkio@cox.net>
Sat, 20 May 2006 01:06:08 +0000 (18:06 -0700)
committerJunio C Hamano <junkio@cox.net>
Sat, 20 May 2006 01:06:08 +0000 (18:06 -0700)
* master:
  Fix build procedure for builtin-init-db
  Allow pickaxe and diff-filter options to be used by git log.
  Libify the index refresh logic
  Builtin git-init-db

1  2 
Makefile
builtin-log.c
builtin.h
cache.h
git.c
read-cache.c
update-index.c

diff --combined Makefile
index 06b31d8a25e449b16670b5c54c83357d389252dd,4fd6520b7edf6215c3baf78298ac309883a7935d..abfc07358256219c60d5aabdcbe6879d4c064e3d
+++ b/Makefile
@@@ -113,14 -113,14 +113,14 @@@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__pow
  ### --- END CONFIGURATION SECTION ---
  
  SCRIPT_SH = \
 -      git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
 +      git-bisect.sh git-branch.sh git-checkout.sh \
        git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
        git-fetch.sh \
        git-format-patch.sh git-ls-remote.sh \
        git-merge-one-file.sh git-parse-remote.sh \
        git-prune.sh git-pull.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
 -      git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
 +      git-resolve.sh git-revert.sh git-sh-setup.sh \
        git-tag.sh git-verify-tag.sh \
        git-applymbox.sh git-applypatch.sh git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@@ -154,7 -154,7 +154,7 @@@ PROGRAMS = 
        git-convert-objects$X git-diff-files$X \
        git-diff-index$X git-diff-stages$X \
        git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
-       git-hash-object$X git-index-pack$X git-init-db$X git-local-fetch$X \
+       git-hash-object$X git-index-pack$X git-local-fetch$X \
        git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \
        git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
        git-peek-remote$X git-prune-packed$X git-read-tree$X \
  
  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)
@@@ -200,7 -200,7 +201,7 @@@ LIB_H = 
        blob.h cache.h commit.h csum-file.h delta.h \
        diff.h object.h pack.h pkt-line.h quote.h refs.h \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
 -      tree-walk.h log-tree.h
 +      tree-walk.h log-tree.h dir.h
  
  DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
        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
@@@ -461,6 -461,7 +462,7 @@@ PYTHON_PATH_SQ = $(subst ','\'',$(PYTHO
  GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
  
  ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
+ ALL_CFLAGS += -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
  LIB_OBJS += $(COMPAT_OBJS)
  export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
  ### Build rules
@@@ -567,10 -568,6 +569,6 @@@ git-http-push$X: revision.o http.o http
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
- init-db.o: init-db.c
-       $(CC) -c $(ALL_CFLAGS) \
-               -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
  $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
  $(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS)
  $(DIFF_OBJS): diffcore.h
@@@ -612,9 -609,6 +610,9 @@@ test-date$X: test-date.c date.o ctype.
  test-delta$X: test-delta.c diff-delta.o patch-delta.o
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
  
 +test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
 +      $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 +
  check:
        for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
  
diff --combined builtin-log.c
index d5bbc1cc06931974de46078b5cac17e315177e40,c4ceee0f9801dcfbfb6aafe386dad3cc48b31560..12a6d19203f054d892c04873fef0f92a5c2ef0c9
@@@ -9,10 -9,6 +9,10 @@@
  #include "diff.h"
  #include "revision.h"
  #include "log-tree.h"
 +#include "builtin.h"
 +
 +/* this is in builtin-diff.c */
 +void add_head(struct rev_info *revs);
  
  static int cmd_log_wc(int argc, const char **argv, char **envp,
                      struct rev_info *rev)
        rev->commit_format = CMIT_FMT_DEFAULT;
        rev->verbose_header = 1;
        argc = setup_revisions(argc, argv, rev, "HEAD");
+       if (rev->always_show_header) {
+               if (rev->diffopt.pickaxe || rev->diffopt.filter) {
+                       rev->always_show_header = 0;
+                       if (rev->diffopt.output_format == DIFF_FORMAT_RAW)
+                               rev->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
+               }
+       }
  
        if (argc > 1)
                die("unrecognized argument: %s", argv[1]);
@@@ -71,160 -74,3 +78,160 @@@ int cmd_log(int argc, const char **argv
        rev.diffopt.recursive = 1;
        return cmd_log_wc(argc, argv, envp, &rev);
  }
 +
 +static int istitlechar(char c)
 +{
 +      return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
 +              (c >= '0' && c <= '9') || c == '.' || c == '_';
 +}
 +
 +static FILE *realstdout = NULL;
 +static char *output_directory = NULL;
 +
 +static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
 +{
 +      char filename[1024];
 +      char *sol;
 +      int len = 0;
 +
 +      if (output_directory) {
 +              strncpy(filename, output_directory, 1010);
 +              len = strlen(filename);
 +              if (filename[len - 1] != '/')
 +                      filename[len++] = '/';
 +      }
 +
 +      sprintf(filename + len, "%04d", nr);
 +      len = strlen(filename);
 +
 +      sol = strstr(commit->buffer, "\n\n");
 +      if (sol) {
 +              int j, space = 1;
 +
 +              sol += 2;
 +              /* strip [PATCH] or [PATCH blabla] */
 +              if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
 +                      char *eos = strchr(sol + 6, ']');
 +                      if (eos) {
 +                              while (isspace(*eos))
 +                                      eos++;
 +                              sol = eos;
 +                      }
 +              }
 +
 +              for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
 +                      if (istitlechar(sol[j])) {
 +                              if (space) {
 +                                      filename[len++] = '-';
 +                                      space = 0;
 +                              }
 +                              filename[len++] = sol[j];
 +                              if (sol[j] == '.')
 +                                      while (sol[j + 1] == '.')
 +                                              j++;
 +                      } else
 +                              space = 1;
 +              }
 +              while (filename[len - 1] == '.' || filename[len - 1] == '-')
 +                      len--;
 +      }
 +      strcpy(filename + len, ".txt");
 +      fprintf(realstdout, "%s\n", filename);
 +      freopen(filename, "w", stdout);
 +}
 +
 +int cmd_format_patch(int argc, const char **argv, char **envp)
 +{
 +      struct commit *commit;
 +      struct commit **list = NULL;
 +      struct rev_info rev;
 +      int nr = 0, total, i, j;
 +      int use_stdout = 0;
 +      int numbered = 0;
 +      int keep_subject = 0;
 +
 +      init_revisions(&rev);
 +      rev.commit_format = CMIT_FMT_EMAIL;
 +      rev.verbose_header = 1;
 +      rev.diff = 1;
 +      rev.diffopt.with_raw = 0;
 +      rev.diffopt.with_stat = 1;
 +      rev.combine_merges = 0;
 +      rev.ignore_merges = 1;
 +      rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 +
 +      /*
 +       * Parse the arguments before setup_revisions(), or something
 +       * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
 +       * possibly a valid SHA1.
 +       */
 +      for (i = 1, j = 1; i < argc; i++) {
 +              if (!strcmp(argv[i], "--stdout"))
 +                      use_stdout = 1;
 +              else if (!strcmp(argv[i], "-n") ||
 +                              !strcmp(argv[i], "--numbered"))
 +                      numbered = 1;
 +              else if (!strcmp(argv[i], "-k") ||
 +                              !strcmp(argv[i], "--keep-subject")) {
 +                      keep_subject = 1;
 +                      rev.total = -1;
 +              } else if (!strcmp(argv[i], "-o")) {
 +                      if (argc < 3)
 +                              die ("Which directory?");
 +                      if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
 +                              die("Could not create directory %s",
 +                                              argv[i + 1]);
 +                      output_directory = strdup(argv[i + 1]);
 +                      i++;
 +              } else
 +                      argv[j++] = argv[i];
 +      }
 +      argc = j;
 +
 +      if (numbered && keep_subject < 0)
 +              die ("-n and -k are mutually exclusive.");
 +
 +      argc = setup_revisions(argc, argv, &rev, "HEAD");
 +      if (argc > 1)
 +              die ("unrecognized argument: %s", argv[1]);
 +
 +      if (rev.pending_objects && rev.pending_objects->next == NULL) {
 +              rev.pending_objects->item->flags |= UNINTERESTING;
 +              add_head(&rev);
 +      }
 +
 +      if (!use_stdout)
 +              realstdout = fdopen(dup(1), "w");
 +
 +      prepare_revision_walk(&rev);
 +      while ((commit = get_revision(&rev)) != NULL) {
 +              /* ignore merges */
 +              if (commit->parents && commit->parents->next)
 +                      continue;
 +              nr++;
 +              list = realloc(list, nr * sizeof(list[0]));
 +              list[nr - 1] = commit;
 +      }
 +      total = nr;
 +      if (numbered)
 +              rev.total = total;
 +      while (0 <= --nr) {
 +              int shown;
 +              commit = list[nr];
 +              rev.nr = total - nr;
 +              if (!use_stdout)
 +                      reopen_stdout(commit, rev.nr, keep_subject);
 +              shown = log_tree_commit(&rev, commit);
 +              free(commit->buffer);
 +              commit->buffer = NULL;
 +              if (shown)
 +                      printf("-- \n%s\n\n", git_version_string);
 +              if (!use_stdout)
 +                      fclose(stdout);
 +      }
 +      if (output_directory)
 +              free(output_directory);
 +      free(list);
 +      return 0;
 +}
 +
diff --combined builtin.h
index d150c7ce5624e7ece666d6a639fb8e59aa018bed,60541262c462a257cd80d6edb8525b0fbbac0f71..34d5e6a7a9ff2af1b288dca7698d898f41022bb7
+++ b/builtin.h
@@@ -20,14 -20,12 +20,15 @@@ extern int cmd_whatchanged(int argc, co
  extern int cmd_show(int argc, const char **argv, char **envp);
  extern int cmd_log(int argc, const char **argv, char **envp);
  extern int cmd_diff(int argc, const char **argv, char **envp);
 +extern int cmd_format_patch(int argc, const char **argv, char **envp);
  extern int cmd_count_objects(int argc, const char **argv, char **envp);
  
  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 --combined cache.h
index f9b704916d1e5420a9c65ac3a3bcfb9cbb62e134,afa8e4f0acbd36947590e428fd86ed0cd2000276..31755c85a0b90c16c87356c6e203576c7e7021a0
+++ b/cache.h
@@@ -114,7 -114,6 +114,7 @@@ static inline unsigned int create_ce_mo
  
  extern struct cache_entry **active_cache;
  extern unsigned int active_nr, active_alloc, active_cache_changed;
 +extern struct cache_tree *active_cache_tree;
  
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@@ -143,7 -142,6 +143,7 @@@ extern void verify_non_filename(const c
  /* Initialize and use the cache information */
  extern int read_cache(void);
  extern int write_cache(int newfd, struct cache_entry **cache, int entries);
 +extern int verify_path(const char *path);
  extern int cache_name_pos(const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -160,6 -158,12 +160,12 @@@ extern int index_pipe(unsigned char *sh
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
+ #define REFRESH_REALLY                0x0001  /* ignore_valid */
+ #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
+ #define REFRESH_QUIET         0x0004  /* be quiet about it */
+ #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
+ extern int refresh_cache(unsigned int flags);
  struct cache_file {
        struct cache_file *next;
        char lockfile[PATH_MAX];
@@@ -253,7 -257,6 +259,7 @@@ extern void *read_object_with_reference
                                        unsigned char *sha1_ret);
  
  const char *show_date(unsigned long time, int timezone);
 +const char *show_rfc2822_date(unsigned long time, int timezone);
  int parse_date(const char *date, char *buf, int bufsize);
  void datestamp(char *buf, int bufsize);
  unsigned long approxidate(const char *);
diff --combined git.c
index 63aa31143563323f1318e26ce4fa9f298473c47f,3216d311b2efdd238f0eb023d6a918e0a43b54ab..eab8c1f45208a9d0ffb881f1d0f19f6b13c3dbfb
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -47,13 -47,11 +47,14 @@@ static void handle_internal_command(in
                { "whatchanged", cmd_whatchanged },
                { "show", cmd_show },
                { "push", cmd_push },
 +              { "fmt-patch", cmd_format_patch },
                { "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 --combined read-cache.c
index 9a272f89fcbbe1ff5c160b0b522dee6ed33f6e09,b95edcc14c2ae4094372f4ec307cdcfa1b8f24fe..36bd4ea0be84516be217945840a78c49872bfa38
@@@ -4,26 -4,11 +4,26 @@@
   * Copyright (C) Linus Torvalds, 2005
   */
  #include "cache.h"
 +#include "cache-tree.h"
 +
 +/* Index extensions.
 + *
 + * The first letter should be 'A'..'Z' for extensions that are not
 + * necessary for a correct operation (i.e. optimization data).
 + * When new extensions are added that _needs_ to be understood in
 + * order to correctly interpret the index file, pick character that
 + * is outside the range, to cause the reader to abort.
 + */
 +
 +#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
 +#define CACHE_EXT_TREE 0x54524545     /* "TREE" */
  
  struct cache_entry **active_cache = NULL;
  static time_t index_file_timestamp;
  unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
  
 +struct cache_tree *active_cache_tree = NULL;
 +
  /*
   * This only updates the "non-critical" parts of the directory
   * cache, ie the parts that aren't tracked by GIT, and only used
@@@ -346,70 -331,6 +346,70 @@@ int ce_path_match(const struct cache_en
        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.
 + *
 + * Also, we don't want double slashes or slashes at the
 + * end that can make pathnames ambiguous.
 + */
 +static int verify_dotfile(const char *rest)
 +{
 +      /*
 +       * 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;
 +
 +      /*
 +       * ".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;
 +      }
 +      return 1;
 +}
 +
 +int verify_path(const char *path)
 +{
 +      char c;
 +
 +      goto inside;
 +      for (;;) {
 +              if (!c)
 +                      return 1;
 +              if (c == '/') {
 +inside:
 +                      c = *path++;
 +                      switch (c) {
 +                      default:
 +                              continue;
 +                      case '/': case '\0':
 +                              break;
 +                      case '.':
 +                              if (verify_dotfile(path))
 +                                      continue;
 +                      }
 +                      return 0;
 +              }
 +              c = *path++;
 +      }
 +}
 +
  /*
   * Do we have another file that has the beginning components being a
   * proper superset of the name we're trying to add?
@@@ -551,8 -472,6 +551,8 @@@ int add_cache_entry(struct cache_entry 
  
        if (!ok_to_add)
                return -1;
 +      if (!verify_path(ce->name))
 +              return -1;
  
        if (!skip_df_check &&
            check_file_directory_conflict(ce, pos, ok_to_replace)) {
        return 0;
  }
  
+ /* Three functions to allow overloaded pointer return; see linux/err.h */
+ static inline void *ERR_PTR(long error)
+ {
+       return (void *) error;
+ }
+ static inline long PTR_ERR(const void *ptr)
+ {
+       return (long) ptr;
+ }
+ static inline long IS_ERR(const void *ptr)
+ {
+       return (unsigned long)ptr > (unsigned long)-1000L;
+ }
+ /*
+  * "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.
+  *
+  * 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 struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
+ {
+       struct stat st;
+       struct cache_entry *updated;
+       int changed, size;
+       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;
+       }
+       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;
+ }
+ int refresh_cache(unsigned int flags)
+ {
+       int i;
+       int has_errors = 0;
+       int really = (flags & REFRESH_REALLY) != 0;
+       int allow_unmerged = (flags & REFRESH_UNMERGED) != 0;
+       int quiet = (flags & REFRESH_QUIET) != 0;
+       int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
+       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;
+                       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;
+                       }
+                       if (quiet)
+                               continue;
+                       printf("%s: needs update\n", ce->name);
+                       has_errors = 1;
+                       continue;
+               }
+               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 verify_hdr(struct cache_header *hdr, unsigned long size)
  {
        SHA_CTX c;
        return 0;
  }
  
 +static int read_index_extension(const char *ext, void *data, unsigned long sz)
 +{
 +      switch (CACHE_EXT(ext)) {
 +      case CACHE_EXT_TREE:
 +              active_cache_tree = cache_tree_read(data, sz);
 +              break;
 +      default:
 +              if (*ext < 'A' || 'Z' < *ext)
 +                      return error("index uses %.4s extension, which we do not understand",
 +                                   ext);
 +              fprintf(stderr, "ignoring %.4s extension\n", ext);
 +              break;
 +      }
 +      return 0;
 +}
 +
  int read_cache(void)
  {
        int fd, i;
                active_cache[i] = ce;
        }
        index_file_timestamp = st.st_mtime;
 +      while (offset <= size - 20 - 8) {
 +              /* After an array of active_nr index entries,
 +               * there can be arbitrary number of extended
 +               * sections, each of which is prefixed with
 +               * extension name (4-byte) and section length
 +               * in 4-byte network byte order.
 +               */
 +              unsigned long extsize;
 +              memcpy(&extsize, map + offset + 4, 4);
 +              extsize = ntohl(extsize);
 +              if (read_index_extension(map + offset,
 +                                       map + offset + 8, extsize) < 0)
 +                      goto unmap;
 +              offset += 8;
 +              offset += extsize;
 +      }
        return active_nr;
  
  unmap:
@@@ -708,17 -712,6 +825,17 @@@ static int ce_write(SHA_CTX *context, i
        return 0;
  }
  
 +static int write_index_ext_header(SHA_CTX *context, int fd,
 +                                unsigned long ext, unsigned long sz)
 +{
 +      ext = htonl(ext);
 +      sz = htonl(sz);
 +      if ((ce_write(context, fd, &ext, 4) < 0) ||
 +          (ce_write(context, fd, &sz, 4) < 0))
 +              return -1;
 +      return 0;
 +}
 +
  static int ce_flush(SHA_CTX *context, int fd)
  {
        unsigned int left = write_buffer_len;
@@@ -815,19 -808,5 +932,19 @@@ int write_cache(int newfd, struct cache
                if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
                        return -1;
        }
 +
 +      /* Write extension data here */
 +      if (active_cache_tree) {
 +              unsigned long sz;
 +              void *data = cache_tree_write(active_cache_tree, &sz);
 +              if (data &&
 +                  !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
 +                  !ce_write(&c, newfd, data, sz))
 +                      ;
 +              else {
 +                      free(data);
 +                      return -1;
 +              }
 +      }
        return ce_flush(&c, newfd);
  }
diff --combined update-index.c
index 21448cc5cc3bfc13f2b21675e89f901d0d7067be,7d6de821e23fc02b19b970df94200edd31bc864f..956b6b34f99a447f55fe48ef3ce1eb652524c985
@@@ -6,7 -6,6 +6,7 @@@
  #include "cache.h"
  #include "strbuf.h"
  #include "quote.h"
 +#include "cache-tree.h"
  #include "tree-walk.h"
  
  /*
@@@ -19,9 -18,6 +19,6 @@@
  static int allow_add;
  static int allow_remove;
  static int allow_replace;
- static int allow_unmerged; /* --refresh needing merge is not error */
- static int not_new; /* --refresh not having working tree files is not error */
- static int quiet; /* --refresh needing update is not error */
  static int info_only;
  static int force_remove;
  static int verbose;
@@@ -29,23 -25,6 +26,6 @@@ static int mark_valid_only = 0
  #define MARK_VALID 1
  #define UNMARK_VALID 2
  
- /* Three functions to allow overloaded pointer return; see linux/err.h */
- static inline void *ERR_PTR(long error)
- {
-       return (void *) error;
- }
- static inline long PTR_ERR(const void *ptr)
- {
-       return (long) ptr;
- }
- static inline long IS_ERR(const void *ptr)
- {
-       return (unsigned long)ptr > (unsigned long)-1000L;
- }
  static void report(const char *fmt, ...)
  {
        va_list vp;
@@@ -72,7 -51,6 +52,7 @@@ static int mark_valid(const char *path
                        active_cache[pos]->ce_flags &= ~htons(CE_VALID);
                        break;
                }
 +              cache_tree_invalidate_path(active_cache_tree, path);
                active_cache_changed = 1;
                return 0;
        }
@@@ -86,12 -64,6 +66,12 @@@ static int add_file_to_cache(const cha
        struct stat st;
  
        status = lstat(path, &st);
 +
 +      /* We probably want to do this in remove_file_from_cache() and
 +       * add_cache_entry() instead...
 +       */
 +      cache_tree_invalidate_path(active_cache_tree, path);
 +
        if (status < 0 || S_ISDIR(st.st_mode)) {
                /* When we used to have "path" and now we want to add
                 * "path/file", we need a way to remove "path" before
        return 0;
  }
  
--/*
-  * "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.
 - * 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.
-- *
-  * 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.
 - * Also, we don't want double slashes or slashes at the
 - * end that can make pathnames ambiguous.
-- */
- static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
 -static int verify_dotfile(const char *rest)
--{
-       struct stat st;
-       struct cache_entry *updated;
-       int changed, size;
-       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;
-       }
-       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.
 -      /*
 -       * The first character was '.', but that
 -       * has already been discarded, we now test
 -       * the rest.
--       */
-       if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))
-               updated->ce_flags &= ~htons(CE_VALID);
 -      switch (*rest) {
 -      /* "." is not allowed */
 -      case '\0': case '/':
 -              return 0;
--
-       return updated;
 -      /*
 -       * ".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;
 -      }
 -      return 1;
--}
--
- static int refresh_cache(int really)
 -static int verify_path(const char *path)
--{
-       int i;
-       int has_errors = 0;
-       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;
-                       printf("%s: needs merge\n", ce->name);
-                       has_errors = 1;
-                       continue;
-               }
 -      char c;
--
-               new = refresh_entry(ce, really);
-               if (!new)
-                       continue;
-               if (IS_ERR(new)) {
-                       if (not_new && PTR_ERR(new) == -ENOENT)
 -      goto inside;
 -      for (;;) {
 -              if (!c)
 -                      return 1;
 -              if (c == '/') {
 -inside:
 -                      c = *path++;
 -                      switch (c) {
 -                      default:
--                              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;
 -                      case '/': case '\0':
 -                              break;
 -                      case '.':
 -                              if (verify_dotfile(path))
 -                                      continue;
--                      }
-                       if (quiet)
-                               continue;
-                       printf("%s: needs update\n", ce->name);
-                       has_errors = 1;
-                       continue;
 -                      return 0;
--              }
-               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;
 -              c = *path++;
--      }
-       return has_errors;
--}
--
  static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
                         const char *path, int stage)
  {
                return error("%s: cannot add to the index - missing --add option?",
                             path);
        report("add '%s'", path);
 +      cache_tree_invalidate_path(active_cache_tree, path);
        return 0;
  }
  
@@@ -295,7 -233,6 +178,7 @@@ static void chmod_path(int flip, const 
        default:
                goto fail;
        }
 +      cache_tree_invalidate_path(active_cache_tree, path);
        active_cache_changed = 1;
        report("chmod %cx '%s'", flip, path);
        return;
@@@ -317,7 -254,6 +200,7 @@@ static void update_one(const char *path
                        die("Unable to mark file %s", path);
                goto free_return;
        }
 +      cache_tree_invalidate_path(active_cache_tree, path);
  
        if (force_remove) {
                if (remove_file_from_cache(p))
@@@ -396,7 -332,6 +279,7 @@@ static void read_index_info(int line_te
                                free(path_name);
                        continue;
                }
 +              cache_tree_invalidate_path(active_cache_tree, path_name);
  
                if (!mode) {
                        /* mode == 0 means there is no such path -- remove */
@@@ -503,7 -438,6 +386,7 @@@ static int unresolve_one(const char *pa
                goto free_return;
        }
  
 +      cache_tree_invalidate_path(active_cache_tree, path);
        remove_file_from_cache(path);
        if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
                error("%s: cannot add our version to the index.", path);
@@@ -602,6 -536,7 +485,7 @@@ int main(int argc, const char **argv
        const char *prefix = setup_git_directory();
        int prefix_length = prefix ? strlen(prefix) : 0;
        char set_executable_bit = 0;
+       unsigned int refresh_flags = 0;
  
        git_config(git_default_config);
  
                                continue;
                        }
                        if (!strcmp(path, "-q")) {
-                               quiet = 1;
+                               refresh_flags |= REFRESH_QUIET;
                                continue;
                        }
                        if (!strcmp(path, "--add")) {
                                continue;
                        }
                        if (!strcmp(path, "--unmerged")) {
-                               allow_unmerged = 1;
+                               refresh_flags |= REFRESH_UNMERGED;
                                continue;
                        }
                        if (!strcmp(path, "--refresh")) {
-                               has_errors |= refresh_cache(0);
+                               has_errors |= refresh_cache(refresh_flags);
                                continue;
                        }
                        if (!strcmp(path, "--really-refresh")) {
-                               has_errors |= refresh_cache(1);
+                               has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
                                continue;
                        }
                        if (!strcmp(path, "--cacheinfo")) {
                                goto finish;
                        }
                        if (!strcmp(path, "--ignore-missing")) {
-                               not_new = 1;
+                               refresh_flags |= REFRESH_IGNORE_MISSING;
                                continue;
                        }
                        if (!strcmp(path, "--verbose")) {