merge-ort: implement a very basic collect_merge_info()

This does not actually collect any necessary info other than the pathnames involved, since it just allocates an all-zero conflict_info and stuffs that into paths. However, it invokes the traverse_trees() machinery to walk over all the paths and sets up the basic infrastructure we need. I have left out a few obvious optimizations to try to make this patch as short and obvious as possible. A subsequent patch will add some of those back in with some more useful data fields before we introduce a patch that actually sets up the conflict_info fields. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Dec 13, 2020 at 08:04 UTC d2bc1994f363ac2049da0cdd0b8a7bed61eeaab0
1 file changed +134 -1
merge-ort.c
+134 -1
@@ -23,6 +23,23 @@
23 #include "tree.h"
24 #include "xdiff-interface.h"
25
26 +/*
27 + * We have many arrays of size 3. Whenever we have such an array, the
28 + * indices refer to one of the sides of the three-way merge. This is so
29 + * pervasive that the constants 0, 1, and 2 are used in many places in the
30 + * code (especially in arithmetic operations to find the other side's index
31 + * or to compute a relevant mask), but sometimes these enum names are used
32 + * to aid code clarity.
33 + *
34 + * See also 'filemask' and 'dirmask' in struct conflict_info; the "ith side"
35 + * referred to there is one of these three sides.
36 + */
37 +enum merge_side {
38 + MERGE_BASE = 0,
39 + MERGE_SIDE1 = 1,
40 + MERGE_SIDE2 = 2
41 +};
42 +
43 struct merge_options_internal {
44 /*
45 * paths: primary data structure in all of merge ort.
@@ -184,12 +201,128 @@ static int err(struct merge_options *opt, const char *err, ...)
201 return -1;
202 }
203
204 +static int collect_merge_info_callback(int n,
205 + unsigned long mask,
206 + unsigned long dirmask,
207 + struct name_entry *names,
208 + struct traverse_info *info)
209 +{
210 + /*
211 + * n is 3. Always.
212 + * common ancestor (mbase) has mask 1, and stored in index 0 of names
213 + * head of side 1 (side1) has mask 2, and stored in index 1 of names
214 + * head of side 2 (side2) has mask 4, and stored in index 2 of names
215 + */
216 + struct merge_options *opt = info->data;
217 + struct merge_options_internal *opti = opt->priv;
218 + struct conflict_info *ci;
219 + struct name_entry *p;
220 + size_t len;
221 + char *fullpath;
222 + unsigned filemask = mask & ~dirmask;
223 + unsigned mbase_null = !(mask & 1);
224 + unsigned side1_null = !(mask & 2);
225 + unsigned side2_null = !(mask & 4);
226 +
227 + /* n = 3 is a fundamental assumption. */
228 + if (n != 3)
229 + BUG("Called collect_merge_info_callback wrong");
230 +
231 + /*
232 + * A bunch of sanity checks verifying that traverse_trees() calls
233 + * us the way I expect. Could just remove these at some point,
234 + * though maybe they are helpful to future code readers.
235 + */
236 + assert(mbase_null == is_null_oid(&names[0].oid));
237 + assert(side1_null == is_null_oid(&names[1].oid));
238 + assert(side2_null == is_null_oid(&names[2].oid));
239 + assert(!mbase_null || !side1_null || !side2_null);
240 + assert(mask > 0 && mask < 8);
241 +
242 + /*
243 + * Get the name of the relevant filepath, which we'll pass to
244 + * setup_path_info() for tracking.
245 + */
246 + p = names;
247 + while (!p->mode)
248 + p++;
249 + len = traverse_path_len(info, p->pathlen);
250 +
251 + /* +1 in both of the following lines to include the NUL byte */
252 + fullpath = xmalloc(len + 1);
253 + make_traverse_path(fullpath, len + 1, info, p->path, p->pathlen);
254 +
255 + /*
256 + * TODO: record information about the path other than all zeros,
257 + * so we can resolve later in process_entries.
258 + */
259 + ci = xcalloc(1, sizeof(struct conflict_info));
260 + strmap_put(&opti->paths, fullpath, ci);
261 +
262 + /* If dirmask, recurse into subdirectories */
263 + if (dirmask) {
264 + struct traverse_info newinfo;
265 + struct tree_desc t[3];
266 + void *buf[3] = {NULL, NULL, NULL};
267 + const char *original_dir_name;
268 + int i, ret;
269 +
270 + ci->match_mask &= filemask;
271 + newinfo = *info;
272 + newinfo.prev = info;
273 + newinfo.name = p->path;
274 + newinfo.namelen = p->pathlen;
275 + newinfo.pathlen = st_add3(newinfo.pathlen, p->pathlen, 1);
276 +
277 + for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
278 + const struct object_id *oid = NULL;
279 + if (dirmask & 1)
280 + oid = &names[i].oid;
281 + buf[i] = fill_tree_descriptor(opt->repo, t + i, oid);
282 + dirmask >>= 1;
283 + }
284 +
285 + original_dir_name = opti->current_dir_name;
286 + opti->current_dir_name = fullpath;
287 + ret = traverse_trees(NULL, 3, t, &newinfo);
288 + opti->current_dir_name = original_dir_name;
289 +
290 + for (i = MERGE_BASE; i <= MERGE_SIDE2; i++)
291 + free(buf[i]);
292 +
293 + if (ret < 0)
294 + return -1;
295 + }
296 +
297 + return mask;
298 +}
299 +
300 static int collect_merge_info(struct merge_options *opt,
301 struct tree *merge_base,
302 struct tree *side1,
303 struct tree *side2)
304 {
192 - die("Not yet implemented.");
305 + int ret;
306 + struct tree_desc t[3];
307 + struct traverse_info info;
308 + const char *toplevel_dir_placeholder = "";
309 +
310 + opt->priv->current_dir_name = toplevel_dir_placeholder;
311 + setup_traverse_info(&info, toplevel_dir_placeholder);
312 + info.fn = collect_merge_info_callback;
313 + info.data = opt;
314 + info.show_all_errors = 1;
315 +
316 + parse_tree(merge_base);
317 + parse_tree(side1);
318 + parse_tree(side2);
319 + init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
320 + init_tree_desc(t + 1, side1->buffer, side1->size);
321 + init_tree_desc(t + 2, side2->buffer, side2->size);
322 +
323 + ret = traverse_trees(NULL, 3, t, &info);
324 +
325 + return ret;
326 }
327
328 static int detect_and_process_renames(struct merge_options *opt,