Skip to content

Commit cf0adba

Browse files
author
Junio C Hamano
committed
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 <[email protected]>
1 parent ef06b91 commit cf0adba

File tree

4 files changed

+117
-20
lines changed

4 files changed

+117
-20
lines changed

builtin-pack-refs.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "cache.h"
22
#include "refs.h"
3+
#include "object.h"
4+
#include "tag.h"
35

46
static const char builtin_pack_refs_usage[] =
57
"git-pack-refs [--all] [--prune]";
@@ -29,12 +31,26 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
2931
int flags, void *cb_data)
3032
{
3133
struct pack_refs_cb_data *cb = cb_data;
34+
int is_tag_ref;
3235

33-
if (!cb->all && strncmp(path, "refs/tags/", 10))
34-
return 0;
3536
/* Do not pack the symbolic refs */
36-
if (!(flags & REF_ISSYMREF))
37-
fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
37+
if ((flags & REF_ISSYMREF))
38+
return 0;
39+
is_tag_ref = !strncmp(path, "refs/tags/", 10);
40+
if (!cb->all && !is_tag_ref)
41+
return 0;
42+
43+
fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
44+
if (is_tag_ref) {
45+
struct object *o = parse_object(sha1);
46+
if (o->type == OBJ_TAG) {
47+
o = deref_tag(o, path, 0);
48+
if (o)
49+
fprintf(cb->refs_file, "%s %s^{}\n",
50+
sha1_to_hex(o->sha1), path);
51+
}
52+
}
53+
3854
if (cb->prune && !do_not_prune(flags)) {
3955
int namelen = strlen(path) + 1;
4056
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);

builtin-show-ref.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
1313
{
1414
struct object *obj;
1515
const char *hex;
16+
unsigned char peeled[20];
1617

1718
if (tags_only || heads_only) {
1819
int match;
@@ -44,12 +45,15 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
4445

4546
match:
4647
found_match++;
47-
obj = parse_object(sha1);
48-
if (!obj) {
49-
if (quiet)
50-
return 0;
51-
die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1));
52-
}
48+
49+
/* This changes the semantics slightly that even under quiet we
50+
* detect and return error if the repository is corrupt and
51+
* ref points at a nonexistent object.
52+
*/
53+
if (!has_sha1_file(sha1))
54+
die("git-show-ref: bad ref %s (%s)", refname,
55+
sha1_to_hex(sha1));
56+
5357
if (quiet)
5458
return 0;
5559

@@ -58,11 +62,25 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
5862
printf("%s\n", hex);
5963
else
6064
printf("%s %s\n", hex, refname);
61-
if (deref_tags && obj->type == OBJ_TAG) {
62-
obj = deref_tag(obj, refname, 0);
63-
hex = find_unique_abbrev(obj->sha1, abbrev);
65+
66+
if (!deref_tags)
67+
return 0;
68+
69+
if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
70+
hex = find_unique_abbrev(peeled, abbrev);
6471
printf("%s %s^{}\n", hex, refname);
6572
}
73+
else {
74+
obj = parse_object(sha1);
75+
if (!obj)
76+
die("git-show-ref: bad ref %s (%s)", refname,
77+
sha1_to_hex(sha1));
78+
if (obj->type == OBJ_TAG) {
79+
obj = deref_tag(obj, refname, 0);
80+
hex = find_unique_abbrev(obj->sha1, abbrev);
81+
printf("%s %s^{}\n", hex, refname);
82+
}
83+
}
6684
return 0;
6785
}
6886

refs.c

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
#include "refs.h"
22
#include "cache.h"
3+
#include "object.h"
4+
#include "tag.h"
35

46
#include <errno.h>
57

68
struct ref_list {
79
struct ref_list *next;
8-
unsigned char flag; /* ISSYMREF? ISPACKED? */
10+
unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
911
unsigned char sha1[20];
1012
char name[FLEX_ARRAY];
1113
};
1214

