Skip to content

Commit

Permalink
Merge branch 'ap/merge-backend-opts'
Browse files Browse the repository at this point in the history
* ap/merge-backend-opts:
  Document that merge strategies can now take their own options
  Extend merge-subtree tests to test -Xsubtree=dir.
  Make "subtree" part more orthogonal to the rest of merge-recursive.
  pull: Fix parsing of -X<option>
  Teach git-pull to pass -X<option> to git-merge
  git merge -X<option>
  git-merge-file --ours, --theirs

Conflicts:
	git-compat-util.h
  • Loading branch information
gitster committed Jan 21, 2010
2 parents e98f80f + 566c511 commit fcb2a7e
Show file tree
Hide file tree
Showing 21 changed files with 392 additions and 54 deletions.
12 changes: 10 additions & 2 deletions Documentation/git-merge-file.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
[-p|--stdout] [-q|--quiet] <current-file> <base-file> <other-file>
[--ours|--theirs] [-p|--stdout] [-q|--quiet]
<current-file> <base-file> <other-file>


DESCRIPTION
Expand All @@ -34,7 +35,9 @@ normally outputs a warning and brackets the conflict with lines containing
>>>>>>> B

If there are conflicts, the user should edit the result and delete one of
the alternatives.
the alternatives. When `--ours` or `--theirs` option is in effect, however,
these conflicts are resolved favouring lines from `<current-file>` or
lines from `<other-file>` respectively.

The exit value of this program is negative on error, and the number of
conflicts otherwise. If the merge was clean, the exit value is 0.
Expand Down Expand Up @@ -62,6 +65,11 @@ OPTIONS
-q::
Quiet; do not warn about conflicts.

--ours::
--theirs::
Instead of leaving conflicts in the file, resolve conflicts
favouring our (or their) side of the lines.


EXAMPLES
--------
Expand Down
5 changes: 5 additions & 0 deletions Documentation/merge-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,8 @@ option can be used to override --squash.
-v::
--verbose::
Be verbose.

-X <option>::
--strategy-option=<option>::
Pass merge strategy specific option through to the merge
strategy.
29 changes: 28 additions & 1 deletion Documentation/merge-strategies.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
MERGE STRATEGIES
----------------

The merge mechanism ('git-merge' and 'git-pull' commands) allows the
backend 'merge strategies' to be chosen with `-s` option. Some strategies
can also take their own options, which can be passed by giving `-X<option>`
arguments to 'git-merge' and/or 'git-pull'.

resolve::
This can only resolve two heads (i.e. the current branch
and another branch you pulled from) using a 3-way merge
Expand All @@ -20,6 +25,27 @@ recursive::
Additionally this can detect and handle merges involving
renames. This is the default merge strategy when
pulling or merging one branch.
+
The 'recursive' strategy can take the following options:

ours;;
This option forces conflicting hunks to be auto-resolved cleanly by
favoring 'our' version. Changes from the other tree that do not
conflict with our side are reflected to the merge result.
+
This should not be confused with the 'ours' merge strategy, which does not
even look at what the other tree contains at all. It discards everything
the other tree did, declaring 'our' history contains all that happened in it.

theirs;;
This is opposite of 'ours'.

subtree[=path];;
This option is a more advanced form of 'subtree' strategy, where
the strategy makes a guess on how two trees must be shifted to
match with each other when merging. Instead, the specified path
is prefixed (or stripped from the beginning) to make the shape of
two trees to match.

octopus::
This resolves cases with more than two heads, but refuses to do
Expand All @@ -33,7 +59,8 @@ ours::
merge is always that of the current branch head, effectively
ignoring all changes from all other branches. It is meant to
be used to supersede old development history of side
branches.
branches. Note that this is different from the -Xours option to
the 'recursive' merge strategy.

subtree::
This is a modified recursive strategy. When merging trees A and
Expand Down
15 changes: 10 additions & 5 deletions builtin-merge-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
mmbuffer_t result = {NULL, 0};
xpparam_t xpp = {XDF_NEED_MINIMAL};
int ret = 0, i = 0, to_stdout = 0;
int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
int merge_style = 0, quiet = 0;
int level = XDL_MERGE_ZEALOUS_ALNUM;
int style = 0, quiet = 0;
int favor = 0;
int nongit;

