From: "Avery Pennarun" <apenwarr@gmail.com>
To: <git@vger.kernel.org>
Cc: <gitster@pobox.com>, "Avery Pennarun" <apenwarr@gmail.com>
Subject: [PATCH 3/8] git-merge-recursive-{ours,theirs}
Date: Wed, 25 Nov 2009 21:23:55 -0500 [thread overview]
Message-ID: <7e1f1179fc5fe2f568e2c75f75366fa40d7bbbfb.1259201377.git.apenwarr@gmail.com> (raw)
In-Reply-To: <905749faf5ccb2c7c54d3318dbc662d69daf8d0e.1259201377.git.apenwarr@gmail.com>
In-Reply-To: <cover.1259201377.git.apenwarr@gmail.com>
This uses the low-level mechanism for "ours" and "theirs" autoresolution
introduced by the previous commit to introduce two additional merge
strategies, merge-recursive-ours and merge-recursive-theirs.
(Patch originally by Junio Hamano <gitster@pobox.com>.)
Signed-off-by: Avery Pennarun <apenwarr@gmail.com>
---
.gitignore | 2 +
Makefile | 3 ++
builtin-checkout.c | 2 +-
builtin-merge-recursive.c | 9 ++++--
builtin-merge.c | 4 ++-
contrib/examples/git-merge.sh | 3 +-
git-compat-util.h | 1 +
git.c | 2 +
ll-merge.c | 20 +++++++-------
ll-merge.h | 2 +-
merge-recursive.c | 21 ++++++++++++++-
merge-recursive.h | 6 +++-
strbuf.c | 9 ++++++
t/t6034-merge-ours-theirs.sh | 56 +++++++++++++++++++++++++++++++++++++++++
14 files changed, 120 insertions(+), 20 deletions(-)
create mode 100755 t/t6034-merge-ours-theirs.sh
diff --git a/.gitignore b/.gitignore
index ac02a58..87467d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,6 +79,8 @@
/git-merge-one-file
/git-merge-ours
/git-merge-recursive
+/git-merge-recursive-ours
+/git-merge-recursive-theirs
/git-merge-resolve
/git-merge-subtree
/git-mergetool
diff --git a/Makefile b/Makefile
index 5a0b3d4..f92b375 100644
--- a/Makefile
+++ b/Makefile
@@ -401,6 +401,8 @@ BUILT_INS += git-format-patch$X
BUILT_INS += git-fsck-objects$X
BUILT_INS += git-get-tar-commit-id$X
BUILT_INS += git-init$X
+BUILT_INS += git-merge-recursive-ours$X
+BUILT_INS += git-merge-recursive-theirs$X
BUILT_INS += git-merge-subtree$X
BUILT_INS += git-peek-remote$X
BUILT_INS += git-repo-config$X
@@ -1909,6 +1911,7 @@ check-docs::
do \
case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \
+ git-merge-recursive-ours | git-merge-recursive-theirs | \
git-merge-resolve | git-merge-subtree | \
git-fsck-objects | git-init-db | \
git-?*--?* ) continue ;; \
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 64f3a11..b392d1b 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -167,7 +167,7 @@ static int checkout_merged(int pos, struct checkout *state)
fill_mm(active_cache[pos+2]->sha1, &theirs);
status = ll_merge(&result_buf, path, &ancestor,
- &ours, "ours", &theirs, "theirs", 1);
+ &ours, "ours", &theirs, "theirs", 1, 0);
free(ancestor.ptr);
free(ours.ptr);
free(theirs.ptr);
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 710674c..f5082da 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -27,9 +27,12 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
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;
+ else if (!suffixcmp(argv[0], "-ours"))
+ o.recursive_variant = MERGE_RECURSIVE_OURS;
+ else if (!suffixcmp(argv[0], "-theirs"))
+ o.recursive_variant = MERGE_RECURSIVE_THEIRS;
}
if (argc < 4)
diff --git a/builtin-merge.c b/builtin-merge.c
index 855cf65..df089bb 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -55,6 +55,8 @@ static int verbosity;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
+ { "recursive-ours", DEFAULT_TWOHEAD | NO_TRIVIAL },
+ { "recursive-theirs", DEFAULT_TWOHEAD | NO_TRIVIAL },
{ "octopus", DEFAULT_OCTOPUS },
{ "resolve", 0 },
{ "ours", NO_FAST_FORWARD | NO_TRIVIAL },
@@ -563,7 +565,7 @@ 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;
o.branch1 = head_arg;
o.branch2 = remoteheads->item->util;
diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh
index 500635f..8f617fc 100755
--- a/contrib/examples/git-merge.sh
+++ b/contrib/examples/git-merge.sh
@@ -31,10 +31,11 @@ LF='
'
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 5c59687..f64cc45 100644
--- 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)
diff --git a/git.c b/git.c
index 11544cd..4735f11 100644
--- a/git.c
+++ b/git.c
@@ -332,6 +332,8 @@ static void handle_internal_command(int argc, const char **argv)
{ "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 2d6b6d6..cc6814f 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -18,7 +18,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor);
+ int virtual_ancestor, int favor);
struct ll_merge_driver {
const char *name;
@@ -38,7 +38,7 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int virtual_ancestor, int favor)
{
/*
* The tentative merge result is "ours" for the final round,
@@ -59,7 +59,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int virtual_ancestor, int favor)
{
xpparam_t xpp;
int style = 0;
@@ -73,7 +73,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
path,
orig, src1, name1,
src2, name2,
- virtual_ancestor);
+ virtual_ancestor, favor);
}
memset(&xpp, 0, sizeof(xpp));
@@ -82,7 +82,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
return xdl_merge(orig,
src1, name1,
src2, name2,
- &xpp, XDL_MERGE_ZEALOUS | style,
+ &xpp, XDL_MERGE_ZEALOUS | style | favor,
result);
}
@@ -92,7 +92,7 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int virtual_ancestor, int favor)
{
char *src, *dst;
long size;
@@ -104,7 +104,7 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
git_xmerge_style = 0;
status = ll_xdl_merge(drv_unused, result, path_unused,
orig, src1, NULL, src2, NULL,
- virtual_ancestor);
+ virtual_ancestor, favor);
git_xmerge_style = saved_style;
if (status <= 0)
return status;
@@ -165,7 +165,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int virtual_ancestor, int favor)
{
char temp[3][50];
struct strbuf cmd = STRBUF_INIT;
@@ -356,7 +356,7 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *ancestor,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
- int virtual_ancestor)
+ int virtual_ancestor, int favor)
{
const char *ll_driver_name;
const struct ll_merge_driver *driver;
@@ -369,5 +369,5 @@ int ll_merge(mmbuffer_t *result_buf,
return driver->fn(driver, result_buf, path,
ancestor,
ours, our_label,
- theirs, their_label, virtual_ancestor);
+ theirs, their_label, virtual_ancestor, favor);
}
diff --git a/ll-merge.h b/ll-merge.h
index 5388422..2c94fdb 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -10,6 +10,6 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *ancestor,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
- int virtual_ancestor);
+ int virtual_ancestor, int favor);
#endif
diff --git a/merge-recursive.c b/merge-recursive.c
index a91208f..257bf8f 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -642,6 +642,23 @@ static int merge_3way(struct merge_options *o,
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));
@@ -657,7 +674,7 @@ static int merge_3way(struct merge_options *o,
merge_status = ll_merge(result_buf, a->path, &orig,
&src1, name1, &src2, name2,
- o->call_depth);
+ o->call_depth, favor);
free(name1);
free(name2);
@@ -1196,7 +1213,7 @@ int merge_trees(struct merge_options *o,
{
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 fd138ca..9d54219 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -6,7 +6,11 @@
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 a6153dc..d71a623 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -10,6 +10,15 @@ int prefixcmp(const char *str, const char *prefix)
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/t6034-merge-ours-theirs.sh b/t/t6034-merge-ours-theirs.sh
new file mode 100755
index 0000000..56a9247
--- /dev/null
+++ b/t/t6034-merge-ours-theirs.sh
@@ -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-theirs 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-ours side &&
+ grep nine file &&
+ ! grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_done
--
1.6.6.rc0.62.gaccf
next prev parent reply other threads:[~2009-11-26 2:43 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-11-26 2:23 [PATCH 0/8] The return of -Xours, -Xtheirs, -Xsubtree=dir Avery Pennarun
2009-11-26 2:23 ` [PATCH 1/8] git-merge-file --ours, --theirs Avery Pennarun
2009-11-26 2:23 ` [PATCH 2/8] builtin-merge.c: call exclude_cmds() correctly Avery Pennarun
2009-11-26 2:23 ` Avery Pennarun [this message]
2009-11-26 2:23 ` [PATCH 4/8] Teach git-merge to pass -X<option> to the backend strategy module Avery Pennarun
2009-11-26 2:23 ` [PATCH 5/8] Teach git-pull to pass -X<option> to git-merge Avery Pennarun
2009-11-26 2:23 ` [PATCH 6/8] Make "subtree" part more orthogonal to the rest of merge-recursive Avery Pennarun
2009-11-26 2:23 ` [PATCH 7/8] Extend merge-subtree tests to test -Xsubtree=dir Avery Pennarun
2009-11-26 2:24 ` [PATCH 8/8] Document that merge strategies can now take their own options Avery Pennarun
2009-11-26 6:17 ` [PATCH 6/8] Make "subtree" part more orthogonal to the rest of merge-recursive Junio C Hamano
2009-11-26 6:16 ` [PATCH 5/8] Teach git-pull to pass -X<option> to git-merge Junio C Hamano
2009-11-26 6:16 ` [PATCH 4/8] Teach git-merge to pass -X<option> to the backend strategy module Junio C Hamano
2009-11-26 6:15 ` [PATCH 3/8] git-merge-recursive-{ours,theirs} Junio C Hamano
2009-11-26 22:05 ` Avery Pennarun
2009-11-30 6:21 ` Junio C Hamano
2009-11-30 18:08 ` Avery Pennarun
2009-11-30 19:56 ` Junio C Hamano
2009-11-30 20:01 ` Junio C Hamano
2009-11-30 20:02 ` Avery Pennarun
2009-11-26 5:36 ` [PATCH 2/8] builtin-merge.c: call exclude_cmds() correctly Junio C Hamano
2009-11-26 22:00 ` Avery Pennarun
2009-11-26 6:17 ` [PATCH 1/8] git-merge-file --ours, --theirs Junio C Hamano
2009-11-26 6:37 ` Nanako Shiraishi
2009-11-26 7:05 ` Junio C Hamano
2009-11-26 7:30 ` Nanako Shiraishi
2009-11-26 21:55 ` Avery Pennarun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7e1f1179fc5fe2f568e2c75f75366fa40d7bbbfb.1259201377.git.apenwarr@gmail.com \
--to=apenwarr@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).