Re: [RFC] branch: list branches by single remote

28 messages, 6 authors, 2016-06-15 · open the first message on its own page

Re: [RFC] branch: list branches by single remote

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:51:54

Michael J Gruber [off-list ref] writes:
quoted
As suggested, I've just called it "--glob" for now.
Well, again, what's the point in replicating

http://permalink.gmane.org/gmane.comp.version-control.git/172228

and how is it different?
As I've mentioned, I've been in the middle of polishing that up.
It is not unusual for a similar itch to happen to different people
independently.

If this were something you reposted even a WIP re-polish within the past
two weeks, I would understand and even sympathise with your irritation,
but please don't expect everybody to dig back FOUR MONTHS worth of mail
backlog to find an topic that may or may not be abandoned by the original
author.  Perhaps we would need a weekly posting of topics people have
posted, found to be not quite ready yet, and are still being polished and
not abandoned [*1*]?

Having said that, I still appreciate that you posted a link to the
previous topic:

  http://thread.gmane.org/gmane.comp.version-control.git/172226

so that the discussions in this thread to scratch the same "itch" can
benefit from the points raised in the previous thread that need to be
considered.

The old thread talks about renaming existing options and transition plans
to make the "listing" mode of "branch" and "tag" more similar, which may
be a good plan in the longer term.

I however can see that teaching "--glob" to both "branch" and "tag" (in
other words, "tag -l" would become a synonym for "tag --glob") an equally
good longer term plan.


[Footnote]

*1* It does not have to be a weekly _posting_ on the list but can be a
well-known Wiki page or even a bug tracker. Whatever medium is used for
this purpose, there _must_ be a built-in mechanism to expire entries away
that are inactive for more than some reasonable limit (say two to three
weeks).

Re: [RFC] branch: list branches by single remote

From: Thiago Farina <hidden>
Date: 2016-06-15 22:51:54

On Wed, Aug 24, 2011 at 3:34 PM, Junio C Hamano [off-list ref] wrote:
[Footnote]

*1* It does not have to be a weekly _posting_ on the list but can be a
well-known Wiki page or even a bug tracker. Whatever medium is used for
this purpose, there _must_ be a built-in mechanism to expire entries away
that are inactive for more than some reasonable limit (say two to three
weeks).
Host the project on Google Code for bug tracking? Use rietveld for
code reviews (ike codereview.chromium.org and codereview.appspot.com)?

Re: [RFC] branch: list branches by single remote

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:54

Junio C Hamano venit, vidit, dixit 24.08.2011 20:34:
Michael J Gruber [off-list ref] writes:
quoted
quoted
As suggested, I've just called it "--glob" for now.
Well, again, what's the point in replicating

http://permalink.gmane.org/gmane.comp.version-control.git/172228

and how is it different?
As I've mentioned, I've been in the middle of polishing that up.
It is not unusual for a similar itch to happen to different people
independently.

If this were something you reposted even a WIP re-polish within the past
two weeks, I would understand and even sympathise with your irritation,
but please don't expect everybody to dig back FOUR MONTHS worth of mail
backlog to find an topic that may or may not be abandoned by the original
author.  Perhaps we would need a weekly posting of topics people have
Well, I've mentioned it earlier in this thread, though without a link.
It's the time when several of us are off-line for a couple weeks. I've
also mentioned it in the thread about filtering by remote.

On a side note, the list here usually does require people to search
back, and much longer than this, on frequently requested issues. But how
does a newcomer discover "frequently"? Right, by searching back years,
not months.
posted, found to be not quite ready yet, and are still being polished and
not abandoned [*1*]?

Having said that, I still appreciate that you posted a link to the
previous topic:

  http://thread.gmane.org/gmane.comp.version-control.git/172226

so that the discussions in this thread to scratch the same "itch" can
benefit from the points raised in the previous thread that need to be
considered.

The old thread talks about renaming existing options and transition plans
to make the "listing" mode of "branch" and "tag" more similar, which may
be a good plan in the longer term.

I however can see that teaching "--glob" to both "branch" and "tag" (in
other words, "tag -l" would become a synonym for "tag --glob") an equally
good longer term plan.
I don't care about the names, but I'd hate to introduce more
inconsistencies. The implementation is a non-brainer, it's really
something only Apple could get a patent on. The other Michael and I came
up with basically the same patch because it follows "automatically". But
the ui is important.

So I'll take the opportunity and discuss this further in the cover
letter for the upcoming series.

Michael

[PATCH 0/5] RFC: patterns for branch list

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:54

This mini series is about introducing patterns to the list mode of
'git branch' much like the pattern for 'git tag -l'. There are several
related things which are to be considered for the ui design:

git log
=======

For "log" and friends, "--glob" is a means to specify revision arguments by
matching refs. Note that we have

--branches[=<pattern>] matching in refs/heads
--tags[=<pattern>] matching in refs/tags
--glob=<glob-pattern> matching in refs/

git tag
=======

For "tag", we have a pattern matching in refs/tags, but only in the list mode
of "tag", which is invoked by "-l", or automatically when there are no
arguments. There is no pattern related option, it's "always on" in list mode.
I.e., in list mode it behaves much like

git log --no-walk --oneline --tags=

with a different format string, and an optional argument stuck at the end of
the line. (git for-each-ref is another related interface, but plumbing, so I'll
skip it here.)

git branch
==========

Analogous to "git tag", "branch" has several modes, one of which is list mode.
It is currently activated (and possibly modified) by "-v" and "-vv", and when
there are no arguments. So, at the least,

git branch -v[v] <pattern>

should match just like "git tag -l <pattern>" does. And that is what the first
patch in my series does.

Then we need an option to invoke list mode. The natural candidate "-l" is taken
by the badly named reflog option, which is why I suggested "--list" and moving
the reflog option to "-g" over time.

I'm open to other suggestions as long as they keep and improve the consistency
between "git tag" and "git branch". I never liked their different implicit
modes/subcommands but have been around long enough to know that they are there
to stay.

"git tag" should probably learn the same long option and others. And why not
verify tags given by a pattern?

Both "tag" and "branch" could activate list mode automatically on an invalid
tag name rather than dieing:

git tag v1.7.6\*
Warning: tag 'v1.7.6*' not found.
v1.7.6
v1.7.6-rc0
v1.7.6-rc1
v1.7.6-rc2
v1.7.6-rc3
v1.7.6.1

On the other hand, one might think about implementing both list modes (tag and
branch) using the revision machinery and custom format strings, or
for-each-ref; rather than both differently as it is now.

'-l' is the natural short option name for '--list'. This is taken for the
rarely used or needed 'create reflog' option. I'd change the latter to
'-g,--create-reflog' (cmp. log) and take '-l,--list' but know the reaction
already.

-v[v] sanity
============

'-v' and '-vv' both take considerable time (because they need to walk).
It makes more sense to have '-v' display cheap output (upstream name)
and '-vv' add expensive output (ahead/behind info). '-vvv' could add super
expensive info (ahead/equivalent/behind a la cherry-mark).

These are changes to current porcelain ui behaviour, so I deem this to be OK
(too late for 1.7.7, of course). The option renaming needs a transition
(not done in this series).

Michael J Gruber (5):
  branch: allow pattern arguments
  branch: introduce --list argument
  t6040; test branch -vv [independent, can go in as is testing current behaviour]
  branch: restructure -v vs. -vv
  branch: give patchsame count with -vvv

 Documentation/git-branch.txt |   20 +++++++++---
 builtin/branch.c             |   65 +++++++++++++++++++++++++----------------
 remote.c                     |   12 ++++++--
 remote.h                     |    2 +-
 t/t3203-branch-output.sh     |   24 +++++++++++++++
 t/t6040-tracking-info.sh     |   41 ++++++++++++++++++++++++--
 wt-status.c                  |    2 +-
 7 files changed, 127 insertions(+), 39 deletions(-)

-- 
1.7.6.845.gc3c05

[PATCH 3/5] t6040; test branch -vv

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

t6040 has a test for 'git branch -v' but not for 'git branch -vv'.
Add one.

Signed-off-by: Michael J Gruber <redacted>
---
 t/t6040-tracking-info.sh |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 19de5b1..19272bc 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -51,6 +51,22 @@ test_expect_success 'branch -v' '
 	test_i18ncmp expect actual
 '
 
