@@ -28,6 +28,7 @@
#include "rerere.h"
#include "help.h"
#include "merge-recursive.h"
+#include "merge-ort-wrappers.h"
#include "resolve-undo.h"
#include "remote.h"
#include "fmt-merge-msg.h"
@@ -88,6 +89,7 @@ static int no_verify;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
{ "octopus", DEFAULT_OCTOPUS },
+ { "ort", NO_TRIVIAL },
{ "resolve", 0 },
{ "ours", NO_FAST_FORWARD | NO_TRIVIAL },
{ "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
@@ -159,10 +161,17 @@ static struct strategy *get_strategy(const char *name)
struct strategy *ret;
static struct cmdnames main_cmds, other_cmds;
static int loaded;
+ char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
if (!name)
return NULL;
+ if (default_strategy &&
+ !strcmp(default_strategy, "ort") &&
+ !strcmp(name, "recursive")) {
+ name = "ort";
+ }
+
for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
if (!strcmp(name, all_strategy[i].name))
return &all_strategy[i];
@@ -701,7 +710,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
return error(_("Unable to write index."));
- if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
+ if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") ||
+ !strcmp(strategy, "ort")) {
struct lock_file lock = LOCK_INIT;
int clean, x;
struct commit *result;
@@ -732,8 +742,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
commit_list_insert(j->item, &reversed);
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
- clean = merge_recursive(&o, head,
- remoteheads->item, reversed, &result);
+ if (!strcmp(strategy, "ort"))
+ clean = merge_ort_recursive(&o, head, remoteheads->item,
+ reversed, &result);
+ else
+ clean = merge_recursive(&o, head, remoteheads->item,
+ reversed, &result);
if (clean < 0)
exit(128);
if (write_locked_index(&the_index, &lock,
@@ -1264,6 +1278,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (branch)
skip_prefix(branch, "refs/heads/", &branch);
+ if (!pull_twohead) {
+ char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
+ if (default_strategy && !strcmp(default_strategy, "ort"))
+ pull_twohead = "ort";
+ }
+
init_diff_ui_defaults();
git_config(git_merge_config, NULL);
@@ -119,6 +119,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
+ replay.strategy = NULL;
sequencer_init_config(&replay);
replay.signoff = opts->signoff;
@@ -136,7 +137,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
opts->committer_date_is_author_date;
replay.ignore_date = opts->ignore_date;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
- replay.strategy = opts->strategy;
+ if (opts->strategy)
+ replay.strategy = opts->strategy;
if (opts->strategy_opts)
parse_strategy_opts(&replay, opts->strategy_opts);
@@ -1771,6 +1773,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.default_backend);
}
+ if (options.type == REBASE_MERGE &&
+ !options.strategy &&
+ getenv("GIT_TEST_MERGE_ALGORITHM"))
+ options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_PRESERVE_MERGES:
@@ -202,6 +202,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
/* These option values will be free()d */
opts->gpg_sign = xstrdup_or_null(opts->gpg_sign);
opts->strategy = xstrdup_or_null(opts->strategy);
+ if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM"))
+ opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
if (cmd == 'q') {
int ret = sequencer_remove_state(opts);
@@ -14,7 +14,8 @@
#include "diff.h"
#include "revision.h"
#include "rerere.h"
-#include "merge-recursive.h"
+#include "merge-ort.h"
+#include "merge-ort-wrappers.h"
#include "refs.h"
#include "strvec.h"
#include "quote.h"
@@ -204,6 +205,20 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
return 0;
}
+ if (!opts->strategy && !strcmp(k, "pull.twohead")) {
+ int ret = git_config_string((const char**)&opts->strategy, k, v);
+ if (ret == 0) {
+ /*
+ * pull.twohead is allowed to be multi-valued; we only
+ * care about the first value.
+ */
+ char *tmp = strchr(opts->strategy, ' ');
+ if (tmp)
+ *tmp = '\0';
+ }
+ return ret;
+ }
+
status = git_gpg_config(k, v, NULL);
if (status)
return status;
@@ -595,8 +610,9 @@ static int do_recursive_merge(struct repository *r,
struct replay_opts *opts)
{
struct merge_options o;
+ struct merge_result result;
struct tree *next_tree, *base_tree, *head_tree;
- int clean;
+ int clean, show_output;
int i;
struct lock_file index_lock = LOCK_INIT;
@@ -620,12 +636,27 @@ static int do_recursive_merge(struct repository *r,
for (i = 0; i < opts->xopts_nr; i++)
parse_merge_opt(&o, opts->xopts[i]);
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree);
- if (is_rebase_i(opts) && clean <= 0)
- fputs(o.obuf.buf, stdout);
- strbuf_release(&o.obuf);
+ if (opts->strategy && !strcmp(opts->strategy, "ort")) {
+ memset(&result, 0, sizeof(result));
+ merge_inmemory_nonrecursive(&o, base_tree, head_tree, next_tree,
+ &result);
+ show_output = !is_rebase_i(opts) || !result.clean;
+ /*
+ * TODO: merge_switch_to_result will update index/working tree;
+ * we only really want to do that if !result.clean || this is
+ * the final patch to be picked. But determining this is the
+ * final patch would take some work, and "head_tree" would need
+ * to be replace with the tree the index matched before we
+ * started doing any picks.
+ */
+ merge_switch_to_result(&o, head_tree, &result, 1, show_output);
+ clean = result.clean;
+ } else {
+ clean = merge_trees(&o, head_tree, next_tree, base_tree);
+ if (is_rebase_i(opts) && clean <= 0)
+ fputs(o.obuf.buf, stdout);
+ strbuf_release(&o.obuf);
+ }
if (clean < 0) {
rollback_lock_file(&index_lock);
return clean;
@@ -1991,7 +2022,10 @@ static int do_pick_commit(struct repository *r,
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
res = -1;
- else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
+ else if (!opts->strategy ||
+ !strcmp(opts->strategy, "recursive") ||
+ !strcmp(opts->strategy, "ort") ||
+ command == TODO_REVERT) {
res = do_recursive_merge(r, base, next, base_label, next_label,
&head, &msgbuf, opts);
if (res < 0)
@@ -3485,7 +3519,9 @@ static int do_merge(struct repository *r,
struct commit_list *bases, *j, *reversed = NULL;
struct commit_list *to_merge = NULL, **tail = &to_merge;
const char *strategy = !opts->xopts_nr &&
- (!opts->strategy || !strcmp(opts->strategy, "recursive")) ?
+ (!opts->strategy ||
+ !strcmp(opts->strategy, "recursive") ||
+ !strcmp(opts->strategy, "ort")) ?
NULL : opts->strategy;
struct merge_options o;
int merge_arg_len, oneline_offset, can_fast_forward, ret, k;
@@ -3722,7 +3758,20 @@ static int do_merge(struct repository *r,
o.branch2 = ref_name.buf;
o.buffer_output = 2;
- ret = merge_recursive(&o, head_commit, merge_commit, reversed, &i);
+ if (opts->strategy && !strcmp(opts->strategy, "ort")) {
+ /*
+ * TODO: Should use merge_inmemory_recursive() and
+ * merge_switch_to_result(), skipping the call to
+ * merge_switch_to_result() when we don't actually need to
+ * update the index and working copy immediately.
+ */
+ ret = merge_ort_recursive(&o,
+ head_commit, merge_commit, reversed,
+ &i);
+ } else {
+ ret = merge_recursive(&o, head_commit, merge_commit, reversed,
+ &i);
+ }
if (ret <= 0)
fputs(o.obuf.buf, stdout);
strbuf_release(&o.obuf);