show-branch.c 14.8 KB
Newer Older
J
Junio C Hamano 已提交
1
#include <stdlib.h>
2
#include <fnmatch.h>
J
Junio C Hamano 已提交
3 4 5 6 7
#include "cache.h"
#include "commit.h"
#include "refs.h"

static const char show_branch_usage[] =
8
"git-show-branch [--all] [--heads] [--tags] [--more=count | --list | --independent | --merge-base ] [<refs>...]";
J
Junio C Hamano 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

#define UNINTERESTING	01

#define REV_SHIFT	 2
#define MAX_REVS	29 /* should not exceed bits_per_int - REV_SHIFT */

static struct commit *interesting(struct commit_list *list)
{
	while (list) {
		struct commit *commit = list->item;
		list = list->next;
		if (commit->object.flags & UNINTERESTING)
			continue;
		return commit;
	}
	return NULL;
}

static struct commit *pop_one_commit(struct commit_list **list_p)
{
	struct commit *commit;
	struct commit_list *list;
	list = *list_p;
	commit = list->item;
	*list_p = list->next;
	free(list);
	return commit;
}

struct commit_name {
J
Junio C Hamano 已提交
39 40
	const char *head_name; /* which head's ancestor? */
	int generation; /* how many parents away from head_name */
J
Junio C Hamano 已提交
41 42
};

J
Junio C Hamano 已提交
43
/* Name the commit as nth generation ancestor of head_name;
J
Junio C Hamano 已提交
44 45
 * we count only the first-parent relationship for naming purposes.
 */
J
Junio C Hamano 已提交
46
static void name_commit(struct commit *commit, const char *head_name, int nth)
J
Junio C Hamano 已提交
47 48 49 50 51
{
	struct commit_name *name;
	if (!commit->object.util)
		commit->object.util = xmalloc(sizeof(struct commit_name));
	name = commit->object.util;
J
Junio C Hamano 已提交
52
	name->head_name = head_name;
J
Junio C Hamano 已提交
53 54 55 56
	name->generation = nth;
}

/* Parent is the first parent of the commit.  We may name it
J
Junio C Hamano 已提交
57
 * as (n+1)th generation ancestor of the same head_name as
J
Junio C Hamano 已提交
58 59 60 61 62 63 64 65 66 67 68
 * commit is nth generation ancestore of, if that generation
 * number is better than the name it already has.
 */
static void name_parent(struct commit *commit, struct commit *parent)
{
	struct commit_name *commit_name = commit->object.util;
	struct commit_name *parent_name = parent->object.util;
	if (!commit_name)
		return;
	if (!parent_name ||
	    commit_name->generation + 1 < parent_name->generation)
J
Junio C Hamano 已提交
69
		name_commit(parent, commit_name->head_name,
J
Junio C Hamano 已提交
70 71 72
			    commit_name->generation + 1);
}

J
Junio C Hamano 已提交
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static int name_first_parent_chain(struct commit *c)
{
	int i = 0;
	while (c) {
		struct commit *p;
		if (!c->object.util)
			break;
		if (!c->parents)
			break;
		p = c->parents->item;
		if (!p->object.util) {
			name_parent(c, p);
			i++;
		}
		c = p;
	}
	return i;
}

static void name_commits(struct commit_list *list,
			 struct commit **rev,
			 char **ref_name,
			 int num_rev)
{
	struct commit_list *cl;
	struct commit *c;
	int i;

	/* First give names to the given heads */
	for (cl = list; cl; cl = cl->next) {
		c = cl->item;
		if (c->object.util)
			continue;
		for (i = 0; i < num_rev; i++) {
			if (rev[i] == c) {
				name_commit(c, ref_name[i], 0);
				break;
			}
		}
	}

	/* Then commits on the first parent ancestry chain */
	do {
		i = 0;
		for (cl = list; cl; cl = cl->next) {
			i += name_first_parent_chain(cl->item);
		}
	} while (i);

	/* Finally, any unnamed commits */
	do {
		i = 0;
		for (cl = list; cl; cl = cl->next) {
			struct commit_list *parents;
			struct commit_name *n;
			int nth;
			c = cl->item;
			if (!c->object.util)
				continue;
			n = c->object.util;
			parents = c->parents;
			nth = 0;
			while (parents) {
				struct commit *p = parents->item;
137
				char newname[1000], *en;
J
Junio C Hamano 已提交
138 139 140 141
				parents = parents->next;
				nth++;
				if (p->object.util)
					continue;
142
				en = newname;
143 144
				switch (n->generation) {
				case 0:
145
					en += sprintf(en, "%s", n->head_name);
146 147
					break;
				case 1:
148
					en += sprintf(en, "%s^", n->head_name);
149 150
					break;
				default:
151 152 153
					en += sprintf(en, "%s~%d",
						n->head_name, n->generation);
					break;
154
				}
155 156 157 158
				if (nth == 1)
					en += sprintf(en, "^");
				else
					en += sprintf(en, "^%d", nth);
J
Junio C Hamano 已提交
159 160 161 162 163 164 165 166
				name_commit(p, strdup(newname), 0);
				i++;
				name_first_parent_chain(p);
			}
		}
	} while (i);
}

