Merge branch 'maint'
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index fb33da111240d9a3d579dfebb05eef951fecee23..39a3b23804d2da715c564459bf320be23d41c1bf 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -157,6 +157,9 @@ static struct cached_refs {
        struct ref_list *loose;
        struct ref_list *packed;
 } cached_refs;
+static struct ref_list *current_ref;
+
+static struct ref_list *extra_refs;
 
 static void free_ref_list(struct ref_list *list)
 {
@@ -214,6 +217,17 @@ static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
        cached_refs->packed = sort_ref_list(list);
 }
 
+void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
+{
+       extra_refs = add_ref(name, sha1, flag, extra_refs, NULL);
+}
+
+void clear_extra_refs(void)
+{
+       free_ref_list(extra_refs);
+       extra_refs = NULL;
+}
+
 static struct ref_list *get_packed_refs(void)
 {
        if (!cached_refs.did_packed) {
@@ -351,6 +365,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
 {
        int len = strlen(path), retval;
        char *gitdir;
+       const char *tmp;
 
        while (len && path[len-1] == '/')
                len--;
@@ -358,16 +373,27 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
                return -1;
        gitdir = xmalloc(len + MAXREFLEN + 8);
        memcpy(gitdir, path, len);
-       memcpy(gitdir + len, "/.git/", 7);
-
-       retval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);
+       memcpy(gitdir + len, "/.git", 6);
+       len += 5;
+
+       tmp = read_gitfile_gently(gitdir);
+       if (tmp) {
+               free(gitdir);
+               len = strlen(tmp);
+               gitdir = xmalloc(len + MAXREFLEN + 3);
+               memcpy(gitdir, tmp, len);
+       }
+       gitdir[len] = '/';
+       gitdir[++len] = '\0';
+       retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
        free(gitdir);
        return retval;
 }
 
 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
-       int depth = MAXDEPTH, len;
+       int depth = MAXDEPTH;
+       ssize_t len;
        char buffer[256];
        static char ref_buffer[256];
 
@@ -476,6 +502,7 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                error("%s does not point to a valid object!", entry->name);
                return 0;
        }
+       current_ref = entry;
        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
 }
 
@@ -485,6 +512,16 @@ int peel_ref(const char *ref, unsigned char *sha1)
        unsigned char base[20];
        struct object *o;
 
+       if (current_ref && (current_ref->name == ref
+               || !strcmp(current_ref->name, ref))) {
+               if (current_ref->flag & REF_KNOWS_PEELED) {
+                       hashcpy(sha1, current_ref->peeled);
+                       return 0;
+               }
+               hashcpy(base, current_ref->sha1);
+               goto fallback;
+       }
+
        if (!resolve_ref(ref, base, 1, &flag))
                return -1;
 
@@ -504,7 +541,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
                }
        }
 
-       /* fallback - callers should not call this for unpacked refs */
+fallback:
        o = parse_object(base);
        if (o && o->type == OBJ_TAG) {
                o = deref_tag(o, ref, 0);
@@ -519,10 +556,15 @@ int peel_ref(const char *ref, unsigned char *sha1)
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
                           void *cb_data)
 {
-       int retval;
+       int retval = 0;
        struct ref_list *packed = get_packed_refs();
        struct ref_list *loose = get_loose_refs();
 
+       struct ref_list *extra;
+
+       for (extra = extra_refs; extra; extra = extra->next)
+               retval = do_one_ref(base, fn, trim, cb_data, extra);
+
        while (packed && loose) {
                struct ref_list *entry;
                int cmp = strcmp(packed->name, loose->name);
@@ -539,15 +581,18 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
                }
                retval = do_one_ref(base, fn, trim, cb_data, entry);
                if (retval)
-                       return retval;
+                       goto end_each;
        }
 
        for (packed = packed ? packed : loose; packed; packed = packed->next) {
                retval = do_one_ref(base, fn, trim, cb_data, packed);
                if (retval)
-                       return retval;
+                       goto end_each;
        }
-       return 0;
+
+end_each:
+       current_ref = NULL;
+       return retval;
 }
 
 int head_ref(each_ref_fn fn, void *cb_data)
@@ -880,7 +925,7 @@ int delete_ref(const char *refname, const unsigned char *sha1)
                i = strlen(lock->lk->filename) - 5; /* .lock */
                lock->lk->filename[i] = 0;
                err = unlink(lock->lk->filename);
-               if (err) {
+               if (err && errno != ENOENT) {
                        ret = 1;
                        error("unlink(%s) failed: %s",
                              lock->lk->filename, strerror(errno));
@@ -1018,7 +1063,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        return 1;
 }
 
-static int close_ref(struct ref_lock *lock)
+int close_ref(struct ref_lock *lock)
 {
        if (close_lock_file(lock->lk))
                return -1;
@@ -1026,7 +1071,7 @@ static int close_ref(struct ref_lock *lock)
        return 0;
 }
 
-static int commit_ref(struct ref_lock *lock)
+int commit_ref(struct ref_lock *lock)
 {
        if (commit_lock_file(lock->lk))
                return -1;
@@ -1367,6 +1412,10 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        tz = strtoul(tz_c, NULL, 10);
        if (get_sha1_hex(logdata, sha1))
                die("Log %s is corrupt.", logfile);
+       if (is_null_sha1(sha1)) {
+               if (get_sha1_hex(logdata + 41, sha1))
+                       die("Log %s is corrupt.", logfile);
+       }
        if (msg)
                *msg = ref_msg(logdata, logend);
        munmap(log_mapped, mapsz);