Store peeled refs in packed-refs (take 2).
authorJunio C Hamano <junkio@cox.net>
Wed, 22 Nov 2006 07:36:35 +0000 (23:36 -0800)
committerJunio C Hamano <junkio@cox.net>
Wed, 22 Nov 2006 07:37:35 +0000 (23:37 -0800)
This fixes the previous implementation which failed to optimize
repositories with tons of lightweight tags.  The updated
packed-refs format begins with "# packed-refs with:" line that
lists the kind of extended data the file records.  Currently,
there is only one such extension defined, "peeled".  This stores
the "peeled tag" on a line that immediately follows a line for a
tag object itself in the format "^<sha-1>".

The header line itself and any extended data are ignored by
older implementation, so packed-refs file generated with this
version can still be used by older git.  packed-refs made by
older git can of course be used with this version.

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

index ee5a5565af9206dcc4c75abdb7efac9896ad8a74..8dc5b9efffc3ad2cd56130155a94aa489d0d0b1b 100644 (file)
@@ -46,8 +46,8 @@ static int handle_one_ref(const char *path, const unsigned char *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);
+                               fprintf(cb->refs_file, "^%s\n",
+                                       sha1_to_hex(o->sha1));
                }
        }
 
@@ -111,6 +111,10 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
        if (!cbdata.refs_file)
                die("unable to create ref-pack file structure (%s)",
                    strerror(errno));
+
+       /* perhaps other traits later as well */
+       fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
+
        for_each_ref(handle_one_ref, &cbdata);
        fflush(cbdata.refs_file);
        fsync(fd);
index 9ae3d08546c74a579e9bb13285424290b4449d4d..073979855b30b72363ed8116288b7bbbb2f0ff74 100644 (file)
@@ -67,8 +67,10 @@ match:
                return 0;
 
        if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
-               hex = find_unique_abbrev(peeled, abbrev);
-               printf("%s %s^{}\n", hex, refname);
+               if (!is_null_sha1(peeled)) {
+                       hex = find_unique_abbrev(peeled, abbrev);
+                       printf("%s %s^{}\n", hex, refname);
+               }
        }
        else {
                obj = parse_object(sha1);
diff --git a/refs.c b/refs.c
index 75cbc0e7ef32c384daafb4e8b8c27f8d1fd40538..96ea8b6907b1e4be9c7b8575ff7e4a36f5a7a5f8 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -5,14 +5,18 @@
 
 #include <errno.h>
 
+/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
+#define REF_KNOWS_PEELED 04
+
 struct ref_list {
        struct ref_list *next;
-       unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
+       unsigned char flag; /* ISSYMREF? ISPACKED? */
        unsigned char sha1[20];
+       unsigned char peeled[20];
        char name[FLEX_ARRAY];
 };
 
-static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
+static const char *parse_ref_line(char *line, unsigned char *sha1)
 {
        /*
         * 42: the answer to everything.
@@ -23,7 +27,6 @@ static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
         *  +1 (newline at the end of the line)
         */
        int len = strlen(line) - 42;
-       int peeled = 0;
 
        if (len <= 0)
                return NULL;
@@ -32,29 +35,18 @@ static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
        if (!isspace(line[40]))
                return NULL;
        line += 41;
-
-       if (isspace(*line)) {
-               /* "SHA-1 SP SP refs/tags/tagname^{} LF"? */
-               line++;
-               len--;
-               peeled = 1;
-       }
+       if (isspace(*line))
+               return NULL;
        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;
 }
 
 static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
-                               int flag, struct ref_list *list)
+                               int flag, struct ref_list *list,
+                               struct ref_list **new_entry)
 {
        int len;
        struct ref_list **p = &list, *entry;
@@ -66,8 +58,11 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
                        break;
 
                /* Same as existing entry? */
-               if (!cmp)
+               if (!cmp) {
+                       if (new_entry)
+                               *new_entry = entry;
                        return list;
+               }
                p = &entry->next;
        }
 
