threads / patch / 49531

v2branch: introduce --show-current display option

Subject: [PATCH v2 0/1] branch: introduce --show-current display option

## tl;dr

30 messages between Oct 10, 2018 and Oct 18, 2018. Diffs are folded; open one to read it.

replies: 29people: 7as markdown or json

Daniels Umanovskis· Oct 10, 2018, 20:54 UTC · lore

v2 reroll of a previously-discussed patch. Thanks to everyone for their comments. Based on feedback:

1. Command is now a verb: git branch --show-current.
2. Changed to gitster's suggested implementation: nothing is printed
 if HEAD does not point to a symbolic ref. A fatal
 error if HEAD is a symbolic ref but does not start with refs/heads/.
3. Added a test to show this works with worktrees

A process question to the list. The patch adds a new localizable string that gets output in case of repository corruption. I happen to speak a couple of the languages that have po files. Is it accepted practice to also include po edits in my patch in such a case, or should that be left to the regular l10n workflow?

Daniels Umanovskis (1):
  branch: introduce --show-current display option
 Documentation/git-branch.txt |  6 +++++-
 builtin/branch.c             | 21 ++++++++++++++++--
 t/t3203-branch-output.sh     | 41 ++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 3 deletions(-)
-- 
2.19.1.330.g93276587c.dirty
Daniels Umanovskis· Oct 10, 2018, 20:54 UTC · re: Daniels Umanovskis · lore

[PATCH v2 1/1] branch: introduce --show-current display option

When called with --show-current, git branch will print the current branch name and terminate. Only the actual name gets printed, without refs/heads. In detached HEAD state, nothing is output.

Intended both for scripting and interactive/informative use. Unlike git branch --list, no filtering is needed to just get the branch name.

Signed-off-by: Daniels Umanovskis <daniels@umanovskis.se>
---
 Documentation/git-branch.txt |  6 +++++-
 builtin/branch.c             | 21 ++++++++++++++++--
 t/t3203-branch-output.sh     | 41 ++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 3 deletions(-)
Show changes to 3 files +65 −3

