threads / rfc / 27997

[RFC] branch: list branches by single remote

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

## tl;dr

77 messages between Aug 2, 2011 and Sep 13, 2011.

replies: 76people: 6as markdown or json

Michael Schubert· Aug 2, 2011, 17:17 UTC · lore

I've always missed some option for "git branch" to limit the output to a single remote. What's the right place to filter the output? The code below doesn't look very smart..

---
 builtin/branch.c |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index 3142daa..22e6be2 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -17,7 +17,7 @@
 #include "revision.h"
 
 static const char * const builtin_branch_usage[] = {
-	"git branch [options] [-r | -a] [--merged | --no-merged]",
+	"git branch [options] [-r | -a] [-R <remote>] [--merged | --no-merged]",
 	"git branch [options] [-l] [-f] <branchname> [<start-point>]",
 	"git branch [options] [-r] (-d | -D) <branchname>...",
 	"git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
@@ -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 *remote;
 	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->remote && strncmp(cb->remote, refname, strlen(cb->remote)))
+		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 *only)
 {
 	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.remote = only;
 	cb.ret = 0;
 	for_each_rawref(append_ref, &cb);
 	if (merge_filter != NO_FILTER) {
@@ -618,6 +623,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	enum branch_track track;
 	int kinds = REF_LOCAL_BRANCH;
 	struct commit_list *with_commit = NULL;
+	char *single_remote = NULL;
 
 	struct option options[] = {
 		OPT_GROUP("Generic options"),
@@ -647,6 +653,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_GROUP("Specific git-branch actions:"),
 		OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
 			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
+		OPT_STRING('R', NULL, &single_remote, "remote", "list only branches by remote"),
 		OPT_BIT('d', NULL, &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),
@@ -696,10 +703,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	if (!!delete + !!rename + !!force_create > 1)
 		usage_with_options(builtin_branch_usage, options);
 
+	if (single_remote)
+		kinds = REF_REMOTE_BRANCH;
+
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
 	else if (argc == 0)
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+		return print_ref_list(kinds, detached, verbose, abbrev, with_commit, single_remote);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
 	else if (rename && (argc == 2))
-- 
1.7.6.396.ge0613.dirty
Jeff King· Aug 4, 2011, 04:06 UTC · re: Michael Schubert · lore

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

On Tue, Aug 02, 2011 at 07:17:38PM +0200, Michael Schubert wrote:
Show 7 quoted lines
> @@ -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->remote && strncmp(cb->remote, refname, strlen(cb->remote)))
> +		return 0;
> +

This isn't right. You are assuming that a remote called "foo" will have all of its branches in refs/remotes/foo. That's true under the default configuration, but technically speaking, the remote tracking branches of "foo" are defined by the right-hand side of foo's fetch refspecs.

So I think you want something more like this:
  int i;
  struct remote *remote = remote_get("foo");
  for (i = 0; i < remote->fetch_refspec_nr; i++) {
          struct refspec *rs = remote->fetch + i;
          /* if it's not a wildcard, then take the rhs verbatim */
          if (!rs->pattern)
                  append_ref(rs->dst);
          else {
                  /* it's a wildcard like refs/remotes/foo/*; glob in
                   * the ref list appropriately. Or we can cheat, noting
                   * that git's only allowed wildcard is "/*" at the
                   * end, and do this: */
                  char *prefix = xstrndup(rs->dst, strlen(rs->dst) - 1);
                  for_each_ref_in(prefix, append_ref, NULL);
          }
  }

instead of the call to "for_each_rawref(append_ref, ...)" that would normally be used. You could even pretty easily allow selecting branches from multiple remotes, too, though I don't know if that is actually useful.

-Peff
Michael J Gruber· Aug 16, 2011, 13:37 UTC · re: Jeff King · lore

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

Jeff King venit, vidit, dixit 04.08.2011 06:06:
Show 14 quoted lines
> On Tue, Aug 02, 2011 at 07:17:38PM +0200, Michael Schubert wrote:
> 
>> @@ -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->remote && strncmp(cb->remote, refname, strlen(cb->remote)))
>> +		return 0;
>> +
> 
> This isn't right. You are assuming that a remote called "foo" will have
> all of its branches in refs/remotes/foo. That's true under the default
> configuration, but technically speaking, the remote tracking branches of
> "foo" are defined by the right-hand side of foo's fetch refspecs.
You are 100% right here, but...
> So I think you want something more like this:
...the op still might want to filter simply by the remote name.
Reminds me that I have to resurrect my patterns-with-branches series....
Michael
Michael Schubert· Aug 16, 2011, 14:19 UTC · re: Michael J Gruber · lore

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

On 08/16/2011 03:37 PM, Michael J Gruber wrote:
Show 21 quoted lines
> Jeff King venit, vidit, dixit 04.08.2011 06:06:
>> On Tue, Aug 02, 2011 at 07:17:38PM +0200, Michael Schubert wrote:
>>
>>> @@ -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->remote && strncmp(cb->remote, refname, strlen(cb->remote)))
>>> +		return 0;
>>> +
>>
>> This isn't right. You are assuming that a remote called "foo" will have
>> all of its branches in refs/remotes/foo. That's true under the default
>> configuration, but technically speaking, the remote tracking branches of
>> "foo" are defined by the right-hand side of foo's fetch refspecs.
> 
> You are 100% right here, but...
> 
>> So I think you want something more like this:
> 
> ...the op still might want to filter simply by the remote name.
There's an interesting discussion related to the subject:
http://thread.gmane.org/gmane.comp.version-control.git/178960
Jeff King· Aug 16, 2011, 15:14 UTC · re: Michael J Gruber · lore

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

On Tue, Aug 16, 2011 at 03:37:33PM +0200, Michael J Gruber wrote:
Show 10 quoted lines
> > This isn't right. You are assuming that a remote called "foo" will have
> > all of its branches in refs/remotes/foo. That's true under the default
> > configuration, but technically speaking, the remote tracking branches of
> > "foo" are defined by the right-hand side of foo's fetch refspecs.
> 
> You are 100% right here, but...
> 
> > So I think you want something more like this:
> 
> ...the op still might want to filter simply by the remote name.

That is a perfectly reasonable approach. It just should be called "--glob" or something, and not "remote". git-tag allows patterns to an explicit "tag -l", but "-l" is already taken for git-branch.

> Reminds me that I have to resurrect my patterns-with-branches series....

Please do. I think it's a much simpler and more versatile solution to the same problem.

-Peff
Michael Schubert· Aug 24, 2011, 15:14 UTC · re: Jeff King · lore

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

On 08/16/2011 05:14 PM, Jeff King wrote:
Show 16 quoted lines
> On Tue, Aug 16, 2011 at 03:37:33PM +0200, Michael J Gruber wrote:
> 
>>> This isn't right. You are assuming that a remote called "foo" will have
>>> all of its branches in refs/remotes/foo. That's true under the default
>>> configuration, but technically speaking, the remote tracking branches of
>>> "foo" are defined by the right-hand side of foo's fetch refspecs.
>>
>> You are 100% right here, but...
>>
>>> So I think you want something more like this:
>>
>> ...the op still might want to filter simply by the remote name.
> 
> That is a perfectly reasonable approach. It just should be called
> "--glob" or something, and not "remote".  git-tag allows patterns to an
> explicit "tag -l", but "-l" is already taken for git-branch.
As suggested, I've just called it "--glob" for now.
--- 8< ---
Subject: [PATCH] branch: add option "glob" to filter listed branches

When calling "git branch" with either "-r" or "-a", there is no way to further limit the output. Add an option "--glob=<pattern>" to allow limiting the output to matching branch names.

Signed-off-by: Michael Schubert <mschub@elegosoft.com>
---
 Documentation/git-branch.txt |    8 ++++++--
 builtin/branch.c             |   13 ++++++++++---
 2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index c50f189..5de3c79 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -8,7 +8,7 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git branch' [--color[=<when>] | --no-color] [-r | -a]
+'git branch' [--color[=<when>] | --no-color] [(-r | -a) [--glob=<pattern>]]
 	[-v [--abbrev=<length> | --no-abbrev]]
 	[(--merged | --no-merged | --contains) [<commit>]]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