+cat >expect <<\EOF
+b1 origin/master: ahead 1, behind 1
+b2 origin/master: ahead 1, behind 1
+b3 origin/master: behind 1
+b4 origin/master: ahead 2
+EOF
+
+test_expect_success 'branch -vv' '
+	(
+		cd test &&
+		git branch -vv
+	) |
+	sed -n -e "$script" >actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'checkout' '
 	(
 		cd test && git checkout b1
-- 
1.7.6.845.gc3c05

[PATCH 5/5] branch: give patchsame count with -vvv

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

This results in output like such as

  mjg/attr-filetype                    9d2536e [origin/next: ahead 35,
behind 1430, same 46] attr: make attributes depend on file type

all on one line.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    2 ++
 builtin/branch.c             |   24 ++++++++++++++++--------
 remote.c                     |   12 +++++++++---
 remote.h                     |    2 +-
 t/t6040-tracking-info.sh     |   19 ++++++++++++++++++-
 wt-status.c                  |    2 +-
 6 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 59d729a..770b2e4 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -117,6 +117,8 @@ OPTIONS
 	Show sha1 and commit subject line for each head, along with
 	the name of the upstream branch (if any). If given twice, print
 	the relationship to the upstream branch (ahead/behind), as well.
+	If given three times, show ahead/behind/same information like
+	`git rev-list --count --cherry-mark`.
 	`--list` is implied by all verbosity options.
 
 --abbrev=<length>::
diff --git a/builtin/branch.c b/builtin/branch.c
index 21ef5fc..0984bb7 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -361,7 +361,7 @@ static int ref_cmp(const void *r1, const void *r2)
 static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
 		int verbose)
 {
-	int ours, theirs;
+	int ours, theirs, patchsame = 0;
 	struct branch *branch = branch_get(branch_name);
 
 	/* verbose >= 1 */
@@ -370,14 +370,22 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
 	strbuf_addch(stat, '[');
 	strbuf_addstr(stat, shorten_unambiguous_ref(branch->merge[0]->dst, 0));
 
-	if (verbose >= 2 && stat_tracking_info(branch, &ours, &theirs)) {
-		strbuf_addstr(stat, ": ");
-		if (!ours)
-			strbuf_addf(stat, _("behind %d"), theirs);
-		else if (!theirs)
+	if (verbose >= 2 && stat_tracking_info(branch, &ours, &theirs, (verbose >=3) ? &patchsame : NULL)) {
+		strbuf_addstr(stat, _(": "));
+		const char *sep = "";
+		if (ours) {
 			strbuf_addf(stat, _("ahead %d"), ours);
-		else
-			strbuf_addf(stat, _("ahead %d, behind %d"), ours, theirs);
+			sep = _(", ");
+		}
+		if (theirs) {
+			strbuf_addstr(stat, sep);
+			strbuf_addf(stat, _("behind %d"), theirs);
+			sep = _(", ");
+		}
+		if (patchsame) {
+			strbuf_addstr(stat, sep);
+			strbuf_addf(stat, _("same %d"), patchsame);
+		}
 	}
 	strbuf_addstr(stat, "] ");
 }
diff --git a/remote.c b/remote.c
index b8ecfa5..40cebef 100644
--- a/remote.c
+++ b/remote.c
@@ -1510,7 +1510,7 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
 /*
  * Return true if there is anything to report, otherwise false.
  */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, int *num_patchsame)
 {
 	unsigned char sha1[20];
 	struct commit *ours, *theirs;
@@ -1553,6 +1553,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
 	rev_argv[rev_argc++] = NULL;
 	rev_argv[rev_argc++] = "--left-right";
 	rev_argv[rev_argc++] = symmetric;
+	if (num_patchsame)
+		rev_argv[rev_argc++] = "--cherry-mark";
 	rev_argv[rev_argc++] = "--";
 	rev_argv[rev_argc] = NULL;
 
@@ -1567,11 +1569,15 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
 	/* ... and count the commits on each side. */
 	*num_ours = 0;
 	*num_theirs = 0;
+	if (num_patchsame)
+		*num_patchsame = 0;
 	while (1) {
 		struct commit *c = get_revision(&revs);
 		if (!c)
 			break;
-		if (c->object.flags & SYMMETRIC_LEFT)
+		if (c->object.flags & PATCHSAME)
+			(*num_patchsame)++;
+		else if (c->object.flags & SYMMETRIC_LEFT)
 			(*num_ours)++;
 		else
 			(*num_theirs)++;
@@ -1591,7 +1597,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
 	int num_ours, num_theirs;
 	const char *base;
 
-	if (!stat_tracking_info(branch, &num_ours, &num_theirs))
+	if (!stat_tracking_info(branch, &num_ours, &num_theirs, NULL))
 		return 0;
 
 	base = branch->merge[0]->dst;
diff --git a/remote.h b/remote.h
index 9a30a9d..31c1f28 100644
--- a/remote.h
+++ b/remote.h
@@ -149,7 +149,7 @@ enum match_refs_flags {
 };
 
 /* Reporting of tracking info */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, int *num_patchsame);
 int format_tracking_info(struct branch *branch, struct strbuf *sb);
 
 struct ref *get_local_heads(void);
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 9539882..f8bff61 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -24,6 +24,7 @@ test_expect_success setup '
 		advance d &&
 		git checkout -b b2 origin &&
 		git reset --hard b1 &&
+		git cherry-pick origin &&
 		git checkout -b b3 origin &&
 		git reset --hard HEAD^ &&
 		git checkout -b b4 origin &&
@@ -53,7 +54,7 @@ test_expect_success 'branch -v' '
 
 cat >expect <<\EOF
 b1 origin/master: ahead 1, behind 1
-b2 origin/master: ahead 1, behind 1
+b2 origin/master: ahead 2, behind 1
 b3 origin/master: behind 1
 b4 origin/master: ahead 2
 EOF
@@ -67,6 +68,22 @@ test_expect_success 'branch -vv' '
 	test_i18ncmp expect actual
 '
 
+cat >expect <<\EOF
+b1 origin/master: ahead 1, behind 1
+b2 origin/master: ahead 1, same 2
+b3 origin/master: behind 1
+b4 origin/master: ahead 2
+EOF
+
+test_expect_success 'branch -vvv' '
+	(
+		cd test &&
+		git branch -vvv
+	) |
+	sed -n -e "$script" >actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'checkout' '
 	(
 		cd test && git checkout b1
diff --git a/wt-status.c b/wt-status.c
index 8836a52..522318a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -890,7 +890,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
 	branch = branch_get(s->branch + 11);
 	if (s->is_initial)
 		color_fprintf(s->fp, header_color, _("Initial commit on "));
-	if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
+	if (!stat_tracking_info(branch, &num_ours, &num_theirs, NULL)) {
 		color_fprintf_ln(s->fp, branch_color_local,
 			"%s", branch_name);
 		return;
-- 
1.7.6.845.gc3c05

[PATCH 1/5] branch: allow pattern arguments

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Allow pattern arguments for the list mode just like for git tag -l.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    7 +++++--
 builtin/branch.c             |   13 +++++++++----
 t/t3203-branch-output.sh     |   10 ++++++++++
 3 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index c50f189..2152d48 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
 	[-v [--abbrev=<length> | --no-abbrev]]
-	[(--merged | --no-merged | --contains) [<commit>]]
+	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
 'git branch' (-d | -D) [-r] <branchname>...
@@ -20,7 +20,10 @@ DESCRIPTION
 
 With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
-branches to be listed, and option `-a` shows both.
+branches to be listed, and option `-a` shows both. This list mode is also
+activated by the `-v` option (see below).
+<pattern> restricts the output to matching branches, the pattern is a shell
+wildcard (i.e., matched using fnmatch(3))
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
diff --git a/builtin/branch.c b/builtin/branch.c
index 73d4170..6b40815 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -260,6 +260,7 @@ static char *resolve_symref(const char *src, const char *prefix)
 
 struct append_ref_cb {
 	struct ref_list *ref_list;
+	const char *pattern;
 	int ret;
 };
 
@@ -297,6 +298,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
 	if ((kind & ref_list->kinds) == 0)
 		return 0;
 
+	if (cb->pattern && fnmatch(cb->pattern, refname, 0))
+		return 0;
+
 	commit = NULL;
 	if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
 		commit = lookup_commit_reference_gently(sha1, 1);
@@ -492,7 +496,7 @@ static void show_detached(struct ref_list *ref_list)
 	}
 }
 
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char *pattern)
 {
 	int i;
 	struct append_ref_cb cb;
@@ -506,6 +510,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 	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);
 	if (merge_filter != NO_FILTER) {
@@ -523,7 +528,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
 	detached = (detached && (kinds & REF_LOCAL_BRANCH));
-	if (detached)
+	if (detached && (!pattern || !fnmatch(pattern, "HEAD", 0)))
 		show_detached(&ref_list);
 
 	for (i = 0; i < ref_list.index; i++) {
@@ -695,8 +700,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (argc == 0)
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+	else if (argc == 0 || (verbose && argc == 1))
+		return print_ref_list(kinds, detached, verbose, abbrev, with_commit, argc ? argv[0] : NULL);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
 	else if (rename && (argc == 2))
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 6b7c118..2df1d3d 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -67,6 +67,16 @@ test_expect_success 'git branch -v shows branch summaries' '
 '
 
 cat >expect <<'EOF'
+two
+one
+EOF
+test_expect_success 'git branch -v pattern shows branch summaries' '
+	git branch -v branch* >tmp &&
+	awk "{print \$NF}" <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
 * (no branch)
   branch-one
   branch-two
-- 
1.7.6.845.gc3c05

[PATCH 4/5] branch: restructure -v vs. -vv

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

ahead/behind info is expensive, upstream name info cheap. Therefore,
make -v output the upstream branch name and -vv add the ahead/behind
info.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    4 ++--
 builtin/branch.c             |   34 ++++++++++++++++------------------
 t/t6040-tracking-info.sh     |    8 ++++----
 3 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 789ff02..59d729a 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -115,8 +115,8 @@ OPTIONS
 -v::
 --verbose::
 	Show sha1 and commit subject line for each head, along with
-	relationship to upstream branch (if any). If given twice, print
-	the name of the upstream branch, as well.
+	the name of the upstream branch (if any). If given twice, print
+	the relationship to the upstream branch (ahead/behind), as well.
 	`--list` is implied by all verbosity options.
 
 --abbrev=<length>::
diff --git a/builtin/branch.c b/builtin/branch.c
index aed0aca..21ef5fc 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -359,29 +359,27 @@ static int ref_cmp(const void *r1, const void *r2)
 }
 
 static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
-		int show_upstream_ref)
+		int verbose)
 {
 	int ours, theirs;
 	struct branch *branch = branch_get(branch_name);
 
-	if (!stat_tracking_info(branch, &ours, &theirs)) {
-		if (branch && branch->merge && branch->merge[0]->dst &&
-		    show_upstream_ref)
-			strbuf_addf(stat, "[%s] ",
-			    shorten_unambiguous_ref(branch->merge[0]->dst, 0));
+	/* verbose >= 1 */
+	if (!(branch && branch->merge && branch->merge[0]->dst))
 		return;
-	}
-
 	strbuf_addch(stat, '[');
-	if (show_upstream_ref)
-		strbuf_addf(stat, "%s: ",
-			shorten_unambiguous_ref(branch->merge[0]->dst, 0));
-	if (!ours)
-		strbuf_addf(stat, _("behind %d] "), theirs);
-	else if (!theirs)
-		strbuf_addf(stat, _("ahead %d] "), ours);
-	else
-		strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs);
+	strbuf_addstr(stat, shorten_unambiguous_ref(branch->merge[0]->dst, 0));
+
+	if (verbose >= 2 && stat_tracking_info(branch, &ours, &theirs)) {
+		strbuf_addstr(stat, ": ");
+		if (!ours)
+			strbuf_addf(stat, _("behind %d"), theirs);
+		else if (!theirs)
+			strbuf_addf(stat, _("ahead %d"), ours);
+		else
+			strbuf_addf(stat, _("ahead %d, behind %d"), ours, theirs);
+	}
+	strbuf_addstr(stat, "] ");
 }
 
 static int matches_merge_filter(struct commit *commit)
