Skip to content

Commit d2bc199

Browse files
newrengitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0c0d705 commit d2bc199

File tree

1 file changed

+134
-1
lines changed

1 file changed

+134
-1
lines changed

merge-ort.c

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@
2323
#include "tree.h"
2424
#include "xdiff-interface.h"
2525

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+
2643
struct merge_options_internal {
2744
/*
2845
* paths: primary data structure in all of merge ort.
@@ -184,12 +201,128 @@ static int err(struct merge_options *opt, const char *err, ...)
184201
return -1;
185202
}
186203

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+
187300
static int collect_merge_info(struct merge_options *opt,
188301
struct tree *merge_base,
189302
struct tree *side1,
190303
struct tree *side2)
191304
{
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;
193326
}
194327

195328
static int detect_and_process_renames(struct merge_options *opt,

0 commit comments

Comments
 (0)