describe.c 11.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2
#include "cache.h"
#include "commit.h"
3
#include "tag.h"
L
Linus Torvalds 已提交
4
#include "refs.h"
S
Shawn O. Pearce 已提交
5
#include "builtin.h"
6
#include "exec_cmd.h"
7
#include "parse-options.h"
J
Jean Privat 已提交
8
#include "diff.h"
9
#include "hash.h"
L
Linus Torvalds 已提交
10

11 12 13
#define SEEN		(1u<<0)
#define MAX_TAGS	(FLAG_BITS - 1)

14
static const char * const describe_usage[] = {
S
Stephan Beyer 已提交
15
	"git describe [options] <committish>*",
J
Jean Privat 已提交
16
	"git describe [options] --dirty",
17 18
	NULL
};
L
Linus Torvalds 已提交
19

20
static int debug;	/* Display lots of verbose info */
21 22
static int all;	/* Any valid ref can be used */
static int tags;	/* Allow lightweight tags */
23
static int longformat;
J
Junio C Hamano 已提交
24
static int abbrev = DEFAULT_ABBREV;
25
static int max_candidates = 10;
26
static struct hash_table names;
27
static const char *pattern;
28
static int always;
J
Jean Privat 已提交
29 30 31 32 33 34 35
static const char *dirty;

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

L
Linus Torvalds 已提交
36

37
struct commit_name {
38 39
	struct commit_name *next;
	unsigned char peeled[20];
40
	struct tag *tag;
41 42
	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
	unsigned name_checked:1;
43
	unsigned char sha1[20];
44
	const char *path;
45
};
46 47 48
static const char *prio_names[] = {
	"head", "lightweight", "annotated",
};
L
Linus Torvalds 已提交
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
static inline unsigned int hash_sha1(const unsigned char *sha1)
{
	unsigned int hash;
	memcpy(&hash, sha1, sizeof(hash));
	return hash;
}

static inline struct commit_name *find_commit_name(const unsigned char *peeled)
{
	struct commit_name *n = lookup_hash(hash_sha1(peeled), &names);
	while (n && !!hashcmp(peeled, n->peeled))
		n = n->next;
	return n;
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
static int replace_name(struct commit_name *e,
			       int prio,
			       const unsigned char *sha1,
			       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) {
			t = lookup_tag(e->sha1);
			if (!t || parse_tag(t))
				return 1;
			e->tag = t;
		}

		t = lookup_tag(sha1);
		if (!t || parse_tag(t))
			return 0;
		*tag = t;

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

	return 0;
}

98
static void add_to_known_names(const char *path,
99
			       struct commit *commit,
100 101
			       int prio,
			       const unsigned char *sha1)
L
Linus Torvalds 已提交
102
{
103 104
	const unsigned char *peeled = commit->object.sha1;
	struct commit_name *e = find_commit_name(peeled);
105 106
	struct tag *tag = NULL;
	if (replace_name(e, prio, sha1, &tag)) {
107
		if (!e) {
108
			void **pos;
109 110
			e = xmalloc(sizeof(struct commit_name));
			commit->util = e;
111 112 113 114 115 116 117 118
			hashcpy(e->peeled, peeled);
			pos = insert_hash(hash_sha1(peeled), e, &names);
			if (pos) {
				e->next = *pos;
				*pos = e;
			} else {
				e->next = NULL;
			}
119
		}
120
		e->tag = tag;
121
		e->prio = prio;
122
		e->name_checked = 0;
123
		hashcpy(e->sha1, sha1);
124
		e->path = path;
L
Linus Torvalds 已提交
125 126 127
	}
}

128
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
L
Linus Torvalds 已提交
129
{
130
	int might_be_tag = !prefixcmp(path, "refs/tags/");
131
	struct commit *commit;
132
	struct object *object;
133 134 135
	unsigned char peeled[20];
	int is_tag, prio;

136 137 138
	if (!all && !might_be_tag)
		return 0;

139 140 141 142 143 144 145 146 147 148 149 150
	if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
		commit = lookup_commit_reference_gently(peeled, 1);
		if (!commit)
			return 0;
		is_tag = !!hashcmp(sha1, commit->object.sha1);
	} else {
		commit = lookup_commit_reference_gently(sha1, 1);
		object = parse_object(sha1);
		if (!commit || !object)
			return 0;
		is_tag = object->type == OBJ_TAG;
	}
151

J
Junio C Hamano 已提交
152 153 154 155
	/* If --all, then any refs are used.
	 * If --tags, then any tags are used.
	 * Otherwise only annotated tags are used.
	 */
156
	if (might_be_tag) {
157
		if (is_tag)
158
			prio = 2;
159
		else
160
			prio = 1;
161 162 163

		if (pattern && fnmatch(pattern, path + 10, 0))
			prio = 0;
164 165 166 167
	}
	else
		prio = 0;

