index: make the index file format extensible.
[gitweb.git] / read-cache.c
index c5474d49758b640f8942b1e3eb1a1c8f47c78ed8..1f71d125780fbaecf613f8b979fcc04b1b630c97 100644 (file)
@@ -4,11 +4,26 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
+#include "cache-tree.h"
+
+/* Index extensions.
+ *
+ * The first letter should be 'A'..'Z' for extensions that are not
+ * necessary for a correct operation (i.e. optimization data).
+ * When new extensions are added that _needs_ to be understood in
+ * order to correctly interpret the index file, pick character that
+ * is outside the range, to cause the reader to abort.
+ */
+
+#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
+#define CACHE_EXT_TREE 0x54524545      /* "TREE" */
 
 struct cache_entry **active_cache = NULL;
 static time_t index_file_timestamp;
 unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
 
+struct cache_tree *active_cache_tree = NULL;
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -27,6 +42,9 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
        ce->ce_uid = htonl(st->st_uid);
        ce->ce_gid = htonl(st->st_gid);
        ce->ce_size = htonl(st->st_size);
+
+       if (assume_unchanged)
+               ce->ce_flags |= htons(CE_VALID);
 }
 
 static int ce_compare_data(struct cache_entry *ce, struct stat *st)
@@ -146,9 +164,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
        return changed;
 }
 
-int ce_match_stat(struct cache_entry *ce, struct stat *st)
+int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid)
 {
-       unsigned int changed = ce_match_stat_basic(ce, st);
+       unsigned int changed;
+
+       /*
+        * If it's marked as always valid in the index, it's
+        * valid whatever the checked-out copy says.
+        */
+       if (!ignore_valid && (ce->ce_flags & htons(CE_VALID)))
+               return 0;
+
+       changed = ce_match_stat_basic(ce, st);
 
        /*
         * Within 1 second of this sequence:
@@ -164,7 +191,7 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
         * effectively mean we can make at most one commit per second,
         * which is not acceptable.  Instead, we check cache entries
         * whose mtime are the same as the index file timestamp more
-        * careful than others.
+        * carefully than others.
         */
        if (!changed &&
            index_file_timestamp &&
@@ -174,10 +201,10 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
        return changed;
 }
 
-int ce_modified(struct cache_entry *ce, struct stat *st)
+int ce_modified(struct cache_entry *ce, struct stat *st, int really)
 {
        int changed, changed_fs;
-       changed = ce_match_stat(ce, st);
+       changed = ce_match_stat(ce, st, really);
        if (!changed)
                return 0;
        /*
@@ -233,6 +260,11 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
                return -1;
        if (len1 > len2)
                return 1;
+
+       /* Compare stages  */
+       flags1 &= CE_STAGEMASK;
+       flags2 &= CE_STAGEMASK;
+
        if (flags1 < flags2)
                return -1;
        if (flags1 > flags2)
@@ -430,6 +462,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
        int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
        int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
+
        pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
 
        /* existing match? Just replace it. */
@@ -495,6 +528,22 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
        return 0;
 }
 
+static int read_index_extension(const char *ext, void *data, unsigned long sz)
+{
+       switch (CACHE_EXT(ext)) {
+       case CACHE_EXT_TREE:
+               active_cache_tree = cache_tree_read(data, sz);
+               break;
+       default:
+               if (*ext < 'A' || 'Z' < *ext)
+                       return error("index uses %.4s extension, which we do not understand",
+                                    ext);
+               fprintf(stderr, "ignoring %.4s extension\n", ext);
+               break;
+       }
+       return 0;
+}
+
 int read_cache(void)
 {
        int fd, i;
@@ -543,6 +592,22 @@ int read_cache(void)
                active_cache[i] = ce;
        }
        index_file_timestamp = st.st_mtime;
+       while (offset <= size - 20 - 8) {
+               /* After an array of active_nr index entries,
+                * there can be arbitrary number of extended
+                * sections, each of which is prefixed with
+                * extension name (4-byte) and section length
+                * in 4-byte network byte order.
+                */
+               unsigned long extsize;
+               memcpy(&extsize, map + offset + 4, 4);
+               extsize = ntohl(extsize);
+               if (read_index_extension(map + offset,
+                                        map + offset + 8, extsize) < 0)
+                       goto unmap;
+               offset += 8;
+               offset += extsize;
+       }
        return active_nr;
 
 unmap:
@@ -577,6 +642,17 @@ static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
        return 0;
 }
 
+static int write_index_ext_header(SHA_CTX *context, int fd,
+                                 unsigned long ext, unsigned long sz)
+{
+       ext = htonl(ext);
+       sz = htonl(sz);
+       if ((ce_write(context, fd, &ext, 4) < 0) ||
+           (ce_write(context, fd, &sz, 4) < 0))
+               return -1;
+       return 0;
+}
+
 static int ce_flush(SHA_CTX *context, int fd)
 {
        unsigned int left = write_buffer_len;
@@ -673,5 +749,19 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
                if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
                        return -1;
        }
+
+       /* Write extension data here */
+       if (active_cache_tree) {
+               unsigned long sz;
+               void *data = cache_tree_write(active_cache_tree, &sz);
+               if (data &&
+                   !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
+                   !ce_write(&c, newfd, data, sz))
+                       ;
+               else {
+                       free(data);
+                       return -1;
+               }
+       }
        return ce_flush(&c, newfd);
 }