@@ -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, option `-a` shows both and `--glob` limits the
+output to branches matching <pattern>.
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
@@ -105,6 +106,9 @@ OPTIONS
 -a::
 	List both remote-tracking branches and local branches.
 
+--glob=<pattern>::
+	List only branches matching pattern.
+
 -v::
 --verbose::
 	Show sha1 and commit subject line for each head, along with
diff --git a/builtin/branch.c b/builtin/branch.c
index 3142daa..74730ad 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -17,7 +17,7 @@
 #include "revision.h"
 
 static const char * const builtin_branch_usage[] = {
-	"git branch [options] [-r | -a] [--merged | --no-merged]",
+	"git branch [options] [(-r | -a) [--glob=<pattern>]] [--merged | --no-merged]",
 	"git branch [options] [-l] [-f] <branchname> [<start-point>]",
 	"git branch [options] [-r] (-d | -D) <branchname>...",
 	"git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
@@ -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 *glob;
 	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->glob && fnmatch(cb->glob, 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 *glob)
 {
 	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.glob = glob;
 	cb.ret = 0;
 	for_each_rawref(append_ref, &cb);
 	if (merge_filter != NO_FILTER) {
@@ -618,6 +623,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	enum branch_track track;
 	int kinds = REF_LOCAL_BRANCH;
 	struct commit_list *with_commit = NULL;
+	char *glob = NULL;
 
 	struct option options[] = {
 		OPT_GROUP("Generic options"),
@@ -647,6 +653,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_GROUP("Specific git-branch actions:"),
 		OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
 			REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
+		OPT_STRING(0, "glob", &glob, "pattern", "list only branches matching the pattern"),
 		OPT_BIT('d', NULL, &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),
@@ -699,7 +706,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)
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+		return print_ref_list(kinds, detached, verbose, abbrev, with_commit, glob);
 	else if (rename && (argc == 1))
 		rename_branch(head, argv[0], rename > 1);
 	else if (rename && (argc == 2))
-- 
1.7.6.577.g8d918.dirty
Michael J Gruber· Aug 24, 2011, 15:37 UTC · re: Michael Schubert · lore

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

Michael Schubert venit, vidit, dixit 24.08.2011 17:14:
Show 19 quoted lines
> On 08/16/2011 05:14 PM, Jeff King wrote:
>> On Tue, Aug 16, 2011 at 03:37:33PM +0200, Michael J Gruber wrote:
>>
>>>> This isn't right. You are assuming that a remote called "foo" will have
>>>> all of its branches in refs/remotes/foo. That's true under the default
>>>> configuration, but technically speaking, the remote tracking branches of
>>>> "foo" are defined by the right-hand side of foo's fetch refspecs.
>>>
>>> You are 100% right here, but...
>>>
>>>> So I think you want something more like this:
>>>
>>> ...the op still might want to filter simply by the remote name.
>>
>> That is a perfectly reasonable approach. It just should be called
>> "--glob" or something, and not "remote".  git-tag allows patterns to an
>> explicit "tag -l", but "-l" is already taken for git-branch.
> 
> 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. You can follow it if you like:

http://repo.or.cz/w/git/mjg.git/shortlog/refs/heads/branch-list-pattern

Also, again: git branch is much more like git tag than it is like git log, so I think the pattern matching options should be, too.

Michael
Michael Schubert· Aug 24, 2011, 16:02 UTC · re: Michael J Gruber · lore

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

On 08/24/2011 05:37 PM, Michael J Gruber wrote:
Show 26 quoted lines
> Michael Schubert venit, vidit, dixit 24.08.2011 17:14:
>> On 08/16/2011 05:14 PM, Jeff King wrote:
>>> On Tue, Aug 16, 2011 at 03:37:33PM +0200, Michael J Gruber wrote:
>>>
>>>>> This isn't right. You are assuming that a remote called "foo" will have
>>>>> all of its branches in refs/remotes/foo. That's true under the default
>>>>> configuration, but technically speaking, the remote tracking branches of
>>>>> "foo" are defined by the right-hand side of foo's fetch refspecs.
>>>>
>>>> You are 100% right here, but...
>>>>
>>>>> So I think you want something more like this:
>>>>
>>>> ...the op still might want to filter simply by the remote name.
>>>
>>> That is a perfectly reasonable approach. It just should be called
>>> "--glob" or something, and not "remote".  git-tag allows patterns to an
>>> explicit "tag -l", but "-l" is already taken for git-branch.
>>
>> 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?
While I just haven't seen this,
> As I've mentioned, I've been in the middle of polishing that up. You can
> follow it if you like:
> 
> http://repo.or.cz/w/git/mjg.git/shortlog/refs/heads/branch-list-pattern

I thought I just send it out not knowing you're actually into resurrecting the "patterns-with-branches series".

Thanks.
Junio C Hamano· Aug 24, 2011, 18:34 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 8 quoted lines
>> 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).

Thiago Farina· Aug 25, 2011, 02:31 UTC · re: Junio C Hamano · lore

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

On Wed, Aug 24, 2011 at 3:34 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 7 quoted lines
> [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)?

Junio C Hamano· Aug 25, 2011, 18:02 UTC · re: Thiago Farina · lore

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

Thiago Farina <tfransosi@gmail.com> writes:
Show 11 quoted lines
> On Wed, Aug 24, 2011 at 3:34 PM, Junio C Hamano <gitster@pobox.com> 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)?

If any of the above expires entries by default unless explicit actions are taken, I am personally fine to go with it.

Michael J Gruber· Aug 25, 2011, 08:29 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 24.08.2011 20:34:
Show 19 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
>>> 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.

Show 19 quoted lines
> 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
Michael J Gruber· Aug 25, 2011, 08:30 UTC · re: Michael J Gruber · lore

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

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
Jeff King· Aug 25, 2011, 17:53 UTC · re: Michael J Gruber · lore

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

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".

Show 8 quoted lines
> 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.

Show 11 quoted lines
> 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"?

Show 7 quoted lines
> -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
Michael J Gruber· Aug 26, 2011, 08:30 UTC · re: Jeff King · lore

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

Jeff King venit, vidit, dixit 25.08.2011 19:53:
Show 20 quoted lines
> 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".
;)
Thanks for doing the search.
Show 15 quoted lines
>> 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.

Show 21 quoted lines
>> "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"?

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".

Show 12 quoted lines
>> -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.
Show 11 quoted lines
> 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
Junio C Hamano· Aug 26, 2011, 16:55 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 8 quoted lines
> Jeff King venit, vidit, dixit 25.08.2011 19:53:
>> On Thu, Aug 25, 2011 at 10:30:16AM +0200, Michael J Gruber wrote:
>> 
>>> 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.
If it is not found, the usual action is create it, no?
Show 13 quoted lines
>>> 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".

As we use fnmatch() and not match_pathspec() for this pattern matching, "git branch peff/" will not list all the topics under the peff/ hierarchy (your example "git branch peff/\*" would be the way), but I would imagine that we may someday want to update it to allow the leading path match here. And at that point, distinction between

	git branch peff  ;# to create a "peff" branch
        git branch peff/ ;# to list "peff/" branches, as "peff/" itself is
        		 ;# an invalid branch name and your auto listing
                         ;# heuristic kicks in

while it might be very useful for experts, becomes too subtle and would confuse new people. We should instead require an explicit -l/--list, and not use the auto listing heuristics (it is fine for -v to imply -l).

Jeff King· Sep 7, 2011, 19:53 UTC · re: Junio C Hamano · lore

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

On Fri, Aug 26, 2011 at 09:55:37AM -0700, Junio C Hamano wrote:
Show 14 quoted lines
> As we use fnmatch() and not match_pathspec() for this pattern matching,
> "git branch peff/" will not list all the topics under the peff/ hierarchy
> (your example "git branch peff/\*" would be the way), but I would imagine
> that we may someday want to update it to allow the leading path match
> here. And at that point, distinction between
> 
> 	git branch peff  ;# to create a "peff" branch
>         git branch peff/ ;# to list "peff/" branches, as "peff/" itself is
>         		 ;# an invalid branch name and your auto listing
>                          ;# heuristic kicks in
> 
> while it might be very useful for experts, becomes too subtle and would
> confuse new people. We should instead require an explicit -l/--list, and
> not use the auto listing heuristics (it is fine for -v to imply -l).

