Show changes to 13 files +143 −6
branch.c, branch.h, builtin/checkout.c, builtin/commit.c, merge-ort.c, merge.c, merge.h, path.c, path.h, repository.c, repository.h, sequencer.c, t/t7201-co.sh
diff --git a/branch.c b/branch.c
index 8bc7a395a7..5bb1c28915 100644
--- a/branch.c
+++ b/branch.c
@@ -860,9 +860,11 @@ void create_branches_recursively(struct repository *r, const char *name,
free(branch_point);
}
-void remove_merge_branch_state(struct repository *r)
+static void do_remove_merge_branch_state(struct repository *r, unsigned flags)
{
unlink(git_path_merge_head(r));
+ if (!(flags & REMOVE_BRANCH_STATE_PRESERVE_CONFLICT_LABELS))
+ unlink(git_path_merge_labels(r));
unlink(git_path_merge_rr(r));
unlink(git_path_merge_msg(r));
unlink(git_path_merge_mode(r));
@@ -871,11 +873,16 @@ void remove_merge_branch_state(struct repository *r)
save_autostash_ref(r, "MERGE_AUTOSTASH");
}
+void remove_merge_branch_state(struct repository *r)
+{
+ do_remove_merge_branch_state(r, 0);
+}
+
void remove_branch_state(struct repository *r, unsigned flags)
{
sequencer_post_commit_cleanup(r, flags & REMOVE_BRANCH_STATE_VERBOSE);
unlink(git_path_squash_msg(r));
- remove_merge_branch_state(r);
+ do_remove_merge_branch_state(r, flags);
}
void die_if_checked_out(const char *branch, int ignore_current_worktree)
diff --git a/branch.h b/branch.h
index 42d1b12918..95b2431f24 100644
--- a/branch.h
+++ b/branch.h
@@ -128,6 +128,7 @@ int validate_branchname(const char *name, struct strbuf *ref);
int validate_new_branchname(const char *name, struct strbuf *ref, int force);
#define REMOVE_BRANCH_STATE_VERBOSE (1u << 0)
+#define REMOVE_BRANCH_STATE_PRESERVE_CONFLICT_LABELS (1u << 1)
/*
* Remove information about the merge state on the current
* branch. (E.g., MERGE_HEAD)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index bdd2d816b6..295fe0e9fa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -15,6 +15,7 @@
#include "hex.h"
#include "hook.h"
#include "merge-ll.h"
+#include "merge.h"
#include "lockfile.h"
#include "mem-pool.h"
#include "object-file.h"
@@ -317,6 +318,7 @@ static int checkout_merged(int pos, const struct checkout *state,
struct cache_entry *ce = the_repository->index->cache[pos];
const char *path = ce->name;
mmfile_t ancestor, ours, theirs;
+ char *base_label, *ours_label, *theirs_label;
enum ll_merge_result merge_status;
int status;
struct object_id oid;
@@ -347,10 +349,19 @@ static int checkout_merged(int pos, const struct checkout *state,
repo_config_get_bool(the_repository, "merge.renormalize", &renormalize);
ll_opts.renormalize = renormalize;
+ if (read_merge_labels(the_repository, &base_label, &ours_label,
+ &theirs_label)) {
+ base_label = xstrdup("base");
+ ours_label = xstrdup("ours");
+ theirs_label = xstrdup("theirs");
+ }
ll_opts.conflict_style = conflict_style;
- merge_status = ll_merge(&result_buf, path, &ancestor, "base",
- &ours, "ours", &theirs, "theirs",
+ merge_status = ll_merge(&result_buf, path, &ancestor, base_label,
+ &ours, ours_label, &theirs, theirs_label,
state->istate, &ll_opts);
+ free(base_label);
+ free(ours_label);
+ free(theirs_label);
free(ancestor.ptr);
free(ours.ptr);
free(theirs.ptr);
@@ -946,7 +957,8 @@ static void report_tracking(struct branch_info *new_branch_info)
static void update_refs_for_switch(const struct checkout_opts *opts,
struct branch_info *old_branch_info,
- struct branch_info *new_branch_info)
+ struct branch_info *new_branch_info,
+ bool merge_conflicts)
{
struct strbuf msg = STRBUF_INIT;
const char *old_desc, *reflog_msg;
@@ -1048,6 +1060,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
}
if (!opts->quiet)
flags |= REMOVE_BRANCH_STATE_VERBOSE;
+ if (merge_conflicts)
+ flags |= REMOVE_BRANCH_STATE_PRESERVE_CONFLICT_LABELS;
remove_branch_state(the_repository, flags);
strbuf_release(&msg);
if (!opts->quiet &&
@@ -1262,7 +1276,9 @@ static int switch_branches(const struct checkout_opts *opts,
if (autostash_res == STASH_APPLY_CONFLICT && !opts->quiet)
fputc('\n', stderr);
- update_refs_for_switch(opts, &old_branch_info, new_branch_info);
+
+ update_refs_for_switch(opts, &old_branch_info, new_branch_info,
+ autostash_res == STASH_APPLY_CONFLICT);
if (created_autostash) {
discard_index(the_repository->index);
diff --git a/builtin/commit.c b/builtin/commit.c
index 205fbd57e3..c374d5e0d5 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1977,6 +1977,7 @@ int cmd_commit(int argc,
sequencer_post_commit_cleanup(the_repository, 0);
unlink(git_path_merge_head(the_repository));
+ unlink(git_path_merge_labels(the_repository));
unlink(git_path_merge_msg(the_repository));
unlink(git_path_merge_mode(the_repository));
unlink(git_path_squash_msg(the_repository));
diff --git a/merge-ort.c b/merge-ort.c
index c410a5d353..783e74c6e3 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -35,6 +35,7 @@
#include "hex.h"
#include "entry.h"
#include "merge-ll.h"
+#include "merge.h"
#include "match-trees.h"
#include "mem-pool.h"
#include "object-file.h"
@@ -418,6 +419,9 @@ struct merge_options_internal {
/* field that holds submodule conflict information */
struct string_list conflicted_submodules;
+
+ /* Copies of the labels used for conflict markers */
+ char *labels[3];
};
struct conflicted_submodule_item {
@@ -4969,6 +4973,13 @@ void merge_switch_to_result(struct merge_options *opt,
return;
}
trace2_region_leave("merge", "write_auto_merge", opt->repo);
+
+ trace2_region_enter("merge", "write_merge_labels", opt->repo);
+ opt->priv = result->priv;
+ write_merge_labels(opt->repo, opt->priv->labels[0], opt->priv->labels[1],
+ opt->priv->labels[2]);
+ opt->priv = NULL;
+ trace2_region_leave("merge", "write_merge_labels", opt->repo);
}
if (display_update_msgs)
merge_display_update_messages(opt, /* detailed */ 0, result);
@@ -5234,6 +5245,14 @@ static void move_opt_priv_to_result_priv(struct merge_options *opt,
* to move it.
*/
assert(opt->priv && !result->priv);
+ if (!result->clean) {
+ opt->priv->labels[0] =
+ mem_pool_strdup(&opt->priv->pool, opt->ancestor);
+ opt->priv->labels[1] =
+ mem_pool_strdup(&opt->priv->pool, opt->branch1);
+ opt->priv->labels[2] =
+ mem_pool_strdup(&opt->priv->pool, opt->branch2);
+ }
result->priv = opt->priv;
result->_properly_initialized = RESULT_INITIALIZED;
opt->priv = NULL;
diff --git a/merge.c b/merge.c
index 0f5e823e63..95495ae1ba 100644
--- a/merge.c
+++ b/merge.c
@@ -8,6 +8,7 @@
#include "merge.h"
#include "commit.h"
#include "repository.h"
+#include "path.h"
#include "run-command.h"
#include "resolve-undo.h"
#include "tree.h"
@@ -111,3 +112,65 @@ int checkout_fast_forward(struct repository *r,
return error(_("unable to write new index file"));
return 0;
}
+
+int write_merge_labels(struct repository *r, const char *base,
+ const char *ours, const char *theirs)
+{
+ FILE *f = fopen_or_warn(git_path_merge_labels(r), "w");
+
+ if (!f)
+ return -1;
+
+ fprintf(f, "%s\n%s\n%s\n", base, ours, theirs);
+ if (fclose(f))
+ return error_errno("could not write '%s'",
+ git_path_merge_labels(r));
+
+ return 0;
+}
+
+static int parse_merge_label_line(const char **p, char **line)
+{
+ const char *eol = strchr(*p, '\n');
+
+ if (!eol)
+ return -1;
+
+ *line = xmemdupz(*p, eol - *p);
+ *p = eol + 1;
+
+ return 0;
+}
+
+int read_merge_labels(struct repository *r,
+ char **pbase, char** pours, char** ptheirs)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *p;
+ char *base = NULL, *ours = NULL, *theirs = NULL;
+ int ret = -1;
+
+ if (strbuf_read_file(&buf, git_path_merge_labels(r), 0) < 0)
+ return -1;
+
+ p = buf.buf;
+ if (parse_merge_label_line(&p, &base))
+ goto out;
+ if (parse_merge_label_line(&p, &ours))
+ goto out;
+ if (parse_merge_label_line(&p, &theirs))
+ goto out;
+ ret = 0;
+ *pbase = base;
+ *pours = ours;
+ *ptheirs = theirs;
+out:
+ if (ret) {
+ free(base);
+ free(ours);
+ free(theirs);
+ }
+ strbuf_release(&buf);
+
+ return ret;
+}
diff --git a/merge.h b/merge.h
index 21ac7ef2f1..0772737a87 100644
--- a/merge.h
+++ b/merge.h
@@ -13,5 +13,9 @@ int checkout_fast_forward(struct repository *r,
const struct object_id *from,
const struct object_id *to,
int overwrite_ignore);
+int write_merge_labels(struct repository *r,
+ const char *base, const char *ours, const char *theirs);
+int read_merge_labels(struct repository *r,
+ char **base, char **ours, char **theirs);
#endif /* MERGE_H */
diff --git a/path.c b/path.c
index c3a709a928..7965762602 100644
--- a/path.c
+++ b/path.c
@@ -1655,3 +1655,4 @@ REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
REPO_GIT_PATH_FUNC(shallow, "shallow")
+REPO_GIT_PATH_FUNC(merge_labels, "MERGE_LABELS")
diff --git a/path.h b/path.h
index 7e7408dd05..8cd12ccfde 100644
--- a/path.h
+++ b/path.h
@@ -142,6 +142,7 @@ const char *git_path_merge_mode(struct repository *r);
const char *git_path_merge_head(struct repository *r);
const char *git_path_fetch_head(struct repository *r);
const char *git_path_shallow(struct repository *r);
+const char *git_path_merge_labels(struct repository *r);
int ends_with_path_components(const char *path, const char *components);
diff --git a/repository.c b/repository.c
index b857e1c580..210fb819b0 100644
--- a/repository.c
+++ b/repository.c
@@ -367,6 +367,7 @@ static void repo_clear_path_cache(struct repo_path_cache *cache)
FREE_AND_NULL(cache->merge_head);
FREE_AND_NULL(cache->fetch_head);
FREE_AND_NULL(cache->shallow);
+ FREE_AND_NULL(cache->merge_labels);
}
void repo_clear(struct repository *repo)
diff --git a/repository.h b/repository.h
index 11f5c2ed10..91b1f57db7 100644
--- a/repository.h
+++ b/repository.h
@@ -36,6 +36,7 @@ struct repo_path_cache {
char *merge_head;
char *fetch_head;
char *shallow;
+ char *merge_labels;
};
struct repository {
diff --git a/sequencer.c b/sequencer.c
index e25ef5eb61..0710aa6400 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5145,6 +5145,7 @@ static int pick_commits(struct repository *r,
unlink(rebase_path_stopped_sha());
unlink(rebase_path_amend());
unlink(rebase_path_patch());
+ unlink(git_path_merge_labels(r));
while (todo_list->current < todo_list->nr) {
struct todo_item *item = todo_list->items + todo_list->current;
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 9ea9462914..7d0dcf8c8b 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -183,6 +183,27 @@ test_expect_success 'format of merge conflict from checkout -m' '
d
>>>>>>> local
EOF
+ test_cmp expect two &&
+
+ test_path_is_file .git/MERGE_LABELS &&
+
+ git checkout --conflict=diff3 two &&
+ cat >expect <<-\EOF &&
+ <<<<<<< simple
+ a
+ c
+ e
+ ||||||| main
+ a
+ b
+ c
+ d
+ e
+ =======
+ b
+ d
+ >>>>>>> local
+ EOF
test_cmp expect two
'