Merge branch 'jc/index-v4'
authorJunio C Hamano <gitster@pobox.com>
Wed, 2 May 2012 20:51:13 +0000 (13:51 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 2 May 2012 20:51:13 +0000 (13:51 -0700)
Trivially shrinks the on-disk size of the index file to save both I/O and
checksum overhead.

The topic should give a solid base to build on further updates, with the
code refactoring in its earlier parts, and the backward compatibility
mechanism in its later parts.

* jc/index-v4:
index-v4: document the entry format
unpack-trees: preserve the index file version of original
update-index: upgrade/downgrade on-disk index version
read-cache.c: write prefix-compressed names in the index
read-cache.c: read prefix-compressed names in index on-disk version v4
read-cache.c: move code to copy incore to ondisk cache to a helper function
read-cache.c: move code to copy ondisk to incore cache to a helper function
read-cache.c: report the header version we do not understand
read-cache.c: make create_from_disk() report number of bytes it consumed
read-cache.c: allow unaligned mapping of the index file
cache.h: hide on-disk index details
varint: make it available outside the context of pack

1  2 
Makefile
cache.h
read-cache.c
unpack-trees.c
diff --combined Makefile
index d6748e075434e355180fba7ccfaf72299a332fb9,0f26c879d83d1707ea4aa51a1851efd01d7b4653..a14732c4cdfb4ce9990e1965c1b180b67e2d6938
+++ b/Makefile
@@@ -440,7 -440,6 +440,7 @@@ SCRIPT_PERL += git-send-email.per
  SCRIPT_PERL += git-svn.perl
  
  SCRIPT_PYTHON += git-remote-testgit.py
 +SCRIPT_PYTHON += git-p4.py
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@@ -481,11 -480,9 +481,11 @@@ TEST_PROGRAMS_NEED_X += test-genrando
  TEST_PROGRAMS_NEED_X += test-index-version
  TEST_PROGRAMS_NEED_X += test-line-buffer
  TEST_PROGRAMS_NEED_X += test-match-trees
 +TEST_PROGRAMS_NEED_X += test-mergesort
  TEST_PROGRAMS_NEED_X += test-mktemp
  TEST_PROGRAMS_NEED_X += test-parse-options
  TEST_PROGRAMS_NEED_X += test-path-utils
 +TEST_PROGRAMS_NEED_X += test-revision-walking
  TEST_PROGRAMS_NEED_X += test-run-command
  TEST_PROGRAMS_NEED_X += test-sha1
  TEST_PROGRAMS_NEED_X += test-sigchain
@@@ -593,7 -590,6 +593,7 @@@ LIB_H += log-tree.
  LIB_H += mailmap.h
  LIB_H += merge-file.h
  LIB_H += merge-recursive.h
 +LIB_H += mergesort.h
  LIB_H += notes.h
  LIB_H += notes-cache.h
  LIB_H += notes-merge.h
@@@ -631,6 -627,7 +631,7 @@@ LIB_H += tree-walk.
  LIB_H += unpack-trees.h
  LIB_H += userdiff.h
  LIB_H += utf8.h
+ LIB_H += varint.h
  LIB_H += xdiff-interface.h
  LIB_H += xdiff/xdiff.h
  
@@@ -698,7 -695,6 +699,7 @@@ LIB_OBJS += mailmap.
  LIB_OBJS += match-trees.o
  LIB_OBJS += merge-file.o
  LIB_OBJS += merge-recursive.o
 +LIB_OBJS += mergesort.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += notes.o
  LIB_OBJS += notes-cache.o
@@@ -757,6 -753,7 +758,7 @@@ LIB_OBJS += url.
  LIB_OBJS += usage.o
  LIB_OBJS += userdiff.o
  LIB_OBJS += utf8.o
+ LIB_OBJS += varint.o
  LIB_OBJS += walker.o
  LIB_OBJS += wrapper.o
  LIB_OBJS += write_or_die.o
@@@ -1854,13 -1851,6 +1856,13 @@@ DEFAULT_PAGER_CQ_SQ = $(subst ','\'',$(
  BASIC_CFLAGS += -DDEFAULT_PAGER='$(DEFAULT_PAGER_CQ_SQ)'
  endif
  
 +ifdef SHELL_PATH
 +SHELL_PATH_CQ = "$(subst ",\",$(subst \,\\,$(SHELL_PATH)))"
 +SHELL_PATH_CQ_SQ = $(subst ','\'',$(SHELL_PATH_CQ))
 +
 +BASIC_CFLAGS += -DSHELL_PATH='$(SHELL_PATH_CQ_SQ)'
 +endif
 +
  ALL_CFLAGS += $(BASIC_CFLAGS)
  ALL_LDFLAGS += $(BASIC_LDFLAGS)
  
@@@ -2270,8 -2260,6 +2272,8 @@@ $(XDIFF_LIB): $(XDIFF_OBJS
  $(VCSSVN_LIB): $(VCSSVN_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
  
 +export DEFAULT_EDITOR DEFAULT_PAGER
 +
  doc:
        $(MAKE) -C Documentation all
  
diff --combined cache.h
index 5bf59ff5c33919ac42ee79dea7911a773c1f698d,a3f1279a3edde0c084de9fee1ac708bbb93f6197..d8f6f1e78e1da3e47996bf7bcc9ac6e5b5f8e2b5
+++ b/cache.h
@@@ -105,6 -105,9 +105,9 @@@ struct cache_header 
        unsigned int hdr_entries;
  };
  
+ #define INDEX_FORMAT_LB 2
+ #define INDEX_FORMAT_UB 4
  /*
   * The "cache_time" is just the low 32 bits of the
   * time. It doesn't matter if it overflows - we only
@@@ -115,48 -118,6 +118,6 @@@ struct cache_time 
        unsigned int nsec;
  };
  
- /*
-  * 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 */
- };
  struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
@@@ -253,9 -214,6 +214,6 @@@ static inline size_t ce_namelen(const s
  }
  
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
- #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)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@@ -306,13 -264,11 +264,11 @@@ static inline unsigned int canon_mode(u
        return S_IFGITLINK;
  }
  
- #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
- #define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
- #define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
  struct index_state {
        struct cache_entry **cache;
+       unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
@@@ -625,8 -581,7 +581,8 @@@ enum push_default_type 
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
        PUSH_DEFAULT_UPSTREAM,
 -      PUSH_DEFAULT_CURRENT
 +      PUSH_DEFAULT_CURRENT,
 +      PUSH_DEFAULT_UNSPECIFIED
  };
  
  extern enum branch_track git_branch_track;
@@@ -709,19 -664,6 +665,19 @@@ static inline void hashclr(unsigned cha
  #define EMPTY_TREE_SHA1_BIN \
         ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
 +#define EMPTY_BLOB_SHA1_HEX \
 +      "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
 +#define EMPTY_BLOB_SHA1_BIN_LITERAL \
 +      "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
 +      "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 +#define EMPTY_BLOB_SHA1_BIN \
 +      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +
 +static inline int is_empty_blob_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +}
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
@@@ -942,22 -884,6 +898,22 @@@ extern const char *fmt_name(const char 
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
  
 +struct ident_split {
 +      const char *name_begin;
 +      const char *name_end;
 +      const char *mail_begin;
 +      const char *mail_end;
 +      const char *date_begin;
 +      const char *date_end;
 +      const char *tz_begin;
 +      const char *tz_end;
 +};
 +/*
 + * Signals an success with 0, but time part of the result may be NULL
 + * if the input lacks timestamp and zone
 + */
 +extern int split_ident_line(struct ident_split *, const char *, int);
 +
  struct checkout {
        const char *base_dir;
        int base_dir_len;
@@@ -1306,6 -1232,4 +1262,6 @@@ extern struct startup_info *startup_inf
  /* builtin/merge.c */
  int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
  
 +int sane_execvp(const char *file, char *const argv[]);
 +
  #endif /* CACHE_H */
diff --combined read-cache.c
index 6c8f3958369b0a2afd0ad85aea5ab23a44678f70,adda1daf03afc68af0ac3b678c7ca805f3aac440..ef355cc9a89b948688756b8c3681ac607518492b
@@@ -12,6 -12,8 +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);
  
@@@ -157,6 -159,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;
@@@ -1179,15 -1191,74 +1181,74 @@@ static struct cache_entry *refresh_cach
        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);
@@@ -1221,7 -1292,74 +1282,74 @@@ int read_index(struct index_state *ista
        return read_index_from(istate, get_index_file());
  }
  
- static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk)
+ #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;
        unsigned int flags;
  
        /* On-disk flags are just 16 bits */
-       flags = ntohs(ondisk->flags);
+       flags = ntoh_s(ondisk->flags);
        len = flags & CE_NAMEMASK;
  
        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);
        else
                name = ondisk->name;
  