J
Junio C Hamano 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
static int mark_seen(struct commit *commit, struct commit_list **seen_p)
{
	if (!commit->object.flags) {
		insert_by_date(commit, seen_p);
		return 1;
	}
	return 0;
}

static void join_revs(struct commit_list **list_p,
		      struct commit_list **seen_p,
		      int num_rev, int extra)
{
	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);

	while (*list_p) {
		struct commit_list *parents;
185
		int still_interesting = !!interesting(*list_p);
J
Junio C Hamano 已提交
186 187 188
		struct commit *commit = pop_one_commit(list_p);
		int flags = commit->object.flags & all_mask;

189
		if (!still_interesting && extra <= 0)
J
Junio C Hamano 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202
			break;

		mark_seen(commit, seen_p);
		if ((flags & all_revs) == all_revs)
			flags |= UNINTERESTING;
		parents = commit->parents;

		while (parents) {
			struct commit *p = parents->item;
			int this_flag = p->object.flags;
			parents = parents->next;
			if ((this_flag & flags) == flags)
				continue;
203 204
			if (!p->object.parsed)
				parse_commit(p);
J
Junio C Hamano 已提交
205 206 207 208 209 210
			if (mark_seen(p, seen_p) && !still_interesting)
				extra--;
			p->object.flags |= flags;
			insert_by_date(p, list_p);
		}
	}
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

	/*
	 * Postprocess to complete well-poisoning.
	 *
	 * At this point we have all the commits we have seen in
	 * seen_p list (which happens to be sorted chronologically but
	 * it does not really matter).  Mark anything that can be
	 * reached from uninteresting commits not interesting.
	 */
	for (;;) {
		int changed = 0;
		struct commit_list *s;
		for (s = *seen_p; s; s = s->next) {
			struct commit *c = s->item;
			struct commit_list *parents;

			if (((c->object.flags & all_revs) != all_revs) &&
			    !(c->object.flags & UNINTERESTING))
				continue;

			/* The current commit is either a merge base or
			 * already uninteresting one.  Mark its parents
			 * as uninteresting commits _only_ if they are
			 * already parsed.  No reason to find new ones
			 * here.
			 */
			parents = c->parents;
			while (parents) {
				struct commit *p = parents->item;
				parents = parents->next;
				if (!(p->object.flags & UNINTERESTING)) {
					p->object.flags |= UNINTERESTING;
					changed = 1;
				}
			}
		}
		if (!changed)
			break;
	}
J
Junio C Hamano 已提交
250 251
}

252
static void show_one_commit(struct commit *commit, int no_name)
J
Junio C Hamano 已提交
253
{
254
	char pretty[256], *cp;
J
Junio C Hamano 已提交
255
	struct commit_name *name = commit->object.util;
256 257 258 259 260
	if (commit->object.parsed)
		pretty_print_commit(CMIT_FMT_ONELINE, commit->buffer, ~0,
				    pretty, sizeof(pretty));
	else
		strcpy(pretty, "(unavailable)");
J
Junio C Hamano 已提交
261 262 263 264
	if (!strncmp(pretty, "[PATCH] ", 8))
		cp = pretty + 8;
	else
		cp = pretty;
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

	if (!no_name) {
		if (name && name->head_name) {
			printf("[%s", name->head_name);
			if (name->generation) {
				if (name->generation == 1)
					printf("^");
				else
					printf("~%d", name->generation);
			}
			printf("] ");
		}
		else
			printf("[%s] ",
			       find_unique_abbrev(commit->object.sha1, 7));
J
Junio C Hamano 已提交
280 281 282 283 284 285 286
	}
	puts(cp);
}

