Teach "git-read-tree -u" to check out submodules as a directory
[gitweb.git] / read-cache.c
index 823c2c3b08e559a5b0a29dc30c00772301b8cb79..d2f332a6222cc3543bde8fa661c54c5c904cf561 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "cache-tree.h"
+#include "refs.h"
 
 /* Index extensions.
  *
@@ -24,8 +25,6 @@ unsigned int active_nr, active_alloc, active_cache_changed;
 
 struct cache_tree *active_cache_tree;
 
-int cache_errno;
-
 static void *cache_mmap;
 static size_t cache_mmap_size;
 
@@ -93,6 +92,23 @@ static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
        return match;
 }
 
+static int ce_compare_gitlink(struct cache_entry *ce)
+{
+       unsigned char sha1[20];
+
+       /*
+        * We don't actually require that the .git directory
+        * under DIRLNK directory be a valid git directory. It
+        * might even be missing (in case nobody populated that
+        * sub-project).
+        *
+        * If so, we consider it always to match.
+        */
+       if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
+               return 0;
+       return hashcmp(sha1, ce->sha1);
+}
+
 static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
 {
        switch (st->st_mode & S_IFMT) {
@@ -104,6 +120,9 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
                if (ce_compare_link(ce, xsize_t(st->st_size)))
                        return DATA_CHANGED;
                break;
+       case S_IFDIR:
+               if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+                       return 0;
        default:
                return TYPE_CHANGED;
        }
@@ -129,6 +148,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
                    (has_symlinks || !S_ISREG(st->st_mode)))
                        changed |= TYPE_CHANGED;
                break;
+       case S_IFDIRLNK:
+               if (!S_ISDIR(st->st_mode))
+                       changed |= TYPE_CHANGED;
+               else if (ce_compare_gitlink(ce))
+                       changed |= DATA_CHANGED;
+               return changed;
        default:
                die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
        }
@@ -327,7 +352,7 @@ int remove_file_from_cache(const char *path)
        return 0;
 }
 
-int add_file_to_index(const char *path, int verbose)
+int add_file_to_cache(const char *path, int verbose)
 {
        int size, namelen;
        struct stat st;
@@ -336,10 +361,14 @@ int add_file_to_index(const char *path, int verbose)
        if (lstat(path, &st))
                die("%s: unable to stat (%s)", path, strerror(errno));
 
-       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
-               die("%s: can only add regular files or symbolic links", path);
+       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+               die("%s: can only add regular files, symbolic links or git-directories", path);
 
        namelen = strlen(path);
+       if (S_ISDIR(st.st_mode)) {
+               while (namelen && path[namelen-1] == '/')
+                       namelen--;
+       }
        size = cache_entry_size(namelen);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
@@ -665,14 +694,15 @@ int add_cache_entry(struct cache_entry *ce, int option)
  * For example, you'd want to do this after doing a "git-read-tree",
  * to link up the stat cache details with the proper files.
  */
-struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, int *err)
 {
        struct stat st;
        struct cache_entry *updated;
        int changed, size;
 
        if (lstat(ce->name, &st) < 0) {
-               cache_errno = errno;
+               if (err)
+                       *err = errno;
                return NULL;
        }
 
@@ -686,7 +716,8 @@ struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
        }
 
        if (ce_modified(ce, &st, really)) {
-               cache_errno = EINVAL;
+               if (err)
+                       *err = EINVAL;
                return NULL;
        }
 
@@ -718,6 +749,8 @@ int refresh_cache(unsigned int flags)
 
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce, *new;
+               int cache_errno = 0;
+
                ce = active_cache[i];
                if (ce_stage(ce)) {
                        while ((i < active_nr) &&
@@ -731,7 +764,7 @@ int refresh_cache(unsigned int flags)
                        continue;
                }
 
-               new = refresh_cache_entry(ce, really);
+               new = refresh_cache_ent(ce, really, &cache_errno);
                if (new == ce)
                        continue;
                if (!new) {
@@ -759,6 +792,11 @@ int refresh_cache(unsigned int flags)
        return has_errors;
 }
 
+struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+{
+       return refresh_cache_ent(ce, really, NULL);
+}
+
 static int verify_hdr(struct cache_header *hdr, unsigned long size)
 {
        SHA_CTX c;