Merge branch 'nd/index-errno'
authorJunio C Hamano <gitster@pobox.com>
Wed, 22 Aug 2012 18:51:42 +0000 (11:51 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 22 Aug 2012 18:51:42 +0000 (11:51 -0700)
Assignments to errno before calling system functions that used to
matter in the old code were left behind after the code structure
changed sufficiently to make them useless.

* nd/index-errno:
  read_index_from: remove bogus errno assignments

1  2 
read-cache.c

diff --combined read-cache.c
index 2f8159fb165f853aafb5e0cee61f35aa854271ec,5a78d6b05fd87c4e2a96b294a51ba8659416c3aa..76513cdc8fbe417e189164a7e9aa1cd4646a3ac2
  #include "commit.h"
  #include "blob.h"
  #include "resolve-undo.h"
 +#include "strbuf.h"
 +#include "varint.h"
  
  static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  
 +/* Mask for the name length in ce_flags in the on-disk index */
 +
 +#define CE_NAMEMASK  (0x0fff)
 +
  /* Index extensions.
   *
   * The first letter should be 'A'..'Z' for extensions that are not
@@@ -58,8 -52,8 +58,8 @@@ void rename_index_entry_at(struct index
  
        new = xmalloc(cache_entry_size(namelen));
        copy_cache_entry(new, old);
 -      new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK);
 -      new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen);
 +      new->ce_flags &= ~CE_STATE_MASK;
 +      new->ce_namelen = namelen;
        memcpy(new->name, new_name, namelen + 1);
  
        cache_tree_invalidate_path(istate->cache_tree, old->name);
@@@ -163,6 -157,16 +163,6 @@@ static int ce_modified_check_fs(struct 
        return 0;
  }
  
 -static int is_empty_blob_sha1(const unsigned char *sha1)
 -{
 -      static const unsigned char empty_blob_sha1[20] = {
 -              0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
 -              0x29,0xae,0x77,0x5a,0xd8,0xc2,0xe4,0x8c,0x53,0x91
 -      };
 -
 -      return !hashcmp(sha1, empty_blob_sha1);
 -}
 -
  static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
  {
        unsigned int changed = 0;
@@@ -399,8 -403,10 +399,8 @@@ int df_name_compare(const char *name1, 
        return c1 - c2;
  }
  
 -int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
 +int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
  {
 -      int len1 = flags1 & CE_NAMEMASK;
 -      int len2 = flags2 & CE_NAMEMASK;
        int len = len1 < len2 ? len1 : len2;
        int cmp;
  
        if (len1 > len2)
                return 1;
  
 -      /* Compare stages  */
 -      flags1 &= CE_STAGEMASK;
 -      flags2 &= CE_STAGEMASK;
 -
 -      if (flags1 < flags2)
 +      if (stage1 < stage2)
                return -1;
 -      if (flags1 > flags2)
 +      if (stage1 > stage2)
                return 1;
        return 0;
  }
  
 -int index_name_pos(const struct index_state *istate, const char *name, int namelen)
 +int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
 +{
 +      return cache_name_stage_compare(name1, len1, 0, name2, len2, 0);
 +}
 +
 +int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
  {
        int first, last;
  
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = istate->cache[next];
 -              int cmp = cache_name_compare(name, namelen, ce->name, ce->ce_flags);
 +              int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
                if (!cmp)
                        return next;
                if (cmp < 0) {
        return -first-1;
  }
  
 +int index_name_pos(const struct index_state *istate, const char *name, int namelen)
 +{
 +      return index_name_stage_pos(istate, name, namelen, 0);
 +}
 +
  /* Remove entry, return true if there are more entries to go.. */
  int remove_index_entry_at(struct index_state *istate, int pos)
  {
@@@ -589,7 -589,7 +589,7 @@@ int add_to_index(struct index_state *is
        size = cache_entry_size(namelen);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
 -      ce->ce_flags = namelen;
 +      ce->ce_namelen = namelen;
        if (!intent_only)
                fill_stat_cache_info(ce, st);
        else
@@@ -691,8 -691,7 +691,8 @@@ struct cache_entry *make_cache_entry(un
  
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, path, len);
 -      ce->ce_flags = create_ce_flags(len, stage);
 +      ce->ce_flags = create_ce_flags(stage);
 +      ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
  
        if (refresh)
@@@ -727,12 -726,11 +727,12 @@@ static int verify_dotfile(const char *r
         * has already been discarded, we now test
         * the rest.
         */
 -      switch (*rest) {
 +
        /* "." is not allowed */
 -      case '\0': case '/':
 +      if (*rest == '\0' || is_dir_sep(*rest))
                return 0;
  
 +      switch (*rest) {
        /*
         * ".git" followed by  NUL or slash is bad. This
         * shares the path end test with the ".." case.
                rest += 2;
        /* fallthrough */
        case '.':
 -              if (rest[1] == '\0' || rest[1] == '/')
 +              if (rest[1] == '\0' || is_dir_sep(rest[1]))
                        return 0;
        }
        return 1;
@@@ -755,19 -753,23 +755,19 @@@ int verify_path(const char *path
  {
        char c;
  
 +      if (has_dos_drive_prefix(path))
 +              return 0;
 +
        goto inside;
        for (;;) {
                if (!c)
                        return 1;
 -              if (c == '/') {
 +              if (is_dir_sep(c)) {
  inside:
                        c = *path++;
 -                      switch (c) {
 -                      default:
 -                              continue;
 -                      case '/': case '\0':
 -                              break;
 -                      case '.':
 -                              if (verify_dotfile(path))
 -                                      continue;
 -                      }
 -                      return 0;
 +                      if ((c == '.' && !verify_dotfile(path)) ||
 +                          is_dir_sep(c) || c == '\0')
 +                              return 0;
                }
                c = *path++;
        }
@@@ -829,7 -831,7 +829,7 @@@ static int has_dir_name(struct index_st
                }
                len = slash - name;
  
 -              pos = index_name_pos(istate, name, create_ce_flags(len, stage));
 +              pos = index_name_stage_pos(istate, name, len, stage);
                if (pos >= 0) {
                        /*
                         * Found one, but not so fast.  This could
@@@ -919,7 -921,7 +919,7 @@@ static int add_index_entry_with_check(s
        int new_only = option & ADD_CACHE_NEW_ONLY;
  
        cache_tree_invalidate_path(istate->cache_tree, ce->name);
 -      pos = index_name_pos(istate, ce->name, ce->ce_flags);
 +      pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
  
        /* existing match? Just replace it. */
        if (pos >= 0) {
                if (!ok_to_replace)
                        return error("'%s' appears as both a file and as a directory",
                                     ce->name);
 -              pos = index_name_pos(istate, ce->name, ce->ce_flags);
 +              pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
                pos = -pos-1;
        }
        return pos + 1;
@@@ -1002,8 -1004,7 +1002,8 @@@ int add_index_entry(struct index_state 
   */
  static struct cache_entry *refresh_cache_ent(struct index_state *istate,
                                             struct cache_entry *ce,
 -                                           unsigned int options, int *err)
 +                                           unsigned int options, int *err,
 +                                           int *changed_ret)
  {
        struct stat st;
        struct cache_entry *updated;
        }
  
        changed = ie_match_stat(istate, ce, &st, options);
 +      if (changed_ret)
 +              *changed_ret = changed;
        if (!changed) {
                /*
                 * The path is unchanged.  If we were told to ignore
@@@ -1106,31 -1105,19 +1106,31 @@@ int refresh_index(struct index_state *i
        int first = 1;
        int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
        unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
 -      const char *needs_update_fmt;
 -      const char *needs_merge_fmt;
 -
 -      needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
 -      needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
 +      const char *modified_fmt;
 +      const char *deleted_fmt;
 +      const char *typechange_fmt;
 +      const char *added_fmt;
 +      const char *unmerged_fmt;
 +
 +      modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
 +      deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
 +      typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n");
 +      added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n");
 +      unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce, *new;
                int cache_errno = 0;
 +              int changed = 0;
 +              int filtered = 0;
  
                ce = istate->cache[i];
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
  
 +              if (pathspec &&
 +                  !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
 +                      filtered = 1;
 +
                if (ce_stage(ce)) {
                        while ((i < istate->cache_nr) &&
                               ! strcmp(istate->cache[i]->name, ce->name))
                        i--;
                        if (allow_unmerged)
                                continue;
 -                      show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg);
 +                      if (!filtered)
 +                              show_file(unmerged_fmt, ce->name, in_porcelain,
 +                                        &first, header_msg);
                        has_errors = 1;
                        continue;
                }
  
 -              if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
 +              if (filtered)
                        continue;
  
 -              new = refresh_cache_ent(istate, ce, options, &cache_errno);
 +              new = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
                if (new == ce)
                        continue;
                if (!new) {
 +                      const char *fmt;
 +
                        if (not_new && cache_errno == ENOENT)
                                continue;
                        if (really && cache_errno == EINVAL) {
                        }
                        if (quiet)
                                continue;
 -                      show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg);
 +
 +                      if (cache_errno == ENOENT)
 +                              fmt = deleted_fmt;
 +                      else if (ce->ce_flags & CE_INTENT_TO_ADD)
 +                              fmt = added_fmt; /* must be before other checks */
 +                      else if (changed & TYPE_CHANGED)
 +                              fmt = typechange_fmt;
 +                      else
 +                              fmt = modified_fmt;
 +                      show_file(fmt,
 +                                ce->name, in_porcelain, &first, header_msg);
                        has_errors = 1;
                        continue;
                }
  
  static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
  {
 -      return refresh_cache_ent(&the_index, ce, really, NULL);
 +      return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
  }
  
 +
 +/*****************************************************************
 + * Index File I/O
 + *****************************************************************/
 +
 +#define INDEX_FORMAT_DEFAULT 3
 +
 +/*
 + * dev/ino/uid/gid/size are also just tracked to the low 32 bits
 + * Again - this is just a (very strong in practice) heuristic that
 + * the inode hasn't changed.
 + *
 + * 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 */
 +};
 +
 +/*
 + * This struct is used when CE_EXTENDED bit is 1
 + * The struct must match ondisk_cache_entry exactly from
 + * ctime till flags
 + */
 +struct ondisk_cache_entry_extended {
 +      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;
 +      unsigned short flags2;
 +      char name[FLEX_ARRAY]; /* more */
 +};
 +
 +/* These are only used for v3 or lower */
 +#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
 +#define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
 +#define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
 +#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
 +                          ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
 +                          ondisk_cache_entry_size(ce_namelen(ce)))
 +
  static int verify_hdr(struct cache_header *hdr, unsigned long size)
  {
        git_SHA_CTX c;
        unsigned char sha1[20];
 +      int hdr_version;
  
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
                return error("bad signature");
 -      if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
 -              return error("bad index version");
 +      hdr_version = ntohl(hdr->hdr_version);
 +      if (hdr_version < 2 || 4 < hdr_version)
 +              return error("bad index version %d", hdr_version);
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, hdr, size - 20);
        git_SHA1_Final(sha1, &c);
@@@ -1291,116 -1205,63 +1291,116 @@@ int read_index(struct index_state *ista
        return read_index_from(istate, get_index_file());
  }
  
 -static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
 +#ifndef NEEDS_ALIGNED_ACCESS
 +#define ntoh_s(var) ntohs(var)
 +#define ntoh_l(var) ntohl(var)
 +#else
 +static inline uint16_t ntoh_s_force_align(void *p)
 +{
 +      uint16_t x;
 +      memcpy(&x, p, sizeof(x));
 +      return ntohs(x);
 +}
 +static inline uint32_t ntoh_l_force_align(void *p)
 +{
 +      uint32_t x;
 +      memcpy(&x, p, sizeof(x));
 +      return ntohl(x);
 +}
 +#define ntoh_s(var) ntoh_s_force_align(&(var))
 +#define ntoh_l(var) ntoh_l_force_align(&(var))
 +#endif
 +
 +static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk,
 +                                                 unsigned int flags,
 +                                                 const char *name,
 +                                                 size_t len)
  {
 +      struct cache_entry *ce = xmalloc(cache_entry_size(len));
 +
 +      ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
 +      ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
 +      ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
 +      ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
 +      ce->ce_dev   = ntoh_l(ondisk->dev);
 +      ce->ce_ino   = ntoh_l(ondisk->ino);
 +      ce->ce_mode  = ntoh_l(ondisk->mode);
 +      ce->ce_uid   = ntoh_l(ondisk->uid);
 +      ce->ce_gid   = ntoh_l(ondisk->gid);
 +      ce->ce_size  = ntoh_l(ondisk->size);
 +      ce->ce_flags = flags & ~CE_NAMEMASK;
 +      ce->ce_namelen = len;
 +      hashcpy(ce->sha1, ondisk->sha1);
 +      memcpy(ce->name, name, len);
 +      ce->name[len] = '\0';
 +      return ce;
 +}
 +
 +/*
 + * Adjacent cache entries tend to share the leading paths, so it makes
 + * sense to only store the differences in later entries.  In the v4
 + * on-disk format of the index, each on-disk cache entry stores the
 + * number of bytes to be stripped from the end of the previous name,
 + * and the bytes to append to the result, to come up with its name.
 + */
 +static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
 +{
 +      const unsigned char *ep, *cp = (const unsigned char *)cp_;
 +      size_t len = decode_varint(&cp);
 +
 +      if (name->len < len)
 +              die("malformed name field in the index");
 +      strbuf_remove(name, name->len - len, len);
 +      for (ep = cp; *ep; ep++)
 +              ; /* find the end */
 +      strbuf_add(name, cp, ep - cp);
 +      return (const char *)ep + 1 - cp_;
 +}
 +
 +static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
 +                                          unsigned long *ent_size,
 +                                          struct strbuf *previous_name)
 +{
 +      struct cache_entry *ce;
        size_t len;
        const char *name;
 +      unsigned int flags;
  
 -      ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
 -      ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
 -      ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
 -      ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
 -      ce->ce_dev   = ntohl(ondisk->dev);
 -      ce->ce_ino   = ntohl(ondisk->ino);
 -      ce->ce_mode  = ntohl(ondisk->mode);
 -      ce->ce_uid   = ntohl(ondisk->uid);
 -      ce->ce_gid   = ntohl(ondisk->gid);
 -      ce->ce_size  = ntohl(ondisk->size);
        /* On-disk flags are just 16 bits */
 -      ce->ce_flags = ntohs(ondisk->flags);
 +      flags = ntoh_s(ondisk->flags);
 +      len = flags & CE_NAMEMASK;
  
 -      hashcpy(ce->sha1, ondisk->sha1);
 -
 -      len = ce->ce_flags & CE_NAMEMASK;
 -
 -      if (ce->ce_flags & CE_EXTENDED) {
 +      if (flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                int extended_flags;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
 -              extended_flags = ntohs(ondisk2->flags2) << 16;
 +              extended_flags = ntoh_s(ondisk2->flags2) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
                        die("Unknown index entry format %08x", extended_flags);
 -              ce->ce_flags |= extended_flags;
 +              flags |= extended_flags;
                name = ondisk2->name;
        }
        else
                name = ondisk->name;
  
 -      if (len == CE_NAMEMASK)
 -              len = strlen(name);
 -      /*
 -       * NEEDSWORK: If the original index is crafted, this copy could
 -       * go unchecked.
 -       */
 -      memcpy(ce->name, name, len + 1);
 -}
 -
 -static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
 -{
 -      long per_entry;
 -
 -      per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry);
 -
 -      /*
 -       * Alignment can cause differences. This should be "alignof", but
 -       * since that's a gcc'ism, just use the size of a pointer.
 -       */
 -      per_entry += sizeof(void *);
 -      return ondisk_size + entries*per_entry;
 +      if (!previous_name) {
 +              /* v3 and earlier */
 +              if (len == CE_NAMEMASK)
 +                      len = strlen(name);
 +              ce = cache_entry_from_ondisk(ondisk, flags, name, len);
 +
 +              *ent_size = ondisk_ce_size(ce);
 +      } else {
 +              unsigned long consumed;
 +              consumed = expand_name_field(previous_name, name);
 +              ce = cache_entry_from_ondisk(ondisk, flags,
 +                                           previous_name->buf,
 +                                           previous_name->len);
 +
 +              *ent_size = (name - ((char *)ondisk)) + consumed;
 +      }
 +      return ce;
  }
  
  /* remember to discard_cache() before reading a different cache! */
@@@ -1408,17 -1269,14 +1408,15 @@@ int read_index_from(struct index_state 
  {
        int fd, i;
        struct stat st;
 -      unsigned long src_offset, dst_offset;
 +      unsigned long src_offset;
        struct cache_header *hdr;
        void *mmap;
        size_t mmap_size;
 +      struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
  
-       errno = EBUSY;
        if (istate->initialized)
                return istate->cache_nr;
  
-       errno = ENOENT;
        istate->timestamp.sec = 0;
        istate->timestamp.nsec = 0;
        fd = open(path, O_RDONLY);
        if (fstat(fd, &st))
                die_errno("cannot stat the open index");
  
-       errno = EINVAL;
        mmap_size = xsize_t(st.st_size);
        if (mmap_size < sizeof(struct cache_header) + 20)
                die("index file smaller than expected");
  
        mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-       close(fd);
        if (mmap == MAP_FAILED)
                die_errno("unable to map index file");
+       close(fd);
  
        hdr = mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
  
 +      istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
        istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
 -
 -      /*
 -       * The disk format is actually larger than the in-memory format,
 -       * due to space for nsec etc, so even though the in-memory one
 -       * has room for a few  more flags, we can allocate using the same
 -       * index size
 -       */
 -      istate->alloc = xmalloc(estimate_cache_size(mmap_size, istate->cache_nr));
        istate->initialized = 1;
  
 +      if (istate->version == 4)
 +              previous_name = &previous_name_buf;
 +      else
 +              previous_name = NULL;
 +
        src_offset = sizeof(*hdr);
 -      dst_offset = 0;
        for (i = 0; i < istate->cache_nr; i++) {
                struct ondisk_cache_entry *disk_ce;
                struct cache_entry *ce;
 +              unsigned long consumed;
  
                disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
 -              ce = (struct cache_entry *)((char *)istate->alloc + dst_offset);
 -              convert_from_disk(disk_ce, ce);
 +              ce = create_from_disk(disk_ce, &consumed, previous_name);
                set_index_entry(istate, i, ce);
  
 -              src_offset += ondisk_ce_size(ce);
 -              dst_offset += ce_size(ce);
 +              src_offset += consumed;
        }
 +      strbuf_release(&previous_name_buf);
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
  
  
  unmap:
        munmap(mmap, mmap_size);
-       errno = EINVAL;
        die("index file corrupt");
  }
  
  int is_index_unborn(struct index_state *istate)
  {
 -      return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
 +      return (!istate->cache_nr && !istate->timestamp.sec);
  }
  
  int discard_index(struct index_state *istate)
  {
 +      int i;
 +
 +      for (i = 0; i < istate->cache_nr; i++)
 +              free(istate->cache[i]);
        resolve_undo_clear_index(istate);
        istate->cache_nr = 0;
        istate->cache_changed = 0;
        istate->name_hash_initialized = 0;
        free_hash(&istate->name_hash);
        cache_tree_free(&(istate->cache_tree));
 -      free(istate->alloc);
 -      istate->alloc = NULL;
        istate->initialized = 0;
  
        /* no need to throw away allocated active_cache */
@@@ -1652,11 -1509,12 +1648,11 @@@ static void ce_smudge_racily_clean_entr
        }
  }
  
 -static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
 +/* Copy miscellaneous fields but not the name */
 +static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
 +                                     struct cache_entry *ce)
  {
 -      int size = ondisk_ce_size(ce);
 -      struct ondisk_cache_entry *ondisk = xcalloc(1, size);
 -      char *name;
 -      int result;
 +      short flags;
  
        ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
        ondisk->gid  = htonl(ce->ce_gid);
        ondisk->size = htonl(ce->ce_size);
        hashcpy(ondisk->sha1, ce->sha1);
 -      ondisk->flags = htons(ce->ce_flags);
 +
 +      flags = ce->ce_flags;
 +      flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
 +      ondisk->flags = htons(flags);
        if (ce->ce_flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
                ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
 -              name = ondisk2->name;
 +              return ondisk2->name;
 +      }
 +      else {
 +              return ondisk->name;
 +      }
 +}
 +
 +static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
 +                        struct strbuf *previous_name)
 +{
 +      int size;
 +      struct ondisk_cache_entry *ondisk;
 +      char *name;
 +      int result;
 +
 +      if (!previous_name) {
 +              size = ondisk_ce_size(ce);
 +              ondisk = xcalloc(1, size);
 +              name = copy_cache_entry_to_ondisk(ondisk, ce);
 +              memcpy(name, ce->name, ce_namelen(ce));
 +      } else {
 +              int common, to_remove, prefix_size;
 +              unsigned char to_remove_vi[16];
 +              for (common = 0;
 +                   (ce->name[common] &&
 +                    common < previous_name->len &&
 +                    ce->name[common] == previous_name->buf[common]);
 +                   common++)
 +                      ; /* still matching */
 +              to_remove = previous_name->len - common;
 +              prefix_size = encode_varint(to_remove, to_remove_vi);
 +
 +              if (ce->ce_flags & CE_EXTENDED)
 +                      size = offsetof(struct ondisk_cache_entry_extended, name);
 +              else
 +                      size = offsetof(struct ondisk_cache_entry, name);
 +              size += prefix_size + (ce_namelen(ce) - common + 1);
 +
 +              ondisk = xcalloc(1, size);
 +              name = copy_cache_entry_to_ondisk(ondisk, ce);
 +              memcpy(name, to_remove_vi, prefix_size);
 +              memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
 +
 +              strbuf_splice(previous_name, common, to_remove,
 +                            ce->name + common, ce_namelen(ce) - common);
        }
 -      else
 -              name = ondisk->name;
 -      memcpy(name, ce->name, ce_namelen(ce));
  
        result = ce_write(c, fd, ondisk, size);
        free(ondisk);
@@@ -1758,11 -1572,10 +1754,11 @@@ int write_index(struct index_state *ist
  {
        git_SHA_CTX c;
        struct cache_header hdr;
 -      int i, err, removed, extended;
 +      int i, err, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
        struct stat st;
 +      struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
  
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
                }
        }
  
 +      if (!istate->version)
 +              istate->version = INDEX_FORMAT_DEFAULT;
 +
 +      /* demote version 3 to version 2 when the latter suffices */
 +      if (istate->version == 3 || istate->version == 2)
 +              istate->version = extended ? 3 : 2;
 +
 +      hdr_version = istate->version;
 +
        hdr.hdr_signature = htonl(CACHE_SIGNATURE);
 -      /* for extended format, increase version so older git won't try to read it */
 -      hdr.hdr_version = htonl(extended ? 3 : 2);
 +      hdr.hdr_version = htonl(hdr_version);
        hdr.hdr_entries = htonl(entries - removed);
  
        git_SHA1_Init(&c);
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
  
 +      previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
 -              if (ce_write_entry(&c, newfd, ce) < 0)
 +              if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
 +      strbuf_release(&previous_name_buf);
  
        /* Write extension data here */
        if (istate->cache_tree) {
@@@ -1856,12 -1659,11 +1852,12 @@@ int read_index_unmerged(struct index_st
                if (!ce_stage(ce))
                        continue;
                unmerged = 1;
 -              len = strlen(ce->name);
 +              len = ce_namelen(ce);
                size = cache_entry_size(len);
                new_ce = xcalloc(1, size);
                memcpy(new_ce->name, ce->name, len);
 -              new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
 +              new_ce->ce_flags = create_ce_flags(0) | CE_CONFLICTED;
 +              new_ce->ce_namelen = len;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",