{"thread":{"id":"49517","subject":"[PATCH 1/2] branch: introduce --current display option","startedAt":"2018-10-09T18:31:28Z","lastAt":"2018-10-10T02:48:10Z","messageCount":5,"participants":["Daniels Umanovskis","Stefan Beller","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"359933","messageId":"20181009183114.16477-1-daniels@umanovskis.se","threadId":"49517","inReplyTo":null,"subject":"[PATCH 1/2] branch: introduce --current display option","fromName":"Daniels Umanovskis","fromEmail":"daniels@umanovskis.se","sentAt":"2018-10-09T18:31:13Z","receivedAt":"2018-10-09T18:31:28Z","isPatch":true,"sender":{"key":"daniels@umanovskis.se","avatar":"https://avatars.githubusercontent.com/u/5055233?v=4"},"body":"When called with --current, git branch will print the current\nbranch name and terminate. It will print HEAD in detached-head state.\n\nRationale: finding out the current branch is useful interactively,\nbut especially in scripting. git branch --list prints many branches,\nand prepends the current one with an asterisk, meaning sed or other\nfiltering is necessary to just get the current branch.\ngit rev-parse --abbrev-ref HEAD is the current way to achieve this\noutput, but that is not intuitive or easy to understand.\n\nSigned-off-by: Daniels Umanovskis <daniels@umanovskis.se>\n---\n builtin/branch.c         | 17 +++++++++++++++++\n t/t3203-branch-output.sh | 18 ++++++++++++++++++\n 2 files changed, 35 insertions(+)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c396c4153..e4c6b0490 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -443,6 +443,18 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \tfree(to_free);\n }\n \n+static void print_current_branch_name()\n+{\n+\tstruct strbuf out = STRBUF_INIT;\n+\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tchar *shortname = shorten_unambiguous_ref(refname, 0);\n+\tstrbuf_addf(&out, _(\"%s\"), shortname);\n+\tfwrite(out.buf, 1, out.len, stdout);\n+\tputchar('\\n');\n+\tfree(shortname);\n+\tstrbuf_release(&out);\n+}\n+\n static void reject_rebase_or_bisect_branch(const char *target)\n {\n \tstruct worktree **worktrees = get_worktrees(0);\n@@ -581,6 +593,7 @@ static int edit_branch_description(const char *branch_name)\n int cmd_branch(int argc, const char **argv, const char *prefix)\n {\n \tint delete = 0, rename = 0, copy = 0, force = 0, list = 0;\n+\tint current = 0;\n \tint reflog = 0, edit_description = 0;\n \tint quiet = 0, unset_upstream = 0;\n \tconst char *new_upstream = NULL;\n@@ -620,6 +633,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT('c', \"copy\", &copy, N_(\"copy a branch and its reflog\"), 1),\n \t\tOPT_BIT('C', NULL, &copy, N_(\"copy a branch, even if target exists\"), 2),\n \t\tOPT_BOOL('l', \"list\", &list, N_(\"list branch names\")),\n+\t\tOPT_BOOL(0, \"current\", &current, N_(\"show current branch name\")),\n \t\tOPT_BOOL(0, \"create-reflog\", &reflog, N_(\"create the branch's reflog\")),\n \t\tOPT_BOOL(0, \"edit-description\", &edit_description,\n \t\t\t N_(\"edit the description for the branch\")),\n@@ -697,6 +711,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tif (!argc)\n \t\t\tdie(_(\"branch name required\"));\n \t\treturn delete_branches(argc, argv, delete > 1, filter.kind, quiet);\n+\t} else if (current) {\n+\t\tprint_current_branch_name();\n+\t\treturn 0;\n \t} else if (list) {\n \t\t/*  git branch --local also shows HEAD when it is detached */\n \t\tif ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex ee6787614..396d81568 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -100,6 +100,24 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '\n \ttest_must_fail git branch -v branch*\n '\n \n+test_expect_success 'git branch `--current` shows current branch' '\n+\tcat >expect <<-\\EOF &&\n+\tbranch-two\n+\tEOF\n+    git checkout branch-two &&\n+\tgit branch --current >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'git branch `--current` shows detached HEAD properly' '\n+\tcat >expect <<-\\EOF &&\n+\tHEAD\n+\tEOF\n+    git checkout HEAD^0 &&\n+\tgit branch --current >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'git branch shows detached HEAD properly' '\n \tcat >expect <<EOF &&\n * (HEAD detached at $(git rev-parse --short HEAD^0))\n-- \n2.19.1.274.g059d67db4.dirty\n\n"},{"id":"359934","messageId":"20181009183114.16477-2-daniels@umanovskis.se","threadId":"49517","inReplyTo":"20181009183114.16477-1-daniels@umanovskis.se","subject":"[PATCH 2/2] doc/git-branch: Document the --current option","fromName":"Daniels Umanovskis","fromEmail":"daniels@umanovskis.se","sentAt":"2018-10-09T18:31:14Z","receivedAt":"2018-10-09T18:31:35Z","isPatch":true,"sender":{"key":"daniels@umanovskis.se","avatar":"https://avatars.githubusercontent.com/u/5055233?v=4"},"body":"Signed-off-by: Daniels Umanovskis <daniels@umanovskis.se>\n---\n Documentation/git-branch.txt | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex bf5316ffa..a7167df74 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -9,7 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n-\t[--list] [-v [--abbrev=<length> | --no-abbrev]]\n+\t[--list] [--current] [-v [--abbrev=<length> | --no-abbrev]]\n \t[--column[=<options>] | --no-column] [--sort=<key>]\n \t[(--merged | --no-merged) [<commit>]]\n \t[--contains [<commit]] [--no-contains [<commit>]]\n@@ -160,6 +160,10 @@ This option is only applicable in non-verbose mode.\n \tbranch --list 'maint-*'`, list only the branches that match\n \tthe pattern(s).\n \n+--current::\n+\tPrint the name of the current branch. In detached HEAD state,\n+\tor if otherwise impossible to resolve the branch name, print\n+\t\"HEAD\".\n -v::\n -vv::\n --verbose::\n-- \n2.19.1.274.g059d67db4.dirty\n\n"},{"id":"359946","messageId":"CAGZ79kaxsbAt7Oa74kNbG0e6vja3H87Za=XZOpPZCv=Oq-03yA@mail.gmail.com","threadId":"49517","inReplyTo":"20181009183114.16477-1-daniels@umanovskis.se","subject":"Re: [PATCH 1/2] branch: introduce --current display option","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-10-09T19:54:13Z","receivedAt":"2018-10-09T19:54:28Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Welcome to the git mailing list!\n\nOn Tue, Oct 9, 2018 at 11:31 AM Daniels Umanovskis\n<daniels@umanovskis.se> wrote:\n>\n> When called with --current, git branch will print the current\n> branch name and terminate. It will print HEAD in detached-head state.\n\nHow does it play with worktrees? (I would expect it to just work as expected:\neach worktree would print its current branch, but a test might help?)\n\n> Rationale: finding out the current branch is useful interactively,\n> but especially in scripting. git branch --list prints many branches,\n> and prepends the current one with an asterisk, meaning sed or other\n> filtering is necessary to just get the current branch.\n> git rev-parse --abbrev-ref HEAD is the current way to achieve this\n> output, but that is not intuitive or easy to understand.\n\nGit used to have (and still has) the approach of dividing its commands\ninto high level (\"porcelain\") commands and low level (\"plumbing\") commands,\nwith the porcelain facing the user and plumbing being good for scripting.\n\nThis patch proposes a radically different approach, which is convenience\nof use.\n\nAs a scripter you'd need to find out if \"branch --current\" is stable in its API\nor if you'd rather parse it out of the branch list, which adds a subtle burden\nto the scripter, as it is convenient to leave out that part and just use what is\nthere. :-)\n\n> +static void print_current_branch_name()\n> +{\n> +       struct strbuf out = STRBUF_INIT;\n> +       const char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n> +       char *shortname = shorten_unambiguous_ref(refname, 0);\n\n\n> +       strbuf_addf(&out, _(\"%s\"), shortname);\n> +       fwrite(out.buf, 1, out.len, stdout);\n\nWhy do we need to add the shortname to a strbuf?\n(and using _( ) that denotes the string should be translated?)\nI would think we could just\n\n    puts(shortname)\n\nhere and leave out all the strbuf out ?\n\n\n> @@ -620,6 +633,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n>                 OPT_BIT('c', \"copy\", &copy, N_(\"copy a branch and its reflog\"), 1),\n>                 OPT_BIT('C', NULL, &copy, N_(\"copy a branch, even if target exists\"), 2),\n>                 OPT_BOOL('l', \"list\", &list, N_(\"list branch names\")),\n> +               OPT_BOOL(0, \"current\", &current, N_(\"show current branch name\")),\n\n(Gah, we're not using OPT_MODE here to select for one out of many.)\n\nLater we have it in code via\n\n    if (!!delete + !!rename + !!new_upstream +\n        list + unset_upstream > 1)\n            usage_with_options(builtin_branch_usage, options);\n\nand I would think we'd want to add in a \"+ !!current\" there, too.\nThen we'd get the usage options when giving --current in combination\nwith say --move\n"},{"id":"359947","messageId":"18720b32-992b-7665-7df4-6cf8659a7b31@umanovskis.se","threadId":"49517","inReplyTo":"CAGZ79kaxsbAt7Oa74kNbG0e6vja3H87Za=XZOpPZCv=Oq-03yA@mail.gmail.com","subject":"Re: [PATCH 1/2] branch: introduce --current display option","fromName":"Daniels Umanovskis","fromEmail":"daniels@umanovskis.se","sentAt":"2018-10-09T20:21:56Z","receivedAt":"2018-10-09T20:22:24Z","isPatch":true,"sender":{"key":"daniels@umanovskis.se","avatar":"https://avatars.githubusercontent.com/u/5055233?v=4"},"body":"Thanks for the feedback!\n\nOn 10/9/18 9:54 PM, Stefan Beller wrote:\n> How does it play with worktrees? (I would expect it to just work as expected:\n> each worktree would print its current branch, but a test might help?)\n\nI'll see about writing a test for that. I've never used git-worktree so\nthis is a good chance to learn.\n\n> Git used to have (and still has) the approach of dividing its commands\n> into high level (\"porcelain\") commands and low level (\"plumbing\") commands,\n> with the porcelain facing the user and plumbing being good for scripting.\n> \n> This patch proposes a radically different approach, which is convenience\n> of use.\n\nRight - a couple of points in response. First, it strikes me that \"print\ncurrent branch\" is one of those things that are both for scripting and\nfor normal/interactive use. Much like the entirety of git-status, which\nis very used by itself, and also in scripts with --porcelain. Current\nbranch is often used in things like your shell prompt, vim statusline,\netc, as well as scripts.\n\nThe amount of upvotes on the stackoverflow question \"how to get the\ncurrent branch name in git?\" illustrates my point :)\n\nSecond, it seems arguable whether `git ref-parse` is guaranteed to be\nscripting-safe, it's not actually a plumbing command, at least if we\ntrust the main git manpage, which lists git-rev-parse as porcelain anyway.\n\n> Why do we need to add the shortname to a strbuf?\n> (and using _( ) that denotes the string should be translated?)\n> I would think we could just\n> \n>     puts(shortname)\n> \n> here and leave out all the strbuf out ?\n> \nOf course, has to be changed for v2. Pitfalls of learning the code from\njust its surroundings, didn't realize that the underscores are for i18n.\n"},{"id":"359977","messageId":"xmqqh8hums9m.fsf@gitster-ct.c.googlers.com","threadId":"49517","inReplyTo":"20181009183114.16477-2-daniels@umanovskis.se","subject":"Re: [PATCH 2/2] doc/git-branch: Document the --current option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-10T02:48:05Z","receivedAt":"2018-10-10T02:48:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Daniels Umanovskis <daniels@umanovskis.se> writes:\n\n> +--current::\n> +\tPrint the name of the current branch. In detached HEAD state,\n> +\tor if otherwise impossible to resolve the branch name, print\n> +\t\"HEAD\".\n\nWhere does \"if otherwise impossible to resolve\" come from?  In the\ncode in [PATCH 1/2], we see this bit\n\n+\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tchar *shortname = shorten_unambiguous_ref(refname, 0);\n\nand the output phase would become puts(shortname).\n\n * Under what condition resolve_ref_unsafe(HEAD) fail to resolve,\n   and when that happens what does it return?  \"HEAD\"?  Can the\n   caller tell the case in which .git/HEAD is a symref that points\n   at refs/heads/HEAD (i.e. we are on a branch whose name is \"HEAD\")\n   and the case in which .git/HEAD fails to resolve and you get\n   \"HEAD\" back?\n\n * Or does the function return NULL in \"otherwise impossible\" case?\n   Does shorten_unambiguous_ref() deal with refname==NULL\n   gracefully?\n\n * Under what condition shorten_unambiguous_ref() fail to compute\n   the branch name discovered by resolve_ref_unsafe()?\n\nAlso, I do not think the implementation is correct.  When you are on\nthe 'frotz' branch, and if you happen to have a tag whose name also\nis 'frotz', then\n\n - Your .git/HEAD points at refs/heads/frotz, and refs/heads/frotz\n   is what resolve_ref_unsafe() gives you.\n\n - You have refs/heads/frotz and refs/tags/frotz in the repository.\n   Asking to shorten the ref refs/heads/frotz unambiguously will\n   *not* yield 'frotz'.  It will give you something like 'heads/frotz'\n   to avoid getting it confused with tags/frotz\n\n - Still \"git branch --list\" would show 'frotz' in such a case, and\n   your \"--current\" would definitely want to match the behaviour.\n\nI think the correct implementation should be more like:\n\n - Ask resolve-ref-unsafe about HEAD; if it is not a symbolic ref,\n   then we are on a detached HEAD.  Silently exit with status 0.\n\n - If it is a symbolic ref, see if the target of the symblic ref\n   (i.e. returned refname) begins with \"refs/heads/\".  Otherwise, we\n   have a repository corruption.  Diagnose it as an error and die().\n\n - Otherwise, strip that leading \"refs/heads/\"; the remainder is the\n   name of the \"current branch\".\n\nI already said \"current\" by itself is an unacceptable name for this\noption, so I won't be repeating myself.\n\n"}]}