mirror of
https://github.com/git/git
synced 2024-10-02 14:45:21 +00:00
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 "^<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>
This commit is contained in:
parent
cf0adba788
commit
f4204ab9f6
|
@ -46,8 +46,8 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
|
||||||
if (o->type == OBJ_TAG) {
|
if (o->type == OBJ_TAG) {
|
||||||
o = deref_tag(o, path, 0);
|
o = deref_tag(o, path, 0);
|
||||||
if (o)
|
if (o)
|
||||||
fprintf(cb->refs_file, "%s %s^{}\n",
|
fprintf(cb->refs_file, "^%s\n",
|
||||||
sha1_to_hex(o->sha1), path);
|
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)
|
if (!cbdata.refs_file)
|
||||||
die("unable to create ref-pack file structure (%s)",
|
die("unable to create ref-pack file structure (%s)",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
|
||||||
|
/* perhaps other traits later as well */
|
||||||
|
fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
|
||||||
|
|
||||||
for_each_ref(handle_one_ref, &cbdata);
|
for_each_ref(handle_one_ref, &cbdata);
|
||||||
fflush(cbdata.refs_file);
|
fflush(cbdata.refs_file);
|
||||||
fsync(fd);
|
fsync(fd);
|
||||||
|
|
|
@ -67,8 +67,10 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
|
if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
|
||||||
hex = find_unique_abbrev(peeled, abbrev);
|
if (!is_null_sha1(peeled)) {
|
||||||
printf("%s %s^{}\n", hex, refname);
|
hex = find_unique_abbrev(peeled, abbrev);
|
||||||
|
printf("%s %s^{}\n", hex, refname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
obj = parse_object(sha1);
|
obj = parse_object(sha1);
|
||||||
|
|
111
refs.c
111
refs.c
|
@ -5,14 +5,18 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
|
||||||
|
#define REF_KNOWS_PEELED 04
|
||||||
|
|
||||||
struct ref_list {
|
struct ref_list {
|
||||||
struct ref_list *next;
|
struct ref_list *next;
|
||||||
unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
|
unsigned char flag; /* ISSYMREF? ISPACKED? */
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
|
unsigned char peeled[20];
|
||||||
char name[FLEX_ARRAY];
|
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.
|
* 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)
|
* +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;
|
||||||
|
@ -32,29 +35,18 @@ static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
|
||||||
if (!isspace(line[40]))
|
if (!isspace(line[40]))
|
||||||
return NULL;
|
return NULL;
|
||||||
line += 41;
|
line += 41;
|
||||||
|
if (isspace(*line))
|
||||||
if (isspace(*line)) {
|
return NULL;
|
||||||
/* "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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
|
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;
|
int len;
|
||||||
struct ref_list **p = &list, *entry;
|
struct ref_list **p = &list, *entry;
|
||||||
|
@ -66,8 +58,11 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Same as existing entry? */
|
/* Same as existing entry? */
|
||||||
if (!cmp)
|
if (!cmp) {
|
||||||
|
if (new_entry)
|
||||||
|
*new_entry = entry;
|
||||||
return list;
|
return list;
|
||||||
|
}
|
||||||
p = &entry->next;
|
p = &entry->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,10 +70,13 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
|
||||||
len = strlen(name) + 1;
|
len = strlen(name) + 1;
|
||||||
entry = xmalloc(sizeof(struct ref_list) + len);
|
entry = xmalloc(sizeof(struct ref_list) + len);
|
||||||
hashcpy(entry->sha1, sha1);
|
hashcpy(entry->sha1, sha1);
|
||||||
|
hashclr(entry->peeled);
|
||||||
memcpy(entry->name, name, len);
|
memcpy(entry->name, name, len);
|
||||||
entry->flag = flag;
|
entry->flag = flag;
|
||||||
entry->next = *p;
|
entry->next = *p;
|
||||||
*p = entry;
|
*p = entry;
|
||||||
|
if (new_entry)
|
||||||
|
*new_entry = entry;
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,27 +112,50 @@ static void invalidate_cached_refs(void)
|
||||||
ca->did_loose = ca->did_packed = 0;
|
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)
|
static struct ref_list *get_packed_refs(void)
|
||||||
{
|
{
|
||||||
if (!cached_refs.did_packed) {
|
if (!cached_refs.did_packed) {
|
||||||
struct ref_list *refs = NULL;
|
|
||||||
FILE *f = fopen(git_path("packed-refs"), "r");
|
FILE *f = fopen(git_path("packed-refs"), "r");
|
||||||
|
cached_refs.packed = NULL;
|
||||||
if (f) {
|
if (f) {
|
||||||
struct ref_list *list = NULL;
|
read_packed_refs(f, &cached_refs);
|
||||||
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);
|
|
||||||
}
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
refs = list;
|
|
||||||
}
|
}
|
||||||
cached_refs.packed = refs;
|
|
||||||
cached_refs.did_packed = 1;
|
cached_refs.did_packed = 1;
|
||||||
}
|
}
|
||||||
return cached_refs.packed;
|
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);
|
error("%s points nowhere!", ref);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
list = add_ref(ref, sha1, flag, list);
|
list = add_ref(ref, sha1, flag, list, NULL);
|
||||||
}
|
}
|
||||||
free(ref);
|
free(ref);
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
@ -225,8 +246,7 @@ 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 (!(list->flag & REF_ISPEELED) &&
|
if (!strcmp(ref, list->name)) {
|
||||||
!strcmp(ref, list->name)) {
|
|
||||||
hashcpy(sha1, list->sha1);
|
hashcpy(sha1, list->sha1);
|
||||||
if (flag)
|
if (flag)
|
||||||
*flag |= REF_ISPACKED;
|
*flag |= REF_ISPACKED;
|
||||||
|
@ -348,8 +368,6 @@ 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;
|
||||||
|
@ -368,22 +386,21 @@ int peel_ref(const char *ref, unsigned char *sha1)
|
||||||
|
|
||||||
if ((flag & REF_ISPACKED)) {
|
if ((flag & REF_ISPACKED)) {
|
||||||
struct ref_list *list = get_packed_refs();
|
struct ref_list *list = get_packed_refs();
|
||||||
int len = strlen(ref);
|
|
||||||
|
|
||||||
while (list) {
|
while (list) {
|
||||||
if ((list->flag & REF_ISPEELED) &&
|
if (!strcmp(list->name, ref)) {
|
||||||
!strncmp(list->name, ref, len) &&
|
if (list->flag & REF_KNOWS_PEELED) {
|
||||||
strlen(list->name) == len + 3 &&
|
hashcpy(sha1, list->peeled);
|
||||||
!strcmp(list->name + len, "^{}")) {
|
return 0;
|
||||||
hashcpy(sha1, list->sha1);
|
}
|
||||||
return 0;
|
/* older pack-refs did not leave peeled ones */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
list = list->next;
|
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);
|
o = parse_object(base);
|
||||||
if (o->type == OBJ_TAG) {
|
if (o->type == OBJ_TAG) {
|
||||||
o = deref_tag(o, ref, 0);
|
o = deref_tag(o, ref, 0);
|
||||||
|
|
7
refs.h
7
refs.h
|
@ -10,14 +10,13 @@ struct ref_lock {
|
||||||
int force_write;
|
int force_write;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define REF_ISSYMREF 01
|
||||||
|
#define REF_ISPACKED 02
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calls the specified function for each ref file until it returns nonzero,
|
* Calls the specified function for each ref file until it returns nonzero,
|
||||||
* and returns the value
|
* 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);
|
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 *);
|
||||||
|
|
Loading…
Reference in a new issue