describe.c 16.6 KB
Newer Older
1
#include "cache.h"
2
#include "config.h"
3
#include "lockfile.h"
4
#include "commit.h"
5
#include "tag.h"
6
#include "blob.h"
7
#include "refs.h"
8
#include "builtin.h"
9
#include "exec_cmd.h"
10
#include "parse-options.h"
11
#include "revision.h"
12
#include "diff.h"
13
#include "hashmap.h"
14
#include "argv-array.h"
15
#include "run-command.h"
16 17
#include "revision.h"
#include "list-objects.h"
18

19 20
#define MAX_TAGS	(FLAG_BITS - 1)

21
static const char * const describe_usage[] = {
22 23
	N_("git describe [<options>] [<commit-ish>...]"),
	N_("git describe [<options>] --dirty"),
24 25
	NULL
};
26

27
static int debug;	/* Display lots of verbose info */
28 29
static int all;	/* Any valid ref can be used */
static int tags;	/* Allow lightweight tags */
30
static int longformat;
31
static int first_parent;
32
static int abbrev = -1; /* unspecified */
33
static int max_candidates = 10;
34
static struct hashmap names;
35
static int have_util;
36
static struct string_list patterns = STRING_LIST_INIT_NODUP;
37
static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
38
static int always;
39
static const char *suffix, *dirty, *broken;
40 41 42 43 44 45

/* diff-index command arguments to check if working tree is dirty. */
static const char *diff_index_args[] = {
	"diff-index", "--quiet", "HEAD", "--", NULL
};

46
struct commit_name {
47
	struct hashmap_entry entry;
48
	struct object_id peeled;
49
	struct tag *tag;
50 51
	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
	unsigned name_checked:1;
52
	struct object_id oid;
53
	char *path;
54
};
55

56
static const char *prio_names[] = {
57
	N_("head"), N_("lightweight"), N_("annotated"),
58
};
59

60
static int commit_name_cmp(const void *unused_cmp_data,
61 62
			   const void *entry,
			   const void *entry_or_key,
63
			   const void *peeled)
64
{
65 66 67
	const struct commit_name *cn1 = entry;
	const struct commit_name *cn2 = entry_or_key;

68
	return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
69 70
}

71
static inline struct commit_name *find_commit_name(const struct object_id *peeled)
72
{
73
	return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
74 75
}

76 77
static int replace_name(struct commit_name *e,
			       int prio,
78
			       const struct object_id *oid,
79 80 81 82 83 84 85 86 87 88 89 90
			       struct tag **tag)
{
	if (!e || e->prio < prio)
		return 1;

	if (e->prio == 2 && prio == 2) {
		/* Multiple annotated tags point to the same commit.
		 * Select one to keep based upon their tagger date.
		 */
		struct tag *t;

		if (!e->tag) {
91
			t = lookup_tag(&e->oid);
92 93 94 95 96
			if (!t || parse_tag(t))
				return 1;
			e->tag = t;
		}

97
		t = lookup_tag(oid);
98 99 100 101 102 103 104 105 106 107 108
		if (!t || parse_tag(t))
			return 0;
		*tag = t;

		if (e->tag->date < t->date)
			return 1;
	}

	return 0;
}

109
static void add_to_known_names(const char *path,
110
			       const struct object_id *peeled,
111
			       int prio,
112
			       const struct object_id *oid)
113
{
114
	struct commit_name *e = find_commit_name(peeled);
115
	struct tag *tag = NULL;
116
	if (replace_name(e, prio, oid, &tag)) {
117 118
		if (!e) {
			e = xmalloc(sizeof(struct commit_name));
119 120
			oidcpy(&e->peeled, peeled);
			hashmap_entry_init(e, sha1hash(peeled->hash));
121
			hashmap_add(&names, e);
122
			e->path = NULL;
123
		}
124
		e->tag = tag;
125
		e->prio = prio;
126
		e->name_checked = 0;
127
		oidcpy(&e->oid, oid);
128 129
		free(e->path);
		e->path = xstrdup(path);
130 131 132
	}
}

