2007-07-14 23:14:45 +00:00
|
|
|
#include "builtin.h"
|
2023-03-21 06:25:58 +00:00
|
|
|
#include "abspath.h"
|
2023-11-20 19:18:52 +00:00
|
|
|
#include "diff.h"
|
2023-11-01 19:24:19 +00:00
|
|
|
#include "hex.h"
|
|
|
|
#include "object-name.h"
|
|
|
|
#include "object-store.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-03-21 06:26:05 +00:00
|
|
|
#include "setup.h"
|
2006-12-06 15:26:06 +00:00
|
|
|
#include "xdiff/xdiff.h"
|
2006-12-20 16:37:07 +00:00
|
|
|
#include "xdiff-interface.h"
|
2008-10-02 12:59:20 +00:00
|
|
|
#include "parse-options.h"
|
2006-12-06 15:26:06 +00:00
|
|
|
|
2008-10-02 12:59:20 +00:00
|
|
|
static const char *const merge_file_usage[] = {
|
2015-01-13 07:44:47 +00:00
|
|
|
N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
|
2008-10-02 12:59:20 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int label_cb(const struct option *opt, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
static int label_count = 0;
|
|
|
|
const char **names = (const char **)opt->value;
|
|
|
|
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 06:45:42 +00:00
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
2008-10-02 12:59:20 +00:00
|
|
|
if (label_count >= 3)
|
|
|
|
return error("too many labels on the command line");
|
|
|
|
names[label_count++] = arg;
|
|
|
|
return 0;
|
|
|
|
}
|
2006-12-06 15:26:06 +00:00
|
|
|
|
2023-11-20 19:18:52 +00:00
|
|
|
static int set_diff_algorithm(xpparam_t *xpp,
|
|
|
|
const char *alg)
|
|
|
|
{
|
|
|
|
long diff_algorithm = parse_algorithm_value(alg);
|
|
|
|
if (diff_algorithm < 0)
|
|
|
|
return -1;
|
|
|
|
xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int diff_algorithm_cb(const struct option *opt,
|
|
|
|
const char *arg, int unset)
|
|
|
|
{
|
|
|
|
xpparam_t *xpp = opt->value;
|
|
|
|
|
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
|
|
|
if (set_diff_algorithm(xpp, arg))
|
|
|
|
return error(_("option diff-algorithm accepts \"myers\", "
|
|
|
|
"\"minimal\", \"patience\" and \"histogram\""));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-14 23:14:45 +00:00
|
|
|
int cmd_merge_file(int argc, const char **argv, const char *prefix)
|
2006-12-06 15:26:06 +00:00
|
|
|
{
|
2022-07-01 10:42:55 +00:00
|
|
|
const char *names[3] = { 0 };
|
|
|
|
mmfile_t mmfs[3] = { 0 };
|
|
|
|
mmbuffer_t result = { 0 };
|
|
|
|
xmparam_t xmp = { 0 };
|
2023-11-01 19:24:19 +00:00
|
|
|
int ret = 0, i = 0, to_stdout = 0, object_id = 0;
|
2010-03-01 21:46:26 +00:00
|
|
|
int quiet = 0;
|
2008-10-02 12:59:20 +00:00
|
|
|
struct option options[] = {
|
2013-08-03 11:51:19 +00:00
|
|
|
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
|
2023-11-01 19:24:19 +00:00
|
|
|
OPT_BOOL(0, "object-id", &object_id, N_("use object IDs instead of filenames")),
|
2012-08-20 12:32:23 +00:00
|
|
|
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
|
xdiff: implement a zealous diff3, or "zdiff3"
"zdiff3" is identical to ordinary diff3 except that it allows compaction
of common lines on the two sides of history at the beginning or end of
the conflict hunk. For example, the following diff3 conflict:
1
2
3
4
<<<<<<
A
B
C
D
E
||||||
5
6
======
A
X
C
Y
E
>>>>>>
7
8
9
has common lines 'A', 'C', and 'E' on the two sides. With zdiff3, one
would instead get the following conflict:
1
2
3
4
A
<<<<<<
B
C
D
||||||
5
6
======
X
C
Y
>>>>>>
E
7
8
9
Note that the common lines, 'A', and 'E' were moved outside the
conflict. Unlike with the two-way conflicts from the 'merge'
conflictStyle, the zdiff3 conflict is NOT split into multiple conflict
regions to allow the common 'C' lines to be shown outside a conflict,
because zdiff3 shows the base version too and the base version cannot be
reasonably split.
Note also that the removing of lines common to the two sides might make
the remaining text inside the conflict region match the base text inside
the conflict region (for example, if the diff3 conflict had '5 6 E' on
the right side of the conflict, then the common line 'E' would be moved
outside and both the base and right side's remaining conflict text would
be the lines '5' and '6'). This has the potential to surprise users and
make them think there should not have been a conflict, but there
definitely was a conflict and it should remain.
Based-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Co-authored-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-01 00:05:06 +00:00
|
|
|
OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
|
|
|
|
XDL_MERGE_ZEALOUS_DIFF3),
|
2012-08-20 12:32:23 +00:00
|
|
|
OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
|
2008-06-20 07:17:27 +00:00
|
|
|
XDL_MERGE_FAVOR_OURS),
|
2012-08-20 12:32:23 +00:00
|
|
|
OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
|
2008-06-20 07:17:27 +00:00
|
|
|
XDL_MERGE_FAVOR_THEIRS),
|
2012-08-20 12:32:23 +00:00
|
|
|
OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
|
2010-03-01 21:46:28 +00:00
|
|
|
XDL_MERGE_FAVOR_UNION),
|
2023-11-20 19:18:52 +00:00
|
|
|
OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"),
|
|
|
|
N_("choose a diff algorithm"),
|
|
|
|
PARSE_OPT_NONEG, diff_algorithm_cb),
|
2010-03-01 21:46:27 +00:00
|
|
|
OPT_INTEGER(0, "marker-size", &xmp.marker_size,
|
2012-08-20 12:32:23 +00:00
|
|
|
N_("for conflicts, use this marker size")),
|
|
|
|
OPT__QUIET(&quiet, N_("do not warn about conflicts")),
|
|
|
|
OPT_CALLBACK('L', NULL, names, N_("name"),
|
2015-01-13 07:44:47 +00:00
|
|
|
N_("set labels for file1/orig-file/file2"), &label_cb),
|
2008-10-02 12:59:20 +00:00
|
|
|
OPT_END(),
|
|
|
|
};
|
|
|
|
|
2010-03-01 21:46:26 +00:00
|
|
|
xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
|
|
|
|
xmp.style = 0;
|
|
|
|
xmp.favor = 0;
|
|
|
|
|
2010-08-06 03:27:43 +00:00
|
|
|
if (startup_info->have_repository) {
|
2008-08-29 17:49:56 +00:00
|
|
|
/* Read the configuration file */
|
|
|
|
git_config(git_xmerge_config, NULL);
|
|
|
|
if (0 <= git_xmerge_style)
|
2010-03-01 21:46:26 +00:00
|
|
|
xmp.style = git_xmerge_style;
|
2008-08-29 17:49:56 +00:00
|
|
|
}
|
2006-12-06 15:26:06 +00:00
|
|
|
|
2009-05-23 18:53:12 +00:00
|
|
|
argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
|
2008-10-02 12:59:20 +00:00
|
|
|
if (argc != 3)
|
|
|
|
usage_with_options(merge_file_usage, options);
|
2008-12-26 10:17:04 +00:00
|
|
|
if (quiet) {
|
|
|
|
if (!freopen("/dev/null", "w", stderr))
|
2016-05-08 09:47:29 +00:00
|
|
|
return error_errno("failed to redirect stderr to /dev/null");
|
2008-12-26 10:17:04 +00:00
|
|
|
}
|
2006-12-06 15:26:06 +00:00
|
|
|
|
2023-11-01 19:24:19 +00:00
|
|
|
if (object_id)
|
|
|
|
setup_git_directory();
|
|
|
|
|
2007-06-05 02:37:13 +00:00
|
|
|
for (i = 0; i < 3; i++) {
|
2017-03-21 01:28:49 +00:00
|
|
|
char *fname;
|
2023-11-01 19:24:19 +00:00
|
|
|
struct object_id oid;
|
2022-07-01 10:42:55 +00:00
|
|
|
mmfile_t *mmf = mmfs + i;
|
2017-03-21 01:28:49 +00:00
|
|
|
|
2008-10-02 12:59:20 +00:00
|
|
|
if (!names[i])
|
|
|
|
names[i] = argv[i];
|
2017-03-21 01:28:49 +00:00
|
|
|
|
|
|
|
fname = prefix_filename(prefix, argv[i]);
|
2022-07-01 10:42:55 +00:00
|
|
|
|
2023-11-01 19:24:19 +00:00
|
|
|
if (object_id) {
|
|
|
|
if (repo_get_oid(the_repository, argv[i], &oid))
|
|
|
|
ret = error(_("object '%s' does not exist"),
|
|
|
|
argv[i]);
|
|
|
|
else if (!oideq(&oid, the_hash_algo->empty_blob))
|
|
|
|
read_mmblob(mmf, &oid);
|
|
|
|
else
|
|
|
|
read_mmfile(mmf, "/dev/null");
|
|
|
|
} else if (read_mmfile(mmf, fname)) {
|
2022-07-01 10:42:55 +00:00
|
|
|
ret = -1;
|
2023-11-01 19:24:19 +00:00
|
|
|
}
|
|
|
|
if (ret != -1 && (mmf->size > MAX_XDIFF_SIZE ||
|
|
|
|
buffer_is_binary(mmf->ptr, mmf->size))) {
|
2022-07-01 10:42:55 +00:00
|
|
|
ret = error("Cannot merge binary files: %s",
|
|
|
|
argv[i]);
|
2023-11-01 19:24:19 +00:00
|
|
|
}
|
2022-07-01 10:42:55 +00:00
|
|
|
|
2017-03-21 01:28:49 +00:00
|
|
|
free(fname);
|
|
|
|
if (ret)
|
2022-07-01 10:42:56 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2007-06-05 02:37:13 +00:00
|
|
|
}
|
2006-12-06 15:26:06 +00:00
|
|
|
|
2010-03-21 00:37:33 +00:00
|
|
|
xmp.ancestor = names[1];
|
2010-03-21 00:35:18 +00:00
|
|
|
xmp.file1 = names[0];
|
|
|
|
xmp.file2 = names[2];
|
|
|
|
ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
|
2006-12-06 15:26:06 +00:00
|
|
|
|
|
|
|
if (ret >= 0) {
|
2023-11-01 19:24:19 +00:00
|
|
|
if (object_id && !to_stdout) {
|
|
|
|
struct object_id oid;
|
|
|
|
if (result.size) {
|
|
|
|
if (write_object_file(result.ptr, result.size, OBJ_BLOB, &oid) < 0)
|
|
|
|
ret = error(_("Could not write object file"));
|
|
|
|
} else {
|
|
|
|
oidcpy(&oid, the_hash_algo->empty_blob);
|
|
|
|
}
|
|
|
|
if (ret >= 0)
|
|
|
|
printf("%s\n", oid_to_hex(&oid));
|
|
|
|
} else {
|
|
|
|
const char *filename = argv[0];
|
|
|
|
char *fpath = prefix_filename(prefix, argv[0]);
|
|
|
|
FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
ret = error_errno("Could not open %s for writing",
|
|
|
|
filename);
|
|
|
|
else if (result.size &&
|
|
|
|
fwrite(result.ptr, result.size, 1, f) != 1)
|
|
|
|
ret = error_errno("Could not write to %s", filename);
|
|
|
|
else if (fclose(f))
|
|
|
|
ret = error_errno("Could not close %s", filename);
|
|
|
|
free(fpath);
|
|
|
|
}
|
2006-12-06 15:26:06 +00:00
|
|
|
free(result.ptr);
|
|
|
|
}
|
|
|
|
|
2015-10-28 22:44:21 +00:00
|
|
|
if (ret > 127)
|
|
|
|
ret = 127;
|
|
|
|
|
2022-07-01 10:42:56 +00:00
|
|
|
cleanup:
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
free(mmfs[i].ptr);
|
|
|
|
|
2006-12-06 15:26:06 +00:00
|
|
|
return ret;
|
|
|
|
}
|