Documentation/git-branch.txt, builtin/branch.c, t/t3203-branch-output.sh

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bf5316ffa..0babb9b1b 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]
-	[--list] [-v [--abbrev=<length> | --no-abbrev]]
+	[--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
 	[--column[=<options>] | --no-column] [--sort=<key>]
 	[(--merged | --no-merged) [<commit>]]
 	[--contains [<commit]] [--no-contains [<commit>]]
@@ -160,6 +160,10 @@ This option is only applicable in non-verbose mode.
 	branch --list 'maint-*'`, list only the branches that match
 	the pattern(s).
 
+--show-current::
+	Print the name of the current branch. In detached HEAD state,
+	nothing is printed.
+
 -v::
 -vv::
 --verbose::
diff --git a/builtin/branch.c b/builtin/branch.c
index c396c4153..ab03073b2 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -443,6 +443,17 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	free(to_free);
 }
 
+static void print_current_branch_name()
+{
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	const char *shortname;
+	if (refname == NULL || !strcmp(refname, "HEAD"))
+		return;
+	if (!skip_prefix(refname, "refs/heads/", &shortname))
+		die(_("unexpected symbolic ref for HEAD: %s"), refname);
+	puts(shortname);
+}
+
 static void reject_rebase_or_bisect_branch(const char *target)
 {
 	struct worktree **worktrees = get_worktrees(0);
@@ -581,6 +592,7 @@ static int edit_branch_description(const char *branch_name)
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
+	int show_current = 0;
 	int reflog = 0, edit_description = 0;
 	int quiet = 0, unset_upstream = 0;
 	const char *new_upstream = NULL;
@@ -620,6 +632,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
 		OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
 		OPT_BOOL('l', "list", &list, N_("list branch names")),
+		OPT_BOOL(0, "show-current", &show_current, N_("show current branch name")),
 		OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
 		OPT_BOOL(0, "edit-description", &edit_description,
 			 N_("edit the description for the branch")),
@@ -662,14 +675,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 && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)
+	if (!delete && !rename && !copy && !edit_description && !new_upstream &&
+	    !show_current && !unset_upstream && argc == 0)
 		list = 1;
 
 	if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
 	    filter.no_commit)
 		list = 1;
 
-	if (!!delete + !!rename + !!copy + !!new_upstream +
+	if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
 	    list + unset_upstream > 1)
 		usage_with_options(builtin_branch_usage, options);
 
@@ -697,6 +711,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		if (!argc)
 			die(_("branch name required"));
 		return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
+	} else if (show_current) {
+		print_current_branch_name();
+		return 0;
 	} else if (list) {
 		/*  git branch --local also shows HEAD when it is detached */
 		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index ee6787614..e9bc3b05f 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -100,6 +100,47 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
 	test_must_fail git branch -v branch*
 '
 
+test_expect_success 'git branch `--show-current` shows current branch' '
+	cat >expect <<-\EOF &&
+	branch-two
+	EOF
+	git checkout branch-two &&
+	git branch --current >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch `--show-current` is silent when detached HEAD' '
+	git checkout HEAD^0 &&
+	git branch --current >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'git branch `--show-current` works properly when tag exists' '
+	cat >expect <<-\EOF &&
+	branch-and-tag-name
+	EOF
+	git checkout -b branch-and-tag-name &&
+	git tag branch-and-tag-name &&
+	git branch --current >actual &&
+	git checkout branch-one &&
+	git branch -d branch-and-tag-name &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch `--show-current` works properly with worktrees' '
+	cat >expect <<-\EOF &&
+	branch-one
+	branch-two
+	EOF
+	git checkout branch-one &&
+	git branch --current >actual &&
+	git worktree add worktree branch-two &&
+	cd worktree &&
+	git branch --current >>../actual &&
+	cd .. &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git branch shows detached HEAD properly' '
 	cat >expect <<EOF &&
 * (HEAD detached at $(git rev-parse --short HEAD^0))
-- 
2.19.1.330.g93276587c.dirty
Jeff King· Oct 11, 2018, 00:34 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Wed, Oct 10, 2018 at 10:54:32PM +0200, Daniels Umanovskis wrote:
> When called with --show-current, git branch will print the current
> branch name and terminate. Only the actual name gets printed,
> without refs/heads. In detached HEAD state, nothing is output.

I also wondered what happens in an unborn-branch state (i.e., we are on refs/heads/master, but have not yet made any commits).

In that case, resolve_ref_unsafe() will return the branch name, but a null oid. And you'll print that. Which seems sensible.

> Intended both for scripting and interactive/informative use.
> Unlike git branch --list, no filtering is needed to just get the
> branch name.

We should not advertise this to be used for scripting. The git-branch command is porcelain, and we reserve the right to change its output between versions, or based on user config. Fortunately, there is already a blessed way to get this in a script, which is:

  git symbolic-ref [--short] HEAD

I'm not opposed to having "branch --show-current" as a more user-facing alternative for people who want to query the state. I do wonder if people would want it to show extra information, like:

  - if we're detached, from where (like the normal "branch --list"
    shows)
  - are we on an unborn branch
  - are we ahead/behind an upstream (like "branch -v")

I guess that's slowly reinventing the first line of "git status -b". Maybe that's a good thing; possibly this should just be a way to get that data without doing the rest of git-status, and it's perhaps more discoverable since it's part of git-branch. I dunno.

It just seems like in its current form it might be in an uncanny valley where it is not quite scriptable plumbing, but not as informative as other porcelain.

-Peff
Rafael Ascensão· Oct 11, 2018, 15:43 UTC · re: Jeff King · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Wed, Oct 10, 2018 at 08:34:40PM -0400, Jeff King wrote:
> It just seems like in its current form it might be in an uncanny valley
> where it is not quite scriptable plumbing, but not as informative as
> other porcelain.
I agree it feels a bit out of place, and still think that
    $ git branch --list HEAD
would be a good candidate to be taught how to print the current branch.

I suggested this in the previous iteration but either got lost in the noise or was uninteresting. If the latter, I would love to receive feedback on it. https://public-inbox.org/git/20181010142423.GA3390@rigel/

Something like the following (not meant as a real patch), would show the current branch when attached, (HEAD detached at hash) when detached, and nothing if unborn branch.

This will also keep the current formatting git branch uses (which is sliglty harder to parse). I view it as a plus. Otherwise people will eventually start parsing it instead of using the recommended plumbing.

-- Cheers Rafael Ascensão

-- >8 --
Show changes to builtin/branch.c +11 −0
diff --git a/builtin/branch.c b/builtin/branch.c
index b67593288c..78a3de526c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -684,6 +684,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
 			filter.kind |= FILTER_REFS_DETACHED_HEAD;
 		filter.name_patterns = argv;
+
+		while (*argv) {
+			if (!strcmp(*argv, "HEAD")) {
+				const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+				skip_prefix(refname, "refs/heads/", &refname);
+				filter.name_patterns[argv - filter.name_patterns] = refname;
+				break;
+			}
+			argv++;
+		}
+
 		/*
 		 * If no sorting parameter is given then we default to sorting
 		 * by 'refname'. This would give us an alphabetically sorted
Daniels Umanovskis· Oct 11, 2018, 16:36 UTC · re: Rafael Ascensão · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On 10/11/18 5:43 PM, Rafael Ascensão wrote:
Show 5 quoted lines
> I agree it feels a bit out of place, and still think that
> 
>     $ git branch --list HEAD
> 
> would be a good candidate to be taught how to print the current branch.

I am not a fan because it would be yet another inconsistency in the Git command interface. An argument given after git branch --list means a pattern for the branches to list. Making HEAD print the current branch would be an exception to what an argument in that place means. Yes, HEAD itself is a very special string in git, but I'm not a fan of syntax where a specific argument value does something very different from any other value in that place.

Jeff King· Oct 11, 2018, 17:51 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Thu, Oct 11, 2018 at 06:36:02PM +0200, Daniels Umanovskis wrote:
Show 14 quoted lines
> On 10/11/18 5:43 PM, Rafael Ascensão wrote:
> > I agree it feels a bit out of place, and still think that
> > 
> >     $ git branch --list HEAD
> > 
> > would be a good candidate to be taught how to print the current branch.
> 
> I am not a fan because it would be yet another inconsistency in the Git
> command interface. An argument given after git branch --list means a
> pattern for the branches to list. Making HEAD print the current branch
> would be an exception to what an argument in that place means. Yes, HEAD
> itself is a very special string in git, but I'm not a fan of syntax
> where a specific argument value does something very different from any
> other value in that place.
Yeah, I agree. If we were to go this route, it should probably be:
  git branch --list-head

Which sounds a lot like what you are proposing, but I think the name implies more strongly "show --list, but only for the HEAD". I.e., for the detached case, show the "HEAD detached at..." text.

-Peff
Rafael Ascensão· Oct 11, 2018, 20:35 UTC · re: Jeff King · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Thu, Oct 11, 2018 at 06:36:02PM +0200, Daniels Umanovskis wrote:
> I am not a fan because it would be yet another inconsistency in the Git
> command interface.

The output of the proposed command is also a bit inconsistent with the usual output given by git branch, specifically the space alignment on the left, color and * marker.

In addition to not respecting --color, it also ignores --verbose and --format. At this stage it's closer to what I would expect from $git rev-parse --abbrev-ref HEAD; than something coming out of $git branch; Resolving HEAD makes it consistent with rest.

On Thu, Oct 11, 2018 at 01:51:36PM -0400, Jeff King wrote:
> Yeah, I agree.

Not sure which parts you meant, so I'll assume you didn't agree with me.

I doesn't seem far fetched to ask for an overview of my current branch, feature1, feature2 and all hotfixes with something like:

  $ git branch --verbose --list HEAD feature1 feature2 hotfix-*;

The 'what's my current branch' could be just a particular case of this form.

My defense to treat HEAD specially comes in the form that from the user perspective, HEAD is already being resolved to a commit when HEAD is detached (Showing the detached at <hash> message.)

Is there a strong reason to *not* "resolve" HEAD when it is attached? Would it be that bad to have some DWIM behaviour here? After all, as HEAD is an invalid name for a branch, nothing would ever match it anyways.

Thanks for the input. :) -- Cheers Rafael Ascensão

Daniels Umanovskis· Oct 11, 2018, 20:46 UTC · re: Rafael Ascensão · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On 10/11/18 10:35 PM, Rafael Ascensão wrote:
> The output of the proposed command is also a bit inconsistent with the
> usual output given by git branch, specifically the space alignment on
> the left, color and * marker.

The proposed command therefore takes a new switch. It's definitely not perfect, but doesn't give the existing --list new and different behavior.

> At this stage it's closer to what I would expect from
> $git rev-parse --abbrev-ref HEAD;

The proposal is largely to have similar output to that command, yes. I expect that "show current branch" is something that's available in the branch command, even completely disregarding questions of whether it's API stable, etc.

Jeff King· Oct 11, 2018, 20:53 UTC · re: Rafael Ascensão · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Thu, Oct 11, 2018 at 09:35:28PM +0100, Rafael Ascensão wrote:
Show 5 quoted lines
> On Thu, Oct 11, 2018 at 01:51:36PM -0400, Jeff King wrote:
> > Yeah, I agree.
> 
> Not sure which parts you meant, so I'll assume you didn't agree
> with me.
Correct. ;)

I like your general idea, but I agree with Daniel that it introduces an inconsistency in the interface.

Show 7 quoted lines
> I doesn't seem far fetched to ask for an overview of my current branch,
> feature1, feature2 and all hotfixes with something like:
> 
>   $ git branch --verbose --list HEAD feature1 feature2 hotfix-*;
> 
> The 'what's my current branch' could be just a particular case of this
> form.

Right, I like that part. It's just that putting "HEAD" there already has a meaning: it would find refs/heads/HEAD.

Now I'll grant that's a bad name for a branch (and the source of other confusions, and I think perhaps even something a few commands actively discourage these days).

Show 8 quoted lines
> My defense to treat HEAD specially comes in the form that from the user
> perspective, HEAD is already being resolved to a commit when HEAD is
> detached (Showing the detached at <hash> message.)
> 
> Is there a strong reason to *not* "resolve" HEAD when it is attached?
> Would it be that bad to have some DWIM behaviour here? After all, as
> HEAD is an invalid name for a branch, nothing would ever match it
> anyways.

I don't think this is about resolving HEAD, or showing it. It's about the fact that arguments to "branch" are currently always branch-names, not full refs.

-Peff
Rafael Ascensão· Oct 11, 2018, 22:34 UTC · re: Jeff King · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Thu, Oct 11, 2018 at 04:53:23PM -0400, Jeff King wrote:
Show 7 quoted lines
> Right, I like that part. It's just that putting "HEAD" there already has
> a meaning: it would find refs/heads/HEAD.
> 
> Now I'll grant that's a bad name for a branch (and the source of other
> confusions, and I think perhaps even something a few commands actively
> discourage these days).
>

Makes sense. My whole premise was based on the fact that refs/heads/HEAD wouldn't be supported. Now it's obvious to me that isn't necessarily true. And now I understand the real issue. Thanks for bearing with me.

I also agree with your proposed '--list-head' suggestion.
So, ideally, instead of my broken suggestion of:
    $ git branch --verbose --list HEAD feature1 hotfix-*;
The equivalent would be:
    $ git branch --verbose --list-head --list feature1 hotfix-*;

and it would coalesce nicely as long as --list-head conforms with the default formatting for --list.

-- Cheers Rafael Ascensão

Junio C Hamano· Oct 11, 2018, 06:54 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

Daniels Umanovskis <daniels@umanovskis.se> writes:
Show 6 quoted lines
> +static void print_current_branch_name()
> +{
> +	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
> +	const char *shortname;
> +	if (refname == NULL || !strcmp(refname, "HEAD"))
> +		return;

Is it a normal situation to have refname==NULL, or is it something worth reporting as an error?

Without passing the &flag argument, I do not think there is a reliable way to ask resolve_ref_unsafe() if "HEAD" is a symbolic ref.

	int flag;
	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flag);
	const char *branchname;
	if (!refname)
		die(...);
	else if (!(flag & REF_ISSYMREF))
		return; /* detached HEAD */
	else if (skip_prefix(refname, "refs/heads/", &branchname))
		puts(branchname);
	else
		die("HEAD (%s) points outside refs/heads/?", refname);
or something like that?
Daniels Umanovskis· Oct 11, 2018, 17:29 UTC · re: Junio C Hamano · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On 10/11/18 8:54 AM, Junio C Hamano wrote:
> Is it a normal situation to have refname==NULL, or is it something
> worth reporting as an error?

Looks like that would be in the case of looping symrefs or file backend failure, so seems a good idea to die() in that case.

> Without passing the &flag argument, I do not think there is a
> reliable way to ask resolve_ref_unsafe() if "HEAD" is a symbolic
> ref.

If I'm reading the code correctly, resolve_ref_unsafe() will return "HEAD" or NULL if there's no symbolic reference, so anything else would indicate a symref, but even in that case checking the flag explicitly is definitely better to clearly show intent.

Will soon reply with v3 cleaning up the suggested patch accordingly.
Jeff King· Oct 11, 2018, 17:52 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Thu, Oct 11, 2018 at 07:29:58PM +0200, Daniels Umanovskis wrote:
Show 8 quoted lines
> > Without passing the &flag argument, I do not think there is a
> > reliable way to ask resolve_ref_unsafe() if "HEAD" is a symbolic
> > ref.
> 
> If I'm reading the code correctly, resolve_ref_unsafe() will return
> "HEAD" or NULL if there's no symbolic reference, so anything else would
> indicate a symref, but even in that case checking the flag explicitly is
> definitely better to clearly show intent.
Yes, that matches my understanding, too.
-Peff
Daniels Umanovskis· Oct 11, 2018, 22:20 UTC · re: Junio C Hamano · lore

[PATCH v3] branch: introduce --show-current display option

When called with --show-current, git branch will print the current branch name and terminate. Only the actual name gets printed, without refs/heads. In detached HEAD state, nothing is output.

Intended both for scripting and interactive/informative use. Unlike git branch --list, no filtering is needed to just get the branch name.

Signed-off-by: Daniels Umanovskis <daniels@umanovskis.se>
---

Cleaned up per suggestions, explicitly passing flags to clearly denote intent. If you consider the patch good conceptually, this implementation should hopefully be good enough to include.

 Documentation/git-branch.txt |  6 +++++-
 builtin/branch.c             | 25 ++++++++++++++++++++--
 t/t3203-branch-output.sh     | 41 ++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 3 deletions(-)
Show changes to 3 files +69 −3

Documentation/git-branch.txt, builtin/branch.c, t/t3203-branch-output.sh

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bf5316ffa..0babb9b1b 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]
-	[--list] [-v [--abbrev=<length> | --no-abbrev]]
+	[--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
 	[--column[=<options>] | --no-column] [--sort=<key>]
 	[(--merged | --no-merged) [<commit>]]
 	[--contains [<commit]] [--no-contains [<commit>]]
@@ -160,6 +160,10 @@ This option is only applicable in non-verbose mode.
 	branch --list 'maint-*'`, list only the branches that match
 	the pattern(s).
 
+--show-current::
+	Print the name of the current branch. In detached HEAD state,
+	nothing is printed.
+
 -v::
 -vv::
 --verbose::
diff --git a/builtin/branch.c b/builtin/branch.c
index c396c4153..46f91dc06 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -443,6 +443,21 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	free(to_free);
 }
 
