struct ref_dir: store a reference to the enclosing ref_cache
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 623fb55da83773edd83149c6629a2ad46a037eec..6720b9020196644ce8d9663040cad79c3ec08e9a 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -106,6 +106,8 @@ struct ref_value {
        unsigned char peeled[20];
 };
 
+struct ref_cache;
+
 struct ref_dir {
        int nr, alloc;
 
@@ -117,6 +119,9 @@ struct ref_dir {
         */
        int sorted;
 
+       /* A pointer to the ref_cache that contains this ref_dir. */
+       struct ref_cache *ref_cache;
+
        struct ref_entry **entries;
 };
 
@@ -171,6 +176,12 @@ struct ref_entry {
        char name[FLEX_ARRAY];
 };
 
+static struct ref_dir *get_ref_dir(struct ref_entry *entry)
+{
+       assert(entry->flag & REF_DIR);
+       return &entry->u.subdir;
+}
+
 static struct ref_entry *create_ref_entry(const char *refname,
                                          const unsigned char *sha1, int flag,
                                          int check_name)
@@ -195,7 +206,7 @@ static void clear_ref_dir(struct ref_dir *dir);
 static void free_ref_entry(struct ref_entry *entry)
 {
        if (entry->flag & REF_DIR)
-               clear_ref_dir(&entry->u.subdir);
+               clear_ref_dir(get_ref_dir(entry));
        free(entry);
 }
 
@@ -228,12 +239,14 @@ static void clear_ref_dir(struct ref_dir *dir)
  * dirname is the name of the directory with a trailing slash (e.g.,
  * "refs/heads/") or "" for the top-level directory.
  */
-static struct ref_entry *create_dir_entry(const char *dirname)
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+                                         const char *dirname)
 {
        struct ref_entry *direntry;
        int len = strlen(dirname);
        direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
        memcpy(direntry->name, dirname, len + 1);
+       direntry->u.subdir.ref_cache = ref_cache;
        direntry->flag = REF_DIR;
        return direntry;
 }
@@ -283,18 +296,17 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname
  * directory if it is missing; otherwise, return NULL if the desired
  * directory cannot be found.
  */
-static struct ref_entry *search_for_subdir(struct ref_dir *dir,
-                                          const char *subdirname, int mkdir)
+static struct ref_dir *search_for_subdir(struct ref_dir *dir,
+                                        const char *subdirname, int mkdir)
 {
        struct ref_entry *entry = search_ref_dir(dir, subdirname);
        if (!entry) {
                if (!mkdir)
                        return NULL;
-               entry = create_dir_entry(subdirname);
+               entry = create_dir_entry(dir->ref_cache, subdirname);
                add_entry_to_dir(dir, entry);
        }
-       assert(entry->flag & REF_DIR);
-       return entry;
+       return get_ref_dir(entry);
 }
 
 /*
@@ -313,14 +325,14 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,
        const char *slash;
        strbuf_init(&dirname, PATH_MAX);
        for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
-               struct ref_entry *entry;
+               struct ref_dir *subdir;
                strbuf_add(&dirname,
                           refname + dirname.len,
                           (slash + 1) - (refname + dirname.len));
-               entry = search_for_subdir(dir, dirname.buf, mkdir);
-               if (!entry)
+               subdir = search_for_subdir(dir, dirname.buf, mkdir);
+               if (!subdir)
                        break;
-               dir = &entry->u.subdir;
+               dir = subdir;
        }
 
        strbuf_release(&dirname);
@@ -449,8 +461,9 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
                struct ref_entry *entry = dir->entries[i];
                int retval;
                if (entry->flag & REF_DIR) {
-                       sort_ref_dir(&entry->u.subdir);
-                       retval = do_for_each_ref_in_dir(&entry->u.subdir, 0,
+                       struct ref_dir *subdir = get_ref_dir(entry);
+                       sort_ref_dir(subdir);
+                       retval = do_for_each_ref_in_dir(subdir, 0,
                                                        base, fn, trim, flags, cb_data);
                } else {
                        retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
@@ -495,10 +508,12 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                if (cmp == 0) {
                        if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
                                /* Both are directories; descend them in parallel. */
-                               sort_ref_dir(&e1->u.subdir);
-                               sort_ref_dir(&e2->u.subdir);
+                               struct ref_dir *subdir1 = get_ref_dir(e1);
+                               struct ref_dir *subdir2 = get_ref_dir(e2);
+                               sort_ref_dir(subdir1);
+                               sort_ref_dir(subdir2);
                                retval = do_for_each_ref_in_dirs(
-                                               &e1->u.subdir, &e2->u.subdir,
+                                               subdir1, subdir2,
                                                base, fn, trim, flags, cb_data);
                                i1++;
                                i2++;