133
static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
134
{
135
	int is_tag = 0;
136
	struct object_id peeled;
137
	int is_annotated, prio;
138 139 140 141 142 143 144 145 146 147 148 149 150
	const char *path_to_match = NULL;

	if (skip_prefix(path, "refs/tags/", &path_to_match)) {
		is_tag = 1;
	} else if (all) {
		if ((exclude_patterns.nr || patterns.nr) &&
		    !skip_prefix(path, "refs/heads/", &path_to_match) &&
		    !skip_prefix(path, "refs/remotes/", &path_to_match)) {
			/* Only accept reference of known type if there are match/exclude patterns */
			return 0;
		}
	} else {
		/* Reject anything outside refs/tags/ unless --all */
151
		return 0;
152
	}
153

154 155 156 157 158 159 160 161
	/*
	 * If we're given exclude patterns, first exclude any tag which match
	 * any of the exclude pattern.
	 */
	if (exclude_patterns.nr) {
		struct string_list_item *item;

		for_each_string_list_item(item, &exclude_patterns) {
162
			if (!wildmatch(item->string, path_to_match, 0))
163 164 165 166
				return 0;
		}
	}

167 168 169 170 171
	/*
	 * If we're given patterns, accept only tags which match at least one
	 * pattern.
	 */
	if (patterns.nr) {
172
		int found = 0;
173 174 175
		struct string_list_item *item;

		for_each_string_list_item(item, &patterns) {
176
			if (!wildmatch(item->string, path_to_match, 0)) {
177
				found = 1;
178
				break;
179 180
			}
		}
181

182
		if (!found)
183 184
			return 0;
	}
185 186

	/* Is it annotated? */
187
	if (!peel_ref(path, &peeled)) {
188
		is_annotated = !!oidcmp(oid, &peeled);
189
	} else {
190
		oidcpy(&peeled, oid);
191
		is_annotated = 0;
192
	}
193

194 195 196 197 198
	/*
	 * By default, we only use annotated tags, but with --tags
	 * we fall back to lightweight ones (even without --tags,
	 * we still remember lightweight ones, only to give hints
	 * in an error message).  --all allows any refs to be used.
199
	 */
200 201 202 203
	if (is_annotated)
		prio = 2;
	else if (is_tag)
		prio = 1;
204 205 206
	else
		prio = 0;

207
	add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
208 209 210
	return 0;
}

211 212
struct possible_tag {
	struct commit_name *name;
213 214
	int depth;
	int found_order;
215
	unsigned flag_within;
216 217
};

218 219 220 221 222 223 224 225 226 227 228
static int compare_pt(const void *a_, const void *b_)
{
	struct possible_tag *a = (struct possible_tag *)a_;
	struct possible_tag *b = (struct possible_tag *)b_;
	if (a->depth != b->depth)
		return a->depth - b->depth;
	if (a->found_order != b->found_order)
		return a->found_order - b->found_order;
	return 0;
}

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
static unsigned long finish_depth_computation(
	struct commit_list **list,
	struct possible_tag *best)
{
	unsigned long seen_commits = 0;
	while (*list) {
		struct commit *c = pop_commit(list);
		struct commit_list *parents = c->parents;
		seen_commits++;
		if (c->object.flags & best->flag_within) {
			struct commit_list *a = *list;
			while (a) {
				struct commit *i = a->item;
				if (!(i->object.flags & best->flag_within))
					break;
				a = a->next;
			}
			if (!a)
				break;
		} else
			best->depth++;
		while (parents) {
			struct commit *p = parents->item;
			parse_commit(p);
			if (!(p->object.flags & SEEN))
254
				commit_list_insert_by_date(p, list);
255 256 257 258 259 260 261
			p->object.flags |= c->object.flags;
			parents = parents->next;
		}
	}
	return seen_commits;
}

262
static void append_name(struct commit_name *n, struct strbuf *dst)
263 264
{
	if (n->prio == 2 && !n->tag) {
265
		n->tag = lookup_tag(&n->oid);
266
		if (!n->tag || parse_tag(n->tag))
267
			die(_("annotated tag %s not available"), n->path);
268 269 270
	}
	if (n->tag && !n->name_checked) {
		if (!n->tag->tag)
271
			die(_("annotated tag %s has no embedded name"), n->path);
272
		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
273
			warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
274
		n->name_checked = 1;
275 276
	}

277 278
	if (n->tag) {
		if (all)
279
			strbuf_addstr(dst, "tags/");
280
		strbuf_addstr(dst, n->tag->tag);
281
	} else {
282
		strbuf_addstr(dst, n->path);
283
	}
284 285
}

