Skip to content

Commit 1c9573c

Browse files
LemmingAvalanchegitster
authored andcommitted
name-rev: learn --format=<pretty>
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> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 211a95d commit 1c9573c

3 files changed

Lines changed: 134 additions & 8 deletions

File tree

Documentation/git-name-rev.adoc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-name-rev - Find symbolic names for given revs
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git name-rev' [--tags] [--refs=<pattern>]
12+
'git name-rev' [--tags] [--refs=<pattern>] [--format=<pretty>]
1313
( --all | --annotate-stdin | <commit-ish>... )
1414

1515
DESCRIPTION
@@ -21,6 +21,13 @@ format parsable by 'git rev-parse'.
2121
OPTIONS
2222
-------
2323

24+
--format=<pretty>::
25+
--no-format::
26+
Format revisions instead of outputting symbolic names. The
27+
default is `--no-format`.
28+
+
29+
Implies `--name-only`.
30+
2431
--tags::
2532
Do not use branch names, but only tags to name the commits
2633

builtin/name-rev.c

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include "commit-graph.h"
1919
#include "wildmatch.h"
2020
#include "mem-pool.h"
21+
#include "pretty.h"
22+
#include "revision.h"
23+
#include "notes.h"
2124

2225
/*
2326
* One day. See the 'name a rev shortly after epoch' test in t6120 when
@@ -33,6 +36,11 @@ struct rev_name {
3336
int from_tag;
3437
};
3538

39+
struct pretty_format {
40+
struct pretty_print_context ctx;
41+
struct userformat_want want;
42+
};
43+
3644
define_commit_slab(commit_rev_name, struct rev_name);
3745

3846
static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
@@ -454,14 +462,35 @@ static const char *get_exact_ref_match(const struct object *o)
454462
}
455463

456464
/* may return a constant string or use "buf" as scratch space */
457-
static const char *get_rev_name(const struct object *o, struct strbuf *buf)
465+
static const char *get_rev_name(const struct object *o,
466+
struct pretty_format *format_ctx,
467+
struct strbuf *buf)
458468
{
459469
struct rev_name *n;
460470
const struct commit *c;
461471

462472
if (o->type != OBJ_COMMIT)
463473
return get_exact_ref_match(o);
464474
c = (const struct commit *) o;
475+
476+
if (format_ctx) {
477+
strbuf_reset(buf);
478+
479+
if (format_ctx->want.notes) {
480+
struct strbuf notebuf = STRBUF_INIT;
481+
482+
format_display_notes(&c->object.oid, &notebuf,
483+
get_log_output_encoding(),
484+
format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT);
485+
format_ctx->ctx.notes_message = strbuf_detach(&notebuf, NULL);
486+
}
487+
488+
pretty_print_commit(&format_ctx->ctx, c, buf);
489+
free(format_ctx->ctx.notes_message);
490+
491+
return buf->buf;
492+
}
493+
465494
n = get_commit_rev_name(c);
466495
if (!n)
467496
return NULL;
@@ -479,6 +508,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
479508

480509
static void show_name(const struct object *obj,
481510
const char *caller_name,
511+
struct pretty_format *format_ctx,
482512
int always, int allow_undefined, int name_only)
483513
{
484514
const char *name;
@@ -487,7 +517,7 @@ static void show_name(const struct object *obj,
487517

488518
if (!name_only)
489519
printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
490-
name = get_rev_name(obj, &buf);
520+
name = get_rev_name(obj, format_ctx, &buf);
491521
if (name)
492522
printf("%s\n", name);
493523
else if (allow_undefined)
@@ -507,7 +537,9 @@ static char const * const name_rev_usage[] = {
507537
NULL
508538
};
509539

510-
static void name_rev_line(char *p, struct name_ref_data *data)
540+
static void name_rev_line(char *p,
541+
struct name_ref_data *data,
542+
struct pretty_format *format_ctx)
511543
{
512544
struct strbuf buf = STRBUF_INIT;
513545
int counter = 0;
@@ -532,7 +564,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
532564
struct object *o =
533565
lookup_object(the_repository, &oid);
534566
if (o)
535-
name = get_rev_name(o, &buf);
567+
name = get_rev_name(o, format_ctx, &buf);
536568
}
537569
*(p+1) = c;
538570

@@ -567,6 +599,10 @@ int cmd_name_rev(int argc,
567599
#endif
568600
int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
569601
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
602+
const char *format = NULL;
603+
struct rev_info format_rev = REV_INFO_INIT;
604+
struct pretty_format *format_ctx = NULL;
605+
struct pretty_format format_pp = {0};
570606
struct option opts[] = {
571607
OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
572608
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,
584620
PARSE_OPT_HIDDEN),
585621
#endif /* WITH_BREAKING_CHANGES */
586622
OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
623+
OPT_STRING(0, "format", &format, N_("format"),
624+
"pretty-print output instead"),
587625
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
588626
OPT_BOOL(0, "always", &always,
589627
N_("show abbreviated commit object as fallback")),
@@ -606,6 +644,29 @@ int cmd_name_rev(int argc,
606644
}
607645
#endif
608646

647+
if (format) {
648+
struct pretty_print_context ctx = {0};
649+
struct userformat_want want = {0};
650+
651+
get_commit_format(format, &format_rev);
652+
ctx.rev = &format_rev;
653+
ctx.fmt = format_rev.commit_format;
654+
ctx.abbrev = format_rev.abbrev;
655+
ctx.date_mode_explicit = format_rev.date_mode_explicit;
656+
ctx.date_mode = format_rev.date_mode;
657+
ctx.color = GIT_COLOR_AUTO;
658+
format_pp.ctx = ctx;
659+
660+
userformat_find_requirements(format, &want);
661+
if (want.notes)
662+
load_display_notes(NULL);
663+
664+
format_pp.want = want;
665+
format_ctx = &format_pp;
666+
667+
data.name_only = true;
668+
}
669+
609670
if (all + annotate_stdin + !!argc > 1) {
610671
error("Specify either a list, or --all, not both!");
611672
usage_with_options(name_rev_usage, opts);
@@ -663,7 +724,7 @@ int cmd_name_rev(int argc,
663724

664725
while (strbuf_getline(&sb, stdin) != EOF) {
665726
strbuf_addch(&sb, '\n');
666-
name_rev_line(sb.buf, &data);
727+
name_rev_line(sb.buf, &data, format_ctx);
667728
}
668729
strbuf_release(&sb);
669730
} else if (all) {
@@ -674,13 +735,13 @@ int cmd_name_rev(int argc,
674735
struct object *obj = get_indexed_object(the_repository, i);
675736
if (!obj || obj->type != OBJ_COMMIT)
676737
continue;
677-
show_name(obj, NULL,
738+
show_name(obj, NULL, format_ctx,
678739
always, allow_undefined, data.name_only);
679740
}
680741
} else {
681742
int i;
682743
for (i = 0; i < revs.nr; i++)
683-
show_name(revs.objects[i].item, revs.objects[i].name,
744+
show_name(revs.objects[i].item, revs.objects[i].name, format_ctx,
684745
always, allow_undefined, data.name_only);
685746
}
686747

t/t6120-describe.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,64 @@ test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
658658
)
659659
'
660660

661+
test_expect_success 'name-rev --format setup' '
662+
mkdir repo-format &&
663+
git -C repo-format init &&
664+
test_commit -C repo-format first &&
665+
test_commit -C repo-format second &&
666+
test_commit -C repo-format third &&
667+
test_commit -C repo-format fourth &&
668+
test_commit -C repo-format fifth &&
669+
test_commit -C repo-format sixth &&
670+
test_commit -C repo-format seventh &&
671+
test_commit -C repo-format eighth
672+
'
673+
674+
test_expect_success 'name-rev --format=%s for argument revs' '
675+
cat >expect <<-\EOF &&
676+
eighth
677+
seventh
678+
fifth
679+
EOF
680+
git -C repo-format name-rev --format=%s \
681+
HEAD HEAD~ HEAD~3 >actual &&
682+
test_cmp expect actual
683+
'
684+
685+
test_expect_success '--name-rev --format=<pretty> --annotate-stdin from rev-list same as log' '
686+
git -C repo-format log --format=reference >expect &&
687+
test_file_not_empty expect &&
688+
git -C repo-format rev-list HEAD >list &&
689+
git -C repo-format name-rev --format=reference \
690+
--annotate-stdin <list >actual &&
691+
test_cmp expect actual
692+
'
693+
694+
test_expect_success '--name-rev --format=<pretty> --annotate-stdin with running text and tree oid' '
695+
cmit_oid=$(git -C repo-format rev-parse :/fifth) &&
696+
reference=$(git -C repo-format log -n1 --format=reference :/fifth) &&
697+
tree=$(git -C repo-format rev-parse HEAD^{tree}) &&
698+
cat >expect <<-EOF &&
699+
We thought we fixed this in ${reference}.
700+
But look at this tree: ${tree}.
701+
EOF
702+
git -C repo-format name-rev --format=reference --annotate-stdin \
703+
>actual <<-EOF &&
704+
We thought we fixed this in ${cmit_oid}.
705+
But look at this tree: ${tree}.
706+
EOF
707+
test_cmp expect actual
708+
'
709+
710+
test_expect_success '--name-rev --format=<pretty> with a note' '
711+
test_when_finished "git -C repo-format notes remove" &&
712+
git -C repo-format notes add -m"Make a note" &&
713+
printf "Make a note\n\n\n" >expect &&
714+
git -C repo-format name-rev --format="tformat:%N" \
715+
HEAD HEAD~ >actual &&
716+
test_cmp expect actual
717+
'
718+
661719
# B
662720
# o
663721
# H \

0 commit comments

Comments
 (0)