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

[PATCH v2 05/10] merge-ort: flesh out implementation of handle_content_merge()

From
Elijah Newren via GitGitGadget <gitgitgadget@gmail.com>
Date
Jan 1, 2021, 02:34 UTC
Message-ID
<69129a20edcfb57a3278285f31584ffcc2b3bad7.1609468488.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.815.v2.git.1609468488.gitgitgadget@gmail.com>
From: Elijah Newren <newren@gmail.com>

This implementation is based heavily on merge_mode_and_contents() from merge-recursive.c, though it has some fixes for recursive merges (i.e. when call_depth > 0), and has a number of changes throughout based on slight differences in data structures and in how the functions are called.

It is, however, based on two new helper functions -- merge_3way() and merge_submodule -- for which we only provide die-not-implemented stubs at this point. Future commits will add implementations of these functions.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 merge-ort.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 143 insertions(+), 6 deletions(-)
diff --git a/merge-ort.c b/merge-ort.c
index 47e230fe341..2cfb7ffa3b0 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -631,6 +631,28 @@ static int collect_merge_info(struct merge_options *opt,
 
 /*** Function Grouping: functions related to threeway content merges ***/
 
+static int merge_submodule(struct merge_options *opt,
+			   const char *path,
+			   const struct object_id *o,
+			   const struct object_id *a,
+			   const struct object_id *b,
+			   struct object_id *result)
+{
+	die("Not yet implemented.");
+}
+
+static int merge_3way(struct merge_options *opt,
+		      const char *path,
+		      const struct object_id *o,
+		      const struct object_id *a,
+		      const struct object_id *b,
+		      const char *pathnames[3],
+		      const int extra_marker_size,
+		      mmbuffer_t *result_buf)
+{
+	die("Not yet implemented.");
+}
+
 static int handle_content_merge(struct merge_options *opt,
 				const char *path,
 				const struct version_info *o,
@@ -640,14 +662,129 @@ static int handle_content_merge(struct merge_options *opt,
 				const int extra_marker_size,
 				struct version_info *result)
 {
-	int clean = 0;
 	/*
-	 * TODO: Needs a two-way or three-way content merge, but we're
-	 * just being lazy and copying the version from HEAD and
-	 * leaving it as conflicted.
+	 * path is the target location where we want to put the file, and
+	 * is used to determine any normalization rules in ll_merge.
+	 *
+	 * The normal case is that path and all entries in pathnames are
+	 * identical, though renames can affect which path we got one of
+	 * the three blobs to merge on various sides of history.
+	 *
+	 * extra_marker_size is the amount to extend conflict markers in
+	 * ll_merge; this is neeed if we have content merges of content
+	 * merges, which happens for example with rename/rename(2to1) and
+	 * rename/add conflicts.
 	 */
-	result->mode = a->mode;
-	oidcpy(&result->oid, &a->oid);
+	unsigned clean = 1;
+
+	/*
+	 * handle_content_merge() needs both files to be of the same type, i.e.
+	 * both files OR both submodules OR both symlinks.  Conflicting types
+	 * needs to be handled elsewhere.
+	 */
+	assert((S_IFMT & a->mode) == (S_IFMT & b->mode));
+
+	/* Merge modes */
+	if (a->mode == b->mode || a->mode == o->mode)
+		result->mode = b->mode;
+	else {
+		/* must be the 100644/100755 case */
+		assert(S_ISREG(a->mode));
+		result->mode = a->mode;
+		clean = (b->mode == o->mode);
+		/*
+		 * FIXME: If opt->priv->call_depth && !clean, then we really
+		 * should not make result->mode match either a->mode or
+		 * b->mode; that causes t6036 "check conflicting mode for
+		 * regular file" to fail.  It would be best to use some other
+		 * mode, but we'll confuse all kinds of stuff if we use one
+		 * where S_ISREG(result->mode) isn't true, and if we use
+		 * something like 0100666, then tree-walk.c's calls to
+		 * canon_mode() will just normalize that to 100644 for us and
+		 * thus not solve anything.
+		 *
+		 * Figure out if there's some kind of way we can work around
+		 * this...
+		 */
+	}
+
+	/*
+	 * Trivial oid merge.
+	 *
+	 * Note: While one might assume that the next four lines would
+	 * be unnecessary due to the fact that match_mask is often
+	 * setup and already handled, renames don't always take care
+	 * of that.
+	 */
+	if (oideq(&a->oid, &b->oid) || oideq(&a->oid, &o->oid))
+		oidcpy(&result->oid, &b->oid);
+	else if (oideq(&b->oid, &o->oid))
+		oidcpy(&result->oid, &a->oid);
+
+	/* Remaining rules depend on file vs. submodule vs. symlink. */
+	else if (S_ISREG(a->mode)) {
+		mmbuffer_t result_buf;
+		int ret = 0, merge_status;
+		int two_way;
+
+		/*
+		 * If 'o' is different type, treat it as null so we do a
+		 * two-way merge.
+		 */
+		two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
+
+		merge_status = merge_3way(opt, path,
+					  two_way ? &null_oid : &o->oid,
+					  &a->oid, &b->oid,
+					  pathnames, extra_marker_size,
+					  &result_buf);
+
+		if ((merge_status < 0) || !result_buf.ptr)
+			ret = err(opt, _("Failed to execute internal merge"));
+
+		if (!ret &&
+		    write_object_file(result_buf.ptr, result_buf.size,
+				      blob_type, &result->oid))
+			ret = err(opt, _("Unable to add %s to database"),
+				  path);
+
+		free(result_buf.ptr);
+		if (ret)
+			return -1;
+		clean &= (merge_status == 0);
+		path_msg(opt, path, 1, _("Auto-merging %s"), path);
+	} else if (S_ISGITLINK(a->mode)) {
+		int two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
+		clean = merge_submodule(opt, pathnames[0],
+					two_way ? &null_oid : &o->oid,
+					&a->oid, &b->oid, &result->oid);
+		if (opt->priv->call_depth && two_way && !clean) {
+			result->mode = o->mode;
+			oidcpy(&result->oid, &o->oid);
+		}
+	} else if (S_ISLNK(a->mode)) {
+		if (opt->priv->call_depth) {
+			clean = 0;
+			result->mode = o->mode;
+			oidcpy(&result->oid, &o->oid);
+		} else {
+			switch (opt->recursive_variant) {
+			case MERGE_VARIANT_NORMAL:
+				clean = 0;
+				oidcpy(&result->oid, &a->oid);
+				break;
+			case MERGE_VARIANT_OURS:
+				oidcpy(&result->oid, &a->oid);
+				break;
+			case MERGE_VARIANT_THEIRS:
+				oidcpy(&result->oid, &b->oid);
+				break;
+			}
+		}
+	} else
+		BUG("unsupported object type in the tree: %06o for %s",
+		    a->mode, path);
+
 	return clean;
 }
 
-- 
gitgitgadget
Previous: Elijah Newren via GitGitGadgetNext: Ævar Arnfjörð Bjarmason
Message 22 of 37 in “merge-ort: add more handling of basic conflict types”
  1. 00/10 merge-ort: add more handling of basic conflict typesElijah Newren via GitGitGadget, Dec 18, 2020
  2. 01/10 merge-ort: handle D/F conflict where directory disappears due to mergeElijah Newren via GitGitGadget, Dec 18, 2020
  3. Derrick StoleeDec 30, 2020
  4. Elijah NewrenDec 30, 2020
  5. Derrick StoleeDec 31, 2020
  6. Elijah NewrenDec 31, 2020
  7. 03/10 merge-ort: implement unique_path() helperElijah Newren via GitGitGadget, Dec 18, 2020
  8. Derrick StoleeDec 30, 2020
  9. Elijah NewrenDec 30, 2020
  10. Derrick StoleeDec 31, 2020
  11. 04/10 merge-ort: handle book-keeping around two- and three-way content mergeElijah Newren via GitGitGadget, Dec 18, 2020
  12. 02/10 merge-ort: handle directory/file conflicts that remainElijah Newren via GitGitGadget, Dec 18, 2020
  13. 05/10 merge-ort: flesh out implementation of handle_content_merge()Elijah Newren via GitGitGadget, Dec 18, 2020
  14. 07/10 merge-ort: copy and adapt merge_submodule() from merge-recursive.cElijah Newren via GitGitGadget, Dec 18, 2020
  15. 09/10 merge-ort: copy find_first_merges() implementation from merge-recursive.cElijah Newren via GitGitGadget, Dec 18, 2020
  16. 10/10 merge-ort: add handling for different types of files at same pathElijah Newren via GitGitGadget, Dec 18, 2020
  17. 06/10 merge-ort: copy and adapt merge_3way() from merge-recursive.cElijah Newren via GitGitGadget, Dec 18, 2020
  18. 08/10 merge-ort: implement format_commit()Elijah Newren via GitGitGadget, Dec 18, 2020
  19. Elijah NewrenDec 29, 2020
  20. 00/10 merge-ort: add more handling of basic conflict typesElijah Newren via GitGitGadget, Jan 1, 2021
  21. 01/10 merge-ort: handle D/F conflict where directory disappears due to mergeElijah Newren via GitGitGadget, Jan 1, 2021
  22. 05/10 merge-ort: flesh out implementation of handle_content_merge()Elijah Newren via GitGitGadget, Jan 1, 2021
  23. A merge-ort TODO comment, and how to test merge-ort?Ævar Arnfjörð Bjarmason, Mar 4, 2021
  24. Elijah NewrenMar 4, 2021
  25. Ævar Arnfjörð BjarmasonMar 4, 2021
  26. Elijah NewrenMar 4, 2021
  27. Ævar Arnfjörð BjarmasonMar 8, 2021
  28. 03/10 merge-ort: implement unique_path() helperElijah Newren via GitGitGadget, Jan 1, 2021
  29. 04/10 merge-ort: handle book-keeping around two- and three-way content mergeElijah Newren via GitGitGadget, Jan 1, 2021
  30. 07/10 merge-ort: copy and adapt merge_submodule() from merge-recursive.cElijah Newren via GitGitGadget, Jan 1, 2021
  31. 02/10 merge-ort: handle directory/file conflicts that remainElijah Newren via GitGitGadget, Jan 1, 2021
  32. 09/10 merge-ort: copy find_first_merges() implementation from merge-recursive.cElijah Newren via GitGitGadget, Jan 1, 2021
  33. 08/10 merge-ort: implement format_commit()Elijah Newren via GitGitGadget, Jan 1, 2021
  34. 10/10 merge-ort: add handling for different types of files at same pathElijah Newren via GitGitGadget, Jan 1, 2021
  35. 06/10 merge-ort: copy and adapt merge_3way() from merge-recursive.cElijah Newren via GitGitGadget, Jan 1, 2021
  36. Derrick StoleeJan 5, 2021
  37. Elijah NewrenJan 6, 2021

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.