@@ -408,7 +406,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
 	}
 
 	if (item->kind == REF_LOCAL_BRANCH)
-		fill_tracking_info(&stat, item->name, verbose > 1);
+		fill_tracking_info(&stat, item->name, verbose);
 
 	strbuf_addf(out, " %s %s%s",
 		find_unique_abbrev(item->commit->object.sha1, abbrev),
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 19272bc..9539882 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -36,10 +36,10 @@ test_expect_success setup '
 
 script='s/^..\(b.\)[	 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
 cat >expect <<\EOF
-b1 ahead 1, behind 1
-b2 ahead 1, behind 1
-b3 behind 1
-b4 ahead 2
+b1 origin/master
+b2 origin/master
+b3 origin/master
+b4 origin/master
 EOF
 
 test_expect_success 'branch -v' '
-- 
1.7.6.845.gc3c05

[PATCH 2/5] branch: introduce --list argument

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Currently, there is no way to invoke the list mode with a pattern
because this is interpreted as branch creation.

Introduce a --list argument which invokes the list mode.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    9 +++++++--
 builtin/branch.c             |   10 +++++++---
 t/t3203-branch-output.sh     |   14 ++++++++++++++
 3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 2152d48..789ff02 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
-	[-v [--abbrev=<length> | --no-abbrev]]
+	[--list] [-v [--abbrev=<length> | --no-abbrev]]
 	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -21,7 +21,7 @@ DESCRIPTION
 With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
 branches to be listed, and option `-a` shows both. This list mode is also
-activated by the `-v` option (see below).
+activated by the `--list` and `-v` options (see below).
 <pattern> restricts the output to matching branches, the pattern is a shell
 wildcard (i.e., matched using fnmatch(3))
 
@@ -108,11 +108,16 @@ OPTIONS
 -a::
 	List both remote-tracking branches and local branches.
 
+--list::
+	Activate the list mode. `git branch <pattern>` would try to create a branch,
+	use `git branch --list <pattern>` to list matching branches.
+
 -v::
 --verbose::
 	Show sha1 and commit subject line for each head, along with
 	relationship to upstream branch (if any). If given twice, print
 	the name of the upstream branch, as well.
+	`--list` is implied by all verbosity options.
 
 --abbrev=<length>::
 	Alter the sha1's minimum display length in the output listing.
diff --git a/builtin/branch.c b/builtin/branch.c
index 6b40815..aed0aca 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -617,7 +617,7 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
 
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
-	int delete = 0, rename = 0, force_create = 0;
+	int delete = 0, rename = 0, force_create = 0, list = 0;
 	int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
 	int reflog = 0;
 	enum branch_track track;
@@ -656,6 +656,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
 		OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
 		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
+		OPT_BOOLEAN(0, "list", &list, "list branch names"),
 		OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
 		OPT__FORCE(&force_create, "force creation (when already exists)"),
 		{
@@ -695,12 +696,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
 			     0);
-	if (!!delete + !!rename + !!force_create > 1)
+	if (!!delete + !!rename + !!force_create + !!list > 1)
 		usage_with_options(builtin_branch_usage, options);
 
+	if (argc == 0 || (verbose && argc == 1))
+		list = 1;
+
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (argc == 0 || (verbose && argc == 1))
+	else if (list)
 		return print_ref_list(kinds, detached, verbose, abbrev, with_commit, argc ? argv[0] : NULL);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 2df1d3d..f2b294b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -32,6 +32,20 @@ test_expect_success 'git branch shows local branches' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --list shows local branches' '
+	git branch --list >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+EOF
+test_expect_success 'git branch --list pattern shows matching local branches' '
+	git branch --list branch* >actual &&
+	test_cmp expect actual
+'
+
 cat >expect <<'EOF'
   origin/HEAD -> origin/branch-one
   origin/branch-one
-- 
1.7.6.845.gc3c05

Re: [PATCH 0/5] RFC: patterns for branch list

From: Jeff King <hidden>
Date: 2016-06-15 22:51:55

On Thu, Aug 25, 2011 at 10:30:16AM +0200, Michael J Gruber wrote:
This mini series is about introducing patterns to the list mode of
'git branch' much like the pattern for 'git tag -l'. There are several
related things which are to be considered for the ui design:
[log vs tag vs branch]
I agree that the ideal UI change would be to move git-branch's "-l" to
"-g", and make "-l|--list" work the same as it does for git-tag.

Even though branch is generally considered a porcelain, I worry a little
about making that change. A script that wants to create a branch has no
real choice other than to use "git branch" (OK, they can use
"update-ref" themselves, but I seriously doubt that most scripts do so).
However, I kind of doubt anyone actually uses "-l"; it is mostly
pointless in the default config, so maybe it is safe.

Searching google code for "git.branch.*-l" turns up only one hit, and it
is somebody who apparently thought that "-l" meant "list".
Analogous to "git tag", "branch" has several modes, one of which is list mode.
It is currently activated (and possibly modified) by "-v" and "-vv", and when
there are no arguments. So, at the least,

git branch -v[v] <pattern>

should match just like "git tag -l <pattern>" does. And that is what the first
patch in my series does.
The order of your patches seems backwards to me. You add
pattern-matching for "-v", but there is no way to get pattern-matching
for the non-verbose case. Shouldn't "--list" come first?

Maybe I am just nitpicking, as I think the end result after the series
is the same. I just found the first patch very confusing.
"git tag" should probably learn the same long option and others. And why not
verify tags given by a pattern?
Yeah, having them both do --list makes sense. Whether it is appropriate
to glob for other operations, I don't know. I think you'd have to
look at each operation individually.
Both "tag" and "branch" could activate list mode automatically on an invalid
tag name rather than dieing:

git tag v1.7.6\*
Warning: tag 'v1.7.6*' not found.
v1.7.6
v1.7.6-rc0
v1.7.6-rc1
v1.7.6-rc2
v1.7.6-rc3
v1.7.6.1
That just seems confusing to me. What is the exit status? Shouldn't the
warning be "error: tag 'v1.7.6*' is not a valid tag name"?
-v[v] sanity
============

'-v' and '-vv' both take considerable time (because they need to walk).
It makes more sense to have '-v' display cheap output (upstream name)
and '-vv' add expensive output (ahead/behind info). '-vvv' could add super
expensive info (ahead/equivalent/behind a la cherry-mark).
I think the original rationale was not so much "how much time does it
take", but rather "how much space do you want each line to take on your
terminal". For many people, the upstream name in "-vv" is just
cluttering noise.

Tag and branch listing are really just specialized versions of
for-each-ref. I wonder if it makes sense to do:

  1. Teach for-each-ref formats replacement tokens for ahead/behind
     counts.

  2. Let the user specify a for-each-ref format for tag and branch
     listing output. Then the various levels of "-v" just become some
     special format strings, and the user is free to ask for whatever
     they want (or even have "branch.defaultListFormat" to get it
     without typing over and over).

-Peff

Re: [PATCH 1/5] branch: allow pattern arguments

From: Jeff King <hidden>
Date: 2016-06-15 22:51:55

On Thu, Aug 25, 2011 at 10:30:17AM +0200, Michael J Gruber wrote:
-	else if (argc == 0)
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+	else if (argc == 0 || (verbose && argc == 1))
+		return print_ref_list(kinds, detached, verbose, abbrev, with_commit, argc ? argv[0] : NULL);
Note that "git tag -l" takes multiple patterns these days (it used to
silently ignore everything after the first one!). "git branch" should
probably do the same.

-Peff

Re: [PATCH 0/5] RFC: patterns for branch list

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Jeff King venit, vidit, dixit 25.08.2011 19:53:
On Thu, Aug 25, 2011 at 10:30:16AM +0200, Michael J Gruber wrote:
quoted
This mini series is about introducing patterns to the list mode of
'git branch' much like the pattern for 'git tag -l'. There are several
related things which are to be considered for the ui design:
quoted
[log vs tag vs branch]
I agree that the ideal UI change would be to move git-branch's "-l" to
"-g", and make "-l|--list" work the same as it does for git-tag.

Even though branch is generally considered a porcelain, I worry a little
about making that change. A script that wants to create a branch has no
real choice other than to use "git branch" (OK, they can use
"update-ref" themselves, but I seriously doubt that most scripts do so).
However, I kind of doubt anyone actually uses "-l"; it is mostly
pointless in the default config, so maybe it is safe.

Searching google code for "git.branch.*-l" turns up only one hit, and it
is somebody who apparently thought that "-l" meant "list".
;)