+static void print_current_branch_name(void)
+{
+	int flags;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	const char *shortname;
+	if (!refname)
+		die(_("could not resolve HEAD"));
+	else if (!(flags & REF_ISSYMREF))
+		return;
+	else if (skip_prefix(refname, "refs/heads/", &shortname))
+		puts(shortname);
+	else
+		die(_("HEAD (%s) points outside of refs/heads/"), refname);
+}
+
 static void reject_rebase_or_bisect_branch(const char *target)
 {
 	struct worktree **worktrees = get_worktrees(0);
@@ -581,6 +596,7 @@ static int edit_branch_description(const char *branch_name)
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
+	int show_current = 0;
 	int reflog = 0, edit_description = 0;
 	int quiet = 0, unset_upstream = 0;
 	const char *new_upstream = NULL;
@@ -620,6 +636,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
 		OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
 		OPT_BOOL('l', "list", &list, N_("list branch names")),
+		OPT_BOOL(0, "show-current", &show_current, N_("show current branch name")),
 		OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
 		OPT_BOOL(0, "edit-description", &edit_description,
 			 N_("edit the description for the branch")),
@@ -662,14 +679,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 && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)
+	if (!delete && !rename && !copy && !edit_description && !new_upstream &&
+	    !show_current && !unset_upstream && argc == 0)
 		list = 1;
 
 	if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
 	    filter.no_commit)
 		list = 1;
 