286
static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
287
{
288
	strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev));
289 290
}

291
static void describe_commit(struct object_id *oid, struct strbuf *dst)
292
{
293
	struct commit *cmit, *gave_up_on = NULL;
294 295
	struct commit_list *list;
	struct commit_name *n;
296
	struct possible_tag all_matches[MAX_TAGS];
297 298
	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
	unsigned long seen_commits = 0;
299
	unsigned int unannotated_cnt = 0;
300

301
	cmit = lookup_commit_reference(oid);
302

303
	n = find_commit_name(&cmit->object.oid);
304
	if (n && (tags || all || n->prio == 2)) {
305 306 307
		/*
		 * Exact match to an existing ref.
		 */
308
		append_name(n, dst);
309
		if (longformat)
310
			append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
311
		if (suffix)
312
			strbuf_addstr(dst, suffix);
313 314 315
		return;
	}

316
	if (!max_candidates)
317
		die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
318
	if (debug)
319
		fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
320

321
	if (!have_util) {
322 323 324 325
		struct hashmap_iter iter;
		struct commit *c;
		struct commit_name *n = hashmap_iter_first(&names, &iter);
		for (; n; n = hashmap_iter_next(&iter)) {
326
			c = lookup_commit_reference_gently(&n->peeled, 1);
327 328 329
			if (c)
				c->util = n;
		}
330 331 332
		have_util = 1;
	}

333
	list = NULL;
334
	cmit->object.flags = SEEN;
335 336
	commit_list_insert(cmit, &list);
	while (list) {
337
		struct commit *c = pop_commit(&list);
338
		struct commit_list *parents = c->parents;
339
		seen_commits++;
340
		n = c->util;
341
		if (n) {
342 343 344
			if (!tags && !all && n->prio < 2) {
				unannotated_cnt++;
			} else if (match_cnt < max_candidates) {
345 346 347 348
				struct possible_tag *t = &all_matches[match_cnt++];
				t->name = n;
				t->depth = seen_commits - 1;
				t->flag_within = 1u << match_cnt;
349
				t->found_order = match_cnt;
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
				c->object.flags |= t->flag_within;
				if (n->prio == 2)
					annotated_cnt++;
			}
			else {
				gave_up_on = c;
				break;
			}
		}
		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
			struct possible_tag *t = &all_matches[cur_match];
			if (!(c->object.flags & t->flag_within))
				t->depth++;
		}
		if (annotated_cnt && !list) {
			if (debug)
366
				fprintf(stderr, _("finished search at %s\n"),
367
					oid_to_hex(&c->object.oid));
368
			break;
369 370 371 372
		}
		while (parents) {
			struct commit *p = parents->item;
			parse_commit(p);
373
			if (!(p->object.flags & SEEN))
374
				commit_list_insert_by_date(p, &list);
375
			p->object.flags |= c->object.flags;
376
			parents = parents->next;
377 378 379

			if (first_parent)
				break;
380 381 382
		}
	}

383
	if (!match_cnt) {
384
		struct object_id *cmit_oid = &cmit->object.oid;
385
		if (always) {
386
			strbuf_add_unique_abbrev(dst, cmit_oid, abbrev);
387
			if (suffix)
388
				strbuf_addstr(dst, suffix);
389 390
			return;
		}
391
		if (unannotated_cnt)
392 393
			die(_("No annotated tags can describe '%s'.\n"
			    "However, there were unannotated tags: try --tags."),
394
			    oid_to_hex(cmit_oid));
395
		else
396 397
			die(_("No tags can describe '%s'.\n"
			    "Try --always, or create some tags."),
398
			    oid_to_hex(cmit_oid));
399
	}
400

René Scharfe's avatar
René Scharfe committed
401
	QSORT(all_matches, match_cnt, compare_pt);
402 403

	if (gave_up_on) {
404
		commit_list_insert_by_date(gave_up_on, &list);
405 406 407 408 409
		seen_commits--;
	}
	seen_commits += finish_depth_computation(&list, &all_matches[0]);
	free_commit_list(list);