Thanks for doing the search.
quoted
Analogous to "git tag", "branch" has several modes, one of which is list mode.
It is currently activated (and possibly modified) by "-v" and "-vv", and when
there are no arguments. So, at the least,

git branch -v[v] <pattern>

should match just like "git tag -l <pattern>" does. And that is what the first
patch in my series does.
The order of your patches seems backwards to me. You add
pattern-matching for "-v", but there is no way to get pattern-matching
for the non-verbose case. Shouldn't "--list" come first?

Maybe I am just nitpicking, as I think the end result after the series
is the same. I just found the first patch very confusing.
It's an RFC series to revive the discussion about what to aim for.
Agreement about "--list" seems to be growing, so a natural first patch
would introduce that.
quoted
"git tag" should probably learn the same long option and others. And why not
verify tags given by a pattern?
Yeah, having them both do --list makes sense. Whether it is appropriate
to glob for other operations, I don't know. I think you'd have to
look at each operation individually.
quoted
Both "tag" and "branch" could activate list mode automatically on an invalid
tag name rather than dieing:

git tag v1.7.6\*
Warning: tag 'v1.7.6*' not found.
v1.7.6
v1.7.6-rc0
v1.7.6-rc1
v1.7.6-rc2
v1.7.6-rc3
v1.7.6.1
That just seems confusing to me. What is the exit status? Shouldn't the
warning be "error: tag 'v1.7.6*' is not a valid tag name"?
Sure, and sorry, copied the wrong one. I'd just like to have the simple
way to say "git branch peff/\*" at least as long as we don't have "-l"
for "--list".
quoted
-v[v] sanity
============

'-v' and '-vv' both take considerable time (because they need to walk).
It makes more sense to have '-v' display cheap output (upstream name)
and '-vv' add expensive output (ahead/behind info). '-vvv' could add super
expensive info (ahead/equivalent/behind a la cherry-mark).
I think the original rationale was not so much "how much time does it
take", but rather "how much space do you want each line to take on your
terminal". For many people, the upstream name in "-vv" is just
cluttering noise.
According to my experience, the ahead/behind computations take so much
time (in a git.git clone with my devel branches) that they render all
"-v" versions unusable, unless I use a restrictive pattern.

On the other hand, I have branches based on all of origin/{master,next}
and others, so having the upstream name is valuable.

Seems that I'm an outlier, though.
Tag and branch listing are really just specialized versions of
for-each-ref. I wonder if it makes sense to do:

  1. Teach for-each-ref formats replacement tokens for ahead/behind
     counts.

  2. Let the user specify a for-each-ref format for tag and branch
     listing output. Then the various levels of "-v" just become some
     special format strings, and the user is free to ask for whatever
     they want (or even have "branch.defaultListFormat" to get it
     without typing over and over).
for-each-peff ;)

For a moment, the use of the walker in builtin/branch.c even tricked me
into thinking that it might not use for-each-ref at all. God forbid!

I actually like the format suggestion. Then we only need to discuss the
default format, which is hopefully less of a problem. But that is
something for later, I'll discard the -v[v[v]] patches for now. Have we
unified log formats and for-each-ref formats and parsers already, btw? I
recall some efforts.

Michael

[PATCHv2 1/5] t6040: test branch -vv

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

t6040 has a test for 'git branch -v' but not for 'git branch -vv'.
Add one.

Signed-off-by: Michael J Gruber <redacted>
---
 t/t6040-tracking-info.sh |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 19de5b1..19272bc 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -51,6 +51,22 @@ test_expect_success 'branch -v' '
 	test_i18ncmp expect actual
 '
 
