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

[PATCH v7 13/17] merge-ort: store more specific conflict information

From
Elijah Newren via GitGitGadget <gitgitgadget@gmail.com>
Date
Jun 18, 2022, 00:20 UTC
Message-ID
<7eb70f77c81bd506c6d6be680961677407bf68df.1655511660.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1122.v7.git.1655511660.gitgitgadget@gmail.com>
From: Elijah Newren <newren@gmail.com>

It is all fine and dandy for a regular Git command that is intended to be run interactively to produce a bunch of messages upon an error.

However, in `merge-ort`'s case, we want to call the command e.g. in server-side software, where the actual error messages are not quite as interesting as machine-readable, immutable terms that describe the exact nature of any given conflict.

With this patch, the `merge-ort` machinery records the exact type (as specified via an `enum` value) as well as the involved path(s) together with the conflict's message.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 merge-ort.c | 267 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 212 insertions(+), 55 deletions(-)
diff --git a/merge-ort.c b/merge-ort.c
index dfec08c88be..432937255f6 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -483,6 +483,100 @@ struct conflict_info {
 	unsigned match_mask:3;
 };
 
+enum conflict_and_info_types {
+	/* "Simple" conflicts and informational messages */
+	INFO_AUTO_MERGING = 0,
+	CONFLICT_CONTENTS,       /* text file that failed to merge */
+	CONFLICT_BINARY,
+	CONFLICT_FILE_DIRECTORY,
+	CONFLICT_DISTINCT_MODES,
+	CONFLICT_MODIFY_DELETE,
+	CONFLICT_PRESENT_DESPITE_SKIPPED,
+
+	/* Regular rename */
+	CONFLICT_RENAME_RENAME,   /* same file renamed differently */
+	CONFLICT_RENAME_COLLIDES, /* rename/add or two files renamed to 1 */
+	CONFLICT_RENAME_DELETE,
+
+	/* Basic directory rename */
+	CONFLICT_DIR_RENAME_SUGGESTED,
+	INFO_DIR_RENAME_APPLIED,
+
+	/* Special directory rename cases */
+	INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME,
+	CONFLICT_DIR_RENAME_FILE_IN_WAY,
+	CONFLICT_DIR_RENAME_COLLISION,
+	CONFLICT_DIR_RENAME_SPLIT,
+
+	/* Basic submodule */
+	INFO_SUBMODULE_FAST_FORWARDING,
+	CONFLICT_SUBMODULE_FAILED_TO_MERGE,
+
+	/* Special submodule cases broken out from FAILED_TO_MERGE */
+	CONFLICT_SUBMODULE_FAILED_TO_MERGE_BUT_POSSIBLE_RESOLUTION,
+	CONFLICT_SUBMODULE_NOT_INITIALIZED,
+	CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
+	CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
+
+	/* Keep this entry _last_ in the list */
+	NB_CONFLICT_TYPES,
+};
+
+/*
+ * Short description of conflict type, relied upon by external tools.
+ *
+ * We can add more entries, but DO NOT change any of these strings.  Also,
+ * Order MUST match conflict_info_and_types.
+ */
+static const char *type_short_descriptions[] = {
+	/*** "Simple" conflicts and informational messages ***/
+	[INFO_AUTO_MERGING] = "Auto-merging",
+	[CONFLICT_CONTENTS] = "CONFLICT (contents)",
+	[CONFLICT_BINARY] = "CONFLICT (binary)",
+	[CONFLICT_FILE_DIRECTORY] = "CONFLICT (file/directory)",
+	[CONFLICT_DISTINCT_MODES] = "CONFLICT (distinct modes)",
+	[CONFLICT_MODIFY_DELETE] = "CONFLICT (modify/delete)",
+	[CONFLICT_PRESENT_DESPITE_SKIPPED] =
+		"CONFLICT (upgrade your version of git)",
+
+	/*** Regular rename ***/
+	[CONFLICT_RENAME_RENAME] = "CONFLICT (rename/rename)",
+	[CONFLICT_RENAME_COLLIDES] = "CONFLICT (rename involved in collision)",
+	[CONFLICT_RENAME_DELETE] = "CONFLICT (rename/delete)",
+
+	/*** Basic directory rename ***/
+	[CONFLICT_DIR_RENAME_SUGGESTED] =
+		"CONFLICT (directory rename suggested)",
+	[INFO_DIR_RENAME_APPLIED] = "Path updated due to directory rename",
+
+	/*** Special directory rename cases ***/
+	[INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME] =
+		"Directory rename skipped since directory was renamed on both sides",
+	[CONFLICT_DIR_RENAME_FILE_IN_WAY] =
+		"CONFLICT (file in way of directory rename)",
+	[CONFLICT_DIR_RENAME_COLLISION] = "CONFLICT(directory rename collision)",
+	[CONFLICT_DIR_RENAME_SPLIT] = "CONFLICT(directory rename unclear split)",
+
+	/*** Basic submodule ***/
+	[INFO_SUBMODULE_FAST_FORWARDING] = "Fast forwarding submodule",
+	[CONFLICT_SUBMODULE_FAILED_TO_MERGE] = "CONFLICT (submodule)",
+
+	/*** Special submodule cases broken out from FAILED_TO_MERGE ***/
+	[CONFLICT_SUBMODULE_FAILED_TO_MERGE_BUT_POSSIBLE_RESOLUTION] =
+		"CONFLICT (submodule with possible resolution)",
+	[CONFLICT_SUBMODULE_NOT_INITIALIZED] =
+		"CONFLICT (submodule not initialized)",
+	[CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE] =
+		"CONFLICT (submodule history not available)",
+	[CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
+		"CONFLICT (submodule may have rewinds)",
+};
+
+struct logical_conflict_info {
+	enum conflict_and_info_types type;
+	struct strvec paths;
+};
+
 /*** Function Grouping: various utility functions ***/
 
 /*
@@ -571,6 +665,11 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
 		/* Release and free each strbuf found in output */
 		strmap_for_each_entry(&opti->conflicts, &iter, e) {
 			struct string_list *list = e->value;
+			for (int i = 0; i < list->nr; i++) {
+				struct logical_conflict_info *info =
+					list->items[i].util;
+				strvec_clear(&info->paths);
+			}
 			/*
 			 * While strictly speaking we don't need to
 			 * free(conflicts) here because we could pass
@@ -629,31 +728,56 @@ static void format_commit(struct strbuf *sb,
 	strbuf_addch(sb, '\n');
 }
 
-__attribute__((format (printf, 4, 5)))
+__attribute__((format (printf, 8, 9)))
 static void path_msg(struct merge_options *opt,
-		     const char *path,
+		     enum conflict_and_info_types type,
 		     int omittable_hint, /* skippable under --remerge-diff */
+		     const char *primary_path,
+		     const char *other_path_1, /* may be NULL */
+		     const char *other_path_2, /* may be NULL */
+		     struct string_list *other_paths, /* may be NULL */
 		     const char *fmt, ...)
 {
 	va_list ap;
 	struct string_list *path_conflicts;
+	struct logical_conflict_info *info;
 	struct strbuf buf = STRBUF_INIT;
 	struct strbuf *dest;
 	struct strbuf tmp = STRBUF_INIT;
 
+	/* Sanity checks */
+	assert(omittable_hint ==
+	       !starts_with(type_short_descriptions[type], "CONFLICT") ||
+	       type == CONFLICT_DIR_RENAME_SUGGESTED ||
+	       type == CONFLICT_PRESENT_DESPITE_SKIPPED);
 	if (opt->record_conflict_msgs_as_headers && omittable_hint)
 		return; /* Do not record mere hints in headers */
 	if (opt->priv->call_depth && opt->verbosity < 5)
 		return; /* Ignore messages from inner merges */
 
 	/* Ensure path_conflicts (ptr to array of logical_conflict) allocated */
-	path_conflicts = strmap_get(&opt->priv->conflicts, path);
+	path_conflicts = strmap_get(&opt->priv->conflicts, primary_path);
 	if (!path_conflicts) {
 		path_conflicts = xmalloc(sizeof(*path_conflicts));
 		string_list_init_dup(path_conflicts);
-		strmap_put(&opt->priv->conflicts, path, path_conflicts);
+		strmap_put(&opt->priv->conflicts, primary_path, path_conflicts);
 	}
 
+	/* Add a logical_conflict at the end to store info from this call */
+	info = xcalloc(1, sizeof(*info));
+	info->type = type;
+	strvec_init(&info->paths);
+
+	/* Handle the list of paths */
+	strvec_push(&info->paths, primary_path);
+	if (other_path_1)
+		strvec_push(&info->paths, other_path_1);
+	if (other_path_2)
+		strvec_push(&info->paths, other_path_2);
+	if (other_paths)
+		for (int i = 0; i < other_paths->nr; i++)
+		strvec_push(&info->paths, other_paths->items[i].string);
+
 	/* Handle message and its format, in normal case */
 	dest = (opt->record_conflict_msgs_as_headers ? &tmp : &buf);
 
@@ -690,7 +814,8 @@ static void path_msg(struct merge_options *opt,
 
 		strbuf_release(&tmp);
 	}
-	string_list_append_nodup(path_conflicts, strbuf_detach(&buf, NULL));
+	string_list_append_nodup(path_conflicts, strbuf_detach(&buf, NULL))
+		->util = info;
 }
 
 static struct diff_filespec *pool_alloc_filespec(struct mem_pool *pool,
@@ -1631,16 +1756,18 @@ static int merge_submodule(struct merge_options *opt,
 		return 0;
 
 	if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) {
-		path_msg(opt, path, 0,
-				_("Failed to merge submodule %s (not checked out)"),
-				path);
+		path_msg(opt, CONFLICT_SUBMODULE_NOT_INITIALIZED, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s (not checked out)"),
+			 path);
 		return 0;
 	}
 
 	if (!(commit_o = lookup_commit_reference(&subrepo, o)) ||
 	    !(commit_a = lookup_commit_reference(&subrepo, a)) ||
 	    !(commit_b = lookup_commit_reference(&subrepo, b))) {
-		path_msg(opt, path, 0,
+		path_msg(opt, CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE, 0,
+			 path, NULL, NULL, NULL,
 			 _("Failed to merge submodule %s (commits not present)"),
 			 path);
 		goto cleanup;
@@ -1649,7 +1776,8 @@ static int merge_submodule(struct merge_options *opt,
 	/* check whether both changes are forward */
 	if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
 	    !repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
-		path_msg(opt, path, 0,
+		path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
+			 path, NULL, NULL, NULL,
 			 _("Failed to merge submodule %s "
 			   "(commits don't follow merge-base)"),
 			 path);
@@ -1659,7 +1787,8 @@ static int merge_submodule(struct merge_options *opt,
 	/* Case #1: a is contained in b or vice versa */
 	if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
 		oidcpy(result, b);
-		path_msg(opt, path, 1,
+		path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
+			 path, NULL, NULL, NULL,
 			 _("Note: Fast-forwarding submodule %s to %s"),
 			 path, oid_to_hex(b));
 		ret = 1;
@@ -1667,7 +1796,8 @@ static int merge_submodule(struct merge_options *opt,
 	}
 	if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
 		oidcpy(result, a);
-		path_msg(opt, path, 1,
+		path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
+			 path, NULL, NULL, NULL,
 			 _("Note: Fast-forwarding submodule %s to %s"),
 			 path, oid_to_hex(a));
 		ret = 1;
@@ -1690,13 +1820,16 @@ static int merge_submodule(struct merge_options *opt,
 					 &merges);
 	switch (parent_count) {
 	case 0:
-		path_msg(opt, path, 0, _("Failed to merge submodule %s"), path);
+		path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s"), path);
 		break;
 
 	case 1:
 		format_commit(&sb, 4, &subrepo,
 			      (struct commit *)merges.objects[0].item);
-		path_msg(opt, path, 0,
+		path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE_BUT_POSSIBLE_RESOLUTION, 0,
+			 path, NULL, NULL, NULL,
 			 _("Failed to merge submodule %s, but a possible merge "
 			   "resolution exists: %s"),
 			 path, sb.buf);
@@ -1706,7 +1839,8 @@ static int merge_submodule(struct merge_options *opt,
 		for (i = 0; i < merges.nr; i++)
 			format_commit(&sb, 4, &subrepo,
 				      (struct commit *)merges.objects[i].item);
-		path_msg(opt, path, 0,
+		path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE_BUT_POSSIBLE_RESOLUTION, 0,
+			 path, NULL, NULL, NULL,
 			 _("Failed to merge submodule %s, but multiple "
 			   "possible merges exist:\n%s"), path, sb.buf);
 		strbuf_release(&sb);
@@ -1832,7 +1966,8 @@ static int merge_3way(struct merge_options *opt,
 				&src1, name1, &src2, name2,
 				&opt->priv->attr_index, &ll_opts);
 	if (merge_status == LL_MERGE_BINARY_CONFLICT)
-		path_msg(opt, path, 0,
+		path_msg(opt, CONFLICT_BINARY, 0,
+			 path, NULL, NULL, NULL,
 			 "warning: Cannot merge binary files: %s (%s vs. %s)",
 			 path, name1, name2);
 
@@ -1944,7 +2079,8 @@ static int handle_content_merge(struct merge_options *opt,
 		if (ret)
 			return -1;
 		clean &= (merge_status == 0);
-		path_msg(opt, path, 1, _("Auto-merging %s"), path);
+		path_msg(opt, INFO_AUTO_MERGING, 1, path, NULL, NULL, NULL,
+			 _("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],
@@ -2082,21 +2218,24 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
 		c_info->reported_already = 1;
 		strbuf_add_separated_string_list(&collision_paths, ", ",
 						 &c_info->source_files);
-		path_msg(opt, new_path, 0,
-			 _("CONFLICT (implicit dir rename): Existing file/dir "
-			   "at %s in the way of implicit directory rename(s) "
-			   "putting the following path(s) there: %s."),
-		       new_path, collision_paths.buf);
+		path_msg(opt, CONFLICT_DIR_RENAME_FILE_IN_WAY, 0,
+			 new_path, NULL, NULL, &c_info->source_files,
+			 _("CONFLICT (implicit dir rename): Existing "
+			   "file/dir at %s in the way of implicit "
+			   "directory rename(s) putting the following "
+			   "path(s) there: %s."),
+			 new_path, collision_paths.buf);
 		clean = 0;
 	} else if (c_info->source_files.nr > 1) {
 		c_info->reported_already = 1;
 		strbuf_add_separated_string_list(&collision_paths, ", ",
 						 &c_info->source_files);
-		path_msg(opt, new_path, 0,
-			 _("CONFLICT (implicit dir rename): Cannot map more "
-			   "than one path to %s; implicit directory renames "
-			   "tried to put these paths there: %s"),
-		       new_path, collision_paths.buf);
+		path_msg(opt, CONFLICT_DIR_RENAME_COLLISION, 0,
+			 new_path, NULL, NULL, &c_info->source_files,
+			 _("CONFLICT (implicit dir rename): Cannot map "
+			   "more than one path to %s; implicit directory "
+			   "renames tried to put these paths there: %s"),
+			 new_path, collision_paths.buf);
 		clean = 0;
 	}
 
@@ -2150,13 +2289,14 @@ static void get_provisional_directory_renames(struct merge_options *opt,
 			continue;
 
 		if (bad_max == max) {
-			path_msg(opt, source_dir, 0,
-			       _("CONFLICT (directory rename split): "
-				 "Unclear where to rename %s to; it was "
-				 "renamed to multiple other directories, with "
-				 "no destination getting a majority of the "
-				 "files."),
-			       source_dir);
+			path_msg(opt, CONFLICT_DIR_RENAME_SPLIT, 0,
+				 source_dir, NULL, NULL, NULL,
+				 _("CONFLICT (directory rename split): "
+				   "Unclear where to rename %s to; it was "
+				   "renamed to multiple other directories, "
+				   "with no destination getting a majority of "
+				   "the files."),
+				 source_dir);
 			*clean = 0;
 		} else {
 			strmap_put(&renames->dir_renames[side],
@@ -2304,7 +2444,8 @@ static char *check_for_directory_rename(struct merge_options *opt,
 	 */
 	otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir);
 	if (otherinfo) {
-		path_msg(opt, rename_info->key, 1,
+		path_msg(opt, INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME, 1,
+			 rename_info->key, path, new_dir, NULL,
 			 _("WARNING: Avoiding applying %s -> %s rename "
 			   "to %s, because %s itself was renamed."),
 			 rename_info->key, new_dir, path, new_dir);
@@ -2444,14 +2585,16 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
 	if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE) {
 		/* Notify user of updated path */
 		if (pair->status == 'A')
-			path_msg(opt, new_path, 1,
+			path_msg(opt, INFO_DIR_RENAME_APPLIED, 1,
+				 new_path, old_path, NULL, NULL,
 				 _("Path updated: %s added in %s inside a "
 				   "directory that was renamed in %s; moving "
 				   "it to %s."),
 				 old_path, branch_with_new_path,
 				 branch_with_dir_rename, new_path);
 		else
-			path_msg(opt, new_path, 1,
+			path_msg(opt, INFO_DIR_RENAME_APPLIED, 1,
+				 new_path, old_path, NULL, NULL,
 				 _("Path updated: %s renamed to %s in %s, "
 				   "inside a directory that was renamed in %s; "
 				   "moving it to %s."),
@@ -2464,7 +2607,8 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
 		 */
 		ci->path_conflict = 1;
 		if (pair->status == 'A')
-			path_msg(opt, new_path, 1,
+			path_msg(opt, CONFLICT_DIR_RENAME_SUGGESTED, 1,
+				 new_path, old_path, NULL, NULL,
 				 _("CONFLICT (file location): %s added in %s "
 				   "inside a directory that was renamed in %s, "
 				   "suggesting it should perhaps be moved to "
@@ -2472,7 +2616,8 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
 				 old_path, branch_with_new_path,
 				 branch_with_dir_rename, new_path);
 		else
-			path_msg(opt, new_path, 1,
+			path_msg(opt, CONFLICT_DIR_RENAME_SUGGESTED, 1,
+				 new_path, old_path, NULL, NULL,
 				 _("CONFLICT (file location): %s renamed to %s "
 				   "in %s, inside a directory that was renamed "
 				   "in %s, suggesting it should perhaps be "
@@ -2628,7 +2773,8 @@ static int process_renames(struct merge_options *opt,
 			 * and remove the setting of base->path_conflict to 1.
 			 */
 			base->path_conflict = 1;
-			path_msg(opt, oldpath, 0,
+			path_msg(opt, CONFLICT_RENAME_RENAME, 0,
+				 pathnames[0], pathnames[1], pathnames[2], NULL,
 				 _("CONFLICT (rename/rename): %s renamed to "
 				   "%s in %s and to %s in %s."),
 				 pathnames[0],
@@ -2723,7 +2869,8 @@ static int process_renames(struct merge_options *opt,
 			memcpy(&newinfo->stages[target_index], &merged,
 			       sizeof(merged));
 			if (!clean) {
-				path_msg(opt, newpath, 0,
+				path_msg(opt, CONFLICT_RENAME_COLLIDES, 0,
+					 newpath, oldpath, NULL, NULL,
 					 _("CONFLICT (rename involved in "
 					   "collision): rename of %s -> %s has "
 					   "content conflicts AND collides "
@@ -2742,7 +2889,8 @@ static int process_renames(struct merge_options *opt,
 			 */
 
 			newinfo->path_conflict = 1;
-			path_msg(opt, newpath, 0,
+			path_msg(opt, CONFLICT_RENAME_DELETE, 0,
+				 newpath, oldpath, NULL, NULL,
 				 _("CONFLICT (rename/delete): %s renamed "
 				   "to %s in %s, but deleted in %s."),
 				 oldpath, newpath, rename_branch, delete_branch);
@@ -2766,7 +2914,8 @@ static int process_renames(struct merge_options *opt,
 			} else if (source_deleted) {
 				/* rename/delete */
 				newinfo->path_conflict = 1;
-				path_msg(opt, newpath, 0,
+				path_msg(opt, CONFLICT_RENAME_DELETE, 0,
+					 newpath, oldpath, NULL, NULL,
 					 _("CONFLICT (rename/delete): %s renamed"
 					   " to %s in %s, but deleted in %s."),
 					 oldpath, newpath,
@@ -3687,7 +3836,8 @@ static void process_entry(struct merge_options *opt,
 		path = unique_path(opt, path, branch);
 		strmap_put(&opt->priv->paths, path, new_ci);
 
-		path_msg(opt, path, 0,
+		path_msg(opt, CONFLICT_FILE_DIRECTORY, 0,
+			 path, old_path, NULL, NULL,
 			 _("CONFLICT (file/directory): directory in the way "
 			   "of %s from %s; moving it to %s instead."),
 			 old_path, branch, path);
@@ -3763,15 +3913,23 @@ static void process_entry(struct merge_options *opt,
 				rename_b = 1;
 			}
 
+			if (rename_a)
+				a_path = unique_path(opt, path, opt->branch1);
+			if (rename_b)
+				b_path = unique_path(opt, path, opt->branch2);
+
 			if (rename_a && rename_b) {
-				path_msg(opt, path, 0,
+				path_msg(opt, CONFLICT_DISTINCT_MODES, 0,
+					 path, a_path, b_path, NULL,
 					 _("CONFLICT (distinct types): %s had "
 					   "different types on each side; "
 					   "renamed both of them so each can "
 					   "be recorded somewhere."),
 					 path);
 			} else {
-				path_msg(opt, path, 0,
+				path_msg(opt, CONFLICT_DISTINCT_MODES, 0,
+					 path, rename_a ? a_path : b_path,
+					 NULL, NULL,
 					 _("CONFLICT (distinct types): %s had "
 					   "different types on each side; "
 					   "renamed one of them so each can be "
@@ -3808,14 +3966,10 @@ static void process_entry(struct merge_options *opt,
 
 			/* Insert entries into opt->priv_paths */
 			assert(rename_a || rename_b);
-			if (rename_a) {
-				a_path = unique_path(opt, path, opt->branch1);
+			if (rename_a)
 				strmap_put(&opt->priv->paths, a_path, ci);
-			}
 
-			if (rename_b)
-				b_path = unique_path(opt, path, opt->branch2);
-			else
+			if (!rename_b)
 				b_path = path;
 			strmap_put(&opt->priv->paths, b_path, new_ci);
 
@@ -3866,7 +4020,8 @@ static void process_entry(struct merge_options *opt,
 				reason = _("add/add");
 			if (S_ISGITLINK(merged_file.mode))
 				reason = _("submodule");
-			path_msg(opt, path, 0,
+			path_msg(opt, CONFLICT_CONTENTS, 0,
+				 path, NULL, NULL, NULL,
 				 _("CONFLICT (%s): Merge conflict in %s"),
 				 reason, path);
 		}
@@ -3910,7 +4065,8 @@ static void process_entry(struct merge_options *opt,
 			 * since the contents were not modified.
 			 */
 		} else {
-			path_msg(opt, path, 0,
+			path_msg(opt, CONFLICT_MODIFY_DELETE, 0,
+				 path, NULL, NULL, NULL,
 				 _("CONFLICT (modify/delete): %s deleted in %s "
 				   "and modified in %s.  Version %s of %s left "
 				   "in tree."),
@@ -4206,7 +4362,8 @@ static int record_conflicted_index_entries(struct merge_options *opt)
 								     path,
 								     "cruft");
 
-					path_msg(opt, path, 1,
+					path_msg(opt, CONFLICT_PRESENT_DESPITE_SKIPPED, 1,
+						 path, NULL, NULL, NULL,
 						 _("Note: %s not up to date and in way of checking out conflicted version; old copy renamed to %s"),
 						 path, new_name);
 					errs |= rename(path, new_name);
-- 
gitgitgadget
Previous: Elijah Newren via GitGitGadgetNext: Johannes Schindelin via GitGitGadget
Message 237 of 240 in “RFC: In-core git merge-tree ("Server side merges")”
  1. 00/12 RFC: In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Jan 22, 2022
  2. 01/12 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Jan 22, 2022
  3. 02/12 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Jan 22, 2022
  4. 03/12 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Jan 22, 2022
  5. René ScharfeJan 23, 2022
  6. Elijah NewrenJan 24, 2022
  7. Ævar Arnfjörð BjarmasonJan 24, 2022
  8. Elijah NewrenJan 24, 2022
  9. 04/12 merge-tree: implement real mergesElijah Newren via GitGitGadget, Jan 22, 2022
  10. Ævar Arnfjörð BjarmasonJan 24, 2022
  11. Elijah NewrenJan 24, 2022
  12. Johannes SchindelinJan 25, 2022
  13. Christian CouderJan 26, 2022
  14. Elijah NewrenJan 29, 2022
  15. 05/12 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Jan 22, 2022
  16. Ævar Arnfjörð BjarmasonJan 24, 2022
  17. Elijah NewrenJan 25, 2022
  18. Johannes SchindelinJan 28, 2022
  19. 07/12 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Jan 22, 2022
  20. Christian CouderJan 26, 2022
  21. Elijah NewrenJan 29, 2022
  22. Johannes SchindelinJan 28, 2022
  23. Elijah NewrenJan 29, 2022
  24. 06/12 merge-ort: allow update messages to be written to different file streamElijah Newren via GitGitGadget, Jan 22, 2022
  25. Johannes SchindelinJan 28, 2022
  26. Elijah NewrenJan 29, 2022
  27. 10/12 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Jan 22, 2022
  28. Ævar Arnfjörð BjarmasonJan 24, 2022
  29. Elijah NewrenJan 24, 2022
  30. 08/12 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Jan 22, 2022
  31. Christian CouderJan 26, 2022
  32. Elijah NewrenJan 29, 2022
  33. Christian CouderJan 26, 2022
  34. Elijah NewrenJan 29, 2022
  35. Johannes SchindelinJan 28, 2022
  36. Elijah NewrenJan 29, 2022
  37. Johannes SixtJan 29, 2022
  38. Elijah NewrenJan 29, 2022
  39. Johannes SchindelinFeb 4, 2022
  40. Elijah NewrenFeb 5, 2022
  41. Johannes SchindelinFeb 21, 2022
  42. Ævar Arnfjörð BjarmasonFeb 21, 2022
  43. machine-parsable git-merge-tree messages (was: [PATCH 08/12] merge-ort: provide a merge_get_conflicted_files() helper function)Ævar Arnfjörð Bjarmason, Feb 21, 2022
  44. Elijah NewrenFeb 23, 2022
  45. Ævar Arnfjörð BjarmasonFeb 28, 2022
  46. Elijah NewrenMar 1, 2022
  47. Johannes SchindelinFeb 22, 2022
  48. Elijah NewrenFeb 23, 2022
  49. Johannes SchindelinFeb 25, 2022
  50. Elijah NewrenFeb 23, 2022
  51. Johannes SchindelinFeb 25, 2022
  52. Junio C HamanoFeb 25, 2022
  53. Elijah NewrenFeb 26, 2022
  54. Johannes SchindelinMar 7, 2022
  55. Elijah NewrenMar 8, 2022
  56. Johannes SchindelinMar 10, 2022
  57. Johannes SchindelinMay 13, 2022
  58. Elijah NewrenMay 17, 2022
  59. Johannes SchindelinJun 3, 2022
  60. Johannes SchindelinJun 5, 2022
  61. Johannes SchindelinJun 5, 2022
  62. Johannes SchindelinJun 6, 2022
  63. Elijah NewrenJun 7, 2022
  64. Elijah NewrenJun 17, 2022
  65. Johannes SchindelinJun 18, 2022
  66. 11/12 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Jan 22, 2022
  67. 12/12 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Jan 22, 2022
  68. 09/12 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Jan 22, 2022
  69. Ævar Arnfjörð BjarmasonJan 24, 2022
  70. Elijah NewrenJan 24, 2022
  71. Johannes SchindelinJan 28, 2022
  72. Elijah NewrenJan 29, 2022
  73. Johannes SchindelinFeb 4, 2022
  74. Christian CouderJan 26, 2022
  75. Johannes SchindelinJan 26, 2022
  76. Christian CouderJan 26, 2022
  77. Johannes SchindelinJan 28, 2022
  78. Christian CouderJan 28, 2022
  79. Johannes SchindelinJan 28, 2022
  80. Elijah NewrenJan 29, 2022
  81. Christian CouderJan 29, 2022
  82. Elijah NewrenJan 29, 2022
  83. Elijah NewrenJan 31, 2022
  84. Johannes SchindelinJan 28, 2022
  85. 00/13 In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Jan 29, 2022
  86. 01/13 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Jan 29, 2022
  87. 02/13 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Jan 29, 2022
  88. 03/13 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Jan 29, 2022
  89. Junio C HamanoFeb 2, 2022
  90. 04/13 merge-tree: implement real mergesElijah Newren via GitGitGadget, Jan 29, 2022
  91. Junio C HamanoFeb 2, 2022
  92. Elijah NewrenFeb 2, 2022
  93. Johannes SchindelinFeb 21, 2022
  94. 05/13 diff: allow diff_warn_rename_limit to write somewhere besides stdoutJohannes Schindelin via GitGitGadget, Jan 29, 2022
  95. 06/13 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Jan 29, 2022
  96. 07/13 merge-ort: allow update messages to be written to different file streamElijah Newren via GitGitGadget, Jan 29, 2022
  97. 11/13 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Jan 29, 2022
  98. Junio C HamanoFeb 2, 2022
  99. Elijah NewrenFeb 2, 2022
  100. Ævar Arnfjörð BjarmasonFeb 3, 2022
  101. Elijah NewrenFeb 3, 2022
  102. 10/13 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Jan 29, 2022
  103. Junio C HamanoFeb 2, 2022
  104. Junio C HamanoFeb 2, 2022
  105. Junio C HamanoFeb 3, 2022
  106. 09/13 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Jan 29, 2022
  107. 08/13 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Jan 29, 2022
  108. Junio C HamanoFeb 2, 2022
  109. Elijah NewrenFeb 2, 2022
  110. 13/13 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Jan 29, 2022
  111. 12/13 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Jan 29, 2022
  112. Junio C HamanoFeb 2, 2022
  113. 00/15 In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Feb 2, 2022
  114. 01/15 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Feb 2, 2022
  115. 02/15 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Feb 2, 2022
  116. 03/15 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Feb 2, 2022
  117. Ævar Arnfjörð BjarmasonFeb 3, 2022
  118. Elijah NewrenFeb 3, 2022
  119. Elijah NewrenFeb 3, 2022
  120. Ævar Arnfjörð BjarmasonFeb 3, 2022
  121. Elijah NewrenFeb 3, 2022
  122. Ævar Arnfjörð BjarmasonFeb 3, 2022
  123. Elijah NewrenFeb 3, 2022
  124. Ævar Arnfjörð BjarmasonFeb 3, 2022
  125. Emily ShafferFeb 7, 2022
  126. Junio C HamanoFeb 7, 2022
  127. 04/15 merge-tree: implement real mergesElijah Newren via GitGitGadget, Feb 2, 2022
  128. Junio C HamanoFeb 2, 2022
  129. Elijah NewrenFeb 2, 2022
  130. Junio C HamanoFeb 2, 2022
  131. Elijah NewrenFeb 3, 2022
  132. Johannes AltmanningerFeb 3, 2022
  133. Elijah NewrenFeb 3, 2022
  134. Johannes SchindelinFeb 21, 2022
  135. Elijah NewrenFeb 22, 2022
  136. Junio C HamanoFeb 3, 2022
  137. Junio C HamanoFeb 21, 2022
  138. Elijah NewrenFeb 22, 2022
  139. Junio C HamanoFeb 23, 2022
  140. Elijah NewrenFeb 24, 2022
  141. Junio C HamanoFeb 24, 2022
  142. Junio C HamanoFeb 24, 2022
  143. Johannes AltmanningerFeb 27, 2022
  144. Johannes AltmanningerFeb 27, 2022
  145. Johannes SchindelinFeb 22, 2022
  146. Josh SteadmonFeb 4, 2022
  147. Elijah NewrenFeb 4, 2022
  148. 05/15 Introduce a variant of the `warning()` function that takes a `FILE *`Johannes Schindelin via GitGitGadget, Feb 2, 2022
  149. 06/15 diff: allow diff_warn_rename_limit to write somewhere besides stderrJohannes Schindelin via GitGitGadget, Feb 2, 2022
  150. 07/15 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Feb 2, 2022
  151. 08/15 merge-ort: allow update messages to be written to different file streamElijah Newren via GitGitGadget, Feb 2, 2022
  152. Ævar Arnfjörð BjarmasonFeb 3, 2022
  153. Elijah NewrenFeb 3, 2022
  154. Ævar Arnfjörð BjarmasonFeb 3, 2022
  155. Elijah NewrenFeb 3, 2022
  156. Ævar Arnfjörð BjarmasonFeb 3, 2022
  157. Elijah NewrenFeb 3, 2022
  158. Johannes SchindelinFeb 21, 2022
  159. Elijah NewrenFeb 22, 2022
  160. Johannes SchindelinFeb 22, 2022
  161. 09/15 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Feb 2, 2022
  162. 13/15 merge-tree: allow `ls-files -u` style info to be NUL terminatedElijah Newren via GitGitGadget, Feb 2, 2022
  163. 14/15 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Feb 2, 2022
  164. 15/15 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Feb 2, 2022
  165. 10/15 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Feb 2, 2022
  166. 11/15 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Feb 2, 2022
  167. 12/15 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Feb 2, 2022
  168. Ævar Arnfjörð BjarmasonFeb 2, 2022
  169. Elijah NewrenFeb 3, 2022
  170. 00/12 In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Feb 12, 2022
  171. 01/12 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Feb 12, 2022
  172. 02/12 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Feb 12, 2022
  173. 03/12 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Feb 12, 2022
  174. 04/12 merge-tree: implement real mergesElijah Newren via GitGitGadget, Feb 12, 2022
  175. Junio C HamanoFeb 14, 2022
  176. Elijah NewrenFeb 15, 2022
  177. Ævar Arnfjörð BjarmasonFeb 15, 2022
  178. 06/12 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Feb 12, 2022
  179. 05/12 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Feb 12, 2022
  180. 07/12 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Feb 12, 2022
  181. 08/12 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Feb 12, 2022
  182. 09/12 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Feb 12, 2022
  183. 12/12 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Feb 12, 2022
  184. 10/12 merge-tree: allow `ls-files -u` style info to be NUL terminatedElijah Newren via GitGitGadget, Feb 12, 2022
  185. 11/12 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Feb 12, 2022
  186. 00/12 In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Feb 20, 2022
  187. 01/12 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Feb 20, 2022
  188. 02/12 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Feb 20, 2022
  189. 04/12 merge-tree: implement real mergesElijah Newren via GitGitGadget, Feb 20, 2022
  190. René ScharfeFeb 20, 2022
  191. Johannes SchindelinFeb 21, 2022
  192. Elijah NewrenFeb 22, 2022
  193. Johannes SchindelinFeb 22, 2022
  194. 03/12 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Feb 20, 2022
  195. 05/12 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Feb 20, 2022
  196. 08/12 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Feb 20, 2022
  197. 06/12 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Feb 20, 2022
  198. 07/12 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Feb 20, 2022
  199. 11/12 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Feb 20, 2022
  200. 12/12 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Feb 20, 2022
  201. Johannes SchindelinFeb 22, 2022
  202. 10/12 merge-tree: allow `ls-files -u` style info to be NUL terminatedElijah Newren via GitGitGadget, Feb 20, 2022
  203. 09/12 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Feb 20, 2022
  204. Ævar Arnfjörð BjarmasonFeb 20, 2022
  205. Johannes SchindelinFeb 21, 2022
  206. Elijah NewrenFeb 22, 2022
  207. Ævar Arnfjörð BjarmasonFeb 22, 2022
  208. 00/12 In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Feb 23, 2022
  209. 01/12 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Feb 23, 2022
  210. 02/12 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Feb 23, 2022
  211. 03/12 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Feb 23, 2022
  212. 04/12 merge-tree: implement real mergesElijah Newren via GitGitGadget, Feb 23, 2022
  213. 05/12 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Feb 23, 2022
  214. 07/12 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Feb 23, 2022
  215. 06/12 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Feb 23, 2022
  216. 08/12 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Feb 23, 2022
  217. 10/12 merge-tree: allow `ls-files -u` style info to be NUL terminatedElijah Newren via GitGitGadget, Feb 23, 2022
  218. 11/12 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Feb 23, 2022
  219. 09/12 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Feb 23, 2022
  220. 12/12 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Feb 23, 2022
  221. Junio C HamanoFeb 23, 2022
  222. 00/17 In-core git merge-tree ("Server side merges")Elijah Newren via GitGitGadget, Jun 18, 2022
  223. 01/17 merge-tree: rename merge_trees() to trivial_merge_trees()Elijah Newren via GitGitGadget, Jun 18, 2022
  224. 02/17 merge-tree: move logic for existing merge into new functionElijah Newren via GitGitGadget, Jun 18, 2022
  225. 03/17 merge-tree: add option parsing and initial shell for real merge functionElijah Newren via GitGitGadget, Jun 18, 2022
  226. 05/17 merge-ort: split out a separate display_update_messages() functionElijah Newren via GitGitGadget, Jun 18, 2022
  227. 04/17 merge-tree: implement real mergesElijah Newren via GitGitGadget, Jun 18, 2022
  228. 06/17 merge-tree: support including merge messages in outputElijah Newren via GitGitGadget, Jun 18, 2022
  229. 07/17 merge-ort: provide a merge_get_conflicted_files() helper functionElijah Newren via GitGitGadget, Jun 18, 2022
  230. 09/17 merge-tree: provide a list of which files have conflictsElijah Newren via GitGitGadget, Jun 18, 2022
  231. 08/17 merge-ort: remove command-line-centric submodule message from merge-ortElijah Newren via GitGitGadget, Jun 18, 2022
  232. 12/17 merge-ort: make `path_messages` a strmap to a string_listJohannes Schindelin via GitGitGadget, Jun 18, 2022
  233. 15/17 merge-tree: allow `ls-files -u` style info to be NUL terminatedElijah Newren via GitGitGadget, Jun 18, 2022
  234. 16/17 merge-tree: add a --allow-unrelated-histories flagElijah Newren via GitGitGadget, Jun 18, 2022
  235. 17/17 git-merge-tree.txt: add a section on potentional usage mistakesElijah Newren via GitGitGadget, Jun 18, 2022
  236. 14/17 merge-ort: optionally produce machine-readable outputElijah Newren via GitGitGadget, Jun 18, 2022
  237. 13/17 merge-ort: store more specific conflict informationElijah Newren via GitGitGadget, Jun 18, 2022
  238. 11/17 merge-ort: store messages in a list, not in a single strbufJohannes Schindelin via GitGitGadget, Jun 18, 2022
  239. 10/17 merge-tree: provide easy access to `ls-files -u` style infoElijah Newren via GitGitGadget, Jun 18, 2022
  240. Johannes SchindelinFeb 21, 2022

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.