168
	if (!all) {
169 170
		if (!prio)
			return 0;
171
	}
172
	add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
L
Linus Torvalds 已提交
173 174 175
	return 0;
}

176 177
struct possible_tag {
	struct commit_name *name;
178 179
	int depth;
	int found_order;
180
	unsigned flag_within;
181 182
};

183 184 185 186 187 188 189 190 191 192 193
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;
}

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
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))
				insert_by_date(p, list);
			p->object.flags |= c->object.flags;
			parents = parents->next;
		}
	}
	return seen_commits;
}

227 228 229 230
static void display_name(struct commit_name *n)
{
	if (n->prio == 2 && !n->tag) {
		n->tag = lookup_tag(n->sha1);
231
		if (!n->tag || parse_tag(n->tag))
232
			die("annotated tag %s not available", n->path);
233 234 235 236
	}
	if (n->tag && !n->name_checked) {
		if (!n->tag->tag)
			die("annotated tag %s has no embedded name", n->path);
237
		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
238
			warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
239
		n->name_checked = 1;
240 241 242 243 244 245
	}

	if (n->tag)
		printf("%s", n->tag->tag);
	else
		printf("%s", n->path);
J
Junio C Hamano 已提交
246 247 248 249 250
}

static void show_suffix(int depth, const unsigned char *sha1)
{
	printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
251 252
}

T
Timo Hirvonen 已提交
253
static void describe(const char *arg, int last_one)
L
Linus Torvalds 已提交
254
{
J
Junio C Hamano 已提交
255
	unsigned char sha1[20];
256
	struct commit *cmit, *gave_up_on = NULL;
L
Linus Torvalds 已提交
257 258
	struct commit_list *list;
	struct commit_name *n;
259
	struct possible_tag all_matches[MAX_TAGS];
260 261
	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
	unsigned long seen_commits = 0;
262
	unsigned int unannotated_cnt = 0;
L
Linus Torvalds 已提交
263

264 265
	if (get_sha1(arg, sha1))
		die("Not a valid object name %s", arg);
J
Junio C Hamano 已提交
266 267
	cmit = lookup_commit_reference(sha1);
	if (!cmit)
268
		die("%s is not a valid '%s' object", arg, commit_type);
J
Junio C Hamano 已提交
269

270
	n = find_commit_name(cmit->object.sha1);
271
	if (n && (tags || all || n->prio == 2)) {
J
Junio C Hamano 已提交
272 273 274
		/*
		 * Exact match to an existing ref.
		 */
275
		display_name(n);
J
Junio C Hamano 已提交
276
		if (longformat)
277
			show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
J
Jean Privat 已提交
278 279
		if (dirty)
			printf("%s", dirty);
280
		printf("\n");
L
Linus Torvalds 已提交
281 282 283
		return;
	}

284 285
	if (!max_candidates)
		die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
286 287 288
	if (debug)
		fprintf(stderr, "searching to describe %s\n", arg);

L
Linus Torvalds 已提交
289
	list = NULL;
290
	cmit->object.flags = SEEN;
L
Linus Torvalds 已提交
291 292
	commit_list_insert(cmit, &list);
	while (list) {
293
		struct commit *c = pop_commit(&list);
294
		struct commit_list *parents = c->parents;
295
		seen_commits++;
296
		n = c->util;
L
Linus Torvalds 已提交
297
		if (n) {
298 299 300
			if (!tags && !all && n->prio < 2) {
				unannotated_cnt++;
			} else if (match_cnt < max_candidates) {
301 302 303 304
				struct possible_tag *t = &all_matches[match_cnt++];
				t->name = n;
				t->depth = seen_commits - 1;
				t->flag_within = 1u << match_cnt;
305
				t->found_order = match_cnt;
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
				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)
				fprintf(stderr, "finished search at %s\n",
					sha1_to_hex(c->object.sha1));
			break;
325 326 327 328
		}
		while (parents) {
			struct commit *p = parents->item;
			parse_commit(p);
329
			if (!(p->object.flags & SEEN))
330
				insert_by_date(p, &list);
331
			p->object.flags |= c->object.flags;
332
			parents = parents->next;
333 334 335
		}
	}

336 337 338
	if (!match_cnt) {
		const unsigned char *sha1 = cmit->object.sha1;
		if (always) {
J
Jean Privat 已提交
339 340 341 342
			printf("%s", find_unique_abbrev(sha1, abbrev));
			if (dirty)
				printf("%s", dirty);
			printf("\n");
343 344
			return;
		}
345 346 347 348 349 350 351 352
		if (unannotated_cnt)
			die("No annotated tags can describe '%s'.\n"
			    "However, there were unannotated tags: try --tags.",
			    sha1_to_hex(sha1));
		else
			die("No tags can describe '%s'.\n"
			    "Try --always, or create some tags.",
			    sha1_to_hex(sha1));
353
	}
354

355
	qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
356 357 358 359 360 361 362 363

	if (gave_up_on) {
		insert_by_date(gave_up_on, &list);
		seen_commits--;
	}
	seen_commits += finish_depth_computation(&list, &all_matches[0]);
	free_commit_list(list);

364 365 366
	if (debug) {
		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
			struct possible_tag *t = &all_matches[cur_match];
367 368
			fprintf(stderr, " %-11s %8d %s\n",
				prio_names[t->name->prio],
369 370 371 372 373 374 375 376 377 378
				t->depth, t->name->path);
		}
		fprintf(stderr, "traversed %lu commits\n", seen_commits);
		if (gave_up_on) {
			fprintf(stderr,
				"more than %i tags found; listed %i most recent\n"
				"gave up search at %s\n",
				max_candidates, max_candidates,
				sha1_to_hex(gave_up_on->object.sha1));
		}
379
	}
