Merge branch 'lt/in-core-index'
authorJunio C Hamano <gitster@pobox.com>
Tue, 12 Feb 2008 00:46:20 +0000 (16:46 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 12 Feb 2008 00:46:20 +0000 (16:46 -0800)
* lt/in-core-index:
lazy index hashing
Create pathname-based hash-table lookup into index
read-cache.c: introduce is_racy_timestamp() helper
read-cache.c: fix a couple more CE_REMOVE conversion
Also use unpack_trees() in do_diff_cache()
Make run_diff_index() use unpack_trees(), not read_tree()
Avoid running lstat(2) on the same cache entry.
index: be careful when handling long names
Make on-disk index representation separate from in-core one

1  2 
builtin-apply.c
builtin-commit.c
builtin-fsck.c
cache.h
diff.c
merge-recursive.c
t/t0000-basic.sh
wt-status.c
diff --combined builtin-apply.c
index a11b1bbeee9568adbf8a3e32f77d14b132985061,30d86f21972d1f7e0bee45d6bf582859b9819921..46dad5b2a1ce6d70d540df8a40da94702dde68c7
@@@ -1946,7 -1946,7 +1946,7 @@@ static int read_file_or_gitlink(struct 
        if (!ce)
                return 0;
  
-       if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+       if (S_ISGITLINK(ce->ce_mode)) {
                strbuf_grow(buf, 100);
                strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
        } else {
@@@ -2023,7 -2023,7 +2023,7 @@@ static int check_to_create_blob(const c
  
  static int verify_index_match(struct cache_entry *ce, struct stat *st)
  {
-       if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+       if (S_ISGITLINK(ce->ce_mode)) {
                if (!S_ISDIR(st->st_mode))
                        return -1;
                return 0;
@@@ -2082,12 -2082,12 +2082,12 @@@ static int check_patch(struct patch *pa
                                return error("%s: does not match index",
                                             old_name);
                        if (cached)
-                               st_mode = ntohl(ce->ce_mode);
+                               st_mode = ce->ce_mode;
                } else if (stat_ret < 0)
                        return error("%s: %s", old_name, strerror(errno));
  
                if (!cached)
-                       st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+                       st_mode = ce_mode_from_stat(ce, st.st_mode);
  
                if (patch->is_new < 0)
                        patch->is_new = 0;
@@@ -2388,7 -2388,7 +2388,7 @@@ static void add_index_file(const char *
        ce = xcalloc(1, ce_size);
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
-       ce->ce_flags = htons(namelen);
+       ce->ce_flags = namelen;
        if (S_ISGITLINK(mode)) {
                const char *s = buf;
  
@@@ -2746,8 -2746,6 +2746,8 @@@ static int apply_patch(int fd, const ch
  static int git_apply_config(const char *var, const char *value)
  {
        if (!strcmp(var, "apply.whitespace")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
                apply_default_whitespace = xstrdup(value);
                return 0;
        }
diff --combined builtin-commit.c
index a43f2019959a950639f013ff534610e228d8c149,c63ff826fc1c87c232b61c0059f12bb26f3098b2..717eb18da0e0cbe901dd8a660649388ccc0a24e1
@@@ -122,23 -122,19 +122,23 @@@ static void rollback_index_files(void
        }
  }
  
 -static void commit_index_files(void)
 +static int commit_index_files(void)
  {
 +      int err = 0;
 +
        switch (commit_style) {
        case COMMIT_AS_IS:
                break; /* nothing to do */
        case COMMIT_NORMAL:
 -              commit_lock_file(&index_lock);
 +              err = commit_lock_file(&index_lock);
                break;
        case COMMIT_PARTIAL:
 -              commit_lock_file(&index_lock);
 +              err = commit_lock_file(&index_lock);
                rollback_lock_file(&false_lock);
                break;
        }
 +
 +      return err;
  }
  
  /*
@@@ -160,7 -156,7 +160,7 @@@ static int list_paths(struct path_list 
  
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
-               if (ce->ce_flags & htons(CE_UPDATE))
+               if (ce->ce_flags & CE_UPDATE)
                        continue;
                if (!pathspec_match(pattern, m, ce->name, 0))
                        continue;
@@@ -601,7 -597,7 +601,7 @@@ static int parse_and_validate_options(i
  
                if (get_sha1(use_message, sha1))
                        die("could not lookup commit %s", use_message);
 -              commit = lookup_commit(sha1);
 +              commit = lookup_commit_reference(sha1);
                if (!commit || parse_commit(commit))
                        die("could not parse commit %s", use_message);
  
@@@ -743,8 -739,6 +743,8 @@@ static void print_summary(const char *p
  int git_commit_config(const char *k, const char *v)
  {
        if (!strcmp(k, "commit.template")) {
 +              if (!v)
 +                      return config_error_nonbool(v);
                template_file = xstrdup(v);
                return 0;
        }
@@@ -931,12 -925,8 +931,12 @@@ int cmd_commit(int argc, const char **a
  
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
 +      unlink(git_path("SQUASH_MSG"));
  
 -      commit_index_files();
 +      if (commit_index_files())
 +              die ("Repository has been updated, but unable to write\n"
 +                   "new_index file. Check that disk is not full or quota is\n"
 +                   "not exceeded, and then \"git reset HEAD\" to recover.");
  
        rerere();
        run_hook(get_index_file(), "post-commit", NULL);
diff --combined builtin-fsck.c
index 8c564345f411baeb1fb91e68ebc2f3be42c2f8af,6fc9525e0485f3dba00fa6bea8df1479221f0ce0..cc7524be80f51bca45ef8a205900cf50135bfc5d
@@@ -360,9 -360,6 +360,9 @@@ static int fsck_commit(struct commit *c
                fprintf(stderr, "Checking commit %s\n",
                        sha1_to_hex(commit->object.sha1));
  
 +      if (!commit->date)
 +              return objerror(&commit->object, "invalid author/committer line");
 +
        if (memcmp(buffer, "tree ", 5))
                return objerror(&commit->object, "invalid format - expected 'tree' line");
        if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
                return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
        if (!commit->parents && show_root)
                printf("root %s\n", sha1_to_hex(commit->object.sha1));
 -      if (!commit->date)
 -              printf("bad commit date in %s\n",
 -                     sha1_to_hex(commit->object.sha1));
        return 0;
  }
  
@@@ -765,7 -765,7 +765,7 @@@ int cmd_fsck(int argc, const char **arg
                        struct blob *blob;
                        struct object *obj;
  
-                       mode = ntohl(active_cache[i]->ce_mode);
+                       mode = active_cache[i]->ce_mode;
                        if (S_ISGITLINK(mode))
                                continue;
                        blob = lookup_blob(active_cache[i]->sha1);
diff --combined cache.h
index 6abcee437238ccc2b46e7d7d74f79db3a4b682a1,e4aeff07d1ac5243aff118f57d4adb806976730b..3867ba7ff5c3946e0449c78b26880a71107730a0
+++ b/cache.h
@@@ -3,6 -3,7 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
+ #include "hash.h"
  
  #include SHA1_HEADER
  #include <zlib.h>
@@@ -94,48 -95,84 +95,84 @@@ struct cache_time 
   * We save the fields in big-endian order to allow using the
   * index file over NFS transparently.
   */
+ struct ondisk_cache_entry {
+       struct cache_time ctime;
+       struct cache_time mtime;
+       unsigned int dev;
+       unsigned int ino;
+       unsigned int mode;
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int size;
+       unsigned char sha1[20];
+       unsigned short flags;
+       char name[FLEX_ARRAY]; /* more */
+ };
  struct cache_entry {
-       struct cache_time ce_ctime;
-       struct cache_time ce_mtime;
+       struct cache_entry *next;
+       unsigned int ce_ctime;
+       unsigned int ce_mtime;
        unsigned int ce_dev;
        unsigned int ce_ino;
        unsigned int ce_mode;
        unsigned int ce_uid;
        unsigned int ce_gid;
        unsigned int ce_size;
+       unsigned int ce_flags;
        unsigned char sha1[20];
-       unsigned short ce_flags;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_NAMEMASK  (0x0fff)
  #define CE_STAGEMASK (0x3000)
- #define CE_UPDATE    (0x4000)
  #define CE_VALID     (0x8000)
  #define CE_STAGESHIFT 12
  
- #define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
- #define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
+ /* In-memory only */
+ #define CE_UPDATE    (0x10000)
+ #define CE_REMOVE    (0x20000)
+ #define CE_UPTODATE  (0x40000)
+ #define CE_UNHASHED  (0x80000)
+ static inline unsigned create_ce_flags(size_t len, unsigned stage)
+ {
+       if (len >= CE_NAMEMASK)
+               len = CE_NAMEMASK;
+       return (len | (stage << CE_STAGESHIFT));
+ }
+ static inline size_t ce_namelen(const struct cache_entry *ce)
+ {
+       size_t len = ce->ce_flags & CE_NAMEMASK;
+       if (len < CE_NAMEMASK)
+               return len;
+       return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
+ }
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
- #define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
+ #define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
+ #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
+ #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+ #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
  static inline unsigned int create_ce_mode(unsigned int mode)
  {
        if (S_ISLNK(mode))
-               return htonl(S_IFLNK);
+               return S_IFLNK;
        if (S_ISDIR(mode) || S_ISGITLINK(mode))
-               return htonl(S_IFGITLINK);
-       return htonl(S_IFREG | ce_permissions(mode));
+               return S_IFGITLINK;
+       return S_IFREG | ce_permissions(mode);
  }
  static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
  {
        extern int trust_executable_bit, has_symlinks;
        if (!has_symlinks && S_ISREG(mode) &&
-           ce && S_ISLNK(ntohl(ce->ce_mode)))
+           ce && S_ISLNK(ce->ce_mode))
                return ce->ce_mode;
        if (!trust_executable_bit && S_ISREG(mode)) {
-               if (ce && S_ISREG(ntohl(ce->ce_mode)))
+               if (ce && S_ISREG(ce->ce_mode))
                        return ce->ce_mode;
                return create_ce_mode(0666);
        }
        S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
  
  #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
+ #define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
  
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct cache_tree *cache_tree;
        time_t timestamp;
-       void *mmap;
-       size_t mmap_size;
+       void *alloc;
+       unsigned name_hash_initialized : 1;
+       struct hash_table name_hash;
  };
  
  extern struct index_state the_index;
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
+ #define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
  #endif
  
  enum object_type {
@@@ -263,6 -303,7 +303,7 @@@ extern int read_index_from(struct index
  extern int write_index(struct index_state *, int newfd);
  extern int discard_index(struct index_state *);
  extern int verify_path(const char *path);
+ extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
  extern int index_name_pos(struct index_state *, 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 */
@@@ -589,7 -630,6 +630,7 @@@ extern int git_config_set_multivar(cons
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value);
 +extern int config_error_nonbool(const char *);
  
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
diff --combined diff.c
index 4d2e23ae1b7dd4bd43d5c03871ce8ab519272115,d464fe3b20efd13bfc7ed08304eb94aca80fa3d9..cd8bc4dcc32757dfed9374fa8769599a6a7857ad
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -158,8 -158,6 +158,8 @@@ int git_diff_ui_config(const char *var
                return 0;
        }
        if (!strcmp(var, "diff.external")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
                external_diff_cmd_cfg = xstrdup(value);
                return 0;
        }
                const char *ep = strrchr(var, '.');
  
                if (ep != var + 4) {
 -                      if (!strcmp(ep, ".command"))
 +                      if (!strcmp(ep, ".command")) {
 +                              if (!value)
 +                                      return config_error_nonbool(var);
                                return parse_lldiff_command(var, ep, value);
 +                      }
                }
        }
  
@@@ -182,8 -177,6 +182,8 @@@ int git_diff_basic_config(const char *v
  {
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
 +              if (!value)
 +                      return config_error_nonbool(var);
                color_parse(value, var, diff_colors[slot]);
                return 0;
        }
        if (!prefixcmp(var, "diff.")) {
                const char *ep = strrchr(var, '.');
                if (ep != var + 4) {
 -                      if (!strcmp(ep, ".funcname"))
 +                      if (!strcmp(ep, ".funcname")) {
 +                              if (!value)
 +                                      return config_error_nonbool(var);
                                return parse_funcname_pattern(var, ep, value);
 +                      }
                }
        }
  
@@@ -1520,17 -1510,22 +1520,22 @@@ static int reuse_worktree_file(const ch
        if (pos < 0)
                return 0;
        ce = active_cache[pos];
-       if ((lstat(name, &st) < 0) ||
-           !S_ISREG(st.st_mode) || /* careful! */
-           ce_match_stat(ce, &st, 0) ||
-           hashcmp(sha1, ce->sha1))
+       /*
+        * This is not the sha1 we are looking for, or
+        * unreusable because it is not a regular file.
+        */
+       if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
                return 0;
-       /* we return 1 only when we can stat, it is a regular file,
-        * stat information matches, and sha1 recorded in the cache
-        * matches.  I.e. we know the file in the work tree really is
-        * the same as the <name, sha1> pair.
+       /*
+        * If ce matches the file in the work tree, we can reuse it.
         */
-       return 1;
+       if (ce_uptodate(ce) ||
+           (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+               return 1;
+       return 0;
  }
  
  static int populate_from_stdin(struct diff_filespec *s)
diff --combined merge-recursive.c
index 34e3167cafc3d09e1a2b32bc9a5c64b4de1e442d,bdf03b1f1f7acacafebc0541309be097cc80336c..dd52342539cd05e59bb13e956d51251417f21e3c
@@@ -333,7 -333,7 +333,7 @@@ static struct path_list *get_unmerged(v
                        item->util = xcalloc(1, sizeof(struct stage_data));
                }
                e = item->util;
-               e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+               e->stages[ce_stage(ce)].mode = ce->ce_mode;
                hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
        }
  
@@@ -844,9 -844,8 +844,9 @@@ static int read_merge_config(const cha
        int namelen;
  
        if (!strcmp(var, "merge.default")) {
 -              if (value)
 -                      default_ll_merge = strdup(value);
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              default_ll_merge = strdup(value);
                return 0;
        }
  
  
        if (!strcmp("name", ep)) {
                if (!value)
 -                      return error("%s: lacks value", var);
 +                      return config_error_nonbool(var);
                fn->description = strdup(value);
                return 0;
        }
  
        if (!strcmp("driver", ep)) {
                if (!value)
 -                      return error("%s: lacks value", var);
 +                      return config_error_nonbool(var);
                /*
                 * merge.<name>.driver specifies the command line:
                 *
  
        if (!strcmp("recursive", ep)) {
                if (!value)
 -                      return error("%s: lacks value", var);
 +                      return config_error_nonbool(var);
                fn->recursive = strdup(value);
                return 0;
        }
diff --combined t/t0000-basic.sh
index cd0de506d208f3d741aa2223e8cc639850326ae7,9f84b8d3acf198aafbd0d2a8445dce7ce976ee3d..92de08822702f780c6895a5d2c070ee009ecfef7
@@@ -46,25 -46,13 +46,25 @@@ test_expect_success 
      '.git/objects should have 3 subdirectories.' \
      'test $(wc -l < full-of-directories) = 3'
  
 +################################################################
 +# Test harness
 +test_expect_success 'success is reported like this' '
 +    :
 +'
 +test_expect_failure 'pretend we have a known breakage' '
 +    false
 +'
 +test_expect_failure 'pretend we have fixed a known breakage' '
 +    :
 +'
 +
  ################################################################
  # Basics of the basics
  
  # updating a new file without --add should fail.
 -test_expect_failure \
 -    'git update-index without --add should fail adding.' \
 -    'git update-index should-be-empty'
 +test_expect_success 'git update-index without --add should fail adding.' '
 +    ! git update-index should-be-empty
 +'
  
  # and with --add it should succeed, even if it is empty (it used to fail).
  test_expect_success \
@@@ -82,9 -70,9 +82,9 @@@ test_expect_success 
  
  # Removing paths.
  rm -f should-be-empty full-of-directories
 -test_expect_failure \
 -    'git update-index without --remove should fail removing.' \
 -    'git update-index should-be-empty'
 +test_expect_success 'git update-index without --remove should fail removing.' '
 +    ! git update-index should-be-empty
 +'
  
  test_expect_success \
      'git update-index with --remove should be able to remove.' \
@@@ -216,9 -204,9 +216,9 @@@ test_expect_success 
      'put invalid objects into the index.' \
      'git update-index --index-info < badobjects'
  
 -test_expect_failure \
 -    'writing this tree without --missing-ok.' \
 -    'git write-tree'
 +test_expect_success 'writing this tree without --missing-ok.' '
 +    ! git write-tree
 +'
  
  test_expect_success \
      'writing this tree with --missing-ok.' \
@@@ -309,4 -297,24 +309,24 @@@ test_expect_success 'absolute path work
        test "$sym" = "$(test-absolute-path $dir2/syml)"
  '
  
+ test_expect_success 'very long name in the index handled sanely' '
+       a=a && # 1
+       a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 16
+       a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 256
+       a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 4096
+       a=${a}q &&
+       >path4 &&
+       git update-index --add path4 &&
+       (
+               git ls-files -s path4 |
+               sed -e "s/      .*/     /" |
+               tr -d "\012"
+               echo "$a"
+       ) | git update-index --index-info &&
+       len=$(git ls-files "a*" | wc -c) &&
+       test $len = 4098
+ '
  test_done
diff --combined wt-status.c
index bfd1b0fcc612a827adc7416626895b89e8504d90,27b946d55299d66031b31949da187ffb670e484b..f14022f2a9b8ce740067f7c6bf03bd51203cf2ca
@@@ -217,19 -217,12 +217,12 @@@ static void wt_status_print_changed_cb(
                wt_status_print_trailer(s);
  }
  
- static void wt_read_cache(struct wt_status *s)
- {
-       discard_cache();
-       read_cache_from(s->index_file);
- }
  static void wt_status_print_initial(struct wt_status *s)
  {
        int i;
        struct strbuf buf;
  
        strbuf_init(&buf, 0);
-       wt_read_cache(s);
        if (active_nr) {
                s->commitable = 1;
                wt_status_print_cached_header(s);
@@@ -256,7 -249,6 +249,6 @@@ static void wt_status_print_updated(str
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
        rev.diffopt.break_opt = 0;
-       wt_read_cache(s);
        run_diff_index(&rev, 1);
  }
  
@@@ -268,7 -260,6 +260,6 @@@ static void wt_status_print_changed(str
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_print_changed_cb;
        rev.diffopt.format_callback_data = s;
-       wt_read_cache(s);
        run_diff_files(&rev, 0);
  }
  
@@@ -335,7 -326,6 +326,6 @@@ static void wt_status_print_verbose(str
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
-       wt_read_cache(s);
        run_diff_index(&rev, 1);
  
        fflush(stdout);
@@@ -402,8 -392,6 +392,8 @@@ int git_status_config(const char *k, co
        }
        if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
 +              if (!v)
 +                      return config_error_nonbool(k);
                color_parse(v, k, wt_status_colors[slot]);
                return 0;
        }