1#include "builtin.h"
2#include "cache.h"
3#include "commit.h"
4#include "tag.h"
5#include "refs.h"
6#include "parse-options.h"
7#include "sha1-lookup.h"
8
9#define CUTOFF_DATE_SLOP 86400 /* one day */
10
11typedef struct rev_name {
12 const char *tip_name;
13 unsigned long taggerdate;
14 int generation;
15 int distance;
16} rev_name;
17
18static long cutoff = LONG_MAX;
19
20/* How many generations are maximally preferred over _one_ merge traversal? */
21#define MERGE_TRAVERSAL_WEIGHT 65535
22
23static void name_rev(struct commit *commit,
24 const char *tip_name, unsigned long taggerdate,
25 int generation, int distance,
26 int deref)
27{
28 struct rev_name *name = (struct rev_name *)commit->util;
29 struct commit_list *parents;
30 int parent_number = 1;
31
32 parse_commit(commit);
33
34 if (commit->date < cutoff)
35 return;
36
37 if (deref) {
38 tip_name = xstrfmt("%s^0", tip_name);
39
40 if (generation)
41 die("generation: %d, but deref?", generation);
42 }
43
44 if (name == NULL) {
45 name = xmalloc(sizeof(rev_name));
46 commit->util = name;
47 goto copy_data;
48 } else if (name->taggerdate > taggerdate ||
49 (name->taggerdate == taggerdate &&
50 name->distance > distance)) {
51copy_data:
52 name->tip_name = tip_name;
53 name->taggerdate = taggerdate;
54 name->generation = generation;
55 name->distance = distance;
56 } else
57 return;
58
59 for (parents = commit->parents;
60 parents;
61 parents = parents->next, parent_number++) {
62 if (parent_number > 1) {
63 int len = strlen(tip_name);
64 char *new_name = xmalloc(len +
65 1 + decimal_length(generation) + /* ~<n> */
66 1 + 2 + /* ^NN */
67 1);
68
69 if (len > 2 && !strcmp(tip_name + len - 2, "^0"))
70 len -= 2;
71 if (generation > 0)
72 sprintf(new_name, "%.*s~%d^%d", len, tip_name,
73 generation, parent_number);
74 else
75 sprintf(new_name, "%.*s^%d", len, tip_name,
76 parent_number);
77
78 name_rev(parents->item, new_name, taggerdate, 0,
79 distance + MERGE_TRAVERSAL_WEIGHT, 0);
80 } else {
81 name_rev(parents->item, tip_name, taggerdate,
82 generation + 1, distance + 1, 0);
83 }
84 }
85}
86
87static int subpath_matches(const char *path, const char *filter)
88{
89 const char *subpath = path;
90
91 while (subpath) {
92 if (!wildmatch(filter, subpath, 0, NULL))
93 return subpath - path;
94 subpath = strchr(subpath, '/');
95 if (subpath)
96 subpath++;
97 }
98 return -1;
99}
100
101static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
102{
103 if (shorten_unambiguous)
104 refname = shorten_unambiguous_ref(refname, 0);
105 else if (starts_with(refname, "refs/heads/"))
106 refname = refname + 11;
107 else if (starts_with(refname, "refs/"))
108 refname = refname + 5;
109 return refname;
110}
111
112struct name_ref_data {
113 int tags_only;
114 int name_only;
115 const char *ref_filter;
116};
117
118static struct tip_table {
119 struct tip_table_entry {
120 unsigned char sha1[20];
121 const char *refname;
122 } *table;
123 int nr;
124 int alloc;
125 int sorted;
126} tip_table;
127
128static void add_to_tip_table(const unsigned char *sha1, const char *refname,
129 int shorten_unambiguous)
130{
131 refname = name_ref_abbrev(refname, shorten_unambiguous);
132
133 ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
134 hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
135 tip_table.table[tip_table.nr].refname = xstrdup(refname);
136 tip_table.nr++;
137 tip_table.sorted = 0;
138}
139
140static int tipcmp(const void *a_, const void *b_)
141{
142 const struct tip_table_entry *a = a_, *b = b_;
143 return hashcmp(a->sha1, b->sha1);
144}
145
146static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
147{
148 struct object *o = parse_object(sha1);
149 struct name_ref_data *data = cb_data;
150 int can_abbreviate_output = data->tags_only && data->name_only;
151 int deref = 0;
152 unsigned long taggerdate = ULONG_MAX;
153
154 if (data->tags_only && !starts_with(path, "refs/tags/"))
155 return 0;
156
157 if (data->ref_filter) {
158 switch (subpath_matches(path, data->ref_filter)) {
159 case -1: /* did not match */
160 return 0;
161 case 0: /* matched fully */
162 break;
163 default: /* matched subpath */
164 can_abbreviate_output = 1;
165 break;
166 }
167 }
168
169 add_to_tip_table(sha1, path, can_abbreviate_output);
170
171 while (o && o->type == OBJ_TAG) {
172 struct tag *t = (struct tag *) o;
173 if (!t->tagged)
174 break; /* broken repository */
175 o = parse_object(t->tagged->sha1);
176 deref = 1;
177 taggerdate = t->date;
178 }
179 if (o && o->type == OBJ_COMMIT) {
180 struct commit *commit = (struct commit *)o;
181
182 path = name_ref_abbrev(path, can_abbreviate_output);
183 name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref);
184 }
185 return 0;
186}
187
188static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
189{
190 struct tip_table_entry *table = table_;
191 return table[ix].sha1;
192}
193
194static const char *get_exact_ref_match(const struct object *o)
195{
196 int found;
197
198 if (!tip_table.table || !tip_table.nr)
199 return NULL;
200
201 if (!tip_table.sorted) {
202 qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table),
203 tipcmp);
204 tip_table.sorted = 1;
205 }
206
207 found = sha1_pos(o->sha1, tip_table.table, tip_table.nr,
208 nth_tip_table_ent);
209 if (0 <= found)
210 return tip_table.table[found].refname;
211 return NULL;
212}
213
214/* returns a static buffer */
215static const char *get_rev_name(const struct object *o)
216{
217 static char buffer[1024];
218 struct rev_name *n;
219 struct commit *c;
220
221 if (o->type != OBJ_COMMIT)
222 return get_exact_ref_match(o);
223 c = (struct commit *) o;
224 n = c->util;
225 if (!n)
226 return NULL;
227
228 if (!n->generation)
229 return n->tip_name;
230 else {
231 int len = strlen(n->tip_name);
232 if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
233 len -= 2;
234 snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
235 n->generation);
236
237 return buffer;
238 }
239}
240
241static void show_name(const struct object *obj,
242 const char *caller_name,
243 int always, int allow_undefined, int name_only)
244{
245 const char *name;
246 const unsigned char *sha1 = obj->sha1;
247
248 if (!name_only)
249 printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
250 name = get_rev_name(obj);
251 if (name)
252 printf("%s\n", name);
253 else if (allow_undefined)
254 printf("undefined\n");
255 else if (always)
256 printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
257 else
258 die("cannot describe '%s'", sha1_to_hex(sha1));
259}
260
261static char const * const name_rev_usage[] = {
262 N_("git name-rev [<options>] <commit>..."),
263 N_("git name-rev [<options>] --all"),
264 N_("git name-rev [<options>] --stdin"),
265 NULL
266};
267
268static void name_rev_line(char *p, struct name_ref_data *data)
269{
270 int forty = 0;
271 char *p_start;
272 for (p_start = p; *p; p++) {
273#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
274 if (!ishex(*p))
275 forty = 0;
276 else if (++forty == 40 &&
277 !ishex(*(p+1))) {
278 unsigned char sha1[40];
279 const char *name = NULL;
280 char c = *(p+1);
281 int p_len = p - p_start + 1;
282
283 forty = 0;
284
285 *(p+1) = 0;
286 if (!get_sha1(p - 39, sha1)) {
287 struct object *o =
288 lookup_object(sha1);
289 if (o)
290 name = get_rev_name(o);
291 }
292 *(p+1) = c;
293
294 if (!name)
295 continue;
296
297 if (data->name_only)
298 printf("%.*s%s", p_len - 40, p_start, name);
299 else
300 printf("%.*s (%s)", p_len, p_start, name);
301 p_start = p + 1;
302 }
303 }
304
305 /* flush */
306 if (p_start != p)
307 fwrite(p_start, p - p_start, 1, stdout);
308}
309
310int cmd_name_rev(int argc, const char **argv, const char *prefix)
311{
312 struct object_array revs = OBJECT_ARRAY_INIT;
313 int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
314 struct name_ref_data data = { 0, 0, NULL };
315 struct option opts[] = {
316 OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
317 OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
318 OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
319 N_("only use refs matching <pattern>")),
320 OPT_GROUP(""),
321 OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
322 OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
323 OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
324 OPT_BOOL(0, "always", &always,
325 N_("show abbreviated commit object as fallback")),
326 {
327 /* A Hidden OPT_BOOL */
328 OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
329 N_("dereference tags in the input (internal use)"),
330 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
331 },
332 OPT_END(),
333 };
334
335 git_config(git_default_config, NULL);
336 argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
337 if (all + transform_stdin + !!argc > 1) {
338 error("Specify either a list, or --all, not both!");
339 usage_with_options(name_rev_usage, opts);
340 }
341 if (all || transform_stdin)
342 cutoff = 0;
343
344 for (; argc; argc--, argv++) {
345 unsigned char sha1[20];
346 struct object *object;
347 struct commit *commit;
348
349 if (get_sha1(*argv, sha1)) {
350 fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
351 *argv);
352 continue;
353 }
354
355 commit = NULL;
356 object = parse_object(sha1);
357 if (object) {
358 struct object *peeled = deref_tag(object, *argv, 0);
359 if (peeled && peeled->type == OBJ_COMMIT)
360 commit = (struct commit *)peeled;
361 }
362
363 if (!object) {
364 fprintf(stderr, "Could not get object for %s. Skipping.\n",
365 *argv);
366 continue;
367 }
368
369 if (commit) {
370 if (cutoff > commit->date)
371 cutoff = commit->date;
372 }
373
374 if (peel_tag) {
375 if (!commit) {
376 fprintf(stderr, "Could not get commit for %s. Skipping.\n",
377 *argv);
378 continue;
379 }
380 object = (struct object *)commit;
381 }
382 add_object_array(object, *argv, &revs);
383 }
384
385 if (cutoff)
386 cutoff = cutoff - CUTOFF_DATE_SLOP;
387 for_each_ref(name_ref, &data);
388
389 if (transform_stdin) {
390 char buffer[2048];
391
392 while (!feof(stdin)) {
393 char *p = fgets(buffer, sizeof(buffer), stdin);
394 if (!p)
395 break;
396 name_rev_line(p, &data);
397 }
398 } else if (all) {
399 int i, max;
400
401 max = get_max_object_index();
402 for (i = 0; i < max; i++) {
403 struct object *obj = get_indexed_object(i);
404 if (!obj || obj->type != OBJ_COMMIT)
405 continue;
406 show_name(obj, NULL,
407 always, allow_undefined, data.name_only);
408 }
409 } else {
410 int i;
411 for (i = 0; i < revs.nr; i++)
412 show_name(revs.objects[i].item, revs.objects[i].name,
413 always, allow_undefined, data.name_only);
414 }
415
416 return 0;
417}