Sorry, I'm atrociously behind on reviewing this topic. But FWIW, I completely agree with this. Detecting invalid branch formats is much too subtle and error prone, and we are better off making a short and easy-to-type version of "--list".

-Peff
Michael J Gruber· Sep 8, 2011, 09:20 UTC · re: Jeff King · lore

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

Jeff King venit, vidit, dixit 07.09.2011 21:53:
Show 21 quoted lines
> On Fri, Aug 26, 2011 at 09:55:37AM -0700, Junio C Hamano wrote:
> 
>> As we use fnmatch() and not match_pathspec() for this pattern matching,
>> "git branch peff/" will not list all the topics under the peff/ hierarchy
>> (your example "git branch peff/\*" would be the way), but I would imagine
>> that we may someday want to update it to allow the leading path match
>> here. And at that point, distinction between
>>
>> 	git branch peff  ;# to create a "peff" branch
>>         git branch peff/ ;# to list "peff/" branches, as "peff/" itself is
>>         		 ;# an invalid branch name and your auto listing
>>                          ;# heuristic kicks in
>>
>> while it might be very useful for experts, becomes too subtle and would
>> confuse new people. We should instead require an explicit -l/--list, and
>> not use the auto listing heuristics (it is fine for -v to imply -l).
> 
> Sorry, I'm atrociously behind on reviewing this topic. But FWIW, I
> completely agree with this. Detecting invalid branch formats is much too
> subtle and error prone, and we are better off making a short and
> easy-to-type version of "--list".
We all agreed on that, you didn't miss anything ;)
Michael
Michael J Gruber· Aug 26, 2011, 14:05 UTC · re: Jeff King · lore

[PATCHv2 0/5] patterns for branch list

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
Michael J Gruber· Aug 28, 2011, 14:54 UTC · re: Michael J Gruber · lore

[PATCHv3 0/5] patterns for branch list

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
Michael J Gruber· Aug 28, 2011, 14:54 UTC · re: Michael J Gruber · lore

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

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

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 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
Michael J Gruber· Aug 28, 2011, 14:54 UTC · re: Michael J Gruber · lore

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

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 <git@drmicha.warpmail.net>
---
 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
Michael J Gruber· Aug 28, 2011, 14:54 UTC · re: Michael J Gruber · lore

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

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 <git@drmicha.warpmail.net>
---
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
Michael J Gruber· Aug 28, 2011, 14:54 UTC · re: Michael J Gruber · lore

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

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 <git@drmicha.warpmail.net>
---
 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
Junio C Hamano· Aug 29, 2011, 05:55 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 13 quoted lines
> +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
> +'
Does this one exclude the "* master" entry at this stage in the series?
Michael J Gruber· Aug 29, 2011, 06:35 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 29.08.2011 07:55:
Show 17 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
>> +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
>> +'
> 
> Does this one exclude the "* master" entry at this stage in the series?

Sheesh, that rebasing mistake (flipping the order of 4 and 5) was present in v2 already. Only recently I learned about rebase-i's "exec" and have to make it a habit to use it for step-by-step series testing.

Sorry, I'll send out v3 a bit later when I see that there are no more comments (and a full step-by-step series test has passed).

Michael
Junio C Hamano· Aug 29, 2011, 06:51 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
> Sheesh, that rebasing mistake (flipping the order of 4 and 5) was
> present in v2 already. Only recently I learned about rebase-i's "exec"
> and have to make it a habit to use it for step-by-step series testing.

That's Ok. Locally moved the offending part of the test to 5 when applying.

Thanks.
Michael J Gruber· Aug 28, 2011, 14:54 UTC · re: Michael J Gruber · lore

[PATCHv3 5/5] branch: allow pattern arguments

Allow pattern arguments for the list mode just like for git tag -l.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 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
Michael Schubert· Sep 6, 2011, 13:10 UTC · re: Michael J Gruber · lore

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

On 08/28/2011 04:54 PM, Michael J Gruber wrote:
Show 11 quoted lines
> +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.
Michael J Gruber· Sep 6, 2011, 14:21 UTC · re: Michael Schubert · lore

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

Michael Schubert venit, vidit, dixit 06.09.2011 15:10:
Show 14 quoted lines
> 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().?

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.

Show 23 quoted lines
> 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
Sverre Rabbelier· Sep 6, 2011, 14:26 UTC · re: Michael J Gruber · lore

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

Heya,
On Tue, Sep 6, 2011 at 16:21, Michael J Gruber <git@drmicha.warpmail.net> wrote:
Show 6 quoted lines
> 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
Michael J Gruber· Sep 6, 2011, 16:11 UTC · re: Sverre Rabbelier · lore

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

Sverre Rabbelier venit, vidit, dixit 06.09.2011 16:26:
Show 12 quoted lines
> Heya,
> 
> On Tue, Sep 6, 2011 at 16:21, Michael J Gruber <git@drmicha.warpmail.net> 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.
> 
Yes, our require_work_tree should really be renamed to
no_worktree_no_workee
The beauty of pidgin english ;)
Michael
Michael J Gruber· Aug 26, 2011, 14:05 UTC · re: Jeff King · lore

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

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

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 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
Michael J Gruber· Aug 26, 2011, 14:05 UTC · re: Jeff King · lore

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

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 <git@drmicha.warpmail.net>
---
 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
Junio C Hamano· Aug 26, 2011, 17:11 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
> Long forms are better to memoize, and more reliably uniform across
> commands.

I think people "memorize" and machines "memoize"; machines will do so just fine without long forms, but I think you are talking about helping people.

The part after ", and" lacks a verb, making it a non-sentence.
Show 5 quoted lines
> Design notes:
>
> -u,--local-user is named following the analogous gnupg option.
>
> -l,--list is not an argument taking option but a mode switch.
Ok.
The remainder looks good. Thanks.
Michael J Gruber· Aug 28, 2011, 14:03 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 26.08.2011 19:11:
Show 7 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
>> Long forms are better to memoize, and more reliably uniform across
>> commands.
> 
> I think people "memorize" and machines "memoize"; machines will do so just
> fine without long forms, but I think you are talking about helping people.
Yep.
> The part after ", and" lacks a verb, making it a non-sentence.

They are better to memorize, and they are more reliably uniform... Maybe drop the comma?

Show 10 quoted lines
> 
>> Design notes:
>>
>> -u,--local-user is named following the analogous gnupg option.
>>
>> -l,--list is not an argument taking option but a mode switch.
> 
> Ok.
> 
> The remainder looks good. Thanks.
Michael J Gruber· Aug 26, 2011, 14:05 UTC · re: Jeff King · lore

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

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

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
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
Junio C Hamano· Aug 26, 2011, 17:13 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 6 quoted lines
> @@ -100,9 +103,11 @@ OPTIONS
>  	Same as `--color=never`.
>  
>  -r::
> +--remotes::
>  	List or delete (if used with -d) the remote-tracking branches.
I am not sure if this should be "--remoteS".
Michael J Gruber· Aug 28, 2011, 14:05 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 26.08.2011 19:13:
Show 10 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
>> @@ -100,9 +103,11 @@ OPTIONS
>>  	Same as `--color=never`.
>>  
>>  -r::
>> +--remotes::
>>  	List or delete (if used with -d) the remote-tracking branches.
> 
> I am not sure if this should be "--remoteS".
like "git log --remotes".
Michael J Gruber· Aug 26, 2011, 14:05 UTC · re: Jeff King · lore

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

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 <git@drmicha.warpmail.net>
---
 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
Junio C Hamano· Aug 26, 2011, 17:43 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
> Currently, there is no way to invoke the list mode explicitly.
..., without giving -v to force verbose output.
Show 6 quoted lines
> 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 <git@drmicha.warpmail.net>
> ---
Show 7 quoted lines
> @@ -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).
Very good to mention "and -v" here ;-)
Show 22 quoted lines
> 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
> +'
> +

