summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 73eb40e)
raw | patch | inline | side by side (parent: 73eb40e)
author | Avery Pennarun <apenwarr@gmail.com> | |
Thu, 26 Nov 2009 02:23:55 +0000 (21:23 -0500) | ||
committer | Junio C Hamano <gitster@pobox.com> | |
Mon, 18 Jan 2010 06:45:06 +0000 (22:45 -0800) |
Teach "-X <option>" command line argument to "git merge" that is passed to
strategy implementations. "ours" and "theirs" autoresolution introduced
by the previous commit can be asked to the recursive strategy.
Signed-off-by: Avery Pennarun <apenwarr@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
strategy implementations. "ours" and "theirs" autoresolution introduced
by the previous commit can be asked to the recursive strategy.
Signed-off-by: Avery Pennarun <apenwarr@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-merge-recursive.c | patch | blob | history | |
builtin-merge.c | patch | blob | history | |
contrib/examples/git-merge.sh | patch | blob | history | |
git-compat-util.h | patch | blob | history | |
git.c | patch | blob | history | |
ll-merge.c | patch | blob | history | |
ll-merge.h | patch | blob | history | |
merge-recursive.c | patch | blob | history | |
merge-recursive.h | patch | blob | history | |
strbuf.c | patch | blob | history | |
t/t6037-merge-ours-theirs.sh | [new file with mode: 0755] | patch | blob |
index 710674c6b2cccec4f1e0baa477a11d78cda72833..1a160d81277471ffb0af03712586d27be3198e6f 100644 (file)
init_merge_options(&o);
if (argv[0]) {
- int namelen = strlen(argv[0]);
- if (8 < namelen &&
- !strcmp(argv[0] + namelen - 8, "-subtree"))
- o.subtree_merge = 1;
+ if (!suffixcmp(argv[0], "-subtree"))
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
}
if (argc < 4)
usagef("%s <base>... -- <head> <remote> ...", argv[0]);
for (i = 1; i < argc; ++i) {
- if (!strcmp(argv[i], "--"))
- break;
+ const char *arg = argv[i];
+
+ if (!prefixcmp(arg, "--")) {
+ if (!arg[2])
+ break;
+ if (!strcmp(arg+2, "ours"))
+ o.recursive_variant = MERGE_RECURSIVE_OURS;
+ else if (!strcmp(arg+2, "theirs"))
+ o.recursive_variant = MERGE_RECURSIVE_THEIRS;
+ else if (!strcmp(arg+2, "subtree"))
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ else
+ die("Unknown option %s", arg);
+ continue;
+ }
if (bases_count < ARRAY_SIZE(bases)-1) {
unsigned char *sha = xmalloc(20);
if (get_sha1(argv[i], sha))
diff --git a/builtin-merge.c b/builtin-merge.c
index 855cf6545eaca06260a7e0868425140850a590d8..42f19e1fd31c10dbbcbd91013d1ba42c87ebcc90 100644 (file)
--- a/builtin-merge.c
+++ b/builtin-merge.c
static unsigned char head[20], stash[20];
static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
+static const char **xopts;
+static size_t xopts_nr, xopts_alloc;
static const char *branch;
static int verbosity;
return 0;
}
+static int option_parse_x(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset)
+ return 0;
+
+ ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+ xopts[xopts_nr++] = xstrdup(arg);
+ return 0;
+}
+
static int option_parse_n(const struct option *opt,
const char *arg, int unset)
{
"abort if fast-forward is not possible"),
OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
"merge strategy to use", option_parse_strategy),
+ OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
+ "option for selected merge strategy", option_parse_x),
OPT_CALLBACK('m', "message", &merge_msg, "message",
"message to be used for the merge commit (if any)",
option_parse_message),
const char *head_arg)
{
const char **args;
- int i = 0, ret;
+ int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
int index_fd;
@@ -563,7 +578,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
init_merge_options(&o);
if (!strcmp(strategy, "subtree"))
- o.subtree_merge = 1;
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+
+ for (x = 0; x < xopts_nr; x++) {
+ if (!strcmp(xopts[x], "ours"))
+ o.recursive_variant = MERGE_RECURSIVE_OURS;
+ else if (!strcmp(xopts[x], "theirs"))
+ o.recursive_variant = MERGE_RECURSIVE_THEIRS;
+ else if (!strcmp(xopts[x], "subtree"))
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ else
+ die("Unknown option for merge-recursive: -X%s", xopts[x]);
+ }
o.branch1 = head_arg;
o.branch2 = remoteheads->item->util;
@@ -581,10 +607,16 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
return clean ? 0 : 1;
} else {
- args = xmalloc((4 + commit_list_count(common) +
+ args = xmalloc((4 + xopts_nr + commit_list_count(common) +
commit_list_count(remoteheads)) * sizeof(char *));
strbuf_addf(&buf, "merge-%s", strategy);
args[i++] = buf.buf;
+ for (x = 0; x < xopts_nr; x++) {
+ char *s = xmalloc(strlen(xopts[x])+2+1);
+ strcpy(s, "--");
+ strcpy(s+2, xopts[x]);
+ args[i++] = s;
+ }
for (j = common; j; j = j->next)
args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
args[i++] = "--";
ret = run_command_v_opt(args, RUN_GIT_CMD);
strbuf_release(&buf);
i = 1;
+ for (x = 0; x < xopts_nr; x++)
+ free((void *)args[i++]);
for (j = common; j; j = j->next)
free((void *)args[i++]);
i += 2;
index 500635fe4bb5a8f60bc76502ef15cd97bd273f74..8f617fcb7089caedce163871c3eb3a97eadb204b 100755 (executable)
'
all_strategies='recur recursive octopus resolve stupid ours subtree'
+all_strategies="$all_strategies recursive-ours recursive-theirs"
default_twohead_strategies='recursive'
default_octopus_strategies='octopus'
no_fast_forward_strategies='subtree ours'
-no_trivial_strategies='recursive recur subtree ours'
+no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
use_strategies=
allow_fast_forward=t
diff --git a/git-compat-util.h b/git-compat-util.h
index 5c596875c2e78a92031915948444971126d91a78..f64cc454e7b63b7922bd425218dfdb52ba5c6038 100644 (file)
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -198,6 +198,7 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
extern int prefixcmp(const char *str, const char *prefix);
+extern int suffixcmp(const char *str, const char *suffix);
extern time_t tm_to_time_t(const struct tm *tm);
static inline const char *skip_prefix(const char *str, const char *prefix)
index 11544cdb4037715b1e9edc14689896d99d6e3284..4735f11083ea753af319f1d334bcd89084d475db 100644 (file)
--- a/git.c
+++ b/git.c
{ "merge-file", cmd_merge_file },
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
{ "mktree", cmd_mktree, RUN_SETUP },
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
diff --git a/ll-merge.c b/ll-merge.c
index 2d6b6d6cb1d2bc2d334bf058feb3444e94b5a781..45f7e0190a13871e70d784923c709447e127fc1c 100644 (file)
--- a/ll-merge.c
+++ b/ll-merge.c
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor);
+ int flag);
struct ll_merge_driver {
const char *name;
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
/*
* The tentative merge result is "ours" for the final round,
* or common ancestor for an internal merge. Still return
* "conflicted merge" status.
*/
- mmfile_t *stolen = virtual_ancestor ? orig : src1;
+ mmfile_t *stolen = (flag & 01) ? orig : src1;
result->ptr = stolen->ptr;
result->size = stolen->size;
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
xpparam_t xpp;
int style = 0;
+ int favor = (flag >> 1) & 03;
if (buffer_is_binary(orig->ptr, orig->size) ||
buffer_is_binary(src1->ptr, src1->size) ||
return ll_binary_merge(drv_unused, result,
path,
orig, src1, name1,
- src2, name2,
- virtual_ancestor);
+ src2, name2, flag);
}
memset(&xpp, 0, sizeof(xpp));
return xdl_merge(orig,
src1, name1,
src2, name2,
- &xpp, XDL_MERGE_ZEALOUS | style,
+ &xpp, XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, style, favor),
result);
}
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
char *src, *dst;
long size;
git_xmerge_style = 0;
status = ll_xdl_merge(drv_unused, result, path_unused,
orig, src1, NULL, src2, NULL,
- virtual_ancestor);
+ flag);
git_xmerge_style = saved_style;
if (status <= 0)
return status;
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
char temp[3][50];
struct strbuf cmd = STRBUF_INIT;
mmfile_t *ancestor,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
- int virtual_ancestor)
+ int flag)
{
const char *ll_driver_name;
const struct ll_merge_driver *driver;
+ int virtual_ancestor = flag & 01;
ll_driver_name = git_path_check_merge(path);
driver = find_ll_merge_driver(ll_driver_name);
return driver->fn(driver, result_buf, path,
ancestor,
ours, our_label,
- theirs, their_label, virtual_ancestor);
+ theirs, their_label, flag);
}
diff --git a/ll-merge.h b/ll-merge.h
index 5388422d091ede134d42406291989c49553f7428..aaed46dec9dabcd7adc614ded30086f61cdf7b48 100644 (file)
--- a/ll-merge.h
+++ b/ll-merge.h
mmfile_t *ancestor,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
- int virtual_ancestor);
+ int flag);
#endif
diff --git a/merge-recursive.c b/merge-recursive.c
index a91208f295d00d73b8fc0f5cd6a92ac422d9eeb4..82f24ac16ed68e7838151710f76cb4f186b69df5 100644 (file)
--- a/merge-recursive.c
+++ b/merge-recursive.c
mmfile_t orig, src1, src2;
char *name1, *name2;
int merge_status;
+ int favor;
+
+ if (o->call_depth)
+ favor = 0;
+ else {
+ switch (o->recursive_variant) {
+ case MERGE_RECURSIVE_OURS:
+ favor = XDL_MERGE_FAVOR_OURS;
+ break;
+ case MERGE_RECURSIVE_THEIRS:
+ favor = XDL_MERGE_FAVOR_THEIRS;
+ break;
+ default:
+ favor = 0;
+ break;
+ }
+ }
if (strcmp(a->path, b->path)) {
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
merge_status = ll_merge(result_buf, a->path, &orig,
&src1, name1, &src2, name2,
- o->call_depth);
+ (!!o->call_depth) | (favor << 1));
free(name1);
free(name2);
{
int code, clean;
- if (o->subtree_merge) {
+ if (o->recursive_variant == MERGE_RECURSIVE_SUBTREE) {
merge = shift_tree_object(head, merge);
common = shift_tree_object(head, common);
}
diff --git a/merge-recursive.h b/merge-recursive.h
index fd138ca14006843a7ce0c38cfde4de580d5ce36e..39de100cd5dcac1a643d1562abf5c8f73877dec1 100644 (file)
--- a/merge-recursive.h
+++ b/merge-recursive.h
struct merge_options {
const char *branch1;
const char *branch2;
- unsigned subtree_merge : 1;
+ enum {
+ MERGE_RECURSIVE_SUBTREE = 1,
+ MERGE_RECURSIVE_OURS,
+ MERGE_RECURSIVE_THEIRS,
+ } recursive_variant;
unsigned buffer_output : 1;
int verbosity;
int diff_rename_limit;
diff --git a/strbuf.c b/strbuf.c
index a6153dca278abe957254fa091fdcd8eb13b90b25..d71a623b9c7a6f5c5d786a692eec852f54a1a317 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
return (unsigned char)*prefix - (unsigned char)*str;
}
+int suffixcmp(const char *str, const char *suffix)
+{
+ int len = strlen(str), suflen = strlen(suffix);
+ if (len < suflen)
+ return -1;
+ else
+ return strcmp(str + len - suflen, suffix);
+}
+
/*
* Used as the default ->buf value, so that people can always assume
* buf is non NULL and ->buf is NUL terminated even for a freshly
diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='Merge-recursive ours and theirs variants'
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3 4 5 6 7 8 9
+ do
+ echo "$i"
+ done >file &&
+ git add file &&
+ cp file elif &&
+ git commit -m initial &&
+
+ sed -e "s/1/one/" -e "s/9/nine/" >file <elif &&
+ git commit -a -m ours &&
+
+ git checkout -b side HEAD^ &&
+
+ sed -e "s/9/nueve/" >file <elif &&
+ git commit -a -m theirs &&
+
+ git checkout master^0
+'
+
+test_expect_success 'plain recursive - should conflict' '
+ git reset --hard master &&
+ test_must_fail git merge -s recursive side &&
+ grep nine file &&
+ grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'recursive favouring theirs' '
+ git reset --hard master &&
+ git merge -s recursive -Xtheirs side &&
+ ! grep nine file &&
+ grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'recursive favouring ours' '
+ git reset --hard master &&
+ git merge -s recursive -X ours side &&
+ grep nine file &&
+ ! grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_done