Use binary searching on large buckets in git-describe.
[git/git.git] / builtin-describe.c
CommitLineData
908e5310
LT
1#include "cache.h"
2#include "commit.h"
635d4134 3#include "tag.h"
908e5310 4#include "refs.h"
80dbae03
SP
5#include "diff.h"
6#include "diffcore.h"
7#include "revision.h"
9a0eaf83 8#include "builtin.h"
908e5310 9
2d9e7c9f
JH
10static const char describe_usage[] =
11"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
908e5310 12
96f1e58f
DR
13static int all; /* Default to annotated tags only */
14static int tags; /* But allow any tags if --tags is specified */
2d9e7c9f 15static int abbrev = DEFAULT_ABBREV;
908e5310 16
c3e3cd4b 17static unsigned int names[256], allocs[256];
908e5310 18static struct commit_name {
80dbae03 19 struct commit *commit;
64deb858 20 int prio; /* annotated tag = 2, tag = 1, head = 0 */
5a2282de 21 char path[FLEX_ARRAY]; /* more */
c3e3cd4b 22} **name_array[256];
908e5310
LT
23
24static struct commit_name *match(struct commit *cmit)
25{
910c0d7b
SP
26 unsigned char level0 = cmit->object.sha1[0];
27 struct commit_name **p = name_array[level0];
28 unsigned int hi = names[level0];
29 unsigned int lo = 0;
908e5310 30
910c0d7b
SP
31 while (lo < hi) {
32 unsigned int mi = (lo + hi) / 2;
33 int cmp = hashcmp(p[mi]->commit->object.sha1,
34 cmit->object.sha1);
35 if (!cmp) {
36 while (mi && p[mi - 1]->commit == cmit)
37 mi--;
38 return p[mi];
39 }
40 if (cmp > 0)
41 hi = mi;
42 else
43 lo = mi+1;
908e5310
LT
44 }
45 return NULL;
46}
47
64deb858 48static void add_to_known_names(const char *path,
80dbae03 49 struct commit *commit,
64deb858 50 int prio)
908e5310
LT
51{
52 int idx;
53 int len = strlen(path)+1;
54 struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
c3e3cd4b 55 unsigned char m = commit->object.sha1[0];
908e5310
LT
56
57 name->commit = commit;
f7122265 58 name->prio = prio;
908e5310 59 memcpy(name->path, path, len);
c3e3cd4b
SP
60 idx = names[m];
61 if (idx >= allocs[m]) {
62 allocs[m] = (idx + 50) * 3 / 2;
63 name_array[m] = xrealloc(name_array[m],
64 allocs[m] * sizeof(*name_array));
908e5310 65 }
c3e3cd4b
SP
66 name_array[m][idx] = name;
67 names[m] = ++idx;
908e5310
LT
68}
69
8da19775 70static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
908e5310
LT
71{
72 struct commit *commit = lookup_commit_reference_gently(sha1, 1);
64deb858
JH
73 struct object *object;
74 int prio;
75
908e5310
LT
76 if (!commit)
77 return 0;
64deb858 78 object = parse_object(sha1);
2d9e7c9f
JH
79 /* If --all, then any refs are used.
80 * If --tags, then any tags are used.
81 * Otherwise only annotated tags are used.
82 */
64deb858 83 if (!strncmp(path, "refs/tags/", 10)) {
1974632c 84 if (object->type == OBJ_TAG)
64deb858
JH
85 prio = 2;
86 else
87 prio = 1;
88 }
89 else
90 prio = 0;
91
635d4134 92 if (!all) {
64deb858
JH
93 if (!prio)
94 return 0;
95 if (!tags && prio < 2)
635d4134 96 return 0;
635d4134 97 }
64deb858 98 add_to_known_names(all ? path + 5 : path + 10, commit, prio);
908e5310
LT
99 return 0;
100}
101
102static int compare_names(const void *_a, const void *_b)
103{
104 struct commit_name *a = *(struct commit_name **)_a;
105 struct commit_name *b = *(struct commit_name **)_b;
106 unsigned long a_date = a->commit->date;
107 unsigned long b_date = b->commit->date;
910c0d7b 108 int cmp = hashcmp(a->commit->object.sha1, b->commit->object.sha1);
64deb858 109
910c0d7b
SP
110 if (cmp)
111 return cmp;
64deb858
JH
112 if (a->prio != b->prio)
113 return b->prio - a->prio;
908e5310
LT
114 return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
115}
116
80dbae03
SP
117struct possible_tag {
118 struct possible_tag *next;
119 struct commit_name *name;
120 unsigned long depth;
121};
122
554fe20d 123static void describe(const char *arg, int last_one)
908e5310 124{
4c34a2c5
JH
125 unsigned char sha1[20];
126 struct commit *cmit;
908e5310
LT
127 struct commit_list *list;
128 static int initialized = 0;
129 struct commit_name *n;
80dbae03 130 struct possible_tag *all_matches, *min_match, *cur_match;
908e5310 131
31fff305
DL
132 if (get_sha1(arg, sha1))
133 die("Not a valid object name %s", arg);
4c34a2c5
JH
134 cmit = lookup_commit_reference(sha1);
135 if (!cmit)
31fff305 136 die("%s is not a valid '%s' object", arg, commit_type);
4c34a2c5 137
908e5310 138 if (!initialized) {
c3e3cd4b 139 unsigned int m;
908e5310 140 initialized = 1;
cb5d709f 141 for_each_ref(get_name, NULL);
c3e3cd4b
SP
142 for (m = 0; m < ARRAY_SIZE(name_array); m++)
143 qsort(name_array[m], names[m],
144 sizeof(*name_array[m]), compare_names);
908e5310
LT
145 }
146
147 n = match(cmit);
148 if (n) {
149 printf("%s\n", n->path);
150 return;
151 }
152
153 list = NULL;
80dbae03
SP
154 all_matches = NULL;
155 cur_match = NULL;
908e5310
LT
156 commit_list_insert(cmit, &list);
157 while (list) {
80dbae03 158 struct commit *c = pop_commit(&list);
dccd0c2a 159 struct commit_list *parents = c->parents;
908e5310
LT
160 n = match(c);
161 if (n) {
80dbae03
SP
162 struct possible_tag *p = xmalloc(sizeof(*p));
163 p->name = n;
164 p->next = NULL;
165 if (cur_match)
166 cur_match->next = p;
167 else
168 all_matches = p;
169 cur_match = p;
dccd0c2a
SP
170 if (n->prio == 2)
171 continue;
172 }
173 while (parents) {
174 struct commit *p = parents->item;
175 parse_commit(p);
176 if (!(p->object.flags & SEEN)) {
177 p->object.flags |= SEEN;
178 insert_by_date(p, &list);
80dbae03 179 }
dccd0c2a 180 parents = parents->next;
80dbae03
SP
181 }
182 }
183
184 if (!all_matches)
185 die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
186
187 min_match = NULL;
188 for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
189 struct rev_info revs;
190 struct commit *tagged = cur_match->name->commit;
191
192 clear_commit_marks(cmit, -1);
193 init_revisions(&revs, NULL);
194 tagged->object.flags |= UNINTERESTING;
195 add_pending_object(&revs, &tagged->object, NULL);
196 add_pending_object(&revs, &cmit->object, NULL);
197
198 prepare_revision_walk(&revs);
199 cur_match->depth = 0;
200 while ((!min_match || cur_match->depth < min_match->depth)
201 && get_revision(&revs))
202 cur_match->depth++;
dccd0c2a
SP
203 if (!min_match || (cur_match->depth < min_match->depth
204 && cur_match->name->prio >= min_match->name->prio))
80dbae03 205 min_match = cur_match;
94d23673 206 free_commit_list(revs.commits);
80dbae03
SP
207 }
208 printf("%s-g%s\n", min_match->name->path,
209 find_unique_abbrev(cmit->object.sha1, abbrev));
210
211 if (!last_one) {
212 for (cur_match = all_matches; cur_match; cur_match = min_match) {
213 min_match = cur_match->next;
214 free(cur_match);
908e5310 215 }
80dbae03 216 clear_commit_marks(cmit, SEEN);
908e5310
LT
217 }
218}
219
9a0eaf83 220int cmd_describe(int argc, const char **argv, const char *prefix)
908e5310
LT
221{
222 int i;
223
224 for (i = 1; i < argc; i++) {
225 const char *arg = argv[i];
908e5310 226
4c34a2c5
JH
227 if (*arg != '-')
228 break;
229 else if (!strcmp(arg, "--all"))
908e5310 230 all = 1;
4c34a2c5 231 else if (!strcmp(arg, "--tags"))
2d9e7c9f 232 tags = 1;
4c34a2c5 233 else if (!strncmp(arg, "--abbrev=", 9)) {
2d9e7c9f 234 abbrev = strtoul(arg + 9, NULL, 10);
f7122265 235 if (abbrev < MINIMUM_ABBREV || 40 < abbrev)
2d9e7c9f 236 abbrev = DEFAULT_ABBREV;
2d9e7c9f 237 }
4c34a2c5 238 else
908e5310 239 usage(describe_usage);
908e5310 240 }
4c34a2c5 241
8c599c74 242 save_commit_buffer = 0;
8112894d 243
5b6df8e4 244 if (argc <= i)
fec9ebf1 245 describe("HEAD", 1);
4c34a2c5 246 else
fec9ebf1
JH
247 while (i < argc) {
248 describe(argv[i], (i == argc - 1));
249 i++;
250 }
4c34a2c5 251
908e5310
LT
252 return 0;
253}