380 381 382

	display_name(all_matches[0].name);
	if (abbrev)
J
Junio C Hamano 已提交
383
		show_suffix(all_matches[0].depth, cmit->object.sha1);
J
Jean Privat 已提交
384 385
	if (dirty)
		printf("%s", dirty);
386
	printf("\n");
387

388 389
	if (!last_one)
		clear_commit_marks(cmit, -1);
L
Linus Torvalds 已提交
390 391
}

S
Shawn O. Pearce 已提交
392
int cmd_describe(int argc, const char **argv, const char *prefix)
L
Linus Torvalds 已提交
393
{
394
	int contains = 0;
395 396 397 398 399
	struct option options[] = {
		OPT_BOOLEAN(0, "contains",   &contains, "find the tag that comes after the commit"),
		OPT_BOOLEAN(0, "debug",      &debug, "debug search strategy on stderr"),
		OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
		OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
400
		OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
401
		OPT__ABBREV(&abbrev),
402 403
		OPT_SET_INT(0, "exact-match", &max_candidates,
			    "only output exact matches", 0),
404
		OPT_INTEGER(0, "candidates", &max_candidates,
405 406 407
			    "consider <n> most recent tags (default: 10)"),
		OPT_STRING(0, "match",       &pattern, "pattern",
			   "only consider tags matching <pattern>"),
408 409
		OPT_BOOLEAN(0, "always",     &always,
			   "show abbreviated commit object as fallback"),
J
Jean Privat 已提交
410 411 412
		{OPTION_STRING, 0, "dirty",  &dirty, "mark",
			   "append <mark> on dirty working tree (default: \"-dirty\")",
		 PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
413 414
		OPT_END(),
	};
L
Linus Torvalds 已提交
415

416
	argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
417 418
	if (max_candidates < 0)
		max_candidates = 0;
419 420
	else if (max_candidates > MAX_TAGS)
		max_candidates = MAX_TAGS;
J
Junio C Hamano 已提交
421

422
	save_commit_buffer = 0;
423

424 425 426
	if (longformat && abbrev == 0)
		die("--long is incompatible with --abbrev=0");

427
	if (contains) {
428
		const char **args = xmalloc((7 + argc) * sizeof(char *));
429 430 431
		int i = 0;
		args[i++] = "name-rev";
		args[i++] = "--name-only";
432
		args[i++] = "--no-undefined";
433 434
		if (always)
			args[i++] = "--always";
435
		if (!all) {
436
			args[i++] = "--tags";
437 438 439 440 441 442
			if (pattern) {
				char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
				sprintf(s, "--refs=refs/tags/%s", pattern);
				args[i++] = s;
			}
		}
443
		memcpy(args + i, argv, argc * sizeof(char *));
444 445
		args[i + argc] = NULL;
		return cmd_name_rev(i + argc, args, prefix);
446 447
	}

448
	init_hash(&names);
A
Anders Kaseorg 已提交
449
	for_each_rawref(get_name, NULL);
450
	if (!names.nr && !always)
451 452
		die("No names found, cannot describe anything.");

453
	if (argc == 0) {
J
Jean Privat 已提交
454 455
		if (dirty && !cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix))
			dirty = NULL;
J
Junio C Hamano 已提交
456
		describe("HEAD", 1);
J
Jean Privat 已提交
457 458
	} else if (dirty) {
		die("--dirty is incompatible with committishes");
459 460 461
	} else {
		while (argc-- > 0) {
			describe(*argv++, argc == 0);
J
Junio C Hamano 已提交
462
		}
463
	}
L
Linus Torvalds 已提交
464 465
	return 0;
}