@@ -75,10 +70,13 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
        len = strlen(name) + 1;
        entry = xmalloc(sizeof(struct ref_list) + len);
        hashcpy(entry->sha1, sha1);
+       hashclr(entry->peeled);
        memcpy(entry->name, name, len);
        entry->flag = flag;
        entry->next = *p;
        *p = entry;
+       if (new_entry)
+               *new_entry = entry;
        return list;
 }
 
@@ -114,27 +112,50 @@ static void invalidate_cached_refs(void)
        ca->did_loose = ca->did_packed = 0;
 }
 
+static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
+{
+       struct ref_list *list = NULL;
+       struct ref_list *last = NULL;
+       char refline[PATH_MAX];
+       int flag = REF_ISPACKED;
+
+       while (fgets(refline, sizeof(refline), f)) {
+               unsigned char sha1[20];
+               const char *name;
+               static const char header[] = "# pack-refs with:";
+
+               if (!strncmp(refline, header, sizeof(header)-1)) {
+                       const char *traits = refline + sizeof(header) - 1;
+                       if (strstr(traits, " peeled "))
+                               flag |= REF_KNOWS_PEELED;
+                       /* perhaps other traits later as well */
+                       continue;
+               }
+
+               name = parse_ref_line(refline, sha1);
+               if (name) {
+                       list = add_ref(name, sha1, flag, list, &last);
+                       continue;
+               }
+               if (last &&
+                   refline[0] == '^' &&
+                   strlen(refline) == 42 &&
+                   refline[41] == '\n' &&
+                   !get_sha1_hex(refline + 1, sha1))
+                       hashcpy(last->peeled, sha1);
+       }
+       cached_refs->packed = list;
+}
+
 static struct ref_list *get_packed_refs(void)
 {
        if (!cached_refs.did_packed) {
-               struct ref_list *refs = NULL;
                FILE *f = fopen(git_path("packed-refs"), "r");
+               cached_refs.packed = NULL;
                if (f) {
-                       struct ref_list *list = NULL;
-                       char refline[PATH_MAX];
-                       while (fgets(refline, sizeof(refline), f)) {
-                               unsigned char sha1[20];
-                               int flag = REF_ISPACKED;
-                               const char *name =
-                                       parse_ref_line(refline, sha1, &flag);
-                               if (!name)
-                                       continue;
-                               list = add_ref(name, sha1, flag, list);
-                       }
+                       read_packed_refs(f, &cached_refs);
                        fclose(f);
-                       refs = list;
                }
-               cached_refs.packed = refs;
                cached_refs.did_packed = 1;
        }
        return cached_refs.packed;
@@ -177,7 +198,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                                error("%s points nowhere!", ref);
                                continue;
                        }
-                       list = add_ref(ref, sha1, flag, list);
+                       list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
                closedir(dir);
@@ -225,8 +246,7 @@ 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 (!(list->flag & REF_ISPEELED) &&
-                                   !strcmp(ref, list->name)) {
+                               if (!strcmp(ref, list->name)) {
                                        hashcpy(sha1, list->sha1);
                                        if (flag)
                                                *flag |= REF_ISPACKED;
@@ -348,8 +368,6 @@ 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;
@@ -368,22 +386,21 @@ int peel_ref(const char *ref, unsigned char *sha1)
 
        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;
+                       if (!strcmp(list->name, ref)) {
+                               if (list->flag & REF_KNOWS_PEELED) {
+                                       hashcpy(sha1, list->peeled);
+                                       return 0;
+                               }
+                               /* older pack-refs did not leave peeled ones */
+                               break;
                        }
                        list = list->next;
                }
-               /* older pack-refs did not leave peeled ones in */
        }
 
-       /* otherwise ... */
+       /* fallback - callers should not call this for unpacked refs */
        o = parse_object(base);
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, ref, 0);
diff --git a/refs.h b/refs.h
index 40048a6919846acdf5ded8b6eb76bfb5d419ad0e..cd1e1d620e6513cd1542d15763ccd5c07f108e48 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -10,14 +10,13 @@ struct ref_lock {
        int force_write;
 };
 
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
  */
-#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 *);