ll-merge: make callers responsible for showing warnings

Since some callers may want to send warning messages to somewhere other
than stdout/stderr, stop printing "warning: Cannot merge binary files"
from ll-merge and instead modify the return status of ll_merge() to
indicate when a merge of binary files has occurred.  Message printing
probably does not belong in a "low-level merge" anyway.

This commit continues printing the message as-is, just from the callers
instead of within ll_merge().  Future changes will start handling the
message differently in the merge-ort codepath.

There was one special case here: the callers in rerere.c do NOT check
for and print such a message; since those code paths explicitly skip
over binary files, there is no reason to check for a return status of
LL_MERGE_BINARY_CONFLICT or print the related message.

Note that my methodology included first modifying ll_merge() to return
a struct, so that the compiler would catch all the callers for me and
ensure I had modified all of them.  After modifying all of them, I then
changed the struct to an enum.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Elijah Newren 2022-02-02 02:37:30 +00:00 committed by Junio C Hamano
parent 7b90ab467a
commit 35f6967161
9 changed files with 63 additions and 32 deletions

View File

@ -3492,7 +3492,7 @@ static int three_way_merge(struct apply_state *state,
{
mmfile_t base_file, our_file, their_file;
mmbuffer_t result = { NULL };
int status;
enum ll_merge_result status;
/* resolve trivial cases first */
if (oideq(base, ours))
@ -3509,6 +3509,9 @@ static int three_way_merge(struct apply_state *state,
&their_file, "theirs",
state->repo->index,
NULL);
if (status == LL_MERGE_BINARY_CONFLICT)
warning("Cannot merge binary files: %s (%s vs. %s)",
path, "ours", "theirs");
free(base_file.ptr);
free(our_file.ptr);
free(their_file.ptr);

View File

@ -245,6 +245,7 @@ static int checkout_merged(int pos, const struct checkout *state,
struct cache_entry *ce = active_cache[pos];
const char *path = ce->name;
mmfile_t ancestor, ours, theirs;
enum ll_merge_result merge_status;
int status;
struct object_id oid;
mmbuffer_t result_buf;
@ -275,13 +276,16 @@ static int checkout_merged(int pos, const struct checkout *state,
memset(&ll_opts, 0, sizeof(ll_opts));
git_config_get_bool("merge.renormalize", &renormalize);
ll_opts.renormalize = renormalize;
status = ll_merge(&result_buf, path, &ancestor, "base",
merge_status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs",
state->istate, &ll_opts);
free(ancestor.ptr);
free(ours.ptr);
free(theirs.ptr);
if (status < 0 || !result_buf.ptr) {
if (merge_status == LL_MERGE_BINARY_CONFLICT)
warning("Cannot merge binary files: %s (%s vs. %s)",
path, "ours", "theirs");
if (merge_status < 0 || !result_buf.ptr) {
free(result_buf.ptr);
return error(_("path '%s': cannot merge"), path);
}

View File

@ -14,7 +14,7 @@
struct ll_merge_driver;
typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
typedef enum ll_merge_result (*ll_merge_fn)(const struct ll_merge_driver *,
mmbuffer_t *result,
const char *path,
mmfile_t *orig, const char *orig_name,
@ -49,7 +49,7 @@ void reset_merge_attributes(void)
/*
* Built-in low-levels
*/
static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
static enum ll_merge_result ll_binary_merge(const struct ll_merge_driver *drv_unused,
mmbuffer_t *result,
const char *path,
mmfile_t *orig, const char *orig_name,
@ -58,6 +58,7 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
const struct ll_merge_options *opts,
int marker_size)
{
enum ll_merge_result ret;
mmfile_t *stolen;
assert(opts);
@ -68,16 +69,19 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
*/
if (opts->virtual_ancestor) {
stolen = orig;
ret = LL_MERGE_OK;
} else {
switch (opts->variant) {
default:
warning("Cannot merge binary files: %s (%s vs. %s)",
path, name1, name2);
/* fallthru */
ret = LL_MERGE_BINARY_CONFLICT;
stolen = src1;
break;
case XDL_MERGE_FAVOR_OURS:
ret = LL_MERGE_OK;
stolen = src1;
break;
case XDL_MERGE_FAVOR_THEIRS:
ret = LL_MERGE_OK;
stolen = src2;
break;
}
@ -87,16 +91,10 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
result->size = stolen->size;
stolen->ptr = NULL;
/*
* With -Xtheirs or -Xours, we have cleanly merged;
* otherwise we got a conflict.
*/
return opts->variant == XDL_MERGE_FAVOR_OURS ||
opts->variant == XDL_MERGE_FAVOR_THEIRS ?
0 : 1;
return ret;
}
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
static enum ll_merge_result ll_xdl_merge(const struct ll_merge_driver *drv_unused,
mmbuffer_t *result,
const char *path,
mmfile_t *orig, const char *orig_name,
@ -105,7 +103,9 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
const struct ll_merge_options *opts,
int marker_size)
{
enum ll_merge_result ret;
xmparam_t xmp;
int status;
assert(opts);
if (orig->size > MAX_XDIFF_SIZE ||
@ -133,10 +133,12 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
xmp.ancestor = orig_name;
xmp.file1 = name1;
xmp.file2 = name2;
return xdl_merge(orig, src1, src2, &xmp, result);
status = xdl_merge(orig, src1, src2, &xmp, result);
ret = (status > 0) ? LL_MERGE_CONFLICT : status;
return ret;
}
static int ll_union_merge(const struct ll_merge_driver *drv_unused,
static enum ll_merge_result ll_union_merge(const struct ll_merge_driver *drv_unused,
mmbuffer_t *result,
const char *path,
mmfile_t *orig, const char *orig_name,
@ -178,7 +180,7 @@ static void create_temp(mmfile_t *src, char *path, size_t len)
/*
* User defined low-level merge driver support.
*/
static int ll_ext_merge(const struct ll_merge_driver *fn,
static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
mmbuffer_t *result,
const char *path,
mmfile_t *orig, const char *orig_name,
@ -194,6 +196,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
const char *args[] = { NULL, NULL };
int status, fd, i;
struct stat st;
enum ll_merge_result ret;
assert(opts);
sq_quote_buf(&path_sq, path);
@ -236,7 +239,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
unlink_or_warn(temp[i]);
strbuf_release(&cmd);
strbuf_release(&path_sq);
return status;
ret = (status > 0) ? LL_MERGE_CONFLICT : status;
return ret;
}
/*
@ -362,7 +366,7 @@ static void normalize_file(mmfile_t *mm, const char *path, struct index_state *i
}
}
int ll_merge(mmbuffer_t *result_buf,
enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
const char *path,
mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label,

View File

@ -82,13 +82,20 @@ struct ll_merge_options {
long xdl_opts;
};
enum ll_merge_result {
LL_MERGE_ERROR = -1,
LL_MERGE_OK = 0,
LL_MERGE_CONFLICT,
LL_MERGE_BINARY_CONFLICT,
};
/**
* Perform a three-way single-file merge in core. This is a thin wrapper
* around `xdl_merge` that takes the path and any merge backend specified in
* `.gitattributes` or `.git/info/attributes` into account.
* Returns 0 for a clean merge.
*/
int ll_merge(mmbuffer_t *result_buf,
enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
const char *path,
mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label,

View File

@ -36,7 +36,7 @@ static void *three_way_filemerge(struct index_state *istate,
mmfile_t *their,
unsigned long *size)
{
int merge_status;
enum ll_merge_result merge_status;
mmbuffer_t res;
/*
@ -50,6 +50,9 @@ static void *three_way_filemerge(struct index_state *istate,
istate, NULL);
if (merge_status < 0)
return NULL;
if (merge_status == LL_MERGE_BINARY_CONFLICT)
warning("Cannot merge binary files: %s (%s vs. %s)",
path, ".our", ".their");
*size = res.size;
return res.ptr;

View File

@ -1743,7 +1743,7 @@ static int merge_3way(struct merge_options *opt,
mmfile_t orig, src1, src2;
struct ll_merge_options ll_opts = {0};
char *base, *name1, *name2;
int merge_status;
enum ll_merge_result merge_status;
if (!opt->priv->attr_index.initialized)
initialize_attr_index(opt);
@ -1787,6 +1787,9 @@ static int merge_3way(struct merge_options *opt,
merge_status = ll_merge(result_buf, path, &orig, base,
&src1, name1, &src2, name2,
&opt->priv->attr_index, &ll_opts);
if (merge_status == LL_MERGE_BINARY_CONFLICT)
warning("Cannot merge binary files: %s (%s vs. %s)",
path, name1, name2);
free(base);
free(name1);

View File

@ -1044,7 +1044,7 @@ static int merge_3way(struct merge_options *opt,
mmfile_t orig, src1, src2;
struct ll_merge_options ll_opts = {0};
char *base, *name1, *name2;
int merge_status;
enum ll_merge_result merge_status;
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
@ -1090,6 +1090,9 @@ static int merge_3way(struct merge_options *opt,
merge_status = ll_merge(result_buf, a->path, &orig, base,
&src1, name1, &src2, name2,
opt->repo->index, &ll_opts);
if (merge_status == LL_MERGE_BINARY_CONFLICT)
warning("Cannot merge binary files: %s (%s vs. %s)",
a->path, name1, name2);
free(base);
free(name1);

View File

@ -344,7 +344,7 @@ static int ll_merge_in_worktree(struct notes_merge_options *o,
{
mmbuffer_t result_buf;
mmfile_t base, local, remote;
int status;
enum ll_merge_result status;
read_mmblob(&base, &p->base);
read_mmblob(&local, &p->local);
@ -358,6 +358,9 @@ static int ll_merge_in_worktree(struct notes_merge_options *o,
free(local.ptr);
free(remote.ptr);
if (status == LL_MERGE_BINARY_CONFLICT)
warning("Cannot merge binary files: %s (%s vs. %s)",
oid_to_hex(&p->obj), o->local_ref, o->remote_ref);
if ((status < 0) || !result_buf.ptr)
die("Failed to execute internal merge");

View File

@ -609,19 +609,20 @@ static int try_merge(struct index_state *istate,
const struct rerere_id *id, const char *path,
mmfile_t *cur, mmbuffer_t *result)
{
int ret;
enum ll_merge_result ret;
mmfile_t base = {NULL, 0}, other = {NULL, 0};
if (read_mmfile(&base, rerere_path(id, "preimage")) ||
read_mmfile(&other, rerere_path(id, "postimage")))
ret = 1;
else
read_mmfile(&other, rerere_path(id, "postimage"))) {
ret = LL_MERGE_CONFLICT;
} else {
/*
* A three-way merge. Note that this honors user-customizable
* low-level merge driver settings.
*/
ret = ll_merge(result, path, &base, NULL, cur, "", &other, "",
istate, NULL);
}
free(base.ptr);
free(other.ptr);