static char *ref_name[MAX_REVS + 1];
static int ref_name_cnt;

287 288 289 290 291 292 293 294 295 296 297 298
static int compare_ref_name(const void *a_, const void *b_)
{
	const char * const*a = a_, * const*b = b_;
	return strcmp(*a, *b);
}

static void sort_ref_range(int bottom, int top)
{
	qsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),
	      compare_ref_name);
}

J
Junio C Hamano 已提交
299 300 301 302 303
static int append_ref(const char *refname, const unsigned char *sha1)
{
	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
	if (!commit)
		return 0;
304
	if (MAX_REVS <= ref_name_cnt) {
J
Junio C Hamano 已提交
305 306 307 308 309 310 311 312 313 314 315 316
		fprintf(stderr, "warning: ignoring %s; "
			"cannot handle more than %d refs",
			refname, MAX_REVS);
		return 0;
	}
	ref_name[ref_name_cnt++] = strdup(refname);
	ref_name[ref_name_cnt] = NULL;
	return 0;
}

static int append_head_ref(const char *refname, const unsigned char *sha1)
{
317 318 319
	unsigned char tmp[20];
	int ofs = 11;
	if (strncmp(refname, "refs/heads/", ofs))
J
Junio C Hamano 已提交
320
		return 0;
321 322 323 324 325 326
	/* If both heads/foo and tags/foo exists, get_sha1 would
	 * get confused.
	 */
	if (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))
		ofs = 5;
	return append_ref(refname + ofs, sha1);
J
Junio C Hamano 已提交
327 328 329 330 331 332 333 334 335
}