struct option options[] = {
OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
OPT_SET_INT(0, "diff3", &merge_style, "use a diff3 based merge", XDL_MERGE_DIFF3),
OPT_SET_INT(0, "diff3", &style, "use a diff3 based merge", XDL_MERGE_DIFF3),
OPT_SET_INT(0, "ours", &favor, "for conflicts, use our version",
XDL_MERGE_FAVOR_OURS),
OPT_SET_INT(0, "theirs", &favor, "for conflicts, use their version",
XDL_MERGE_FAVOR_THEIRS),
OPT__QUIET(&quiet),
OPT_CALLBACK('L', NULL, names, "name",
"set labels for file1/orig_file/file2", &label_cb),
Expand All @@ -45,7 +50,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
/* Read the configuration file */
git_config(git_xmerge_config, NULL);
if (0 <= git_xmerge_style)
merge_style = git_xmerge_style;
style = git_xmerge_style;
}

argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
Expand All @@ -68,7 +73,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
}

ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
&xpp, merge_level | merge_style, &result);
&xpp, XDL_MERGE_FLAGS(level, style, favor), &result);

for (i = 0; i < 3; i++)
free(mmfs[i].ptr);
Expand Down
27 changes: 19 additions & 8 deletions builtin-merge-recursive.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,30 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
struct commit *result;

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 (argv[0] && !suffixcmp(argv[0], "-subtree"))
o.subtree_shift = "";

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.subtree_shift = "";
else if (!prefixcmp(arg+2, "subtree="))
o.subtree_shift = arg + 10;
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))
Expand Down
42 changes: 39 additions & 3 deletions builtin-merge.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ static struct commit_list *remoteheads;
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;
static int allow_rerere_auto;
Expand Down Expand Up @@ -148,6 +150,17 @@ static int option_parse_strategy(const struct option *opt,
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)
{
Expand Down Expand Up @@ -175,6 +188,8 @@ static struct option builtin_merge_options[] = {
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
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),
Expand Down Expand Up @@ -537,7 +552,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
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;
Expand Down Expand Up @@ -566,7 +581,20 @@ 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.subtree_shift = "";

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.subtree_shift = "";
else if (!prefixcmp(xopts[x], "subtree="))
o.subtree_shift = xopts[x]+8;
else
die("Unknown option for merge-recursive: -X%s", xopts[x]);
}

o.branch1 = head_arg;
o.branch2 = remoteheads->item->util;
Expand All @@ -584,10 +612,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++] = "--";
Expand All @@ -598,6 +632,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
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;
Expand Down
1 change: 1 addition & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,7 @@ extern int diff_auto_refresh_index;

/* match-trees.c */
void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);

/*
* whitespace rules.
Expand Down
3 changes: 2 additions & 1 deletion contrib/examples/git-merge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,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);

static inline const char *skip_prefix(const char *str, const char *prefix)
{
Expand Down
30 changes: 25 additions & 5 deletions git-pull.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ test -f "$GIT_DIR/MERGE_HEAD" && die_merge

strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
log_arg= verbosity=
merge_args=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
rebase=$(git config --bool branch.$curr_branch_short.rebase)
Expand Down Expand Up @@ -83,6 +84,18 @@ do
esac
strategy_args="${strategy_args}-s $strategy "
;;
-X*)
case "$#,$1" in
1,-X)
usage ;;
*,-X)
xx="-X $(git rev-parse --sq-quote "$2")"
shift ;;
*,*)
xx=$(git rev-parse --sq-quote "$1") ;;
esac
merge_args="$merge_args$xx "
;;
-r|--r|--re|--reb|--reba|--rebas|--rebase)
rebase=true
;;
Expand Down Expand Up @@ -254,8 +267,15 @@ then
fi

merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
test true = "$rebase" &&
exec git-rebase $diffstat $strategy_args --onto $merge_head \
${oldremoteref:-$merge_head}
exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
"$merge_name" HEAD $merge_head $verbosity
case "$rebase" in
true)
eval="git-rebase $diffstat $strategy_args $merge_args"
eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
;;
*)
eval="git-merge $diffstat $no_commit $squash $no_ff $ff_only"
eval="$eval $log_arg $strategy_args $merge_args"
eval="$eval \"$merge_name\" HEAD $merge_head $verbosity"
;;
esac
eval "exec $eval"
2 changes: 2 additions & 0 deletions git.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
Loading

0 comments on commit fcb2a7e

Please sign in to comment.