Merge branch 'nd/index-errno' into maint-1.7.11
authorJunio C Hamano <gitster@pobox.com>
Tue, 11 Sep 2012 17:53:21 +0000 (10:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 11 Sep 2012 17:53:21 +0000 (10:53 -0700)
* nd/index-errno:
read_index_from: remove bogus errno assignments

1  2 
read-cache.c
diff --combined read-cache.c
index 7d4bf68e33d35ebeb5e9f985ac5c9dc2962fc8ba,5a78d6b05fd87c4e2a96b294a51ba8659416c3aa..d8543e4d42f0cf7f8850751806cc40bc05578117
@@@ -12,8 -12,6 +12,8 @@@
  #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);
  
@@@ -159,6 -157,16 +159,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;
@@@ -397,15 -405,10 +397,15 @@@ int df_name_compare(const char *name1, 
  
  int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
  {
 -      int len1 = flags1 & CE_NAMEMASK;
 -      int len2 = flags2 & CE_NAMEMASK;
 -      int len = len1 < len2 ? len1 : len2;
 -      int cmp;
 +      int len1, len2, len, cmp;
 +
 +      len1 = flags1 & CE_NAMEMASK;
 +      if (CE_NAMEMASK <= len1)
 +              len1 = strlen(name1 + CE_NAMEMASK) + CE_NAMEMASK;
 +      len2 = flags2 & CE_NAMEMASK;
 +      if (CE_NAMEMASK <= len2)
 +              len2 = strlen(name2 + CE_NAMEMASK) + CE_NAMEMASK;
 +      len = len1 < len2 ? len1 : len2;
  
        cmp = memcmp(name1, name2, len);
        if (cmp)
@@@ -723,12 -726,11 +723,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;
@@@ -751,19 -753,23 +751,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++;
        }
@@@ -998,8 -1004,7 +998,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
@@@ -1102,31 -1105,19 +1102,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, strlen(ce->name), 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);
@@@ -1287,115 -1205,63 +1287,115 @@@ 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;
 +      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);
 -
 -      hashcpy(ce->sha1, ondisk->sha1);
 -
 -      len = ce->ce_flags & CE_NAMEMASK;
 +      flags = ntoh_s(ondisk->flags);
 +      len = 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! */
@@@ -1403,17 -1269,14 +1403,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 */
@@@ -1647,10 -1509,13 +1643,10 @@@ 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;
 -
        ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
        ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
                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);
@@@ -1748,11 -1572,10 +1744,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 (is_null_sha1(ce->sha1))
 +                      return error("cache entry has null sha1: %s", ce->name);
 +              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) {