-       if (len == CE_NAMEMASK)
-               len = strlen(name);
-       ce = xmalloc(cache_entry_size(len));
-       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);
-       ce->ce_flags = flags;
-       hashcpy(ce->sha1, ondisk->sha1);
-       memcpy(ce->name, name, len);
-       ce->name[len] = '\0';
+       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;
  }
  
@@@ -1279,6 -1412,7 +1402,7 @@@ int read_index_from(struct index_state 
        struct cache_header *hdr;
        void *mmap;
        size_t mmap_size;
+       struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
  
        errno = EBUSY;
        if (istate->initialized)
        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 *));
        istate->initialized = 1;
  
+       if (istate->version == 4)
+               previous_name = &previous_name_buf;
+       else
+               previous_name = NULL;
        src_offset = sizeof(*hdr);
        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 = create_from_disk(disk_ce);
+               ce = create_from_disk(disk_ce, &consumed, previous_name);
                set_index_entry(istate, i, ce);
  
-               src_offset += ondisk_ce_size(ce);
+               src_offset += consumed;
        }
+       strbuf_release(&previous_name_buf);
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
  
@@@ -1510,13 -1652,10 +1642,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);
@@@ -1573,10 -1753,11 +1743,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) {
diff --combined unpack-trees.c
index 36523da22aedba160c5a29f95bf339f05dfc6feb,2a037d6a423a7ab9d55036972e52dfad88d369b4..1d7393d84c7ca1506108495f4dd17233a5af7324
@@@ -102,28 -102,21 +102,28 @@@ void setup_unpack_trees_porcelain(struc
                opts->unpack_rejects[i].strdup_strings = 1;
  }
  
 -static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 -      unsigned int set, unsigned int clear)
 +static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 +                       unsigned int set, unsigned int clear)
  {
 -      unsigned int size = ce_size(ce);
 -      struct cache_entry *new = xmalloc(size);
 -
        clear |= CE_HASHED | CE_UNHASHED;
  
        if (set & CE_REMOVE)
                set |= CE_WT_REMOVE;
  
 +      ce->next = NULL;
 +      ce->ce_flags = (ce->ce_flags & ~clear) | set;
 +      add_index_entry(&o->result, ce,
 +                      ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 +}
 +
 +static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 +      unsigned int set, unsigned int clear)
 +{
 +      unsigned int size = ce_size(ce);
 +      struct cache_entry *new = xmalloc(size);
 +
        memcpy(new, ce, size);
 -      new->next = NULL;
 -      new->ce_flags = (new->ce_flags & ~clear) | set;
 -      add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 +      do_add_entry(o, new, set, clear);
  }
  
  /*
@@@ -594,7 -587,7 +594,7 @@@ static int unpack_nondirectories(int n
  
        for (i = 0; i < n; i++)
                if (src[i] && src[i] != o->df_conflict_entry)
 -                      add_entry(o, src[i], 0, 0);
 +                      do_add_entry(o, src[i], 0, 0);
        return 0;
  }
  
@@@ -779,7 -772,7 +779,7 @@@ static int unpack_callback(int n, unsig
        if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
                return -1;
  
 -      if (src[0]) {
 +      if (o->merge && src[0]) {
                if (ce_stage(src[0]))
                        mark_ce_used_same_name(src[0], o);
                else
@@@ -1027,6 -1020,7 +1027,7 @@@ int unpack_trees(unsigned len, struct t
        o->result.initialized = 1;
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
+       o->result.version = o->src_index->version;
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);