merge-tree: provide easy access to `ls-files -u` style info

Much like `git merge` updates the index with information of the form (mode, oid, stage, name) provide this output for conflicted files for merge-tree as well. Provide a --name-only option for users to exclude the mode, oid, and stage and only get the list of conflicted filenames. 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 b520bc6caa35e621396dd69ae4d84314615cf7ac
3 files changed +62 -9
Documentation/git-merge-tree.txt
+28 -6
@@ -40,6 +40,13 @@ After the merge completes, a new toplevel tree object is created. See
40 OPTIONS
41 -------
42
43 +--name-only::
44 + In the Conflicted file info section, instead of writing a list
45 + of (mode, oid, stage, path) tuples to output for conflicted
46 + files, just provide a list of filenames with conflicts (and
47 + do not list filenames multiple times if they have multiple
48 + conflicting stages).
49 +
50 --[no-]messages::
51 Write any informational messages such as "Auto-merging <path>"
52 or CONFLICT notices to the end of stdout. If unspecified, the
@@ -58,7 +65,7 @@ line:
65 Whereas for a conflicted merge, the output is by default of the form:
66
67 <OID of toplevel tree>
61 - <Conflicted file list>
68 + <Conflicted file info>
69 <Informational messages>
70
71 These are discussed individually below.
@@ -72,19 +79,24 @@ working tree at the end of `git merge`. If there were conflicts, then
79 files within this tree may have embedded conflict markers.
80
81 [[CFI]]
75 -Conflicted file list
82 +Conflicted file info
83 ~~~~~~~~~~~~~~~~~~~~
84
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]).
85 +This is a sequence of lines with the format
86 +
87 + <mode> <object> <stage> <filename>
88 +
89 +The filename will be quoted as explained for the configuration
90 +variable `core.quotePath` (see linkgit:git-config[1]). However, if
91 +the `--name-only` option is passed, the mode, object, and stage will
92 +be omitted.
93
94 [[IM]]
95 Informational messages
96 ~~~~~~~~~~~~~~~~~~~~~~
97
98 This always starts with a blank line to separate it from the previous
87 -section, and then has free-form messages about the merge, such as:
99 +sections, and then has free-form messages about the merge, such as:
100
101 * "Auto-merging <file>"
102 * "CONFLICT (rename/delete): <oldfile> renamed...but deleted in..."
@@ -116,6 +128,16 @@ used as a part of a series of steps such as:
128 Note that when the exit status is non-zero, `NEWTREE` in this sequence
129 will contain a lot more output than just a tree.
130
131 +For conflicts, the output includes the same information that you'd get
132 +with linkgit:git-merge[1]:
133 +
134 + * what would be written to the working tree (the
135 + <<OIDTLT,OID of toplevel tree>>)
136 + * the higher order stages that would be written to the index (the
137 + <<CFI,Conflicted file info>>)
138 + * any messages that would have been printed to stdout (the
139 + <<IM,Informational messages>>)
140 +
141 [[DEPMERGE]]
142 DEPRECATED DESCRIPTION
143 ----------------------
builtin/merge-tree.c
+10 -1
@@ -400,6 +400,7 @@ enum mode {
400 struct merge_tree_options {
401 int mode;
402 int show_messages;
403 + int name_only;
404 };
405
406 static int real_merge(struct merge_tree_options *o,
@@ -453,7 +454,11 @@ static int real_merge(struct merge_tree_options *o,
454 merge_get_conflicted_files(&result, &conflicted_files);
455 for (i = 0; i < conflicted_files.nr; i++) {
456 const char *name = conflicted_files.items[i].string;
456 - if (last && !strcmp(last, name))
457 + struct stage_info *c = conflicted_files.items[i].util;
458 + if (!o->name_only)
459 + printf("%06o %s %d\t",
460 + c->mode, oid_to_hex(&c->oid), c->stage);
461 + else if (last && !strcmp(last, name))
462 continue;
463 write_name_quoted_relative(
464 name, prefix, stdout, line_termination);
@@ -488,6 +493,10 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
493 N_("do a trivial merge only"), MODE_TRIVIAL),
494 OPT_BOOL(0, "messages", &o.show_messages,
495 N_("also show informational/conflict messages")),
496 + OPT_BOOL_F(0, "name-only",
497 + &o.name_only,
498 + N_("list filenames without modes/oids/stages"),
499 + PARSE_OPT_NONEG),
500 OPT_END()
501 };
502
t/t4301-merge-tree-write-tree.sh
+24 -2
@@ -65,6 +65,7 @@ test_expect_success 'Content merge and a few conflicts' '
65 expected_tree=$(git rev-parse AUTO_MERGE) &&
66
67 # We will redo the merge, while we are still in a conflicted state!
68 + git ls-files -u >conflicted-file-info &&
69 test_when_finished "git reset --hard" &&
70
71 test_expect_code 1 git merge-tree --write-tree side1 side2 >RESULT &&
@@ -108,7 +109,7 @@ anonymize_hash() {
109 }
110
111 test_expect_success 'test conflict notices and such' '
111 - test_expect_code 1 git merge-tree --write-tree side1 side2 >out &&
112 + test_expect_code 1 git merge-tree --write-tree --name-only side1 side2 >out &&
113 anonymize_hash out >actual &&
114
115 # Expected results:
@@ -143,7 +144,7 @@ do
144 done
145
146 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 + test_expect_code 1 git merge-tree --write-tree --no-messages --name-only side1 side2 >out &&
148 anonymize_hash out >actual &&
149
150 test_write_lines HASH greeting whatever~side1 >expect &&
@@ -151,4 +152,25 @@ test_expect_success 'Just the conflicted files without the messages' '
152 test_cmp expect actual
153 '
154
155 +test_expect_success 'Check conflicted oids and modes without messages' '
156 + test_expect_code 1 git merge-tree --write-tree --no-messages side1 side2 >out &&
157 + anonymize_hash out >actual &&
158 +
159 + # Compare the basic output format
160 + q_to_tab >expect <<-\EOF &&
161 + HASH
162 + 100644 HASH 1Qgreeting
163 + 100644 HASH 2Qgreeting
164 + 100644 HASH 3Qgreeting
165 + 100644 HASH 1Qwhatever~side1
166 + 100644 HASH 2Qwhatever~side1
167 + EOF
168 +
169 + test_cmp expect actual &&
170 +
171 + # Check the actual hashes against the `ls-files -u` output too
172 + tail -n +2 out | sed -e s/side1/HEAD/ >actual &&
173 + test_cmp conflicted-file-info actual
174 +'
175 +
176 test_done