[1/2] branch: introduce --current display option
diff mbox series

Message ID 20181009183114.16477-1-daniels@umanovskis.se
State New
Headers show
Series
  • [1/2] branch: introduce --current display option
Related show

Commit Message

Daniels Umanovskis Oct. 9, 2018, 6:31 p.m. UTC
When called with --current, git branch will print the current
branch name and terminate. It will print HEAD in detached-head state.

Rationale: finding out the current branch is useful interactively,
but especially in scripting. git branch --list prints many branches,
and prepends the current one with an asterisk, meaning sed or other
filtering is necessary to just get the current branch.
git rev-parse --abbrev-ref HEAD is the current way to achieve this
output, but that is not intuitive or easy to understand.

Signed-off-by: Daniels Umanovskis <daniels@umanovskis.se>
---
 builtin/branch.c         | 17 +++++++++++++++++
 t/t3203-branch-output.sh | 18 ++++++++++++++++++
 2 files changed, 35 insertions(+)

Comments

Stefan Beller Oct. 9, 2018, 7:54 p.m. UTC | #1
Welcome to the git mailing list!

On Tue, Oct 9, 2018 at 11:31 AM Daniels Umanovskis
<daniels@umanovskis.se> wrote:
>
> When called with --current, git branch will print the current
> branch name and terminate. It will print HEAD in detached-head state.

How does it play with worktrees? (I would expect it to just work as expected:
each worktree would print its current branch, but a test might help?)

> Rationale: finding out the current branch is useful interactively,
> but especially in scripting. git branch --list prints many branches,
> and prepends the current one with an asterisk, meaning sed or other
> filtering is necessary to just get the current branch.
> git rev-parse --abbrev-ref HEAD is the current way to achieve this
> output, but that is not intuitive or easy to understand.

Git used to have (and still has) the approach of dividing its commands
into high level ("porcelain") commands and low level ("plumbing") commands,
with the porcelain facing the user and plumbing being good for scripting.

This patch proposes a radically different approach, which is convenience
of use.

As a scripter you'd need to find out if "branch --current" is stable in its API
or if you'd rather parse it out of the branch list, which adds a subtle burden
to the scripter, as it is convenient to leave out that part and just use what is
there. :-)

> +static void print_current_branch_name()
> +{
> +       struct strbuf out = STRBUF_INIT;
> +       const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
> +       char *shortname = shorten_unambiguous_ref(refname, 0);


> +       strbuf_addf(&out, _("%s"), shortname);
> +       fwrite(out.buf, 1, out.len, stdout);

Why do we need to add the shortname to a strbuf?
(and using _( ) that denotes the string should be translated?)
I would think we could just

    puts(shortname)

here and leave out all the strbuf out ?


> @@ -620,6 +633,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, "current", &current, N_("show current branch name")),

(Gah, we're not using OPT_MODE here to select for one out of many.)

Later we have it in code via

    if (!!delete + !!rename + !!new_upstream +
        list + unset_upstream > 1)
            usage_with_options(builtin_branch_usage, options);

and I would think we'd want to add in a "+ !!current" there, too.
Then we'd get the usage options when giving --current in combination
with say --move
Daniels Umanovskis Oct. 9, 2018, 8:21 p.m. UTC | #2
Thanks for the feedback!

On 10/9/18 9:54 PM, Stefan Beller wrote:
> How does it play with worktrees? (I would expect it to just work as expected:
> each worktree would print its current branch, but a test might help?)

I'll see about writing a test for that. I've never used git-worktree so
this is a good chance to learn.

> Git used to have (and still has) the approach of dividing its commands
> into high level ("porcelain") commands and low level ("plumbing") commands,
> with the porcelain facing the user and plumbing being good for scripting.
> 
> This patch proposes a radically different approach, which is convenience
> of use.

Right - a couple of points in response. First, it strikes me that "print
current branch" is one of those things that are both for scripting and
for normal/interactive use. Much like the entirety of git-status, which
is very used by itself, and also in scripts with --porcelain. Current
branch is often used in things like your shell prompt, vim statusline,
etc, as well as scripts.

The amount of upvotes on the stackoverflow question "how to get the
current branch name in git?" illustrates my point :)

Second, it seems arguable whether `git ref-parse` is guaranteed to be
scripting-safe, it's not actually a plumbing command, at least if we
trust the main git manpage, which lists git-rev-parse as porcelain anyway.

> Why do we need to add the shortname to a strbuf?
> (and using _( ) that denotes the string should be translated?)
> I would think we could just
> 
>     puts(shortname)
> 
> here and leave out all the strbuf out ?
> 
Of course, has to be changed for v2. Pitfalls of learning the code from
just its surroundings, didn't realize that the underscores are for i18n.

Patch
diff mbox series

diff --git a/builtin/branch.c b/builtin/branch.c
index c396c4153..e4c6b0490 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -443,6 +443,18 @@  static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	free(to_free);
 }
 
+static void print_current_branch_name()
+{
+	struct strbuf out = STRBUF_INIT;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	char *shortname = shorten_unambiguous_ref(refname, 0);
+	strbuf_addf(&out, _("%s"), shortname);
+	fwrite(out.buf, 1, out.len, stdout);
+	putchar('\n');
+	free(shortname);
+	strbuf_release(&out);
+}
+
 static void reject_rebase_or_bisect_branch(const char *target)
 {
 	struct worktree **worktrees = get_worktrees(0);
@@ -581,6 +593,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 current = 0;
 	int reflog = 0, edit_description = 0;
 	int quiet = 0, unset_upstream = 0;
 	const char *new_upstream = NULL;
@@ -620,6 +633,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, "current", &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")),
@@ -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 (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..396d81568 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -100,6 +100,24 @@  test_expect_success 'git branch -v pattern does not show branch summaries' '
 	test_must_fail git branch -v branch*
 '
 
+test_expect_success 'git branch `--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 `--current` shows detached HEAD properly' '
+	cat >expect <<-\EOF &&
+	HEAD
+	EOF
+    git checkout HEAD^0 &&
+	git branch --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))