+cat >expect <<\EOF
+b1 origin/master: ahead 1, behind 1
+b2 origin/master: ahead 1, behind 1
+b3 origin/master: behind 1
+b4 origin/master: ahead 2
+EOF
+
+test_expect_success 'branch -vv' '
+	(
+		cd test &&
+		git branch -vv
+	) |
+	sed -n -e "$script" >actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'checkout' '
 	(
 		cd test && git checkout b1
-- 
1.7.6.845.gc3c05

[PATCHv2 0/5] patterns for branch list

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

In this iteration, I have incorporated Jeffs and Junios suggestions and Junios
patches. Thanks!

So:

git branch --list is introduced before the patterns (though it is not needed at
that point)

Multiple patterns are allowed.

The -v/vv/vvv changing patches are gone.

Also, the independent test for "-vv" is put first (which certainly can go in
now).  Next come long option names for "git tag" and "git branch", then the
pattern related patches.

I have also rebased on next with nk/branch-v-abbrev (which conflicts, though
"trivially").

"-l -> -g" migration for "git branch" and such are issues for a later series.

Michael J Gruber (5):
  t6040: test branch -vv
  git-tag: introduce long forms for the options
  git-branch: introduce missing long forms for the options
  branch: introduce --list option
  branch: allow pattern arguments

 Documentation/git-branch.txt |   20 +++++++++++++++--
 Documentation/git-tag.txt    |    8 +++++++
 builtin/branch.c             |   46 +++++++++++++++++++++++++++++++----------
 builtin/tag.c                |   16 +++++++-------
 t/t3203-branch-output.sh     |   24 +++++++++++++++++++++
 t/t6040-tracking-info.sh     |   16 ++++++++++++++
 6 files changed, 108 insertions(+), 22 deletions(-)

-- 
1.7.6.845.gc3c05

[PATCHv2 2/5] git-tag: introduce long forms for the options

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Long forms are better to memoize, and more reliably uniform across
commands.

Design notes:

-u,--local-user is named following the analogous gnupg option.

-l,--list is not an argument taking option but a mode switch.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-tag.txt |    8 ++++++++
 builtin/tag.c             |   16 ++++++++--------
 2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fb1c0ac..c83cb13 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -43,12 +43,15 @@ GnuPG key for signing.
 OPTIONS
 -------
 -a::
+--annotate::
 	Make an unsigned, annotated tag object
 
 -s::
+--sign::
 	Make a GPG-signed tag, using the default e-mail address's key
 
 -u <key-id>::
+--local-user=<key-id>::
 	Make a GPG-signed tag, using the given key
 
 -f::
@@ -56,9 +59,11 @@ OPTIONS
 	Replace an existing tag with the given name (instead of failing)
 
 -d::
+--delete::
 	Delete existing tags with the given names.
 
 -v::
+--verify::
 	Verify the gpg signature of the given tag names.
 
 -n<num>::
@@ -69,6 +74,7 @@ OPTIONS
 	If the tag is not annotated, the commit message is displayed instead.
 
 -l <pattern>::
+--list <pattern>::
 	List tags with names that match the given pattern (or all if no
 	pattern is given).  Running "git tag" without arguments also
 	lists all tags. The pattern is a shell wildcard (i.e., matched
@@ -79,6 +85,7 @@ OPTIONS
 	Only list tags which contain the specified commit.
 
 -m <msg>::
+--message=<msg>::
 	Use the given tag message (instead of prompting).
 	If multiple `-m` options are given, their values are
 	concatenated as separate paragraphs.
@@ -86,6 +93,7 @@ OPTIONS
 	is given.
 
 -F <file>::
+--file=<file>::
 	Take the tag message from the given file.  Use '-' to
 	read the message from the standard input.
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
diff --git a/builtin/tag.c b/builtin/tag.c
index 667515e..9d89616 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -429,21 +429,21 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct msg_arg msg = { 0, STRBUF_INIT };
 	struct commit_list *with_commit = NULL;
 	struct option options[] = {
-		OPT_BOOLEAN('l', NULL, &list, "list tag names"),
+		OPT_BOOLEAN('l', "list", &list, "list tag names"),
 		{ OPTION_INTEGER, 'n', NULL, &lines, "n",
 				"print <n> lines of each tag message",
 				PARSE_OPT_OPTARG, NULL, 1 },
-		OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
-		OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
+		OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
+		OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
 
 		OPT_GROUP("Tag creation options"),
-		OPT_BOOLEAN('a', NULL, &annotate,
+		OPT_BOOLEAN('a', "annotate", &annotate,
 					"annotated tag, needs a message"),
-		OPT_CALLBACK('m', NULL, &msg, "message",
+		OPT_CALLBACK('m', "message", &msg, "message",
 			     "tag message", parse_msg_arg),
-		OPT_FILENAME('F', NULL, &msgfile, "read message from file"),
-		OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
-		OPT_STRING('u', NULL, &keyid, "key-id",
+		OPT_FILENAME('F', "file", &msgfile, "read message from file"),
+		OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
+		OPT_STRING('u', "local-user", &keyid, "key-id",
 					"use another key to sign the tag"),
 		OPT__FORCE(&force, "replace the tag if exists"),
 
-- 
1.7.6.845.gc3c05

[PATCHv2 4/5] branch: introduce --list option

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Currently, there is no way to invoke the list mode explicitly.
Introduce a --list option which invokes the list mode. This will be
beneficial for invoking list mode with pattern matching, which otherwise
would be interpreted as branch creation.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    9 +++++++--
 builtin/branch.c             |   12 +++++++++---
 t/t3203-branch-output.sh     |   14 ++++++++++++++
 3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 4c64ac9..ac278fb 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
-	[-v [--abbrev=<length> | --no-abbrev]]
+	[--list] [-v [--abbrev=<length> | --no-abbrev]]
 	[(--merged | --no-merged | --contains) [<commit>]]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -20,7 +20,8 @@ DESCRIPTION
 
 With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
-branches to be listed, and option `-a` shows both.
+branches to be listed, and option `-a` shows both. This list mode is also
+activated by the `--list` and `-v` options (see below).
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
@@ -110,11 +111,15 @@ OPTIONS
 --all::
 	List both remote-tracking branches and local branches.
 
+--list::
+	Activate the list mode.
+
 -v::
 --verbose::
 	Show sha1 and commit subject line for each head, along with
 	relationship to upstream branch (if any). If given twice, print
 	the name of the upstream branch, as well.
+	`--list` is implied by all verbosity options.
 
 --abbrev=<length>::
 	Alter the sha1's minimum display length in the output listing.
diff --git a/builtin/branch.c b/builtin/branch.c
index 94e41ae..4a33b07 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -608,7 +608,7 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
 
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
-	int delete = 0, rename = 0, force_create = 0;
+	int delete = 0, rename = 0, force_create = 0, list = 0;
 	int verbose = 0, abbrev = -1, detached = 0;
 	int reflog = 0;
 	enum branch_track track;
@@ -647,6 +647,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
 		OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
 		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
+		OPT_BOOLEAN(0, "list", &list, "list branch names"),
 		OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
 		OPT__FORCE(&force_create, "force creation (when already exists)"),
 		{
@@ -686,7 +687,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
 			     0);
-	if (!!delete + !!rename + !!force_create > 1)
+
+	if (!delete && !rename && !force_create &&
+	    (argc == 0 || (verbose && argc)))
+		list = 1;
+
+	if (!!delete + !!rename + !!force_create + !!list > 1)
 		usage_with_options(builtin_branch_usage, options);
 
 	if (abbrev == -1)
@@ -694,7 +700,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (argc == 0)
+	else if (list)
 		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 6b7c118..61e095c 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -32,6 +32,20 @@ test_expect_success 'git branch shows local branches' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --list shows local branches' '
+	git branch --list >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+EOF
+test_expect_success 'git branch --list pattern shows matching local branches' '
+	git branch --list branch* >actual &&
+	test_cmp expect actual
+'
+
 cat >expect <<'EOF'
   origin/HEAD -> origin/branch-one
   origin/branch-one
-- 
1.7.6.845.gc3c05

[PATCHv2 5/5] branch: allow pattern arguments

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Allow pattern arguments for the list mode just like for git tag -l.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    8 ++++++--
 builtin/branch.c             |   24 +++++++++++++++++++++---
 t/t3203-branch-output.sh     |   10 ++++++++++
 3 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index ac278fb..2b8bc84 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
 	[--list] [-v [--abbrev=<length> | --no-abbrev]]
-	[(--merged | --no-merged | --contains) [<commit>]]
+	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
 'git branch' (-d | -D) [-r] <branchname>...
@@ -22,6 +22,9 @@ With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
 branches to be listed, and option `-a` shows both. This list mode is also
 activated by the `--list` and `-v` options (see below).
+<pattern> restricts the output to matching branches, the pattern is a shell
+wildcard (i.e., matched using fnmatch(3))
+Multiple patterns may be given; if any of them matches, the tag is shown.
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
@@ -112,7 +115,8 @@ OPTIONS
 	List both remote-tracking branches and local branches.
 
 --list::
-	Activate the list mode.
+	Activate the list mode. `git branch <pattern>` would try to create a branch,
+	use `git branch --list <pattern>` to list matching branches.
 
 -v::
 --verbose::
diff --git a/builtin/branch.c b/builtin/branch.c
index 4a33b07..e6bef49 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -260,9 +260,22 @@ static char *resolve_symref(const char *src, const char *prefix)
 
 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 (!fnmatch(*pattern, refname, 0))
+			return 1;
+		pattern++;
+	}
+	return 0;
+}
+
 static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
 	struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
@@ -297,6 +310,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
 	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(sha1, 1);
@@ -492,7 +508,7 @@ static void show_detached(struct ref_list *ref_list)
 	}
 }
 
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
 {
 	int i;
 	struct append_ref_cb cb;
@@ -506,6 +522,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 	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);
 	if (merge_filter != NO_FILTER) {
@@ -523,7 +540,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
 	detached = (detached && (kinds & REF_LOCAL_BRANCH));
-	if (detached)
+	if (detached && match_patterns(pattern, "HEAD"))
 		show_detached(&ref_list);
 
 	for (i = 0; i < ref_list.index; i++) {
@@ -701,7 +718,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
 	else if (list)
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+		return print_ref_list(kinds, detached, verbose, abbrev,
+				      with_commit, argv);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
 	else if (rename && (argc == 2))
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 61e095c..f2b294b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -81,6 +81,16 @@ test_expect_success 'git branch -v shows branch summaries' '
 '
 
 cat >expect <<'EOF'
+two
+one
+EOF
+test_expect_success 'git branch -v pattern shows branch summaries' '
+	git branch -v branch* >tmp &&
+	awk "{print \$NF}" <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
 * (no branch)
   branch-one
   branch-two
-- 
1.7.6.845.gc3c05

[PATCHv2 3/5] git-branch: introduce missing long forms for the options

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:55

Long forms are better to memoize, and more reliably uniform across
commands.

Signed-off-by: Michael J Gruber <redacted>
---
I'm somewhat torn between --move and --rename for -m. We have no real precedent
besides "git remote rename".

I left out -M and -D because I feel they should really be -m -f resp. -d -f.
---
 Documentation/git-branch.txt |    5 +++++
 builtin/branch.c             |   10 +++++-----
 2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 507b8d0..4c64ac9 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -64,6 +64,7 @@ way to clean up all obsolete remote-tracking branches.
 OPTIONS
 -------
 -d::
+--delete::
 	Delete a branch. The branch must be fully merged in its
 	upstream branch, or in `HEAD` if no upstream was set with
 	`--track` or `--set-upstream`.
@@ -72,6 +73,7 @@ OPTIONS
 	Delete a branch irrespective of its merged status.
 
 -l::
+--create-reflog::
 	Create the branch's reflog.  This activates recording of
 	all changes made to the branch ref, enabling use of date
 	based sha1 expressions such as "<branchname>@\{yesterday}".
@@ -84,6 +86,7 @@ OPTIONS
 	already. Without `-f` 'git branch' refuses to change an existing branch.
 
 -m::
+--move::
 	Move/rename a branch and the corresponding reflog.
 
 -M::
@@ -100,9 +103,11 @@ OPTIONS
 	Same as `--color=never`.
 
 -r::
+--remotes::
 	List or delete (if used with -d) the remote-tracking branches.
 
 -a::
+--all::
 	List both remote-tracking branches and local branches.
 
 -v::
diff --git a/builtin/branch.c b/builtin/branch.c
index aa705a0..94e41ae 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -624,7 +624,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
 			BRANCH_TRACK_OVERRIDE),
 		OPT__COLOR(&branch_use_color, "use colored output"),
-		OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
+		OPT_SET_INT('r', "remotes",     &kinds, "act on remote-tracking branches",
 			REF_REMOTE_BRANCH),
 		{
 			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
@@ -641,13 +641,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT__ABBREV(&abbrev),
 
 		OPT_GROUP("Specific git-branch actions:"),
-		OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
+		OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
 			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
-		OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1),
+		OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1),
 		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
-		OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
+		OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
 		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
-		OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
+		OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
 		OPT__FORCE(&force_create, "force creation (when already exists)"),
 		{
 			OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
-- 
1.7.6.845.gc3c05

[PATCHv3 0/5] patterns for branch list

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:56

v3 has reworded commit messages for 2,3,4 and tests for combinations
of branch options in 4, as suggested.

No code changes (besides the additional tests in 4).

Michael J Gruber (5):
  t6040: test branch -vv
  git-tag: introduce long forms for the options
  git-branch: introduce missing long forms for the options
  branch: introduce --list option
  branch: allow pattern arguments

 Documentation/git-branch.txt |   20 +++++++++++++++--
 Documentation/git-tag.txt    |    8 +++++++
 builtin/branch.c             |   46 +++++++++++++++++++++++++++++++----------
 builtin/tag.c                |   16 +++++++-------
 t/t3200-branch.sh            |   32 +++++++++++++++++++++++++++++
 t/t3203-branch-output.sh     |   24 +++++++++++++++++++++
 t/t6040-tracking-info.sh     |   16 ++++++++++++++
 7 files changed, 140 insertions(+), 22 deletions(-)

-- 
1.7.6.845.gc3c05

[PATCHv3 1/5] t6040: test branch -vv

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:56

t6040 has a test for 'git branch -v' but not for 'git branch -vv'.
Add one.

Signed-off-by: Michael J Gruber <redacted>
---
 t/t6040-tracking-info.sh |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 19de5b1..19272bc 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -51,6 +51,22 @@ test_expect_success 'branch -v' '
 	test_i18ncmp expect actual
 '
 
+cat >expect <<\EOF
+b1 origin/master: ahead 1, behind 1
+b2 origin/master: ahead 1, behind 1
+b3 origin/master: behind 1
+b4 origin/master: ahead 2
+EOF
+
+test_expect_success 'branch -vv' '
+	(
+		cd test &&
+		git branch -vv
+	) |
+	sed -n -e "$script" >actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'checkout' '
 	(
 		cd test && git checkout b1
-- 
1.7.6.845.gc3c05

[PATCHv3 2/5] git-tag: introduce long forms for the options

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:56

Long forms are better to memorize and more reliably uniform across
commands.

Design notes:

-u,--local-user is named following the analogous gnupg option.

-l,--list is not an argument taking option but a mode switch.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-tag.txt |    8 ++++++++
 builtin/tag.c             |   16 ++++++++--------
 2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fb1c0ac..c83cb13 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -43,12 +43,15 @@ GnuPG key for signing.
 OPTIONS
 -------
 -a::
+--annotate::
 	Make an unsigned, annotated tag object
 
 -s::
+--sign::
 	Make a GPG-signed tag, using the default e-mail address's key
 
 -u <key-id>::
+--local-user=<key-id>::
 	Make a GPG-signed tag, using the given key
 
 -f::
@@ -56,9 +59,11 @@ OPTIONS
 	Replace an existing tag with the given name (instead of failing)
 
 -d::
+--delete::
 	Delete existing tags with the given names.
 
 -v::
+--verify::
 	Verify the gpg signature of the given tag names.
 
 -n<num>::
@@ -69,6 +74,7 @@ OPTIONS
 	If the tag is not annotated, the commit message is displayed instead.
 
 -l <pattern>::
+--list <pattern>::
 	List tags with names that match the given pattern (or all if no
 	pattern is given).  Running "git tag" without arguments also
 	lists all tags. The pattern is a shell wildcard (i.e., matched
@@ -79,6 +85,7 @@ OPTIONS
 	Only list tags which contain the specified commit.
 
 -m <msg>::
+--message=<msg>::
 	Use the given tag message (instead of prompting).
 	If multiple `-m` options are given, their values are
 	concatenated as separate paragraphs.
@@ -86,6 +93,7 @@ OPTIONS
 	is given.
 
 -F <file>::
+--file=<file>::
 	Take the tag message from the given file.  Use '-' to
 	read the message from the standard input.
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
diff --git a/builtin/tag.c b/builtin/tag.c
index 667515e..9d89616 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -429,21 +429,21 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct msg_arg msg = { 0, STRBUF_INIT };
 	struct commit_list *with_commit = NULL;
 	struct option options[] = {
-		OPT_BOOLEAN('l', NULL, &list, "list tag names"),
+		OPT_BOOLEAN('l', "list", &list, "list tag names"),
 		{ OPTION_INTEGER, 'n', NULL, &lines, "n",
 				"print <n> lines of each tag message",
 				PARSE_OPT_OPTARG, NULL, 1 },
-		OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
-		OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
+		OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
+		OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
 
 		OPT_GROUP("Tag creation options"),
-		OPT_BOOLEAN('a', NULL, &annotate,
+		OPT_BOOLEAN('a', "annotate", &annotate,
 					"annotated tag, needs a message"),
-		OPT_CALLBACK('m', NULL, &msg, "message",
+		OPT_CALLBACK('m', "message", &msg, "message",
 			     "tag message", parse_msg_arg),
-		OPT_FILENAME('F', NULL, &msgfile, "read message from file"),
-		OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
-		OPT_STRING('u', NULL, &keyid, "key-id",
+		OPT_FILENAME('F', "file", &msgfile, "read message from file"),
+		OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
+		OPT_STRING('u', "local-user", &keyid, "key-id",
 					"use another key to sign the tag"),
 		OPT__FORCE(&force, "replace the tag if exists"),
 
-- 
1.7.6.845.gc3c05

[PATCHv3 4/5] branch: introduce --list option

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:56

Currently, there is no way to invoke the list mode explicitly, without
giving -v to force verbose output.

Introduce a --list option which invokes the list mode. This will be
beneficial for invoking list mode with pattern matching, which otherwise
would be interpreted as branch creation.

Along with --list, test also combinations of existing options.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    9 +++++++--
 builtin/branch.c             |   12 +++++++++---
 t/t3200-branch.sh            |   32 ++++++++++++++++++++++++++++++++
 t/t3203-branch-output.sh     |   14 ++++++++++++++
 4 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 4c64ac9..ac278fb 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
-	[-v [--abbrev=<length> | --no-abbrev]]
+	[--list] [-v [--abbrev=<length> | --no-abbrev]]
 	[(--merged | --no-merged | --contains) [<commit>]]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -20,7 +20,8 @@ DESCRIPTION
 
 With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
-branches to be listed, and option `-a` shows both.
+branches to be listed, and option `-a` shows both. This list mode is also
+activated by the `--list` and `-v` options (see below).
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
@@ -110,11 +111,15 @@ OPTIONS
 --all::
 	List both remote-tracking branches and local branches.
 
+--list::
+	Activate the list mode.
+
 -v::
 --verbose::
 	Show sha1 and commit subject line for each head, along with
 	relationship to upstream branch (if any). If given twice, print
 	the name of the upstream branch, as well.
+	`--list` is implied by all verbosity options.
 
 --abbrev=<length>::
 	Alter the sha1's minimum display length in the output listing.
diff --git a/builtin/branch.c b/builtin/branch.c
index 94e41ae..4a33b07 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -608,7 +608,7 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
 
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
-	int delete = 0, rename = 0, force_create = 0;
+	int delete = 0, rename = 0, force_create = 0, list = 0;
 	int verbose = 0, abbrev = -1, detached = 0;
 	int reflog = 0;
 	enum branch_track track;
@@ -647,6 +647,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
 		OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
 		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
+		OPT_BOOLEAN(0, "list", &list, "list branch names"),
 		OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
 		OPT__FORCE(&force_create, "force creation (when already exists)"),
 		{
@@ -686,7 +687,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
 			     0);
-	if (!!delete + !!rename + !!force_create > 1)
+
+	if (!delete && !rename && !force_create &&
+	    (argc == 0 || (verbose && argc)))
+		list = 1;
+
+	if (!!delete + !!rename + !!force_create + !!list > 1)
 		usage_with_options(builtin_branch_usage, options);
 
 	if (abbrev == -1)
@@ -694,7 +700,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (argc == 0)
+	else if (list)
 		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index cb6458d..931373c 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -98,6 +98,38 @@ test_expect_success 'git branch -m q r/q should fail when r exists' '
 	test_must_fail git branch -m q r/q
 '
 
+test_expect_success 'git branch -v -d t should work' '
+	git branch t &&
+	test .git/refs/heads/t &&
+	git branch -v -d t &&
+	test ! -f .git/refs/heads/t
+'
+
+test_expect_success 'git branch -v -m t s should work' '
+	git branch t &&
+	test .git/refs/heads/t &&
+	git branch -v -m t s &&
+	test ! -f .git/refs/heads/t &&
+	test -f .git/refs/heads/s &&
+	git branch -d s
+'
+
+test_expect_success 'git branch -m -d t s should fail' '
+	git branch t &&
+	test .git/refs/heads/t &&
+	test_must_fail git branch -m -d t s &&
+	git branch -d t &&
+	test ! -f .git/refs/heads/t
+'
+
+test_expect_success 'git branch --list -d t should fail' '
+	git branch t &&
+	test .git/refs/heads/t &&
+	test_must_fail git branch --list -d t &&
+	git branch -d t &&
+	test ! -f .git/refs/heads/t
+'
+
 test_expect_success 'git branch -M foo bar should fail when bar is checked out' '
 	git branch bar &&
 	git checkout -b foo &&
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 6b7c118..61e095c 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -32,6 +32,20 @@ test_expect_success 'git branch shows local branches' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --list shows local branches' '
+	git branch --list >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+EOF
+test_expect_success 'git branch --list pattern shows matching local branches' '
+	git branch --list branch* >actual &&
+	test_cmp expect actual
+'
+
 cat >expect <<'EOF'
   origin/HEAD -> origin/branch-one
   origin/branch-one
-- 
1.7.6.845.gc3c05

[PATCHv3 3/5] git-branch: introduce missing long forms for the options

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:56

Long forms are better to memorize and more reliably uniform across
commands.

Names follow precedents, e.g. "git log --remotes".

Signed-off-by: Michael J Gruber <redacted>
---
I'm somewhat torn between --move and --rename for -m. We have no real precedent
besides "git remote rename".

I left out -M and -D because I feel they should really be -m -f resp. -d -f.
---
 Documentation/git-branch.txt |    5 +++++
 builtin/branch.c             |   10 +++++-----
 2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 507b8d0..4c64ac9 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -64,6 +64,7 @@ way to clean up all obsolete remote-tracking branches.
 OPTIONS
 -------
 -d::
+--delete::
 	Delete a branch. The branch must be fully merged in its
 	upstream branch, or in `HEAD` if no upstream was set with
 	`--track` or `--set-upstream`.
@@ -72,6 +73,7 @@ OPTIONS
 	Delete a branch irrespective of its merged status.
 
 -l::
+--create-reflog::
 	Create the branch's reflog.  This activates recording of
 	all changes made to the branch ref, enabling use of date
 	based sha1 expressions such as "<branchname>@\{yesterday}".
@@ -84,6 +86,7 @@ OPTIONS
 	already. Without `-f` 'git branch' refuses to change an existing branch.
 
 -m::
+--move::
 	Move/rename a branch and the corresponding reflog.
 
 -M::
@@ -100,9 +103,11 @@ OPTIONS
 	Same as `--color=never`.
 
 -r::
+--remotes::
 	List or delete (if used with -d) the remote-tracking branches.
 
 -a::
+--all::
 	List both remote-tracking branches and local branches.
 
 -v::
diff --git a/builtin/branch.c b/builtin/branch.c
index aa705a0..94e41ae 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -624,7 +624,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
 			BRANCH_TRACK_OVERRIDE),
 		OPT__COLOR(&branch_use_color, "use colored output"),
-		OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
+		OPT_SET_INT('r', "remotes",     &kinds, "act on remote-tracking branches",
 			REF_REMOTE_BRANCH),
 		{
 			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
@@ -641,13 +641,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT__ABBREV(&abbrev),
 
 		OPT_GROUP("Specific git-branch actions:"),
-		OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
+		OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
 			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
-		OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1),
+		OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1),
 		OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
-		OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
+		OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
 		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
-		OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
+		OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
 		OPT__FORCE(&force_create, "force creation (when already exists)"),
 		{
 			OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
-- 
1.7.6.845.gc3c05

[PATCHv3 5/5] branch: allow pattern arguments

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:56

Allow pattern arguments for the list mode just like for git tag -l.

Signed-off-by: Michael J Gruber <redacted>
---
 Documentation/git-branch.txt |    8 ++++++--
 builtin/branch.c             |   24 +++++++++++++++++++++---
 t/t3203-branch-output.sh     |   10 ++++++++++
 3 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index ac278fb..2b8bc84 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
 	[--list] [-v [--abbrev=<length> | --no-abbrev]]
-	[(--merged | --no-merged | --contains) [<commit>]]
+	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
 'git branch' (-d | -D) [-r] <branchname>...
@@ -22,6 +22,9 @@ With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
 branches to be listed, and option `-a` shows both. This list mode is also
 activated by the `--list` and `-v` options (see below).
+<pattern> restricts the output to matching branches, the pattern is a shell
+wildcard (i.e., matched using fnmatch(3))
+Multiple patterns may be given; if any of them matches, the tag is shown.
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
@@ -112,7 +115,8 @@ OPTIONS
 	List both remote-tracking branches and local branches.
 
 --list::
-	Activate the list mode.
+	Activate the list mode. `git branch <pattern>` would try to create a branch,
+	use `git branch --list <pattern>` to list matching branches.
 
 -v::
 --verbose::
diff --git a/builtin/branch.c b/builtin/branch.c
index 4a33b07..e6bef49 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -260,9 +260,22 @@ static char *resolve_symref(const char *src, const char *prefix)
 
 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 (!fnmatch(*pattern, refname, 0))
+			return 1;
+		pattern++;
+	}
+	return 0;
+}
+
 static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
 	struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
@@ -297,6 +310,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
 	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(sha1, 1);
@@ -492,7 +508,7 @@ static void show_detached(struct ref_list *ref_list)
 	}
 }
 
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
 {
 	int i;
 	struct append_ref_cb cb;
@@ -506,6 +522,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 	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);
 	if (merge_filter != NO_FILTER) {
@@ -523,7 +540,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
 	detached = (detached && (kinds & REF_LOCAL_BRANCH));
-	if (detached)
+	if (detached && match_patterns(pattern, "HEAD"))
 		show_detached(&ref_list);
 
 	for (i = 0; i < ref_list.index; i++) {
@@ -701,7 +718,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
 	else if (list)
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+		return print_ref_list(kinds, detached, verbose, abbrev,
+				      with_commit, argv);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
 	else if (rename && (argc == 2))
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 61e095c..f2b294b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -81,6 +81,16 @@ test_expect_success 'git branch -v shows branch summaries' '
 '
 
 cat >expect <<'EOF'
+two
+one
+EOF
+test_expect_success 'git branch -v pattern shows branch summaries' '
+	git branch -v branch* >tmp &&
+	awk "{print \$NF}" <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
 * (no branch)
   branch-one
   branch-two
-- 
1.7.6.845.gc3c05

Re: [PATCHv3 5/5] branch: allow pattern arguments

From: Michael Schubert <hidden>
Date: 2016-06-15 22:51:59

On 08/28/2011 04:54 PM, Michael J Gruber wrote:
+static int match_patterns(const char **pattern, const char *refname)
+{
+	if (!*pattern)
+		return 1; /* no pattern always matches */
+	while (*pattern) {
+		if (!fnmatch(*pattern, refname, 0))
+			return 1;
+		pattern++;
+	}
+	return 0;
+}
Nitpick: maybe builtin/branch.c and builtin/tag.c could share match_pattern().?

A second thought: the printed "remotes/" prefix could be confusing for users,
since it seems to be part of the refname. For example:

$ git branch -a
* master
  maint
  man
  remotes/origin/master
  remotes/origin/maint
  remotes/origin/man

$ git branch -a --list remotes/origin*
[no output]

but

$ git branch -a --list origin*
  remotes/origin/master
  remotes/origin/maint
  remotes/origin/man

(Sorry in case I missed that) What's the reason you decided --list to show local
branches only? Maybe --list could show all refnames without any extra prefix.?

Thanks.

Re: [PATCHv3 5/5] branch: allow pattern arguments

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:59

Michael Schubert venit, vidit, dixit 06.09.2011 15:10:
On 08/28/2011 04:54 PM, Michael J Gruber wrote:
quoted
+static int match_patterns(const char **pattern, const char *refname)
+{
+	if (!*pattern)
+		return 1; /* no pattern always matches */
+	while (*pattern) {
+		if (!fnmatch(*pattern, refname, 0))
+			return 1;
+		pattern++;
+	}
+	return 0;
+}
Nitpick: maybe builtin/branch.c and builtin/tag.c could share match_pattern().?
I think all commands which list refs should share code - for-each-ref,
branch, tag, replace. I suggest to unify log and for-each-ref-formats
first, the unify ref listers.
A second thought: the printed "remotes/" prefix could be confusing for users,
since it seems to be part of the refname. For example:

$ git branch -a
* master
  maint
  man
  remotes/origin/master
  remotes/origin/maint
  remotes/origin/man

$ git branch -a --list remotes/origin*
[no output]

but

$ git branch -a --list origin*
  remotes/origin/master
  remotes/origin/maint
  remotes/origin/man

(Sorry in case I missed that) What's the reason you decided --list to show local
branches only? Maybe --list could show all refnames without any extra prefix.?
I didn't decide anything here. "git branch" lists all local branches and
has been doing that forever, and "--list" is and should be a noop
without a pattern. That was the idea: -r/-a select refnames,
--list/-v/-vv activate the list mode(s), and the pattern is matched
against the selected refs.

(This is one reason why I didn't want it to be named --glob - the
pattern is a filter, not a selector.)

That being said, I find the fact that "-a --list remotes/origin/*" does
not match anything somewhat disconcerting, although it fits in with the
general idea of "git branch": it deals with branch names, not ref names.
Have you ever tried to delete a remote branch?

git branch -r -d origin/maint # workee
git branch -r -d remotes/origin/maint # no workee

Without -r, it doesn't work either.

So, the way it is it fits in with how "git branch" works, and is
different from for-each-ref (and rev-list --glob). I don't like it,
because you may have a local branch origin/maint (i.e.
refs/heads/origin/maint) which you can't distinguish from the remote
branch by a pattern, only by using or not using -r. But it's the same as
with "git branch -d", really.

Michael

Re: [PATCHv3 5/5] branch: allow pattern arguments

From: Sverre Rabbelier <hidden>
Date: 2016-06-15 22:51:59

Heya,

On Tue, Sep 6, 2011 at 16:21, Michael J Gruber [off-list ref] wrote:
Have you ever tried to delete a remote branch?

git branch -r -d origin/maint # workee
git branch -r -d remotes/origin/maint # no workee

Without -r, it doesn't work either.
Heh, I read this as "worktree" / "no worktree" at least 4 times.

-- 
Cheers,

Sverre Rabbelier

Re: [PATCHv3 5/5] branch: allow pattern arguments

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:51:59

Sverre Rabbelier venit, vidit, dixit 06.09.2011 16:26:
Heya,

On Tue, Sep 6, 2011 at 16:21, Michael J Gruber [off-list ref] wrote:
quoted
Have you ever tried to delete a remote branch?

git branch -r -d origin/maint # workee
git branch -r -d remotes/origin/maint # no workee

Without -r, it doesn't work either.
Heh, I read this as "worktree" / "no worktree" at least 4 times.
Yes, our require_work_tree should really be renamed to

no_worktree_no_workee

The beauty of pidgin english ;)

Michael
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help