git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 06/20] merge-ort: implement a very basic collect_merge_info()

From
Elijah Newren <newren@gmail.com>
Date
Oct 30, 2020, 03:41 UTC
Message-ID
<20201030034131.1479968-7-newren@gmail.com>
In-Reply-To
<20201030034131.1479968-1-newren@gmail.com>

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>
---
 merge-ort.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 119 insertions(+), 2 deletions(-)
diff --git a/merge-ort.c b/merge-ort.c
index ffea7ac8ea..d652f1f062 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -77,13 +77,130 @@ static int err(struct merge_options *opt, const char *err, ...)
 	return -1;
 }
 
+static int collect_merge_info_callback(int n,
+				       unsigned long mask,
+				       unsigned long dirmask,
+				       struct name_entry *names,
+				       struct traverse_info *info)
+{
+	/*
+	 * n is 3.  Always.
+	 * common ancestor (mbase) has mask 1, and stored in index 0 of names
+	 * head of side 1  (side1) has mask 2, and stored in index 1 of names
+	 * head of side 2  (side2) has mask 4, and stored in index 2 of names
+	 */
+	struct merge_options *opt = info->data;
+	struct merge_options_internal *opti = opt->priv;
+	struct conflict_info *ci;
+	struct name_entry *p;
+	size_t len;
+	char *fullpath;
+	unsigned filemask = mask & ~dirmask;
+	unsigned mbase_null = !(mask & 1);
+	unsigned side1_null = !(mask & 2);
+	unsigned side2_null = !(mask & 4);
+
+	/* n = 3 is a fundamental assumption. */
+	if (n != 3)
+		BUG("Called collect_merge_info_callback wrong");
+
+	/*
+	 * A bunch of sanity checks verifying that traverse_trees() calls
+	 * us the way I expect.  Could just remove these at some point,
+	 * though maybe they are helpful to future code readers.
+	 */
+	assert(mbase_null == is_null_oid(&names[0].oid));
+	assert(side1_null == is_null_oid(&names[1].oid));
+	assert(side2_null == is_null_oid(&names[2].oid));
+	assert(!mbase_null || !side1_null || !side2_null);
+	assert(mask > 0 && mask < 8);
+
+	/* Other invariant checks, mostly for documentation purposes. */
+	assert(mask == (dirmask | filemask));
+
+	/*
+	 * Get the name of the relevant filepath, which we'll pass to
+	 * setup_path_info() for tracking.
+	 */
+	p = names;
+	while (!p->mode)
+		p++;
+	len = traverse_path_len(info, p->pathlen);
+
+	/* +1 in both of the following lines to include the NUL byte */
+	fullpath = xmalloc(len+1);
+	make_traverse_path(fullpath, len+1, info, p->path, p->pathlen);
+
+	/*
+	 * TODO: record information about the path other than all zeros,
+	 * so we can resolve later in process_entries.
+	 */
+	ci = xcalloc(1, sizeof(struct conflict_info));
+	strmap_put(&opti->paths, fullpath, ci);
+
+	/* If dirmask, recurse into subdirectories */
+	if (dirmask) {
+		struct traverse_info newinfo;
+		struct tree_desc t[3];
+		void *buf[3] = {NULL,};
+		const char *original_dir_name;
+		int i, ret;
+
+		ci->match_mask &= filemask;
+		newinfo = *info;
+		newinfo.prev = info;
+		newinfo.name = p->path;
+		newinfo.namelen = p->pathlen;
+		newinfo.pathlen = st_add3(newinfo.pathlen, p->pathlen, 1);
+
+		for (i = 0; i < 3; i++, dirmask >>= 1) {
+			const struct object_id *oid = NULL;
+			if (dirmask & 1)
+				oid = &names[i].oid;
+			buf[i] = fill_tree_descriptor(opt->repo, t + i, oid);
+		}
+
+		original_dir_name = opti->current_dir_name;
+		opti->current_dir_name = fullpath;
+		ret = traverse_trees(NULL, 3, t, &newinfo);
+		opti->current_dir_name = original_dir_name;
+
+		for (i = 0; i < 3; i++)
+			free(buf[i]);
+
+		if (ret < 0)
+			return -1;
+	}
+
+	return mask;
+}
+
 static int collect_merge_info(struct merge_options *opt,
 			      struct tree *merge_base,
 			      struct tree *side1,
 			      struct tree *side2)
 {
-	/* TODO: Implement this using traverse_trees() */
-	die("Not yet implemented.");
+	int ret;
+	struct tree_desc t[3];
+	struct traverse_info info;
+	char *toplevel_dir_placeholder = "";
+
+	opt->priv->current_dir_name = toplevel_dir_placeholder;
+	setup_traverse_info(&info, toplevel_dir_placeholder);
+	info.fn = collect_merge_info_callback;
+	info.data = opt;
+	info.show_all_errors = 1;
+
+	parse_tree(merge_base);
+	parse_tree(side1);
+	parse_tree(side2);
+	init_tree_desc(t+0, merge_base->buffer, merge_base->size);
+	init_tree_desc(t+1, side1->buffer, side1->size);
+	init_tree_desc(t+2, side2->buffer, side2->size);
+
+	ret = traverse_trees(NULL, 3, t, &info);
+
+	return ret;
 }
 
 static int detect_and_process_renames(struct merge_options *opt,
-- 
2.29.1.56.ga287c268e6.dirty
Previous: Elijah NewrenNext: Elijah Newren
Message 6 of 22 in “fundamentals of merge-ort implementation”
  1. 00/20 fundamentals of merge-ort implementationElijah Newren, Oct 30, 2020
  2. 07/20 merge-ort: avoid repeating fill_tree_descriptor() on the same treeElijah Newren, Oct 30, 2020
  3. 04/20 merge-ort: use histogram diffElijah Newren, Oct 30, 2020
  4. 01/20 merge-ort: setup basic internal data structuresElijah Newren, Oct 30, 2020
  5. 02/20 merge-ort: add some high-level algorithm structureElijah Newren, Oct 30, 2020
  6. 06/20 merge-ort: implement a very basic collect_merge_info()Elijah Newren, Oct 30, 2020
  7. 13/20 merge-ort: step 1 of tree writing -- record basenames, modes, and oidsElijah Newren, Oct 30, 2020
  8. 11/20 merge-ort: add a preliminary simple process_entries() implementationElijah Newren, Oct 30, 2020
  9. 12/20 merge-ort: have process_entries operate in a defined orderElijah Newren, Oct 30, 2020
  10. 10/20 merge-ort: avoid recursing into identical treesElijah Newren, Oct 30, 2020
  11. 14/20 merge-ort: step 2 of tree writing -- function to create tree objectElijah Newren, Oct 30, 2020
  12. 15/20 merge-ort: step 3 of tree writing -- handling subdirectories as we goElijah Newren, Oct 30, 2020
  13. 20/20 merge-ort: free data structures in merge_finalize()Elijah Newren, Oct 30, 2020
  14. 19/20 merge-ort: add implementation of record_unmerged_index_entries()Elijah Newren, Oct 30, 2020
  15. 18/20 tree: enable cmp_cache_name_compare() to be used elsewhereElijah Newren, Oct 30, 2020
  16. 17/20 merge-ort: add implementation of checkout()Elijah Newren, Oct 30, 2020
  17. 16/20 merge-ort: basic outline for merge_switch_to_result()Elijah Newren, Oct 30, 2020
  18. 05/20 merge-ort: add an err() function similar to one from merge-recursiveElijah Newren, Oct 30, 2020
  19. 09/20 merge-ort: record stage and auxiliary info for every pathElijah Newren, Oct 30, 2020
  20. 03/20 merge-ort: port merge_start() from merge-recursiveElijah Newren, Oct 30, 2020
  21. 08/20 merge-ort: compute a few more useful fields for collect_merge_infoElijah Newren, Oct 30, 2020
  22. Elijah NewrenOct 30, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.