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 <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2006-11-19 13:22:44 -08:00
parent ef06b91804
commit cf0adba788
4 changed files with 117 additions and 20 deletions

View File

@ -1,5 +1,7 @@
#include "cache.h" #include "cache.h"
#include "refs.h" #include "refs.h"
#include "object.h"
#include "tag.h"
static const char builtin_pack_refs_usage[] = static const char builtin_pack_refs_usage[] =
"git-pack-refs [--all] [--prune]"; "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) int flags, void *cb_data)
{ {
struct pack_refs_cb_data *cb = 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 */ /* Do not pack the symbolic refs */
if (!(flags & REF_ISSYMREF)) 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); 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)) { if (cb->prune && !do_not_prune(flags)) {
int namelen = strlen(path) + 1; int namelen = strlen(path) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);

View File

@ -13,6 +13,7 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
{ {
struct object *obj; struct object *obj;
const char *hex; const char *hex;
unsigned char peeled[20];
if (tags_only || heads_only) { if (tags_only || heads_only) {
int match; int match;
@ -44,12 +45,15 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
match: match:
found_match++; found_match++;
obj = parse_object(sha1);
if (!obj) { /* This changes the semantics slightly that even under quiet we
if (quiet) * detect and return error if the repository is corrupt and
return 0; * ref points at a nonexistent object.
die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1)); */
} if (!has_sha1_file(sha1))
die("git-show-ref: bad ref %s (%s)", refname,
sha1_to_hex(sha1));
if (quiet) if (quiet)
return 0; return 0;
@ -58,11 +62,25 @@ match:
printf("%s\n", hex); printf("%s\n", hex);
else else
printf("%s %s\n", hex, refname); printf("%s %s\n", hex, refname);
if (deref_tags && obj->type == OBJ_TAG) {
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); obj = deref_tag(obj, refname, 0);
hex = find_unique_abbrev(obj->sha1, abbrev); hex = find_unique_abbrev(obj->sha1, abbrev);
printf("%s %s^{}\n", hex, refname); printf("%s %s^{}\n", hex, refname);
} }
}
return 0; return 0;
} }

73
refs.c
View File

@ -1,16 +1,18 @@
#include "refs.h" #include "refs.h"
#include "cache.h" #include "cache.h"
#include "object.h"
#include "tag.h"
#include <errno.h> #include <errno.h>
struct ref_list { struct ref_list {
struct ref_list *next; struct ref_list *next;
unsigned char flag; /* ISSYMREF? ISPACKED? */ unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
unsigned char sha1[20]; unsigned char sha1[20];
char name[FLEX_ARRAY]; 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. * 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) * +1 (newline at the end of the line)
*/ */
int len = strlen(line) - 42; int len = strlen(line) - 42;
int peeled = 0;
if (len <= 0) if (len <= 0)
return NULL; return NULL;
@ -29,11 +32,24 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
if (!isspace(line[40])) if (!isspace(line[40]))
return NULL; return NULL;
line += 41; 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') if (line[len] != '\n')
return NULL; return NULL;
line[len] = 0; line[len] = 0;
if (peeled && (len < 3 || strcmp(line + len - 3, "^{}")))
return NULL;
if (!peeled)
*flag &= ~REF_ISPEELED;
else
*flag |= REF_ISPEELED;
return line; return line;
} }
@ -108,10 +124,12 @@ static struct ref_list *get_packed_refs(void)
char refline[PATH_MAX]; char refline[PATH_MAX];
while (fgets(refline, sizeof(refline), f)) { while (fgets(refline, sizeof(refline), f)) {
unsigned char sha1[20]; 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) if (!name)
continue; continue;
list = add_ref(name, sha1, REF_ISPACKED, list); list = add_ref(name, sha1, flag, list);
} }
fclose(f); fclose(f);
refs = list; refs = list;
@ -207,7 +225,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
if (lstat(path, &st) < 0) { if (lstat(path, &st) < 0) {
struct ref_list *list = get_packed_refs(); struct ref_list *list = get_packed_refs();
while (list) { while (list) {
if (!strcmp(ref, list->name)) { if (!(list->flag & REF_ISPEELED) &&
!strcmp(ref, list->name)) {
hashcpy(sha1, list->sha1); hashcpy(sha1, list->sha1);
if (flag) if (flag)
*flag |= REF_ISPACKED; *flag |= REF_ISPACKED;
@ -329,6 +348,8 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
return 0; return 0;
if (is_null_sha1(entry->sha1)) if (is_null_sha1(entry->sha1))
return 0; return 0;
if (entry->flag & REF_ISPEELED)
return 0;
if (!has_sha1_file(entry->sha1)) { if (!has_sha1_file(entry->sha1)) {
error("%s does not point to a valid object!", entry->name); error("%s does not point to a valid object!", entry->name);
return 0; 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); 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, static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
void *cb_data) void *cb_data)
{ {

4
refs.h
View File

@ -16,6 +16,8 @@ struct ref_lock {
*/ */
#define REF_ISSYMREF 01 #define REF_ISSYMREF 01
#define REF_ISPACKED 02 #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); 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 head_ref(each_ref_fn, void *);
extern int for_each_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_branch_ref(each_ref_fn, void *);
extern int for_each_remote_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 **/ /** Reads the refs file specified into sha1 **/
extern int get_ref_sha1(const char *ref, unsigned char *sha1); extern int get_ref_sha1(const char *ref, unsigned char *sha1);