From: Junio C Hamano <gitster@pobox.com>
To: Phillip Wood <phillip.wood123@gmail.com>
Cc: git@vger.kernel.org, Elijah Newren <newren@gmail.com>,
Johannes Sixt <j6t@kdbg.org>
Subject: Re: [PATCH v2 2/2] merge: remember conflict labels
Date: Mon, 05 Oct 2026 09:19:59 -0700 [thread overview]
Message-ID: <xmqqld8cktlc.fsf@gitster.g> (raw)
In-Reply-To: <18bdf7df49dde2c8e7f73f3b46c656abb6b26293.1791206658.git.phillip.wood@dunelm.org.uk> (Phillip Wood's message of "Mon, 5 Oct 2026 14:24:49 +0100")
Phillip Wood <phillip.wood123@gmail.com> writes:
> @@ -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)
Not complaining and I have no improvement suggestions, but this
phrasing made me imagine that we would be passing this flag bit
in code paths where we want to write the extra file out.
But that does not match the reality. merge_switch_to_result() calls
write_merge_labels() unconditionally. The bit controls if the file
written survives the clean-up after the operation.
> @@ -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;
OK.
> 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);
OK, so here we assume STASH_APPLY_CONFLICT result means we called
write_merge_labels() and left the file. If not, we did not call it
and the file should not be there.
But then can't we just unconditionally leave the file, instead of
not removing what we wouldn't have created?
> 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));
Here we clean it up unconditionally after we are about to
successfully finish "git commit".
> @@ -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);
> + }
OK, merge_switch_to_result() is the only thing that consumes these,
and it will never happen after we call merge_finalize() where we
destroy the mempool, so this allocation should be safe.
> +static char *parse_merge_label_line(struct strbuf *buf, FILE *fp)
> +{
> + if (strbuf_getline(buf, fp) == EOF)
> + return NULL;
> +
> + return xmemdupz(buf->buf, buf->len);
> +}
Wouldn't strbuf_detach() be more intuitive?
> +int read_merge_labels(struct repository *r,
> + char **pbase, char** pours, char** ptheirs)
Be consistent. Asterisk sticks to variables, not types.
> +{
> + struct strbuf buf = STRBUF_INIT;
> + char *base = NULL, *ours = NULL, *theirs = NULL;
> + int ret = -1;
> + FILE *fp = fopen(git_path_merge_labels(r), "r");
> +
> + if (!fp)
> + return -1;
> +
> + base = parse_merge_label_line(&buf, fp);
> + if (!base)
> + goto out;
> +
> + ours = parse_merge_label_line(&buf, fp);
> + if (!ours)
> + goto out;
> +
> + theirs = parse_merge_label_line(&buf, fp);
> + if (!theirs)
> + goto out;
The repetitions are a bit annoying, but it does not get much better:
int i;
char bot[3] = {0}; /* base, ours, theirs */
for (i = 0; i < ARRAY_SIZE(bot); i++)
if (!(bot[i] = parse_merge_label_line(&buf, fp)))
goto out;
so I am OK with what was posted.
It may be helpful to future developers to leave a comment that we
deliberately ignore cruft after these three lines in the file and
why, instead of diagnosing it as an error.
> + ret = 0;
> + *pbase = base;
> + *pours = ours;
> + *ptheirs = theirs;
> +out:
> + if (ret) {
> + free(base);
> + free(ours);
> + free(theirs);
> + }
> + fclose(fp);
> + strbuf_release(&buf);
> +
> + return ret;
> +}
next prev parent reply other threads:[~2026-10-05 16:20 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 9:48 [PATCH 0/2] checkout -m: recreate conflict labels Phillip Wood
2026-09-30 9:48 ` [PATCH 1/2] remove_branch_state: convert boolean argument to flags Phillip Wood
2026-09-30 9:48 ` [PATCH 2/2] merge: remember conflict labels Phillip Wood
2026-09-30 16:42 ` Junio C Hamano
2026-10-01 8:54 ` Phillip Wood
2026-10-01 17:14 ` Junio C Hamano
2026-09-30 20:24 ` [PATCH 0/2] checkout -m: recreate " Johannes Sixt
2026-09-30 20:40 ` Junio C Hamano
2026-09-30 21:20 ` Johannes Sixt
2026-10-01 8:45 ` Phillip Wood
2026-10-05 13:24 ` [PATCH v2 " Phillip Wood
2026-10-05 13:24 ` [PATCH v2 1/2] remove_branch_state: convert boolean argument to flags Phillip Wood
2026-10-05 13:24 ` [PATCH v2 2/2] merge: remember conflict labels Phillip Wood
2026-10-05 16:19 ` Junio C Hamano [this message]
2026-10-06 15:21 ` Phillip Wood
2026-10-05 16:31 ` Junio C Hamano
2026-10-06 15:05 ` Phillip Wood
2026-10-06 15:46 ` Junio C Hamano
2026-10-07 13:38 ` Phillip Wood
2026-10-05 14:48 ` [PATCH v2 0/2] checkout -m: recreate " Johannes Sixt
2026-10-05 15:09 ` Phillip Wood
2026-10-05 15:53 ` Junio C Hamano
2026-10-09 9:13 ` [PATCH v3 " Phillip Wood
2026-10-09 9:13 ` [PATCH v3 1/2] remove_branch_state: convert boolean argument to flags Phillip Wood
2026-10-09 9:13 ` [PATCH v3 2/2] merge: remember conflict labels Phillip Wood
2026-10-10 0:24 ` Junio C Hamano
2026-10-09 20:31 ` [PATCH v3 0/2] checkout -m: recreate " 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=xmqqld8cktlc.fsf@gitster.g \
--to=gitster@pobox.com \
--cc=git@vger.kernel.org \
--cc=j6t@kdbg.org \
--cc=newren@gmail.com \
--cc=phillip.wood123@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