git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Elijah Newren <newren@gmail.com>, Elijah Newren <newren@gmail.com>
Subject: [PATCH 1/2] merge-ort: add a new mergeability_only option
Date: Sat, 10 May 2025 22:02:40 +0000	[thread overview]
Message-ID: <09292804cffc41d15f1156e0b4bba4cba7ec7ca4.1746914561.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1920.git.1746914561.gitgitgadget@gmail.com>

From: Elijah Newren <newren@gmail.com>

Git Forges may be interested in whether two branches can be merged while
not being interested in what the resulting merge tree is nor which files
conflicted.  For such cases, add a new mergeability_only option.  This
option allows the merge machinery to, in the outer layer of the merge:
  * exit upon first conflict
  * avoid writing merged blobs/trees to the object store

Note that since the recursive merge of merge bases (corresponding to
call_depth > 0) can conflict without the outer final merge (corresponding
to call_depth == 0) conflicting, we can't short-circuit nor avoid
writing merges blobs/trees to the object store during those inner
merges.

There is a further potential micro-optimization here.  rename/rename
conflicts have the potential for nested conflicts even without recursive
merges; because of that, handle_content_merge() can be called multiple
times and is done via different paths.  Currently, we only exit early in
process_entries(), which is where the final handle_content_merge() is
invoked.  Since rename/rename conflicts have an additional earlier
handle_content_merge() call that can be invoked from
detect_and_process_renames() (via process_renames()), we could
potentially exit earlier at that call point.  However, rename/rename
conflicts are exceptionally rare, and feeding the extra logic through
didn't seem worth it.  (And, if we don't exit early at that point, then
any resulting blobs need to be written to the store so that subsequent
handle_content_merge() calls trying to use the blob don't throw
exceptions.)

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 merge-ort.c | 38 +++++++++++++++++++++++++++++++-------
 merge-ort.h |  1 +
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/merge-ort.c b/merge-ort.c
index 77310a4a52c9..47b3d1730ece 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -2127,6 +2127,7 @@ static int handle_content_merge(struct merge_options *opt,
 				const struct version_info *b,
 				const char *pathnames[3],
 				const int extra_marker_size,
+				const int record_object,
 				struct version_info *result)
 {
 	/*
@@ -2214,7 +2215,7 @@ static int handle_content_merge(struct merge_options *opt,
 			ret = -1;
 		}
 
-		if (!ret &&
+		if (!ret && record_object &&
 		    write_object_file(result_buf.ptr, result_buf.size,
 				      OBJ_BLOB, &result->oid)) {
 			path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0,
@@ -2897,6 +2898,7 @@ static int process_renames(struct merge_options *opt,
 			struct version_info merged;
 			struct conflict_info *base, *side1, *side2;
 			unsigned was_binary_blob = 0;
+			const int record_object = true;
 
 			pathnames[0] = oldpath;
 			pathnames[1] = newpath;
@@ -2947,6 +2949,7 @@ static int process_renames(struct merge_options *opt,
 							   &side2->stages[2],
 							   pathnames,
 							   1 + 2 * opt->priv->call_depth,
+							   record_object,
 							   &merged);
 			if (clean_merge < 0)
 				return -1;
@@ -3061,6 +3064,7 @@ static int process_renames(struct merge_options *opt,
 
 			struct conflict_info *base, *side1, *side2;
 			int clean;
+			const int record_object = true;
 
 			pathnames[0] = oldpath;
 			pathnames[other_source_index] = oldpath;
@@ -3080,6 +3084,7 @@ static int process_renames(struct merge_options *opt,
 						     &side2->stages[2],
 						     pathnames,
 						     1 + 2 * opt->priv->call_depth,
+						     record_object,
 						     &merged);
 			if (clean < 0)
 				return -1;
@@ -3931,9 +3936,12 @@ static int write_completed_directory(struct merge_options *opt,
 		 * Write out the tree to the git object directory, and also
 		 * record the mode and oid in dir_info->result.
 		 */
+		int record_tree = (!opt->mergeability_only ||
+				   opt->priv->call_depth);
 		dir_info->is_null = 0;
 		dir_info->result.mode = S_IFDIR;
-		if (write_tree(&dir_info->result.oid, &info->versions, offset,
+		if (record_tree &&
+		    write_tree(&dir_info->result.oid, &info->versions, offset,
 			       opt->repo->hash_algo->rawsz) < 0)
 			ret = -1;
 	}
@@ -4231,10 +4239,13 @@ static int process_entry(struct merge_options *opt,
 		struct version_info *o = &ci->stages[0];
 		struct version_info *a = &ci->stages[1];
 		struct version_info *b = &ci->stages[2];
+		int record_object = (!opt->mergeability_only ||
+				     opt->priv->call_depth);
 
 		clean_merge = handle_content_merge(opt, path, o, a, b,
 						   ci->pathnames,
 						   opt->priv->call_depth * 2,
+						   record_object,
 						   &merged_file);
 		if (clean_merge < 0)
 			return -1;
@@ -4395,6 +4406,8 @@ static int process_entries(struct merge_options *opt,
 						   STRING_LIST_INIT_NODUP,
 						   NULL, 0 };
 	int ret = 0;
+	const int record_tree = (!opt->mergeability_only ||
+				 opt->priv->call_depth);
 
 	trace2_region_enter("merge", "process_entries setup", opt->repo);
 	if (strmap_empty(&opt->priv->paths)) {
@@ -4454,6 +4467,12 @@ static int process_entries(struct merge_options *opt,
 				ret = -1;
 				goto cleanup;
 			};
+			if (!ci->merged.clean && opt->mergeability_only &&
+			    !opt->priv->call_depth) {
+				ret = 0;
+				goto cleanup;
+			}
+
 		}
 	}
 	trace2_region_leave("merge", "processing", opt->repo);
@@ -4468,7 +4487,8 @@ static int process_entries(struct merge_options *opt,
 		fflush(stdout);
 		BUG("dir_metadata accounting completely off; shouldn't happen");
 	}
-	if (write_tree(result_oid, &dir_metadata.versions, 0,
+	if (record_tree &&
+	    write_tree(result_oid, &dir_metadata.versions, 0,
 		       opt->repo->hash_algo->rawsz) < 0)
 		ret = -1;
 cleanup:
@@ -4715,6 +4735,8 @@ void merge_display_update_messages(struct merge_options *opt,
 
 	if (opt->record_conflict_msgs_as_headers)
 		BUG("Either display conflict messages or record them as headers, not both");
+	if (opt->mergeability_only)
+		BUG("Displaying conflict messages incompatible with mergeability-only checks");
 
 	trace2_region_enter("merge", "display messages", opt->repo);
 
@@ -5171,10 +5193,12 @@ redo:
 	result->path_messages = &opt->priv->conflicts;
 
 	if (result->clean >= 0) {
-		result->tree = parse_tree_indirect(&working_tree_oid);
-		if (!result->tree)
-			die(_("unable to read tree (%s)"),
-			    oid_to_hex(&working_tree_oid));
+		if (!opt->mergeability_only) {
+			result->tree = parse_tree_indirect(&working_tree_oid);
+			if (!result->tree)
+				die(_("unable to read tree (%s)"),
+				    oid_to_hex(&working_tree_oid));
+		}
 		/* existence of conflicted entries implies unclean */
 		result->clean &= strmap_empty(&opt->priv->conflicted);
 	}
diff --git a/merge-ort.h b/merge-ort.h
index 30750c03962f..6045579825da 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -83,6 +83,7 @@ struct merge_options {
 	/* miscellaneous control options */
 	const char *subtree_shift;
 	unsigned renormalize : 1;
+	unsigned mergeability_only : 1; /* exit early, write fewer objects */
 	unsigned record_conflict_msgs_as_headers : 1;
 	const char *msg_header_prefix;
 
-- 
gitgitgadget


  reply	other threads:[~2025-05-10 22:02 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-10 22:02 [PATCH 0/2] merge-tree: add new --mergeability-only option Elijah Newren via GitGitGadget
2025-05-10 22:02 ` Elijah Newren via GitGitGadget [this message]
2025-05-10 22:02 ` [PATCH 2/2] merge-tree: add a new --mergeability-only flag Elijah Newren via GitGitGadget
2025-05-12 17:04 ` [PATCH 0/2] merge-tree: add new --mergeability-only option Junio C Hamano
2025-05-12 17:41   ` Elijah Newren
2025-05-12 18:27     ` Junio C Hamano
2025-05-12 18:37       ` Elijah Newren
2025-05-12 23:42 ` [PATCH v2 0/2] merge-tree: add new --dry-run option Elijah Newren via GitGitGadget
2025-05-12 23:42   ` [PATCH v2 1/2] merge-ort: add a new mergeability_only option Elijah Newren via GitGitGadget
2025-05-12 23:42   ` [PATCH v2 2/2] merge-tree: add a new --dry-run flag Elijah Newren via GitGitGadget
2025-05-13  7:15     ` Kristoffer Haugsbakk
2025-05-13 15:28       ` Elijah Newren
2025-05-13 13:24     ` Junio C Hamano
2025-05-13 15:30       ` Elijah Newren
2025-05-14 14:08         ` Junio C Hamano
2025-05-14  0:24   ` [PATCH v3 0/2] merge-tree: add new --dry-run option Elijah Newren via GitGitGadget
2025-05-14  0:24     ` [PATCH v3 1/2] merge-ort: add a new mergeability_only option Elijah Newren via GitGitGadget
2025-05-14  0:24     ` [PATCH v3 2/2] merge-tree: add a new --dry-run flag Elijah Newren via GitGitGadget
2025-05-15 13:07       ` Junio C Hamano
2025-05-16 13:18       ` Phillip Wood
2025-05-16 16:03         ` Elijah Newren
2025-05-14 15:34     ` [PATCH v3 0/2] merge-tree: add new --dry-run option Kristoffer Haugsbakk
2025-05-16 20:04     ` [PATCH v4 0/2] merge-tree: add new --quiet option Elijah Newren via GitGitGadget
2025-05-16 20:04       ` [PATCH v4 1/2] merge-ort: add a new mergeability_only option Elijah Newren via GitGitGadget
2025-05-16 20:04       ` [PATCH v4 2/2] merge-tree: add a new --quiet flag Elijah Newren via GitGitGadget
2025-05-17 19:52         ` Kristoffer Haugsbakk
2025-05-17 19:57           ` Kristoffer Haugsbakk
2025-05-19  9:05       ` [PATCH v4 0/2] merge-tree: add new --quiet option Phillip Wood
2025-05-19 15:59         ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=09292804cffc41d15f1156e0b4bba4cba7ec7ca4.1746914561.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=newren@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).