merge/pull: add the "--compact-summary" option

"git merge" and "git pull" shows "git diff --stat --summary @{1}" when they finish to indicate the extent of the changes brought into the history by default. While it gives a good overview, it becomes annoying when there are very many created or deleted paths. Introduce "--compact-summary" option to these two commands that tells it to instead show "git diff --compact-summary @{1}", which gives the same information in a lot more compact form in such a situation. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jun 12, 2025 at 15:25 UTC 3a54f5bd5db0478c39a52b4da149e3c2413f46eb
5 files changed +69 -6
Documentation/git-merge.adoc
+1 -1
@@ -9,7 +9,7 @@ git-merge - Join two or more development histories together
9 SYNOPSIS
10 --------
11 [synopsis]
12 -git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
12 +git merge [-n] [--stat] [--compact-summary] [--no-commit] [--squash] [--[no-]edit]
13 [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
14 [--[no-]allow-unrelated-histories]
15 [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>]
Documentation/merge-options.adoc
+3
@@ -113,6 +113,9 @@ include::signoff-option.adoc[]
113 With `-n` or `--no-stat` do not show a diffstat at the end of the
114 merge.
115
116 +`--compact-summary`::
117 + Show a compact-summary at the end of the merge.
118 +
119 `--squash`::
120 `--no-squash`::
121 Produce the working tree and index state as if a real merge
builtin/merge.c
+35 -4
@@ -69,7 +69,10 @@ static const char * const builtin_merge_usage[] = {
69 NULL
70 };
71
72 -static int show_diffstat = 1, shortlog_len = -1, squash;
72 +#define MERGE_SHOW_DIFFSTAT 1
73 +#define MERGE_SHOW_COMPACTSUMMARY 2
74 +
75 +static int show_diffstat = MERGE_SHOW_DIFFSTAT, shortlog_len = -1, squash;
76 static int option_commit = -1;
77 static int option_edit = -1;
78 static int allow_trivial = 1, have_message, verify_signatures;
@@ -243,12 +246,28 @@ static int option_parse_strategy(const struct option *opt UNUSED,
246 return 0;
247 }
248
249 +static int option_parse_compact_summary(const struct option *opt,
250 + const char *name UNUSED, int unset)
251 +{
252 + int *setting = opt->value;
253 +
254 + if (unset)
255 + *setting = 0;
256 + else
257 + *setting = MERGE_SHOW_COMPACTSUMMARY;
258 + return 0;
259 +}
260 +
261 static struct option builtin_merge_options[] = {
262 OPT_SET_INT('n', NULL, &show_diffstat,
263 N_("do not show a diffstat at the end of the merge"), 0),
264 OPT_BOOL(0, "stat", &show_diffstat,
265 N_("show a diffstat at the end of the merge")),
266 OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
267 + OPT_CALLBACK_F(0, "compact-summary", &show_diffstat, N_("compact-summary"),
268 + N_("show a compact-summary at the end of the merge"),
269 + PARSE_OPT_NOARG,
270 + option_parse_compact_summary),
271 {
272 .type = OPTION_INTEGER,
273 .long_name = "log",
@@ -494,8 +513,19 @@ static void finish(struct commit *head_commit,
513 struct diff_options opts;
514 repo_diff_setup(the_repository, &opts);
515 init_diffstat_widths(&opts);
497 - opts.output_format |=
498 - DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
516 +
517 + switch (show_diffstat) {
518 + case MERGE_SHOW_DIFFSTAT: /* 1 */
519 + opts.output_format |=
520 + DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
521 + break;
522 + case MERGE_SHOW_COMPACTSUMMARY: /* 2 */
523 + opts.output_format |= DIFF_FORMAT_DIFFSTAT;
524 + opts.flags.stat_with_summary = 1;
525 + break;
526 + default:
527 + break;
528 + }
529 opts.detect_rename = DIFF_DETECT_RENAME;
530 diff_setup_done(&opts);
531 diff_tree_oid(head, new_head, "", &opts);
@@ -643,7 +673,8 @@ static int git_merge_config(const char *k, const char *v,
673 }
674
675 if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
646 - show_diffstat = git_config_bool(k, v);
676 + show_diffstat = git_config_bool(k, v)
677 + ? MERGE_SHOW_DIFFSTAT : 0;
678 } else if (!strcmp(k, "merge.verifysignatures")) {
679 verify_signatures = git_config_bool(k, v);
680 } else if (!strcmp(k, "pull.twohead")) {
builtin/pull.c
+3
@@ -143,6 +143,9 @@ static struct option pull_options[] = {
143 OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
144 N_("(synonym to --stat)"),
145 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
146 + OPT_PASSTHRU(0, "compact-summary", &opt_diffstat, NULL,
147 + N_("show a compact-summary at the end of the merge"),
148 + PARSE_OPT_NOARG),
149 OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
150 N_("add (at most <n>) entries from shortlog to merge commit message"),
151 PARSE_OPT_OPTARG),
t/t7600-merge.sh
+27 -1
@@ -185,8 +185,19 @@ test_expect_success 'reject non-strategy with a git-merge-foo name' '
185 test_expect_success 'merge c0 with c1' '
186 echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected &&
187
188 + cat >expect <<-\EOF &&
189 + Updating FROM..TO
190 + Fast-forward
191 + file | 2 +-
192 + other | 9 +++++++++
193 + 2 files changed, 10 insertions(+), 1 deletion(-)
194 + create mode 100644 other
195 + EOF
196 +
197 git reset --hard c0 &&
189 - git merge c1 &&
198 + git merge c1 >out &&
199 + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual &&
200 + test_cmp expect actual &&
201 verify_merge file result.1 &&
202 verify_head "$c1" &&
203
@@ -205,6 +216,21 @@ test_expect_success 'merge c0 with c1 with --ff-only' '
216 verify_head "$c1"
217 '
218
219 +test_expect_success 'the same merge with compact summary' '
220 + cat >expect <<-\EOF &&
221 + Updating FROM..TO
222 + Fast-forward
223 + file | 2 +-
224 + other (new) | 9 +++++++++
225 + 2 files changed, 10 insertions(+), 1 deletion(-)
226 + EOF
227 +
228 + git reset --hard c0 &&
229 + git merge --compact-summary c1 >out &&
230 + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual &&
231 + test_cmp expect actual
232 +'
233 +
234 test_debug 'git log --graph --decorate --oneline --all'
235
236 test_expect_success 'merge from unborn branch' '