13-
static const char *parse_ref_line(char *line, unsigned char *sha1)
15+
static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
1416
{
1517
/*
1618
* 42: the answer to everything.
@@ -21,6 +23,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
2123
* +1 (newline at the end of the line)
2224
*/
2325
int len = strlen(line) - 42;
26+
int peeled = 0;
2427

2528
if (len <= 0)
2629
return NULL;
@@ -29,11 +32,24 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
2932
if (!isspace(line[40]))
3033
return NULL;
3134
line += 41;
32-
if (isspace(*line))
33-
return NULL;
35+
36+
if (isspace(*line)) {
37+
/* "SHA-1 SP SP refs/tags/tagname^{} LF"? */
38+
line++;
39+
len--;
40+
peeled = 1;
41+
}
3442
if (line[len] != '\n')
3543
return NULL;
3644
line[len] = 0;
45+
46+
if (peeled && (len < 3 || strcmp(line + len - 3, "^{}")))
47+
return NULL;
48+
49+
if (!peeled)
50+
*flag &= ~REF_ISPEELED;
51+
else
52+
*flag |= REF_ISPEELED;
3753
return line;
3854
}
3955

@@ -108,10 +124,12 @@ static struct ref_list *get_packed_refs(void)
108124
char refline[PATH_MAX];
109125
while (fgets(refline, sizeof(refline), f)) {
110126
unsigned char sha1[20];
111-
const char *name = parse_ref_line(refline, sha1);
127+
int flag = REF_ISPACKED;
128+
const char *name =
129+
parse_ref_line(refline, sha1, &flag);
112130
if (!name)
113131
continue;
114-
list = add_ref(name, sha1, REF_ISPACKED, list);
132+
list = add_ref(name, sha1, flag, list);
115133
}
116134
fclose(f);
117135
refs = list;
@@ -207,7 +225,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
207225
if (lstat(path, &st) < 0) {
208226
struct ref_list *list = get_packed_refs();
209227
while (list) {
210-
if (!strcmp(ref, list->name)) {
228+
if (!(list->flag & REF_ISPEELED) &&
229+
!strcmp(ref, list->name)) {
211230
hashcpy(sha1, list->sha1);
212231
if (flag)
213232
*flag |= REF_ISPACKED;
@@ -329,13 +348,53 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
329348
return 0;
330349
if (is_null_sha1(entry->sha1))
331350
return 0;
351+
if (entry->flag & REF_ISPEELED)
352+
return 0;
332353
if (!has_sha1_file(entry->sha1)) {
333354
error("%s does not point to a valid object!", entry->name);
334355
return 0;
335356
}
336357
return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
337358
}
338359

360+
int peel_ref(const char *ref, unsigned char *sha1)
361+
{
362+
int flag;
363+
unsigned char base[20];
364+
struct object *o;
365+
366+
if (!resolve_ref(ref, base, 1, &flag))
367+
return -1;
368+
369+
if ((flag & REF_ISPACKED)) {
370+
struct ref_list *list = get_packed_refs();
371+
int len = strlen(ref);
372+
373+
while (list) {
374+
if ((list->flag & REF_ISPEELED) &&
375+
!strncmp(list->name, ref, len) &&
376+
strlen(list->name) == len + 3 &&
377+
!strcmp(list->name + len, "^{}")) {
378+
hashcpy(sha1, list->sha1);
379+
return 0;
380+
}
381+
list = list->next;
382+
}
383+
/* older pack-refs did not leave peeled ones in */
384+
}
385+
386+
/* otherwise ... */
387+
o = parse_object(base);
388+
if (o->type == OBJ_TAG) {
389+
o = deref_tag(o, ref, 0);
390+
if (o) {
391+
hashcpy(sha1, o->sha1);
392+
return 0;
393+
}
394+
}
395+
return -1;
396+
}
397+
339398
static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
340399
void *cb_data)
341400
{

refs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ struct ref_lock {
1616
*/
1717
#define REF_ISSYMREF 01
1818
#define REF_ISPACKED 02
19+
#define REF_ISPEELED 04 /* internal use */
20+
1921
typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
2022
extern int head_ref(each_ref_fn, void *);
2123
extern int for_each_ref(each_ref_fn, void *);
2224
extern int for_each_tag_ref(each_ref_fn, void *);
2325
extern int for_each_branch_ref(each_ref_fn, void *);
2426
extern int for_each_remote_ref(each_ref_fn, void *);
2527

28+
extern int peel_ref(const char *, unsigned char *);
29+
2630
/** Reads the refs file specified into sha1 **/
2731
extern int get_ref_sha1(const char *ref, unsigned char *sha1);
2832

0 commit comments

Comments
 (0)