public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] name-rev: learn --format=<pretty>
@ 2026-03-13 16:03 kristofferhaugsbakk
  2026-03-13 16:03 ` [PATCH 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: kristofferhaugsbakk @ 2026-03-13 16:03 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Topic name: kh/name-rev-pretty-format

Topic summary: Teach git-name-rev(1) a mode to pretty format revisions
instead of outputting symbolic names.

(See the second patch for details.)

The first patch is just for `CodingGuidelines`. Unrelated.

I have gone through three approaches: pretty print, `log-tree:
log_tree_commit`, and pretty print again. At first I was confused
when using `pretty_print_commit` because I couldn’t seem to get the same
output as git-log(1):

    # for git-log(1)
    git log --format=<pretty>
    # for git-name-rev(1)
    git rev-list HEAD |
        git name-rev --format=<pretty> --annotate-stdin

Because there were some minor differences for a few things I tried:

• fuller: log has the `commit <commit>` header; pretty does not
• oneline: log has the oid; pretty does not

Then I tried `log_tree_commit`. But then I got some things that I didn’t
want. This function also didn’t fit in with the git-name-rev(1) processing
since it just dumps straight to standard out instead of allowing you to
accumulate things in a scratch buffer (strbuf). So then I went back to the
`pretty_print_commit` approach.

Notes are handled by reading the display refs. I can imagine that it would
be better for this command to use `--[no-]notes` arguments like the ones
that git-log(1) has for explicit control (without using env. variables).

[1/2] name-rev: wrap both blocks in braces
[2/2] name-rev: learn --format=<pretty>

 Documentation/git-name-rev.adoc |  9 +++-
 builtin/name-rev.c              | 85 ++++++++++++++++++++++++++++-----
 t/t6120-describe.sh             | 58 ++++++++++++++++++++++
 3 files changed, 139 insertions(+), 13 deletions(-)


base-commit: 67006b9db8b772423ad0706029286096307d2567
-- 
2.53.0.32.gf6228eaf9cc


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/2] name-rev: wrap both blocks in braces
  2026-03-13 16:03 [PATCH 0/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
@ 2026-03-13 16:03 ` kristofferhaugsbakk
  2026-03-14  0:22   ` Junio C Hamano
  2026-03-13 16:03 ` [PATCH 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
  2026-03-20 13:09 ` [PATCH v2 0/2] " kristofferhaugsbakk
  2 siblings, 1 reply; 13+ messages in thread
From: kristofferhaugsbakk @ 2026-03-13 16:03 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

See `CodingGuidelines`:

    - When there are multiple arms to a conditional and some of them
      require braces, enclose even a single line block in braces for
      consistency. [...]

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 builtin/name-rev.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 6188cf98ce0..9d2774f3723 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -466,9 +466,9 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 	if (!n)
 		return NULL;
 
-	if (!n->generation)
+	if (!n->generation) {
 		return n->tip_name;
-	else {
+	} else {
 		strbuf_reset(buf);
 		strbuf_addstr(buf, n->tip_name);
 		strbuf_strip_suffix(buf, "^0");
@@ -516,10 +516,10 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 	for (p_start = p; *p; p++) {
 #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
-		if (!ishex(*p))
+		if (!ishex(*p)) {
 			counter = 0;
-		else if (++counter == hexsz &&
-			 !ishex(*(p+1))) {
+		} else if (++counter == hexsz &&
+			   !ishex(*(p + 1))) {
 			struct object_id oid;
 			const char *name = NULL;
 			char c = *(p+1);
-- 
2.53.0.32.gf6228eaf9cc


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/2] name-rev: learn --format=<pretty>
  2026-03-13 16:03 [PATCH 0/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
  2026-03-13 16:03 ` [PATCH 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
@ 2026-03-13 16:03 ` kristofferhaugsbakk
  2026-03-14  0:22   ` Junio C Hamano
  2026-03-20 13:09 ` [PATCH v2 0/2] " kristofferhaugsbakk
  2 siblings, 1 reply; 13+ messages in thread
From: kristofferhaugsbakk @ 2026-03-13 16:03 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Teach git-name-rev(1) to format the given revisions instead of creating
symbolic names.

Sometimes you want to format commits. Most of the time you’re walking
the graph, e.g. getting a range of commits like `master..topic`. That’s
a job for git-log(1).

But sometimes you might want to format commits that you encounter
on demand:

• Full hashes in running text that you might want to pretty-print
• git-last-modified(1) outputs full hashes that you can do the same with
• git-cherry(1) has `-v` for commit subject, but maybe you want
  something else?

But now you can’t use git-log(1), git-show(1), or git-rev-list(1):

• You can’t feed commits piecemeal to these commands, one input for one
  output; they block until standard in is closed
• You can’t feed a list of possibly duplicate commits, like the output
  of git-last-modified(1); they effectively deduplicate the output

Beyond these two points there’s also the input massage problem: you
cannot feed mixed input (revisions mixed with arbitrary text).

One might hope that git-cat-file(1) can save us. But it doesn’t support
pretty formats.

But there is one command that already both handles revisions as
arguments, revisions on standard input, and even revisions mixed
in with arbitrary text. Namely git-name-rev(1).

Teach it to work in a format mode where the output for each revision is
the pretty output (implies `--name-only`). This can be used to format
any revision expression when given as arguments, and all full commit
hashes in running text on stdin.

Just bring the hashes (to the pipeline). We will pretty print them.

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---
 Documentation/git-name-rev.adoc |  9 +++-
 builtin/name-rev.c              | 75 ++++++++++++++++++++++++++++++---
 t/t6120-describe.sh             | 58 +++++++++++++++++++++++++
 3 files changed, 134 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-name-rev.adoc b/Documentation/git-name-rev.adoc
index d4f1c4d5945..8f050cd4763 100644
--- a/Documentation/git-name-rev.adoc
+++ b/Documentation/git-name-rev.adoc
@@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
 SYNOPSIS
 --------
 [verse]
-'git name-rev' [--tags] [--refs=<pattern>]
+'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
 	       ( --all | --annotate-stdin | <commit-ish>... )
 
 DESCRIPTION
@@ -21,6 +21,13 @@ format parsable by 'git rev-parse'.
 OPTIONS
 -------
 
+--format=<pretty>::
+--no-format::
+	Format revisions instead of outputting symbolic names. The
+	default is `--no-format`.
++
+Implies `--name-only`.
+
 --tags::
 	Do not use branch names, but only tags to name the commits
 
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 9d2774f3723..30d981104c6 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -18,6 +18,9 @@
 #include "commit-graph.h"
 #include "wildmatch.h"
 #include "mem-pool.h"
+#include "pretty.h"
+#include "revision.h"
+#include "notes.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -33,6 +36,11 @@ struct rev_name {
 	int from_tag;
 };
 
+struct pretty_format {
+	struct pretty_print_context ctx;
+	struct userformat_want want;
+};
+
 define_commit_slab(commit_rev_name, struct rev_name);
 
 static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
@@ -454,7 +462,9 @@ static const char *get_exact_ref_match(const struct object *o)
 }
 
 /* may return a constant string or use "buf" as scratch space */
-static const char *get_rev_name(const struct object *o, struct strbuf *buf)
+static const char *get_rev_name(const struct object *o,
+				struct pretty_format *format_ctx,
+				struct strbuf *buf)
 {
 	struct rev_name *n;
 	const struct commit *c;
@@ -462,6 +472,25 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 	if (o->type != OBJ_COMMIT)
 		return get_exact_ref_match(o);
 	c = (const struct commit *) o;
+
+	if (format_ctx) {
+		strbuf_reset(buf);
+
+		if (format_ctx->want.notes) {
+			struct strbuf notebuf = STRBUF_INIT;
+
+			format_display_notes(&c->object.oid, &notebuf,
+					     get_log_output_encoding(),
+					     format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
+			format_ctx->ctx.notes_message = strbuf_detach(&notebuf, NULL);
+		}
+
+		pretty_print_commit(&format_ctx->ctx, c, buf);
+		free(format_ctx->ctx.notes_message);
+
+		return buf->buf;
+	}
+
 	n = get_commit_rev_name(c);
 	if (!n)
 		return NULL;
@@ -479,6 +508,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 
 static void show_name(const struct object *obj,
 		      const char *caller_name,
+		      struct pretty_format *format_ctx,
 		      int always, int allow_undefined, int name_only)
 {
 	const char *name;
@@ -487,7 +517,7 @@ static void show_name(const struct object *obj,
 
 	if (!name_only)
 		printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
-	name = get_rev_name(obj, &buf);
+	name = get_rev_name(obj, format_ctx, &buf);
 	if (name)
 		printf("%s\n", name);
 	else if (allow_undefined)
@@ -507,7 +537,9 @@ static char const * const name_rev_usage[] = {
 	NULL
 };
 
-static void name_rev_line(char *p, struct name_ref_data *data)
+static void name_rev_line(char *p,
+			  struct name_ref_data *data,
+			  struct pretty_format *format_ctx)
 {
 	struct strbuf buf = STRBUF_INIT;
 	int counter = 0;
@@ -532,7 +564,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 				struct object *o =
 					lookup_object(the_repository, &oid);
 				if (o)
-					name = get_rev_name(o, &buf);
+					name = get_rev_name(o, format_ctx, &buf);
 			}
 			*(p+1) = c;
 
@@ -567,6 +599,10 @@ int cmd_name_rev(int argc,
 #endif
 	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
 	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
+	const char *format = NULL;
+	struct rev_info format_rev = REV_INFO_INIT;
+	struct pretty_format *format_ctx = NULL;
+	struct pretty_format format_pp = {0};
 	struct option opts[] = {
 		OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
 		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
@@ -584,6 +620,8 @@ int cmd_name_rev(int argc,
 			   PARSE_OPT_HIDDEN),
 #endif /* WITH_BREAKING_CHANGES */
 		OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
+		OPT_STRING(0, "format", &format, N_("format"),
+			   "pretty-print output instead"),
 		OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
 		OPT_BOOL(0, "always",     &always,
 			   N_("show abbreviated commit object as fallback")),
@@ -606,6 +644,29 @@ int cmd_name_rev(int argc,
 	}
 #endif
 
+	if (format) {
+		struct pretty_print_context ctx = {0};
+		struct userformat_want want = {0};
+
+		get_commit_format(format, &format_rev);
+		ctx.rev = &format_rev;
+		ctx.fmt = format_rev.commit_format;
+		ctx.abbrev = format_rev.abbrev;
+		ctx.date_mode_explicit = format_rev.date_mode_explicit;
+		ctx.date_mode = format_rev.date_mode;
+		ctx.color = GIT_COLOR_AUTO;
+		format_pp.ctx = ctx;
+
+		userformat_find_requirements(format, &want);
+		if (want.notes)
+			load_display_notes(NULL);
+
+		format_pp.want = want;
+		format_ctx = &format_pp;
+
+		data.name_only = true;
+	}
+
 	if (all + annotate_stdin + !!argc > 1) {
 		error("Specify either a list, or --all, not both!");
 		usage_with_options(name_rev_usage, opts);
@@ -663,7 +724,7 @@ int cmd_name_rev(int argc,
 
 		while (strbuf_getline(&sb, stdin) != EOF) {
 			strbuf_addch(&sb, '\n');
-			name_rev_line(sb.buf, &data);
+			name_rev_line(sb.buf, &data, format_ctx);
 		}
 		strbuf_release(&sb);
 	} else if (all) {
@@ -674,13 +735,13 @@ int cmd_name_rev(int argc,
 			struct object *obj = get_indexed_object(the_repository, i);
 			if (!obj || obj->type != OBJ_COMMIT)
 				continue;
-			show_name(obj, NULL,
+			show_name(obj, NULL, format_ctx,
 				  always, allow_undefined, data.name_only);
 		}
 	} else {
 		int i;
 		for (i = 0; i < revs.nr; i++)
-			show_name(revs.objects[i].item, revs.objects[i].name,
+			show_name(revs.objects[i].item, revs.objects[i].name, format_ctx,
 				  always, allow_undefined, data.name_only);
 	}
 
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 2c70cc561ad..6dba392d343 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -658,6 +658,64 @@ test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
 	)
 '
 
+test_expect_success 'name-rev --format setup' '
+	mkdir repo-format &&
+	git -C repo-format init &&
+	test_commit -C repo-format first &&
+	test_commit -C repo-format second &&
+	test_commit -C repo-format third &&
+	test_commit -C repo-format fourth &&
+	test_commit -C repo-format fifth &&
+	test_commit -C repo-format sixth &&
+	test_commit -C repo-format seventh &&
+	test_commit -C repo-format eighth
+'
+
+test_expect_success 'name-rev --format=%s for argument revs' '
+	cat >expect <<-\EOF &&
+	eighth
+	seventh
+	fifth
+	EOF
+	git -C repo-format name-rev --format=%s \
+		HEAD HEAD~ HEAD~3 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--name-rev --format=<pretty> --annotate-stdin from rev-list same as log' '
+	git -C repo-format log --format=reference >expect &&
+	test_file_not_empty expect &&
+	git -C repo-format rev-list HEAD >list &&
+	git -C repo-format name-rev --format=reference \
+		--annotate-stdin <list >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--name-rev --format=<pretty> --annotate-stdin with running text and tree oid' '
+	cmit_oid=$(git -C repo-format rev-parse :/fifth) &&
+	reference=$(git -C repo-format log -n1 --format=reference :/fifth) &&
+	tree=$(git -C repo-format rev-parse HEAD^{tree}) &&
+	cat >expect <<-EOF &&
+	We thought we fixed this in ${reference}.
+	But look at this tree: ${tree}.
+	EOF
+	git -C repo-format name-rev --format=reference --annotate-stdin \
+		>actual <<-EOF &&
+	We thought we fixed this in ${cmit_oid}.
+	But look at this tree: ${tree}.
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--name-rev --format=<pretty> with a note' '
+	test_when_finished "git -C repo-format notes remove" &&
+	git -C repo-format notes add -m"Make a note" &&
+	printf "Make a note\n\n\n" >expect &&
+	git -C repo-format name-rev --format="tformat:%N" \
+		HEAD HEAD~ >actual &&
+	test_cmp expect actual
+'
+
 #               B
 #               o
 #  H             \
-- 
2.53.0.32.gf6228eaf9cc


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] name-rev: wrap both blocks in braces
  2026-03-13 16:03 ` [PATCH 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
@ 2026-03-14  0:22   ` Junio C Hamano
  2026-03-17 22:10     ` Kristoffer Haugsbakk
  0 siblings, 1 reply; 13+ messages in thread
From: Junio C Hamano @ 2026-03-14  0:22 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk

kristofferhaugsbakk@fastmail.com writes:

> -		else if (++counter == hexsz &&
> -			 !ishex(*(p+1))) {
> +		} else if (++counter == hexsz &&
> +			   !ishex(*(p + 1))) {
>  			struct object_id oid;
>  			const char *name = NULL;
>  			char c = *(p+1);

You are correcting "p+1" to "p + 1" to honor our coding style in a
few lines above "while at it", but there are three others in the
same block (we can see one of them in the post-context), which means
these are now inconsistent.  Fixing all of them would make it a far
larger change than qualifies as a "while at it" change.  Either make
it another step that is an unrelated clean up, or leave it as-is.

The primary thrust of this patch does make sense and is executed
well.

Thanks.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] name-rev: learn --format=<pretty>
  2026-03-13 16:03 ` [PATCH 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
@ 2026-03-14  0:22   ` Junio C Hamano
  2026-03-17 22:07     ` Kristoffer Haugsbakk
  0 siblings, 1 reply; 13+ messages in thread
From: Junio C Hamano @ 2026-03-14  0:22 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk

kristofferhaugsbakk@fastmail.com writes:

> diff --git a/Documentation/git-name-rev.adoc b/Documentation/git-name-rev.adoc
> index d4f1c4d5945..8f050cd4763 100644
> --- a/Documentation/git-name-rev.adoc
> +++ b/Documentation/git-name-rev.adoc
> @@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
>  SYNOPSIS
>  --------
>  [verse]
> -'git name-rev' [--tags] [--refs=<pattern>]
> +'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
>  	       ( --all | --annotate-stdin | <commit-ish>... )

We acquired a new option.  Do we need a matching change to
the contents of name_rev_usage[] array?

> +--format=<pretty>::
> +--no-format::
> +	Format revisions instead of outputting symbolic names. The
> +	default is `--no-format`.
> ++
> +Implies `--name-only`.

If it is implication, would 

    git name-rev --format=reference --no-name-only

do what is naturally expected?

> @@ -462,6 +472,25 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
>  	if (o->type != OBJ_COMMIT)
>  		return get_exact_ref_match(o);
>  	c = (const struct commit *) o;
> +
> +	if (format_ctx) {
> +		strbuf_reset(buf);
> +
> +		if (format_ctx->want.notes) {
> +			struct strbuf notebuf = STRBUF_INIT;
> +
> +			format_display_notes(&c->object.oid, &notebuf,
> +					     get_log_output_encoding(),
> +					     format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
> +			format_ctx->ctx.notes_message = strbuf_detach(&notebuf, NULL);
> +		}
> +
> +		pretty_print_commit(&format_ctx->ctx, c, buf);
> +		free(format_ctx->ctx.notes_message);

Is free() the expected thing to do here, or FREE_AND_NULL()?  Unlike
callers like log-tree.c:show_log() where a context is prepared, used
once, and then discarded, format_pp is initialized in cmd_name_rev()
once and then repeatedly used by show_name() potentially multiple
times, so there may be a risk of getting confused by this leftover
non-NULL pointer that points at an already free'd piece of memory.

Or there may not be---I did not check, but you as the author must
have already checked, hence this question.

> +		return buf->buf;
> +	}
> +
>  	n = get_commit_rev_name(c);
>  	if (!n)
>  		return NULL;
> @@ -479,6 +508,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
>  
>  static void show_name(const struct object *obj,
>  		      const char *caller_name,
> +		      struct pretty_format *format_ctx,
>  		      int always, int allow_undefined, int name_only)
>  {
>  	const char *name;
> @@ -487,7 +517,7 @@ static void show_name(const struct object *obj,
>  
>  	if (!name_only)
>  		printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
> -	name = get_rev_name(obj, &buf);
> +	name = get_rev_name(obj, format_ctx, &buf);
>  	if (name)
>  		printf("%s\n", name);
>  	else if (allow_undefined)
> @@ -507,7 +537,9 @@ static char const * const name_rev_usage[] = {
>  	NULL
>  };
>  
> -static void name_rev_line(char *p, struct name_ref_data *data)
> +static void name_rev_line(char *p,
> +			  struct name_ref_data *data,
> +			  struct pretty_format *format_ctx)
>  {
>  	struct strbuf buf = STRBUF_INIT;
>  	int counter = 0;
> @@ -532,7 +564,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
>  				struct object *o =
>  					lookup_object(the_repository, &oid);
>  				if (o)
> -					name = get_rev_name(o, &buf);
> +					name = get_rev_name(o, format_ctx, &buf);
>  			}
>  			*(p+1) = c;
>  
> @@ -567,6 +599,10 @@ int cmd_name_rev(int argc,
>  #endif
>  	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
>  	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
> +	const char *format = NULL;
> +	struct rev_info format_rev = REV_INFO_INIT;
> +	struct pretty_format *format_ctx = NULL;
> +	struct pretty_format format_pp = {0};

Hmph, would we want to use the full init_revisions() instead of
static REV_INFO_INIT that initialises a lot more members of the
struct properly, most importantly the "repo" member that points at
the repostiory to be used?

> +	if (format) {
> +		struct pretty_print_context ctx = {0};
> +		struct userformat_want want = {0};
> +
> +		get_commit_format(format, &format_rev);
> +		ctx.rev = &format_rev;
> +		ctx.fmt = format_rev.commit_format;
> +		ctx.abbrev = format_rev.abbrev;
> +		ctx.date_mode_explicit = format_rev.date_mode_explicit;
> +		ctx.date_mode = format_rev.date_mode;
> +		ctx.color = GIT_COLOR_AUTO;
> +		format_pp.ctx = ctx;

Why does this code initialize and assign to a on-stack ctx first and
then assign it to format_pp.ctx, instead of working on format_pp.ctx
directly?

> +		userformat_find_requirements(format, &want);
> +		if (want.notes)
> +			load_display_notes(NULL);
> +
> +		format_pp.want = want;
> +		format_ctx = &format_pp;
> +
> +		data.name_only = true;
> +	}
> +

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] name-rev: learn --format=<pretty>
  2026-03-14  0:22   ` Junio C Hamano
@ 2026-03-17 22:07     ` Kristoffer Haugsbakk
  2026-03-18 15:36       ` Kristoffer Haugsbakk
  0 siblings, 1 reply; 13+ messages in thread
From: Kristoffer Haugsbakk @ 2026-03-17 22:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Kristoffer Haugsbakk

On Sat, Mar 14, 2026, at 01:22, Junio C Hamano wrote:
> kristofferhaugsbakk@fastmail.com writes:
>
>> diff --git a/Documentation/git-name-rev.adoc b/Documentation/git-name-rev.adoc
>> index d4f1c4d5945..8f050cd4763 100644
>> --- a/Documentation/git-name-rev.adoc
>> +++ b/Documentation/git-name-rev.adoc
>> @@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
>>  SYNOPSIS
>>  --------
>>  [verse]
>> -'git name-rev' [--tags] [--refs=<pattern>]
>> +'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
>>  	       ( --all | --annotate-stdin | <commit-ish>... )
>
> We acquired a new option.  Do we need a matching change to
> the contents of name_rev_usage[] array?

I looked at it and it seemed that `[<options>]` were supposed to stay
inside that placeholder. But since this is a new mode, maybe:

    diff --git builtin/name-rev.c builtin/name-rev.c
    index 6188cf98ce0..13e67a7723c 100644
    --- builtin/name-rev.c
    +++ builtin/name-rev.c
    @@ -504,6 +504,7 @@ static char const * const name_rev_usage[] = {
            N_("git name-rev [<options>] <commit>..."),
            N_("git name-rev [<options>] --all"),
            N_("git name-rev [<options>] --annotate-stdin"),
    +       N_("git name-rev --format=<pretty> ..."),
            NULL
     };

>
>> +--format=<pretty>::
>> +--no-format::
>> +	Format revisions instead of outputting symbolic names. The
>> +	default is `--no-format`.
>> ++
>> +Implies `--name-only`.
>
> If it is implication, would
>
>     git name-rev --format=reference --no-name-only
>
> do what is naturally expected?

No it wouldn’t. You’re right, it really locks in that option with no
escape hatch.

But in my next version I have switched to a parse-options callback so
that `... --format=... --no-name-only` really does turn off `--name-only`.

>
>> @@ -462,6 +472,25 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
>>  	if (o->type != OBJ_COMMIT)
>>  		return get_exact_ref_match(o);
>>  	c = (const struct commit *) o;
>> +
>> +	if (format_ctx) {
>> +		strbuf_reset(buf);
>> +
>> +		if (format_ctx->want.notes) {
>> +			struct strbuf notebuf = STRBUF_INIT;
>> +
>> +			format_display_notes(&c->object.oid, &notebuf,
>> +					     get_log_output_encoding(),
>> +					     format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
>> +			format_ctx->ctx.notes_message = strbuf_detach(&notebuf, NULL);
>> +		}
>> +
>> +		pretty_print_commit(&format_ctx->ctx, c, buf);
>> +		free(format_ctx->ctx.notes_message);
>
> Is free() the expected thing to do here, or FREE_AND_NULL()?  Unlike
> callers like log-tree.c:show_log() where a context is prepared, used
> once, and then discarded, format_pp is initialized in cmd_name_rev()
> once and then repeatedly used by show_name() potentially multiple
> times, so there may be a risk of getting confused by this leftover
> non-NULL pointer that points at an already free'd piece of memory.
>
> Or there may not be---I did not check, but you as the author must
> have already checked, hence this question.

This is supposed to be tested by `--name-rev --format=<pretty> with a
note`; it has a note on the first revision but not the second.

Here we never use this pointer again and we get a fresh pointer from the
strbuf before freeing again (whether it gets populated with a pointer or
not).

But it does sound better to just null it. There’s no need to have it
laying around.

>
>> +		return buf->buf;
>> +	}
>> +
>>[snip]
>> @@ -567,6 +599,10 @@ int cmd_name_rev(int argc,
>>  #endif
>>  	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
>>  	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
>> +	const char *format = NULL;
>> +	struct rev_info format_rev = REV_INFO_INIT;
>> +	struct pretty_format *format_ctx = NULL;
>> +	struct pretty_format format_pp = {0};
>
> Hmph, would we want to use the full init_revisions() instead of
> static REV_INFO_INIT that initialises a lot more members of the
> struct properly, most importantly the "repo" member that points at
> the repostiory to be used?

Here I looked at the doc for `pretty.h:get_commit_format` in order to
learn what I needed to set up. Since it doesn’t say much I did the least
work that I could get away with when it comes to struct
initializing. Since it does seem like a struct for a lot of different
situations while this is just a formatting situation.

I might have done less research than I ought to.

>
>> +	if (format) {
>> +		struct pretty_print_context ctx = {0};
>> +		struct userformat_want want = {0};
>> +
>> +		get_commit_format(format, &format_rev);
>> +		ctx.rev = &format_rev;
>> +		ctx.fmt = format_rev.commit_format;
>> +		ctx.abbrev = format_rev.abbrev;
>> +		ctx.date_mode_explicit = format_rev.date_mode_explicit;
>> +		ctx.date_mode = format_rev.date_mode;
>> +		ctx.color = GIT_COLOR_AUTO;
>> +		format_pp.ctx = ctx;
>
> Why does this code initialize and assign to a on-stack ctx first and
> then assign it to format_pp.ctx, instead of working on format_pp.ctx
> directly?

You’re right. I’ll just assign directly.

>
>> +		userformat_find_requirements(format, &want);
>> +		if (want.notes)
>> +			load_display_notes(NULL);
>> +
>> +		format_pp.want = want;
>> +		format_ctx = &format_pp;
>> +
>> +		data.name_only = true;
>> +	}
>> +

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] name-rev: wrap both blocks in braces
  2026-03-14  0:22   ` Junio C Hamano
@ 2026-03-17 22:10     ` Kristoffer Haugsbakk
  0 siblings, 0 replies; 13+ messages in thread
From: Kristoffer Haugsbakk @ 2026-03-17 22:10 UTC (permalink / raw)
  To: Junio C Hamano, Kristoffer Haugsbakk; +Cc: git

On Sat, Mar 14, 2026, at 01:22, Junio C Hamano wrote:
> kristofferhaugsbakk@fastmail.com writes:
>
>> -		else if (++counter == hexsz &&
>> -			 !ishex(*(p+1))) {
>> +		} else if (++counter == hexsz &&
>> +			   !ishex(*(p + 1))) {
>>  			struct object_id oid;
>>  			const char *name = NULL;
>>  			char c = *(p+1);
>
> You are correcting "p+1" to "p + 1" to honor our coding style in a
> few lines above "while at it", but there are three others in the
> same block (we can see one of them in the post-context), which means
> these are now inconsistent.  Fixing all of them would make it a far
> larger change than qualifies as a "while at it" change.  Either make
> it another step that is an unrelated clean up, or leave it as-is.

Correct observation. clang-format got ahold of a few stray lines. But I
thought it only corrected the indentation, not that it also caught this
arithmetic formatting. I’ll remove it in the next version.

Thanks

>
> The primary thrust of this patch does make sense and is executed
> well.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] name-rev: learn --format=<pretty>
  2026-03-17 22:07     ` Kristoffer Haugsbakk
@ 2026-03-18 15:36       ` Kristoffer Haugsbakk
  0 siblings, 0 replies; 13+ messages in thread
From: Kristoffer Haugsbakk @ 2026-03-18 15:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Mar 17, 2026, at 23:07, Kristoffer Haugsbakk wrote:
>>[snip]
>>
>> Or there may not be---I did not check, but you as the author must
>> have already checked, hence this question.
>
> This is supposed to be tested by `--name-rev --format=<pretty> with a
> note`; it has a note on the first revision but not the second.
>
> Here we never use this pointer again and we get a fresh pointer from the
> strbuf before freeing again (whether it gets populated with a pointer or
> not).

Here I meant to write: “whether it gets populated with any note data
or not”.

>
> But it does sound better to just null it. There’s no need to have it
> laying around.
>[snip]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 0/2] name-rev: learn --format=<pretty>
  2026-03-13 16:03 [PATCH 0/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
  2026-03-13 16:03 ` [PATCH 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
  2026-03-13 16:03 ` [PATCH 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
@ 2026-03-20 13:09 ` kristofferhaugsbakk
  2026-03-20 13:09   ` [PATCH v2 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
  2026-03-20 13:09   ` [PATCH v2 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
  2 siblings, 2 replies; 13+ messages in thread
From: kristofferhaugsbakk @ 2026-03-20 13:09 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Topic name (applied): kh/name-rev-custom-format

Topic summary: Teach git-name-rev(1) a mode to pretty format revisions
instead of outputting symbolic names.

(See the second patch for details.)

The first patch is just for `CodingGuidelines`. Unrelated.

(See the previous cover letter for a slightly different introduction.)

This started because I had a faint wish in the back of my head (that I was
probably not going to act on) to implement a third-party git-format-rev(1)
command which does what `git name-rev --format=<pretty>` does with this
series applied. I thought I could just use one invocation of some git(1)
command to feed to stdin and read from stdout. Like git-log(1), or
git-rev-list(1). But due to apparent limitations (see patch [2/2] message)
there didn’t seem to be anything better than invoking a git(1) command per
revision/hash that was encountered in the text. I wanted something more
efficient. (I had no need for something more efficient. I just wanted it.)

I then picked git-name-rev(1) since that was the easiest thing to implement
for me. Add an option to an existing command.

Right now this is just version 2 and I don’t know if this is a good
approach or not. If it ever is accepted (in whatever form) I don’t expect
it to be soon.

git(1) already has a lot of commands. This doesn’t expand the footprint. On
the other hand, I might be naive about what a proper formatter needs in
terms of command options. Right now there is the helper `--notes`. But is
more needed?

• --abbrev ?
• --date ?

I don’t know. Maybe at some point this would be too much, too crowded, for
git-name-rev(1). Then it might make sense to split it out to a separate
builtin?

These are some thoughts and context.

§ Changes in v2

Implement `--notes` and respond to reviewer feedback. Details on the second
patch.

The first patch now just formats what it intends to (there was stray
clang-formatting).

[1/2] name-rev: wrap both blocks in braces
[2/2] name-rev: learn --format=<pretty>

 Documentation/git-name-rev.adoc |  10 ++-
 builtin/name-rev.c              | 108 ++++++++++++++++++++++++++++----
 t/t6120-describe.sh             |  96 ++++++++++++++++++++++++++++
 3 files changed, 202 insertions(+), 12 deletions(-)

Interdiff against v1:
diff --git a/Documentation/git-name-rev.adoc b/Documentation/git-name-rev.adoc
index 8f050cd4763..65348690c8c 100644
--- a/Documentation/git-name-rev.adoc
+++ b/Documentation/git-name-rev.adoc
@@ -26,7 +26,8 @@ OPTIONS
 	Format revisions instead of outputting symbolic names. The
 	default is `--no-format`.
 +
-Implies `--name-only`.
+Implies `--name-only`. The negation `--no-format` implies
+`--no-name-only` (the default for the command).
 
 --tags::
 	Do not use branch names, but only tags to name the commits
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 30d981104c6..9a008d8b7a8 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -41,6 +41,11 @@ struct pretty_format {
 	struct userformat_want want;
 };
 
+struct format_cb_data {
+    const char *format;
+    int *name_only;
+};
+
 define_commit_slab(commit_rev_name, struct rev_name);
 
 static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
@@ -486,7 +491,7 @@ static const char *get_rev_name(const struct object *o,
 		}
 
 		pretty_print_commit(&format_ctx->ctx, c, buf);
-		free(format_ctx->ctx.notes_message);
+		FREE_AND_NULL(format_ctx->ctx.notes_message);
 
 		return buf->buf;
 	}
@@ -551,7 +556,7 @@ static void name_rev_line(char *p,
 		if (!ishex(*p)) {
 			counter = 0;
 		} else if (++counter == hexsz &&
-			   !ishex(*(p + 1))) {
+			 !ishex(*(p+1))) {
 			struct object_id oid;
 			const char *name = NULL;
 			char c = *(p+1);
@@ -586,6 +591,16 @@ static void name_rev_line(char *p,
 	strbuf_release(&buf);
 }
 
+static int format_cb(const struct option *option,
+		     const char *arg,
+		     int unset)
+{
+	struct format_cb_data *data = option->value;
+	data->format = arg;
+	*data->name_only = !unset;
+	return 0;
+}
+
 int cmd_name_rev(int argc,
 		 const char **argv,
 		 const char *prefix,
@@ -599,10 +614,12 @@ int cmd_name_rev(int argc,
 #endif
 	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
 	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
-	const char *format = NULL;
+	static struct format_cb_data format_cb_data = { 0 };
+	struct display_notes_opt format_notes_opt;
 	struct rev_info format_rev = REV_INFO_INIT;
 	struct pretty_format *format_ctx = NULL;
-	struct pretty_format format_pp = {0};
+	struct pretty_format format_pp = { 0 };
+	struct string_list notes = STRING_LIST_INIT_NODUP;
 	struct option opts[] = {
 		OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
 		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
@@ -620,8 +637,10 @@ int cmd_name_rev(int argc,
 			   PARSE_OPT_HIDDEN),
 #endif /* WITH_BREAKING_CHANGES */
 		OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
-		OPT_STRING(0, "format", &format, N_("format"),
-			   "pretty-print output instead"),
+		OPT_CALLBACK(0, "format", &format_cb_data, N_("format"),
+			     N_("pretty-print output instead"), format_cb),
+		OPT_STRING_LIST(0, "notes", &notes, N_("notes"),
+				N_("display notes for --format")),
 		OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
 		OPT_BOOL(0, "always",     &always,
 			   N_("show abbreviated commit object as fallback")),
@@ -630,6 +649,8 @@ int cmd_name_rev(int argc,
 		OPT_END(),
 	};
 
+	init_display_notes(&format_notes_opt);
+	format_cb_data.name_only = &data.name_only;
 	mem_pool_init(&string_pool, 0);
 	init_commit_rev_name(&rev_names);
 	repo_config(the_repository, git_default_config, NULL);
@@ -644,27 +665,29 @@ int cmd_name_rev(int argc,
 	}
 #endif
 
-	if (format) {
-		struct pretty_print_context ctx = {0};
-		struct userformat_want want = {0};
-
-		get_commit_format(format, &format_rev);
-		ctx.rev = &format_rev;
-		ctx.fmt = format_rev.commit_format;
-		ctx.abbrev = format_rev.abbrev;
-		ctx.date_mode_explicit = format_rev.date_mode_explicit;
-		ctx.date_mode = format_rev.date_mode;
-		ctx.color = GIT_COLOR_AUTO;
-		format_pp.ctx = ctx;
-
-		userformat_find_requirements(format, &want);
-		if (want.notes)
-			load_display_notes(NULL);
+	if (format_cb_data.format) {
+		get_commit_format(format_cb_data.format, &format_rev);
+		format_pp.ctx.rev = &format_rev;
+		format_pp.ctx.fmt = format_rev.commit_format;
+		format_pp.ctx.abbrev = format_rev.abbrev;
+		format_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;
+		format_pp.ctx.date_mode = format_rev.date_mode;
+		format_pp.ctx.color = GIT_COLOR_AUTO;
+
+		userformat_find_requirements(format_cb_data.format,
+					     &format_pp.want);
+		if (format_pp.want.notes) {
+			int ignore_show_notes = 0;
+			struct string_list_item *n;
+
+			for_each_string_list_item(n, &notes)
+				enable_ref_display_notes(&format_notes_opt,
+							 &ignore_show_notes,
+							 n->string);
+			load_display_notes(&format_notes_opt);
+		}
 
-		format_pp.want = want;
 		format_ctx = &format_pp;
-
-		data.name_only = true;
 	}
 
 	if (all + annotate_stdin + !!argc > 1) {
@@ -747,6 +770,8 @@ int cmd_name_rev(int argc,
 
 	string_list_clear(&data.ref_filters, 0);
 	string_list_clear(&data.exclude_filters, 0);
+	string_list_clear(&notes, 0);
+	release_display_notes(&format_notes_opt);
 	mem_pool_discard(&string_pool, 0);
 	object_array_clear(&revs);
 	return 0;
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 6dba392d343..0b7e9fe396d 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -671,6 +671,25 @@ test_expect_success 'name-rev --format setup' '
 	test_commit -C repo-format eighth
 '
 
+test_expect_success 'name-rev --format --no-name-only' '
+	cat >expect <<-\EOF &&
+	HEAD~3 [fifth]
+	HEAD [eighth]
+	HEAD~5 [third]
+	EOF
+	git -C repo-format name-rev --format="[%s]" \
+		--no-name-only HEAD~3 HEAD HEAD~5 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'name-rev --format --no-format is the same as regular name-rev' '
+	git -C repo-format name-rev HEAD~2 HEAD~3 >expect &&
+	test_file_not_empty expect &&
+	git -C repo-format name-rev --format="huh?" \
+		--no-format HEAD~2 HEAD~3 >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'name-rev --format=%s for argument revs' '
 	cat >expect <<-\EOF &&
 	eighth
@@ -682,7 +701,7 @@ test_expect_success 'name-rev --format=%s for argument revs' '
 	test_cmp expect actual
 '
 
-test_expect_success '--name-rev --format=<pretty> --annotate-stdin from rev-list same as log' '
+test_expect_success '--name-rev --format=reference --annotate-stdin from rev-list same as log' '
 	git -C repo-format log --format=reference >expect &&
 	test_file_not_empty expect &&
 	git -C repo-format rev-list HEAD >list &&
@@ -707,7 +726,7 @@ test_expect_success '--name-rev --format=<pretty> --annotate-stdin with running
 	test_cmp expect actual
 '
 
-test_expect_success '--name-rev --format=<pretty> with a note' '
+test_expect_success 'name-rev --format=<pretty> with %N (note)' '
 	test_when_finished "git -C repo-format notes remove" &&
 	git -C repo-format notes add -m"Make a note" &&
 	printf "Make a note\n\n\n" >expect &&
@@ -716,6 +735,25 @@ test_expect_success '--name-rev --format=<pretty> with a note' '
 	test_cmp expect actual
 '
 
+test_expect_success 'name-rev --format=<pretty> --notes<ref>' '
+	# One custom notes ref
+	test_when_finished "git -C repo-format notes remove" &&
+	test_when_finished "git -C repo-format notes --ref=word remove" &&
+	git -C repo-format notes add -m"default" &&
+	git -C repo-format notes --ref=word add -m"custom" &&
+	printf "custom\n\n" >expect &&
+	git -C repo-format name-rev --format="tformat:%N" \
+		--notes=word \
+		HEAD >actual &&
+	test_cmp expect actual &&
+	# Glob all
+	printf "default\ncustom\n\n" >expect &&
+	git -C repo-format name-rev --format="tformat:%N" \
+		--notes=* \
+		HEAD >actual &&
+	test_cmp expect actual
+'
+
 #               B
 #               o
 #  H             \
Range-diff against v1:
1:  6f88b4c96a9 ! 1:  9cb5cfd1ec3 name-rev: wrap both blocks in braces
    @@ builtin/name-rev.c: static void name_rev_line(char *p, struct name_ref_data *dat
     +		if (!ishex(*p)) {
      			counter = 0;
     -		else if (++counter == hexsz &&
    --			 !ishex(*(p+1))) {
     +		} else if (++counter == hexsz &&
    -+			   !ishex(*(p + 1))) {
    + 			 !ishex(*(p+1))) {
      			struct object_id oid;
      			const char *name = NULL;
    - 			char c = *(p+1);
2:  6430627e611 ! 2:  52a52060776 name-rev: learn --format=<pretty>
    @@ Documentation/git-name-rev.adoc: format parsable by 'git rev-parse'.
     +	Format revisions instead of outputting symbolic names. The
     +	default is `--no-format`.
     ++
    -+Implies `--name-only`.
    ++Implies `--name-only`. The negation `--no-format` implies
    ++`--no-name-only` (the default for the command).
     +
      --tags::
      	Do not use branch names, but only tags to name the commits
    @@ builtin/name-rev.c: struct rev_name {
     +	struct pretty_print_context ctx;
     +	struct userformat_want want;
     +};
    ++
    ++struct format_cb_data {
    ++    const char *format;
    ++    int *name_only;
    ++};
     +
      define_commit_slab(commit_rev_name, struct rev_name);
      
    @@ builtin/name-rev.c: static const char *get_rev_name(const struct object *o, stru
     +		}
     +
     +		pretty_print_commit(&format_ctx->ctx, c, buf);
    -+		free(format_ctx->ctx.notes_message);
    ++		FREE_AND_NULL(format_ctx->ctx.notes_message);
     +
     +		return buf->buf;
     +	}
    @@ builtin/name-rev.c: static void name_rev_line(char *p, struct name_ref_data *dat
      			}
      			*(p+1) = c;
      
    +@@ builtin/name-rev.c: static void name_rev_line(char *p, struct name_ref_data *data)
    + 	strbuf_release(&buf);
    + }
    + 
    ++static int format_cb(const struct option *option,
    ++		     const char *arg,
    ++		     int unset)
    ++{
    ++	struct format_cb_data *data = option->value;
    ++	data->format = arg;
    ++	*data->name_only = !unset;
    ++	return 0;
    ++}
    ++
    + int cmd_name_rev(int argc,
    + 		 const char **argv,
    + 		 const char *prefix,
     @@ builtin/name-rev.c: int cmd_name_rev(int argc,
      #endif
      	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
      	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
    -+	const char *format = NULL;
    ++	static struct format_cb_data format_cb_data = { 0 };
    ++	struct display_notes_opt format_notes_opt;
     +	struct rev_info format_rev = REV_INFO_INIT;
     +	struct pretty_format *format_ctx = NULL;
    -+	struct pretty_format format_pp = {0};
    ++	struct pretty_format format_pp = { 0 };
    ++	struct string_list notes = STRING_LIST_INIT_NODUP;
      	struct option opts[] = {
      		OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
      		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
    @@ builtin/name-rev.c: int cmd_name_rev(int argc,
      			   PARSE_OPT_HIDDEN),
      #endif /* WITH_BREAKING_CHANGES */
      		OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
    -+		OPT_STRING(0, "format", &format, N_("format"),
    -+			   "pretty-print output instead"),
    ++		OPT_CALLBACK(0, "format", &format_cb_data, N_("format"),
    ++			     N_("pretty-print output instead"), format_cb),
    ++		OPT_STRING_LIST(0, "notes", &notes, N_("notes"),
    ++				N_("display notes for --format")),
      		OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
      		OPT_BOOL(0, "always",     &always,
      			   N_("show abbreviated commit object as fallback")),
    +@@ builtin/name-rev.c: int cmd_name_rev(int argc,
    + 		OPT_END(),
    + 	};
    + 
    ++	init_display_notes(&format_notes_opt);
    ++	format_cb_data.name_only = &data.name_only;
    + 	mem_pool_init(&string_pool, 0);
    + 	init_commit_rev_name(&rev_names);
    + 	repo_config(the_repository, git_default_config, NULL);
     @@ builtin/name-rev.c: int cmd_name_rev(int argc,
      	}
      #endif
      
    -+	if (format) {
    -+		struct pretty_print_context ctx = {0};
    -+		struct userformat_want want = {0};
    ++	if (format_cb_data.format) {
    ++		get_commit_format(format_cb_data.format, &format_rev);
    ++		format_pp.ctx.rev = &format_rev;
    ++		format_pp.ctx.fmt = format_rev.commit_format;
    ++		format_pp.ctx.abbrev = format_rev.abbrev;
    ++		format_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;
    ++		format_pp.ctx.date_mode = format_rev.date_mode;
    ++		format_pp.ctx.color = GIT_COLOR_AUTO;
     +
    -+		get_commit_format(format, &format_rev);
    -+		ctx.rev = &format_rev;
    -+		ctx.fmt = format_rev.commit_format;
    -+		ctx.abbrev = format_rev.abbrev;
    -+		ctx.date_mode_explicit = format_rev.date_mode_explicit;
    -+		ctx.date_mode = format_rev.date_mode;
    -+		ctx.color = GIT_COLOR_AUTO;
    -+		format_pp.ctx = ctx;
    ++		userformat_find_requirements(format_cb_data.format,
    ++					     &format_pp.want);
    ++		if (format_pp.want.notes) {
    ++			int ignore_show_notes = 0;
    ++			struct string_list_item *n;
     +
    -+		userformat_find_requirements(format, &want);
    -+		if (want.notes)
    -+			load_display_notes(NULL);
    ++			for_each_string_list_item(n, &notes)
    ++				enable_ref_display_notes(&format_notes_opt,
    ++							 &ignore_show_notes,
    ++							 n->string);
    ++			load_display_notes(&format_notes_opt);
    ++		}
     +
    -+		format_pp.want = want;
     +		format_ctx = &format_pp;
    -+
    -+		data.name_only = true;
     +	}
     +
      	if (all + annotate_stdin + !!argc > 1) {
    @@ builtin/name-rev.c: int cmd_name_rev(int argc,
      				  always, allow_undefined, data.name_only);
      	}
      
    + 	string_list_clear(&data.ref_filters, 0);
    + 	string_list_clear(&data.exclude_filters, 0);
    ++	string_list_clear(&notes, 0);
    ++	release_display_notes(&format_notes_opt);
    + 	mem_pool_discard(&string_pool, 0);
    + 	object_array_clear(&revs);
    + 	return 0;
     
      ## t/t6120-describe.sh ##
     @@ t/t6120-describe.sh: test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
    @@ t/t6120-describe.sh: test_expect_success 'name-rev --annotate-stdin works with c
     +	test_commit -C repo-format eighth
     +'
     +
    ++test_expect_success 'name-rev --format --no-name-only' '
    ++	cat >expect <<-\EOF &&
    ++	HEAD~3 [fifth]
    ++	HEAD [eighth]
    ++	HEAD~5 [third]
    ++	EOF
    ++	git -C repo-format name-rev --format="[%s]" \
    ++		--no-name-only HEAD~3 HEAD HEAD~5 >actual &&
    ++	test_cmp expect actual
    ++'
    ++
    ++test_expect_success 'name-rev --format --no-format is the same as regular name-rev' '
    ++	git -C repo-format name-rev HEAD~2 HEAD~3 >expect &&
    ++	test_file_not_empty expect &&
    ++	git -C repo-format name-rev --format="huh?" \
    ++		--no-format HEAD~2 HEAD~3 >actual &&
    ++	test_cmp expect actual
    ++'
    ++
     +test_expect_success 'name-rev --format=%s for argument revs' '
     +	cat >expect <<-\EOF &&
     +	eighth
    @@ t/t6120-describe.sh: test_expect_success 'name-rev --annotate-stdin works with c
     +	test_cmp expect actual
     +'
     +
    -+test_expect_success '--name-rev --format=<pretty> --annotate-stdin from rev-list same as log' '
    ++test_expect_success '--name-rev --format=reference --annotate-stdin from rev-list same as log' '
     +	git -C repo-format log --format=reference >expect &&
     +	test_file_not_empty expect &&
     +	git -C repo-format rev-list HEAD >list &&
    @@ t/t6120-describe.sh: test_expect_success 'name-rev --annotate-stdin works with c
     +	test_cmp expect actual
     +'
     +
    -+test_expect_success '--name-rev --format=<pretty> with a note' '
    ++test_expect_success 'name-rev --format=<pretty> with %N (note)' '
     +	test_when_finished "git -C repo-format notes remove" &&
     +	git -C repo-format notes add -m"Make a note" &&
     +	printf "Make a note\n\n\n" >expect &&
    @@ t/t6120-describe.sh: test_expect_success 'name-rev --annotate-stdin works with c
     +		HEAD HEAD~ >actual &&
     +	test_cmp expect actual
     +'
    ++
    ++test_expect_success 'name-rev --format=<pretty> --notes<ref>' '
    ++	# One custom notes ref
    ++	test_when_finished "git -C repo-format notes remove" &&
    ++	test_when_finished "git -C repo-format notes --ref=word remove" &&
    ++	git -C repo-format notes add -m"default" &&
    ++	git -C repo-format notes --ref=word add -m"custom" &&
    ++	printf "custom\n\n" >expect &&
    ++	git -C repo-format name-rev --format="tformat:%N" \
    ++		--notes=word \
    ++		HEAD >actual &&
    ++	test_cmp expect actual &&
    ++	# Glob all
    ++	printf "default\ncustom\n\n" >expect &&
    ++	git -C repo-format name-rev --format="tformat:%N" \
    ++		--notes=* \
    ++		HEAD >actual &&
    ++	test_cmp expect actual
    ++'
     +
      #               B
      #               o

base-commit: 67006b9db8b772423ad0706029286096307d2567
-- 
2.53.0.32.gf6228eaf9cc


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 1/2] name-rev: wrap both blocks in braces
  2026-03-20 13:09 ` [PATCH v2 0/2] " kristofferhaugsbakk
@ 2026-03-20 13:09   ` kristofferhaugsbakk
  2026-03-20 13:09   ` [PATCH v2 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
  1 sibling, 0 replies; 13+ messages in thread
From: kristofferhaugsbakk @ 2026-03-20 13:09 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

See `CodingGuidelines`:

    - When there are multiple arms to a conditional and some of them
      require braces, enclose even a single line block in braces for
      consistency. [...]

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---

Notes (series):
    v2:
    Fix stray formatting of `(p+1)`

 builtin/name-rev.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 6188cf98ce0..171e7bd0e98 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -466,9 +466,9 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 	if (!n)
 		return NULL;
 
-	if (!n->generation)
+	if (!n->generation) {
 		return n->tip_name;
-	else {
+	} else {
 		strbuf_reset(buf);
 		strbuf_addstr(buf, n->tip_name);
 		strbuf_strip_suffix(buf, "^0");
@@ -516,9 +516,9 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 	for (p_start = p; *p; p++) {
 #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
-		if (!ishex(*p))
+		if (!ishex(*p)) {
 			counter = 0;
-		else if (++counter == hexsz &&
+		} else if (++counter == hexsz &&
 			 !ishex(*(p+1))) {
 			struct object_id oid;
 			const char *name = NULL;
-- 
2.53.0.32.gf6228eaf9cc


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/2] name-rev: learn --format=<pretty>
  2026-03-20 13:09 ` [PATCH v2 0/2] " kristofferhaugsbakk
  2026-03-20 13:09   ` [PATCH v2 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
@ 2026-03-20 13:09   ` kristofferhaugsbakk
  2026-03-20 15:25     ` D. Ben Knoble
  1 sibling, 1 reply; 13+ messages in thread
From: kristofferhaugsbakk @ 2026-03-20 13:09 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk

From: Kristoffer Haugsbakk <code@khaugsbakk.name>

Teach git-name-rev(1) to format the given revisions instead of creating
symbolic names.

Sometimes you want to format commits. Most of the time you’re walking
the graph, e.g. getting a range of commits like `master..topic`. That’s
a job for git-log(1).

But sometimes you might want to format commits that you encounter
on demand:

• Full hashes in running text that you might want to pretty-print
• git-last-modified(1) outputs full hashes that you can do the same with
• git-cherry(1) has `-v` for commit subject, but maybe you want
  something else?

But now you can’t use git-log(1), git-show(1), or git-rev-list(1):

• You can’t feed commits piecemeal to these commands, one input for one
  output; they block until standard in is closed
• You can’t feed a list of possibly duplicate commits, like the output
  of git-last-modified(1); they effectively deduplicate the output

Beyond these two points there’s also the input massage problem: you
cannot feed mixed input (revisions mixed with arbitrary text).

One might hope that git-cat-file(1) can save us. But it doesn’t support
pretty formats.

But there is one command that already both handles revisions as
arguments, revisions on standard input, and even revisions mixed
in with arbitrary text. Namely git-name-rev(1).

Teach it to work in a format mode where the output for each revision is
the pretty output (implies `--name-only`). This can be used to format
any revision expression when given as arguments, and all full commit
hashes in running text on stdin.

Just bring the hashes (to the pipeline). We will pretty print them.

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
---

Notes (series):
    v2:
    • Propely implement “--format implies --name-only”
    • Don’t use a needless intermediary struct
    • Add a new member to `name_rev_usage[]`
    • FREE_AND_NULL notes string. There is no use-after-free but we have no
      reason to leave a freed pointer just laying there
    • Implement `--notes` for `%N` atom use (not just restrict to the default
      notes ref)
    • Previous review pointed out `init_revisions()` but this still just uses
      `REV_INFO_INIT` since it seemed enough. But I have no problem with
      changing it whatsoever. (This series is still very work-in-progress in
      any case.)
    • Tweak test name to mention the specific pretty format (reference); it
      doesn’t generalize to e.g. `oneline` because you get different output in
      that case
    
    Mostly from: https://lore.kernel.org/git/xmqq8qbvz2dm.fsf@gitster.g/

 Documentation/git-name-rev.adoc |  10 +++-
 builtin/name-rev.c              | 100 +++++++++++++++++++++++++++++---
 t/t6120-describe.sh             |  96 ++++++++++++++++++++++++++++++
 3 files changed, 198 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-name-rev.adoc b/Documentation/git-name-rev.adoc
index d4f1c4d5945..65348690c8c 100644
--- a/Documentation/git-name-rev.adoc
+++ b/Documentation/git-name-rev.adoc
@@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
 SYNOPSIS
 --------
 [verse]
-'git name-rev' [--tags] [--refs=<pattern>]
+'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
 	       ( --all | --annotate-stdin | <commit-ish>... )
 
 DESCRIPTION
@@ -21,6 +21,14 @@ format parsable by 'git rev-parse'.
 OPTIONS
 -------
 
+--format=<pretty>::
+--no-format::
+	Format revisions instead of outputting symbolic names. The
+	default is `--no-format`.
++
+Implies `--name-only`. The negation `--no-format` implies
+`--no-name-only` (the default for the command).
+
 --tags::
 	Do not use branch names, but only tags to name the commits
 
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 171e7bd0e98..9a008d8b7a8 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -18,6 +18,9 @@
 #include "commit-graph.h"
 #include "wildmatch.h"
 #include "mem-pool.h"
+#include "pretty.h"
+#include "revision.h"
+#include "notes.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -33,6 +36,16 @@ struct rev_name {
 	int from_tag;
 };
 
+struct pretty_format {
+	struct pretty_print_context ctx;
+	struct userformat_want want;
+};
+
+struct format_cb_data {
+    const char *format;
+    int *name_only;
+};
+
 define_commit_slab(commit_rev_name, struct rev_name);
 
 static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
@@ -454,7 +467,9 @@ static const char *get_exact_ref_match(const struct object *o)
 }
 
 /* may return a constant string or use "buf" as scratch space */
-static const char *get_rev_name(const struct object *o, struct strbuf *buf)
+static const char *get_rev_name(const struct object *o,
+				struct pretty_format *format_ctx,
+				struct strbuf *buf)
 {
 	struct rev_name *n;
 	const struct commit *c;
@@ -462,6 +477,25 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 	if (o->type != OBJ_COMMIT)
 		return get_exact_ref_match(o);
 	c = (const struct commit *) o;
+
+	if (format_ctx) {
+		strbuf_reset(buf);
+
+		if (format_ctx->want.notes) {
+			struct strbuf notebuf = STRBUF_INIT;
+
+			format_display_notes(&c->object.oid, &notebuf,
+					     get_log_output_encoding(),
+					     format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
+			format_ctx->ctx.notes_message = strbuf_detach(&notebuf, NULL);
+		}
+
+		pretty_print_commit(&format_ctx->ctx, c, buf);
+		FREE_AND_NULL(format_ctx->ctx.notes_message);
+
+		return buf->buf;
+	}
+
 	n = get_commit_rev_name(c);
 	if (!n)
 		return NULL;
@@ -479,6 +513,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 
 static void show_name(const struct object *obj,
 		      const char *caller_name,
+		      struct pretty_format *format_ctx,
 		      int always, int allow_undefined, int name_only)
 {
 	const char *name;
@@ -487,7 +522,7 @@ static void show_name(const struct object *obj,
 
 	if (!name_only)
 		printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
-	name = get_rev_name(obj, &buf);
+	name = get_rev_name(obj, format_ctx, &buf);
 	if (name)
 		printf("%s\n", name);
 	else if (allow_undefined)
@@ -507,7 +542,9 @@ static char const * const name_rev_usage[] = {
 	NULL
 };
 
-static void name_rev_line(char *p, struct name_ref_data *data)
+static void name_rev_line(char *p,
+			  struct name_ref_data *data,
+			  struct pretty_format *format_ctx)
 {
 	struct strbuf buf = STRBUF_INIT;
 	int counter = 0;
@@ -532,7 +569,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 				struct object *o =
 					lookup_object(the_repository, &oid);
 				if (o)
-					name = get_rev_name(o, &buf);
+					name = get_rev_name(o, format_ctx, &buf);
 			}
 			*(p+1) = c;
 
@@ -554,6 +591,16 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 	strbuf_release(&buf);
 }
 
+static int format_cb(const struct option *option,
+		     const char *arg,
+		     int unset)
+{
+	struct format_cb_data *data = option->value;
+	data->format = arg;
+	*data->name_only = !unset;
+	return 0;
+}
+
 int cmd_name_rev(int argc,
 		 const char **argv,
 		 const char *prefix,
@@ -567,6 +614,12 @@ int cmd_name_rev(int argc,
 #endif
 	int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
 	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
+	static struct format_cb_data format_cb_data = { 0 };
+	struct display_notes_opt format_notes_opt;
+	struct rev_info format_rev = REV_INFO_INIT;
+	struct pretty_format *format_ctx = NULL;
+	struct pretty_format format_pp = { 0 };
+	struct string_list notes = STRING_LIST_INIT_NODUP;
 	struct option opts[] = {
 		OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
 		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
@@ -584,6 +637,10 @@ int cmd_name_rev(int argc,
 			   PARSE_OPT_HIDDEN),
 #endif /* WITH_BREAKING_CHANGES */
 		OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
+		OPT_CALLBACK(0, "format", &format_cb_data, N_("format"),
+			     N_("pretty-print output instead"), format_cb),
+		OPT_STRING_LIST(0, "notes", &notes, N_("notes"),
+				N_("display notes for --format")),
 		OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
 		OPT_BOOL(0, "always",     &always,
 			   N_("show abbreviated commit object as fallback")),
@@ -592,6 +649,8 @@ int cmd_name_rev(int argc,
 		OPT_END(),
 	};
 
+	init_display_notes(&format_notes_opt);
+	format_cb_data.name_only = &data.name_only;
 	mem_pool_init(&string_pool, 0);
 	init_commit_rev_name(&rev_names);
 	repo_config(the_repository, git_default_config, NULL);
@@ -606,6 +665,31 @@ int cmd_name_rev(int argc,
 	}
 #endif
 
+	if (format_cb_data.format) {
+		get_commit_format(format_cb_data.format, &format_rev);
+		format_pp.ctx.rev = &format_rev;
+		format_pp.ctx.fmt = format_rev.commit_format;
+		format_pp.ctx.abbrev = format_rev.abbrev;
+		format_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;
+		format_pp.ctx.date_mode = format_rev.date_mode;
+		format_pp.ctx.color = GIT_COLOR_AUTO;
+
+		userformat_find_requirements(format_cb_data.format,
+					     &format_pp.want);
+		if (format_pp.want.notes) {
+			int ignore_show_notes = 0;
+			struct string_list_item *n;
+
+			for_each_string_list_item(n, &notes)
+				enable_ref_display_notes(&format_notes_opt,
+							 &ignore_show_notes,
+							 n->string);
+			load_display_notes(&format_notes_opt);
+		}
+
+		format_ctx = &format_pp;
+	}
+
 	if (all + annotate_stdin + !!argc > 1) {
 		error("Specify either a list, or --all, not both!");
 		usage_with_options(name_rev_usage, opts);
@@ -663,7 +747,7 @@ int cmd_name_rev(int argc,
 
 		while (strbuf_getline(&sb, stdin) != EOF) {
 			strbuf_addch(&sb, '\n');
-			name_rev_line(sb.buf, &data);
+			name_rev_line(sb.buf, &data, format_ctx);
 		}
 		strbuf_release(&sb);
 	} else if (all) {
@@ -674,18 +758,20 @@ int cmd_name_rev(int argc,
 			struct object *obj = get_indexed_object(the_repository, i);
 			if (!obj || obj->type != OBJ_COMMIT)
 				continue;
-			show_name(obj, NULL,
+			show_name(obj, NULL, format_ctx,
 				  always, allow_undefined, data.name_only);
 		}
 	} else {
 		int i;
 		for (i = 0; i < revs.nr; i++)
-			show_name(revs.objects[i].item, revs.objects[i].name,
+			show_name(revs.objects[i].item, revs.objects[i].name, format_ctx,
 				  always, allow_undefined, data.name_only);
 	}
 
 	string_list_clear(&data.ref_filters, 0);
 	string_list_clear(&data.exclude_filters, 0);
+	string_list_clear(&notes, 0);
+	release_display_notes(&format_notes_opt);
 	mem_pool_discard(&string_pool, 0);
 	object_array_clear(&revs);
 	return 0;
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 2c70cc561ad..0b7e9fe396d 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -658,6 +658,102 @@ test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
 	)
 '
 
+test_expect_success 'name-rev --format setup' '
+	mkdir repo-format &&
+	git -C repo-format init &&
+	test_commit -C repo-format first &&
+	test_commit -C repo-format second &&
+	test_commit -C repo-format third &&
+	test_commit -C repo-format fourth &&
+	test_commit -C repo-format fifth &&
+	test_commit -C repo-format sixth &&
+	test_commit -C repo-format seventh &&
+	test_commit -C repo-format eighth
+'
+
+test_expect_success 'name-rev --format --no-name-only' '
+	cat >expect <<-\EOF &&
+	HEAD~3 [fifth]
+	HEAD [eighth]
+	HEAD~5 [third]
+	EOF
+	git -C repo-format name-rev --format="[%s]" \
+		--no-name-only HEAD~3 HEAD HEAD~5 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'name-rev --format --no-format is the same as regular name-rev' '
+	git -C repo-format name-rev HEAD~2 HEAD~3 >expect &&
+	test_file_not_empty expect &&
+	git -C repo-format name-rev --format="huh?" \
+		--no-format HEAD~2 HEAD~3 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'name-rev --format=%s for argument revs' '
+	cat >expect <<-\EOF &&
+	eighth
+	seventh
+	fifth
+	EOF
+	git -C repo-format name-rev --format=%s \
+		HEAD HEAD~ HEAD~3 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--name-rev --format=reference --annotate-stdin from rev-list same as log' '
+	git -C repo-format log --format=reference >expect &&
+	test_file_not_empty expect &&
+	git -C repo-format rev-list HEAD >list &&
+	git -C repo-format name-rev --format=reference \
+		--annotate-stdin <list >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--name-rev --format=<pretty> --annotate-stdin with running text and tree oid' '
+	cmit_oid=$(git -C repo-format rev-parse :/fifth) &&
+	reference=$(git -C repo-format log -n1 --format=reference :/fifth) &&
+	tree=$(git -C repo-format rev-parse HEAD^{tree}) &&
+	cat >expect <<-EOF &&
+	We thought we fixed this in ${reference}.
+	But look at this tree: ${tree}.
+	EOF
+	git -C repo-format name-rev --format=reference --annotate-stdin \
+		>actual <<-EOF &&
+	We thought we fixed this in ${cmit_oid}.
+	But look at this tree: ${tree}.
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'name-rev --format=<pretty> with %N (note)' '
+	test_when_finished "git -C repo-format notes remove" &&
+	git -C repo-format notes add -m"Make a note" &&
+	printf "Make a note\n\n\n" >expect &&
+	git -C repo-format name-rev --format="tformat:%N" \
+		HEAD HEAD~ >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'name-rev --format=<pretty> --notes<ref>' '
+	# One custom notes ref
+	test_when_finished "git -C repo-format notes remove" &&
+	test_when_finished "git -C repo-format notes --ref=word remove" &&
+	git -C repo-format notes add -m"default" &&
+	git -C repo-format notes --ref=word add -m"custom" &&
+	printf "custom\n\n" >expect &&
+	git -C repo-format name-rev --format="tformat:%N" \
+		--notes=word \
+		HEAD >actual &&
+	test_cmp expect actual &&
+	# Glob all
+	printf "default\ncustom\n\n" >expect &&
+	git -C repo-format name-rev --format="tformat:%N" \
+		--notes=* \
+		HEAD >actual &&
+	test_cmp expect actual
+'
+
 #               B
 #               o
 #  H             \
-- 
2.53.0.32.gf6228eaf9cc


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/2] name-rev: learn --format=<pretty>
  2026-03-20 13:09   ` [PATCH v2 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
@ 2026-03-20 15:25     ` D. Ben Knoble
  2026-03-23 17:34       ` Kristoffer Haugsbakk
  0 siblings, 1 reply; 13+ messages in thread
From: D. Ben Knoble @ 2026-03-20 15:25 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk

On Fri, Mar 20, 2026 at 9:13 AM <kristofferhaugsbakk@fastmail.com> wrote:
>
> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> Teach git-name-rev(1) to format the given revisions instead of creating
> symbolic names.
[snip]
> ---
>
> Notes (series):
>     v2:
>     • Propely implement “--format implies --name-only”
>     • Don’t use a needless intermediary struct
>     • Add a new member to `name_rev_usage[]`
>     • FREE_AND_NULL notes string. There is no use-after-free but we have no
>       reason to leave a freed pointer just laying there
>     • Implement `--notes` for `%N` atom use (not just restrict to the default
>       notes ref)
[snip]
> diff --git a/Documentation/git-name-rev.adoc b/Documentation/git-name-rev.adoc
> index d4f1c4d5945..65348690c8c 100644
> --- a/Documentation/git-name-rev.adoc
> +++ b/Documentation/git-name-rev.adoc
> @@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
>  SYNOPSIS
>  --------
>  [verse]
> -'git name-rev' [--tags] [--refs=<pattern>]
> +'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
>                ( --all | --annotate-stdin | <commit-ish>... )
>
>  DESCRIPTION
> @@ -21,6 +21,14 @@ format parsable by 'git rev-parse'.
>  OPTIONS
>  -------
>
> +--format=<pretty>::
> +--no-format::
> +       Format revisions instead of outputting symbolic names. The
> +       default is `--no-format`.
> ++
> +Implies `--name-only`. The negation `--no-format` implies
> +`--no-name-only` (the default for the command).
> +
>  --tags::
>         Do not use branch names, but only tags to name the commits

IIUC that this patch also adds --notes, should it be documented here?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/2] name-rev: learn --format=<pretty>
  2026-03-20 15:25     ` D. Ben Knoble
@ 2026-03-23 17:34       ` Kristoffer Haugsbakk
  0 siblings, 0 replies; 13+ messages in thread
From: Kristoffer Haugsbakk @ 2026-03-23 17:34 UTC (permalink / raw)
  To: D. Ben Knoble; +Cc: git

On Fri, Mar 20, 2026, at 16:25, D. Ben Knoble wrote:
>>[snip]
>> +--format=<pretty>::
>> +--no-format::
>> +       Format revisions instead of outputting symbolic names. The
>> +       default is `--no-format`.
>> ++
>> +Implies `--name-only`. The negation `--no-format` implies
>> +`--no-name-only` (the default for the command).
>> +
>>  --tags::
>>         Do not use branch names, but only tags to name the commits
>
> IIUC that this patch also adds --notes, should it be documented here?

Yes certainly. It dropped from my mind when doing the last session for
this version.

Thanks for spotting.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-03-23 17:35 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-13 16:03 [PATCH 0/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
2026-03-13 16:03 ` [PATCH 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
2026-03-14  0:22   ` Junio C Hamano
2026-03-17 22:10     ` Kristoffer Haugsbakk
2026-03-13 16:03 ` [PATCH 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
2026-03-14  0:22   ` Junio C Hamano
2026-03-17 22:07     ` Kristoffer Haugsbakk
2026-03-18 15:36       ` Kristoffer Haugsbakk
2026-03-20 13:09 ` [PATCH v2 0/2] " kristofferhaugsbakk
2026-03-20 13:09   ` [PATCH v2 1/2] name-rev: wrap both blocks in braces kristofferhaugsbakk
2026-03-20 13:09   ` [PATCH v2 2/2] name-rev: learn --format=<pretty> kristofferhaugsbakk
2026-03-20 15:25     ` D. Ben Knoble
2026-03-23 17:34       ` Kristoffer Haugsbakk

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox