Merge branch 'kn/for-each-branch'
Update "git branch" that list existing branches, using the ref-filter API that is shared with "git tag" and "git for-each-ref". * kn/for-each-branch: branch: add '--points-at' option branch.c: use 'ref-filter' APIs branch.c: use 'ref-filter' data structures branch: drop non-commit error reporting branch: move 'current' check down to the presentation layer branch: roll show_detached HEAD into regular ref_list branch: bump get_head_description() to the top branch: refactor width computationmaint
						commit
						7f11b48521
					
				|  | @ -11,7 +11,8 @@ SYNOPSIS | ||||||
| 'git branch' [--color[=<when>] | --no-color] [-r | -a] | 'git branch' [--color[=<when>] | --no-color] [-r | -a] | ||||||
| 	[--list] [-v [--abbrev=<length> | --no-abbrev]] | 	[--list] [-v [--abbrev=<length> | --no-abbrev]] | ||||||
| 	[--column[=<options>] | --no-column] | 	[--column[=<options>] | --no-column] | ||||||
| 	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...] | 	[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>] | ||||||
|  | 	[--points-at <object>] [<pattern>...] | ||||||
| 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] | 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] | ||||||
| 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>] | 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>] | ||||||
| 'git branch' --unset-upstream [<branchname>] | 'git branch' --unset-upstream [<branchname>] | ||||||
|  | @ -231,6 +232,19 @@ start-point is either a local or remote-tracking branch. | ||||||
| 	The new name for an existing branch. The same restrictions as for | 	The new name for an existing branch. The same restrictions as for | ||||||
| 	<branchname> apply. | 	<branchname> apply. | ||||||
|  |  | ||||||
|  | --sort=<key>:: | ||||||
|  | 	Sort based on the key given. Prefix `-` to sort in descending | ||||||
|  | 	order of the value. You may use the --sort=<key> option | ||||||
|  | 	multiple times, in which case the last key becomes the primary | ||||||
|  | 	key. The keys supported are the same as those in `git | ||||||
|  | 	for-each-ref`. Sort order defaults to sorting based on the | ||||||
|  | 	full refname (including `refs/...` prefix). This lists | ||||||
|  | 	detached HEAD (if present) first, then local branches and | ||||||
|  | 	finally remote-tracking branches. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | --points-at <object>:: | ||||||
|  | 	Only list branches of the given object. | ||||||
|  |  | ||||||
| Examples | Examples | ||||||
| -------- | -------- | ||||||
|  |  | ||||||
							
								
								
									
										516
									
								
								builtin/branch.c
								
								
								
								
							
							
						
						
									
										516
									
								
								builtin/branch.c
								
								
								
								
							|  | @ -19,18 +19,17 @@ | ||||||
