show, log: include conflict/warning messages in --remerge-diff headers

Conflicts such as modify/delete, rename/rename, or file/directory are not representable via content conflict markers, and the normal output messages notifying users about these were dropped with --remerge-diff. While we don't want these messages randomly shown before the commit and diff headers, we do want them to still be shown; include them as part of the diff headers instead. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Feb 2, 2022 at 02:37 UTC 20323d104ec389505e83b9376c8ecab94e852fb8
4 files changed +206
log-tree.c
+51
@@ -19,6 +19,7 @@
19 #include "line-log.h"
20 #include "help.h"
21 #include "range-diff.h"
22 +#include "strmap.h"
23
24 static struct decoration name_decoration = { "object names" };
25 static int decoration_loaded;
@@ -907,6 +908,52 @@ static int do_diff_combined(struct rev_info *opt, struct commit *commit)
908 return !opt->loginfo;
909 }
910
911 +static void setup_additional_headers(struct diff_options *o,
912 + struct strmap *all_headers)
913 +{
914 + struct hashmap_iter iter;
915 + struct strmap_entry *entry;
916 +
917 + /*
918 + * Make o->additional_path_headers contain the subset of all_headers
919 + * that match o->pathspec. If there aren't any that match o->pathspec,
920 + * then make o->additional_path_headers be NULL.
921 + */
922 +
923 + if (!o->pathspec.nr) {
924 + o->additional_path_headers = all_headers;
925 + return;
926 + }
927 +
928 + o->additional_path_headers = xmalloc(sizeof(struct strmap));
929 + strmap_init_with_options(o->additional_path_headers, NULL, 0);
930 + strmap_for_each_entry(all_headers, &iter, entry) {
931 + if (match_pathspec(the_repository->index, &o->pathspec,
932 + entry->key, strlen(entry->key),
933 + 0 /* prefix */, NULL /* seen */,
934 + 0 /* is_dir */))
935 + strmap_put(o->additional_path_headers,
936 + entry->key, entry->value);
937 + }
938 + if (!strmap_get_size(o->additional_path_headers)) {
939 + strmap_clear(o->additional_path_headers, 0);
940 + FREE_AND_NULL(o->additional_path_headers);
941 + }
942 +}
943 +
944 +static void cleanup_additional_headers(struct diff_options *o)
945 +{
946 + if (!o->pathspec.nr) {
947 + o->additional_path_headers = NULL;
948 + return;
949 + }
950 + if (!o->additional_path_headers)
951 + return;
952 +
953 + strmap_clear(o->additional_path_headers, 0);
954 + FREE_AND_NULL(o->additional_path_headers);
955 +}
956 +
957 static int do_remerge_diff(struct rev_info *opt,
958 struct commit_list *parents,
959 struct object_id *oid,
@@ -924,6 +971,8 @@ static int do_remerge_diff(struct rev_info *opt,
971 /* Setup merge options */
972 init_merge_options(&o, the_repository);
973 o.show_rename_progress = 0;
974 + o.record_conflict_msgs_as_headers = 1;
975 + o.msg_header_prefix = "remerge";
976
977 ctx.abbrev = DEFAULT_ABBREV;
978 format_commit_message(parent1, "%h (%s)", &parent1_desc, &ctx);
@@ -940,10 +989,12 @@ static int do_remerge_diff(struct rev_info *opt,
989 merge_incore_recursive(&o, bases, parent1, parent2, &res);
990
991 /* Show the diff */
992 + setup_additional_headers(&opt->diffopt, res.path_messages);
993 diff_tree_oid(&res.tree->object.oid, oid, "", &opt->diffopt);
994 log_tree_diff_flush(opt);
995
996 /* Cleanup */
997 + cleanup_additional_headers(&opt->diffopt);
998 strbuf_release(&parent1_desc);
999 strbuf_release(&parent2_desc);
1000 merge_finalize(&o, &res);
merge-ort.c
+1
@@ -4585,6 +4585,7 @@ redo:
4585 trace2_region_leave("merge", "process_entries", opt->repo);
4586
4587 /* Set return values */
4588 + result->path_messages = &opt->priv->output;
4589 result->tree = parse_tree_indirect(&working_tree_oid);
4590 /* existence of conflicted entries implies unclean */
4591 result->clean &= strmap_empty(&opt->priv->conflicted);
merge-ort.h
+10
@@ -5,6 +5,7 @@
5
6 struct commit;
7 struct tree;
8 +struct strmap;
9
10 struct merge_result {
11 /*
@@ -23,6 +24,15 @@ struct merge_result {
24 */
25 struct tree *tree;
26
27 + /*
28 + * Special messages and conflict notices for various paths
29 + *
30 + * This is a map of pathnames to strbufs. It contains various
31 + * warning/conflict/notice messages (possibly multiple per path)
32 + * that callers may want to use.
33 + */
34 + struct strmap *path_messages;
35 +
36 /*
37 * Additional metadata used by merge_switch_to_result() or future calls
38 * to merge_incore_*(). Includes data needed to update the index (if
t/t4069-remerge-diff.sh
+144
@@ -60,6 +60,7 @@ test_expect_success 'remerge-diff with both a resolved conflict and an unrelated
60 git log -1 --oneline ab_resolution >tmp &&
61 cat <<-EOF >>tmp &&
62 diff --git a/numbers b/numbers
63 + remerge CONFLICT (content): Merge conflict in numbers
64 index a1fb731..6875544 100644
65 --- a/numbers
66 +++ b/numbers
@@ -88,4 +89,147 @@ test_expect_success 'remerge-diff with both a resolved conflict and an unrelated
89 test_cmp expect actual
90 '
91
92 +test_expect_success 'setup non-content conflicts' '
93 + git switch --orphan base &&
94 +
95 + test_write_lines 1 2 3 4 5 6 7 8 9 >numbers &&
96 + test_write_lines a b c d e f g h i >letters &&
97 + test_write_lines in the way >content &&
98 + git add numbers letters content &&
99 + git commit -m base &&
100 +
101 + git branch side1 &&
102 + git branch side2 &&
103 +
104 + git checkout side1 &&
105 + test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
106 + git mv letters letters_side1 &&
107 + git mv content file_or_directory &&
108 + git add numbers &&
109 + git commit -m side1 &&
110 +
111 + git checkout side2 &&
112 + git rm numbers &&
113 + git mv letters letters_side2 &&
114 + mkdir file_or_directory &&
115 + echo hello >file_or_directory/world &&
116 + git add file_or_directory/world &&
117 + git commit -m side2 &&
118 +
119 + git checkout -b resolution side1 &&
120 + test_must_fail git merge side2 &&
121 + test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
122 + git add numbers &&
123 + git add letters_side1 &&
124 + git rm letters &&
125 + git rm letters_side2 &&
126 + git add file_or_directory~HEAD &&
127 + git mv file_or_directory~HEAD wanted_content &&
128 + git commit -m resolved
129 +'
130 +
131 +test_expect_success 'remerge-diff with non-content conflicts' '
132 + git log -1 --oneline resolution >tmp &&
133 + cat <<-EOF >>tmp &&
134 + diff --git a/file_or_directory~HASH (side1) b/wanted_content
135 + similarity index 100%
136 + rename from file_or_directory~HASH (side1)
137 + rename to wanted_content
138 + remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
139 + diff --git a/letters b/letters
140 + remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
141 + diff --git a/letters_side2 b/letters_side2
142 + deleted file mode 100644
143 + index b236ae5..0000000
144 + --- a/letters_side2
145 + +++ /dev/null
146 + @@ -1,9 +0,0 @@
147 + -a
148 + -b
149 + -c
150 + -d
151 + -e
152 + -f
153 + -g
154 + -h
155 + -i
156 + diff --git a/numbers b/numbers
157 + remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1). Version HASH (side1) of numbers left in tree.
158 + EOF
159 + # We still have some sha1 hashes above; rip them out so test works
160 + # with sha256
161 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
162 +
163 + git show --oneline --remerge-diff resolution >tmp &&
164 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
165 + test_cmp expect actual
166 +'
167 +
168 +test_expect_success 'remerge-diff w/ diff-filter=U: all conflict headers, no diff content' '
169 + git log -1 --oneline resolution >tmp &&
170 + cat <<-EOF >>tmp &&
171 + diff --git a/file_or_directory~HASH (side1) b/file_or_directory~HASH (side1)
172 + remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
173 + diff --git a/letters b/letters
174 + remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
175 + diff --git a/numbers b/numbers
176 + remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1). Version HASH (side1) of numbers left in tree.
177 + EOF
178 + # We still have some sha1 hashes above; rip them out so test works
179 + # with sha256
180 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
181 +
182 + git show --oneline --remerge-diff --diff-filter=U resolution >tmp &&
183 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
184 + test_cmp expect actual
185 +'
186 +
187 +test_expect_success 'remerge-diff w/ diff-filter=R: relevant file + conflict header' '
188 + git log -1 --oneline resolution >tmp &&
189 + cat <<-EOF >>tmp &&
190 + diff --git a/file_or_directory~HASH (side1) b/wanted_content
191 + similarity index 100%
192 + rename from file_or_directory~HASH (side1)
193 + rename to wanted_content
194 + remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
195 + EOF
196 + # We still have some sha1 hashes above; rip them out so test works
197 + # with sha256
198 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
199 +
200 + git show --oneline --remerge-diff --diff-filter=R resolution >tmp &&
201 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
202 + test_cmp expect actual
203 +'
204 +
205 +test_expect_success 'remerge-diff w/ pathspec: limits to relevant file including conflict header' '
206 + git log -1 --oneline resolution >tmp &&
207 + cat <<-EOF >>tmp &&
208 + diff --git a/letters b/letters
209 + remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
210 + diff --git a/letters_side2 b/letters_side2
211 + deleted file mode 100644
212 + index b236ae5..0000000
213 + --- a/letters_side2
214 + +++ /dev/null
215 + @@ -1,9 +0,0 @@
216 + -a
217 + -b
218 + -c
219 + -d
220 + -e
221 + -f
222 + -g
223 + -h
224 + -i
225 + EOF
226 + # We still have some sha1 hashes above; rip them out so test works
227 + # with sha256
228 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
229 +
230 + git show --oneline --remerge-diff --full-history resolution -- "letters*" >tmp &&
231 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
232 + test_cmp expect actual
233 +'
234 +
235 test_done