-	if (!!delete + !!rename + !!copy + !!new_upstream +
+	if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
 	    list + unset_upstream > 1)
 		usage_with_options(builtin_branch_usage, options);
 
@@ -697,6 +715,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		if (!argc)
 			die(_("branch name required"));
 		return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
+	} else if (show_current) {
+		print_current_branch_name();
+		return 0;
 	} else if (list) {
 		/*  git branch --local also shows HEAD when it is detached */
 		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index ee6787614..8d2020aea 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -100,6 +100,47 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
 	test_must_fail git branch -v branch*
 '
 
+test_expect_success 'git branch `--show-current` shows current branch' '
+	cat >expect <<-\EOF &&
+	branch-two
+	EOF
+	git checkout branch-two &&
+	git branch --show-current >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch `--show-current` is silent when detached HEAD' '
+	git checkout HEAD^0 &&
+	git branch --show-current >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'git branch `--show-current` works properly when tag exists' '
+	cat >expect <<-\EOF &&
+	branch-and-tag-name
+	EOF
+	git checkout -b branch-and-tag-name &&
+	git tag branch-and-tag-name &&
+	git branch --show-current >actual &&
+	git checkout branch-one &&
+	git branch -d branch-and-tag-name &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch `--show-current` works properly with worktrees' '
+	cat >expect <<-\EOF &&
+	branch-one
+	branch-two
+	EOF
+	git checkout branch-one &&
+	git branch --show-current >actual &&
+	git worktree add worktree branch-two &&
+	cd worktree &&
+	git branch --show-current >>../actual &&
+	cd .. &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git branch shows detached HEAD properly' '
 	cat >expect <<EOF &&
 * (HEAD detached at $(git rev-parse --short HEAD^0))
-- 
2.19.1.329.gad8739a7f.dirty
Junio C Hamano· Oct 11, 2018, 23:15 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v3] branch: introduce --show-current display option

Daniels Umanovskis <daniels@umanovskis.se> writes:
> +static void print_current_branch_name(void)

Thanks for fixing this (I fixed this in the previous round in my tree but forgot to tell you about it).

Show 16 quoted lines
> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> index ee6787614..8d2020aea 100755
> --- a/t/t3203-branch-output.sh
> +++ b/t/t3203-branch-output.sh
> @@ -100,6 +100,47 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
>  	test_must_fail git branch -v branch*
>  '
>  
> +test_expect_success 'git branch `--show-current` shows current branch' '
> +	cat >expect <<-\EOF &&
> +	branch-two
> +	EOF
> +	git checkout branch-two &&
> +	git branch --show-current >actual &&
> +	test_cmp expect actual
> +'

OK, that's trivial. We checkout a branch and make sure show-current reports the name of that branch. Good.

Show 5 quoted lines
> +test_expect_success 'git branch `--show-current` is silent when detached HEAD' '
> +	git checkout HEAD^0 &&
> +	git branch --show-current >actual &&
> +	test_must_be_empty actual
> +'

OK, and at the same time we make sure the command exits with success. Good.

Show 11 quoted lines
> +test_expect_success 'git branch `--show-current` works properly when tag exists' '
> +	cat >expect <<-\EOF &&
> +	branch-and-tag-name
> +	EOF
> +	git checkout -b branch-and-tag-name &&
> +	git tag branch-and-tag-name &&
> +	git branch --show-current >actual &&
> +	git checkout branch-one &&
> +	git branch -d branch-and-tag-name &&
> +	test_cmp expect actual
> +'

It is a bit curious why you remove the branch but not the tag after this test. If we are cleaning after ourselves, removing both would be equally good, if not cleaner. If having both absolutely harms later tests but having just one is OK, then any failure in this test between the time branch-and-tag-name tag gets created and the time branch-and-tag-name branch gets removed will leave the repository with both the tag and the branch, which will be the state in which later tests start, so having "branch -d" at this spot in the sequence is not a good idea anyway.

So two equally valid choices are to remove "branch -d" and then either:

 (1) leave both branch and tag after this test in the test
     repository
 (2) use test_when_finished, i.e.
	echo branch-and-tag-name >expect &&
	test_when_finished "git branch -D branch-and-tag-name" &&
	git checkout -b branch-and-tag-name &&
	test_when_finished "git tag -d branch-and-tag-name" &&
	git tag branch-and-tag-name &&
	...
     to arrange them to be cleaned once this test is done.

(1) is only valid if they do not harm later tests. I guess you remove the branch because you did not want to touch later tests that checks output from "git branch --list", in which case you'd want (2).

Show 13 quoted lines
> +test_expect_success 'git branch `--show-current` works properly with worktrees' '
> +	cat >expect <<-\EOF &&
> +	branch-one
> +	branch-two
> +	EOF
> +	git checkout branch-one &&
> +	git branch --show-current >actual &&
> +	git worktree add worktree branch-two &&
> +	cd worktree &&
> +	git branch --show-current >>../actual &&
> +	cd .. &&
> +	test_cmp expect actual
> +'

Please do *not* cd around without being in a subshell. If the second --show-current failed for some reason, "cd .." will not be executed, and the next and subsequent test will start inside ./worktree subdirectory, which is likely to break the expectations of them. Perhaps something like

	git checkout branch-one &&
	git worktree add worktree branch-two &&
	(
		git branch --show-current &&
		cd worktree && git branch --show-current
	) >actual &&
	test_cmp expect actual
