Merge branch 'nd/narrow' (early part) into jc/add-i-t-a
authorJunio C Hamano <gitster@pobox.com>
Sat, 29 Nov 2008 01:22:35 +0000 (17:22 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 29 Nov 2008 01:22:35 +0000 (17:22 -0800)
* 'nd/narrow' (early part):
Extend index to save more flags

1  2 
cache.h
read-cache.c
diff --combined cache.h
index 487e5e1b1bf66007a2d2d753daaf97ddb879b85a,964f7e31cb3548c8f5c0ddf0f31c0a11d0c6e97b..ef2e7f93f766ded444bd5f8c82d5df8717ff70f5
+++ b/cache.h
@@@ -115,6 -115,26 +115,26 @@@ struct ondisk_cache_entry 
        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 {
        unsigned int ce_ctime;
        unsigned int ce_mtime;
  #define CE_VALID     (0x8000)
  #define CE_STAGESHIFT 12
  
- /* In-memory only */
+ /*
+  * Range 0xFFFF0000 in ce_flags is divided into
+  * two parts: in-memory flags and on-disk ones.
+  * Flags in CE_EXTENDED_FLAGS will get saved on-disk
+  * if you want to save a new flag, add it in
+  * CE_EXTENDED_FLAGS
+  *
+  * In-memory only flags
+  */
  #define CE_UPDATE    (0x10000)
  #define CE_REMOVE    (0x20000)
  #define CE_UPTODATE  (0x40000)
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
  
+ /*
+  * Extended on-disk flags
+  */
+ /* CE_EXTENDED2 is for future extension */
+ #define CE_EXTENDED2 0x80000000
+ #define CE_EXTENDED_FLAGS (0)
+ /*
+  * Safeguard to avoid saving wrong flags:
+  *  - CE_EXTENDED2 won't get saved until its semantic is known
+  *  - Bits in 0x0000FFFF have been saved in ce_flags already
+  *  - Bits in 0x003F0000 are currently in-memory flags
+  */
+ #if CE_EXTENDED_FLAGS & 0x803FFFFF
+ #error "CE_EXTENDED_FLAGS out of range"
+ #endif
  /*
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
@@@ -177,7 -223,9 +223,9 @@@ static inline size_t ce_namelen(const s
  }
  
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
- #define ondisk_ce_size(ce) ondisk_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_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
@@@ -220,8 -268,10 +268,10 @@@ static inline int ce_to_dtype(const str
        (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
        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)
+ #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
+ #define cache_entry_size(len) flexible_size(cache_entry,len)
+ #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;
@@@ -262,8 -312,6 +312,8 @@@ static inline void remove_name_hash(str
  
  #define read_cache() read_index(&the_index)
  #define read_cache_from(path) read_index_from(&the_index, (path))
 +#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 +#define is_cache_unborn() is_index_unborn(&the_index)
  #define read_cache_unmerged() read_index_unmerged(&the_index)
  #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
  #define discard_cache() discard_index(&the_index)
  #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, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 +#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #endif
  
  enum object_type {
@@@ -369,9 -416,7 +419,9 @@@ extern int init_db(const char *template
  
  /* Initialize and use the cache information */
  extern int read_index(struct index_state *);
 +extern int read_index_preload(struct index_state *, const char **pathspec);
  extern int read_index_from(struct index_state *, const char *path);
 +extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
  extern int write_index(const struct index_state *, int newfd);
  extern int discard_index(struct index_state *);
@@@ -398,7 -443,6 +448,7 @@@ extern int add_to_index(struct index_st
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 +extern int index_name_is_other(const struct index_state *, const char *, int);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
@@@ -427,8 -471,6 +477,8 @@@ struct lock_file 
        char on_list;
        char filename[PATH_MAX];
  };
 +#define LOCK_DIE_ON_ERROR 1
 +#define LOCK_NODEREF 2
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
@@@ -438,7 -480,7 +488,7 @@@ extern int commit_locked_index(struct l
  extern void set_alternate_index_output(const char *);
  extern int close_lock_file(struct lock_file *);
  extern void rollback_lock_file(struct lock_file *);
 -extern int delete_ref(const char *, const unsigned char *sha1);
 +extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -460,7 -502,6 +510,7 @@@ extern size_t packed_git_limit
  extern size_t delta_base_cache_limit;
  extern int auto_crlf;
  extern int fsync_object_files;
 +extern int core_preload_index;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -500,13 -541,6 +550,13 @@@ extern int check_repository_format(void
  #define DATA_CHANGED    0x0020
  #define TYPE_CHANGED    0x0040
  
 +extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 +      __attribute__((format (printf, 3, 4)));
 +extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
 +      __attribute__((format (printf, 3, 4)));
 +extern char *git_pathdup(const char *fmt, ...)
 +      __attribute__((format (printf, 1, 2)));
 +
  /* Return a statically allocated filename matching the sha1 signature */
  extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
@@@ -531,13 -565,6 +581,13 @@@ static inline void hashclr(unsigned cha
  {
        memset(hash, 0, 20);
  }
 +extern int is_empty_blob_sha1(const unsigned char *sha1);
 +
 +#define EMPTY_TREE_SHA1_HEX \
 +      "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 +#define EMPTY_TREE_SHA1_BIN \
 +       "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
 +       "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
@@@ -583,16 -610,12 +633,16 @@@ extern int force_object_loose(const uns
  /* just like read_sha1_file(), but non fatal in presence of bad objects */
  extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
  
 +/* global flag to enable extra checks when accessing packed objects */
 +extern int do_check_packed_object_crc;
 +
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
  
  extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
  extern int has_sha1_file(const unsigned char *sha1);
 +extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
  extern int has_pack_file(const unsigned char *sha1);
  extern int has_pack_index(const unsigned char *sha1);
@@@ -701,8 -724,7 +751,8 @@@ extern struct packed_git 
        int index_version;
        time_t mtime;
        int pack_fd;
 -      int pack_local;
 +      unsigned pack_local:1,
 +               pack_keep:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@@ -774,7 -796,7 +824,7 @@@ extern const unsigned char *nth_packed_
  extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
  extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
 -extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 +extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
  extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
  extern int matches_pack_name(struct packed_git *p, const char *name);
diff --combined read-cache.c
index 22a814311d2cfc032129b43f6da56706c7026b9c,ea262fc37e2eb38da4bfbe507989867f9e68393a..abc627ba0b42244a0bc609fe5e26fb08c101319e
@@@ -160,7 -160,7 +160,7 @@@ static int ce_modified_check_fs(struct 
        return 0;
  }
  
 -static int is_empty_blob_sha1(const unsigned char *sha1)
 +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,
@@@ -1098,7 -1098,7 +1098,7 @@@ static int verify_hdr(struct cache_head
  
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
                return error("bad signature");
-       if (hdr->hdr_version != htonl(2))
+       if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
                return error("bad index version");
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, hdr, size - 20);
@@@ -1133,6 -1133,7 +1133,7 @@@ int read_index(struct index_state *ista
  static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
  {
        size_t len;
+       const char *name;
  
        ce->ce_ctime = ntohl(ondisk->ctime.sec);
        ce->ce_mtime = ntohl(ondisk->mtime.sec);
        /* On-disk flags are just 16 bits */
        ce->ce_flags = ntohs(ondisk->flags);
  
-       /* For future extension: we do not understand this entry yet */
-       if (ce->ce_flags & CE_EXTENDED)
-               die("Unknown index entry format");
        hashcpy(ce->sha1, ondisk->sha1);
  
        len = ce->ce_flags & CE_NAMEMASK;
+       if (ce->ce_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;
+               /* 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;
+               name = ondisk2->name;
+       }
+       else
+               name = ondisk->name;
        if (len == CE_NAMEMASK)
-               len = strlen(ondisk->name);
+               len = strlen(name);
        /*
         * NEEDSWORK: If the original index is crafted, this copy could
         * go unchecked.
         */
-       memcpy(ce->name, ondisk->name, len + 1);
+       memcpy(ce->name, name, len + 1);
  }
  
  static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
@@@ -1269,11 -1282,6 +1282,11 @@@ unmap
        die("index file corrupt");
  }
  
 +int is_index_unborn(struct index_state *istate)
 +{
 +      return (!istate->cache_nr && !istate->alloc && !istate->timestamp);
 +}
 +
  int discard_index(struct index_state *istate)
  {
        istate->cache_nr = 0;
@@@ -1422,6 -1430,7 +1435,7 @@@ static int ce_write_entry(git_SHA_CTX *
  {
        int size = ondisk_ce_size(ce);
        struct ondisk_cache_entry *ondisk = xcalloc(1, size);
+       char *name;
  
        ondisk->ctime.sec = htonl(ce->ce_ctime);
        ondisk->ctime.nsec = 0;
        ondisk->size = htonl(ce->ce_size);
        hashcpy(ondisk->sha1, ce->sha1);
        ondisk->flags = htons(ce->ce_flags);
-       memcpy(ondisk->name, ce->name, ce_namelen(ce));
+       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;
+       }
+       else
+               name = ondisk->name;
+       memcpy(name, ce->name, ce_namelen(ce));
  
        return ce_write(c, fd, ondisk, size);
  }
@@@ -1444,16 -1461,25 +1466,25 @@@ int write_index(const struct index_stat
  {
        git_SHA_CTX c;
        struct cache_header hdr;
-       int i, err, removed;
+       int i, err, removed, extended;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
  
-       for (i = removed = 0; i < entries; i++)
+       for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
                        removed++;
  
+               /* reduce extended entries if possible */
+               cache[i]->ce_flags &= ~CE_EXTENDED;
+               if (cache[i]->ce_flags & CE_EXTENDED_FLAGS) {
+                       extended++;
+                       cache[i]->ce_flags |= CE_EXTENDED;
+               }
+       }
        hdr.hdr_signature = htonl(CACHE_SIGNATURE);
-       hdr.hdr_version = htonl(2);
+       /* for extended format, increase version so older git won't try to read it */
+       hdr.hdr_version = htonl(extended ? 3 : 2);
        hdr.hdr_entries = htonl(entries - removed);
  
        git_SHA1_Init(&c);
  int read_index_unmerged(struct index_state *istate)
  {
        int i;
 -      struct cache_entry **dst;
 -      struct cache_entry *last = NULL;
 +      int unmerged = 0;
  
        read_index(istate);
 -      dst = istate->cache;
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i];
 -              if (ce_stage(ce)) {
 -                      remove_name_hash(ce);
 -                      if (last && !strcmp(ce->name, last->name))
 -                              continue;
 -                      cache_tree_invalidate_path(istate->cache_tree, ce->name);
 -                      last = ce;
 +              struct cache_entry *new_ce;
 +              int size, len;
 +
 +              if (!ce_stage(ce))
                        continue;
 -              }
 -              *dst++ = ce;
 +              unmerged = 1;
 +              len = strlen(ce->name);
 +              size = cache_entry_size(len);
 +              new_ce = xcalloc(1, size);
 +              hashcpy(new_ce->sha1, ce->sha1);
 +              memcpy(new_ce->name, ce->name, len);
 +              new_ce->ce_flags = create_ce_flags(len, 0);
 +              new_ce->ce_mode = ce->ce_mode;
 +              if (add_index_entry(istate, new_ce, 0))
 +                      return error("%s: cannot drop to stage #0",
 +                                   ce->name);
 +              i = index_name_pos(istate, new_ce->name, len);
        }
 -      istate->cache_nr = dst - istate->cache;
 -      return !!last;
 +      return unmerged;
  }
  
  struct update_callback_data
@@@ -1575,30 -1596,3 +1606,30 @@@ int add_files_to_cache(const char *pref
        return !!data.add_errors;
  }
  
 +/*
 + * Returns 1 if the path is an "other" path with respect to
 + * the index; that is, the path is not mentioned in the index at all,
 + * either as a file, a directory with some files in the index,
 + * or as an unmerged entry.
 + *
 + * We helpfully remove a trailing "/" from directories so that
 + * the output of read_directory can be used as-is.
 + */
 +int index_name_is_other(const struct index_state *istate, const char *name,
 +              int namelen)
 +{
 +      int pos;
 +      if (namelen && name[namelen - 1] == '/')
 +              namelen--;
 +      pos = index_name_pos(istate, name, namelen);
 +      if (0 <= pos)
 +              return 0;       /* exact match */
 +      pos = -pos - 1;
 +      if (pos < istate->cache_nr) {
 +              struct cache_entry *ce = istate->cache[pos];
 +              if (ce_namelen(ce) == namelen &&
 +                  !memcmp(ce->name, name, namelen))
 +                      return 0; /* Yup, this one exists unmerged */
 +      }
 +      return 1;
 +}