From cf0adba7885342e1bbcf0689fece9d13e39784b4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 19 Nov 2006 13:22:44 -0800 Subject: Store peeled refs in packed-refs file. This would speed up "show-ref -d" in a repository with mostly packed tags. Signed-off-by: Junio C Hamano --- builtin-pack-refs.c | 24 +++++++++++++++--- builtin-show-ref.c | 36 +++++++++++++++++++------- refs.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++----- refs.h | 4 +++ 4 files changed, 117 insertions(+), 20 deletions(-) diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 042d2718f9..ee5a5565af 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -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); diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 06ec400d7f..9ae3d08546 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -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 0e156c5dee..75cbc0e7ef 100644 --- a/refs.c +++ b/refs.c @@ -1,16 +1,18 @@ #include "refs.h" #include "cache.h" +#include "object.h" +#include "tag.h" #include 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 a57d43726a..40048a6919 100644 --- 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); -- cgit v1.2.1 From f4204ab9f6a192cdb9a68150e031d7183688bfeb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 21 Nov 2006 23:36:35 -0800 Subject: Store peeled refs in packed-refs (take 2). 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 "^". 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 --- builtin-pack-refs.c | 8 +++- builtin-show-ref.c | 6 ++- refs.c | 111 ++++++++++++++++++++++++++++++---------------------- refs.h | 7 ++-- 4 files changed, 77 insertions(+), 55 deletions(-) diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index ee5a5565af..8dc5b9efff 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -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); diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 9ae3d08546..073979855b 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -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 75cbc0e7ef..96ea8b6907 100644 --- a/refs.c +++ b/refs.c @@ -5,14 +5,18 @@ #include +/* 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 40048a6919..cd1e1d620e 100644 --- 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 *); -- cgit v1.2.1