diff mbox series

[v3] branch, for-each-ref, tag: add option to omit empty lines

Message ID 20230407175316.6404-1-oystwa@gmail.com (mailing list archive)
State Accepted
Commit aabfdc9514a51d8d9a2f3afbc29e5044ccf7191b
Headers show
Series [v3] branch, for-each-ref, tag: add option to omit empty lines | expand

Commit Message

Øystein Walle April 7, 2023, 5:53 p.m. UTC
If the given format string expands to the empty string a newline is
still printed it. This makes using the output linewise more tedious. For
example, git update-ref --stdin does not accept empty lines.

Add options to branch and for-each-ref to not print these empty lines.
The default behavior remains the same.

Signed-off-by: Øystein Walle <oystwa@gmail.com>
---
Dang, you're right. But yes, it was a near-identical patch to
builtin/tag.c. Along with a test, of course.

I see you already applied the first of these patches so in this
iteration there's only one. 

 Documentation/git-branch.txt       |  4 ++++
 Documentation/git-for-each-ref.txt |  4 ++++
 Documentation/git-tag.txt          |  4 ++++
 builtin/branch.c                   |  6 +++++-
 builtin/for-each-ref.c             |  7 +++++--
 builtin/tag.c                      |  6 +++++-
 t/t3203-branch-output.sh           | 24 ++++++++++++++++++++++++
 t/t6300-for-each-ref.sh            |  8 ++++++++
 t/t7004-tag.sh                     | 16 ++++++++++++++++
 9 files changed, 75 insertions(+), 4 deletions(-)

Comments

Junio C Hamano April 7, 2023, 6:48 p.m. UTC | #1
Øystein Walle <oystwa@gmail.com> writes:

> If the given format string expands to the empty string a newline is
> still printed it. This makes using the output linewise more tedious. For
> example, git update-ref --stdin does not accept empty lines.
>
> Add options to branch and for-each-ref to not print these empty lines.
> The default behavior remains the same.
>
> Signed-off-by: Øystein Walle <oystwa@gmail.com>
> ---
> Dang, you're right. But yes, it was a near-identical patch to
> builtin/tag.c. Along with a test, of course.

There are small nits like "do not explicitly initialize statics to
0", which may not be big enough to warrant a reroll.  Other than
that, looking good.

Thanks, will replace.
Andrei Rybak April 12, 2023, 11:44 p.m. UTC | #2
A couple of drive-by nitpicks about the commit message:

On 07/04/2023 19:53, Øystein Walle wrote:
> Subject: [PATCH v3] branch, for-each-ref, tag: add option to omit empty lines
> 
> If the given format string expands to the empty string a newline is
> still printed it. This makes using the output linewise more tedious. For

It seems that a word is missing in the first sentence. Perhaps,

   s/printed it/printed for it/

?

> example, git update-ref --stdin does not accept empty lines.
> 
> Add options to branch and for-each-ref to not print these empty lines.

"git tag" is mentioned in the subject line, but not here.

> The default behavior remains the same.
> 
> Signed-off-by: Øystein Walle <oystwa@gmail.com>
> ---
> Dang, you're right. But yes, it was a near-identical patch to
> builtin/tag.c. Along with a test, of course.
> 
> I see you already applied the first of these patches so in this
> iteration there's only one.
> 
>   Documentation/git-branch.txt       |  4 ++++
>   Documentation/git-for-each-ref.txt |  4 ++++
>   Documentation/git-tag.txt          |  4 ++++
>   builtin/branch.c                   |  6 +++++-
>   builtin/for-each-ref.c             |  7 +++++--
>   builtin/tag.c                      |  6 +++++-
>   t/t3203-branch-output.sh           | 24 ++++++++++++++++++++++++
>   t/t6300-for-each-ref.sh            |  8 ++++++++
>   t/t7004-tag.sh                     | 16 ++++++++++++++++
>   9 files changed, 75 insertions(+), 4 deletions(-)
>
Øystein Walle April 13, 2023, 7:17 a.m. UTC | #3
Hi Andrei,

