Linus Torvalds <torvalds@xxxxxxxx> writes: > On Sun, 19 Nov 2006, Marco Costalba wrote: >> >> Tested also with show-ref only, not useful to me, but just as a comparison. >> >> - git tree 1420ms >> - linux tree 1021ms >> >> Better, but still slower then what I would expected. > > MUCH slower than expected. This is still a WIP and for discussion only (I have to leave to pick up my wife now so I won't be making any further progress tonight), but I think parse_object() in show-ref is costing us. One worry is that existing git will misinterpret packed-refs file prepared with this version. If you say "git show-ref -d --tags" to this version, it takes the fast path which seems to shave about 1/3 of the runtime. --- diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 042d271..0c770f5 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,24 @@ static int handle_one_ref(const char *pa 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); + 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 06ec400..32cbcb8 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -13,6 +13,7 @@ static int show_ref(const char *refname, { struct object *obj; const char *hex; + unsigned char peeled[20]; if (tags_only || heads_only) { int match; @@ -44,6 +45,17 @@ static int show_ref(const char *refname, match: found_match++; + + /* Fastpath for demonstration only */ + if (deref_tags && (flag & REF_ISPACKED) && + !peel_ref(refname, peeled) && !quiet && !hash_only) { + hex = find_unique_abbrev(sha1, abbrev); + printf("%s %s\n", hex, refname); + hex = find_unique_abbrev(peeled, abbrev); + printf("%s %s^{}\n", hex, refname); + return 0; + } + obj = parse_object(sha1); if (!obj) { if (quiet) @@ -59,8 +71,13 @@ match: 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); + unsigned char sha1[20]; + if ((flag & REF_ISPACKED) && !peel_ref(refname, sha1)) + hex = find_unique_abbrev(sha1, abbrev); + else { + 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 f003a0b..347c9b7 100644 --- 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. @@ -34,6 +36,10 @@ static const char *parse_ref_line(char * if (line[len] != '\n') return NULL; line[len] = 0; + if (len < 3 || strcmp(line + len - 3, "^{}")) + *flag &= ~REF_ISPEELED; + else + *flag |= REF_ISPEELED; return line; } @@ -108,10 +114,12 @@ static struct ref_list *get_packed_refs( 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 +215,8 @@ const char *resolve_ref(const char *ref, 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; @@ -322,6 +331,41 @@ int read_ref(const char *ref, unsigned c return -1; } +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(sha1); + 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) { @@ -347,6 +391,8 @@ static int do_for_each_ref(const char *b continue; if (is_null_sha1(entry->sha1)) continue; + if (entry->flag & REF_ISPEELED) + continue; if (!has_sha1_file(entry->sha1)) { error("%s does not point to a valid object!", entry->name); continue; @@ -357,15 +403,22 @@ static int do_for_each_ref(const char *b return retval; } - packed = packed ? packed : loose; - while (packed) { - if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1, - packed->flag, cb_data); - if (retval) - return retval; + for (packed = packed ? packed : loose; packed; packed = packed->next) { + if (strncmp(base, packed->name, trim)) + continue; + if (is_null_sha1(packed->sha1)) + continue; + if (packed->flag & REF_ISPEELED) + continue; + if (!has_sha1_file(packed->sha1)) { + error("%s does not point to a valid object!", + packed->name); + continue; } - packed = packed->next; + retval = fn(packed->name + trim, packed->sha1, + packed->flag, cb_data); + if (retval) + return retval; } return 0; } diff --git a/refs.h b/refs.h index a57d437..40048a6 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, 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); - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html