merge-tree: provide a list of which files have conflicts

Callers of `git merge-tree --write-tree` will often want to know which files had conflicts. While they could potentially attempt to parse the CONFLICT notices printed, those messages are not meant to be machine readable. Provide a simpler mechanism of just printing the files (in the same format as `git ls-files` with quoting, but restricted to unmerged files) in the output before the free-form messages. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jun 18, 2022 at 00:20 UTC 7fa3338870d66dd3946c5c3a0bd09dadb798893d
3 files changed +43 -3
Documentation/git-merge-tree.txt
+9
@@ -58,6 +58,7 @@ line:
58 Whereas for a conflicted merge, the output is by default of the form:
59
60 <OID of toplevel tree>
61 + <Conflicted file list>
62 <Informational messages>
63
64 These are discussed individually below.
@@ -70,6 +71,14 @@ This is a tree object that represents what would be checked out in the
71 working tree at the end of `git merge`. If there were conflicts, then
72 files within this tree may have embedded conflict markers.
73
74 +[[CFI]]
75 +Conflicted file list
76 +~~~~~~~~~~~~~~~~~~~~
77 +
78 +This is a sequence of lines containing a filename on each line, quoted
79 +as explained for the configuration variable `core.quotePath` (see
80 +linkgit:git-config[1]).
81 +
82 [[IM]]
83 Informational messages
84 ~~~~~~~~~~~~~~~~~~~~~~
builtin/merge-tree.c
+23 -3
@@ -11,6 +11,9 @@
11 #include "blob.h"
12 #include "exec-cmd.h"
13 #include "merge-blobs.h"
14 +#include "quote.h"
15 +
16 +static int line_termination = '\n';
17
18 struct merge_list {
19 struct merge_list *next;
@@ -400,7 +403,8 @@ struct merge_tree_options {
403 };
404
405 static int real_merge(struct merge_tree_options *o,
403 - const char *branch1, const char *branch2)
406 + const char *branch1, const char *branch2,
407 + const char *prefix)
408 {
409 struct commit *parent1, *parent2;
410 struct commit_list *merge_bases = NULL;
@@ -441,8 +445,24 @@ static int real_merge(struct merge_tree_options *o,
445 o->show_messages = !result.clean;
446
447 puts(oid_to_hex(&result.tree->object.oid));
448 + if (!result.clean) {
449 + struct string_list conflicted_files = STRING_LIST_INIT_NODUP;
450 + const char *last = NULL;
451 + int i;
452 +
453 + merge_get_conflicted_files(&result, &conflicted_files);
454 + for (i = 0; i < conflicted_files.nr; i++) {
455 + const char *name = conflicted_files.items[i].string;
456 + if (last && !strcmp(last, name))
457 + continue;
458 + write_name_quoted_relative(
459 + name, prefix, stdout, line_termination);
460 + last = name;
461 + }
462 + string_list_clear(&conflicted_files, 1);
463 + }
464 if (o->show_messages) {
445 - printf("\n");
465 + putchar(line_termination);
466 merge_display_update_messages(&opt, &result);
467 }
468 merge_finalize(&opt, &result);
@@ -508,7 +528,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
528
529 /* Do the relevant type of merge */
530 if (o.mode == MODE_REAL)
511 - return real_merge(&o, argv[0], argv[1]);
531 + return real_merge(&o, argv[0], argv[1], prefix);
532 else
533 return trivial_merge(argv[0], argv[1], argv[2]);
534 }
t/t4301-merge-tree-write-tree.sh
+11
@@ -117,6 +117,8 @@ test_expect_success 'test conflict notices and such' '
117 # "whatever" has *both* a modify/delete and a file/directory conflict
118 cat <<-EOF >expect &&
119 HASH
120 + greeting
121 + whatever~side1
122
123 Auto-merging greeting
124 CONFLICT (content): Merge conflict in greeting
@@ -140,4 +142,13 @@ do
142 '
143 done
144
145 +test_expect_success 'Just the conflicted files without the messages' '
146 + test_expect_code 1 git merge-tree --write-tree --no-messages side1 side2 >out &&
147 + anonymize_hash out >actual &&
148 +
149 + test_write_lines HASH greeting whatever~side1 >expect &&
150 +
151 + test_cmp expect actual
152 +'
153 +
154 test_done