On Thu, 13 Apr 2023 at 01:44, Andrei Rybak <rybak.a.v@gmail.com> wrote:

> It seems that a word is missing in the first sentence. Perhaps,
>
>    s/printed it/printed for it/
>
> ?

Sort of... I think I meant s/printed it/printed/ :-)

> "git tag" is mentioned in the subject line, but not here.

It should definitely be added, yes. Junio, should I resend or will you touch up
the message? Not sure what the proper procedure is since it's already in seen.

Thanks.

Øsse
Junio C Hamano April 13, 2023, 3:13 p.m. UTC | #4
Øystein Walle <oystwa@gmail.com> writes:

> Sort of... I think I meant s/printed it/printed/ :-)
>
>> "git tag" is mentioned in the subject line, but not here.
>
> It should definitely be added, yes. Junio, should I resend or will you touch up
> the message? Not sure what the proper procedure is since it's already in seen.

As I write in "What's cooking", being in 'seen' does not count all
that much and we can freely replace them and erace the trace of
"past mistakes", until the series hits 'next'.  For small changes
like this, telling me to touch them up, as long as the necessary
changes are obvious, is just fine.  Sending out v4 is also fine for
a series of any size.

I just locally amended the commit log message.

Thanks.

1:  e0053ad012 ! 1:  aabfdc9514 branch, for-each-ref, tag: add option to omit empty lines
    @@ Metadata
      ## Commit message ##
         branch, for-each-ref, tag: add option to omit empty lines
     
    -    If the given format string expands to the empty string a newline is
    -    still printed it. This makes using the output linewise more tedious. For
    +    If the given format string expands to the empty string, a newline is
    +    still printed. This makes using the output linewise more tedious. For
         example, git update-ref --stdin does not accept empty lines.
     
    -    Add options to branch and for-each-ref to not print these empty lines.
    -    The default behavior remains the same.
    +    Add options to "git branch", "git for-each-ref", and "git tag" to
    +    not print these empty lines.  The default behavior remains the same.
     
         Signed-off-by: Øystein Walle <oystwa@gmail.com>
         Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff mbox series

Patch

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index d382ac69f7..d207da9101 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -156,6 +156,10 @@  in another worktree linked to the same repository.
 --ignore-case::
 	Sorting and filtering branches are case insensitive.
 
+--omit-empty::
+	Do not print a newline after formatted refs where the format expands
+	to the empty string.
+
 --column[=<options>]::
 --no-column::
 	Display branch listing in columns. See configuration variable
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 0713e49b49..1e215d4e73 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -98,6 +98,10 @@  OPTIONS
 --ignore-case::
 	Sorting and filtering refs are case insensitive.
 
+--omit-empty::
+	Do not print a newline after formatted refs where the format expands
+	to the empty string.
+
 FIELD NAMES
 -----------
 
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fdc72b5875..7f61c1edb3 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -131,6 +131,10 @@  options for details.
 --ignore-case::
 	Sorting and filtering tags are case insensitive.
 
+--omit-empty::
+	Do not print a newline after formatted refs where the format expands
+	to the empty string.
+
 --column[=<options>]::
 --no-column::
 	Display tag listing in columns. See configuration variable
diff --git a/builtin/branch.c b/builtin/branch.c
index 6413a016c5..98d5fa2c8e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -44,6 +44,7 @@  static const char *head;
 static struct object_id head_oid;
 static int recurse_submodules = 0;
 static int submodule_propagate_branches = 0;
+static int omit_empty = 0;
 
 static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -466,7 +467,8 @@  static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 			string_list_append(output, out.buf);
 		} else {
 			fwrite(out.buf, 1, out.len, stdout);
-			putchar('\n');
+			if (out.len || !omit_empty)
+				putchar('\n');
 		}
 	}
 
@@ -675,6 +677,8 @@  int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
 		OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
 		OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+		OPT_BOOL(0, "omit-empty",  &omit_empty,
+			N_("do not output a newline after empty formatted refs")),
 		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")),
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 0bdc49a6e1..695fc8f4a5 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -22,7 +22,7 @@  int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 	int i;
 	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
