Store peeled refs in packed-refs file.
authorJunio C Hamano <junkio@cox.net>
Sun, 19 Nov 2006 21:22:44 +0000 (13:22 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 20 Nov 2006 02:45:44 +0000 (18:45 -0800)
This would speed up "show-ref -d" in a repository with mostly
packed tags.

Signed-off-by: Junio C Hamano <junkio@cox.net>
builtin-pack-refs.c
builtin-show-ref.c
refs.c
refs.h

index 042d2718f904303600a57cfe8b5de83f5c0d34db..ee5a5565af9206dcc4c75abdb7efac9896ad8a74 100644 (file)
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "refs.h"
+#include "object.h"
+#include "tag.h"
 
 static const char builtin_pack_refs_usage[] =
 "git-pack-refs [--all] [--prune]";
@@ -29,12 +31,26 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
                          int flags, void *cb_data)
 {
        struct pack_refs_cb_data *cb = cb_data;
+       int is_tag_ref;
 
-       if (!cb->all && strncmp(path, "refs/tags/", 10))
-               return 0;
        /* Do not pack the symbolic refs */
-       if (!(flags & REF_ISSYMREF))
-               fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+       if ((flags & REF_ISSYMREF))
+               return 0;
+       is_tag_ref = !strncmp(path, "refs/tags/", 10);
+       if (!cb->all && !is_tag_ref)
+               return 0;
+
+       fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+       if (is_tag_ref) {
+               struct object *o = parse_object(sha1);
+               if (o->type == OBJ_TAG) {
+                       o = deref_tag(o, path, 0);
+                       if (o)
+                               fprintf(cb->refs_file, "%s  %s^{}\n",
+                                       sha1_to_hex(o->sha1), path);
+               }
+       }
+
        if (cb->prune && !do_not_prune(flags)) {
                int namelen = strlen(path) + 1;
                struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
index 06ec400d7f3f2e589111a561a6b18da067a02f1f..9ae3d08546c74a579e9bb13285424290b4449d4d 100644 (file)
@@ -13,6 +13,7 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
 {
        struct object *obj;
        const char *hex;
+       unsigned char peeled[20];
 
        if (tags_only || heads_only) {
                int match;
@@ -44,12 +45,15 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
 
 match:
        found_match++;
-       obj = parse_object(sha1);
-       if (!obj) {
-               if (quiet)
-                       return 0;
-               die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1));
-       }
+
+       /* This changes the semantics slightly that even under quiet we
+        * detect and return error if the repository is corrupt and
+        * ref points at a nonexistent object.
+        */
+       if (!has_sha1_file(sha1))
+               die("git-show-ref: bad ref %s (%s)", refname,
+                   sha1_to_hex(sha1));
+
        if (quiet)
                return 0;
 
@@ -58,11 +62,25 @@ match:
                printf("%s\n", hex);
        else
                printf("%s %s\n", hex, refname);
-       if (deref_tags && obj->type == OBJ_TAG) {
-               obj = deref_tag(obj, refname, 0);
-               hex = find_unique_abbrev(obj->sha1, abbrev);
+
+       if (!deref_tags)
+               return 0;
+
+       if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
+               hex = find_unique_abbrev(peeled, abbrev);
                printf("%s %s^{}\n", hex, refname);
        }
+       else {
+               obj = parse_object(sha1);
+               if (!obj)
+                       die("git-show-ref: bad ref %s (%s)", refname,
+                           sha1_to_hex(sha1));
+               if (obj->type == OBJ_TAG) {
+                       obj = deref_tag(obj, refname, 0);
+                       hex = find_unique_abbrev(obj->sha1, abbrev);
+                       printf("%s %s^{}\n", hex, refname);
+               }
+       }
        return 0;
 }
 
diff --git a/refs.c b/refs.c
index 0e156c5dee1edff895c576a4491ec71f931ff492..75cbc0e7ef32c384daafb4e8b8c27f8d1fd40538 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1,16 +1,18 @@
 #include "refs.h"
 #include "cache.h"
+#include "object.h"
+#include "tag.h"
 
 #include <errno.h>
 
 struct ref_list {
        struct ref_list *next;
-       unsigned char flag; /* ISSYMREF? ISPACKED? */
+       unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
        unsigned char sha1[20];
        char name[FLEX_ARRAY];
 };
 
-static const char *parse_ref_line(char *line, unsigned char *sha1)
+static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
 {
        /*
         * 42: the answer to everything.
@@ -21,6 +23,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
         *  +1 (newline at the end of the line)
         */
        int len = strlen(line) - 42;
+       int peeled = 0;
 
        if (len <= 0)
                return NULL;
@@ -29,11 +32,24 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
        if (!isspace(line[40]))
                return NULL;
        line += 41;
-       if (isspace(*line))
-               return NULL;
+
+       if (isspace(*line)) {
+               /* "SHA-1 SP SP refs/tags/tagname^{} LF"? */
+               line++;
+               len--;
+               peeled = 1;
+       }
        if (line[len] != '\n')
                return NULL;
        line[len] = 0;
+
+       if (peeled && (len < 3 || strcmp(line + len - 3, "^{}")))
+               return NULL;
+
+       if (!peeled)
+               *flag &= ~REF_ISPEELED;
+       else
+               *flag |= REF_ISPEELED;
        return line;
 }
 
@@ -108,10 +124,12 @@ static struct ref_list *get_packed_refs(void)
                        char refline[PATH_MAX];
                        while (fgets(refline, sizeof(refline), f)) {
                                unsigned char sha1[20];
-                               const char *name = parse_ref_line(refline, sha1);
+                               int flag = REF_ISPACKED;
+                               const char *name =
+                                       parse_ref_line(refline, sha1, &flag);
                                if (!name)
                                        continue;
-                               list = add_ref(name, sha1, REF_ISPACKED, list);
+                               list = add_ref(name, sha1, flag, list);
                        }
                        fclose(f);
                        refs = list;
@@ -207,7 +225,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                if (lstat(path, &st) < 0) {
                        struct ref_list *list = get_packed_refs();
                        while (list) {
-                               if (!strcmp(ref, list->name)) {
+                               if (!(list->flag & REF_ISPEELED) &&
+                                   !strcmp(ref, list->name)) {
                                        hashcpy(sha1, list->sha1);
                                        if (flag)
                                                *flag |= REF_ISPACKED;
@@ -329,6 +348,8 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                return 0;
        if (is_null_sha1(entry->sha1))
                return 0;
+       if (entry->flag & REF_ISPEELED)
+               return 0;
        if (!has_sha1_file(entry->sha1)) {
                error("%s does not point to a valid object!", entry->name);
                return 0;
@@ -336,6 +357,44 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
 }
 
+int peel_ref(const char *ref, unsigned char *sha1)
+{
+       int flag;
+       unsigned char base[20];
+       struct object *o;
+
+       if (!resolve_ref(ref, base, 1, &flag))
+               return -1;
+
+       if ((flag & REF_ISPACKED)) {
+               struct ref_list *list = get_packed_refs();
+               int len = strlen(ref);
+
+               while (list) {
+                       if ((list->flag & REF_ISPEELED) &&
+                           !strncmp(list->name, ref, len) &&
+                           strlen(list->name) == len + 3 &&
+                           !strcmp(list->name + len, "^{}")) {
+                               hashcpy(sha1, list->sha1);
+                               return 0;
+                       }
+                       list = list->next;
+               }
+               /* older pack-refs did not leave peeled ones in */
+       }
+
+       /* otherwise ... */
+       o = parse_object(base);
+       if (o->type == OBJ_TAG) {
+               o = deref_tag(o, ref, 0);
+               if (o) {
+                       hashcpy(sha1, o->sha1);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
                           void *cb_data)
 {
diff --git a/refs.h b/refs.h
index a57d43726a0fee5686e2c0d505f250c6b4adc150..40048a6919846acdf5ded8b6eb76bfb5d419ad0e 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -16,6 +16,8 @@ struct ref_lock {
  */
 #define REF_ISSYMREF 01
 #define REF_ISPACKED 02
+#define REF_ISPEELED 04 /* internal use */
+
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);
@@ -23,6 +25,8 @@ extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 
+extern int peel_ref(const char *, unsigned char *);
+
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);