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,