410
	if (debug) {
411 412 413 414 415 416 417 418 419
		static int label_width = -1;
		if (label_width < 0) {
			int i, w;
			for (i = 0; i < ARRAY_SIZE(prio_names); i++) {
				w = strlen(_(prio_names[i]));
				if (label_width < w)
					label_width = w;
			}
		}
420 421
		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
			struct possible_tag *t = &all_matches[cur_match];
422 423
			fprintf(stderr, " %-*s %8d %s\n",
				label_width, _(prio_names[t->name->prio]),
424 425
				t->depth, t->name->path);
		}
426
		fprintf(stderr, _("traversed %lu commits\n"), seen_commits);
427 428
		if (gave_up_on) {
			fprintf(stderr,
429 430
				_("more than %i tags found; listed %i most recent\n"
				"gave up search at %s\n"),
431
				max_candidates, max_candidates,
432
				oid_to_hex(&gave_up_on->object.oid));
433
		}
434
	}
435

436
	append_name(all_matches[0].name, dst);
437
	if (abbrev)
438
		append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
439
	if (suffix)
440 441 442
		strbuf_addstr(dst, suffix);
}

443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
struct process_commit_data {
	struct object_id current_commit;
	struct object_id looking_for;
	struct strbuf *dst;
	struct rev_info *revs;
};

static void process_commit(struct commit *commit, void *data)
{
	struct process_commit_data *pcd = data;
	pcd->current_commit = commit->object.oid;
}

static void process_object(struct object *obj, const char *path, void *data)
{
	struct process_commit_data *pcd = data;

	if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
		reset_revision_walk();
		describe_commit(&pcd->current_commit, pcd->dst);
		strbuf_addf(pcd->dst, ":%s", path);
		free_commit_list(pcd->revs->commits);
		pcd->revs->commits = NULL;
	}
}

static void describe_blob(struct object_id oid, struct strbuf *dst)
{
	struct rev_info revs;
	struct argv_array args = ARGV_ARRAY_INIT;
	struct process_commit_data pcd = { null_oid, oid, dst, &revs};

	argv_array_pushl(&args, "internal: The first arg is not parsed",
		"--objects", "--in-commit-order", "--reverse", "HEAD",
		NULL);

	init_revisions(&revs, NULL);
	if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
		BUG("setup_revisions could not handle all args?");

	if (prepare_revision_walk(&revs))
		die("revision walk setup failed");

	traverse_commit_list(&revs, process_commit, process_object, &pcd);
	reset_revision_walk();
}

490 491 492 493 494 495 496 497 498 499 500
static void describe(const char *arg, int last_one)
{
	struct object_id oid;
	struct commit *cmit;
	struct strbuf sb = STRBUF_INIT;

	if (debug)
		fprintf(stderr, _("describe %s\n"), arg);

	if (get_oid(arg, &oid))
		die(_("Not a valid object name %s"), arg);
501
	cmit = lookup_commit_reference_gently(&oid, 1);
502

503 504
	if (cmit)
		describe_commit(&oid, &sb);
505
	else if (oid_object_info(the_repository, &oid, NULL) == OBJ_BLOB)
506 507 508
		describe_blob(oid, &sb);
	else
		die(_("%s is neither a commit nor blob"), arg);
509 510

	puts(sb.buf);
511

512 513
	if (!last_one)
		clear_commit_marks(cmit, -1);
514 515

	strbuf_release(&sb);
516 517
}