or its modern equivalent
	git checkout branch-one &&
	git worktree add worktree branch-two &&
	(
		git branch --show-current &&
		git -C worktree branch --show-current
	) >actual &&
	test_cmp expect actual
Note that the latter _could_ be written without subshell, i.e.
	git branch --show-current >actual &&
	git -C worktree branch --show-current >>actual &&

but I personally tend to prefer with a single redirection into ">actual", as that is easier to later add _more_ commands to redirect into 'actual' to be inspected without having to worry about details like repeating ">>actual" or only the first one must be ">actual" (iow, the preference comes mostly from maintainability concerns).

Thanks.
> +
>  test_expect_success 'git branch shows detached HEAD properly' '
>  	cat >expect <<EOF &&
>  * (HEAD detached at $(git rev-parse --short HEAD^0))
Daniels Umanovskis· Oct 11, 2018, 23:31 UTC · re: Junio C Hamano · lore

Re: [PATCH v3] branch: introduce --show-current display option

On 10/12/18 1:15 AM, Junio C Hamano wrote:
Show 10 quoted lines
> It is a bit curious why you remove the branch but not the tag after
> this test.  [..]
> 
> So two equally valid choices are to remove "branch -d" and then
> either:
> 
>  (1) leave both branch and tag after this test in the test
>      repository
> 
>  (2) use test_when_finished [..]

Thanks for this explanation! You're right, I removed the branch because it otherwise breaks subsequent tests, while the tag doesn't matter. I'll go take a look at how test_when_finished can be used.

> Please do *not* cd around without being in a subshell.  
Understood, thanks for explaining this as well.
Daniels Umanovskis· Oct 12, 2018, 13:33 UTC · re: Junio C Hamano · lore

[PATCH v4] branch: introduce --show-current display option

When called with --show-current, git branch will print the current branch name and terminate. Only the actual name gets printed, without refs/heads. In detached HEAD state, nothing is output.

Intended both for scripting and interactive/informative use. Unlike git branch --list, no filtering is needed to just get the branch name.

Signed-off-by: Daniels Umanovskis <daniels@umanovskis.se>
---
Compared to v3, fixed up test cases according to Junio's input
 Documentation/git-branch.txt |  6 +++++-
 builtin/branch.c             | 25 +++++++++++++++++++++++--
 t/t3203-branch-output.sh     | 43 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+), 3 deletions(-)
Show changes to 3 files +71 −3

