describe.c 10.7 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"
L
Linus Torvalds 已提交
9

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

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

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

36
struct commit_name {
37
	struct tag *tag;
38 39
	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
	unsigned name_checked:1;
40
	unsigned char sha1[20];
J
Junio C Hamano 已提交
41
	char path[FLEX_ARRAY]; /* more */
42
};
43 44 45
static const char *prio_names[] = {
	"head", "lightweight", "annotated",
};
L
Linus Torvalds 已提交
46

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
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;
}

80
static void add_to_known_names(const char *path,
81
			       struct commit *commit,
82 83
			       int prio,
			       const unsigned char *sha1)
L
Linus Torvalds 已提交
84
{
85
	struct commit_name *e = commit->util;
86 87
	struct tag *tag = NULL;
	if (replace_name(e, prio, sha1, &tag)) {
88 89 90
		size_t len = strlen(path)+1;
		free(e);
		e = xmalloc(sizeof(struct commit_name) + len);
91
		e->tag = tag;
92
		e->prio = prio;
93
		e->name_checked = 0;
94
		hashcpy(e->sha1, sha1);
95 96
		memcpy(e->path, path, len);
		commit->util = e;
L
Linus Torvalds 已提交
97
	}
98
	found_names = 1;
L
Linus Torvalds 已提交
99 100
}

101
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
L
Linus Torvalds 已提交
102
{
103
	int might_be_tag = !prefixcmp(path, "refs/tags/");
104
	struct commit *commit;
105
	struct object *object;
106 107 108
	unsigned char peeled[20];
	int is_tag, prio;

109 110 111
	if (!all && !might_be_tag)
		return 0;

112 113 114 115 116 117 118 119 120 121 122 123
	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;
	}
124

J
Junio C Hamano 已提交
125 126 127 128
	/* If --all, then any refs are used.
	 * If --tags, then any tags are used.
	 * Otherwise only annotated tags are used.
	 */
129
	if (might_be_tag) {
130
		if (is_tag)
131
			prio = 2;
132
		else
133
			prio = 1;
134 135 136

		if (pattern && fnmatch(pattern, path + 10, 0))
			prio = 0;
137 138 139 140
	}
	else
		prio = 0;

141
	if (!all) {
142 143
		if (!prio)
			return 0;
144
	}
145
	add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
L
Linus Torvalds 已提交
146 147 148
	return 0;
}

149 150
struct possible_tag {
	struct commit_name *name;
151 152
	int depth;
	int found_order;
153
	unsigned flag_within;
154 155
};

156 157 158 159 160 161 162 163 164 165 166
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;
}

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
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;
}

200 201 202 203
static void display_name(struct commit_name *n)
{
	if (n->prio == 2 && !n->tag) {
		n->tag = lookup_tag(n->sha1);
204
		if (!n->tag || parse_tag(n->tag))
205
			die("annotated tag %s not available", n->path);
206 207 208 209
	}
	if (n->tag && !n->name_checked) {
		if (!n->tag->tag)
			die("annotated tag %s has no embedded name", n->path);
210
		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
211
			warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
212
		n->name_checked = 1;
213 214 215 216 217 218
	}

	if (n->tag)
		printf("%s", n->tag->tag);
	else
		printf("%s", n->path);
J
Junio C Hamano 已提交
219 220 221 222 223
}

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

T
Timo Hirvonen 已提交
226
static void describe(const char *arg, int last_one)
L
Linus Torvalds 已提交
227
{
J
Junio C Hamano 已提交
228
	unsigned char sha1[20];
229
	struct commit *cmit, *gave_up_on = NULL;
L
Linus Torvalds 已提交
230 231
	struct commit_list *list;
	struct commit_name *n;
232
	struct possible_tag all_matches[MAX_TAGS];
233 234
	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
	unsigned long seen_commits = 0;
235
	unsigned int unannotated_cnt = 0;
L
Linus Torvalds 已提交
236

237 238
	if (get_sha1(arg, sha1))
		die("Not a valid object name %s", arg);
J
Junio C Hamano 已提交
239 240
	cmit = lookup_commit_reference(sha1);
	if (!cmit)
241
		die("%s is not a valid '%s' object", arg, commit_type);
J
Junio C Hamano 已提交
242

243
	n = cmit->util;
244
	if (n && (tags || all || n->prio == 2)) {
J
Junio C Hamano 已提交
245 246 247
		/*
		 * Exact match to an existing ref.
		 */
248
		display_name(n);
J
Junio C Hamano 已提交
249
		if (longformat)
250
			show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
J
Jean Privat 已提交
251 252
		if (dirty)
			printf("%s", dirty);
253
		printf("\n");
L
Linus Torvalds 已提交
254 255 256
		return;
	}

257 258
	if (!max_candidates)
		die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
259 260 261
	if (debug)
		fprintf(stderr, "searching to describe %s\n", arg);

L
Linus Torvalds 已提交
262
	list = NULL;
263
	cmit->object.flags = SEEN;
L
Linus Torvalds 已提交
264 265
	commit_list_insert(cmit, &list);
	while (list) {
266
		struct commit *c = pop_commit(&list);
267
		struct commit_list *parents = c->parents;
268
		seen_commits++;
269
		n = c->util;
L
Linus Torvalds 已提交
270
		if (n) {
271 272 273
			if (!tags && !all && n->prio < 2) {
				unannotated_cnt++;
			} else if (match_cnt < max_candidates) {
274 275 276 277
				struct possible_tag *t = &all_matches[match_cnt++];
				t->name = n;
				t->depth = seen_commits - 1;
				t->flag_within = 1u << match_cnt;
278
				t->found_order = match_cnt;
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
				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;
298 299 300 301
		}
		while (parents) {
			struct commit *p = parents->item;
			parse_commit(p);
302
			if (!(p->object.flags & SEEN))
303
				insert_by_date(p, &list);
304
			p->object.flags |= c->object.flags;
305
			parents = parents->next;
306 307 308
		}
	}

309 310 311
	if (!match_cnt) {
		const unsigned char *sha1 = cmit->object.sha1;
		if (always) {
J
Jean Privat 已提交
312 313 314 315
			printf("%s", find_unique_abbrev(sha1, abbrev));
			if (dirty)
				printf("%s", dirty);
			printf("\n");
316 317
			return;
		}
318 319 320 321 322 323 324 325
		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));
326
	}