Could we have a test to check the code you updated to sanity check the combination of options as well? I suspect the reason your initial round botched the "branch -v -m foo" without realizing may be because we do not cover the error checking.

Michael J Gruber· Aug 28, 2011, 14:37 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 26.08.2011 19:43:
Show 50 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
>> 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.
>>
>> Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
>> ---
> 
>> @@ -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).
> 
> Very good to mention "and -v" here ;-)
> 
>> 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
>> +'
>> +
> 
> Could we have a test to check the code you updated to sanity check the
> combination of options as well? I suspect the reason your initial round
> botched the "branch -v -m foo" without realizing may be because we do not
> cover the error checking.

Currently, "-m -d" is forbidden", but "-m -v" is "-m", same for "-d -v". Do we want to keep it like that? Probably. I'll add the tests to 4/5.

Michael
Jeff King· Sep 7, 2011, 19:56 UTC · re: Michael J Gruber · lore

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

On Sun, Aug 28, 2011 at 04:37:04PM +0200, Michael J Gruber wrote:
> Currently, "-m -d" is forbidden", but "-m -v" is "-m", same for "-d -v".
> Do we want to keep it like that? Probably. I'll add the tests to 4/5.

Yes, I think so. "-v" just means "be more verbose"; the fact that there is currently nothing to be more verbose about with "-m" and "-d" is irrelevant.

It does make me a little nervous about the "'git branch -v' automatically means 'git branch --list -v'" patch, though. It closes the door in the future to us being more or less verbose about branch creation details (and while helpful, it creates a slight inconsistency in the interface).

If we are adding "-l" anyway, is it really necessary? It's not much harder to do "git branch -lv" once that is in place.

-Peff
Michael J Gruber· Sep 8, 2011, 09:24 UTC · re: Jeff King · lore

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

Jeff King venit, vidit, dixit 07.09.2011 21:56:
Show 17 quoted lines
> On Sun, Aug 28, 2011 at 04:37:04PM +0200, Michael J Gruber wrote:
> 
>> Currently, "-m -d" is forbidden", but "-m -v" is "-m", same for "-d -v".
>> Do we want to keep it like that? Probably. I'll add the tests to 4/5.
> 
> Yes, I think so. "-v" just means "be more verbose"; the fact that
> there is currently nothing to be more verbose about with "-m" and "-d"
> is irrelevant.
> 
> It does make me a little nervous about the "'git branch -v'
> automatically means 'git branch --list -v'" patch, though. It closes the
> door in the future to us being more or less verbose about branch
> creation details (and while helpful, it creates a slight inconsistency
> in the interface).
> 
> If we are adding "-l" anyway, is it really necessary? It's not much
> harder to do "git branch -lv" once that is in place.

Well, it will take a while to (re-)take "-l". For the sake of consistency I wouldn't mind making "--verbose" strictly a "modifier" for whatever mode/subcommand of the command is going on, even though it would mean having to type "-v --list" for a long time. In general, -v/--verbose should always be like that, but is not (e.g. tag -v).

This is in next now but no harm would be done changing it now.
Michael
Michael J Gruber· Sep 8, 2011, 14:25 UTC · re: Michael J Gruber · lore

[PATCHv4 0/5] mg/branch-list amendment

So, this is what the top 2 commits in mg/branch-list should be replaced with if we don't let "-v" activate list mode, so that we can have a verbose option for branch creation later on, i.e.:

git branch -v foo is the verbose version of git branch
Michael J Gruber (2):
  branch: introduce --list option
  branch: allow pattern arguments
 Documentation/git-branch.txt |   17 +++++++++++++----
 builtin/branch.c             |   35 +++++++++++++++++++++++++++++------
 t/t3200-branch.sh            |   32 ++++++++++++++++++++++++++++++++
 t/t3203-branch-output.sh     |   28 ++++++++++++++++++++++++++++
 4 files changed, 102 insertions(+), 10 deletions(-)
-- 
1.7.7.rc0.469.g9eb94
Michael J Gruber· Sep 8, 2011, 14:25 UTC · re: Michael J Gruber · lore

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

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.

Along with --list, test also combinations of existing options.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 Documentation/git-branch.txt |   11 ++++++++---
 builtin/branch.c             |   11 ++++++++---
 t/t3200-branch.sh            |   32 ++++++++++++++++++++++++++++++++
 t/t3203-branch-output.sh     |    5 +++++
 4 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 4c64ac9..26024b6 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` option (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,9 +111,13 @@ 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
+	When in list mode,
+	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.
 
diff --git a/builtin/branch.c b/builtin/branch.c
index bd3a315..b17ad26 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -612,7 +612,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;
@@ -651,6 +651,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)"),
 		{
@@ -693,7 +694,11 @@ 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)
+		list = 1;
+
+	if (!!delete + !!rename + !!force_create + !!list > 1)
 		usage_with_options(builtin_branch_usage, options);
 
 	if (abbrev == -1)
@@ -701,7 +706,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 9e69c8c..c466b20 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
+'
+
 mv .git/config .git/config-saved
 
 test_expect_success 'git branch -m q q2 without config should succeed' '
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 6b7c118..97d10b1 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -32,6 +32,11 @@ 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'
   origin/HEAD -> origin/branch-one
   origin/branch-one
-- 
1.7.7.rc0.469.g9eb94
Michael J Gruber· Sep 8, 2011, 14:25 UTC · re: Michael J Gruber · lore

[PATCHv4 5/5] branch: allow pattern arguments

Allow pattern arguments for the list mode just like for git tag -l.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 Documentation/git-branch.txt |    8 ++++++--
 builtin/branch.c             |   24 +++++++++++++++++++++---
 t/t3203-branch-output.sh     |   23 +++++++++++++++++++++++
 3 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 26024b6..2e27ee4 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` option (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 b17ad26..099c75c 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++) {
@@ -707,7 +724,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 97d10b1..76fe7e0 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -38,6 +38,15 @@ test_expect_success 'git branch --list shows local branches' '
 '
 
 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
   origin/branch-two
@@ -72,6 +81,20 @@ test_expect_success 'git branch -v shows branch summaries' '
 '
 
 cat >expect <<'EOF'
+two
+one
+EOF
+test_expect_success 'git branch --list -v pattern shows branch summaries' '
+	git branch --list -v branch* >tmp &&
+	awk "{print \$NF}" <tmp >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch -v pattern does not show branch summaries' '
+	test_must_fail git branch -v branch*
+'
+
+cat >expect <<'EOF'
 * (no branch)
   branch-one
   branch-two
-- 
1.7.7.rc0.469.g9eb94
Junio C Hamano· Sep 8, 2011, 21:03 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 7 quoted lines
> Jeff King venit, vidit, dixit 07.09.2011 21:56:
> ...
>> It does make me a little nervous about the "'git branch -v'
>> automatically means 'git branch --list -v'" patch, though. It closes the
>> door in the future to us being more or less verbose about branch
>> creation details (and while helpful, it creates a slight inconsistency
>> in the interface).

Hasn't 'git branch -v' meant listing in verbose mode for a long enough time that changing it now would mean a moderately major regression?

At least my copy of v1.7.0 seems to list with "git branch -v".
Jeff King· Sep 8, 2011, 21:11 UTC · re: Junio C Hamano · lore

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

On Thu, Sep 08, 2011 at 02:03:52PM -0700, Junio C Hamano wrote:
> Hasn't 'git branch -v' meant listing in verbose mode for a long enough
> time that changing it now would mean a moderately major regression?
> 
> At least my copy of v1.7.0 seems to list with "git branch -v".

No, it will mean the same thing as in v1.7.0; it is not the "-v" which does it, but the lack of non-option arguments.

Right now, "-v" just means "if we are listing, do it verbosely". And if you don't specify any non-option arguments, it means "list". So right now:

  git branch        ;# list
  git branch -v     ;# list verbosely
  git branch -v foo ;# create branch 'foo', -v does nothing
Michael's proposal was:
  git branch -v foo ;# assume verbose list, interpret 'foo' as pattern

which is actually a regression, albeit one that probably doesn't matter (because "-v" didn't ever do anything with a non-option argument).