@@ -521,9 +536,10 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                                i2++;
                        }
                        if (e->flag & REF_DIR) {
-                               sort_ref_dir(&e->u.subdir);
+                               struct ref_dir *subdir = get_ref_dir(e);
+                               sort_ref_dir(subdir);
                                retval = do_for_each_ref_in_dir(
-                                               &e->u.subdir, 0,
+                                               subdir, 0,
                                                base, fn, trim, flags, cb_data);
                        } else {
                                retval = do_one_ref(base, fn, trim, flags, cb_data, e);
@@ -607,26 +623,26 @@ static int is_refname_available(const char *refname, const char *oldrefname,
  */
 static struct ref_cache {
        struct ref_cache *next;
-       char did_loose;
-       char did_packed;
-       struct ref_dir loose;
-       struct ref_dir packed;
+       struct ref_entry *loose;
+       struct ref_entry *packed;
        /* The submodule name, or "" for the main repo. */
        char name[FLEX_ARRAY];
 } *ref_cache;
 
 static void clear_packed_ref_cache(struct ref_cache *refs)
 {
-       if (refs->did_packed)
-               clear_ref_dir(&refs->packed);
-       refs->did_packed = 0;
+       if (refs->packed) {
+               free_ref_entry(refs->packed);
+               refs->packed = NULL;
+       }
 }
 
 static void clear_loose_ref_cache(struct ref_cache *refs)
 {
-       if (refs->did_loose)
-               clear_ref_dir(&refs->loose);
-       refs->did_loose = 0;
+       if (refs->loose) {
+               free_ref_entry(refs->loose);
+               refs->loose = NULL;
+       }
 }
 
 static struct ref_cache *create_ref_cache(const char *submodule)
@@ -740,22 +756,22 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
 
 static struct ref_dir *get_packed_refs(struct ref_cache *refs)
 {
-       if (!refs->did_packed) {
+       if (!refs->packed) {
                const char *packed_refs_file;
                FILE *f;
 
+               refs->packed = create_dir_entry(refs, "");
                if (*refs->name)
                        packed_refs_file = git_path_submodule(refs->name, "packed-refs");
                else
                        packed_refs_file = git_path("packed-refs");
                f = fopen(packed_refs_file, "r");
                if (f) {
-                       read_packed_refs(f, &refs->packed);
+                       read_packed_refs(f, get_ref_dir(refs->packed));
                        fclose(f);
                }
-               refs->did_packed = 1;
        }
-       return &refs->packed;
+       return get_ref_dir(refs->packed);
 }
 
 void add_packed_ref(const char *refname, const unsigned char *sha1)
@@ -769,8 +785,8 @@ void add_packed_ref(const char *refname, const unsigned char *sha1)
  * dirname must end with '/'.  dir must be the directory entry
  * corresponding to dirname.
  */
-static void get_ref_dir(struct ref_cache *refs, const char *dirname,
-                       struct ref_dir *dir)
+static void read_loose_refs(struct ref_cache *refs, const char *dirname,
+                           struct ref_dir *dir)
 {
        DIR *d;
        const char *path;
@@ -808,8 +824,8 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname,
                        ; /* silently ignore */
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
-                       get_ref_dir(refs, refname.buf,
-                                   &search_for_subdir(dir, refname.buf, 1)->u.subdir);
+                       read_loose_refs(refs, refname.buf,
+                                       search_for_subdir(dir, refname.buf, 1));
                } else {
                        if (*refs->name) {
                                hashclr(sha1);
@@ -833,12 +849,13 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname,
 
 static struct ref_dir *get_loose_refs(struct ref_cache *refs)
 {
-       if (!refs->did_loose) {
-               get_ref_dir(refs, "refs/",
-                           &search_for_subdir(&refs->loose, "refs/", 1)->u.subdir);
-               refs->did_loose = 1;
+       if (!refs->loose) {
+               refs->loose = create_dir_entry(refs, "");
+               read_loose_refs(refs, "refs/",
+                               search_for_subdir(get_ref_dir(refs->loose),
+                                                 "refs/", 1));
        }
-       return &refs->loose;
+       return get_ref_dir(refs->loose);
 }
 
 /* We allow "recursive" symbolic refs. Only within reason, though */