327

328
	qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
329 330 331 332 333 334 335 336

	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);

337 338 339
	if (debug) {
		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
			struct possible_tag *t = &all_matches[cur_match];
340 341
			fprintf(stderr, " %-11s %8d %s\n",
				prio_names[t->name->prio],
342 343 344 345 346 347 348 349 350 351
				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));
		}
352
	}
353 354 355

	display_name(all_matches[0].name);
	if (abbrev)
J
Junio C Hamano 已提交
356
		show_suffix(all_matches[0].depth, cmit->object.sha1);
J
Jean Privat 已提交
357 358
	if (dirty)
		printf("%s", dirty);
359
	printf("\n");
360

361 362
	if (!last_one)
		clear_commit_marks(cmit, -1);
L
Linus Torvalds 已提交
363 364
}

S
Shawn O. Pearce 已提交
365
int cmd_describe(int argc, const char **argv, const char *prefix)
L
Linus Torvalds 已提交
366
{
367
	int contains = 0;
368 369 370 371 372
	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"),
373
		OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
374
		OPT__ABBREV(&abbrev),
375 376
		OPT_SET_INT(0, "exact-match", &max_candidates,
			    "only output exact matches", 0),
377
		OPT_INTEGER(0, "candidates", &max_candidates,
378 379 380
			    "consider <n> most recent tags (default: 10)"),
		OPT_STRING(0, "match",       &pattern, "pattern",
			   "only consider tags matching <pattern>"),
381 382
		OPT_BOOLEAN(0, "always",     &always,
			   "show abbreviated commit object as fallback"),
J
Jean Privat 已提交
383 384 385
		{OPTION_STRING, 0, "dirty",  &dirty, "mark",
			   "append <mark> on dirty working tree (default: \"-dirty\")",
		 PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
386 387
		OPT_END(),
	};
L
Linus Torvalds 已提交
388

389
	argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
390 391
	if (max_candidates < 0)
		max_candidates = 0;
392 393
	else if (max_candidates > MAX_TAGS)
		max_candidates = MAX_TAGS;
J
Junio C Hamano 已提交
394

395
	save_commit_buffer = 0;
396

397 398 399
	if (longformat && abbrev == 0)
		die("--long is incompatible with --abbrev=0");

400
	if (contains) {
401
		const char **args = xmalloc((7 + argc) * sizeof(char *));
402 403 404
		int i = 0;
		args[i++] = "name-rev";
		args[i++] = "--name-only";
405
		args[i++] = "--no-undefined";
406 407
		if (always)
			args[i++] = "--always";
408
		if (!all) {
409
			args[i++] = "--tags";
410 411 412 413 414 415
			if (pattern) {
				char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
				sprintf(s, "--refs=refs/tags/%s", pattern);
				args[i++] = s;
			}
		}
416
		memcpy(args + i, argv, argc * sizeof(char *));
417 418
		args[i + argc] = NULL;
		return cmd_name_rev(i + argc, args, prefix);
419 420
	}

421
	for_each_ref(get_name, NULL);
J
Junio C Hamano 已提交
422
	if (!found_names && !always)
423 424
		die("No names found, cannot describe anything.");

425
	if (argc == 0) {
J
Jean Privat 已提交
426 427
		if (dirty && !cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix))
			dirty = NULL;
J
Junio C Hamano 已提交
428
		describe("HEAD", 1);
J
Jean Privat 已提交
429 430
	} else if (dirty) {
		die("--dirty is incompatible with committishes");
431 432 433
	} else {
		while (argc-- > 0) {
			describe(*argv++, argc == 0);
J
Junio C Hamano 已提交
434
		}
435
	}
L
Linus Torvalds 已提交
436 437
	return 0;
}