Whereas mine is:
  git branch -v foo ;# create branch 'foo' verbosely

Which happens to do exactly the same thing as the current behavior, because there are no verbose messages to add to "git branch". But it leaves the door open to adding them in the future (and it's consistent with "git branch -m -v" and "git branch -d -v", both of which could use "-v" to do their operations more verbosely).

-Peff
Junio C Hamano· Sep 8, 2011, 21:17 UTC · re: Junio C Hamano · lore

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

Junio C Hamano <gitster@pobox.com> writes:
Show 14 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
>
>> Jeff King venit, vidit, dixit 07.09.2011 21:56:
>> ...
>>> It does make me a little nervous about the "'git branch -v'
>>> automatically means 'git branch --list -v'" patch, though. It closes the
>>> door in the future to us being more or less verbose about branch
>>> creation details (and while helpful, it creates a slight inconsistency
>>> in the interface).
>
> Hasn't 'git branch -v' meant listing in verbose mode for a long enough
> time that changing it now would mean a moderately major regression?
>
> At least my copy of v1.7.0 seems to list with "git branch -v".
Ah, nevermind.
As the series is already in 'next', here is what I came up with.
-- >8 --
From: Michael J Gruber <git@drmicha.warpmail.net>
Date: Thu, 8 Sep 2011 14:09:50 -0700
Subject: [PATCH] branch: -v does not automatically imply --list

"branch -v" without other options or parameters still works in the list mode, but that is not because there is "-v" but because there is no parameter nor option.

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-branch.txt |    6 +++---
 builtin/branch.c             |    3 +--
 t/t3203-branch-output.sh     |    8 ++++++--
 3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 2b8bc84..f46013c 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -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 `--list` and `-v` options (see below).
+activated by the `--list` option (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.
@@ -120,10 +120,10 @@ OPTIONS
 
 -v::
 --verbose::
-	Show sha1 and commit subject line for each head, along with
+	When in list mode,
+	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 98a420f..099c75c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -712,8 +712,7 @@ 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 &&
-	    (argc == 0 || (verbose && argc)))
+	if (!delete && !rename && !force_create && argc == 0)
 		list = 1;
 
 	if (!!delete + !!rename + !!force_create + !!list > 1)
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index f2b294b..76fe7e0 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -84,12 +84,16 @@ cat >expect <<'EOF'
 two
 one
 EOF
-test_expect_success 'git branch -v pattern shows branch summaries' '
-	git branch -v branch* >tmp &&
+test_expect_success 'git branch --list -v pattern shows branch summaries' '
+	git branch --list -v branch* >tmp &&
 	awk "{print \$NF}" <tmp >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch -v pattern does not show branch summaries' '
+	test_must_fail git branch -v branch*
+'
+
 cat >expect <<'EOF'
 * (no branch)
   branch-one
-- 
1.7.7.rc0.188.g3793ac
Jeff King· Sep 9, 2011, 01:08 UTC · re: Junio C Hamano · lore

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

On Thu, Sep 08, 2011 at 02:17:24PM -0700, Junio C Hamano wrote:
Show 7 quoted lines
> "branch -v" without other options or parameters still works in the list
> mode, but that is not because there is "-v" but because there is no
> parameter nor option.
> [...]
> +test_expect_success 'git branch -v pattern does not show branch summaries' '
> +	test_must_fail git branch -v branch*
> +'

You might also want to affirm that it does not just fail, but still creates a branch, like:

  test_expect_success 'git branch -v <name> creates a branch' '
         git rev-parse HEAD >expect &&
         git branch -v foo &&
         git rev-parse foo >actual &&
         test_cmp expect actual
  '
-Peff
Michael J Gruber· Sep 9, 2011, 06:54 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 08.09.2011 23:17:
Show 20 quoted lines
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> Michael J Gruber <git@drmicha.warpmail.net> writes:
>>
>>> Jeff King venit, vidit, dixit 07.09.2011 21:56:
>>> ...
>>>> It does make me a little nervous about the "'git branch -v'
>>>> automatically means 'git branch --list -v'" patch, though. It closes the
>>>> door in the future to us being more or less verbose about branch
>>>> creation details (and while helpful, it creates a slight inconsistency
>>>> in the interface).
>>
>> Hasn't 'git branch -v' meant listing in verbose mode for a long enough
>> time that changing it now would mean a moderately major regression?
>>
>> At least my copy of v1.7.0 seems to list with "git branch -v".
> 
> Ah, nevermind.
> 
> As the series is already in 'next', here is what I came up with.

I thought you'll rebuild next anyways after 1.7.7, but either way it's fine. Thanks for holding this series in next long enough to really cook it (and Jeff for revisiting it), it's much better now, keeping the (undocumented, but expected) behavior of "git branch -v foo".

Show 81 quoted lines
> 
> -- >8 --
> From: Michael J Gruber <git@drmicha.warpmail.net>
> Date: Thu, 8 Sep 2011 14:09:50 -0700
> Subject: [PATCH] branch: -v does not automatically imply --list
> 
> "branch -v" without other options or parameters still works in the list
> mode, but that is not because there is "-v" but because there is no
> parameter nor option.
> 
> Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  Documentation/git-branch.txt |    6 +++---
>  builtin/branch.c             |    3 +--
>  t/t3203-branch-output.sh     |    8 ++++++--
>  3 files changed, 10 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
> index 2b8bc84..f46013c 100644
> --- a/Documentation/git-branch.txt
> +++ b/Documentation/git-branch.txt
> @@ -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 `--list` and `-v` options (see below).
> +activated by the `--list` option (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.
> @@ -120,10 +120,10 @@ OPTIONS
>  
>  -v::
>  --verbose::
> -	Show sha1 and commit subject line for each head, along with
> +	When in list mode,
> +	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 98a420f..099c75c 100644
> --- a/builtin/branch.c
> +++ b/builtin/branch.c
> @@ -712,8 +712,7 @@ 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 &&
> -	    (argc == 0 || (verbose && argc)))
> +	if (!delete && !rename && !force_create && argc == 0)
>  		list = 1;
>  
>  	if (!!delete + !!rename + !!force_create + !!list > 1)
> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> index f2b294b..76fe7e0 100755
> --- a/t/t3203-branch-output.sh
> +++ b/t/t3203-branch-output.sh
> @@ -84,12 +84,16 @@ cat >expect <<'EOF'
>  two
>  one
>  EOF
> -test_expect_success 'git branch -v pattern shows branch summaries' '
> -	git branch -v branch* >tmp &&
> +test_expect_success 'git branch --list -v pattern shows branch summaries' '
> +	git branch --list -v branch* >tmp &&
>  	awk "{print \$NF}" <tmp >actual &&
>  	test_cmp expect actual
>  '
>  
> +test_expect_success 'git branch -v pattern does not show branch summaries' '
> +	test_must_fail git branch -v branch*
> +'
> +
>  cat >expect <<'EOF'
>  * (no branch)
>    branch-one
Junio C Hamano· Sep 9, 2011, 16:02 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 9 quoted lines
> Junio C Hamano venit, vidit, dixit 08.09.2011 23:17:
>> Ah, nevermind.
>> 
>> As the series is already in 'next', here is what I came up with.
>
> I thought you'll rebuild next anyways after 1.7.7, but either way it's
> fine. Thanks for holding this series in next long enough to really cook
> it (and Jeff for revisiting it), it's much better now, keeping the
> (undocumented, but expected) behavior of "git branch -v foo".
Thank *you* for all the work.
I recall Peff had some comments on your new tests last night, by the way.
Michael J Gruber· Sep 9, 2011, 19:29 UTC · re: Junio C Hamano · lore

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

Junio C Hamano venit, vidit, dixit 09.09.2011 18:02:
Show 16 quoted lines
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
>> Junio C Hamano venit, vidit, dixit 08.09.2011 23:17:
>>> Ah, nevermind.
>>>
>>> As the series is already in 'next', here is what I came up with.
>>
>> I thought you'll rebuild next anyways after 1.7.7, but either way it's
>> fine. Thanks for holding this series in next long enough to really cook
>> it (and Jeff for revisiting it), it's much better now, keeping the
>> (undocumented, but expected) behavior of "git branch -v foo".
> 
> Thank *you* for all the work.
> 
> I recall Peff had some comments on your new tests last night, by the way.
> 

Yes, we have tests (now) for "-v -d", "-v -m", "-v branch*" and combinations with --list. Testing "-v foo" for creation would be good (and belong into t3200). Is there anything to amend?

Michael
Jeff King· Sep 9, 2011, 19:30 UTC · re: Michael J Gruber · lore

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

On Fri, Sep 09, 2011 at 09:29:32PM +0200, Michael J Gruber wrote:
Show 5 quoted lines
> > I recall Peff had some comments on your new tests last night, by the way.
> 
> Yes, we have tests (now) for "-v -d", "-v -m", "-v branch*" and
> combinations with --list. Testing "-v foo" for creation would be good
> (and belong into t3200). Is there anything to amend?
I think my comment was just to add the "-v foo" test.
-Peff
Michael J Gruber· Sep 9, 2011, 19:40 UTC · re: Jeff King · lore

[PATCH] t3200: test branch creation with -v

Make sure that "git branch -v t" creates branch "t".
Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 t/t3200-branch.sh |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index c466b20..8381f0c 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -105,6 +105,13 @@ test_expect_success 'git branch -v -d t should work' '
 	test ! -f .git/refs/heads/t
 '
 
+test_expect_success 'git branch -v t should work' '
+	git branch -v t &&
+	test .git/refs/heads/t &&
+	git branch -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 &&
-- 
1.7.7.rc0.469.g9eb94
Jeff King· Sep 9, 2011, 19:43 UTC · re: Michael J Gruber · lore

Re: [PATCH] t3200: test branch creation with -v

On Fri, Sep 09, 2011 at 09:40:59PM +0200, Michael J Gruber wrote:
> +test_expect_success 'git branch -v t should work' '
> +	git branch -v t &&
> +	test .git/refs/heads/t &&
test -f ?

Also, don't we have test_path_is_file which yields slightly prettier output (and maybe some portability benefits; I don't remember)?

