2007-12-24 08:36:00 +00:00
|
|
|
/*
|
|
|
|
* Low level 3-way in-core file merge.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2007 Junio C Hamano
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cache.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2007-12-24 08:36:00 +00:00
|
|
|
#include "attr.h"
|
|
|
|
#include "xdiff-interface.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
#include "ll-merge.h"
|
2015-06-04 22:10:29 +00:00
|
|
|
#include "quote.h"
|
2007-12-24 08:36:00 +00:00
|
|
|
|
|
|
|
struct ll_merge_driver;
|
|
|
|
|
|
|
|
typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
|
|
|
|
mmbuffer_t *result,
|
|
|
|
const char *path,
|
2010-03-21 00:38:58 +00:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 08:36:00 +00:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 05:49:53 +00:00
|
|
|
const struct ll_merge_options *opts,
|
2010-01-16 06:37:32 +00:00
|
|
|
int marker_size);
|
2007-12-24 08:36:00 +00: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;
|
|
|
|
};
|
|
|
|
|
2019-09-02 22:39:44 +00:00
|
|
|
static struct attr_check *merge_attributes;
|
|
|
|
static struct attr_check *load_merge_attributes(void)
|
|
|
|
{
|
|
|
|
if (!merge_attributes)
|
|
|
|
merge_attributes = attr_check_initl("merge", "conflict-marker-size", NULL);
|
|
|
|
return merge_attributes;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset_merge_attributes(void)
|
|
|
|
{
|
|
|
|
attr_check_free(merge_attributes);
|
|
|
|
merge_attributes = NULL;
|
|
|
|
}
|
|
|
|
|
2007-12-24 08:36:00 +00:00
|
|
|
/*
|
|
|
|
* 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 09:01:52 +00:00
|
|
|
const char *path,
|
2010-03-21 00:38:58 +00:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 08:36:00 +00:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 05:49:53 +00:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
2010-08-26 05:49:53 +00:00
|
|
|
mmfile_t *stolen;
|
|
|
|
assert(opts);
|
|
|
|
|
2007-12-24 08:36:00 +00:00
|
|
|
/*
|
2016-04-14 22:12:15 +00:00
|
|
|
* The tentative merge result is the common ancestor for an
|
|
|
|
* internal merge. For the final merge, it is "ours" by
|
|
|
|
* default but -Xours/-Xtheirs can tweak the choice.
|
2007-12-24 08:36:00 +00:00
|
|
|
*/
|
2012-09-09 04:27:19 +00:00
|
|
|
if (opts->virtual_ancestor) {
|
|
|
|
stolen = orig;
|
|
|
|
} else {
|
|
|
|
switch (opts->variant) {
|
|
|
|
default:
|
2012-09-15 04:39:56 +00: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 09:01:52 +00:00
|
|
|
path, name1, name2);
|
|
|
|
/* fallthru */
|
2012-09-09 04:27:19 +00:00
|
|
|
case XDL_MERGE_FAVOR_OURS:
|
|
|
|
stolen = src1;
|
|
|
|
break;
|
|
|
|
case XDL_MERGE_FAVOR_THEIRS:
|
|
|
|
stolen = src2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-12-24 08:36:00 +00:00
|
|
|
|
|
|
|
result->ptr = stolen->ptr;
|
|
|
|
result->size = stolen->size;
|
|
|
|
stolen->ptr = NULL;
|
2012-09-09 04:27:19 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* With -Xtheirs or -Xours, we have cleanly merged;
|
|
|
|
* otherwise we got a conflict.
|
|
|
|
*/
|
|
|
|
return (opts->variant ? 0 : 1);
|
2007-12-24 08:36:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
|
|
|
|
mmbuffer_t *result,
|
2009-07-01 20:18:04 +00:00
|
|
|
const char *path,
|
2010-03-21 00:38:58 +00:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 08:36:00 +00:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 05:49:53 +00:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
2010-01-17 05:01:28 +00:00
|
|
|
xmparam_t xmp;
|
2010-08-26 05:49:53 +00:00
|
|
|
assert(opts);
|
2007-12-24 08:36:00 +00: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-24 23:12:45 +00: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 08:36:00 +00:00
|
|
|
buffer_is_binary(src1->ptr, src1->size) ||
|
|
|
|
buffer_is_binary(src2->ptr, src2->size)) {
|
|
|
|
return ll_binary_merge(drv_unused, result,
|
2009-07-01 20:18:04 +00:00
|
|
|
path,
|
2010-03-21 00:38:58 +00:00
|
|
|
orig, orig_name,
|
|
|
|
src1, name1,
|
2007-12-24 08:36:00 +00:00
|
|
|
src2, name2,
|
2010-08-26 05:49:53 +00:00
|
|
|
opts, marker_size);
|
2007-12-24 08:36:00 +00:00
|
|
|
}
|
|
|
|
|
2010-01-17 05:01:28 +00:00
|
|
|
memset(&xmp, 0, sizeof(xmp));
|
2010-03-01 21:46:26 +00:00
|
|
|
xmp.level = XDL_MERGE_ZEALOUS;
|
2010-08-26 05:49:53 +00:00
|
|
|
xmp.favor = opts->variant;
|
2010-08-26 05:50:45 +00:00
|
|
|
xmp.xpp.flags = opts->xdl_opts;
|
2008-08-29 17:59:16 +00:00
|
|
|
if (git_xmerge_style >= 0)
|
2010-03-01 21:46:26 +00:00
|
|
|
xmp.style = git_xmerge_style;
|
2010-01-16 06:37:32 +00:00
|
|
|
if (marker_size > 0)
|
|
|
|
xmp.marker_size = marker_size;
|
2010-03-21 00:38:58 +00:00
|
|
|
xmp.ancestor = orig_name;
|
2010-03-21 00:35:18 +00:00
|
|
|
xmp.file1 = name1;
|
|
|
|
xmp.file2 = name2;
|
|
|
|
return xdl_merge(orig, src1, src2, &xmp, result);
|
2007-12-24 08:36:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ll_union_merge(const struct ll_merge_driver *drv_unused,
|
|
|
|
mmbuffer_t *result,
|
|
|
|
const char *path_unused,
|
2010-03-21 00:38:58 +00:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 08:36:00 +00:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 05:49:53 +00:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
2010-03-01 21:46:25 +00:00
|
|
|
/* Use union favor */
|
2010-08-26 05:49:53 +00:00
|
|
|
struct ll_merge_options o;
|
|
|
|
assert(opts);
|
|
|
|
o = *opts;
|
|
|
|
o.variant = XDL_MERGE_FAVOR_UNION;
|
2010-03-01 21:46:25 +00:00
|
|
|
return ll_xdl_merge(drv_unused, result, path_unused,
|
2010-03-21 00:38:58 +00:00
|
|
|
orig, NULL, src1, NULL, src2, NULL,
|
2010-08-26 05:49:53 +00:00
|
|
|
&o, marker_size);
|
2007-12-24 08:36:00 +00: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-24 21:06:08 +00:00
|
|
|
static void create_temp(mmfile_t *src, char *path, size_t len)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
2015-09-24 21:06:08 +00:00
|
|
|
xsnprintf(path, len, ".merge_file_XXXXXX");
|
2007-12-24 08:36:00 +00:00
|
|
|
fd = xmkstemp(path);
|
avoid "write_in_full(fd, buf, len) != len" pattern
The return value of write_in_full() is either "-1", or the
requested number of bytes[1]. If we make a partial write
before seeing an error, we still return -1, not a partial
value. This goes back to f6aa66cb95 (write_in_full: really
write in full or return error on disk full., 2007-01-11).
So checking anything except "was the return value negative"
is pointless. And there are a couple of reasons not to do
so:
1. It can do a funny signed/unsigned comparison. If your
"len" is signed (e.g., a size_t) then the compiler will
promote the "-1" to its unsigned variant.
This works out for "!= len" (unless you really were
trying to write the maximum size_t bytes), but is a
bug if you check "< len" (an example of which was fixed
recently in config.c).
We should avoid promoting the mental model that you
need to check the length at all, so that new sites are
not tempted to copy us.
2. Checking for a negative value is shorter to type,
especially when the length is an expression.
3. Linus says so. In d34cf19b89 (Clean up write_in_full()
users, 2007-01-11), right after the write_in_full()
semantics were changed, he wrote:
I really wish every "write_in_full()" user would just
check against "<0" now, but this fixes the nasty and
stupid ones.
Appeals to authority aside, this makes it clear that
writing it this way does not have an intentional
benefit. It's a historical curiosity that we never
bothered to clean up (and which was undoubtedly
cargo-culted into new sites).
So let's convert these obviously-correct cases (this
includes write_str_in_full(), which is just a wrapper for
write_in_full()).
[1] A careful reader may notice there is one way that
write_in_full() can return a different value. If we ask
write() to write N bytes and get a return value that is
_larger_ than N, we could return a larger total. But
besides the fact that this would imply a totally broken
version of write(), it would already invoke undefined
behavior. Our internal remaining counter is an unsigned
size_t, which means that subtracting too many byte will
wrap it around to a very large number. So we'll instantly
begin reading off the end of the buffer, trying to write
gigabytes (or petabytes) of data.
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-13 17:16:03 +00:00
|
|
|
if (write_in_full(fd, src->ptr, src->size) < 0)
|
2009-06-27 15:58:47 +00:00
|
|
|
die_errno("unable to write temp-file");
|
2007-12-24 08:36:00 +00: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 00:38:58 +00:00
|
|
|
mmfile_t *orig, const char *orig_name,
|
2007-12-24 08:36:00 +00:00
|
|
|
mmfile_t *src1, const char *name1,
|
|
|
|
mmfile_t *src2, const char *name2,
|
2010-08-26 05:49:53 +00:00
|
|
|
const struct ll_merge_options *opts,
|
|
|
|
int marker_size)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
2010-01-16 06:37:32 +00:00
|
|
|
char temp[4][50];
|
2008-11-22 23:13:00 +00:00
|
|
|
struct strbuf cmd = STRBUF_INIT;
|
2015-06-04 22:10:29 +00:00
|
|
|
struct strbuf_expand_dict_entry dict[6];
|
|
|
|
struct strbuf path_sq = STRBUF_INIT;
|
2009-12-30 10:53:57 +00:00
|
|
|
const char *args[] = { NULL, NULL };
|
2007-12-24 08:36:00 +00:00
|
|
|
int status, fd, i;
|
|
|
|
struct stat st;
|
2010-08-26 05:49:53 +00:00
|
|
|
assert(opts);
|
2007-12-24 08:36:00 +00:00
|
|
|
|
2015-06-04 22:10:29 +00:00
|
|
|
sq_quote_buf(&path_sq, path);
|
2010-05-14 09:31:33 +00: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-04 22:10:29 +00:00
|
|
|
dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
|
|
|
|
dict[5].placeholder = NULL; dict[5].value = NULL;
|
2010-05-14 09:31:33 +00:00
|
|
|
|
2007-12-24 08:36:00 +00:00
|
|
|
if (fn->cmdline == NULL)
|
|
|
|
die("custom merge driver %s lacks command line.", fn->name);
|
|
|
|
|
|
|
|
result->ptr = NULL;
|
|
|
|
result->size = 0;
|
2015-09-24 21:06:08 +00: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 08:36:00 +00:00
|
|
|
|
2008-11-22 23:13:00 +00:00
|
|
|
strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
|
2007-12-24 08:36:00 +00:00
|
|
|
|
2009-12-30 10:53:57 +00:00
|
|
|
args[0] = cmd.buf;
|
|
|
|
status = run_command_v_opt(args, RUN_USING_SHELL);
|
2007-12-24 08:36:00 +00:00
|
|
|
fd = open(temp[1], O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
goto bad;
|
|
|
|
if (fstat(fd, &st))
|
|
|
|
goto close_bad;
|
|
|
|
result->size = st.st_size;
|
2016-02-22 22:44:28 +00:00
|
|
|
result->ptr = xmallocz(result->size);
|
2007-12-24 08:36:00 +00:00
|
|
|
if (read_in_full(fd, result->ptr, result->size) != result->size) {
|
2017-06-15 23:15:48 +00:00
|
|
|
FREE_AND_NULL(result->ptr);
|
2007-12-24 08:36:00 +00:00
|
|
|
result->size = 0;
|
|
|
|
}
|
|
|
|
close_bad:
|
|
|
|
close(fd);
|
|
|
|
bad:
|
|
|
|
for (i = 0; i < 3; i++)
|
2009-04-29 21:22:56 +00:00
|
|
|
unlink_or_warn(temp[i]);
|
2008-11-22 23:13:00 +00:00
|
|
|
strbuf_release(&cmd);
|
2015-06-04 22:10:29 +00:00
|
|
|
strbuf_release(&path_sq);
|
2007-12-24 08:36:00 +00: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 17:46:53 +00:00
|
|
|
static int read_merge_config(const char *var, const char *value, void *cb)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
|
|
|
struct ll_merge_driver *fn;
|
2013-01-23 06:24:23 +00:00
|
|
|
const char *key, *name;
|
2020-04-10 19:44:28 +00:00
|
|
|
size_t namelen;
|
2007-12-24 08:36:00 +00:00
|
|
|
|
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 12:43:04 +00:00
|
|
|
if (!strcmp(var, "merge.default"))
|
|
|
|
return git_config_string(&default_ll_merge, var, value);
|
2007-12-24 08:36:00 +00: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 06:24:23 +00:00
|
|
|
if (parse_config_key(var, "merge", &name, &namelen, &key) < 0 || !name)
|
2007-12-24 08:36:00 +00: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 12:43:04 +00:00
|
|
|
if (!strcmp("name", key))
|
|
|
|
return git_config_string(&fn->description, var, value);
|
2007-12-24 08:36:00 +00:00
|
|
|
|
2013-01-23 06:24:23 +00:00
|
|
|
if (!strcmp("driver", key)) {
|
2007-12-24 08:36:00 +00: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 06:37:32 +00:00
|
|
|
* %L - conflict marker length
|
2015-06-04 22:10:29 +00:00
|
|
|
* %P - the original path (safely quoted for the shell)
|
2007-12-24 08:36:00 +00: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 19:47:54 +00:00
|
|
|
fn->cmdline = xstrdup(value);
|
2007-12-24 08:36:00 +00: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 12:43:04 +00:00
|
|
|
if (!strcmp("recursive", key))
|
|
|
|
return git_config_string(&fn->recursive, var, value);
|
2007-12-24 08:36:00 +00: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 17:46:53 +00:00
|
|
|
git_config(read_merge_config, NULL);
|
2007-12-24 08:36:00 +00: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];
|
|
|
|
}
|
|
|
|
|
2018-09-21 15:57:27 +00:00
|
|
|
static void normalize_file(mmfile_t *mm, const char *path, struct index_state *istate)
|
2010-07-02 19:20:47 +00:00
|
|
|
{
|
|
|
|
struct strbuf strbuf = STRBUF_INIT;
|
2018-09-21 15:57:27 +00:00
|
|
|
if (renormalize_buffer(istate, path, mm->ptr, mm->size, &strbuf)) {
|
2010-07-02 19:20:47 +00:00
|
|
|
free(mm->ptr);
|
|
|
|
mm->size = strbuf.len;
|
|
|
|
mm->ptr = strbuf_detach(&strbuf, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-24 08:36:00 +00:00
|
|
|
int ll_merge(mmbuffer_t *result_buf,
|
|
|
|
const char *path,
|
2010-03-21 00:38:58 +00:00
|
|
|
mmfile_t *ancestor, const char *ancestor_label,
|
2007-12-24 08:36:00 +00:00
|
|
|
mmfile_t *ours, const char *our_label,
|
|
|
|
mmfile_t *theirs, const char *their_label,
|
2018-09-21 15:57:27 +00:00
|
|
|
struct index_state *istate,
|
2010-08-26 05:49:53 +00:00
|
|
|
const struct ll_merge_options *opts)
|
2007-12-24 08:36:00 +00:00
|
|
|
{
|
2019-09-02 22:39:44 +00:00
|
|
|
struct attr_check *check = load_merge_attributes();
|
2011-01-16 01:08:42 +00:00
|
|
|
static const struct ll_merge_options default_opts;
|
2010-01-16 06:37:32 +00:00
|
|
|
const char *ll_driver_name = NULL;
|
|
|
|
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
2007-12-24 08:36:00 +00:00
|
|
|
const struct ll_merge_driver *driver;
|
|
|
|
|
2011-01-16 01:08:42 +00:00
|
|
|
if (!opts)
|
|
|
|
opts = &default_opts;
|
2010-08-26 05:49:53 +00:00
|
|
|
|
|
|
|
if (opts->renormalize) {
|
2018-09-21 15:57:27 +00:00
|
|
|
normalize_file(ancestor, path, istate);
|
|
|
|
normalize_file(ours, path, istate);
|
|
|
|
normalize_file(theirs, path, istate);
|
2010-07-02 19:20:47 +00:00
|
|
|
}
|
2017-01-28 02:01:57 +00:00
|
|
|
|
2018-10-19 04:34:02 +00:00
|
|
|
git_check_attr(istate, path, check);
|
2018-09-12 19:32:02 +00:00
|
|
|
ll_driver_name = check->items[0].value;
|
|
|
|
if (check->items[1].value) {
|
|
|
|
marker_size = atoi(check->items[1].value);
|
|
|
|
if (marker_size <= 0)
|
|
|
|
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
2010-01-16 06:37:32 +00:00
|
|
|
}
|
2007-12-24 08:36:00 +00:00
|
|
|
driver = find_ll_merge_driver(ll_driver_name);
|
2016-04-14 22:35:09 +00:00
|
|
|
|
|
|
|
if (opts->virtual_ancestor) {
|
|
|
|
if (driver->recursive)
|
|
|
|
driver = find_ll_merge_driver(driver->recursive);
|
2018-11-08 04:40:24 +00:00
|
|
|
}
|
|
|
|
if (opts->extra_marker_size) {
|
|
|
|
marker_size += opts->extra_marker_size;
|
2016-04-14 22:35:09 +00:00
|
|
|
}
|
2010-03-21 00:38:58 +00:00
|
|
|
return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
|
2010-01-16 06:37:32 +00:00
|
|
|
ours, our_label, theirs, their_label,
|
2010-08-26 05:49:53 +00:00
|
|
|
opts, marker_size);
|
2007-12-24 08:36:00 +00:00
|
|
|
}
|
2010-01-17 07:28:46 +00:00
|
|
|
|
2018-09-21 15:57:27 +00:00
|
|
|
int ll_merge_marker_size(struct index_state *istate, const char *path)
|
2010-01-17 07:28:46 +00:00
|
|
|
{
|
2017-01-28 02:01:57 +00:00
|
|
|
static struct attr_check *check;
|
2010-01-17 07:28:46 +00:00
|
|
|
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
|
|
|
|
2017-01-28 02:01:57 +00:00
|
|
|
if (!check)
|
|
|
|
check = attr_check_initl("conflict-marker-size", NULL);
|
2018-10-19 04:34:02 +00:00
|
|
|
git_check_attr(istate, path, check);
|
2018-09-12 19:32:02 +00:00
|
|
|
if (check->items[0].value) {
|
2017-01-28 02:01:57 +00:00
|
|
|
marker_size = atoi(check->items[0].value);
|
2010-01-17 07:28:46 +00:00
|
|
|
if (marker_size <= 0)
|
|
|
|
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
|
|
|
|
}
|
|
|
|
return marker_size;
|
2007-12-24 08:36:00 +00:00
|
|
|
}
|