-	int maxcount = 0, icase = 0;
+	int maxcount = 0, icase = 0, omit_empty = 0;
 	struct ref_array array;
 	struct ref_filter filter;
 	struct ref_format format = REF_FORMAT_INIT;
@@ -40,6 +40,8 @@  int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 			N_("quote placeholders suitably for python"), QUOTE_PYTHON),
 		OPT_BIT(0 , "tcl",  &format.quote_style,
 			N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
+		OPT_BOOL(0, "omit-empty",  &omit_empty,
+			N_("do not output a newline after empty formatted refs")),
 
 		OPT_GROUP(""),
 		OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
@@ -112,7 +114,8 @@  int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 		if (format_ref_array_item(array.items[i], &format, &output, &err))
 			die("%s", err.buf);
 		fwrite(output.buf, 1, output.len, stdout);
-		putchar('\n');
+		if (output.len || !omit_empty)
+			putchar('\n');
 	}
 
 	strbuf_release(&err);
diff --git a/builtin/tag.c b/builtin/tag.c
index 782bb3aa2f..ab5f5c74f4 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -41,6 +41,7 @@  static const char * const git_tag_usage[] = {
 static unsigned int colopts;
 static int force_sign_annotate;
 static int config_sign_tag = -1; /* unspecified */
+static int omit_empty = 0;
 
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
 		     struct ref_format *format)
@@ -79,7 +80,8 @@  static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
 		if (format_ref_array_item(array.items[i], format, &output, &err))
 			die("%s", err.buf);
 		fwrite(output.buf, 1, output.len, stdout);
-		putchar('\n');
+		if (output.len || !omit_empty)
+			putchar('\n');
 	}
 
 	strbuf_release(&err);
@@ -474,6 +476,8 @@  int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
 		OPT_MERGED(&filter, N_("print only tags that are merged")),
 		OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+		OPT_BOOL(0, "omit-empty",  &omit_empty,
+			N_("do not output a newline after empty formatted refs")),
 		OPT_REF_SORT(&sorting_options),
 		{
 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 1c0f7ea24e..93f8295339 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -355,6 +355,30 @@  test_expect_success 'git branch with --format=%(rest) must fail' '
 	test_must_fail git branch --format="%(rest)" >actual
 '
 
+test_expect_success 'git branch --format --omit-empty' '
+	cat >expect <<-\EOF &&
+	Refname is (HEAD detached from fromtag)
+	Refname is refs/heads/ambiguous
+	Refname is refs/heads/branch-one
+	Refname is refs/heads/branch-two
+
+	Refname is refs/heads/ref-to-branch
+	Refname is refs/heads/ref-to-remote
+	EOF
+	git branch --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF &&
+	Refname is (HEAD detached from fromtag)
+	Refname is refs/heads/ambiguous
+	Refname is refs/heads/branch-one
+	Refname is refs/heads/branch-two
+	Refname is refs/heads/ref-to-branch
+	Refname is refs/heads/ref-to-remote
+	EOF
+	git branch --omit-empty --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 6614469d2d..5c00607608 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -1374,6 +1374,14 @@  test_expect_success 'for-each-ref --ignore-case ignores case' '
 	test_cmp expect actual
 '
 
+test_expect_success 'for-each-ref --omit-empty works' '
+	git for-each-ref --format="%(refname)" >actual &&
+	test_line_count -gt 1 actual &&
+	git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
+	echo refs/heads/main >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
 	# name refs numerically to avoid case-insensitive filesystem conflicts
 	nr=0 &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 32b312fa80..0fe6ba93a2 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -2046,6 +2046,22 @@  test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success '--format --omit-empty works' '
+	cat >expect <<-\EOF &&
+	refname : refs/tags/v1.0
+
+	refname : refs/tags/v1.1.3
+	EOF
+	git tag -l --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF &&
+	refname : refs/tags/v1.0
+	refname : refs/tags/v1.1.3
+	EOF
+	git tag -l --omit-empty --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git tag -l with --format="%(rest)" must fail' '
 	test_must_fail git tag -l --format="%(rest)" "v1*"
 '