> +	git branch -d t &&
> +	test ! -f .git/refs/heads/t
Ditto for 'test_path_is_missing' here.
-Peff
Jeff King· Sep 9, 2011, 19:45 UTC · re: Jeff King · lore

Re: [PATCH] t3200: test branch creation with -v

On Fri, Sep 09, 2011 at 03:43:57PM -0400, Jeff King wrote:
Show 7 quoted lines
> On Fri, Sep 09, 2011 at 09:40:59PM +0200, Michael J Gruber wrote:
> 
> > +test_expect_success 'git branch -v t should work' '
> > +	git branch -v t &&
> > +	test .git/refs/heads/t &&
> 
> test -f ?
Hmm, this also seems to be a problem in the other tests, too.
-Peff
Michael J Gruber· Sep 10, 2011, 13:29 UTC · re: Jeff King · lore

Re: [PATCH] t3200: test branch creation with -v

Jeff King venit, vidit, dixit 09.09.2011 21:43:
Show 15 quoted lines
> On Fri, Sep 09, 2011 at 09:40:59PM +0200, Michael J Gruber wrote:
> 
>> +test_expect_success 'git branch -v t should work' ' +	git branch
>> -v t && +	test .git/refs/heads/t &&
> 
> test -f ?
> 
> Also, don't we have test_path_is_file which yields slightly prettier 
> output (and maybe some portability benefits; I don't remember)?
> 
>> +	git branch -d t && +	test ! -f .git/refs/heads/t
> 
> Ditto for 'test_path_is_missing' here.
> 
> -Peff

Well, I tried to follow the surrounding style. That t3200 could benefit from some attention, though, which I did not want to mix in with the issue at hand.

Michael
Jeff King· Sep 13, 2011, 03:57 UTC · re: Michael J Gruber · lore

Re: [PATCH] t3200: test branch creation with -v

On Sat, Sep 10, 2011 at 03:29:43PM +0200, Michael J Gruber wrote:
Show 20 quoted lines
> Jeff King venit, vidit, dixit 09.09.2011 21:43:
> > On Fri, Sep 09, 2011 at 09:40:59PM +0200, Michael J Gruber wrote:
> > 
> >> +test_expect_success 'git branch -v t should work' ' +	git branch
> >> -v t && +	test .git/refs/heads/t &&
> > 
> > test -f ?
> > 
> > Also, don't we have test_path_is_file which yields slightly prettier 
> > output (and maybe some portability benefits; I don't remember)?
> > 
> >> +	git branch -d t && +	test ! -f .git/refs/heads/t
> > 
> > Ditto for 'test_path_is_missing' here.
> > 
> > -Peff
> 
> Well, I tried to follow the surrounding style. That t3200 could benefit
> from some attention, though, which I did not want to mix in with the
> issue at hand.

The "test_path_is_file" thing is style. But not using "test -f" is just wrong; you are testing "is .git/refs/heads/t an empty string?" which is useless.

You want this on top of what's in mg/branch-list:
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index c466b20..b513115 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -100,14 +100,14 @@ test_expect_success 'git branch -m q r/q should fail when r exists' '
 
 test_expect_success 'git branch -v -d t should work' '
 	git branch t &&
-	test .git/refs/heads/t &&
+	test -f .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 &&
+	test -f .git/refs/heads/t &&
 	git branch -v -m t s &&
 	test ! -f .git/refs/heads/t &&
 	test -f .git/refs/heads/s &&
@@ -116,7 +116,7 @@ test_expect_success 'git branch -v -m t s should work' '
 
 test_expect_success 'git branch -m -d t s should fail' '
 	git branch t &&
-	test .git/refs/heads/t &&
+	test -f .git/refs/heads/t &&
 	test_must_fail git branch -m -d t s &&
 	git branch -d t &&
 	test ! -f .git/refs/heads/t
@@ -124,7 +124,7 @@ test_expect_success 'git branch -m -d t s should fail' '
 
 test_expect_success 'git branch --list -d t should fail' '
 	git branch t &&
-	test .git/refs/heads/t &&
+	test -f .git/refs/heads/t &&
 	test_must_fail git branch --list -d t &&
 	git branch -d t &&
 	test ! -f .git/refs/heads/t

I suspect you didn't notice the bogosity before because those are just
confirming the precondition that "git branch" actually created the file.

-Peff
Michael J Gruber· Sep 13, 2011, 12:12 UTC · re: Jeff King · lore

Re: [PATCH] t3200: test branch creation with -v

Jeff King venit, vidit, dixit 13.09.2011 05:57:
Show 28 quoted lines
> On Sat, Sep 10, 2011 at 03:29:43PM +0200, Michael J Gruber wrote:
> 
>> Jeff King venit, vidit, dixit 09.09.2011 21:43:
>>> On Fri, Sep 09, 2011 at 09:40:59PM +0200, Michael J Gruber wrote:
>>>
>>>> +test_expect_success 'git branch -v t should work' ' +	git branch
>>>> -v t && +	test .git/refs/heads/t &&
>>>
>>> test -f ?
>>>
>>> Also, don't we have test_path_is_file which yields slightly prettier 
>>> output (and maybe some portability benefits; I don't remember)?
>>>
>>>> +	git branch -d t && +	test ! -f .git/refs/heads/t
>>>
>>> Ditto for 'test_path_is_missing' here.
>>>
>>> -Peff
>>
>> Well, I tried to follow the surrounding style. That t3200 could benefit
>> from some attention, though, which I did not want to mix in with the
>> issue at hand.
> 
> The "test_path_is_file" thing is style. But not using "test -f" is just
> wrong; you are testing "is .git/refs/heads/t an empty string?" which is
> useless.
> 
> You want this on top of what's in mg/branch-list:
Yes, sorry. How did I miss that?
I'd prefer your style anyway, but also prefer changing t3200 in one go.
Show 45 quoted lines
> 
> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
> index c466b20..b513115 100755
> --- a/t/t3200-branch.sh
> +++ b/t/t3200-branch.sh
> @@ -100,14 +100,14 @@ test_expect_success 'git branch -m q r/q should fail when r exists' '
>  
>  test_expect_success 'git branch -v -d t should work' '
>  	git branch t &&
> -	test .git/refs/heads/t &&
> +	test -f .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 &&
> +	test -f .git/refs/heads/t &&
>  	git branch -v -m t s &&
>  	test ! -f .git/refs/heads/t &&
>  	test -f .git/refs/heads/s &&
> @@ -116,7 +116,7 @@ test_expect_success 'git branch -v -m t s should work' '
>  
>  test_expect_success 'git branch -m -d t s should fail' '
>  	git branch t &&
> -	test .git/refs/heads/t &&
> +	test -f .git/refs/heads/t &&
>  	test_must_fail git branch -m -d t s &&
>  	git branch -d t &&
>  	test ! -f .git/refs/heads/t
> @@ -124,7 +124,7 @@ test_expect_success 'git branch -m -d t s should fail' '
>  
>  test_expect_success 'git branch --list -d t should fail' '
>  	git branch t &&
> -	test .git/refs/heads/t &&
> +	test -f .git/refs/heads/t &&
>  	test_must_fail git branch --list -d t &&
>  	git branch -d t &&
>  	test ! -f .git/refs/heads/t
> 
> I suspect you didn't notice the bogosity before because those are just
> confirming the precondition that "git branch" actually created the file.
> 
> -Peff
Jeff King· Sep 13, 2011, 16:13 UTC · re: Michael J Gruber · lore

[PATCH] t3200: clean up checks for file existence

This patch uses test_path_is_file and test_path_is_missing instead of "test -f / ! test -f" checks. The former are more verbose in case of failure and more precise (e.g., is_missing will check that the entry is actually missing, not just not a regular file).

As a bonus, this also fixes a few buggy tests that used "test foo" instead of "test -f foo", and consequently always reported success.

Signed-off-by: Jeff King <peff@peff.net>
---
On Tue, Sep 13, 2011 at 02:12:52PM +0200, Michael J Gruber wrote:
> I'd prefer your style anyway, but also prefer changing t3200 in one go.

Here it is, on top of mg/branch-list. The "test without -f" bits could actually be squashed in to your commits if we really want clean history, but I don't think it's a big deal.

 t/t3200-branch.sh |   50 +++++++++++++++++++++++++-------------------------
 1 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index c466b20..3988ec9 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -23,7 +23,7 @@ test_expect_success \
 test_expect_success \
     'git branch --help should not have created a bogus branch' '
      git branch --help </dev/null >/dev/null 2>/dev/null;
-     ! test -f .git/refs/heads/--help
+     test_path_is_missing .git/refs/heads/--help
 '
 
 test_expect_success 'branch -h in broken repository' '
@@ -39,11 +39,11 @@ test_expect_success 'branch -h in broken repository' '
 
 test_expect_success \
     'git branch abc should create a branch' \
-    'git branch abc && test -f .git/refs/heads/abc'
+    'git branch abc && test_path_is_file .git/refs/heads/abc'
 
 test_expect_success \
     'git branch a/b/c should create a branch' \
-    'git branch a/b/c && test -f .git/refs/heads/a/b/c'
+    'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
 
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
@@ -52,15 +52,15 @@ test_expect_success \
     'git branch -l d/e/f should create a branch and a log' \
 	'GIT_COMMITTER_DATE="2005-05-26 23:30" \
      git branch -l d/e/f &&
-	 test -f .git/refs/heads/d/e/f &&
-	 test -f .git/logs/refs/heads/d/e/f &&
+	 test_path_is_file .git/refs/heads/d/e/f &&
+	 test_path_is_file .git/logs/refs/heads/d/e/f &&
 	 test_cmp expect .git/logs/refs/heads/d/e/f'
 
 test_expect_success \
     'git branch -d d/e/f should delete a branch and a log' \
 	'git branch -d d/e/f &&
-	 test ! -f .git/refs/heads/d/e/f &&
-	 test ! -f .git/logs/refs/heads/d/e/f'
+	 test_path_is_missing .git/refs/heads/d/e/f &&
+	 test_path_is_missing .git/logs/refs/heads/d/e/f'
 
 test_expect_success \
     'git branch j/k should work after branch j has been deleted' \
@@ -78,13 +78,13 @@ test_expect_success \
     'git branch -m m m/m should work' \
        'git branch -l m &&
         git branch -m m m/m &&
-        test -f .git/logs/refs/heads/m/m'
+        test_path_is_file .git/logs/refs/heads/m/m'
 
 test_expect_success \
     'git branch -m n/n n should work' \
        'git branch -l n/n &&
         git branch -m n/n n
-        test -f .git/logs/refs/heads/n'
+        test_path_is_file .git/logs/refs/heads/n'
 
 test_expect_success 'git branch -m o/o o should fail when o/p exists' '
 	git branch o/o &&
@@ -100,34 +100,34 @@ test_expect_success 'git branch -m q r/q should fail when r exists' '
 
 test_expect_success 'git branch -v -d t should work' '
 	git branch t &&
-	test .git/refs/heads/t &&
+	test_path_is_file .git/refs/heads/t &&
 	git branch -v -d t &&
-	test ! -f .git/refs/heads/t
+	test_path_is_missing .git/refs/heads/t
 '
 
 test_expect_success 'git branch -v -m t s should work' '
 	git branch t &&
-	test .git/refs/heads/t &&
+	test_path_is_file .git/refs/heads/t &&
 	git branch -v -m t s &&
-	test ! -f .git/refs/heads/t &&
-	test -f .git/refs/heads/s &&
+	test_path_is_missing .git/refs/heads/t &&
+	test_path_is_file .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_path_is_file .git/refs/heads/t &&
 	test_must_fail git branch -m -d t s &&
 	git branch -d t &&
-	test ! -f .git/refs/heads/t
+	test_path_is_missing .git/refs/heads/t
 '
 
 test_expect_success 'git branch --list -d t should fail' '
 	git branch t &&
-	test .git/refs/heads/t &&
+	test_path_is_file .git/refs/heads/t &&
 	test_must_fail git branch --list -d t &&
 	git branch -d t &&
-	test ! -f .git/refs/heads/t
+	test_path_is_missing .git/refs/heads/t
 '
 
 mv .git/config .git/config-saved
@@ -144,12 +144,12 @@ git config branch.s/s.dummy Hello
 test_expect_success \
     'git branch -m s/s s should work when s/t is deleted' \
        'git branch -l s/s &&
-        test -f .git/logs/refs/heads/s/s &&
+        test_path_is_file .git/logs/refs/heads/s/s &&
         git branch -l s/t &&
-        test -f .git/logs/refs/heads/s/t &&
+        test_path_is_file .git/logs/refs/heads/s/t &&
         git branch -d s/t &&
         git branch -m s/s s &&
-        test -f .git/logs/refs/heads/s'
+        test_path_is_file .git/logs/refs/heads/s'
 
 test_expect_success 'config information was renamed, too' \
 	"test $(git config branch.s.dummy) = Hello &&
@@ -160,8 +160,8 @@ test_expect_success 'renaming a symref is not allowed' \
 	git symbolic-ref refs/heads/master2 refs/heads/master &&
 	test_must_fail git branch -m master2 master3 &&
 	git symbolic-ref refs/heads/master2 &&
-	test -f .git/refs/heads/master &&
-	! test -f .git/refs/heads/master3
+	test_path_is_file .git/refs/heads/master &&
+	test_path_is_missing .git/refs/heads/master3
 '
 
 test_expect_success SYMLINKS \
@@ -270,8 +270,8 @@ test_expect_success \
     'git checkout -b g/h/i -l should create a branch and a log' \
 	'GIT_COMMITTER_DATE="2005-05-26 23:30" \
      git checkout -b g/h/i -l master &&
-	 test -f .git/refs/heads/g/h/i &&
-	 test -f .git/logs/refs/heads/g/h/i &&
+	 test_path_is_file .git/refs/heads/g/h/i &&
+	 test_path_is_file .git/logs/refs/heads/g/h/i &&
 	 test_cmp expect .git/logs/refs/heads/g/h/i'
 
 test_expect_success 'checkout -b makes reflog by default' '
-- 
1.7.7.rc1.2.gb2409
Junio C Hamano· Sep 13, 2011, 17:13 UTC · re: Jeff King · lore

Re: [PATCH] t3200: clean up checks for file existence

Jeff King <peff@peff.net> writes:
> Here it is, on top of mg/branch-list. The "test without -f" bits could
> actually be squashed in to your commits if we really want clean history,
> but I don't think it's a big deal.
Thanks.

In the longer term, we might want to update these tests further so that they do not fail when implementation is updated not to write loose refs, e.g.

>  test_expect_success \
>      'git branch abc should create a branch' \
> -    'git branch abc && test -f .git/refs/heads/abc'
> +    'git branch abc && test_path_is_file .git/refs/heads/abc'
    git branch abc && git show-ref -q --verify refs/heads/abc
But this patch is about correctness first, so I'll queue it as-is.
 
Jeff King· Sep 13, 2011, 17:16 UTC · re: Junio C Hamano · lore

Re: [PATCH] t3200: clean up checks for file existence

On Tue, Sep 13, 2011 at 10:13:56AM -0700, Junio C Hamano wrote:
> In the longer term, we might want to update these tests further so that
> they do not fail when implementation is updated not to write loose refs,
> e.g.

Yeah, I noticed that, too, but figured to leave it for when such a change came about (which is probably going to need to fix tests everywhere, not just here). I'm happy to fix this area now, though, if you want.

-Peff
Michael J Gruber· Aug 26, 2011, 14:05 UTC · re: Jeff King · lore

[PATCHv2 5/5] branch: allow pattern arguments

Allow pattern arguments for the list mode just like for git tag -l.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 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
Michael J Gruber· Aug 25, 2011, 08:30 UTC · re: Michael J Gruber · lore

[PATCH 1/5] branch: allow pattern arguments

Allow pattern arguments for the list mode just like for git tag -l.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 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
Jeff King· Aug 25, 2011, 17:54 UTC · re: Michael J Gruber · lore

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

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
Junio C Hamano· Aug 25, 2011, 18:32 UTC · re: Jeff King · lore

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

Jeff King <peff@peff.net> writes:
Show 10 quoted lines
> 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.
Agreed.
Junio C Hamano· Aug 25, 2011, 19:29 UTC · re: Junio C Hamano · lore

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

Junio C Hamano <gitster@pobox.com> writes:
Show 14 quoted lines
> Jeff King <peff@peff.net> writes:
>
>> 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.
>
> Agreed.
... and can be done without too much additional code.
An offtopic side note about naming:

I called it "const char **pattern" not "patterns" and that is deliberate. I find it natural to be able to call the 2nd pattern, counting from 0 as any CS person would, by spelling "pattern[2]". "patterns[2]" sounds as if I am have multiple sets of patterns and refering to the second set of patterns.

 builtin/branch.c |   25 +++++++++++++++++++------
 1 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index 495bf2e..0fa62bd 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -260,10 +260,22 @@ static char *resolve_symref(const char *src, const char *prefix)
 
 struct append_ref_cb {
 	struct ref_list *ref_list;
-	const char *pattern;
+	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);
@@ -298,7 +310,7 @@ 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))
+	if (!match_patterns(cb->pattern, refname))
 		return 0;
 
 	commit = NULL;
@@ -496,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, const char *pattern)
+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;
@@ -528,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 && (!pattern || !fnmatch(pattern, "HEAD", 0)))
+	if (detached && match_patterns(pattern, "HEAD"))
 		show_detached(&ref_list);
 
 	for (i = 0; i < ref_list.index; i++) {
@@ -703,8 +715,9 @@ 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 || (verbose && argc == 1))
-		return print_ref_list(kinds, detached, verbose, abbrev, with_commit, argc ? argv[0] : NULL);
+	else if (argc == 0 || (verbose && argc))
+		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))
Michael J Gruber· Aug 25, 2011, 08:30 UTC · re: Michael J Gruber · lore

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

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 <git@drmicha.warpmail.net>
---
 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
