Merge branch 'vr/help-per-platform'
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index c5e167bb9f8569c6afe9d7fc380761dfddb5e555..da74a2b29a20a2c94fef61641e35980007c1ab95 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -259,8 +259,13 @@ 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(get_ref_dir(entry));
+       if (entry->flag & REF_DIR) {
+               /*
+                * Do not use get_ref_dir() here, as that might
+                * trigger the reading of loose refs.
+                */
+               clear_ref_dir(&entry->u.subdir);
+       }
        free(entry);
 }
 
@@ -273,6 +278,12 @@ static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
 {
        ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
        dir->entries[dir->nr++] = entry;
+       /* optimize for the case that entries are added in order */
+       if (dir->nr == 1 ||
+           (dir->nr == dir->sorted + 1 &&
+            strcmp(dir->entries[dir->nr - 2]->name,
+                   dir->entries[dir->nr - 1]->name) < 0))
+               dir->sorted = dir->nr;
 }
 
 /*
@@ -315,6 +326,23 @@ static int ref_entry_cmp(const void *a, const void *b)
 
 static void sort_ref_dir(struct ref_dir *dir);
 
+struct string_slice {
+       size_t len;
+       const char *str;
+};
+
+static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
+{
+       struct string_slice *key = (struct string_slice *)key_;
+       struct ref_entry *ent = *(struct ref_entry **)ent_;
+       int entlen = strlen(ent->name);
+       int cmplen = key->len < entlen ? key->len : entlen;
+       int cmp = memcmp(key->str, ent->name, cmplen);
+       if (cmp)
+               return cmp;
+       return key->len - entlen;
+}
+
 /*
  * Return the entry with the given refname from the ref_dir
  * (non-recursively), sorting dir if necessary.  Return NULL if no
@@ -323,20 +351,17 @@ static void sort_ref_dir(struct ref_dir *dir);
 static struct ref_entry *search_ref_dir(struct ref_dir *dir,
                                        const char *refname, size_t len)
 {
-       struct ref_entry *e, **r;
+       struct ref_entry **r;
+       struct string_slice key;
 
        if (refname == NULL || !dir->nr)
                return NULL;
 
        sort_ref_dir(dir);
-
-       e = xmalloc(sizeof(struct ref_entry) + len + 1);
-       memcpy(e->name, refname, len);
-       e->name[len] = '\0';
-
-       r = bsearch(&e, dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
-
-       free(e);
+       key.len = len;
+       key.str = refname;
+       r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
+                   ref_entry_cmp_sslice);
 
        if (r == NULL)
                return NULL;
@@ -352,9 +377,9 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir,
  * directory cannot be found.  dir must already be complete.
  */
 static struct ref_dir *search_for_subdir(struct ref_dir *dir,
-                                        const char *subdirname, int mkdir)
+                                        const char *subdirname, size_t len,
+                                        int mkdir)
 {
-       size_t len = strlen(subdirname);
        struct ref_entry *entry = search_ref_dir(dir, subdirname, len);
        if (!entry) {
                if (!mkdir)
@@ -383,15 +408,11 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
 static struct ref_dir *find_containing_dir(struct ref_dir *dir,
                                           const char *refname, int mkdir)
 {
-       struct strbuf dirname;
        const char *slash;
-       strbuf_init(&dirname, PATH_MAX);
        for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+               size_t dirnamelen = slash - refname + 1;
                struct ref_dir *subdir;
-               strbuf_add(&dirname,
-                          refname + dirname.len,
-                          (slash + 1) - (refname + dirname.len));
-               subdir = search_for_subdir(dir, dirname.buf, mkdir);
+               subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
                if (!subdir) {
                        dir = NULL;
                        break;
@@ -399,7 +420,6 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,
                dir = subdir;
        }
 
-       strbuf_release(&dirname);
        return dir;
 }