[PATCH] Fix rename/copy when dealing with temporarily broken pairs.
[gitweb.git] / read-cache.c
index 01e84b758b8f742ee852e08fcc20a14e3509821c..e9618f0914b36a4d1bd60785e53ad484e8748e36 100644 (file)
@@ -3,12 +3,31 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include <stdarg.h>
 #include "cache.h"
 
 struct cache_entry **active_cache = NULL;
 unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
 
+/*
+ * This only updates the "non-critical" parts of the directory
+ * cache, ie the parts that aren't tracked by GIT, and only used
+ * to validate the cache.
+ */
+void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
+{
+       ce->ce_ctime.sec = htonl(st->st_ctime);
+       ce->ce_mtime.sec = htonl(st->st_mtime);
+#ifdef USE_NSEC
+       ce->ce_ctime.nsec = htonl(st->st_ctim.tv_nsec);
+       ce->ce_mtime.nsec = htonl(st->st_mtim.tv_nsec);
+#endif
+       ce->ce_dev = htonl(st->st_dev);
+       ce->ce_ino = htonl(st->st_ino);
+       ce->ce_uid = htonl(st->st_uid);
+       ce->ce_gid = htonl(st->st_gid);
+       ce->ce_size = htonl(st->st_size);
+}
+
 int ce_match_stat(struct cache_entry *ce, struct stat *st)
 {
        unsigned int changed = 0;
@@ -31,7 +50,7 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
        if (ce->ce_ctime.sec != htonl(st->st_ctime))
                changed |= CTIME_CHANGED;
 
-#ifdef NSEC
+#ifdef USE_NSEC
        /*
         * nsec seems unreliable - not all filesystems support it, so
         * as long as it is in the inode cache you get right nsec
@@ -46,14 +65,43 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
        if (ce->ce_uid != htonl(st->st_uid) ||
            ce->ce_gid != htonl(st->st_gid))
                changed |= OWNER_CHANGED;
-       if (ce->ce_dev != htonl(st->st_dev) ||
-           ce->ce_ino != htonl(st->st_ino))
+       if (ce->ce_ino != htonl(st->st_ino))
                changed |= INODE_CHANGED;
+
+#ifdef USE_STDEV
+       /*
+        * st_dev breaks on network filesystems where different
+        * clients will have different views of what "device"
+        * the filesystem is on
+        */
+       if (ce->ce_dev != htonl(st->st_dev))
+               changed |= INODE_CHANGED;
+#endif
+
        if (ce->ce_size != htonl(st->st_size))
                changed |= DATA_CHANGED;
        return changed;
 }
 
+int base_name_compare(const char *name1, int len1, int mode1,
+                     const char *name2, int len2, int mode2)
+{
+       unsigned char c1, c2;
+       int len = len1 < len2 ? len1 : len2;
+       int cmp;
+
+       cmp = memcmp(name1, name2, len);
+       if (cmp)
+               return cmp;
+       c1 = name1[len];
+       c2 = name2[len];
+       if (!c1 && S_ISDIR(mode1))
+               c1 = '/';
+       if (!c2 && S_ISDIR(mode2))
+               c2 = '/';
+       return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+}
+
 int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
 {
        int len1 = flags1 & CE_NAMEMASK;
@@ -84,7 +132,7 @@ int cache_name_pos(const char *name, int namelen)
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = active_cache[next];
-               int cmp = cache_name_compare(name, namelen, ce->name, htons(ce->ce_flags));
+               int cmp = cache_name_compare(name, namelen, ce->name, ntohs(ce->ce_flags));
                if (!cmp)
                        return next;
                if (cmp < 0) {
@@ -152,11 +200,13 @@ static int check_file_directory_conflict(const struct cache_entry *ce,
        cp = pathbuf;
        while (1) {
                char *ep = strchr(cp, '/');
+               int len;
                if (!ep)
                        break;
                *ep = 0;    /* first cut it at slash */
+               len = ep - pathbuf;
                pos = cache_name_pos(pathbuf,
-                                    htons(create_ce_flags(ep-cp, stage)));
+                                    ntohs(create_ce_flags(len, stage)));
                if (0 <= pos) {
                        /* Our leading path component is registered as a file,
                         * and we are trying to make it a directory.  This is
@@ -180,7 +230,7 @@ static int check_file_directory_conflict(const struct cache_entry *ce,
         * a directory there?
         */
        pos = cache_name_pos(path,
-                            htons(create_ce_flags(namelen, stage)));
+                            ntohs(create_ce_flags(namelen, stage)));
 
        /* (0 <= pos) cannot happen because add_cache_entry()
         * should have taken care of that case.
@@ -229,7 +279,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
        int pos;
        int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
        int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
-       pos = cache_name_pos(ce->name, htons(ce->ce_flags));
+       pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
 
        /* existing match? Just replace it */
        if (pos >= 0) {
@@ -257,7 +307,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
        if (check_file_directory_conflict(ce, ok_to_replace)) {
                if (!ok_to_replace)
                        return -1;
-               pos = cache_name_pos(ce->name, htons(ce->ce_flags));
+               pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
                pos = -pos-1;
        }
 
@@ -344,7 +394,7 @@ int read_cache(void)
 }
 
 #define WRITE_BUFFER_SIZE 8192
-static char write_buffer[WRITE_BUFFER_SIZE];
+static unsigned char write_buffer[WRITE_BUFFER_SIZE];
 static unsigned long write_buffer_len;
 
 static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
@@ -390,11 +440,15 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
 {
        SHA_CTX c;
        struct cache_header hdr;
-       int i;
+       int i, removed;
+
+       for (i = removed = 0; i < entries; i++)
+               if (!cache[i]->ce_mode)
+                       removed++;
 
        hdr.hdr_signature = htonl(CACHE_SIGNATURE);
        hdr.hdr_version = htonl(2);
-       hdr.hdr_entries = htonl(entries);
+       hdr.hdr_entries = htonl(entries - removed);
 
        SHA1_Init(&c);
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
@@ -402,6 +456,8 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
 
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
+               if (!ce->ce_mode)
+                       continue;
                if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
                        return -1;
        }