Junio C Hamano· Aug 25, 2011, 18:34 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
> 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.

Probably this is --list "option", but I think this is a good idea to allow "we are in list mode" without having to invoke "verbose" output.

Junio C Hamano· Aug 25, 2011, 19:52 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 11 quoted lines
> @@ -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;
> +
I am afraid this is not quite right.

What happens if one of delete/rename/force-create can take a single argument, or more importantly, if we someday gain another option that can take zero or one argument and is incompatible with other operating mode?

For example, what happens to this command line with your "git branch":
	$ git branch -v -m boo

The first test passes (no explicit --list), and then you violate the "only one of these operating mode can be set" assertion you made yourself by flipping list on, no?

It is a good practice to always run the defaulting tweak *before* checking options that are mutually incompatible, to catch your own mistake in the tweaking code.

On top of my previous "multiple patterns allowed" tweak, a replacement patch to this file may look like this. I suspect there should be a better way to represent the mode in a single variable and enforce that there is only one chosen, but that is left for another day.

 builtin/branch.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index 0fa62bd..4243e7c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -629,7 +629,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;
@@ -668,6 +668,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)"),
 		{
@@ -710,12 +711,17 @@ 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 (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (argc == 0 || (verbose && argc))
+	else if (list)
 		return print_ref_list(kinds, detached, verbose, abbrev,
 				      with_commit, argv);
 	else if (rename && (argc == 1))
Michael J Gruber· Aug 25, 2011, 08:30 UTC · re: Michael J Gruber · lore

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

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

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 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
Michael J Gruber· Aug 25, 2011, 08:30 UTC · re: Michael J Gruber · lore

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

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 <git@drmicha.warpmail.net>
---
 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
Junio C Hamano· Aug 25, 2011, 19:02 UTC · re: Michael J Gruber · lore

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

Michael J Gruber <git@drmicha.warpmail.net> writes:
> ahead/behind info is expensive, upstream name info cheap. Therefore,
> make -v output the upstream branch name and -vv add the ahead/behind
> info.

While I do not care too much about the end result, and I even suspect that swapping the meaning might be better at the semantic level, I do not agree with the above line of reasoning. It is not based on what the end user wants at all, but is based on how easily you can produce results.

If a relatively expensive information is what is more often wanted and the users are willing to pay the price to compute it, it is very reasonable to use short-and-sweet -v for showing only that information, and have them type -vv to ask for a less common information.

I also suspect that "ahead/behind" and "who-is-upstream" may be orthogonal and we may not tie these to degree of verbosity. Jeff's suggestion to teach these to for-each-ref may be along a similar thought.

You however may be able to argue "When the user wants A/B, s/he always wants who-is-upstream, and asking for A/B and not wanting who-is-upstream is insane". If "A/B" does not make sense without seeing "who-is-upstream", but if seeing "who-is-upstream" alone makes sense without seeing "A/B", then you can argue that these two are indeed degrees of verbosity, and it may be justifiable to make -v give "who-is-upstream", and make -vv give "A/B and who-is-upstream".

But I suspect that much more people have more than one topics cooking on top of upstream (i.e. need for A/B) than people who work with these topics based on different upstreams (i.e. need for "who-is-upstream"). People who base their topics on only one upstream do not need "who-is-upstream" to understand A/B.

I am wondering if "branch -v=<comma separated tokens>", where currently available tokens are "sha1", "upstream", "ahead/behind", is the right way to keep these orthogonal and extensible (of course you would want a shorter mnemonic). Then existing -v and -vv can be understood as aliases to:

	-v   ===  -v=sha1,ahead/behind
        -vv  ===  -v=sha1,upstream,ahead/behind

and you can naturally extend the vocabulary with "patchsame" to define your -vvv introduced in [5/5].

Michael J Gruber· Aug 25, 2011, 08:30 UTC · re: Michael J Gruber · lore

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

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 <git@drmicha.warpmail.net>
---
 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

← back to recent threads