rev-list: add option for --pretty=format without header

In general, we encourage users to use plumbing commands, like git rev-list, over porcelain commands, like git log, when scripting. However, git rev-list has one glaring problem that prevents it from being used in certain cases: when --pretty is used with a custom format, it always prints out a line containing "commit" and the object ID. This makes it unsuitable for many scripting needs, and forces users to use git log instead. While we can't change this behavior for backwards compatibility, we can add an option to suppress this behavior, so let's do so, and call it "--no-commit-header". Additionally, add the corresponding positive option to switch it back on. Note that this option doesn't affect the built-in formats, only custom formats. This is exactly the same behavior as users already have from git log and is what most users will be used to. Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

brian m. carlson committed Jul 11, 2021 at 21:55 UTC d1c5ae78ce1260c94c9e626b83dc0901e6843178
4 files changed +112 -12
Documentation/rev-list-options.txt
+8
@@ -1064,6 +1064,14 @@ ifdef::git-rev-list[]
1064 --header::
1065 Print the contents of the commit in raw-format; each record is
1066 separated with a NUL character.
1067 +
1068 +--no-commit-header::
1069 + Suppress the header line containing "commit" and the object ID printed before
1070 + the specified format. This has no effect on the built-in formats; only custom
1071 + formats are affected.
1072 +
1073 +--commit-header::
1074 + Overrides a previous `--no-commit-header`.
1075 endif::git-rev-list[]
1076
1077 --parents::
builtin/rev-list.c
+24 -9
@@ -127,13 +127,15 @@ static void show_commit(struct commit *commit, void *data)
127 if (info->header_prefix)
128 fputs(info->header_prefix, stdout);
129
130 - if (!revs->graph)
131 - fputs(get_revision_mark(revs, commit), stdout);
132 - if (revs->abbrev_commit && revs->abbrev)
133 - fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
134 - stdout);
135 - else
136 - fputs(oid_to_hex(&commit->object.oid), stdout);
130 + if (revs->include_header) {
131 + if (!revs->graph)
132 + fputs(get_revision_mark(revs, commit), stdout);
133 + if (revs->abbrev_commit && revs->abbrev)
134 + fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
135 + stdout);
136 + else
137 + fputs(oid_to_hex(&commit->object.oid), stdout);
138 + }
139 if (revs->print_parents) {
140 struct commit_list *parents = commit->parents;
141 while (parents) {
@@ -153,7 +155,7 @@ static void show_commit(struct commit *commit, void *data)
155 show_decorations(revs, commit);
156 if (revs->commit_format == CMIT_FMT_ONELINE)
157 putchar(' ');
156 - else
158 + else if (revs->include_header)
159 putchar('\n');
160
161 if (revs->verbose_header) {
@@ -512,6 +514,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
514 repo_init_revisions(the_repository, &revs, prefix);
515 revs.abbrev = DEFAULT_ABBREV;
516 revs.commit_format = CMIT_FMT_UNSPECIFIED;
517 + revs.include_header = 1;
518
519 /*
520 * Scan the argument list before invoking setup_revisions(), so that we
@@ -627,6 +630,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
630 continue;
631 }
632
633 + if (!strcmp(arg, ("--commit-header"))) {
634 + revs.include_header = 1;
635 + continue;
636 + }
637 +
638 + if (!strcmp(arg, ("--no-commit-header"))) {
639 + revs.include_header = 0;
640 + continue;
641 + }
642 +
643 if (!strcmp(arg, "--disk-usage")) {
644 show_disk_usage = 1;
645 info.flags |= REV_LIST_QUIET;
@@ -636,10 +649,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
649 usage(rev_list_usage);
650
651 }
652 + if (revs.commit_format != CMIT_FMT_USERFORMAT)
653 + revs.include_header = 1;
654 if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
655 /* The command line has a --pretty */
656 info.hdr_termination = '\n';
642 - if (revs.commit_format == CMIT_FMT_ONELINE)
657 + if (revs.commit_format == CMIT_FMT_ONELINE || !revs.include_header)
658 info.header_prefix = "";
659 else
660 info.header_prefix = "commit ";
revision.h
+2 -1
@@ -215,7 +215,8 @@ struct rev_info {
215 missing_newline:1,
216 date_mode_explicit:1,
217 preserve_subject:1,
218 - encode_email_headers:1;
218 + encode_email_headers:1,
219 + include_header:1;
220 unsigned int disable_stdin:1;
221 /* --show-linear-break */
222 unsigned int track_linear:1,
t/t6006-rev-list-format.sh
+78 -2
@@ -41,22 +41,59 @@ test_expect_success 'setup' '
41 echo "$added_iso88591" | git commit -F - &&
42 head1=$(git rev-parse --verify HEAD) &&
43 head1_short=$(git rev-parse --verify --short $head1) &&
44 + head1_short4=$(git rev-parse --verify --short=4 $head1) &&
45 tree1=$(git rev-parse --verify HEAD:) &&
46 tree1_short=$(git rev-parse --verify --short $tree1) &&
47 echo "$changed" > foo &&
48 echo "$changed_iso88591" | git commit -a -F - &&
49 head2=$(git rev-parse --verify HEAD) &&
50 head2_short=$(git rev-parse --verify --short $head2) &&
51 + head2_short4=$(git rev-parse --verify --short=4 $head2) &&
52 tree2=$(git rev-parse --verify HEAD:) &&
53 tree2_short=$(git rev-parse --verify --short $tree2) &&
54 git config --unset i18n.commitEncoding
55 '
56
55 -# usage: test_format name format_string [failure] <expected_output
57 +# usage: test_format [argument...] name format_string [failure] <expected_output
58 test_format () {
59 + local args=
60 + while true
61 + do
62 + case "$1" in
63 + --*)
64 + args="$args $1"
65 + shift;;
66 + *)
67 + break;;
68 + esac
69 + done
70 cat >expect.$1
71 test_expect_${3:-success} "format $1" "
59 - git rev-list --pretty=format:'$2' main >output.$1 &&
72 + git rev-list $args --pretty=format:'$2' main >output.$1 &&
73 + test_cmp expect.$1 output.$1
74 + "
75 +}
76 +
77 +# usage: test_pretty [argument...] name format_name [failure] <expected_output
78 +test_pretty () {
79 + local args=
80 + while true
81 + do
82 + case "$1" in
83 + --*)
84 + args="$args $1"
85 + shift;;
86 + *)
87 + break;;
88 + esac
89 + done
90 + cat >expect.$1
91 + test_expect_${3:-success} "pretty $1 (without --no-commit-header)" "
92 + git rev-list $args --pretty='$2' main >output.$1 &&
93 + test_cmp expect.$1 output.$1
94 + "
95 + test_expect_${3:-success} "pretty $1 (with --no-commit-header)" "
96 + git rev-list $args --no-commit-header --pretty='$2' main >output.$1 &&
97 test_cmp expect.$1 output.$1
98 "
99 }
@@ -93,6 +130,20 @@ $head1
130 $head1_short
131 EOF
132
133 +test_format --no-commit-header hash-no-header %H%n%h <<EOF
134 +$head2
135 +$head2_short
136 +$head1
137 +$head1_short
138 +EOF
139 +
140 +test_format --abbrev-commit --abbrev=0 --no-commit-header hash-no-header-abbrev %H%n%h <<EOF
141 +$head2
142 +$head2_short4
143 +$head1
144 +$head1_short4
145 +EOF
146 +
147 test_format tree %T%n%t <<EOF
148 commit $head2
149 $tree2
@@ -181,6 +232,31 @@ $added
232
233 EOF
234
235 +test_format --no-commit-header raw-body-no-header %B <<EOF
236 +$changed
237 +
238 +$added
239 +
240 +EOF
241 +
242 +test_pretty oneline oneline <<EOF
243 +$head2 $changed
244 +$head1 $added
245 +EOF
246 +
247 +test_pretty short short <<EOF
248 +commit $head2
249 +Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
250 +
251 + $changed
252 +
253 +commit $head1
254 +Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
255 +
256 + $added
257 +
258 +EOF
259 +
260 test_expect_success 'basic colors' '
261 cat >expect <<-EOF &&
262 commit $head2