@matheusbsbs / matheus-bagliano / commits / 8cc5b29065
Fork 0

git merge -X<option>

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>

Avery Pennarun committed Nov 25, 2009 at 21:23 UTC 8cc5b29065e19267cbc08b39c34674b02c2e3d59
11 files changed +162 -26
builtin-merge-recursive.c
+17 -6
@@ -26,18 +26,29 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
26
27 init_merge_options(&o);
28 if (argv[0]) {
29 - int namelen = strlen(argv[0]);
30 - if (8 < namelen &&
31 - !strcmp(argv[0] + namelen - 8, "-subtree"))
32 - o.subtree_merge = 1;
29 + if (!suffixcmp(argv[0], "-subtree"))
30 + o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
31 }
32
33 if (argc < 4)
34 usagef("%s <base>... -- <head> <remote> ...", argv[0]);
35
36 for (i = 1; i < argc; ++i) {
39 - if (!strcmp(argv[i], "--"))
40 - break;
37 + const char *arg = argv[i];
38 +
39 + if (!prefixcmp(arg, "--")) {
40 + if (!arg[2])
41 + break;
42 + if (!strcmp(arg+2, "ours"))
43 + o.recursive_variant = MERGE_RECURSIVE_OURS;
44 + else if (!strcmp(arg+2, "theirs"))
45 + o.recursive_variant = MERGE_RECURSIVE_THEIRS;
46 + else if (!strcmp(arg+2, "subtree"))
47 + o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
48 + else
49 + die("Unknown option %s", arg);
50 + continue;
51 + }
52 if (bases_count < ARRAY_SIZE(bases)-1) {
53 unsigned char *sha = xmalloc(20);
54 if (get_sha1(argv[i], sha))
builtin-merge.c
+37 -3
@@ -50,6 +50,8 @@ static struct commit_list *remoteheads;
50 static unsigned char head[20], stash[20];
51 static struct strategy **use_strategies;
52 static size_t use_strategies_nr, use_strategies_alloc;
53 +static const char **xopts;
54 +static size_t xopts_nr, xopts_alloc;
55 static const char *branch;
56 static int verbosity;
57
@@ -146,6 +148,17 @@ static int option_parse_strategy(const struct option *opt,
148 return 0;
149 }
150
151 +static int option_parse_x(const struct option *opt,
152 + const char *arg, int unset)
153 +{
154 + if (unset)
155 + return 0;
156 +
157 + ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
158 + xopts[xopts_nr++] = xstrdup(arg);
159 + return 0;
160 +}
161 +
162 static int option_parse_n(const struct option *opt,
163 const char *arg, int unset)
164 {
@@ -172,6 +185,8 @@ static struct option builtin_merge_options[] = {
185 "abort if fast-forward is not possible"),
186 OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
187 "merge strategy to use", option_parse_strategy),
188 + OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
189 + "option for selected merge strategy", option_parse_x),
190 OPT_CALLBACK('m', "message", &merge_msg, "message",
191 "message to be used for the merge commit (if any)",
192 option_parse_message),
@@ -534,7 +549,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
549 const char *head_arg)
550 {
551 const char **args;
537 - int i = 0, ret;
552 + int i = 0, x = 0, ret;
553 struct commit_list *j;
554 struct strbuf buf = STRBUF_INIT;
555 int index_fd;
@@ -563,7 +578,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
578
579 init_merge_options(&o);
580 if (!strcmp(strategy, "subtree"))
566 - o.subtree_merge = 1;
581 + o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
582 +
583 + for (x = 0; x < xopts_nr; x++) {
584 + if (!strcmp(xopts[x], "ours"))
585 + o.recursive_variant = MERGE_RECURSIVE_OURS;
586 + else if (!strcmp(xopts[x], "theirs"))
587 + o.recursive_variant = MERGE_RECURSIVE_THEIRS;
588 + else if (!strcmp(xopts[x], "subtree"))
589 + o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
590 + else
591 + die("Unknown option for merge-recursive: -X%s", xopts[x]);
592 + }
593
594 o.branch1 = head_arg;
595 o.branch2 = remoteheads->item->util;
@@ -581,10 +607,16 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
607 rollback_lock_file(lock);
608 return clean ? 0 : 1;
609 } else {
584 - args = xmalloc((4 + commit_list_count(common) +
610 + args = xmalloc((4 + xopts_nr + commit_list_count(common) +
611 commit_list_count(remoteheads)) * sizeof(char *));
612 strbuf_addf(&buf, "merge-%s", strategy);
613 args[i++] = buf.buf;
614 + for (x = 0; x < xopts_nr; x++) {
615 + char *s = xmalloc(strlen(xopts[x])+2+1);
616 + strcpy(s, "--");
617 + strcpy(s+2, xopts[x]);
618 + args[i++] = s;
619 + }
620 for (j = common; j; j = j->next)
621 args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
622 args[i++] = "--";
@@ -595,6 +627,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
627 ret = run_command_v_opt(args, RUN_GIT_CMD);
628 strbuf_release(&buf);
629 i = 1;
630 + for (x = 0; x < xopts_nr; x++)
631 + free((void *)args[i++]);
632 for (j = common; j; j = j->next)
633 free((void *)args[i++]);
634 i += 2;
contrib/examples/git-merge.sh
+2 -1
@@ -31,10 +31,11 @@ LF='
31 '
32
33 all_strategies='recur recursive octopus resolve stupid ours subtree'
34 +all_strategies="$all_strategies recursive-ours recursive-theirs"
35 default_twohead_strategies='recursive'
36 default_octopus_strategies='octopus'
37 no_fast_forward_strategies='subtree ours'
37 -no_trivial_strategies='recursive recur subtree ours'
38 +no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
39 use_strategies=
40
41 allow_fast_forward=t
git-compat-util.h
+1
@@ -198,6 +198,7 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
198 extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
199
200 extern int prefixcmp(const char *str, const char *prefix);
201 +extern int suffixcmp(const char *str, const char *suffix);
202 extern time_t tm_to_time_t(const struct tm *tm);
203
204 static inline const char *skip_prefix(const char *str, const char *prefix)
git.c
+2
@@ -332,6 +332,8 @@ static void handle_internal_command(int argc, const char **argv)
332 { "merge-file", cmd_merge_file },
333 { "merge-ours", cmd_merge_ours, RUN_SETUP },
334 { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
335 + { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
336 + { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
337 { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
338 { "mktree", cmd_mktree, RUN_SETUP },
339 { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
ll-merge.c
+13 -12
@@ -18,7 +18,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
18 mmfile_t *orig,
19 mmfile_t *src1, const char *name1,
20 mmfile_t *src2, const char *name2,
21 - int virtual_ancestor);
21 + int flag);
22
23 struct ll_merge_driver {
24 const char *name;
@@ -38,14 +38,14 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
38 mmfile_t *orig,
39 mmfile_t *src1, const char *name1,
40 mmfile_t *src2, const char *name2,
41 - int virtual_ancestor)
41 + int flag)
42 {
43 /*
44 * The tentative merge result is "ours" for the final round,
45 * or common ancestor for an internal merge. Still return
46 * "conflicted merge" status.
47 */
48 - mmfile_t *stolen = virtual_ancestor ? orig : src1;
48 + mmfile_t *stolen = (flag & 01) ? orig : src1;
49
50 result->ptr = stolen->ptr;
51 result->size = stolen->size;
@@ -59,10 +59,11 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
59 mmfile_t *orig,
60 mmfile_t *src1, const char *name1,
61 mmfile_t *src2, const char *name2,
62 - int virtual_ancestor)
62 + int flag)
63 {
64 xpparam_t xpp;
65 int style = 0;
66 + int favor = (flag >> 1) & 03;
67
68 if (buffer_is_binary(orig->ptr, orig->size) ||
69 buffer_is_binary(src1->ptr, src1->size) ||
@@ -72,8 +73,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
73 return ll_binary_merge(drv_unused, result,
74 path,
75 orig, src1, name1,
75 - src2, name2,
76 - virtual_ancestor);
76 + src2, name2, flag);
77 }
78
79 memset(&xpp, 0, sizeof(xpp));
@@ -82,7 +82,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
82 return xdl_merge(orig,
83 src1, name1,
84 src2, name2,
85 - &xpp, XDL_MERGE_ZEALOUS | style,
85 + &xpp, XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, style, favor),
86 result);
87 }
88
@@ -92,7 +92,7 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
92 mmfile_t *orig,
93 mmfile_t *src1, const char *name1,
94 mmfile_t *src2, const char *name2,
95 - int virtual_ancestor)
95 + int flag)
96 {
97 char *src, *dst;
98 long size;
@@ -104,7 +104,7 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
104 git_xmerge_style = 0;
105 status = ll_xdl_merge(drv_unused, result, path_unused,
106 orig, src1, NULL, src2, NULL,
107 - virtual_ancestor);
107 + flag);
108 git_xmerge_style = saved_style;
109 if (status <= 0)
110 return status;
@@ -165,7 +165,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
165 mmfile_t *orig,
166 mmfile_t *src1, const char *name1,
167 mmfile_t *src2, const char *name2,
168 - int virtual_ancestor)
168 + int flag)
169 {
170 char temp[3][50];
171 struct strbuf cmd = STRBUF_INIT;
@@ -356,10 +356,11 @@ int ll_merge(mmbuffer_t *result_buf,
356 mmfile_t *ancestor,
357 mmfile_t *ours, const char *our_label,
358 mmfile_t *theirs, const char *their_label,
359 - int virtual_ancestor)
359 + int flag)
360 {
361 const char *ll_driver_name;
362 const struct ll_merge_driver *driver;
363 + int virtual_ancestor = flag & 01;
364
365 ll_driver_name = git_path_check_merge(path);
366 driver = find_ll_merge_driver(ll_driver_name);
@@ -369,5 +370,5 @@ int ll_merge(mmbuffer_t *result_buf,
370 return driver->fn(driver, result_buf, path,
371 ancestor,
372 ours, our_label,
372 - theirs, their_label, virtual_ancestor);
373 + theirs, their_label, flag);
374 }
ll-merge.h
+1 -1
@@ -10,6 +10,6 @@ int ll_merge(mmbuffer_t *result_buf,
10 mmfile_t *ancestor,
11 mmfile_t *ours, const char *our_label,
12 mmfile_t *theirs, const char *their_label,
13 - int virtual_ancestor);
13 + int flag);
14
15 #endif
merge-recursive.c
+19 -2
@@ -642,6 +642,23 @@ static int merge_3way(struct merge_options *o,
642 mmfile_t orig, src1, src2;
643 char *name1, *name2;
644 int merge_status;
645 + int favor;
646 +
647 + if (o->call_depth)
648 + favor = 0;
649 + else {
650 + switch (o->recursive_variant) {
651 + case MERGE_RECURSIVE_OURS:
652 + favor = XDL_MERGE_FAVOR_OURS;
653 + break;
654 + case MERGE_RECURSIVE_THEIRS:
655 + favor = XDL_MERGE_FAVOR_THEIRS;
656 + break;
657 + default:
658 + favor = 0;
659 + break;
660 + }
661 + }
662
663 if (strcmp(a->path, b->path)) {
664 name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
@@ -657,7 +674,7 @@ static int merge_3way(struct merge_options *o,
674
675 merge_status = ll_merge(result_buf, a->path, &orig,
676 &src1, name1, &src2, name2,
660 - o->call_depth);
677 + (!!o->call_depth) | (favor << 1));
678
679 free(name1);
680 free(name2);
@@ -1196,7 +1213,7 @@ int merge_trees(struct merge_options *o,
1213 {
1214 int code, clean;
1215
1199 - if (o->subtree_merge) {
1216 + if (o->recursive_variant == MERGE_RECURSIVE_SUBTREE) {
1217 merge = shift_tree_object(head, merge);
1218 common = shift_tree_object(head, common);
1219 }
merge-recursive.h
+5 -1
@@ -6,7 +6,11 @@
6 struct merge_options {
7 const char *branch1;
8 const char *branch2;
9 - unsigned subtree_merge : 1;
9 + enum {
10 + MERGE_RECURSIVE_SUBTREE = 1,
11 + MERGE_RECURSIVE_OURS,
12 + MERGE_RECURSIVE_THEIRS,
13 + } recursive_variant;
14 unsigned buffer_output : 1;
15 int verbosity;
16 int diff_rename_limit;
strbuf.c
+9
@@ -10,6 +10,15 @@ int prefixcmp(const char *str, const char *prefix)
10 return (unsigned char)*prefix - (unsigned char)*str;
11 }
12
13 +int suffixcmp(const char *str, const char *suffix)
14 +{
15 + int len = strlen(str), suflen = strlen(suffix);
16 + if (len < suflen)
17 + return -1;
18 + else
19 + return strcmp(str + len - suflen, suffix);
20 +}
21 +
22 /*
23 * Used as the default ->buf value, so that people can always assume
24 * buf is non NULL and ->buf is NUL terminated even for a freshly
t/t6037-merge-ours-theirs.sh new
+56
@@ -0,0 +1,56 @@
1 +#!/bin/sh
2 +
3 +test_description='Merge-recursive ours and theirs variants'
4 +. ./test-lib.sh
5 +
6 +test_expect_success setup '
7 + for i in 1 2 3 4 5 6 7 8 9
8 + do
9 + echo "$i"
10 + done >file &&
11 + git add file &&
12 + cp file elif &&
13 + git commit -m initial &&
14 +
15 + sed -e "s/1/one/" -e "s/9/nine/" >file <elif &&
16 + git commit -a -m ours &&
17 +
18 + git checkout -b side HEAD^ &&
19 +
20 + sed -e "s/9/nueve/" >file <elif &&
21 + git commit -a -m theirs &&
22 +
23 + git checkout master^0
24 +'
25 +
26 +test_expect_success 'plain recursive - should conflict' '
27 + git reset --hard master &&
28 + test_must_fail git merge -s recursive side &&
29 + grep nine file &&
30 + grep nueve file &&
31 + ! grep 9 file &&
32 + grep one file &&
33 + ! grep 1 file
34 +'
35 +
36 +test_expect_success 'recursive favouring theirs' '
37 + git reset --hard master &&
38 + git merge -s recursive -Xtheirs side &&
39 + ! grep nine file &&
40 + grep nueve file &&
41 + ! grep 9 file &&
42 + grep one file &&
43 + ! grep 1 file
44 +'
45 +
46 +test_expect_success 'recursive favouring ours' '
47 + git reset --hard master &&
48 + git merge -s recursive -X ours side &&
49 + grep nine file &&
50 + ! grep nueve file &&
51 + ! grep 9 file &&
52 + grep one file &&
53 + ! grep 1 file
54 +'
55 +
56 +test_done