| #include "column.h" | #include "column.h" | ||||||
| #include "utf8.h" | #include "utf8.h" | ||||||
| #include "wt-status.h" | #include "wt-status.h" | ||||||
|  | #include "ref-filter.h" | ||||||
|  |  | ||||||
| static const char * const builtin_branch_usage[] = { | static const char * const builtin_branch_usage[] = { | ||||||
| 	N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"), | 	N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"), | ||||||
| 	N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"), | 	N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"), | ||||||
| 	N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."), | 	N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."), | ||||||
| 	N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"), | 	N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"), | ||||||
|  | 	N_("git branch [<options>] [-r | -a] [--points-at]"), | ||||||
| 	NULL | 	NULL | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #define REF_LOCAL_BRANCH    0x01 |  | ||||||
| #define REF_REMOTE_BRANCH   0x02 |  | ||||||
|  |  | ||||||
| static const char *head; | static const char *head; | ||||||
| static unsigned char head_sha1[20]; | static unsigned char head_sha1[20]; | ||||||
|  |  | ||||||
|  | @ -52,13 +51,6 @@ enum color_branch { | ||||||
| 	BRANCH_COLOR_UPSTREAM = 5 | 	BRANCH_COLOR_UPSTREAM = 5 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static enum merge_filter { |  | ||||||
| 	NO_FILTER = 0, |  | ||||||
| 	SHOW_NOT_MERGED, |  | ||||||
| 	SHOW_MERGED |  | ||||||
| } merge_filter; |  | ||||||
| static unsigned char merge_filter_ref[20]; |  | ||||||
|  |  | ||||||
| static struct string_list output = STRING_LIST_INIT_DUP; | static struct string_list output = STRING_LIST_INIT_DUP; | ||||||
| static unsigned int colopts; | static unsigned int colopts; | ||||||
|  |  | ||||||
|  | @ -121,7 +113,7 @@ static int branch_merged(int kind, const char *name, | ||||||
| 	void *reference_name_to_free = NULL; | 	void *reference_name_to_free = NULL; | ||||||
| 	int merged; | 	int merged; | ||||||
|  |  | ||||||
| 	if (kind == REF_LOCAL_BRANCH) { | 	if (kind == FILTER_REFS_BRANCHES) { | ||||||
| 		struct branch *branch = branch_get(name); | 		struct branch *branch = branch_get(name); | ||||||
| 		const char *upstream = branch_get_upstream(branch, NULL); | 		const char *upstream = branch_get_upstream(branch, NULL); | ||||||
| 		unsigned char sha1[20]; | 		unsigned char sha1[20]; | ||||||
|  | @ -199,14 +191,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, | ||||||
| 	struct strbuf bname = STRBUF_INIT; | 	struct strbuf bname = STRBUF_INIT; | ||||||
|  |  | ||||||
| 	switch (kinds) { | 	switch (kinds) { | ||||||
| 	case REF_REMOTE_BRANCH: | 	case FILTER_REFS_REMOTES: | ||||||
| 		fmt = "refs/remotes/%s"; | 		fmt = "refs/remotes/%s"; | ||||||
| 		/* For subsequent UI messages */ | 		/* For subsequent UI messages */ | ||||||
| 		remote_branch = 1; | 		remote_branch = 1; | ||||||
|  |  | ||||||
| 		force = 1; | 		force = 1; | ||||||
| 		break; | 		break; | ||||||
| 	case REF_LOCAL_BRANCH: | 	case FILTER_REFS_BRANCHES: | ||||||
| 		fmt = "refs/heads/%s"; | 		fmt = "refs/heads/%s"; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
|  | @ -223,7 +215,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, | ||||||
| 		int flags = 0; | 		int flags = 0; | ||||||
|  |  | ||||||
| 		strbuf_branchname(&bname, argv[i]); | 		strbuf_branchname(&bname, argv[i]); | ||||||
| 		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { | 		if (kinds == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) { | ||||||
| 			error(_("Cannot delete the branch '%s' " | 			error(_("Cannot delete the branch '%s' " | ||||||
| 			      "which you are currently on."), bname.buf); | 			      "which you are currently on."), bname.buf); | ||||||
| 			ret = 1; | 			ret = 1; | ||||||
|  | @ -279,147 +271,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, | ||||||
| 	return(ret); | 	return(ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| struct ref_item { |  | ||||||
| 	char *name; |  | ||||||
| 	char *dest; |  | ||||||
| 	unsigned int kind, width; |  | ||||||
| 	struct commit *commit; |  | ||||||
| 	int ignore; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct ref_list { |  | ||||||
| 	struct rev_info revs; |  | ||||||
| 	int index, alloc, maxwidth, verbose, abbrev; |  | ||||||
| 	struct ref_item *list; |  | ||||||
| 	struct commit_list *with_commit; |  | ||||||
| 	int kinds; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static char *resolve_symref(const char *src, const char *prefix) |  | ||||||
| { |  | ||||||
| 	unsigned char sha1[20]; |  | ||||||
| 	int flag; |  | ||||||
| 	const char *dst; |  | ||||||
|  |  | ||||||
| 	dst = resolve_ref_unsafe(src, 0, sha1, &flag); |  | ||||||
| 	if (!(dst && (flag & REF_ISSYMREF))) |  | ||||||
| 		return NULL; |  | ||||||
| 	if (prefix) |  | ||||||
| 		skip_prefix(dst, prefix, &dst); |  | ||||||
| 	return xstrdup(dst); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct append_ref_cb { |  | ||||||
| 	struct ref_list *ref_list; |  | ||||||
| 	const char **pattern; |  | ||||||
| 	int ret; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int match_patterns(const char **pattern, const char *refname) |  | ||||||
| { |  | ||||||
| 	if (!*pattern) |  | ||||||
| 		return 1; /* no pattern always matches */ |  | ||||||
| 	while (*pattern) { |  | ||||||
| 		if (!wildmatch(*pattern, refname, 0, NULL)) |  | ||||||
| 			return 1; |  | ||||||
| 		pattern++; |  | ||||||
| 	} |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data) |  | ||||||
| { |  | ||||||
| 	struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data); |  | ||||||
| 	struct ref_list *ref_list = cb->ref_list; |  | ||||||
| 	struct ref_item *newitem; |  | ||||||
| 	struct commit *commit; |  | ||||||
| 	int kind, i; |  | ||||||
| 	const char *prefix, *orig_refname = refname; |  | ||||||
|  |  | ||||||
| 	static struct { |  | ||||||
| 		int kind; |  | ||||||
| 		const char *prefix; |  | ||||||
| 	} ref_kind[] = { |  | ||||||
| 		{ REF_LOCAL_BRANCH, "refs/heads/" }, |  | ||||||
| 		{ REF_REMOTE_BRANCH, "refs/remotes/" }, |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	/* Detect kind */ |  | ||||||
| 	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) { |  | ||||||
| 		prefix = ref_kind[i].prefix; |  | ||||||
| 		if (skip_prefix(refname, prefix, &refname)) { |  | ||||||
| 			kind = ref_kind[i].kind; |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if (ARRAY_SIZE(ref_kind) <= i) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	/* Don't add types the caller doesn't want */ |  | ||||||
| 	if ((kind & ref_list->kinds) == 0) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	if (!match_patterns(cb->pattern, refname)) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	commit = NULL; |  | ||||||
| 	if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) { |  | ||||||
| 		commit = lookup_commit_reference_gently(oid->hash, 1); |  | ||||||
| 		if (!commit) { |  | ||||||
| 			cb->ret = error(_("branch '%s' does not point at a commit"), refname); |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		/* Filter with with_commit if specified */ |  | ||||||
| 		if (!is_descendant_of(commit, ref_list->with_commit)) |  | ||||||
| 			return 0; |  | ||||||
|  |  | ||||||
| 		if (merge_filter != NO_FILTER) |  | ||||||
| 			add_pending_object(&ref_list->revs, |  | ||||||
| 					   (struct object *)commit, refname); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc); |  | ||||||
|  |  | ||||||
| 	/* Record the new item */ |  | ||||||
| 	newitem = &(ref_list->list[ref_list->index++]); |  | ||||||
| 	newitem->name = xstrdup(refname); |  | ||||||
| 	newitem->kind = kind; |  | ||||||
| 	newitem->commit = commit; |  | ||||||
| 	newitem->width = utf8_strwidth(refname); |  | ||||||
| 	newitem->dest = resolve_symref(orig_refname, prefix); |  | ||||||
| 	newitem->ignore = 0; |  | ||||||
| 	/* adjust for "remotes/" */ |  | ||||||
| 	if (newitem->kind == REF_REMOTE_BRANCH && |  | ||||||
| 	    ref_list->kinds != REF_REMOTE_BRANCH) |  | ||||||
| 		newitem->width += 8; |  | ||||||
| 	if (newitem->width > ref_list->maxwidth) |  | ||||||
| 		ref_list->maxwidth = newitem->width; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void free_ref_list(struct ref_list *ref_list) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < ref_list->index; i++) { |  | ||||||
| 		free(ref_list->list[i].name); |  | ||||||
| 		free(ref_list->list[i].dest); |  | ||||||
| 	} |  | ||||||
| 	free(ref_list->list); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int ref_cmp(const void *r1, const void *r2) |  | ||||||
| { |  | ||||||
| 	struct ref_item *c1 = (struct ref_item *)(r1); |  | ||||||
| 	struct ref_item *c2 = (struct ref_item *)(r2); |  | ||||||
|  |  | ||||||
| 	if (c1->kind != c2->kind) |  | ||||||
| 		return c1->kind - c2->kind; |  | ||||||
| 	return strcmp(c1->name, c2->name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void fill_tracking_info(struct strbuf *stat, const char *branch_name, | static void fill_tracking_info(struct strbuf *stat, const char *branch_name, | ||||||
| 		int show_upstream_ref) | 		int show_upstream_ref) | ||||||
| { | { | ||||||
|  | @ -482,8 +333,8 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name, | ||||||
| 	free(ref); | 	free(ref); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void add_verbose_info(struct strbuf *out, struct ref_item *item, | static void add_verbose_info(struct strbuf *out, struct ref_array_item *item, | ||||||
| 			     int verbose, int abbrev) | 			     struct ref_filter *filter, const char *refname) | ||||||
| { | { | ||||||
| 	struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; | 	struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; | ||||||
| 	const char *sub = _(" **** invalid ref ****"); | 	const char *sub = _(" **** invalid ref ****"); | ||||||
|  | @ -494,81 +345,16 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item, | ||||||
| 		sub = subject.buf; | 		sub = subject.buf; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (item->kind == REF_LOCAL_BRANCH) | 	if (item->kind == FILTER_REFS_BRANCHES) | ||||||
| 		fill_tracking_info(&stat, item->name, verbose > 1); | 		fill_tracking_info(&stat, refname, filter->verbose > 1); | ||||||
|  |  | ||||||
| 	strbuf_addf(out, " %s %s%s", | 	strbuf_addf(out, " %s %s%s", | ||||||
| 		find_unique_abbrev(item->commit->object.sha1, abbrev), | 		find_unique_abbrev(item->commit->object.sha1, filter->abbrev), | ||||||
| 		stat.buf, sub); | 		stat.buf, sub); | ||||||
| 	strbuf_release(&stat); | 	strbuf_release(&stat); | ||||||
| 	strbuf_release(&subject); | 	strbuf_release(&subject); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, |  | ||||||
| 			   int abbrev, int current, char *prefix) |  | ||||||
| { |  | ||||||
| 	char c; |  | ||||||
| 	int color; |  | ||||||
| 	struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; |  | ||||||
|  |  | ||||||
| 	if (item->ignore) |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	switch (item->kind) { |  | ||||||
| 	case REF_LOCAL_BRANCH: |  | ||||||
| 		color = BRANCH_COLOR_LOCAL; |  | ||||||
| 		break; |  | ||||||
| 	case REF_REMOTE_BRANCH: |  | ||||||
| 		color = BRANCH_COLOR_REMOTE; |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		color = BRANCH_COLOR_PLAIN; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c = ' '; |  | ||||||
| 	if (current) { |  | ||||||
| 		c = '*'; |  | ||||||
| 		color = BRANCH_COLOR_CURRENT; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	strbuf_addf(&name, "%s%s", prefix, item->name); |  | ||||||
| 	if (verbose) { |  | ||||||
| 		int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf); |  | ||||||
| 		strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), |  | ||||||
| 			    maxwidth + utf8_compensation, name.buf, |  | ||||||
| 			    branch_get_color(BRANCH_COLOR_RESET)); |  | ||||||
| 	} else |  | ||||||
| 		strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), |  | ||||||
| 			    name.buf, branch_get_color(BRANCH_COLOR_RESET)); |  | ||||||
|  |  | ||||||
| 	if (item->dest) |  | ||||||
| 		strbuf_addf(&out, " -> %s", item->dest); |  | ||||||
| 	else if (verbose) |  | ||||||
| 		/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ |  | ||||||
| 		add_verbose_info(&out, item, verbose, abbrev); |  | ||||||
| 	if (column_active(colopts)) { |  | ||||||
| 		assert(!verbose && "--column and --verbose are incompatible"); |  | ||||||
| 		string_list_append(&output, out.buf); |  | ||||||
| 	} else { |  | ||||||
| 		printf("%s\n", out.buf); |  | ||||||
| 	} |  | ||||||
| 	strbuf_release(&name); |  | ||||||
| 	strbuf_release(&out); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int calc_maxwidth(struct ref_list *refs) |  | ||||||
| { |  | ||||||
| 	int i, w = 0; |  | ||||||
| 	for (i = 0; i < refs->index; i++) { |  | ||||||
| 		if (refs->list[i].ignore) |  | ||||||
| 			continue; |  | ||||||
| 		if (refs->list[i].width > w) |  | ||||||
| 			w = refs->list[i].width; |  | ||||||
| 	} |  | ||||||
| 	return w; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static char *get_head_description(void) | static char *get_head_description(void) | ||||||
| { | { | ||||||
| 	struct strbuf desc = STRBUF_INIT; | 	struct strbuf desc = STRBUF_INIT; | ||||||
|  | @ -600,101 +386,131 @@ static char *get_head_description(void) | ||||||
| 	return strbuf_detach(&desc, NULL); | 	return strbuf_detach(&desc, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void show_detached(struct ref_list *ref_list) | static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth, | ||||||
|  | 				      struct ref_filter *filter, const char *remote_prefix) | ||||||
| { | { | ||||||
| 	struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1); | 	char c; | ||||||
|  | 	int current = 0; | ||||||
|  | 	int color; | ||||||
|  | 	struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; | ||||||
|  | 	const char *prefix = ""; | ||||||
|  | 	const char *desc = item->refname; | ||||||
|  | 	char *to_free = NULL; | ||||||
|  |  | ||||||
| 	if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) { | 	switch (item->kind) { | ||||||
| 		struct ref_item item; | 	case FILTER_REFS_BRANCHES: | ||||||
| 		item.name = get_head_description(); | 		skip_prefix(desc, "refs/heads/", &desc); | ||||||
| 		item.width = utf8_strwidth(item.name); | 		if (!filter->detached && !strcmp(desc, head)) | ||||||
| 		item.kind = REF_LOCAL_BRANCH; | 			current = 1; | ||||||
| 		item.dest = NULL; | 		else | ||||||
| 		item.commit = head_commit; | 			color = BRANCH_COLOR_LOCAL; | ||||||
| 		item.ignore = 0; | 		break; | ||||||
| 		if (item.width > ref_list->maxwidth) | 	case FILTER_REFS_REMOTES: | ||||||
| 			ref_list->maxwidth = item.width; | 		skip_prefix(desc, "refs/remotes/", &desc); | ||||||
| 		print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, ""); | 		color = BRANCH_COLOR_REMOTE; | ||||||
| 		free(item.name); | 		prefix = remote_prefix; | ||||||
| 	} | 		break; | ||||||
|  | 	case FILTER_REFS_DETACHED_HEAD: | ||||||
|  | 		desc = to_free = get_head_description(); | ||||||
|  | 		current = 1; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		color = BRANCH_COLOR_PLAIN; | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) | 	c = ' '; | ||||||
|  | 	if (current) { | ||||||
|  | 		c = '*'; | ||||||
|  | 		color = BRANCH_COLOR_CURRENT; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	strbuf_addf(&name, "%s%s", prefix, desc); | ||||||
|  | 	if (filter->verbose) { | ||||||
|  | 		int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf); | ||||||
|  | 		strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), | ||||||
|  | 			    maxwidth + utf8_compensation, name.buf, | ||||||
|  | 			    branch_get_color(BRANCH_COLOR_RESET)); | ||||||
|  | 	} else | ||||||
|  | 		strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), | ||||||
|  | 			    name.buf, branch_get_color(BRANCH_COLOR_RESET)); | ||||||
|  |  | ||||||
|  | 	if (item->symref) { | ||||||
|  | 		skip_prefix(item->symref, "refs/remotes/", &desc); | ||||||
|  | 		strbuf_addf(&out, " -> %s", desc); | ||||||
|  | 	} | ||||||
|  | 	else if (filter->verbose) | ||||||
|  | 		/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ | ||||||
|  | 		add_verbose_info(&out, item, filter, desc); | ||||||
|  | 	if (column_active(colopts)) { | ||||||
|  | 		assert(!filter->verbose && "--column and --verbose are incompatible"); | ||||||
|  | 		string_list_append(&output, out.buf); | ||||||
|  | 	} else { | ||||||
|  | 		printf("%s\n", out.buf); | ||||||
|  | 	} | ||||||
|  | 	strbuf_release(&name); | ||||||
|  | 	strbuf_release(&out); | ||||||
|  | 	free(to_free); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int calc_maxwidth(struct ref_array *refs, int remote_bonus) | ||||||
|  | { | ||||||
|  | 	int i, max = 0; | ||||||
|  | 	for (i = 0; i < refs->nr; i++) { | ||||||
|  | 		struct ref_array_item *it = refs->items[i]; | ||||||
|  | 		const char *desc = it->refname; | ||||||
|  | 		int w; | ||||||
|  |  | ||||||
|  | 		skip_prefix(it->refname, "refs/heads/", &desc); | ||||||
|  | 		skip_prefix(it->refname, "refs/remotes/", &desc); | ||||||
|  | 		w = utf8_strwidth(desc); | ||||||
|  |  | ||||||
|  | 		if (it->kind == FILTER_REFS_REMOTES) | ||||||
|  | 			w += remote_bonus; | ||||||
|  | 		if (w > max) | ||||||
|  | 			max = w; | ||||||
|  | 	} | ||||||
|  | 	return max; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
| 	struct append_ref_cb cb; | 	struct ref_array array; | ||||||
| 	struct ref_list ref_list; | 	int maxwidth = 0; | ||||||
|  | 	const char *remote_prefix = ""; | ||||||
|  |  | ||||||
| 	memset(&ref_list, 0, sizeof(ref_list)); |  | ||||||
| 	ref_list.kinds = kinds; |  | ||||||
| 	ref_list.verbose = verbose; |  | ||||||
| 	ref_list.abbrev = abbrev; |  | ||||||
| 	ref_list.with_commit = with_commit; |  | ||||||
| 	if (merge_filter != NO_FILTER) |  | ||||||
| 		init_revisions(&ref_list.revs, NULL); |  | ||||||
| 	cb.ref_list = &ref_list; |  | ||||||
| 	cb.pattern = pattern; |  | ||||||
| 	cb.ret = 0; |  | ||||||
| 	for_each_rawref(append_ref, &cb); |  | ||||||
| 	/* | 	/* | ||||||
| 	 * The following implementation is currently duplicated in ref-filter. It | 	 * If we are listing more than just remote branches, | ||||||
| 	 * will eventually be removed when we port branch.c to use ref-filter APIs. | 	 * then remote branches will have a "remotes/" prefix. | ||||||
|  | 	 * We need to account for this in the width. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (merge_filter != NO_FILTER) { | 	if (filter->kind != FILTER_REFS_REMOTES) | ||||||
| 		struct commit *filter; | 		remote_prefix = "remotes/"; | ||||||
| 		filter = lookup_commit_reference_gently(merge_filter_ref, 0); |  | ||||||
| 		if (!filter) |  | ||||||
| 			die(_("object '%s' does not point to a commit"), |  | ||||||
| 			    sha1_to_hex(merge_filter_ref)); |  | ||||||
|  |  | ||||||
| 		filter->object.flags |= UNINTERESTING; | 	memset(&array, 0, sizeof(array)); | ||||||
| 		add_pending_object(&ref_list.revs, |  | ||||||
| 				   (struct object *) filter, ""); |  | ||||||
| 		ref_list.revs.limited = 1; |  | ||||||
|  |  | ||||||
| 		if (prepare_revision_walk(&ref_list.revs)) | 	verify_ref_format("%(refname)%(symref)"); | ||||||
| 			die(_("revision walk setup failed")); | 	filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN); | ||||||
|  |  | ||||||
| 		for (i = 0; i < ref_list.index; i++) { | 	if (filter->verbose) | ||||||
| 			struct ref_item *item = &ref_list.list[i]; | 		maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); | ||||||
| 			struct commit *commit = item->commit; |  | ||||||
| 			int is_merged = !!(commit->object.flags & UNINTERESTING); |  | ||||||
| 			item->ignore = is_merged != (merge_filter == SHOW_MERGED); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for (i = 0; i < ref_list.index; i++) { | 	/* | ||||||
| 			struct ref_item *item = &ref_list.list[i]; | 	 * If no sorting parameter is given then we default to sorting | ||||||
| 			clear_commit_marks(item->commit, ALL_REV_FLAGS); | 	 * by 'refname'. This would give us an alphabetically sorted | ||||||
| 		} | 	 * array with the 'HEAD' ref at the beginning followed by | ||||||
| 		clear_commit_marks(filter, ALL_REV_FLAGS); | 	 * local branches 'refs/heads/...' and finally remote-tacking | ||||||
|  | 	 * branches 'refs/remotes/...'. | ||||||
|  | 	 */ | ||||||
|  | 	if (!sorting) | ||||||
|  | 		sorting = ref_default_sorting(); | ||||||
|  | 	ref_array_sort(sorting, &array); | ||||||
|  |  | ||||||
| 		if (verbose) | 	for (i = 0; i < array.nr; i++) | ||||||
| 			ref_list.maxwidth = calc_maxwidth(&ref_list); | 		format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix); | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); | 	ref_array_clear(&array); | ||||||
|  |  | ||||||
| 	detached = (detached && (kinds & REF_LOCAL_BRANCH)); |  | ||||||
| 	if (detached && match_patterns(pattern, "HEAD")) |  | ||||||
| 		show_detached(&ref_list); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < ref_list.index; i++) { |  | ||||||
| 		int current = !detached && |  | ||||||
| 			(ref_list.list[i].kind == REF_LOCAL_BRANCH) && |  | ||||||
| 			!strcmp(ref_list.list[i].name, head); |  | ||||||
| 		char *prefix = (kinds != REF_REMOTE_BRANCH && |  | ||||||
| 				ref_list.list[i].kind == REF_REMOTE_BRANCH) |  | ||||||
| 				? "remotes/" : ""; |  | ||||||
| 		print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose, |  | ||||||
| 			       abbrev, current, prefix); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	free_ref_list(&ref_list); |  | ||||||
|  |  | ||||||
| 	if (cb.ret) |  | ||||||
| 		error(_("some refs could not be read")); |  | ||||||
|  |  | ||||||
| 	return cb.ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void rename_branch(const char *oldname, const char *newname, int force) | static void rename_branch(const char *oldname, const char *newname, int force) | ||||||
|  | @ -750,24 +566,6 @@ static void rename_branch(const char *oldname, const char *newname, int force) | ||||||
| 	strbuf_release(&newsection); | 	strbuf_release(&newsection); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * This function is duplicated in ref-filter. It will eventually be removed |  | ||||||
|  * when we port branch.c to use ref-filter APIs. |  | ||||||
|  */ |  | ||||||
| static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset) |  | ||||||
| { |  | ||||||
| 	merge_filter = ((opt->long_name[0] == 'n') |  | ||||||
| 			? SHOW_NOT_MERGED |  | ||||||
| 			: SHOW_MERGED); |  | ||||||
| 	if (unset) |  | ||||||
| 		merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */ |  | ||||||
| 	if (!arg) |  | ||||||
| 		arg = "HEAD"; |  | ||||||
| 	if (get_sha1(arg, merge_filter_ref)) |  | ||||||
| 		die(_("malformed object name %s"), arg); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const char edit_description[] = "BRANCH_DESCRIPTION"; | static const char edit_description[] = "BRANCH_DESCRIPTION"; | ||||||
|  |  | ||||||
| static int edit_branch_description(const char *branch_name) | static int edit_branch_description(const char *branch_name) | ||||||
|  | @ -807,17 +605,16 @@ static int edit_branch_description(const char *branch_name) | ||||||
| int cmd_branch(int argc, const char **argv, const char *prefix) | int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| { | { | ||||||
| 	int delete = 0, rename = 0, force = 0, list = 0; | 	int delete = 0, rename = 0, force = 0, list = 0; | ||||||
| 	int verbose = 0, abbrev = -1, detached = 0; |  | ||||||
| 	int reflog = 0, edit_description = 0; | 	int reflog = 0, edit_description = 0; | ||||||
| 	int quiet = 0, unset_upstream = 0; | 	int quiet = 0, unset_upstream = 0; | ||||||
| 	const char *new_upstream = NULL; | 	const char *new_upstream = NULL; | ||||||
| 	enum branch_track track; | 	enum branch_track track; | ||||||
| 	int kinds = REF_LOCAL_BRANCH; | 	struct ref_filter filter; | ||||||
| 	struct commit_list *with_commit = NULL; | 	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; | ||||||
|  |  | ||||||
| 	struct option options[] = { | 	struct option options[] = { | ||||||
| 		OPT_GROUP(N_("Generic options")), | 		OPT_GROUP(N_("Generic options")), | ||||||
| 		OPT__VERBOSE(&verbose, | 		OPT__VERBOSE(&filter.verbose, | ||||||
| 			N_("show hash and subject, give twice for upstream branch")), | 			N_("show hash and subject, give twice for upstream branch")), | ||||||
| 		OPT__QUIET(&quiet, N_("suppress informational messages")), | 		OPT__QUIET(&quiet, N_("suppress informational messages")), | ||||||
| 		OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"), | 		OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"), | ||||||
|  | @ -827,15 +624,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| 		OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"), | 		OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"), | ||||||
| 		OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"), | 		OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"), | ||||||
| 		OPT__COLOR(&branch_use_color, N_("use colored output")), | 		OPT__COLOR(&branch_use_color, N_("use colored output")), | ||||||
| 		OPT_SET_INT('r', "remotes",     &kinds, N_("act on remote-tracking branches"), | 		OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"), | ||||||
| 			REF_REMOTE_BRANCH), | 			FILTER_REFS_REMOTES), | ||||||
| 		OPT_CONTAINS(&with_commit, N_("print only branches that contain the commit")), | 		OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")), | ||||||
| 		OPT_WITH(&with_commit, N_("print only branches that contain the commit")), | 		OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")), | ||||||
| 		OPT__ABBREV(&abbrev), | 		OPT__ABBREV(&filter.abbrev), | ||||||
|  |  | ||||||
| 		OPT_GROUP(N_("Specific git-branch actions:")), | 		OPT_GROUP(N_("Specific git-branch actions:")), | ||||||
| 		OPT_SET_INT('a', "all", &kinds, N_("list both remote-tracking and local branches"), | 		OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), | ||||||
| 			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH), | 			FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES), | ||||||
| 		OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), | 		OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), | ||||||
| 		OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), | 		OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), | ||||||
| 		OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), | 		OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), | ||||||
|  | @ -845,22 +642,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| 		OPT_BOOL(0, "edit-description", &edit_description, | 		OPT_BOOL(0, "edit-description", &edit_description, | ||||||
| 			 N_("edit the description for the branch")), | 			 N_("edit the description for the branch")), | ||||||
| 		OPT__FORCE(&force, N_("force creation, move/rename, deletion")), | 		OPT__FORCE(&force, N_("force creation, move/rename, deletion")), | ||||||
| 		{ | 		OPT_MERGED(&filter, N_("print only branches that are merged")), | ||||||
| 			OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, | 		OPT_NO_MERGED(&filter, N_("print only branches that are not merged")), | ||||||
| 			N_("commit"), N_("print only not merged branches"), |  | ||||||
| 			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, |  | ||||||
| 			opt_parse_merge_filter, (intptr_t) "HEAD", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			OPTION_CALLBACK, 0, "merged", &merge_filter_ref, |  | ||||||
| 			N_("commit"), N_("print only merged branches"), |  | ||||||
| 			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, |  | ||||||
| 			opt_parse_merge_filter, (intptr_t) "HEAD", |  | ||||||
| 		}, |  | ||||||
| 		OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), | 		OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), | ||||||
|  | 		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), | ||||||
|  | 			     N_("field name to sort on"), &parse_opt_ref_sorting), | ||||||
|  | 		{ | ||||||
|  | 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), | ||||||
|  | 			N_("print only branches of the object"), 0, parse_opt_object_name | ||||||
|  | 		}, | ||||||
| 		OPT_END(), | 		OPT_END(), | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | 	memset(&filter, 0, sizeof(filter)); | ||||||
|  | 	filter.kind = FILTER_REFS_BRANCHES; | ||||||
|  | 	filter.abbrev = -1; | ||||||
|  |  | ||||||
| 	if (argc == 2 && !strcmp(argv[1], "-h")) | 	if (argc == 2 && !strcmp(argv[1], "-h")) | ||||||
| 		usage_with_options(builtin_branch_usage, options); | 		usage_with_options(builtin_branch_usage, options); | ||||||
|  |  | ||||||
|  | @ -872,11 +669,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| 	if (!head) | 	if (!head) | ||||||
| 		die(_("Failed to resolve HEAD as a valid ref.")); | 		die(_("Failed to resolve HEAD as a valid ref.")); | ||||||
| 	if (!strcmp(head, "HEAD")) | 	if (!strcmp(head, "HEAD")) | ||||||
| 		detached = 1; | 		filter.detached = 1; | ||||||
| 	else if (!skip_prefix(head, "refs/heads/", &head)) | 	else if (!skip_prefix(head, "refs/heads/", &head)) | ||||||
| 		die(_("HEAD not found below refs/heads!")); | 		die(_("HEAD not found below refs/heads!")); | ||||||
| 	hashcpy(merge_filter_ref, head_sha1); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, | 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, | ||||||
| 			     0); | 			     0); | ||||||
|  | @ -884,17 +679,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| 	if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0) | 	if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0) | ||||||
| 		list = 1; | 		list = 1; | ||||||
|  |  | ||||||
| 	if (with_commit || merge_filter != NO_FILTER) | 	if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr) | ||||||
| 		list = 1; | 		list = 1; | ||||||
|  |  | ||||||
| 	if (!!delete + !!rename + !!new_upstream + | 	if (!!delete + !!rename + !!new_upstream + | ||||||
| 	    list + unset_upstream > 1) | 	    list + unset_upstream > 1) | ||||||
| 		usage_with_options(builtin_branch_usage, options); | 		usage_with_options(builtin_branch_usage, options); | ||||||
|  |  | ||||||
| 	if (abbrev == -1) | 	if (filter.abbrev == -1) | ||||||
| 		abbrev = DEFAULT_ABBREV; | 		filter.abbrev = DEFAULT_ABBREV; | ||||||
| 	finalize_colopts(&colopts, -1); | 	finalize_colopts(&colopts, -1); | ||||||
| 	if (verbose) { | 	if (filter.verbose) { | ||||||
| 		if (explicitly_enable_column(colopts)) | 		if (explicitly_enable_column(colopts)) | ||||||
| 			die(_("--column and --verbose are incompatible")); | 			die(_("--column and --verbose are incompatible")); | ||||||
| 		colopts = 0; | 		colopts = 0; | ||||||
|  | @ -908,20 +703,23 @@ int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| 	if (delete) { | 	if (delete) { | ||||||
| 		if (!argc) | 		if (!argc) | ||||||
| 			die(_("branch name required")); | 			die(_("branch name required")); | ||||||
| 		return delete_branches(argc, argv, delete > 1, kinds, quiet); | 		return delete_branches(argc, argv, delete > 1, filter.kind, quiet); | ||||||
| 	} else if (list) { | 	} else if (list) { | ||||||
| 		int ret = print_ref_list(kinds, detached, verbose, abbrev, | 		/*  git branch --local also shows HEAD when it is detached */ | ||||||
| 					 with_commit, argv); | 		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached) | ||||||
|  | 			filter.kind |= FILTER_REFS_DETACHED_HEAD; | ||||||
|  | 		filter.name_patterns = argv; | ||||||
|  | 		print_ref_list(&filter, sorting); | ||||||
| 		print_columns(&output, colopts, NULL); | 		print_columns(&output, colopts, NULL); | ||||||
| 		string_list_clear(&output, 0); | 		string_list_clear(&output, 0); | ||||||
| 		return ret; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	else if (edit_description) { | 	else if (edit_description) { | ||||||
| 		const char *branch_name; | 		const char *branch_name; | ||||||
| 		struct strbuf branch_ref = STRBUF_INIT; | 		struct strbuf branch_ref = STRBUF_INIT; | ||||||
|  |  | ||||||
| 		if (!argc) { | 		if (!argc) { | ||||||
| 			if (detached) | 			if (filter.detached) | ||||||
| 				die(_("Cannot give description to detached HEAD")); | 				die(_("Cannot give description to detached HEAD")); | ||||||
| 			branch_name = head; | 			branch_name = head; | ||||||
| 		} else if (argc == 1) | 		} else if (argc == 1) | ||||||
|  | @ -1009,7 +807,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) | ||||||
| 		if (!branch) | 		if (!branch) | ||||||
| 			die(_("no such branch '%s'"), argv[0]); | 			die(_("no such branch '%s'"), argv[0]); | ||||||
|  |  | ||||||
| 		if (kinds != REF_LOCAL_BRANCH) | 		if (filter.kind != FILTER_REFS_BRANCHES) | ||||||
| 			die(_("-a and -r options to 'git branch' do not make sense with a branch name")); | 			die(_("-a and -r options to 'git branch' do not make sense with a branch name")); | ||||||
|  |  | ||||||
| 		if (track == BRANCH_TRACK_OVERRIDE) | 		if (track == BRANCH_TRACK_OVERRIDE) | ||||||
|  |  | ||||||
|  | @ -1331,7 +1331,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, | ||||||
| 	 * obtain the commit using the 'oid' available and discard all | 	 * obtain the commit using the 'oid' available and discard all | ||||||
| 	 * non-commits early. The actual filtering is done later. | 	 * non-commits early. The actual filtering is done later. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (filter->merge_commit || filter->with_commit) { | 	if (filter->merge_commit || filter->with_commit || filter->verbose) { | ||||||
| 		commit = lookup_commit_reference_gently(oid->hash, 1); | 		commit = lookup_commit_reference_gently(oid->hash, 1); | ||||||
| 		if (!commit) | 		if (!commit) | ||||||
| 			return 0; | 			return 0; | ||||||
|  |  | ||||||
|  | @ -45,6 +45,7 @@ struct ref_array_item { | ||||||
| struct ref_array { | struct ref_array { | ||||||
| 	int nr, alloc; | 	int nr, alloc; | ||||||
| 	struct ref_array_item **items; | 	struct ref_array_item **items; | ||||||
|  | 	struct rev_info *revs; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct ref_filter { | struct ref_filter { | ||||||
|  | @ -60,9 +61,12 @@ struct ref_filter { | ||||||
| 	struct commit *merge_commit; | 	struct commit *merge_commit; | ||||||
|  |  | ||||||
| 	unsigned int with_commit_tag_algo : 1, | 	unsigned int with_commit_tag_algo : 1, | ||||||
| 		match_as_path : 1; | 		match_as_path : 1, | ||||||
|  | 		detached : 1; | ||||||
| 	unsigned int kind, | 	unsigned int kind, | ||||||
| 		lines; | 		lines; | ||||||
|  | 	int abbrev, | ||||||
|  | 		verbose; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct ref_filter_cbdata { | struct ref_filter_cbdata { | ||||||
|  |  | ||||||
|  | @ -38,18 +38,20 @@ test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"' | ||||||
| 	test_must_fail git fast-import <input | 	test_must_fail git fast-import <input | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_success 'git branch shows badly named ref' ' | test_expect_success 'git branch shows badly named ref as warning' ' | ||||||
| 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
| 	grep -e "broken\.\.\.ref" output | 	grep -e "broken\.\.\.ref" error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_success 'branch -d can delete badly named ref' ' | test_expect_success 'branch -d can delete badly named ref' ' | ||||||
| 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	git branch -d broken...ref && | 	git branch -d broken...ref && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -57,7 +59,8 @@ test_expect_success 'branch -D can delete badly named ref' ' | ||||||
| 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	git branch -D broken...ref && | 	git branch -D broken...ref && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -85,7 +88,8 @@ test_expect_success 'branch -D cannot delete absolute path' ' | ||||||
| test_expect_success 'git branch cannot create a badly named ref' ' | test_expect_success 'git branch cannot create a badly named ref' ' | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	test_must_fail git branch broken...ref && | 	test_must_fail git branch broken...ref && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -95,7 +99,8 @@ test_expect_success 'branch -m cannot rename to a bad ref name' ' | ||||||
| 	git branch goodref && | 	git branch goodref && | ||||||
| 	test_must_fail git branch -m goodref broken...ref && | 	test_must_fail git branch -m goodref broken...ref && | ||||||
| 	test_cmp_rev master goodref && | 	test_cmp_rev master goodref && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -104,14 +109,16 @@ test_expect_failure 'branch -m can rename from a bad ref name' ' | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	git branch -m broken...ref renamed && | 	git branch -m broken...ref renamed && | ||||||
| 	test_cmp_rev master renamed && | 	test_cmp_rev master renamed && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_success 'push cannot create a badly named ref' ' | test_expect_success 'push cannot create a badly named ref' ' | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref && | 	test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -131,7 +138,8 @@ test_expect_failure 'push --mirror can delete badly named ref' ' | ||||||
| 		cp .git/refs/heads/master .git/refs/heads/broken...ref | 		cp .git/refs/heads/master .git/refs/heads/broken...ref | ||||||
| 	) && | 	) && | ||||||
| 	git -C src push --mirror "file://$top/dest" && | 	git -C src push --mirror "file://$top/dest" && | ||||||
| 	git -C dest branch >output && | 	git -C dest branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -159,7 +167,8 @@ test_expect_success 'update-ref -d can delete broken name' ' | ||||||
| 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | 	cp .git/refs/heads/master .git/refs/heads/broken...ref && | ||||||
| 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | 	test_when_finished "rm -f .git/refs/heads/broken...ref" && | ||||||
| 	git update-ref -d refs/heads/broken...ref && | 	git update-ref -d refs/heads/broken...ref && | ||||||
| 	git branch >output && | 	git branch >output 2>error && | ||||||
|  | 	! grep -e "broken\.\.\.ref" error && | ||||||
| 	! grep -e "broken\.\.\.ref" output | 	! grep -e "broken\.\.\.ref" output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -143,4 +143,24 @@ EOF | ||||||
| 	test_i18ncmp expect actual | 	test_i18ncmp expect actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'git branch `--sort` option' ' | ||||||
|  | 	cat >expect <<-\EOF && | ||||||
|  | 	  branch-two | ||||||
|  | 	* (HEAD detached from fromtag) | ||||||
|  | 	  branch-one | ||||||
|  | 	  master | ||||||
|  | 	EOF | ||||||
|  | 	git branch --sort=objectsize >actual && | ||||||
|  | 	test_i18ncmp expect actual | ||||||
|  | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'git branch --points-at option' ' | ||||||
|  | 	cat >expect <<-\EOF && | ||||||
|  | 	  branch-one | ||||||
|  | 	  master | ||||||
|  | 	EOF | ||||||
|  | 	git branch --points-at=branch-one >actual && | ||||||
|  | 	test_cmp expect actual | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano