]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/name-rev.c
Tenth batch for 2.14
[thirdparty/git.git] / builtin / name-rev.c
CommitLineData
d6b64ed0 1#include "builtin.h"
bd321bcc
JS
2#include "cache.h"
3#include "commit.h"
4#include "tag.h"
5#include "refs.h"
edefb1a2 6#include "parse-options.h"
b23e0b93 7#include "sha1-lookup.h"
bd321bcc 8
c075aea5
JH
9#define CUTOFF_DATE_SLOP 86400 /* one day */
10
bd321bcc
JS
11typedef struct rev_name {
12 const char *tip_name;
dddbad72 13 timestamp_t taggerdate;
bd321bcc 14 int generation;
ac076c29 15 int distance;
ef1e7406 16 int from_tag;
bd321bcc
JS
17} rev_name;
18
5589e87f 19static timestamp_t cutoff = TIME_MAX;
bd321bcc 20
ac076c29
JS
21/* How many generations are maximally preferred over _one_ merge traversal? */
22#define MERGE_TRAVERSAL_WEIGHT 65535
23
0041bf65
JH
24static int is_better_name(struct rev_name *name,
25 const char *tip_name,
78089b71 26 timestamp_t taggerdate,
0041bf65 27 int generation,
ef1e7406
JH
28 int distance,
29 int from_tag)
0041bf65 30{
ef1e7406
JH
31 /*
32 * When comparing names based on tags, prefer names
33 * based on the older tag, even if it is farther away.
34 */
35 if (from_tag && name->from_tag)
36 return (name->taggerdate > taggerdate ||
37 (name->taggerdate == taggerdate &&
38 name->distance > distance));
39
40 /*
41 * We know that at least one of them is a non-tag at this point.
42 * favor a tag over a non-tag.
43 */
44 if (name->from_tag != from_tag)
45 return from_tag;
46
47 /*
48 * We are now looking at two non-tags. Tiebreak to favor
49 * shorter hops.
50 */
51 if (name->distance != distance)
52 return name->distance > distance;
53
54 /* ... or tiebreak to favor older date */
55 if (name->taggerdate != taggerdate)
56 return name->taggerdate > taggerdate;
57
58 /* keep the current one if we cannot decide */
59 return 0;
0041bf65
JH
60}
61
bd321bcc 62static void name_rev(struct commit *commit,
dddbad72 63 const char *tip_name, timestamp_t taggerdate,
ef1e7406 64 int generation, int distance, int from_tag,
bd321bcc
JS
65 int deref)
66{
d3ff6f55 67 struct rev_name *name = (struct rev_name *)commit->util;
bd321bcc 68 struct commit_list *parents;
f2e6f1c9 69 int parent_number = 1;
53082246 70 char *to_free = NULL;
bd321bcc 71
0064053b 72 parse_commit(commit);
bd321bcc
JS
73
74 if (commit->date < cutoff)
75 return;
76
77 if (deref) {
53082246 78 tip_name = to_free = xstrfmt("%s^0", tip_name);
bd321bcc
JS
79
80 if (generation)
81 die("generation: %d, but deref?", generation);
82 }
83
84 if (name == NULL) {
85 name = xmalloc(sizeof(rev_name));
d3ff6f55 86 commit->util = name;
bd321bcc 87 goto copy_data;
0041bf65 88 } else if (is_better_name(name, tip_name, taggerdate,
ef1e7406 89 generation, distance, from_tag)) {
bd321bcc
JS
90copy_data:
91 name->tip_name = tip_name;
75504248 92 name->taggerdate = taggerdate;
bd321bcc 93 name->generation = generation;
ac076c29 94 name->distance = distance;
ef1e7406 95 name->from_tag = from_tag;
53082246
JS
96 } else {
97 free(to_free);
bd321bcc 98 return;
53082246 99 }
bd321bcc
JS
100
101 for (parents = commit->parents;
102 parents;
103 parents = parents->next, parent_number++) {
f2e6f1c9 104 if (parent_number > 1) {
34e02deb 105 size_t len;
75faa45a 106 char *new_name;
bd321bcc 107
34e02deb 108 strip_suffix(tip_name, "^0", &len);
bd321bcc 109 if (generation > 0)
34e02deb 110 new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
75faa45a 111 generation, parent_number);
bd321bcc 112 else
34e02deb 113 new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
75faa45a 114 parent_number);
bd321bcc 115
75504248 116 name_rev(parents->item, new_name, taggerdate, 0,
ef1e7406
JH
117 distance + MERGE_TRAVERSAL_WEIGHT,
118 from_tag, 0);
bd321bcc 119 } else {
75504248 120 name_rev(parents->item, tip_name, taggerdate,
ef1e7406
JH
121 generation + 1, distance + 1,
122 from_tag, 0);
bd321bcc
JS
123 }
124 }
125}
126
98c5c4ad
NK
127static int subpath_matches(const char *path, const char *filter)
128{
129 const char *subpath = path;
130
131 while (subpath) {
eb07894f 132 if (!wildmatch(filter, subpath, 0, NULL))
98c5c4ad
NK
133 return subpath - path;
134 subpath = strchr(subpath, '/');
135 if (subpath)
136 subpath++;
137 }
138 return -1;
139}
140
9608a190
JH
141static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
142{
143 if (shorten_unambiguous)
144 refname = shorten_unambiguous_ref(refname, 0);
59556548 145 else if (starts_with(refname, "refs/heads/"))
9608a190 146 refname = refname + 11;
59556548 147 else if (starts_with(refname, "refs/"))
9608a190
JH
148 refname = refname + 5;
149 return refname;
150}
151
2afc29aa
JS
152struct name_ref_data {
153 int tags_only;
23615708 154 int name_only;
290be667 155 struct string_list ref_filters;
96415b49 156 struct string_list exclude_filters;
2afc29aa
JS
157};
158
b23e0b93
JH
159static struct tip_table {
160 struct tip_table_entry {
511dca80 161 struct object_id oid;
b23e0b93
JH
162 const char *refname;
163 } *table;
164 int nr;
165 int alloc;
166 int sorted;
167} tip_table;
168
511dca80 169static void add_to_tip_table(const struct object_id *oid, const char *refname,
b23e0b93
JH
170 int shorten_unambiguous)
171{
172 refname = name_ref_abbrev(refname, shorten_unambiguous);
173
174 ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
511dca80 175 oidcpy(&tip_table.table[tip_table.nr].oid, oid);
b23e0b93
JH
176 tip_table.table[tip_table.nr].refname = xstrdup(refname);
177 tip_table.nr++;
178 tip_table.sorted = 0;
179}
180
181static int tipcmp(const void *a_, const void *b_)
182{
183 const struct tip_table_entry *a = a_, *b = b_;
511dca80 184 return oidcmp(&a->oid, &b->oid);
b23e0b93
JH
185}
186
73868486 187static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
bd321bcc 188{
c251c83d 189 struct object *o = parse_object(oid);
2afc29aa 190 struct name_ref_data *data = cb_data;
98c5c4ad 191 int can_abbreviate_output = data->tags_only && data->name_only;
bd321bcc 192 int deref = 0;
dddbad72 193 timestamp_t taggerdate = TIME_MAX;
bd321bcc 194
59556548 195 if (data->tags_only && !starts_with(path, "refs/tags/"))
2afc29aa
JS
196 return 0;
197
96415b49
JK
198 if (data->exclude_filters.nr) {
199 struct string_list_item *item;
200
201 for_each_string_list_item(item, &data->exclude_filters) {
202 if (subpath_matches(path, item->string) >= 0)
203 return 0;
204 }
205 }
206
290be667
JK
207 if (data->ref_filters.nr) {
208 struct string_list_item *item;
209 int matched = 0;
210
211 /* See if any of the patterns match. */
212 for_each_string_list_item(item, &data->ref_filters) {
213 /*
214 * Check all patterns even after finding a match, so
215 * that we can see if a match with a subpath exists.
216 * When a user asked for 'refs/tags/v*' and 'v1.*',
217 * both of which match, the user is showing her
218 * willingness to accept a shortened output by having
219 * the 'v1.*' in the acceptable refnames, so we
220 * shouldn't stop when seeing 'refs/tags/v1.4' matches
221 * 'refs/tags/v*'. We should show it as 'v1.4'.
222 */
223 switch (subpath_matches(path, item->string)) {
224 case -1: /* did not match */
225 break;
226 case 0: /* matched fully */
227 matched = 1;
228 break;
229 default: /* matched subpath */
230 matched = 1;
231 can_abbreviate_output = 1;
232 break;
233 }
98c5c4ad 234 }
290be667
JK
235
236 /* If none of the patterns matched, stop now */
237 if (!matched)
238 return 0;
98c5c4ad 239 }
bd321bcc 240
511dca80 241 add_to_tip_table(oid, path, can_abbreviate_output);
b23e0b93 242
1974632c 243 while (o && o->type == OBJ_TAG) {
bd321bcc
JS
244 struct tag *t = (struct tag *) o;
245 if (!t->tagged)
246 break; /* broken repository */
c251c83d 247 o = parse_object(&t->tagged->oid);
bd321bcc 248 deref = 1;
75504248 249 taggerdate = t->date;
bd321bcc 250 }
1974632c 251 if (o && o->type == OBJ_COMMIT) {
bd321bcc 252 struct commit *commit = (struct commit *)o;
ef1e7406 253 int from_tag = starts_with(path, "refs/tags/");
bd321bcc 254
ef1e7406
JH
255 if (taggerdate == ULONG_MAX)
256 taggerdate = ((struct commit *)o)->date;
9608a190 257 path = name_ref_abbrev(path, can_abbreviate_output);
ef1e7406
JH
258 name_rev(commit, xstrdup(path), taggerdate, 0, 0,
259 from_tag, deref);
bd321bcc
JS
260 }
261 return 0;
262}
263
b23e0b93
JH
264static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
265{
266 struct tip_table_entry *table = table_;
511dca80 267 return table[ix].oid.hash;
b23e0b93
JH
268}
269
270static const char *get_exact_ref_match(const struct object *o)
271{
272 int found;
273
274 if (!tip_table.table || !tip_table.nr)
275 return NULL;
276
277 if (!tip_table.sorted) {
9ed0d8d6 278 QSORT(tip_table.table, tip_table.nr, tipcmp);
b23e0b93
JH
279 tip_table.sorted = 1;
280 }
281
ed1c9977 282 found = sha1_pos(o->oid.hash, tip_table.table, tip_table.nr,
b23e0b93
JH
283 nth_tip_table_ent);
284 if (0 <= found)
285 return tip_table.table[found].refname;
286 return NULL;
287}
288
903fc7da
JK
289/* may return a constant string or use "buf" as scratch space */
290static const char *get_rev_name(const struct object *o, struct strbuf *buf)
bd321bcc 291{
d3ff6f55
LT
292 struct rev_name *n;
293 struct commit *c;
294
1974632c 295 if (o->type != OBJ_COMMIT)
b23e0b93 296 return get_exact_ref_match(o);
d3ff6f55
LT
297 c = (struct commit *) o;
298 n = c->util;
bd321bcc 299 if (!n)
a2cf9f44 300 return NULL;
bd321bcc
JS
301
302 if (!n->generation)
303 return n->tip_name;
59d3f541
JS
304 else {
305 int len = strlen(n->tip_name);
306 if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
307 len -= 2;
903fc7da
JK
308 strbuf_reset(buf);
309 strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
310 return buf->buf;
59d3f541 311 }
bd321bcc 312}
1f1e895f 313
da2478db
JH
314static void show_name(const struct object *obj,
315 const char *caller_name,
316 int always, int allow_undefined, int name_only)
317{
318 const char *name;
f2fd0760 319 const struct object_id *oid = &obj->oid;
903fc7da 320 struct strbuf buf = STRBUF_INIT;
da2478db
JH
321
322 if (!name_only)
f2fd0760 323 printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
903fc7da 324 name = get_rev_name(obj, &buf);
da2478db
JH
325 if (name)
326 printf("%s\n", name);
327 else if (allow_undefined)
328 printf("undefined\n");
329 else if (always)
f2fd0760 330 printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
da2478db 331 else
f2fd0760 332 die("cannot describe '%s'", oid_to_hex(oid));
903fc7da 333 strbuf_release(&buf);
da2478db
JH
334}
335
edefb1a2 336static char const * const name_rev_usage[] = {
9c9b4f2f
AH
337 N_("git name-rev [<options>] <commit>..."),
338 N_("git name-rev [<options>] --all"),
339 N_("git name-rev [<options>] --stdin"),
edefb1a2
PH
340 NULL
341};
342
e8b55fab
JH
343static void name_rev_line(char *p, struct name_ref_data *data)
344{
903fc7da 345 struct strbuf buf = STRBUF_INIT;
e8b55fab
JH
346 int forty = 0;
347 char *p_start;
348 for (p_start = p; *p; p++) {
349#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
350 if (!ishex(*p))
351 forty = 0;
511dca80 352 else if (++forty == GIT_SHA1_HEXSZ &&
e8b55fab 353 !ishex(*(p+1))) {
511dca80 354 struct object_id oid;
e8b55fab
JH
355 const char *name = NULL;
356 char c = *(p+1);
7c5b1675 357 int p_len = p - p_start + 1;
e8b55fab
JH
358
359 forty = 0;
360
361 *(p+1) = 0;
511dca80 362 if (!get_oid(p - (GIT_SHA1_HEXSZ - 1), &oid)) {
e8b55fab 363 struct object *o =
511dca80 364 lookup_object(oid.hash);
e8b55fab 365 if (o)
903fc7da 366 name = get_rev_name(o, &buf);
e8b55fab
JH
367 }
368 *(p+1) = c;
369
370 if (!name)
371 continue;
372
7c5b1675 373 if (data->name_only)
511dca80 374 printf("%.*s%s", p_len - GIT_SHA1_HEXSZ, p_start, name);
7c5b1675
RS
375 else
376 printf("%.*s (%s)", p_len, p_start, name);
e8b55fab
JH
377 p_start = p + 1;
378 }
379 }
380
381 /* flush */
382 if (p_start != p)
383 fwrite(p_start, p - p_start, 1, stdout);
903fc7da
JK
384
385 strbuf_release(&buf);
e8b55fab
JH
386}
387
d6b64ed0 388int cmd_name_rev(int argc, const char **argv, const char *prefix)
bd321bcc 389{
3cd47459 390 struct object_array revs = OBJECT_ARRAY_INIT;
adfc1857 391 int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
96415b49 392 struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
edefb1a2 393 struct option opts[] = {
d5d09d47
SB
394 OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
395 OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
290be667 396 OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
422cad0a 397 N_("only use refs matching <pattern>")),
96415b49
JK
398 OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"),
399 N_("ignore refs matching <pattern>")),
edefb1a2 400 OPT_GROUP(""),
d5d09d47
SB
401 OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
402 OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
403 OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
404 OPT_BOOL(0, "always", &always,
422cad0a 405 N_("show abbreviated commit object as fallback")),
adfc1857
JH
406 {
407 /* A Hidden OPT_BOOL */
408 OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
409 N_("dereference tags in the input (internal use)"),
410 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
411 },
edefb1a2
PH
412 OPT_END(),
413 };
bd321bcc 414
ef90d6d4 415 git_config(git_default_config, NULL);
37782920 416 argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
05efb7b7 417 if (all + transform_stdin + !!argc > 1) {
edefb1a2
PH
418 error("Specify either a list, or --all, not both!");
419 usage_with_options(name_rev_usage, opts);
420 }
421 if (all || transform_stdin)
422 cutoff = 0;
bd321bcc 423
edefb1a2 424 for (; argc; argc--, argv++) {
511dca80 425 struct object_id oid;
118aa4ac 426 struct object *object;
bd321bcc
JS
427 struct commit *commit;
428
511dca80 429 if (get_oid(*argv, &oid)) {
bd321bcc
JS
430 fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
431 *argv);
432 continue;
433 }
434
118aa4ac 435 commit = NULL;
c251c83d 436 object = parse_object(&oid);
118aa4ac
JH
437 if (object) {
438 struct object *peeled = deref_tag(object, *argv, 0);
439 if (peeled && peeled->type == OBJ_COMMIT)
440 commit = (struct commit *)peeled;
441 }
442
443 if (!object) {
444 fprintf(stderr, "Could not get object for %s. Skipping.\n",
bd321bcc
JS
445 *argv);
446 continue;
447 }
448
118aa4ac
JH
449 if (commit) {
450 if (cutoff > commit->date)
451 cutoff = commit->date;
452 }
adfc1857
JH
453
454 if (peel_tag) {
455 if (!commit) {
456 fprintf(stderr, "Could not get commit for %s. Skipping.\n",
457 *argv);
458 continue;
459 }
460 object = (struct object *)commit;
461 }
118aa4ac 462 add_object_array(object, *argv, &revs);
bd321bcc
JS
463 }
464
c075aea5
JH
465 if (cutoff)
466 cutoff = cutoff - CUTOFF_DATE_SLOP;
73868486 467 for_each_ref(name_ref, &data);
bd321bcc
JS
468
469 if (transform_stdin) {
470 char buffer[2048];
bd321bcc
JS
471
472 while (!feof(stdin)) {
e8b55fab 473 char *p = fgets(buffer, sizeof(buffer), stdin);
bd321bcc
JS
474 if (!p)
475 break;
e8b55fab 476 name_rev_line(p, &data);
bd321bcc
JS
477 }
478 } else if (all) {
fc046a75 479 int i, max;
bd321bcc 480
fc046a75 481 max = get_max_object_index();
a83123de
BS
482 for (i = 0; i < max; i++) {
483 struct object *obj = get_indexed_object(i);
e8b14d7e 484 if (!obj || obj->type != OBJ_COMMIT)
a83123de
BS
485 continue;
486 show_name(obj, NULL,
da2478db 487 always, allow_undefined, data.name_only);
a83123de 488 }
1f1e895f
LT
489 } else {
490 int i;
da2478db
JH
491 for (i = 0; i < revs.nr; i++)
492 show_name(revs.objects[i].item, revs.objects[i].name,
493 always, allow_undefined, data.name_only);
1f1e895f 494 }
bd321bcc
JS
495
496 return 0;
497}