mirror of
https://github.com/git/git.git
synced 2024-11-30 21:44:02 +08:00
e4da43b1f0
The prefix_filename() function returns a pointer to static storage, which makes it easy to use dangerously. We already fixed one buggy caller in hash-object recently, and the calls in apply.c are suspicious (I didn't dig in enough to confirm that there is a bug, but we call the function once in apply_all_patches() and then again indirectly from parse_chunk()). Let's make it harder to get wrong by allocating the return value. For simplicity, we'll do this even when the prefix is empty (and we could just return the original file pointer). That will cause us to allocate sometimes when we wouldn't otherwise need to, but this function isn't called in performance critical code-paths (and it already _might_ allocate on any given call, so a caller that cares about performance is questionable anyway). The downside is that the callers need to remember to free() the result to avoid leaking. Most of them already used xstrdup() on the result, so we know they are OK. The remainder have been converted to use free() as appropriate. I considered retaining a prefix_filename_unsafe() for cases where we know the static lifetime is OK (and handling the cleanup is awkward). This is only a handful of cases, though, and it's not worth the mental energy in worrying about whether the "unsafe" variant is OK to use in any situation. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
116 lines
3.1 KiB
C
116 lines
3.1 KiB
C
#include "builtin.h"
|
|
#include "cache.h"
|
|
#include "xdiff/xdiff.h"
|
|
#include "xdiff-interface.h"
|
|
#include "parse-options.h"
|
|
|
|
static const char *const merge_file_usage[] = {
|
|
N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
|
|
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;
|
|
|
|
if (label_count >= 3)
|
|
return error("too many labels on the command line");
|
|
names[label_count++] = arg;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_merge_file(int argc, const char **argv, const char *prefix)
|
|
{
|
|
const char *names[3] = { NULL, NULL, NULL };
|
|
mmfile_t mmfs[3];
|
|
mmbuffer_t result = {NULL, 0};
|
|
xmparam_t xmp = {{0}};
|
|
int ret = 0, i = 0, to_stdout = 0;
|
|
int quiet = 0;
|
|
struct option options[] = {
|
|
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
|
|
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
|
|
OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
|
|
XDL_MERGE_FAVOR_OURS),
|
|
OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
|
|
XDL_MERGE_FAVOR_THEIRS),
|
|
OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
|
|
XDL_MERGE_FAVOR_UNION),
|
|
OPT_INTEGER(0, "marker-size", &xmp.marker_size,
|
|
N_("for conflicts, use this marker size")),
|
|
OPT__QUIET(&quiet, N_("do not warn about conflicts")),
|
|
OPT_CALLBACK('L', NULL, names, N_("name"),
|
|
N_("set labels for file1/orig-file/file2"), &label_cb),
|
|
OPT_END(),
|
|
};
|
|
|
|
xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
|
|
xmp.style = 0;
|
|
xmp.favor = 0;
|
|
|
|
if (startup_info->have_repository) {
|
|
/* Read the configuration file */
|
|
git_config(git_xmerge_config, NULL);
|
|
if (0 <= git_xmerge_style)
|
|
xmp.style = git_xmerge_style;
|
|
}
|
|
|
|
argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
|
|
if (argc != 3)
|
|
usage_with_options(merge_file_usage, options);
|
|
if (quiet) {
|
|
if (!freopen("/dev/null", "w", stderr))
|
|
return error_errno("failed to redirect stderr to /dev/null");
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
char *fname;
|
|
int ret;
|
|
|
|
if (!names[i])
|
|
names[i] = argv[i];
|
|
|
|
fname = prefix_filename(prefix, argv[i]);
|
|
ret = read_mmfile(mmfs + i, fname);
|
|
free(fname);
|
|
if (ret)
|
|
return -1;
|
|
|
|
if (mmfs[i].size > MAX_XDIFF_SIZE ||
|
|
buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
|
|
return error("Cannot merge binary files: %s",
|
|
argv[i]);
|
|
}
|
|
|
|
xmp.ancestor = names[1];
|
|
xmp.file1 = names[0];
|
|
xmp.file2 = names[2];
|
|
ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
free(mmfs[i].ptr);
|
|
|
|
if (ret >= 0) {
|
|
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(result.ptr);
|
|
free(fpath);
|
|
}
|
|
|
|
if (ret > 127)
|
|
ret = 127;
|
|
|
|
return ret;
|
|
}
|