static int append_tag_ref(const char *refname, const unsigned char *sha1)
{
	if (strncmp(refname, "refs/tags/", 10))
		return 0;
	return append_ref(refname + 5, sha1);
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
static int count_slash(const char *s)
{
	int cnt = 0;
	while (*s)
		if (*s++ == '/')
			cnt++;
	return cnt;
}

static int append_matching_ref(const char *refname, const unsigned char *sha1)
{
	/* we want to allow pattern hold/<asterisk> to show all
	 * branches under refs/heads/hold/, and v0.99.9? to show
	 * refs/tags/v0.99.9a and friends.
	 */
	const char *tail;
	int slash = count_slash(refname);
	for (tail = refname; *tail && match_ref_slash < slash; )
		if (*tail++ == '/')
			slash--;
	if (!*tail)
		return 0;
	if (fnmatch(match_ref_pattern, tail, 0))
		return 0;
	if (!strncmp("refs/heads/", refname, 11))
		return append_head_ref(refname, sha1);
	if (!strncmp("refs/tags/", refname, 10))
		return append_tag_ref(refname, sha1);
	return append_ref(refname, sha1);
}

J
Junio C Hamano 已提交
369 370
static void snarf_refs(int head, int tag)
{
371 372
	if (head) {
		int orig_cnt = ref_name_cnt;
J
Junio C Hamano 已提交
373
		for_each_ref(append_head_ref);
374 375 376 377
		sort_ref_range(orig_cnt, ref_name_cnt);
	}
	if (tag) {
		int orig_cnt = ref_name_cnt;
J
Junio C Hamano 已提交
378
		for_each_ref(append_tag_ref);
379 380
		sort_ref_range(orig_cnt, ref_name_cnt);
	}
J
Junio C Hamano 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
}

static int rev_is_head(char *head_path, int headlen,
		       char *name,
		       unsigned char *head_sha1, unsigned char *sha1)
{
	int namelen;
	if ((!head_path[0]) || memcmp(head_sha1, sha1, 20))
		return 0;
	namelen = strlen(name);
	if ((headlen < namelen) ||
	    memcmp(head_path + headlen - namelen, name, namelen))
		return 0;
	if (headlen == namelen ||
	    head_path[headlen - namelen - 1] == '/')
		return 1;
	return 0;
}

static int show_merge_base(struct commit_list *seen, int num_rev)
{
	int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
	int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
404
	int exit_status = 1;
J
Junio C Hamano 已提交
405 406 407 408 409 410 411

	while (seen) {
		struct commit *commit = pop_one_commit(&seen);
		int flags = commit->object.flags & all_mask;
		if (!(flags & UNINTERESTING) &&
		    ((flags & all_revs) == all_revs)) {
			puts(sha1_to_hex(commit->object.sha1));
412 413
			exit_status = 0;
			commit->object.flags |= UNINTERESTING;
J
Junio C Hamano 已提交
414 415
		}
	}
416
	return exit_status;
J
Junio C Hamano 已提交
417 418
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
static int show_independent(struct commit **rev,
			    int num_rev,
			    char **ref_name,
			    unsigned int *rev_mask)
{
	int i;

	for (i = 0; i < num_rev; i++) {
		struct commit *commit = rev[i];
		unsigned int flag = rev_mask[i];

		if (commit->object.flags == flag)
			puts(sha1_to_hex(commit->object.sha1));
		commit->object.flags |= UNINTERESTING;
	}
	return 0;
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
static void append_one_rev(const char *av)
{
	unsigned char revkey[20];
	if (!get_sha1(av, revkey)) {
		append_ref(av, revkey);
		return;
	}
	if (strchr(av, '*') || strchr(av, '?')) {
		/* glob style match */
		int saved_matches = ref_name_cnt;
		match_ref_pattern = av;
		match_ref_slash = count_slash(av);
		for_each_ref(append_matching_ref);
		if (saved_matches == ref_name_cnt &&
		    ref_name_cnt < MAX_REVS)
			error("no matching refs with %s", av);
		return;
	}
	die("bad sha1 reference %s", av);
}

J
Junio C Hamano 已提交
458 459 460 461
int main(int ac, char **av)
{
	struct commit *rev[MAX_REVS], *commit;
	struct commit_list *list = NULL, *seen = NULL;
462
	unsigned int rev_mask[MAX_REVS];
J
Junio C Hamano 已提交
463 464
	int num_rev, i, extra = 0;
	int all_heads = 0, all_tags = 0;
465
	int all_mask, all_revs;
J
Junio C Hamano 已提交
466
	char head_path[128];
J
Junio C Hamano 已提交
467
	const char *head_path_p;
J
Junio C Hamano 已提交
468 469 470
	int head_path_len;
	unsigned char head_sha1[20];
	int merge_base = 0;
471
	int independent = 0;
472 473
	int no_name = 0;
	int sha1_name = 0;
474 475
	int shown_merge_point = 0;
	int topo_order = 0;
J
Junio C Hamano 已提交
476

477 478
	setup_git_directory();

J
Junio C Hamano 已提交
479 480 481 482 483 484 485 486 487 488
	while (1 < ac && av[1][0] == '-') {
		char *arg = av[1];
		if (!strcmp(arg, "--all"))
			all_heads = all_tags = 1;
		else if (!strcmp(arg, "--heads"))
			all_heads = 1;
		else if (!strcmp(arg, "--tags"))
			all_tags = 1;
		else if (!strcmp(arg, "--more"))
			extra = 1;
489 490
		else if (!strcmp(arg, "--list"))
			extra = -1;
491 492 493 494
		else if (!strcmp(arg, "--no-name"))
			no_name = 1;
		else if (!strcmp(arg, "--sha1-name"))
			sha1_name = 1;
495
		else if (!strncmp(arg, "--more=", 7))
J
Junio C Hamano 已提交
496 497 498
			extra = atoi(arg + 7);
		else if (!strcmp(arg, "--merge-base"))
			merge_base = 1;
499 500
		else if (!strcmp(arg, "--independent"))
			independent = 1;
501 502
		else if (!strcmp(arg, "--topo-order"))
			topo_order = 1;
J
Junio C Hamano 已提交
503 504 505 506 507 508
		else
			usage(show_branch_usage);
		ac--; av++;
	}
	ac--; av++;

509 510 511 512
	/* Only one of these is allowed */
	if (1 < independent + merge_base + (extra != 0))
		usage(show_branch_usage);

J
Junio C Hamano 已提交
513 514 515
	if (all_heads + all_tags)
		snarf_refs(all_heads, all_tags);

516 517 518 519 520
	if (ac) {
		while (0 < ac) {
			append_one_rev(*av);
			ac--; av++;
		}
J
Junio C Hamano 已提交
521
	}
522 523
	else {
		/* If no revs given, then add heads */
J
Junio C Hamano 已提交
524
		snarf_refs(1, 0);
525 526 527 528 529
	}
	if (!ref_name_cnt) {
		fprintf(stderr, "No revs to be shown.\n");
		exit(0);
	}
J
Junio C Hamano 已提交
530 531 532

	for (num_rev = 0; ref_name[num_rev]; num_rev++) {
		unsigned char revkey[20];
533
		unsigned int flag = 1u << (num_rev + REV_SHIFT);
J
Junio C Hamano 已提交
534 535 536 537

		if (MAX_REVS <= num_rev)
			die("cannot handle more than %d revs.", MAX_REVS);
		if (get_sha1(ref_name[num_rev], revkey))
538
			die("'%s' is not a valid ref.\n", ref_name[num_rev]);
J
Junio C Hamano 已提交
539 540 541 542 543 544 545 546 547 548 549
		commit = lookup_commit_reference(revkey);
		if (!commit)
			die("cannot find commit %s (%s)",
			    ref_name[num_rev], revkey);
		parse_commit(commit);
		mark_seen(commit, &seen);

		/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
		 * and so on.  REV_SHIFT bits from bit 0 are used for
		 * internal bookkeeping.
		 */
550 551 552
		commit->object.flags |= flag;
		if (commit->object.flags == flag)
			insert_by_date(commit, &list);
J
Junio C Hamano 已提交
553 554
		rev[num_rev] = commit;
	}
555 556 557 558 559
	for (i = 0; i < num_rev; i++)
		rev_mask[i] = rev[i]->object.flags;

	if (0 <= extra)
		join_revs(&list, &seen, num_rev, extra);
J
Junio C Hamano 已提交
560

J
Junio C Hamano 已提交
561 562 563 564 565 566 567
	head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
	if (head_path_p) {
		head_path_len = strlen(head_path_p);
		memcpy(head_path, head_path_p, head_path_len + 1);
	}
	else {
		head_path_len = 0;
J
Junio C Hamano 已提交
568
		head_path[0] = 0;
J
Junio C Hamano 已提交
569
	}
J
Junio C Hamano 已提交
570 571 572 573

	if (merge_base)
		return show_merge_base(seen, num_rev);

574 575 576 577
	if (independent)
		return show_independent(rev, num_rev, ref_name, rev_mask);

	/* Show list; --more=-1 means list-only */
578
	if (1 < num_rev || extra < 0) {
J
Junio C Hamano 已提交
579 580 581 582 583 584 585
		for (i = 0; i < num_rev; i++) {
			int j;
			int is_head = rev_is_head(head_path,
						  head_path_len,
						  ref_name[i],
						  head_sha1,
						  rev[i]->object.sha1);
586 587 588 589 590 591 592 593 594
			if (extra < 0)
				printf("%c [%s] ",
				       is_head ? '*' : ' ', ref_name[i]);
			else {
				for (j = 0; j < i; j++)
					putchar(' ');
				printf("%c [%s] ",
				       is_head ? '*' : '!', ref_name[i]);
			}
595 596
			/* header lines never need name */
			show_one_commit(rev[i], 1);
J
Junio C Hamano 已提交
597
		}
598 599 600 601 602
		if (0 <= extra) {
			for (i = 0; i < num_rev; i++)
				putchar('-');
			putchar('\n');
		}
603
	}
604 605
	if (extra < 0)
		exit(0);
606

J
Junio C Hamano 已提交
607
	/* Sort topologically */
608 609
	if (topo_order)
		sort_in_topological_order(&seen);
J
Junio C Hamano 已提交
610 611

	/* Give names to commits */
612 613
	if (!sha1_name && !no_name)
		name_commits(seen, rev, ref_name, num_rev);
J
Junio C Hamano 已提交
614 615 616 617

	all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
	all_revs = all_mask & ~((1u << REV_SHIFT) - 1);

J
Junio C Hamano 已提交
618 619 620
	while (seen) {
		struct commit *commit = pop_one_commit(&seen);
		int this_flag = commit->object.flags;
621

622
		shown_merge_point |= ((this_flag & all_revs) == all_revs);
J
Junio C Hamano 已提交
623

624 625 626 627 628 629
		if (1 < num_rev) {
			for (i = 0; i < num_rev; i++)
				putchar((this_flag & (1u << (i + REV_SHIFT)))
					? '+' : ' ');
			putchar(' ');
		}
630
		show_one_commit(commit, no_name);
631 632 633

		if (shown_merge_point && --extra < 0)
			break;
J
Junio C Hamano 已提交
634 635 636
	}
	return 0;
}