518
int cmd_describe(int argc, const char **argv, const char *prefix)
519
{
520
	int contains = 0;
521
	struct option options[] = {
522 523 524 525 526 527
		OPT_BOOL(0, "contains",   &contains, N_("find the tag that comes after the commit")),
		OPT_BOOL(0, "debug",      &debug, N_("debug search strategy on stderr")),
		OPT_BOOL(0, "all",        &all, N_("use any ref")),
		OPT_BOOL(0, "tags",       &tags, N_("use any tag, even unannotated")),
		OPT_BOOL(0, "long",       &longformat, N_("always use long format")),
		OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
528
		OPT__ABBREV(&abbrev),
529
		OPT_SET_INT(0, "exact-match", &max_candidates,
530
			    N_("only output exact matches"), 0),
531
		OPT_INTEGER(0, "candidates", &max_candidates,
532
			    N_("consider <n> most recent tags (default: 10)")),
533
		OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
534
			   N_("only consider tags matching <pattern>")),
535 536
		OPT_STRING_LIST(0, "exclude", &exclude_patterns, N_("pattern"),
			   N_("do not consider tags matching <pattern>")),
537 538
		OPT_BOOL(0, "always",        &always,
			N_("show abbreviated commit object as fallback")),
539
		{OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
540 541
			N_("append <mark> on dirty working tree (default: \"-dirty\")"),
			PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
542 543 544
		{OPTION_STRING, 0, "broken",  &broken, N_("mark"),
			N_("append <mark> on broken working tree (default: \"-broken\")"),
			PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
545 546
		OPT_END(),
	};
547

548
	git_config(git_default_config, NULL);
549
	argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
550 551 552
	if (abbrev < 0)
		abbrev = DEFAULT_ABBREV;

553 554
	if (max_candidates < 0)
		max_candidates = 0;
555 556
	else if (max_candidates > MAX_TAGS)
		max_candidates = MAX_TAGS;
557

558
	save_commit_buffer = 0;
559

560
	if (longformat && abbrev == 0)
561
		die(_("--long is incompatible with --abbrev=0"));
562

563
	if (contains) {
564
		struct string_list_item *item;
565 566 567
		struct argv_array args;

		argv_array_init(&args);
568 569
		argv_array_pushl(&args, "name-rev",
				 "--peel-tag", "--name-only", "--no-undefined",
570
				 NULL);
571
		if (always)
572
			argv_array_push(&args, "--always");
573
		if (!all) {
574
			argv_array_push(&args, "--tags");
575 576
			for_each_string_list_item(item, &patterns)
				argv_array_pushf(&args, "--refs=refs/tags/%s", item->string);
577 578
			for_each_string_list_item(item, &exclude_patterns)
				argv_array_pushf(&args, "--exclude=refs/tags/%s", item->string);
579
		}
580 581 582 583
		if (argc)
			argv_array_pushv(&args, argv);
		else
			argv_array_push(&args, "HEAD");
584
		return cmd_name_rev(args.argc, args.argv, prefix);
585 586
	}

587
	hashmap_init(&names, commit_name_cmp, NULL, 0);
588
	for_each_rawref(get_name, NULL);
589
	if (!hashmap_get_size(&names) && !always)
590
		die(_("No names found, cannot describe anything."));
591

592
	if (argc == 0) {
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
		if (broken) {
			struct child_process cp = CHILD_PROCESS_INIT;
			argv_array_pushv(&cp.args, diff_index_args);
			cp.git_cmd = 1;
			cp.no_stdin = 1;
			cp.no_stdout = 1;

			if (!dirty)
				dirty = "-dirty";

			switch (run_command(&cp)) {
			case 0:
				suffix = NULL;
				break;
			case 1:
				suffix = dirty;
				break;
			default:
				/* diff-index aborted abnormally */
				suffix = broken;
			}
		} else if (dirty) {
615
			static struct lock_file index_lock;
616 617 618
			struct rev_info revs;
			struct argv_array args = ARGV_ARRAY_INIT;
			int fd, result;
619 620 621 622 623 624 625 626

			read_cache_preload(NULL);
			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
				      NULL, NULL, NULL);
			fd = hold_locked_index(&index_lock, 0);
			if (0 <= fd)
				update_index_if_able(&the_index, &index_lock);

627 628 629 630 631 632 633
			init_revisions(&revs, prefix);
			argv_array_pushv(&args, diff_index_args);
			if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
				BUG("malformed internal diff-index command line");
			result = run_diff_index(&revs, 0);

			if (!diff_result_code(&revs.diffopt, result))
634 635 636
				suffix = NULL;
			else
				suffix = dirty;
637
		}
638
		describe("HEAD", 1);
639
	} else if (dirty) {
640
		die(_("--dirty is incompatible with commit-ishes"));
641 642
	} else if (broken) {
		die(_("--broken is incompatible with commit-ishes"));
643
	} else {
644
		while (argc-- > 0)
645 646
			describe(*argv++, argc == 0);
	}
647 648
	return 0;
}