2007-12-24 11:36:00 +03:00
|
|
|
/*
|
|
|
|
* Low level 3-way in-core file merge.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2007 Junio C Hamano
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cache.h"
|
|
|
|
#include "attr.h"
|
|
|
|
#include "xdiff-interface.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
#include "ll-merge.h"
|
2015-06-05 01:10:29 +03:00
|
|
|
#include "quote.h"
|
2007-12-24 11:36:00 +03:00
|
|
|
|
|
|
|
struct ll_merge_driver;
|
|
|
|
|
|
|
|
typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
|
|
|
|
mmbuffer_t *result,
|
|
|
|
const char *path,
|
2010-03-21 03:38:58 +03:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 11:36:00 +03:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 09:49:53 +04:00
|
|
|
const struct ll_merge_options *opts,
|
2010-01-16 09:37:32 +03:00
|
|
|
int marker_size);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
|
|
|
struct ll_merge_driver {
|
|
|
|
const char *name;
|
|
|
|
const char *description;
|
|
|
|
ll_merge_fn fn;
|
|
|
|
const char *recursive;
|
|
|
|
struct ll_merge_driver *next;
|
|
|
|
char *cmdline;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Built-in low-levels
|
|
|
|
*/
|
|
|
|
static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
|
|
|
|
mmbuffer_t *result,
|
ll-merge: warn about inability to merge binary files only when we can't
When a path being merged is auto detected to be a binary file, we
warned "Cannot merge binary files" before switching to activate the
binary ll-merge driver. When we are merging with the -Xours/theirs
option, however, we know what the "clean" merge result is, and the
warning is inappropriate.
In addition, when the path is explicitly marked as a binary file,
this warning was not issued, even though without -Xours/theirs, we
cannot cleanly automerge such a path, which was inconsistent.
Move the warning code from ll_xdl_merge() to ll_binary_merge(), and
issue the message only when we cannot cleanly automerge.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-09-12 13:01:52 +04:00
|
|
|
const char *path,
|
2010-03-21 03:38:58 +03:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 11:36:00 +03:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 09:49:53 +04:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
2010-08-26 09:49:53 +04:00
|
|
|
mmfile_t *stolen;
|
|
|
|
assert(opts);
|
|
|
|
|
2007-12-24 11:36:00 +03:00
|
|
|
/*
|
2012-09-09 08:27:19 +04:00
|
|
|
* The tentative merge result is the or common ancestor for an internal merge.
|
2007-12-24 11:36:00 +03:00
|
|
|
*/
|
2012-09-09 08:27:19 +04:00
|
|
|
if (opts->virtual_ancestor) {
|
|
|
|
stolen = orig;
|
|
|
|
} else {
|
|
|
|
switch (opts->variant) {
|
|
|
|
default:
|
2012-09-15 08:39:56 +04:00
|
|
|
warning("Cannot merge binary files: %s (%s vs. %s)",
|
ll-merge: warn about inability to merge binary files only when we can't
When a path being merged is auto detected to be a binary file, we
warned "Cannot merge binary files" before switching to activate the
binary ll-merge driver. When we are merging with the -Xours/theirs
option, however, we know what the "clean" merge result is, and the
warning is inappropriate.
In addition, when the path is explicitly marked as a binary file,
this warning was not issued, even though without -Xours/theirs, we
cannot cleanly automerge such a path, which was inconsistent.
Move the warning code from ll_xdl_merge() to ll_binary_merge(), and
issue the message only when we cannot cleanly automerge.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-09-12 13:01:52 +04:00
|
|
|
path, name1, name2);
|
|
|
|
/* fallthru */
|
2012-09-09 08:27:19 +04:00
|
|
|
case XDL_MERGE_FAVOR_OURS:
|
|
|
|
stolen = src1;
|
|
|
|
break;
|
|
|
|
case XDL_MERGE_FAVOR_THEIRS:
|
|
|
|
stolen = src2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-12-24 11:36:00 +03:00
|
|
|
|
|
|
|
result->ptr = stolen->ptr;
|
|
|
|
result->size = stolen->size;
|
|
|
|
stolen->ptr = NULL;
|
2012-09-09 08:27:19 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* With -Xtheirs or -Xours, we have cleanly merged;
|
|
|
|
* otherwise we got a conflict.
|
|
|
|
*/
|
|
|
|
return (opts->variant ? 0 : 1);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
|
|
|
|
mmbuffer_t *result,
|
2009-07-02 00:18:04 +04:00
|
|
|
const char *path,
|
2010-03-21 03:38:58 +03:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 11:36:00 +03:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 09:49:53 +04:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
2010-01-17 08:01:28 +03:00
|
|
|
xmparam_t xmp;
|
2010-08-26 09:49:53 +04:00
|
|
|
assert(opts);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
xdiff: reject files larger than ~1GB
The xdiff code is not prepared to handle extremely large
files. It uses "int" in many places, which can overflow if
we have a very large number of lines or even bytes in our
input files. This can cause us to produce incorrect diffs,
with no indication that the output is wrong. Or worse, we
may even underallocate a buffer whose size is the result of
an overflowing addition.
We're much better off to tell the user that we cannot diff
or merge such a large file. This patch covers both cases,
but in slightly different ways:
1. For merging, we notice the large file and cleanly fall
back to a binary merge (which is effectively "we cannot
merge this").
2. For diffing, we make the binary/text distinction much
earlier, and in many different places. For this case,
we'll use the xdi_diff as our choke point, and reject
any diff there before it hits the xdiff code.
This means in most cases we'll die() immediately after.
That's not ideal, but in practice we shouldn't
generally hit this code path unless the user is trying
to do something tricky. We already consider files
larger than core.bigfilethreshold to be binary, so this
code would only kick in when that is circumvented
(either by bumping that value, or by using a
.gitattribute to mark a file as diffable).
In other words, we can avoid being "nice" here, because
there is already nice code that tries to do the right
thing. We are adding the suspenders to the nice code's
belt, so notice when it has been worked around (both to
protect the user from malicious inputs, and because it
is better to die() than generate bogus output).
The maximum size was chosen after experimenting with feeding
large files to the xdiff code. It's just under a gigabyte,
which leaves room for two obvious cases:
- a diff3 merge conflict result on files of maximum size X
could be 3*X plus the size of the markers, which would
still be only about 3G, which fits in a 32-bit int.
- some of the diff code allocates arrays of one int per
record. Even if each file consists only of blank lines,
then a file smaller than 1G will have fewer than 1G
records, and therefore the int array will fit in 4G.
Since the limit is arbitrary anyway, I chose to go under a
gigabyte, to leave a safety margin (e.g., we would not want
to overflow by allocating "(records + 1) * sizeof(int)" or
similar.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 02:12:45 +03:00
|
|
|
if (orig->size > MAX_XDIFF_SIZE ||
|
|
|
|
src1->size > MAX_XDIFF_SIZE ||
|
|
|
|
src2->size > MAX_XDIFF_SIZE ||
|
|
|
|
buffer_is_binary(orig->ptr, orig->size) ||
|
2007-12-24 11:36:00 +03:00
|
|
|
buffer_is_binary(src1->ptr, src1->size) ||
|
|
|
|
buffer_is_binary(src2->ptr, src2->size)) {
|
|
|
|
return ll_binary_merge(drv_unused, result,
|
2009-07-02 00:18:04 +04:00
|
|
|
path,
|
2010-03-21 03:38:58 +03:00
|
|
|
orig, orig_name,
|
|
|
|
src1, name1,
|
2007-12-24 11:36:00 +03:00
|
|
|
src2, name2,
|
2010-08-26 09:49:53 +04:00
|
|
|
opts, marker_size);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
|
|
|
|
2010-01-17 08:01:28 +03:00
|
|
|
memset(&xmp, 0, sizeof(xmp));
|
2010-03-02 00:46:26 +03:00
|
|
|
xmp.level = XDL_MERGE_ZEALOUS;
|
2010-08-26 09:49:53 +04:00
|
|
|
xmp.favor = opts->variant;
|
2010-08-26 09:50:45 +04:00
|
|
|
xmp.xpp.flags = opts->xdl_opts;
|
2008-08-29 21:59:16 +04:00
|
|
|
if (git_xmerge_style >= 0)
|
2010-03-02 00:46:26 +03:00
|
|
|
xmp.style = git_xmerge_style;
|
2010-01-16 09:37:32 +03:00
|
|
|
if (marker_size > 0)
|
|
|
|
xmp.marker_size = marker_size;
|
2010-03-21 03:38:58 +03:00
|
|
|
xmp.ancestor = orig_name;
|
2010-03-21 03:35:18 +03:00
|
|
|
xmp.file1 = name1;
|
|
|
|
xmp.file2 = name2;
|
|
|
|
return xdl_merge(orig, src1, src2, &xmp, result);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ll_union_merge(const struct ll_merge_driver *drv_unused,
|
|
|
|
mmbuffer_t *result,
|
|
|
|
const char *path_unused,
|
2010-03-21 03:38:58 +03:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 11:36:00 +03:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 09:49:53 +04:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
2010-03-02 00:46:25 +03:00
|
|
|
/* Use union favor */
|
2010-08-26 09:49:53 +04:00
|
|
|
struct ll_merge_options o;
|
|
|
|
assert(opts);
|
|
|
|
o = *opts;
|
|
|
|
o.variant = XDL_MERGE_FAVOR_UNION;
|
2010-03-02 00:46:25 +03:00
|
|
|
return ll_xdl_merge(drv_unused, result, path_unused,
|
2010-03-21 03:38:58 +03:00
|
|
|
orig, NULL, src1, NULL, src2, NULL,
|
2010-08-26 09:49:53 +04:00
|
|
|
&o, marker_size);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#define LL_BINARY_MERGE 0
|
|
|
|
#define LL_TEXT_MERGE 1
|
|
|
|
#define LL_UNION_MERGE 2
|
|
|
|
static struct ll_merge_driver ll_merge_drv[] = {
|
|
|
|
{ "binary", "built-in binary merge", ll_binary_merge },
|
|
|
|
{ "text", "built-in 3-way text merge", ll_xdl_merge },
|
|
|
|
{ "union", "built-in union merge", ll_union_merge },
|
|
|
|
};
|
|
|
|
|
2015-09-25 00:06:08 +03:00
|
|
|
static void create_temp(mmfile_t *src, char *path, size_t len)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
2015-09-25 00:06:08 +03:00
|
|
|
xsnprintf(path, len, ".merge_file_XXXXXX");
|
2007-12-24 11:36:00 +03:00
|
|
|
fd = xmkstemp(path);
|
|
|
|
if (write_in_full(fd, src->ptr, src->size) != src->size)
|
2009-06-27 19:58:47 +04:00
|
|
|
die_errno("unable to write temp-file");
|
2007-12-24 11:36:00 +03:00
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* User defined low-level merge driver support.
|
|
|
|
*/
|
|
|
|
static int ll_ext_merge(const struct ll_merge_driver *fn,
|
|
|
|
mmbuffer_t *result,
|
|
|
|
const char *path,
|
2010-03-21 03:38:58 +03:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 11:36:00 +03:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 09:49:53 +04:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
2010-01-16 09:37:32 +03:00
|
|
|
char temp[4][50];
|
2008-11-23 02:13:00 +03:00
|
|
|
struct strbuf cmd = STRBUF_INIT;
|
2015-06-05 01:10:29 +03:00
|
|
|
struct strbuf_expand_dict_entry dict[6];
|
|
|
|
struct strbuf path_sq = STRBUF_INIT;
|
2009-12-30 13:53:57 +03:00
|
|
|
const char *args[] = { NULL, NULL };
|
2007-12-24 11:36:00 +03:00
|
|
|
int status, fd, i;
|
|
|
|
struct stat st;
|
2010-08-26 09:49:53 +04:00
|
|
|
assert(opts);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
2015-06-05 01:10:29 +03:00
|
|
|
sq_quote_buf(&path_sq, path);
|
2010-05-14 13:31:33 +04:00
|
|
|
dict[0].placeholder = "O"; dict[0].value = temp[0];
|
|
|
|
dict[1].placeholder = "A"; dict[1].value = temp[1];
|
|
|
|
dict[2].placeholder = "B"; dict[2].value = temp[2];
|
|
|
|
dict[3].placeholder = "L"; dict[3].value = temp[3];
|
2015-06-05 01:10:29 +03:00
|
|
|
dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
|
|
|
|
dict[5].placeholder = NULL; dict[5].value = NULL;
|
2010-05-14 13:31:33 +04:00
|
|
|
|
2007-12-24 11:36:00 +03:00
|
|
|
if (fn->cmdline == NULL)
|
|
|
|
die("custom merge driver %s lacks command line.", fn->name);
|
|
|
|
|
|
|
|
result->ptr = NULL;
|
|
|
|
result->size = 0;
|
2015-09-25 00:06:08 +03:00
|
|
|
create_temp(orig, temp[0], sizeof(temp[0]));
|
|
|
|
create_temp(src1, temp[1], sizeof(temp[1]));
|
|
|
|
create_temp(src2, temp[2], sizeof(temp[2]));
|
|
|
|
xsnprintf(temp[3], sizeof(temp[3]), "%d", marker_size);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
2008-11-23 02:13:00 +03:00
|
|
|
strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
2009-12-30 13:53:57 +03:00
|
|
|
args[0] = cmd.buf;
|
|
|
|
status = run_command_v_opt(args, RUN_USING_SHELL);
|
2007-12-24 11:36:00 +03:00
|
|
|
fd = open(temp[1], O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
goto bad;
|
|
|
|
if (fstat(fd, &st))
|
|
|
|
goto close_bad;
|
|
|
|
result->size = st.st_size;
|
|
|
|
result->ptr = xmalloc(result->size + 1);
|
|
|
|
if (read_in_full(fd, result->ptr, result->size) != result->size) {
|
|
|
|
free(result->ptr);
|
|
|
|
result->ptr = NULL;
|
|
|
|
result->size = 0;
|
|
|
|
}
|
|
|
|
close_bad:
|
|
|
|
close(fd);
|
|
|
|
bad:
|
|
|
|
for (i = 0; i < 3; i++)
|
2009-04-30 01:22:56 +04:00
|
|
|
unlink_or_warn(temp[i]);
|
2008-11-23 02:13:00 +03:00
|
|
|
strbuf_release(&cmd);
|
2015-06-05 01:10:29 +03:00
|
|
|
strbuf_release(&path_sq);
|
2007-12-24 11:36:00 +03:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* merge.default and merge.driver configuration items
|
|
|
|
*/
|
|
|
|
static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
|
|
|
|
static const char *default_ll_merge;
|
|
|
|
|
2008-05-14 21:46:53 +04:00
|
|
|
static int read_merge_config(const char *var, const char *value, void *cb)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
|
|
|
struct ll_merge_driver *fn;
|
2013-01-23 10:24:23 +04:00
|
|
|
const char *key, *name;
|
2007-12-24 11:36:00 +03:00
|
|
|
int namelen;
|
|
|
|
|
ll-merge.c: refactor `read_merge_config()` to use `git_config_string()`
There is one slight behavior change, previously "merge.default"
silently ignored a NULL value and didn't raise any error. But,
in the same function, all other values raise an error on a NULL
value. So to conform with other call sites in Git, a NULL value
for "merge.default" raises an error.
The the new config-set API is not very useful here, because much of
the function is dedicated to processing "merge.<name>.variable",
which the new API does not handle well. If it were for variables
like, "merge.summary", "merge.tool", and "merge.verbosity", we could
use the new API.
Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
Reviewed-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-08-13 16:43:04 +04:00
|
|
|
if (!strcmp(var, "merge.default"))
|
|
|
|
return git_config_string(&default_ll_merge, var, value);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We are not interested in anything but "merge.<name>.variable";
|
|
|
|
* especially, we do not want to look at variables such as
|
|
|
|
* "merge.summary", "merge.tool", and "merge.verbosity".
|
|
|
|
*/
|
2013-01-23 10:24:23 +04:00
|
|
|
if (parse_config_key(var, "merge", &name, &namelen, &key) < 0 || !name)
|
2007-12-24 11:36:00 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find existing one as we might be processing merge.<name>.var2
|
|
|
|
* after seeing merge.<name>.var1.
|
|
|
|
*/
|
|
|
|
for (fn = ll_user_merge; fn; fn = fn->next)
|
|
|
|
if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
|
|
|
|
break;
|
|
|
|
if (!fn) {
|
|
|
|
fn = xcalloc(1, sizeof(struct ll_merge_driver));
|
|
|
|
fn->name = xmemdupz(name, namelen);
|
|
|
|
fn->fn = ll_ext_merge;
|
|
|
|
*ll_user_merge_tail = fn;
|
|
|
|
ll_user_merge_tail = &(fn->next);
|
|
|
|
}
|
|
|
|
|
ll-merge.c: refactor `read_merge_config()` to use `git_config_string()`
There is one slight behavior change, previously "merge.default"
silently ignored a NULL value and didn't raise any error. But,
in the same function, all other values raise an error on a NULL
value. So to conform with other call sites in Git, a NULL value
for "merge.default" raises an error.
The the new config-set API is not very useful here, because much of
the function is dedicated to processing "merge.<name>.variable",
which the new API does not handle well. If it were for variables
like, "merge.summary", "merge.tool", and "merge.verbosity", we could
use the new API.
Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
Reviewed-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-08-13 16:43:04 +04:00
|
|
|
if (!strcmp("name", key))
|
|
|
|
return git_config_string(&fn->description, var, value);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
2013-01-23 10:24:23 +04:00
|
|
|
if (!strcmp("driver", key)) {
|
2007-12-24 11:36:00 +03:00
|
|
|
if (!value)
|
|
|
|
return error("%s: lacks value", var);
|
|
|
|
/*
|
|
|
|
* merge.<name>.driver specifies the command line:
|
|
|
|
*
|
|
|
|
* command-line
|
|
|
|
*
|
|
|
|
* The command-line will be interpolated with the following
|
|
|
|
* tokens and is given to the shell:
|
|
|
|
*
|
|
|
|
* %O - temporary file name for the merge base.
|
|
|
|
* %A - temporary file name for our version.
|
|
|
|
* %B - temporary file name for the other branches' version.
|
2010-01-16 09:37:32 +03:00
|
|
|
* %L - conflict marker length
|
2015-06-05 01:10:29 +03:00
|
|
|
* %P - the original path (safely quoted for the shell)
|
2007-12-24 11:36:00 +03:00
|
|
|
*
|
|
|
|
* The external merge driver should write the results in the
|
|
|
|
* file named by %A, and signal that it has done with zero exit
|
|
|
|
* status.
|
|
|
|
*/
|
2009-06-14 23:47:54 +04:00
|
|
|
fn->cmdline = xstrdup(value);
|
2007-12-24 11:36:00 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ll-merge.c: refactor `read_merge_config()` to use `git_config_string()`
There is one slight behavior change, previously "merge.default"
silently ignored a NULL value and didn't raise any error. But,
in the same function, all other values raise an error on a NULL
value. So to conform with other call sites in Git, a NULL value
for "merge.default" raises an error.
The the new config-set API is not very useful here, because much of
the function is dedicated to processing "merge.<name>.variable",
which the new API does not handle well. If it were for variables
like, "merge.summary", "merge.tool", and "merge.verbosity", we could
use the new API.
Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
Reviewed-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-08-13 16:43:04 +04:00
|
|
|
if (!strcmp("recursive", key))
|
|
|
|
return git_config_string(&fn->recursive, var, value);
|
2007-12-24 11:36:00 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void initialize_ll_merge(void)
|
|
|
|
{
|
|
|
|
if (ll_user_merge_tail)
|
|
|
|
return;
|
|
|
|
ll_user_merge_tail = &ll_user_merge;
|
2008-05-14 21:46:53 +04:00
|
|
|
git_config(read_merge_config, NULL);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
|
|
|
|
{
|
|
|
|
struct ll_merge_driver *fn;
|
|
|
|
const char *name;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
initialize_ll_merge();
|
|
|
|
|
|
|
|
if (ATTR_TRUE(merge_attr))
|
|
|
|
return &ll_merge_drv[LL_TEXT_MERGE];
|
|
|
|
else if (ATTR_FALSE(merge_attr))
|
|
|
|
return &ll_merge_drv[LL_BINARY_MERGE];
|
|
|
|
else if (ATTR_UNSET(merge_attr)) {
|
|
|
|
if (!default_ll_merge)
|
|
|
|
return &ll_merge_drv[LL_TEXT_MERGE];
|
|
|
|
else
|
|
|
|
name = default_ll_merge;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
name = merge_attr;
|
|
|
|
|
|
|
|
for (fn = ll_user_merge; fn; fn = fn->next)
|
|
|
|
if (!strcmp(fn->name, name))
|
|
|
|
return fn;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
|
|
|
|
if (!strcmp(ll_merge_drv[i].name, name))
|
|
|
|
return &ll_merge_drv[i];
|
|
|
|
|
|
|
|
/* default to the 3-way */
|
|
|
|
return &ll_merge_drv[LL_TEXT_MERGE];
|
|
|
|
}
|
|
|
|
|
2010-01-16 09:37:32 +03:00
|
|
|
static int git_path_check_merge(const char *path, struct git_attr_check check[2])
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
2010-01-16 09:37:32 +03:00
|
|
|
if (!check[0].attr) {
|
|
|
|
check[0].attr = git_attr("merge");
|
|
|
|
check[1].attr = git_attr("conflict-marker-size");
|
|
|
|
}
|
2011-08-04 08:36:33 +04:00
|
|
|
return git_check_attr(path, 2, check);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
|
|
|
|
2010-07-02 23:20:47 +04:00
|
|
|
static void normalize_file(mmfile_t *mm, const char *path)
|
|
|
|
{
|
|
|
|
struct strbuf strbuf = STRBUF_INIT;
|
|
|
|
if (renormalize_buffer(path, mm->ptr, mm->size, &strbuf)) {
|
|
|
|
free(mm->ptr);
|
|
|
|
mm->size = strbuf.len;
|
|
|
|
mm->ptr = strbuf_detach(&strbuf, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-24 11:36:00 +03:00
|
|
|
int ll_merge(mmbuffer_t *result_buf,
|
|
|
|
const char *path,
|
2010-03-21 03:38:58 +03:00
|
|
|
mmfile_t *ancestor, const char *ancestor_label,
|
2007-12-24 11:36:00 +03:00
|
|
|
mmfile_t *ours, const char *our_label,
|
|
|
|
mmfile_t *theirs, const char *their_label,
|
2010-08-26 09:49:53 +04:00
|
|
|
const struct ll_merge_options *opts)
|
2007-12-24 11:36:00 +03:00
|
|
|
{
|
2010-01-16 09:37:32 +03:00
|
|
|
static struct git_attr_check check[2];
|
2011-01-16 04:08:42 +03:00
|
|
|
static const struct ll_merge_options default_opts;
|
2010-01-16 09:37:32 +03:00
|
|
|
const char *ll_driver_name = NULL;
|
|
|
|
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
2007-12-24 11:36:00 +03:00
|
|
|
const struct ll_merge_driver *driver;
|
|
|
|
|
2011-01-16 04:08:42 +03:00
|
|
|
if (!opts)
|
|
|
|
opts = &default_opts;
|
2010-08-26 09:49:53 +04:00
|
|
|
|
|
|
|
if (opts->renormalize) {
|
2010-07-02 23:20:47 +04:00
|
|
|
normalize_file(ancestor, path);
|
|
|
|
normalize_file(ours, path);
|
|
|
|
normalize_file(theirs, path);
|
|
|
|
}
|
2010-01-16 09:37:32 +03:00
|
|
|
if (!git_path_check_merge(path, check)) {
|
|
|
|
ll_driver_name = check[0].value;
|
|
|
|
if (check[1].value) {
|
|
|
|
marker_size = atoi(check[1].value);
|
|
|
|
if (marker_size <= 0)
|
|
|
|
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
|
|
|
}
|
|
|
|
}
|
2007-12-24 11:36:00 +03:00
|
|
|
driver = find_ll_merge_driver(ll_driver_name);
|
2010-08-26 09:49:53 +04:00
|
|
|
if (opts->virtual_ancestor && driver->recursive)
|
2007-12-24 11:36:00 +03:00
|
|
|
driver = find_ll_merge_driver(driver->recursive);
|
2010-03-21 03:38:58 +03:00
|
|
|
return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
|
2010-01-16 09:37:32 +03:00
|
|
|
ours, our_label, theirs, their_label,
|
2010-08-26 09:49:53 +04:00
|
|
|
opts, marker_size);
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|
2010-01-17 10:28:46 +03:00
|
|
|
|
|
|
|
int ll_merge_marker_size(const char *path)
|
|
|
|
{
|
|
|
|
static struct git_attr_check check;
|
|
|
|
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
|
|
|
|
|
|
|
if (!check.attr)
|
|
|
|
check.attr = git_attr("conflict-marker-size");
|
2011-08-04 08:36:33 +04:00
|
|
|
if (!git_check_attr(path, 1, &check) && check.value) {
|
2010-01-17 10:28:46 +03:00
|
|
|
marker_size = atoi(check.value);
|
|
|
|
if (marker_size <= 0)
|
|
|
|
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
|
|
|
}
|
|
|
|
return marker_size;
|
2007-12-24 11:36:00 +03:00
|
|
|
}
|