Documentation/git-branch.txt, builtin/branch.c, t/t3203-branch-output.sh

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bf5316ffa..0babb9b1b 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]
-	[--list] [-v [--abbrev=<length> | --no-abbrev]]
+	[--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
 	[--column[=<options>] | --no-column] [--sort=<key>]
 	[(--merged | --no-merged) [<commit>]]
 	[--contains [<commit]] [--no-contains [<commit>]]
@@ -160,6 +160,10 @@ This option is only applicable in non-verbose mode.
 	branch --list 'maint-*'`, list only the branches that match
 	the pattern(s).
 
+--show-current::
+	Print the name of the current branch. In detached HEAD state,
+	nothing is printed.
+
 -v::
 -vv::
 --verbose::
diff --git a/builtin/branch.c b/builtin/branch.c
index c396c4153..46f91dc06 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -443,6 +443,21 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	free(to_free);
 }
 
+static void print_current_branch_name(void)
+{
+	int flags;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	const char *shortname;
+	if (!refname)
+		die(_("could not resolve HEAD"));
+	else if (!(flags & REF_ISSYMREF))
+		return;
+	else if (skip_prefix(refname, "refs/heads/", &shortname))
+		puts(shortname);
+	else
+		die(_("HEAD (%s) points outside of refs/heads/"), refname);
+}
+
 static void reject_rebase_or_bisect_branch(const char *target)
 {
 	struct worktree **worktrees = get_worktrees(0);
@@ -581,6 +596,7 @@ static int edit_branch_description(const char *branch_name)
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
+	int show_current = 0;
 	int reflog = 0, edit_description = 0;
 	int quiet = 0, unset_upstream = 0;
 	const char *new_upstream = NULL;
@@ -620,6 +636,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
 		OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
 		OPT_BOOL('l', "list", &list, N_("list branch names")),
+		OPT_BOOL(0, "show-current", &show_current, N_("show current branch name")),
 		OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
 		OPT_BOOL(0, "edit-description", &edit_description,
 			 N_("edit the description for the branch")),
@@ -662,14 +679,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 && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)
+	if (!delete && !rename && !copy && !edit_description && !new_upstream &&
+	    !show_current && !unset_upstream && argc == 0)
 		list = 1;
 
 	if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
 	    filter.no_commit)
 		list = 1;
 
-	if (!!delete + !!rename + !!copy + !!new_upstream +
+	if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
 	    list + unset_upstream > 1)
 		usage_with_options(builtin_branch_usage, options);
 
@@ -697,6 +715,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		if (!argc)
 			die(_("branch name required"));
 		return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
+	} else if (show_current) {
+		print_current_branch_name();
+		return 0;
 	} else if (list) {
 		/*  git branch --local also shows HEAD when it is detached */
 		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index ee6787614..1bf708dff 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -100,6 +100,49 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
 	test_must_fail git branch -v branch*
 '
 
+test_expect_success 'git branch `--show-current` shows current branch' '
+	cat >expect <<-\EOF &&
+	branch-two
+	EOF
+	git checkout branch-two &&
+	git branch --show-current >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch `--show-current` is silent when detached HEAD' '
+	git checkout HEAD^0 &&
+	git branch --show-current >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'git branch `--show-current` works properly when tag exists' '
+	cat >expect <<-\EOF &&
+	branch-and-tag-name
+	EOF
+	test_when_finished "git branch -D branch-and-tag-name" &&
+	git checkout -b branch-and-tag-name &&
+	test_when_finished "git tag -d branch-and-tag-name" &&
+	git tag branch-and-tag-name &&
+	git branch --show-current >actual &&
+	git checkout branch-one &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git branch `--show-current` works properly with worktrees' '
+	cat >expect <<-\EOF &&
+	branch-one
+	branch-two
+	EOF
+	git checkout branch-one &&
+	git worktree add worktree branch-two &&
+	(
+		git branch --show-current &&
+		cd worktree &&
+		git branch --show-current
+	) >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git branch shows detached HEAD properly' '
 	cat >expect <<EOF &&
 * (HEAD detached at $(git rev-parse --short HEAD^0))
-- 
2.11.0
Eric Sunshine· Oct 12, 2018, 13:43 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v4] branch: introduce --show-current display option

On Fri, Oct 12, 2018 at 9:34 AM Daniels Umanovskis <daniels@umanovskis.se> wrote:

Show 18 quoted lines
> When called with --show-current, git branch will print the current
> branch name and terminate. Only the actual name gets printed,
> without refs/heads. In detached HEAD state, nothing is output.
>
> Signed-off-by: Daniels Umanovskis <daniels@umanovskis.se>
> ---
> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> @@ -100,6 +100,49 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
> +test_expect_success 'git branch `--show-current` works properly when tag exists' '
> +       cat >expect <<-\EOF &&
> +       branch-and-tag-name
> +       EOF
> +       test_when_finished "git branch -D branch-and-tag-name" &&
> +       git checkout -b branch-and-tag-name &&
> +       test_when_finished "git tag -d branch-and-tag-name" &&
> +       git tag branch-and-tag-name &&
> +       git branch --show-current >actual &&
> +       git checkout branch-one &&

This cleanup "checkout" needs to be encapsulated within a test_when_finished(), doesn't it? Preferably just after the "git checkout -b" invocation.

> +       test_cmp expect actual
> +'
Junio C Hamano· Oct 16, 2018, 23:09 UTC · re: Eric Sunshine · lore

Re: [PATCH v4] branch: introduce --show-current display option

Eric Sunshine <sunshine@sunshineco.com> writes:
Show 14 quoted lines
>> +test_expect_success 'git branch `--show-current` works properly when tag exists' '
>> +       cat >expect <<-\EOF &&
>> +       branch-and-tag-name
>> +       EOF
>> +       test_when_finished "git branch -D branch-and-tag-name" &&
>> +       git checkout -b branch-and-tag-name &&
>> +       test_when_finished "git tag -d branch-and-tag-name" &&
>> +       git tag branch-and-tag-name &&
>> +       git branch --show-current >actual &&
>> +       git checkout branch-one &&
>
> This cleanup "checkout" needs to be encapsulated within a
> test_when_finished(), doesn't it? Preferably just after the "git
> checkout -b" invocation.
In the meantime, here is what I'll have in 'pu' on top.
 t/t3203-branch-output.sh | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
Show changes to t/t3203-branch-output.sh +5 −4
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 1bf708dffc..d1f4fec9de 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -119,12 +119,14 @@ test_expect_success 'git branch `--show-current` works properly when tag exists'
 	cat >expect <<-\EOF &&
 	branch-and-tag-name
 	EOF
-	test_when_finished "git branch -D branch-and-tag-name" &&
+	test_when_finished "
+		git checkout branch-one
+		git branch -D branch-and-tag-name
+	" &&
 	git checkout -b branch-and-tag-name &&
 	test_when_finished "git tag -d branch-and-tag-name" &&
 	git tag branch-and-tag-name &&
 	git branch --show-current >actual &&
-	git checkout branch-one &&
 	test_cmp expect actual
 '
 
@@ -137,8 +139,7 @@ test_expect_success 'git branch `--show-current` works properly with worktrees'
 	git worktree add worktree branch-two &&
 	(
 		git branch --show-current &&
-		cd worktree &&
-		git branch --show-current
+		git -C worktree branch --show-current
 	) >actual &&
 	test_cmp expect actual
 '
-- 
2.19.1-328-g5a0cc8aca7
Eric Sunshine· Oct 16, 2018, 23:26 UTC · re: Junio C Hamano · lore

Re: [PATCH v4] branch: introduce --show-current display option

On Tue, Oct 16, 2018 at 7:09 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 24 quoted lines
> Eric Sunshine <sunshine@sunshineco.com> writes:
> > This cleanup "checkout" needs to be encapsulated within a
> > test_when_finished(), doesn't it? Preferably just after the "git
> > checkout -b" invocation.
>
> In the meantime, here is what I'll have in 'pu' on top.
>
> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> @@ -119,12 +119,14 @@ test_expect_success 'git branch `--show-current` works properly when tag exists'
>         cat >expect <<-\EOF &&
>         branch-and-tag-name
>         EOF
> -       test_when_finished "git branch -D branch-and-tag-name" &&
> +       test_when_finished "
> +               git checkout branch-one
> +               git branch -D branch-and-tag-name
> +       " &&
>         git checkout -b branch-and-tag-name &&
>         test_when_finished "git tag -d branch-and-tag-name" &&
>         git tag branch-and-tag-name &&
>         git branch --show-current >actual &&
> -       git checkout branch-one &&
>         test_cmp expect actual
>  '
This make sense to me.
Show 10 quoted lines
> @@ -137,8 +139,7 @@ test_expect_success 'git branch `--show-current` works properly with worktrees'
>         git worktree add worktree branch-two &&
>         (
>                 git branch --show-current &&
> -               cd worktree &&
> -               git branch --show-current
> +               git -C worktree branch --show-current
>         ) >actual &&
>         test_cmp expect actual
>  '

The subshell '(...)' could become '{...}' now that the 'cd' is gone, but that's a minor point.

Johannes Schindelin· Oct 17, 2018, 10:18 UTC · re: Eric Sunshine · lore

Re: [PATCH v4] branch: introduce --show-current display option

Hi Eric,
On Tue, 16 Oct 2018, Eric Sunshine wrote:
Show 41 quoted lines
> On Tue, Oct 16, 2018 at 7:09 PM Junio C Hamano <gitster@pobox.com> wrote:
> > Eric Sunshine <sunshine@sunshineco.com> writes:
> > > This cleanup "checkout" needs to be encapsulated within a
> > > test_when_finished(), doesn't it? Preferably just after the "git
> > > checkout -b" invocation.
> >
> > In the meantime, here is what I'll have in 'pu' on top.
> >
> > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> > @@ -119,12 +119,14 @@ test_expect_success 'git branch `--show-current` works properly when tag exists'
> >         cat >expect <<-\EOF &&
> >         branch-and-tag-name
> >         EOF
> > -       test_when_finished "git branch -D branch-and-tag-name" &&
> > +       test_when_finished "
> > +               git checkout branch-one
> > +               git branch -D branch-and-tag-name
> > +       " &&
> >         git checkout -b branch-and-tag-name &&
> >         test_when_finished "git tag -d branch-and-tag-name" &&
> >         git tag branch-and-tag-name &&
> >         git branch --show-current >actual &&
> > -       git checkout branch-one &&
> >         test_cmp expect actual
> >  '
> 
> This make sense to me.
> 
> > @@ -137,8 +139,7 @@ test_expect_success 'git branch `--show-current` works properly with worktrees'
> >         git worktree add worktree branch-two &&
> >         (
> >                 git branch --show-current &&
> > -               cd worktree &&
> > -               git branch --show-current
> > +               git -C worktree branch --show-current
> >         ) >actual &&
> >         test_cmp expect actual
> >  '
> 
> The subshell '(...)' could become '{...}' now that the 'cd' is gone,
> but that's a minor point.
Maybe not so minor.

I realized yesterday that the &&-chain linting we use for every single test case takes a noticeable chunk of time:

	$ time ./t0006-date.sh --quiet
	# passed all 67 test(s)
	1..67
	real    0m20.973s
	user    0m2.662s
	sys     0m14.789s
	$ time ./t0006-date.sh --quiet --no-chain-lint
	# passed all 67 test(s)
	1..67
	real    0m13.607s
	user    0m1.330s
	sys     0m8.070s

My suspicion: it is essentially the `(exit 117)` that adds about 100ms to every of those 67 test cases.

(Remember: a subshell requires a fork, and the `fork()` emulation on Windows requires all kinds of things to be copied to a new process, including memory and open file descriptors, before the `exec()` will undo at least part of that.)

With that in mind, I would like to suggest that we should start to be very careful about using subshells in our test suite.

Ciao, Dscho

Eric Sunshine· Oct 17, 2018, 10:39 UTC · re: Johannes Schindelin · lore

Re: [PATCH v4] branch: introduce --show-current display option

On Wed, Oct 17, 2018 at 6:18 AM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
Show 10 quoted lines
> I realized yesterday that the &&-chain linting we use for every single
> test case takes a noticeable chunk of time:
>
>         $ time ./t0006-date.sh --quiet
>         real    0m20.973s
>         $ time ./t0006-date.sh --quiet --no-chain-lint
>         real    0m13.607s
>
> My suspicion: it is essentially the `(exit 117)` that adds about 100ms to
> every of those 67 test cases.
The subshell chain-linter adds a 'sed' and 'grep' invocation to each test which doesn't help. (v1 of the subshell chain-linter only added a 'sed', but that changed with v2.)
> With that in mind, I would like to suggest that we should start to be very
> careful about using subshells in our test suite.
You could disable the subshell chain-linter like this if you want test the (exit 117) goop in isolation:
--- 8< ---
Show changes to t/test-lib.sh +1 −2
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 3f95bfda60..48323e503c 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -675,8 +675,7 @@ test_run_ () {
 		trace=
 		# 117 is magic because it is unlikely to match the exit
 		# code of other programs
-		if $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!') ||
-			test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+		if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
 		then
 			error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
 		fi
--- 8< ---
Johannes Schindelin· Oct 18, 2018, 09:51 UTC · re: Eric Sunshine · lore

Re: [PATCH v4] branch: introduce --show-current display option

Hi Eric,
On Wed, 17 Oct 2018, Eric Sunshine wrote:
Show 35 quoted lines
> On Wed, Oct 17, 2018 at 6:18 AM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > I realized yesterday that the &&-chain linting we use for every single
> > test case takes a noticeable chunk of time:
> >
> >         $ time ./t0006-date.sh --quiet
> >         real    0m20.973s
> >         $ time ./t0006-date.sh --quiet --no-chain-lint
> >         real    0m13.607s
> >
> > My suspicion: it is essentially the `(exit 117)` that adds about 100ms to
> > every of those 67 test cases.
> 
> The subshell chain-linter adds a 'sed' and 'grep' invocation to each test which doesn't help. (v1 of the subshell chain-linter only added a 'sed', but that changed with v2.)
> 
> > With that in mind, I would like to suggest that we should start to be very
> > careful about using subshells in our test suite.
> 
> You could disable the subshell chain-linter like this if you want test the (exit 117) goop in isolation:
> 
> --- 8< ---
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index 3f95bfda60..48323e503c 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -675,8 +675,7 @@ test_run_ () {
>  		trace=
>  		# 117 is magic because it is unlikely to match the exit
>  		# code of other programs
> -		if $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!') ||
> -			test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
> +		if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
>  		then
>  			error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
>  		fi
> --- 8< ---

You're right! This is actually responsible for about five of those seven seconds. The subshell still hurts a little, as it means that every single of the almost 20,000 test cases we have gets slowed down by ~0.03s, which amounts to almost 10 minutes.

This is "only" for the Windows phase of our Continuous Testing, of course. Yet I think we can do better than this.

How difficult/involved, do you think, would it be to add a t/helper/ command for chain linting?

Ciao, Dscho

Eric Sunshine· Oct 18, 2018, 14:19 UTC · re: Johannes Schindelin · lore

Re: [PATCH v4] branch: introduce --show-current display option

On Thu, Oct 18, 2018 at 5:51 AM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

Show 18 quoted lines
> On Wed, 17 Oct 2018, Eric Sunshine wrote:
> > On Wed, Oct 17, 2018 at 6:18 AM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > > My suspicion: it is essentially the `(exit 117)` that adds about 100ms to
> > > every of those 67 test cases.
> >
> > The subshell chain-linter adds a 'sed' and 'grep' invocation to each test which doesn't help. (v1 of the subshell chain-linter only added a 'sed', but that changed with v2.)
> > You could disable the subshell chain-linter like this if you want test the (exit 117) goop in isolation:
>
> You're right! This is actually responsible for about five of those seven
> seconds. The subshell still hurts a little, as it means that every single
> of the almost 20,000 test cases we have gets slowed down by ~0.03s, which
> amounts to almost 10 minutes.
>
> This is "only" for the Windows phase of our Continuous Testing, of course.
> Yet I think we can do better than this.
>
> How difficult/involved, do you think, would it be to add a t/helper/
> command for chain linting?

Probably more effort than it's worth, and it would only save one process invocation.

Since the subshell portion of the chain-linting is done by pure textual inspection, an alternative I had considered was to just perform it as a preprocess over the entire test suite, much like the other t/Makefile "test-lint" targets. In other words, the entire test suite might be tested in one go with something like this:

    sed -f chainlint.sed t*.sh | grep -q '?![A-Z][A-Z]*?!' &&
        echo "BROKEN &&-chain"

That won't work today since chainlint.sed isn't written to understand everything which we might see outside of a test_expect_*, but doing it that way is within the realm of possibility. There were two reasons why I didn't pursue that approach.

First, although I was expecting Windows folks to complain (or at least speak up) about the extra 'sed' and 'grep', nobody did, so my impression was that those two extra commands were likely lost in the noise of the rest of the boilerplate commands invoked by test_expect_success(), test_run_(), test_eval_(), etc., and by whatever expensive commands are invoked by each test itself. Second, the top-level &&-chain "(exit 117)" linting kicks in even when you run a single test script manually, say after editing a test, which is exactly when you want to discover that you botched a &&-chain, so it seemed a good idea for the subshell &&-chain linter to follow suit. The t/Makefile "test-lint" targets, on the other hand, don't kick in when running test scripts in isolation.

However, a pragmatic way to gain back those 10 minutes might be simply to disable the chain-linter for continuous integration testing on Windows, but leave it enabled on other platforms. This way, we'd still catch broken &&-chains, with the exception of tests which are specific to Windows, of which I think there are very few.

Junio C Hamano· Oct 16, 2018, 05:59 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v4] branch: introduce --show-current display option

Daniels Umanovskis <daniels@umanovskis.se> writes:
Show 11 quoted lines
> +test_expect_success 'git branch `--show-current` works properly with worktrees' '
> +	cat >expect <<-\EOF &&
> +	branch-one
> +	branch-two
> +	EOF
> +	git checkout branch-one &&
> +	git worktree add worktree branch-two &&
> +	(
> +		git branch --show-current &&
> +		cd worktree &&
> +		git branch --show-current
This is not wrong per-se, but
		git branch --show-current &&
		git -C worktree branch --show-current
would be shorter.
> +	) >actual &&
> +	test_cmp expect actual
> +'
Rafael Ascensão· Oct 17, 2018, 09:39 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v4] branch: introduce --show-current display option

On Fri, Oct 12, 2018 at 03:33:21PM +0200, Daniels Umanovskis wrote:
> Intended both for scripting and interactive/informative use.
> Unlike git branch --list, no filtering is needed to just get the
> branch name.
Are we going forward with advertising this as a scriptable alternative?
> +	} else if (show_current) {
> +		print_current_branch_name();
> +		return 0;

Do we need the slightly different check done in print_current_branch_name() ? A very similar check is already done early in cmd_branch.

builtin/branch.c:671
	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
	if (!head)
		die(_("Failed to resolve HEAD as a valid ref."));
	if (!strcmp(head, "HEAD"))
		filter.detached = 1;
	else if (!skip_prefix(head, "refs/heads/", &head))
		die(_("HEAD not found below refs/heads!"));
What's being proposed can be achieved with

+ } else if (show_current) { + if (!filter.detached) + puts(head); + return 0;

without failing tests.

-- Cheers, Rafael Ascensão

Daniels Umanovskis· Oct 17, 2018, 17:36 UTC · re: Rafael Ascensão · lore

Re: [PATCH v4] branch: introduce --show-current display option

On 10/17/18 11:39 AM, Rafael Ascensão wrote:
Show 6 quoted lines
> On Fri, Oct 12, 2018 at 03:33:21PM +0200, Daniels Umanovskis wrote:
>> Intended both for scripting and interactive/informative use.
>> Unlike git branch --list, no filtering is needed to just get the
>> branch name.
> 
> Are we going forward with advertising this as a scriptable alternative?

That's probably up to the maintainers, but I would not explicitly point it out as a script command, so my patch doesn't mention scripting use in the documentation for it. In reality it's useful for "soft scripting" like setting the shell $PS1, which doesn't require API stability guarantees the way proper scripts do.

SZEDER Gábor· Oct 11, 2018, 22:53 UTC · re: Daniels Umanovskis · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Wed, Oct 10, 2018 at 10:54:32PM +0200, Daniels Umanovskis wrote:
[...]
Show 13 quoted lines
> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> index ee6787614..e9bc3b05f 100755
> --- a/t/t3203-branch-output.sh
> +++ b/t/t3203-branch-output.sh
> @@ -100,6 +100,47 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
>  	test_must_fail git branch -v branch*
>  '
>  
> +test_expect_success 'git branch `--show-current` shows current branch' '
> +	cat >expect <<-\EOF &&
> +	branch-two
> +	EOF
> +	git checkout branch-two &&
Up to this point everything talked about '--show-current' ...
> +	git branch --current >actual &&
... but here and in all the following tests you run
  git branch --current
which then fails with "error: unknown option `current'"
Show 41 quoted lines
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'git branch `--show-current` is silent when detached HEAD' '
> +	git checkout HEAD^0 &&
> +	git branch --current >actual &&
> +	test_must_be_empty actual
> +'
> +
> +test_expect_success 'git branch `--show-current` works properly when tag exists' '
> +	cat >expect <<-\EOF &&
> +	branch-and-tag-name
> +	EOF
> +	git checkout -b branch-and-tag-name &&
> +	git tag branch-and-tag-name &&
> +	git branch --current >actual &&
> +	git checkout branch-one &&
> +	git branch -d branch-and-tag-name &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'git branch `--show-current` works properly with worktrees' '
> +	cat >expect <<-\EOF &&
> +	branch-one
> +	branch-two
> +	EOF
> +	git checkout branch-one &&
> +	git branch --current >actual &&
> +	git worktree add worktree branch-two &&
> +	cd worktree &&
> +	git branch --current >>../actual &&
> +	cd .. &&
> +	test_cmp expect actual
> +'
> +
>  test_expect_success 'git branch shows detached HEAD properly' '
>  	cat >expect <<EOF &&
>  * (HEAD detached at $(git rev-parse --short HEAD^0))
> -- 
> 2.19.1.330.g93276587c.dirty
> 
SZEDER Gábor· Oct 11, 2018, 22:56 UTC · re: SZEDER Gábor · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On Fri, Oct 12, 2018 at 12:53:26AM +0200, SZEDER Gábor wrote:
Show 27 quoted lines
> On Wed, Oct 10, 2018 at 10:54:32PM +0200, Daniels Umanovskis wrote:
> 
> [...]
> 
> > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
> > index ee6787614..e9bc3b05f 100755
> > --- a/t/t3203-branch-output.sh
> > +++ b/t/t3203-branch-output.sh
> > @@ -100,6 +100,47 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
> >  	test_must_fail git branch -v branch*
> >  '
> >  
> > +test_expect_success 'git branch `--show-current` shows current branch' '
> > +	cat >expect <<-\EOF &&
> > +	branch-two
> > +	EOF
> > +	git checkout branch-two &&
> 
> Up to this point everything talked about '--show-current' ...
> 
> > +	git branch --current >actual &&
> 
> ... but here and in all the following tests you run
> 
>   git branch --current
> 
> which then fails with "error: unknown option `current'"
Ah, OK, just noticed v3 which has already fixed this.
Daniels Umanovskis· Oct 11, 2018, 22:58 UTC · re: SZEDER Gábor · lore

Re: [PATCH v2 1/1] branch: introduce --show-current display option

On 10/12/18 12:56 AM, SZEDER Gábor wrote:
> Ah, OK, just noticed v3 which has already fixed this.
> 

Yeah - squashed the wrong commits locally for v2. Thanks for pointing this out anyway!

← back to recent threads