threads / patch / 14238

patch, 14 partsAdd new test to ensure git-merge handles more than 25 refs.

Subject: [PATCH 08/14] Add new test to ensure git-merge handles more than 25 refs.

## tl;dr

26 messages between Jul 1, 2008 and Jul 8, 2008. Diffs are folded; open one to read it.

replies: 25people: 3as markdown or json

Miklos Vajna· Jul 1, 2008, 02:37 UTC · lore

[PATCH 00/14] Build in merge

Sorry for the resend, but I again squashed an unrelated hunk to the last patch, so I'm sending the changed patches:

08: Add new test to ensure git-merge handles more than 25 refs. 13: git-commit-tree: make it usable from other builtins 14: Build in merge

I really hope I won't overlook this next time.

The interdiff from the previous version is empty, I just moved that unrelated hunk from patch 13 to patch 08.

Junio C Hamano (2):
  Introduce get_merge_bases_many()
  Introduce reduce_heads()
Miklos Vajna (12):
  Move split_cmdline() to alias.c
  Move commit_list_count() to commit.c
  Move parse-options's skip_prefix() to git-compat-util.h
  Add new test to ensure git-merge handles pull.twohead and
    pull.octopus
  Move read_cache_unmerged() to read-cache.c
  git-fmt-merge-msg: make it usable from other builtins
  Introduce get_octopus_merge_bases() in commit.c
  Add new test to ensure git-merge handles more than 25 refs.
  Add new test case to ensure git-merge reduces octopus parents when
    possible
  Add new test case to ensure git-merge prepends the custom merge
    message
  git-commit-tree: make it usable from other builtins
  Build in merge
 Makefile                                      |    2 +-
 alias.c                                       |   54 ++
 builtin-commit-tree.c                         |   71 +-
 builtin-fmt-merge-msg.c                       |  155 ++--
 builtin-merge-recursive.c                     |    8 -
 builtin-merge.c                               | 1148 +++++++++++++++++++++++++
 builtin-read-tree.c                           |   24 -
 builtin-remote.c                              |   39 +-
 builtin.h                                     |    8 +
 cache.h                                       |    3 +
 commit.c                                      |  136 +++-
 commit.h                                      |    4 +
 git-merge.sh => contrib/examples/git-merge.sh |    0 
 git-compat-util.h                             |    6 +
 git.c                                         |   54 +--
 parse-options.c                               |    6 -
 read-cache.c                                  |   31 +
 t/t7601-merge-pull-config.sh                  |  129 +++
 t/t7602-merge-octopus-many.sh                 |   52 ++
 t/t7603-merge-reduce-heads.sh                 |   63 ++
 t/t7604-merge-custom-message.sh               |   37 +
 21 files changed, 1811 insertions(+), 219 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)
 create mode 100755 t/t7601-merge-pull-config.sh
 create mode 100755 t/t7602-merge-octopus-many.sh
 create mode 100755 t/t7603-merge-reduce-heads.sh
 create mode 100755 t/t7604-merge-custom-message.sh
Miklos Vajna· Jul 1, 2008, 02:37 UTC · re: Miklos Vajna · lore

The old shell version handled only 25 refs but we no longer have this limitation. Add a test to make sure this limitation will not be introduced again in the future.

Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
 t/t7602-merge-octopus-many.sh |   52 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 52 insertions(+), 0 deletions(-)
 create mode 100755 t/t7602-merge-octopus-many.sh
Show changes to t/t7602-merge-octopus-many.sh +52 −0
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
new file mode 100755
index 0000000..fcb8285
--- /dev/null
+++ b/t/t7602-merge-octopus-many.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Testing octopus merge with more than 25 refs.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo c0 > c0.c &&
+	git add c0.c &&
+	git commit -m c0 &&
+	git tag c0 &&
+	i=1 &&
+	while test $i -le 30
+	do
+		git reset --hard c0 &&
+		echo c$i > c$i.c &&
+		git add c$i.c &&
+		git commit -m c$i &&
+		git tag c$i &&
+		i=`expr $i + 1` || return 1
+	done
+'
+
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
+	git reset --hard c1 &&
+	i=2 &&
+	refs="" &&
+	while test $i -le 30
+	do
+		refs="$refs c$i"
+		i=`expr $i + 1`
+	done
+	git merge $refs &&
+	test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+	i=1 &&
+	while test $i -le 30
+	do
+		test "$(git rev-parse c$i)" = "$(git rev-parse HEAD^$i)" &&
+		i=`expr $i + 1` || return 1
+	done &&
+	git diff --exit-code &&
+	i=1 &&
+	while test $i -le 30
+	do
+		test -f c$i.c &&
+		i=`expr $i + 1` || return 1
+	done
+'
+
+test_done
-- 
1.5.6.1
Miklos Vajna· Jul 1, 2008, 02:37 UTC · re: Miklos Vajna · lore

[PATCH 13/14] git-commit-tree: make it usable from other builtins

Move all functionality (except option parsing) from cmd_commit_tree() to commit_tree(), so that other builtins can use it without a child process.

Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
 builtin-commit-tree.c |   71 ++++++++++++++++++++++++++++--------------------
 builtin.h             |    4 +++
 2 files changed, 45 insertions(+), 30 deletions(-)
Show changes to 2 files +45 −30

builtin-commit-tree.c, builtin.h

diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index 3881f6c..7a9a309 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -45,41 +45,19 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int cmd_commit_tree(int argc, const char **argv, const char *prefix)
+int commit_tree(const char *msg, unsigned char *tree,
+		struct commit_list *parents, unsigned char *ret)
 {
-	int i;
-	struct commit_list *parents = NULL;
-	unsigned char tree_sha1[20];
-	unsigned char commit_sha1[20];
-	struct strbuf buffer;
 	int encoding_is_utf8;
+	struct strbuf buffer;
 
-	git_config(git_default_config, NULL);
-
-	if (argc < 2)
-		usage(commit_tree_usage);
-	if (get_sha1(argv[1], tree_sha1))
-		die("Not a valid object name %s", argv[1]);
-
-	check_valid(tree_sha1, OBJ_TREE);
-	for (i = 2; i < argc; i += 2) {
-		unsigned char sha1[20];
-		const char *a, *b;
-		a = argv[i]; b = argv[i+1];
-		if (!b || strcmp(a, "-p"))
-			usage(commit_tree_usage);
-
-		if (get_sha1(b, sha1))
-			die("Not a valid object name %s", b);
-		check_valid(sha1, OBJ_COMMIT);
-		new_parent(lookup_commit(sha1), &parents);
-	}
+	check_valid(tree, OBJ_TREE);
 
 	/* Not having i18n.commitencoding is the same as having utf-8 */
 	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
 
 	strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
-	strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1));
+	strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
 
 	/*
 	 * NOTE! This ordering means that the same exact tree merged with a
@@ -102,14 +80,47 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 	strbuf_addch(&buffer, '\n');
 
 	/* And add the comment */
-	if (strbuf_read(&buffer, 0, 0) < 0)
-		die("git-commit-tree: read returned %s", strerror(errno));
+	strbuf_addstr(&buffer, msg);
 
 	/* And check the encoding */
 	if (encoding_is_utf8 && !is_utf8(buffer.buf))
 		fprintf(stderr, commit_utf8_warn);
 
-	if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) {
+	return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+}
+
+int cmd_commit_tree(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct commit_list *parents = NULL;
+	unsigned char tree_sha1[20];
+	unsigned char commit_sha1[20];
+	struct strbuf buffer = STRBUF_INIT;
+
+	git_config(git_default_config, NULL);
+
+	if (argc < 2)
+		usage(commit_tree_usage);
+	if (get_sha1(argv[1], tree_sha1))
+		die("Not a valid object name %s", argv[1]);
+
+	for (i = 2; i < argc; i += 2) {
+		unsigned char sha1[20];
+		const char *a, *b;
+		a = argv[i]; b = argv[i+1];
+		if (!b || strcmp(a, "-p"))
+			usage(commit_tree_usage);
+
+		if (get_sha1(b, sha1))
+			die("Not a valid object name %s", b);
+		check_valid(sha1, OBJ_COMMIT);
+		new_parent(lookup_commit(sha1), &parents);
+	}
+
+	if (strbuf_read(&buffer, 0, 0) < 0)
+		die("git-commit-tree: read returned %s", strerror(errno));
+
+	if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1)) {
 		printf("%s\n", sha1_to_hex(commit_sha1));
 		return 0;
 	}
diff --git a/builtin.h b/builtin.h
index 2b01fea..05ee56f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -3,6 +3,8 @@
 
 #include "git-compat-util.h"
 #include "strbuf.h"
+#include "cache.h"
+#include "commit.h"
 
 extern const char git_version_string[];
 extern const char git_usage_string[];
@@ -14,6 +16,8 @@ extern void prune_packed_objects(int);
 extern int read_line_with_nul(char *buf, int size, FILE *file);
 extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
 	struct strbuf *out);
+extern int commit_tree(const char *msg, unsigned char *tree,
+		struct commit_list *parents, unsigned char *ret);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
-- 
1.5.6.1
Miklos Vajna· Jul 1, 2008, 02:37 UTC · re: Miklos Vajna · lore

[PATCH 14/14] Build in merge

Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
 Makefile                                      |    2 +-
 builtin-merge.c                               | 1148 +++++++++++++++++++++++++
 builtin.h                                     |    1 +
 git-merge.sh => contrib/examples/git-merge.sh |    0 
 git.c                                         |    1 +
 5 files changed, 1151 insertions(+), 1 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)
Show changes to 5 files +1151 −1

Makefile, builtin-merge.c, builtin.h, git-merge.sh, git.c

diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
-SCRIPT_SH += git-merge.sh
 SCRIPT_SH += git-merge-stupid.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-parse-remote.sh
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
 BUILTIN_OBJS += builtin-ls-tree.o
 BUILTIN_OBJS += builtin-mailinfo.o
 BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
 BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..bb8d985
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1148 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "path-list.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+enum strategy {
+	DEFAULT_TWOHEAD = 1,
+	DEFAULT_OCTOPUS = 2,
+	NO_FAST_FORWARD = 4,
+	NO_TRIVIAL = 8
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git-merge [options] <remote>...",
+	"git-merge [options] <msg> HEAD <remote>",
+	NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+static struct commit_list *remoteheads;
+static unsigned char head[20], stash[20];
+static struct path_list use_strategies;
+static const char *branch;
+
+static struct path_list_item strategy_items[] = {
+	{ "recur",      (void *)NO_TRIVIAL },
+	{ "recursive",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },
+	{ "octopus",    (void *)DEFAULT_OCTOPUS },
+	{ "resolve",    (void *)0 },
+	{ "stupid",     (void *)0 },
+	{ "ours",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
+	{ "subtree",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
+};
+static struct path_list strategies = { strategy_items,
+	ARRAY_SIZE(strategy_items), 0, 0 };
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+	const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addf(buf, "%s\n\n", arg);
+		have_message = 1;
+	}
+	return 0;
+}
+
+static struct path_list_item *unsorted_path_list_lookup(const char *path,
+	struct path_list *list)
+{
+	int i;
+
+	if (!path)
+		return NULL;
+
+	for (i = 0; i < list->nr; i++)
+		if (!strcmp(path, list->items[i].path))
+			return &list->items[i];
+	return NULL;
+}
+
+static inline void path_list_append_strategy(struct path_list_item *item)
+{
+	path_list_append(item->path, &use_strategies)->util = item->util;
+}
+
+static int option_parse_strategy(const struct option *opt,
+	const char *arg, int unset)
+{
+	int i;
+	struct path_list_item *item = unsorted_path_list_lookup(arg, &strategies);
+
+	if (unset)
+		return 0;
+
+	if (item)
+		path_list_append_strategy(item);
+	else {
+		struct strbuf err;
+		strbuf_init(&err, 0);
+		for (i = 0; i < strategies.nr; i++)
+			strbuf_addf(&err, " %s", strategies.items[i].path);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", arg);
+		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+		exit(1);
+	}
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+		const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	OPT_BOOLEAN(0, "log", &option_log,
+		"add list of one-line log to merge commit message"),
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast forward (default)"),
+	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"message to be used for the merge commit (if any)",
+		option_parse_message),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die("could not run stash.");
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die("stash failed");
+	else if (!len)
+		return;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+	struct strbuf sb;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	strbuf_init(&sb, 0);
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	drop_save();
+}
+
+static void squash_message(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf out;
+	struct commit_list *j;
+	int fd;
+
+	printf("Squash commit -- not updating HEAD\n");
+	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die("Could not write to %s", git_path("SQUASH_MSG"));
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit = lookup_commit(head);
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	strbuf_init(&out, 0);
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+			NULL, NULL, rev.date_mode, 0);
+	}
+	write(fd, out.buf, out.len);
+	close(fd);
+	strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+	env[0] = index;
+	env[1] = NULL;
+
+	if (squash)
+		argv[1] = "1";
+	else
+		argv[1] = "0";
+	argv[2] = NULL;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message;
+
+	strbuf_init(&reflog_message, 0);
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message();
+	} else {
+		if (!merge_msg.len)
+			printf("No merge message -- not updating HEAD\n");
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_use_color_default > 0)
+			DIFF_OPT_SET(&opts, COLOR_DIFF);
+		if (diff_setup_done(&opts) < 0)
+			die("diff_setup_done failed");
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook("post-merge");
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct object *remote_head;
+	unsigned char branch_head[20], buf_sha[20];
+	struct strbuf buf;
+	const char *ptr;
+	int len = 0;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+	if (!remote_head)
+		die("'%s' does not point to a commit", remote);
+
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, remote);
+	resolve_ref(buf.buf, branch_head, 0, 0);
+
+	if (!hashcmp(remote_head->sha1, branch_head)) {
+		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+			sha1_to_hex(branch_head), remote);
+		return;
+	}
+	/* See if remote matches <name>~<number>, or <name>^ */
+	ptr = strrchr(remote, '^');
+	if (ptr && ptr[1] == '\0') {
+		for (len = 0, ptr = remote + strlen(remote);
+				remote < ptr && ptr[-1] == '^';
+				ptr--)
+			len++;
+	}
+	else {
+		ptr = strrchr(remote, '~');
+		if (ptr && ptr[1] != '0' && isdigit(ptr[1])) {
+			len = ptr-remote;
+			ptr++;
+			for (ptr++; *ptr; ptr++)
+				if (!isdigit(*ptr)) {
+					len = 0;
+					break;
+				}
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, len+11);
+		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+			strbuf_addf(msg,
+				"%s\t\tbranch '%s' (early part) of .\n",
+				sha1_to_hex(remote_head->sha1), truname.buf);
+			return;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		FILE *fp;
+		struct strbuf line;
+		char *ptr;
+
+		strbuf_init(&line, 0);
+		fp = fopen(git_path("FETCH_HEAD"), "r");
+		if (!fp)
+			die("could not open %s for reading: %s",
+				git_path("FETCH_HEAD"), strerror(errno));
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		return;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		const char **argv;
+		int argc;
+		char *buf;
+
+		buf = xstrdup(v);
+		argc = split_cmdline(buf, &argv);
+		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+		argc++;
+		parse_options(argc, argv, builtin_merge_options,
+			      builtin_merge_usage, 0);
+		free(buf);
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+	unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	opts.head_idx = 2;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(char *strategy, struct commit_list *common,
+	const char *head_arg)
+{
+	const char **args;
+	int i = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf;
+
+	args = xmalloc((4 + commit_list_count(common) +
+			commit_list_count(remoteheads)) * sizeof(char *));
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remoteheads; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remoteheads; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	const struct index_state *state = &the_index;
+	int i, ret = 0;
+
+	for (i = 0; i < state->cache_nr; i++)
+		if (ce_stage(state->cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	if (read_cache_unmerged())
+		die("you need to resolve your current index first");
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	dir.show_ignored = 1;
+	dir.exclude_per_dir = ".gitignore";
+	opts.dir = &dir;
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die("unable to write new index file");
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct path_list *list)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	list->strdup_paths = 1;
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			path_list_append(q, list);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			path_list_append(q, list);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, enum strategy strategy)
+{
+	struct path_list list;
+	int i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list);
+	if (list.nr) {
+		for (i = 0; i < list.nr; i++) {
+			struct path_list_item *item;
+
+			item = unsorted_path_list_lookup(list.items[i].path,
+				&strategies);
+			if (item)
+				path_list_append_strategy(item);
+		}
+		return;
+	}
+	for (i = 0; i < strategies.nr; i++)
+		if ((enum strategy)strategies.items[i].util & strategy)
+			path_list_append_strategy(&strategies.items[i]);
+}
+
+static int merge_trivial(void)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list parent;
+
+	write_tree_trivial(result_tree);
+	printf("Wonderful.\n");
+	parent.item = remoteheads->item;
+	parent.next = NULL;
+	commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+	finish(result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+	unsigned char *result_tree,
+	struct path_list_item *wt_strategy)
+{
+	struct commit_list *parents = NULL, *j;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	if (allow_fast_forward) {
+		parents = remoteheads;
+		commit_list_insert(lookup_commit(head), &parents);
+		parents = reduce_heads(parents);
+	} else {
+		struct commit_list **pptr = &parents;
+
+		pptr = &commit_list_insert(lookup_commit(head),
+				pptr)->next;
+		for (j = remoteheads; j; j = j->next)
+			pptr = &commit_list_insert(j->item, pptr)->next;
+	}
+	free_commit_list(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	strbuf_addf(&buf, "Merge made by %s.", wt_strategy->path);
+	finish(result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	FILE *fp;
+	int pos;
+
+	fp = fopen(git_path("MERGE_MSG"), "a");
+	if (!fp)
+		die("Could open %s for writing", git_path("MERGE_MSG"));
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere();
+	printf("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n");
+	return 1;
+}
+
+static inline unsigned nth_strategy_flags(struct path_list *s, int nth)
+{
+	return (unsigned) s->items[nth].util;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+	struct commit *second_token = NULL;
+	if (argc > 1) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die("'%s' is not a commit", argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	if (read_cache() < 0)
+		die("failed to read the cache");
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	struct strbuf buf;
+	const char *head_arg;
+	int flag, head_invalid = 0, i;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	struct path_list_item *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	setup_work_tree();
+	if (unmerged_cache())
+		die("You are in the middle of a conflicted merge.");
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = resolve_ref("HEAD", head, 0, &flag);
+	if (branch && flag & REF_ISSYMREF) {
+		const char *ptr = skip_prefix(branch, "refs/heads/");
+		if (ptr)
+			branch = ptr;
+	} else
+		head_invalid = 1;
+
+	git_config(git_merge_config, NULL);
+
+	/* for color.ui */
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
+	argc = parse_options(argc, argv, builtin_merge_options,
+			builtin_merge_usage, 0);
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die("You cannot combine --squash with --no-ff.");
+		option_commit = 0;
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+	strbuf_init(&buf, 0);
+
+	if (!have_message && is_old_style_invocation(argc, argv)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+	} else if (head_invalid) {
+		struct object *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die("Can merge only exactly one commit into "
+				"empty head");
+		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+		if (!remote_head)
+			die("%s - not something we can merge", argv[0]);
+		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+				DIE_ON_ERR);
+		reset_hard(remote_head->sha1, 0);
+		return 0;
+	} else {
+		struct strbuf msg;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged;
+		 * prepare the standard merge summary message to
+		 * be appended to the given message.  If remote
+		 * is invalid we will die later in the common
+		 * codepath so we discard the error in this
+		 * loop.
+		 */
+		strbuf_init(&msg, 0);
+		for (i = 0; i < argc; i++)
+			merge_name(argv[i], &msg);
+		fmt_merge_msg(option_log, &msg, &merge_msg);
+		if (merge_msg.len)
+			strbuf_setlen(&merge_msg, merge_msg.len-1);
+	}
+
+	if (head_invalid || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (i = 0; i < argc; i++)
+		strbuf_addf(&buf, " %s", argv[i]);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (i = 0; i < argc; i++) {
+		struct object *o;
+
+		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+		if (!o)
+			die("%s - not something we can merge", argv[i]);
+		remotes = &commit_list_insert(lookup_commit(o->sha1),
+			remotes)->next;
+
+		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+		setenv(buf.buf, argv[i], 1);
+		strbuf_reset(&buf);
+	}
+
+	if (!use_strategies.nr) {
+		if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies.nr; i++) {
+		if (nth_strategy_flags(&use_strategies, i) & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (nth_strategy_flags(&use_strategies, i) & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads->next)
+		common = get_merge_bases(lookup_commit(head),
+				remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(lookup_commit(head), &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+		DIE_ON_ERR);
+
+	if (!common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads->next && !common->next &&
+			common->item == remoteheads->item) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		return 0;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg;
+		struct object *o;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+
+		printf("Updating %s..%s\n",
+			hex,
+			find_unique_abbrev(remoteheads->item->object.sha1,
+			DEFAULT_ABBREV));
+		refresh_cache(REFRESH_QUIET);
+		strbuf_init(&msg, 0);
+		strbuf_addstr(&msg, "Fast forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+			0, NULL, OBJ_COMMIT);
+		if (!o)
+			return 1;
+
+		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+			return 1;
+
+		finish(o->sha1, msg.buf);
+		drop_save();
+		return 0;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_ERROR_ON_NO_NAME);
+			printf("Trying really trivial in-index merge...\n");
+			if (!read_tree_trivial(common->item->object.sha1,
+					head, remoteheads->item->object.sha1))
+				return merge_trivial();
+			printf("Nope.\n");
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(lookup_commit(head),
+				j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			return 0;
+		}
+	}
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies.nr != 1) {
+		/*
+		 * Stash away the local changes so that we can try more
+		 * than one.
+		 */
+		save_state();
+	} else {
+		memcpy(stash, null_sha1, 20);
+	}
+
+	for (i = 0; i < use_strategies.nr; i++) {
+		int ret;
+		if (i) {
+			printf("Rewinding the tree to pristine...\n");
+			restore_state();
+		}
+		if (use_strategies.nr != 1)
+			printf("Trying merge strategy %s...\n",
+				use_strategies.items[i].path);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = &use_strategies.items[i];
+
+		ret = try_merge_strategy(use_strategies.items[i].path,
+			common, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy =
+						&use_strategies.items[i];
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok)
+		return finish_automerge(common, result_tree, wt_strategy);
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state();
+		if (use_strategies.nr > 1)
+			fprintf(stderr,
+				"No merge strategy handled the merge.\n");
+		else
+			fprintf(stderr, "Merge with strategy %s failed.\n",
+				use_strategies.items[0].path);
+		return 2;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf("Rewinding the tree to pristine...\n");
+		restore_state();
+		printf("Using the %s to prepare resolving by hand.\n",
+			best_strategy->path);
+		try_merge_strategy(best_strategy->path, common, head_arg);
+	}
+
+	if (squash)
+		finish(NULL, NULL);
+	else {
+		int fd;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next)
+			strbuf_addf(&buf, "%s\n",
+				sha1_to_hex(j->item->object.sha1));
+		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing",
+				git_path("MERGE_HEAD"));
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_HEAD"));
+		close(fd);
+		strbuf_addch(&merge_msg, '\n');
+		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MSG"));
+		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+			merge_msg.len)
+			die("Could not write to %s", git_path("MERGE_MSG"));
+		close(fd);
+	}
+
+	if (merge_was_ok) {
+		fprintf(stderr, "Automatic merge went well; "
+			"stopped before committing as requested\n");
+		return 0;
+	} else
+		return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
-- 
1.5.6.1
Junio C Hamano· Jul 1, 2008, 06:23 UTC · re: Miklos Vajna · lore

Re: [PATCH 14/14] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 10 quoted lines
> +static int read_tree_trivial(unsigned char *common, unsigned char *head,
> +	unsigned char *one)
> +{
> +	int i, nr_trees = 0;
> +	struct tree *trees[MAX_UNPACK_TREES];
> +	struct tree_desc t[MAX_UNPACK_TREES];
> +	struct unpack_trees_options opts;
> +
> +	memset(&opts, 0, sizeof(opts));
> +	opts.head_idx = 2;
Do you still need this assignment here?
Show 26 quoted lines
> +int cmd_merge(int argc, const char **argv, const char *prefix)
> +{
> +	unsigned char result_tree[20];
> +	struct strbuf buf;
> +	const char *head_arg;
> +	int flag, head_invalid = 0, i;
> +	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
> +	struct commit_list *common = NULL;
> +	struct path_list_item *best_strategy = NULL, *wt_strategy = NULL;
> +	struct commit_list **remotes = &remoteheads;
> +
> +	setup_work_tree();
> +	if (unmerged_cache())
> +		die("You are in the middle of a conflicted merge.");
> +
> +	/*
> +	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
> +	 * current branch.
> +	 */
> +	branch = resolve_ref("HEAD", head, 0, &flag);
> +	if (branch && flag & REF_ISSYMREF) {
> +		const char *ptr = skip_prefix(branch, "refs/heads/");
> +		if (ptr)
> +			branch = ptr;
> +	} else
> +		head_invalid = 1;

Wait a minute... Are you calling a detached HEAD as head_invalid here? I am not too much worried about variable naming, but you later do ...

Show 12 quoted lines
> +	if (!have_message && is_old_style_invocation(argc, argv)) {
> ...
> +	} else if (head_invalid) {
> +		struct object *remote_head;
> +		/*
> +		 * If the merged head is a valid one there is no reason
> +		 * to forbid "git merge" into a branch yet to be born.
> +		 * We do the same for "git pull".
> +		 */
> +		if (argc != 1)
> +			die("Can merge only exactly one commit into "
> +				"empty head");

Which is about HEAD pointing at a branch that isn't born yet. They are two very different concepts.

Either the above "else if (head_invalid)" is wrong, or more likely the setting of head_invalid we saw earlier is wrong.

Probably what you meant was:
	- "char *branch" points at either "HEAD" (when detached) or
          the name of the branch (e.g. "master" when "refs/heads/master");
	- "unsigned char head[]" stores the commit object name of the
          current HEAD (or 0{40} if the current branch is unborn);
        - set head_invalid to true only when the current branch is unborn.
So perhaps...
	branch = resolve_ref("HEAD", head, 0, &flag);
        if (branch && (flag & REF_ISSYMREF) && !prefixcmp(branch, "refs/heads/"))
		branch += 11;
        head_invalid = is_null_sha1(head);

And probably we can even drop (flag & REF_ISSYMREF) from the above safely. Do we even care if the head is detached in this program? I doubt it.

Show 13 quoted lines
> ...
> +		strbuf_init(&msg, 0);
> +		strbuf_addstr(&msg, "Fast forward");
> +		if (have_message)
> +			strbuf_addstr(&msg,
> +				" (no commit created; -m option ignored)");
> +		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
> +			0, NULL, OBJ_COMMIT);
> +		if (!o)
> +			return 1;
> +
> +		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
> +			return 1;

Not exiting with 0 status from around here upon error is an improvement, but does the user see sensible error messages in addition to the exit status?

Getting warmer ;-)
Miklos Vajna· Jul 1, 2008, 12:50 UTC · re: Junio C Hamano · lore

Re: [PATCH 14/14] Build in merge

On Mon, Jun 30, 2008 at 11:23:17PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 14 quoted lines
> Miklos Vajna <vmiklos@frugalware.org> writes:
> 
> > +static int read_tree_trivial(unsigned char *common, unsigned char *head,
> > +	unsigned char *one)
> > +{
> > +	int i, nr_trees = 0;
> > +	struct tree *trees[MAX_UNPACK_TREES];
> > +	struct tree_desc t[MAX_UNPACK_TREES];
> > +	struct unpack_trees_options opts;
> > +
> > +	memset(&opts, 0, sizeof(opts));
> > +	opts.head_idx = 2;
> 
> Do you still need this assignment here?
No, it was duplicated.
Show 68 quoted lines
> > +int cmd_merge(int argc, const char **argv, const char *prefix)
> > +{
> > +	unsigned char result_tree[20];
> > +	struct strbuf buf;
> > +	const char *head_arg;
> > +	int flag, head_invalid = 0, i;
> > +	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
> > +	struct commit_list *common = NULL;
> > +	struct path_list_item *best_strategy = NULL, *wt_strategy = NULL;
> > +	struct commit_list **remotes = &remoteheads;
> > +
> > +	setup_work_tree();
> > +	if (unmerged_cache())
> > +		die("You are in the middle of a conflicted merge.");
> > +
> > +	/*
> > +	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
> > +	 * current branch.
> > +	 */
> > +	branch = resolve_ref("HEAD", head, 0, &flag);
> > +	if (branch && flag & REF_ISSYMREF) {
> > +		const char *ptr = skip_prefix(branch, "refs/heads/");
> > +		if (ptr)
> > +			branch = ptr;
> > +	} else
> > +		head_invalid = 1;
> 
> Wait a minute...  Are you calling a detached HEAD as head_invalid here?  I
> am not too much worried about variable naming, but you later do ...
> 
> > +	if (!have_message && is_old_style_invocation(argc, argv)) {
> > ...
> > +	} else if (head_invalid) {
> > +		struct object *remote_head;
> > +		/*
> > +		 * If the merged head is a valid one there is no reason
> > +		 * to forbid "git merge" into a branch yet to be born.
> > +		 * We do the same for "git pull".
> > +		 */
> > +		if (argc != 1)
> > +			die("Can merge only exactly one commit into "
> > +				"empty head");
> 
> Which is about HEAD pointing at a branch that isn't born yet.  They are
> two very different concepts.
> 
> Either the above "else if (head_invalid)" is wrong, or more likely the
> setting of head_invalid we saw earlier is wrong.
> 
> Probably what you meant was:
> 
> 	- "char *branch" points at either "HEAD" (when detached) or
>           the name of the branch (e.g. "master" when "refs/heads/master");
> 
> 	- "unsigned char head[]" stores the commit object name of the
>           current HEAD (or 0{40} if the current branch is unborn);
> 
>         - set head_invalid to true only when the current branch is unborn.
> 
> So perhaps...
> 
> 	branch = resolve_ref("HEAD", head, 0, &flag);
>         if (branch && (flag & REF_ISSYMREF) && !prefixcmp(branch, "refs/heads/"))
> 		branch += 11;
>         head_invalid = is_null_sha1(head);
> 
> And probably we can even drop (flag & REF_ISSYMREF) from the above safely.
> Do we even care if the head is detached in this program?  I doubt it.

You are right. I though we care about it, but now I realized such a comment was only in git-merge.annotated (in Dscho's fork), not in git-merge.sh. And yes, I mixed up head_invalid for two purposes. Thanks for clearing the situation.

Show 16 quoted lines
> > +		strbuf_init(&msg, 0);
> > +		strbuf_addstr(&msg, "Fast forward");
> > +		if (have_message)
> > +			strbuf_addstr(&msg,
> > +				" (no commit created; -m option ignored)");
> > +		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
> > +			0, NULL, OBJ_COMMIT);
> > +		if (!o)
> > +			return 1;
> > +
> > +		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
> > +			return 1;
> 
> Not exiting with 0 status from around here upon error is an improvement,
> but does the user see sensible error messages in addition to the exit
> status?
Now he/she does. :-)

(I updated my working branch on repo.or.cz, will send a patch soon, as well.)

Miklos Vajna· Jul 1, 2008, 13:18 UTC · re: Miklos Vajna · lore

[PATCH 14/14] Build in merge

Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
Interdiff: git diff 01a7dae..7709df7
Individual commits (there is only one change this time): git log 7709df7

Ah and please don't forget to apply the testsuite fix I sent in $gmane/86981. (Or is it easier if I resend the whole series?)

 Makefile                                      |    2 +-
 builtin-merge.c                               | 1158 +++++++++++++++++++++++++
 builtin.h                                     |    1 +
 git-merge.sh => contrib/examples/git-merge.sh |    0 
 git.c                                         |    1 +
 5 files changed, 1161 insertions(+), 1 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)
Show changes to 5 files +1161 −1

Makefile, builtin-merge.c, builtin.h, git-merge.sh, git.c

diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
-SCRIPT_SH += git-merge.sh
 SCRIPT_SH += git-merge-stupid.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-parse-remote.sh
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
 BUILTIN_OBJS += builtin-ls-tree.o
 BUILTIN_OBJS += builtin-mailinfo.o
 BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
 BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..b261993
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1158 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "path-list.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+enum strategy {
+	DEFAULT_TWOHEAD = 1,
+	DEFAULT_OCTOPUS = 2,
+	NO_FAST_FORWARD = 4,
+	NO_TRIVIAL = 8
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git-merge [options] <remote>...",
+	"git-merge [options] <msg> HEAD <remote>",
+	NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+static struct commit_list *remoteheads;
+static unsigned char head[20], stash[20];
+static struct path_list use_strategies;
+static const char *branch;
+
+static struct path_list_item strategy_items[] = {
+	{ "recur",      (void *)NO_TRIVIAL },
+	{ "recursive",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },
+	{ "octopus",    (void *)DEFAULT_OCTOPUS },
+	{ "resolve",    (void *)0 },
+	{ "stupid",     (void *)0 },
+	{ "ours",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
+	{ "subtree",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
+};
+static struct path_list strategies = { strategy_items,
+	ARRAY_SIZE(strategy_items), 0, 0 };
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+	const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addf(buf, "%s\n\n", arg);
+		have_message = 1;
+	}
+	return 0;
+}
+
+static struct path_list_item *unsorted_path_list_lookup(const char *path,
+	struct path_list *list)
+{
+	int i;
+
+	if (!path)
+		return NULL;
+
+	for (i = 0; i < list->nr; i++)
+		if (!strcmp(path, list->items[i].path))
+			return &list->items[i];
+	return NULL;
+}
+
+static inline void path_list_append_strategy(struct path_list_item *item)
+{
+	path_list_append(item->path, &use_strategies)->util = item->util;
+}
+
+static int option_parse_strategy(const struct option *opt,
+	const char *arg, int unset)
+{
+	int i;
+	struct path_list_item *item = unsorted_path_list_lookup(arg, &strategies);
+
+	if (unset)
+		return 0;
+
+	if (item)
+		path_list_append_strategy(item);
+	else {
+		struct strbuf err;
+		strbuf_init(&err, 0);
+		for (i = 0; i < strategies.nr; i++)
+			strbuf_addf(&err, " %s", strategies.items[i].path);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", arg);
+		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+		exit(1);
+	}
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+		const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	OPT_BOOLEAN(0, "log", &option_log,
+		"add list of one-line log to merge commit message"),
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast forward (default)"),
+	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"message to be used for the merge commit (if any)",
+		option_parse_message),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die("could not run stash.");
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die("stash failed");
+	else if (!len)
+		return;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+	struct strbuf sb;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	strbuf_init(&sb, 0);
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	drop_save();
+}
+
+static void squash_message(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf out;
+	struct commit_list *j;
+	int fd;
+
+	printf("Squash commit -- not updating HEAD\n");
+	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die("Could not write to %s", git_path("SQUASH_MSG"));
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit = lookup_commit(head);
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	strbuf_init(&out, 0);
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+			NULL, NULL, rev.date_mode, 0);
+	}
+	write(fd, out.buf, out.len);
+	close(fd);
+	strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+	env[0] = index;
+	env[1] = NULL;
+
+	if (squash)
+		argv[1] = "1";
+	else
+		argv[1] = "0";
+	argv[2] = NULL;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message;
+
+	strbuf_init(&reflog_message, 0);
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message();
+	} else {
+		if (!merge_msg.len)
+			printf("No merge message -- not updating HEAD\n");
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_use_color_default > 0)
+			DIFF_OPT_SET(&opts, COLOR_DIFF);
+		if (diff_setup_done(&opts) < 0)
+			die("diff_setup_done failed");
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook("post-merge");
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct object *remote_head;
+	unsigned char branch_head[20], buf_sha[20];
+	struct strbuf buf;
+	const char *ptr;
+	int len, early;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+	if (!remote_head)
+		die("'%s' does not point to a commit", remote);
+
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, remote);
+	resolve_ref(buf.buf, branch_head, 0, 0);
+
+	if (!hashcmp(remote_head->sha1, branch_head)) {
+		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+			sha1_to_hex(branch_head), remote);
+		return;
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, len+11);
+		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    sha1_to_hex(remote_head->sha1),
+				    truname.buf,
+				    (early ? " (early part)" : ""));
+			return;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		FILE *fp;
+		struct strbuf line;
+		char *ptr;
+
+		strbuf_init(&line, 0);
+		fp = fopen(git_path("FETCH_HEAD"), "r");
+		if (!fp)
+			die("could not open %s for reading: %s",
+				git_path("FETCH_HEAD"), strerror(errno));
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		return;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		const char **argv;
+		int argc;
+		char *buf;
+
+		buf = xstrdup(v);
+		argc = split_cmdline(buf, &argv);
+		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+		argc++;
+		parse_options(argc, argv, builtin_merge_options,
+			      builtin_merge_usage, 0);
+		free(buf);
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+	unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(char *strategy, struct commit_list *common,
+	const char *head_arg)
+{
+	const char **args;
+	int i = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf;
+
+	args = xmalloc((4 + commit_list_count(common) +
+			commit_list_count(remoteheads)) * sizeof(char *));
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remoteheads; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remoteheads; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	const struct index_state *state = &the_index;
+	int i, ret = 0;
+
+	for (i = 0; i < state->cache_nr; i++)
+		if (ce_stage(state->cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	if (read_cache_unmerged())
+		die("you need to resolve your current index first");
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	dir.show_ignored = 1;
+	dir.exclude_per_dir = ".gitignore";
+	opts.dir = &dir;
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die("unable to write new index file");
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct path_list *list)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	list->strdup_paths = 1;
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			path_list_append(q, list);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			path_list_append(q, list);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, enum strategy strategy)
+{
+	struct path_list list;
+	int i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list);
+	if (list.nr) {
+		for (i = 0; i < list.nr; i++) {
+			struct path_list_item *item;
+
+			item = unsorted_path_list_lookup(list.items[i].path,
+				&strategies);
+			if (item)
+				path_list_append_strategy(item);
+		}
+		return;
+	}
+	for (i = 0; i < strategies.nr; i++)
+		if ((enum strategy)strategies.items[i].util & strategy)
+			path_list_append_strategy(&strategies.items[i]);
+}
+
+static int merge_trivial(void)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list parent;
+
+	write_tree_trivial(result_tree);
+	printf("Wonderful.\n");
+	parent.item = remoteheads->item;
+	parent.next = NULL;
+	commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+	finish(result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+	unsigned char *result_tree,
+	struct path_list_item *wt_strategy)
+{
+	struct commit_list *parents = NULL, *j;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	if (allow_fast_forward) {
+		parents = remoteheads;
+		commit_list_insert(lookup_commit(head), &parents);
+		parents = reduce_heads(parents);
+	} else {
+		struct commit_list **pptr = &parents;
+
+		pptr = &commit_list_insert(lookup_commit(head),
+				pptr)->next;
+		for (j = remoteheads; j; j = j->next)
+			pptr = &commit_list_insert(j->item, pptr)->next;
+	}
+	free_commit_list(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	strbuf_addf(&buf, "Merge made by %s.", wt_strategy->path);
+	finish(result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	FILE *fp;
+	int pos;
+
+	fp = fopen(git_path("MERGE_MSG"), "a");
+	if (!fp)
+		die("Could open %s for writing", git_path("MERGE_MSG"));
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere();
+	printf("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n");
+	return 1;
+}
+
+static inline unsigned nth_strategy_flags(struct path_list *s, int nth)
+{
+	return (unsigned) s->items[nth].util;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+	struct commit *second_token = NULL;
+	if (argc > 1) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die("'%s' is not a commit", argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	if (read_cache() < 0)
+		die("failed to read the cache");
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	struct strbuf buf;
+	const char *head_arg;
+	int flag, head_invalid = 0, i;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	struct path_list_item *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	setup_work_tree();
+	if (unmerged_cache())
+		die("You are in the middle of a conflicted merge.");
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = resolve_ref("HEAD", head, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
+	if (is_null_sha1(head))
+		head_invalid = 1;
+
+	git_config(git_merge_config, NULL);
+
+	/* for color.ui */
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
+	argc = parse_options(argc, argv, builtin_merge_options,
+			builtin_merge_usage, 0);
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die("You cannot combine --squash with --no-ff.");
+		option_commit = 0;
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+	strbuf_init(&buf, 0);
+
+	if (!have_message && is_old_style_invocation(argc, argv)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+	} else if (head_invalid) {
+		struct object *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die("Can merge only exactly one commit into "
+				"empty head");
+		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+		if (!remote_head)
+			die("%s - not something we can merge", argv[0]);
+		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+				DIE_ON_ERR);
+		reset_hard(remote_head->sha1, 0);
+		return 0;
+	} else {
+		struct strbuf msg;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged;
+		 * prepare the standard merge summary message to
+		 * be appended to the given message.  If remote
+		 * is invalid we will die later in the common
+		 * codepath so we discard the error in this
+		 * loop.
+		 */
+		strbuf_init(&msg, 0);
+		for (i = 0; i < argc; i++)
+			merge_name(argv[i], &msg);
+		fmt_merge_msg(option_log, &msg, &merge_msg);
+		if (merge_msg.len)
+			strbuf_setlen(&merge_msg, merge_msg.len-1);
+	}
+
+	if (head_invalid || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (i = 0; i < argc; i++)
+		strbuf_addf(&buf, " %s", argv[i]);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (i = 0; i < argc; i++) {
+		struct object *o;
+
+		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+		if (!o)
+			die("%s - not something we can merge", argv[i]);
+		remotes = &commit_list_insert(lookup_commit(o->sha1),
+			remotes)->next;
+
+		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+		setenv(buf.buf, argv[i], 1);
+		strbuf_reset(&buf);
+	}
+
+	if (!use_strategies.nr) {
+		if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies.nr; i++) {
+		if (nth_strategy_flags(&use_strategies, i) & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (nth_strategy_flags(&use_strategies, i) & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads->next)
+		common = get_merge_bases(lookup_commit(head),
+				remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(lookup_commit(head), &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+		DIE_ON_ERR);
+
+	if (!common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads->next && !common->next &&
+			common->item == remoteheads->item) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		return 0;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg;
+		struct object *o;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(remoteheads->item->object.sha1,
+			DEFAULT_ABBREV));
+
+		printf("Updating %s..%s\n",
+			find_unique_abbrev(head, DEFAULT_ABBREV),
+			hex);
+		refresh_cache(REFRESH_QUIET);
+		strbuf_init(&msg, 0);
+		strbuf_addstr(&msg, "Fast forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+			0, NULL, OBJ_COMMIT);
+		if (!o) {
+			error("%s is not a commit", hex);
+			return 1;
+		}
+
+		if (checkout_fast_forward(head, remoteheads->item->object.sha1)) {
+			error("failed to fast-forward to %s", hex);
+			return 1;
+		}
+
+		finish(o->sha1, msg.buf);
+		drop_save();
+		return 0;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_ERROR_ON_NO_NAME);
+			printf("Trying really trivial in-index merge...\n");
+			if (!read_tree_trivial(common->item->object.sha1,
+					head, remoteheads->item->object.sha1))
+				return merge_trivial();
+			printf("Nope.\n");
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(lookup_commit(head),
+				j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			return 0;
+		}
+	}
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies.nr != 1) {
+		/*
+		 * Stash away the local changes so that we can try more
+		 * than one.
+		 */
+		save_state();
+	} else {
+		memcpy(stash, null_sha1, 20);
+	}
+
+	for (i = 0; i < use_strategies.nr; i++) {
+		int ret;
+		if (i) {
+			printf("Rewinding the tree to pristine...\n");
+			restore_state();
+		}
+		if (use_strategies.nr != 1)
+			printf("Trying merge strategy %s...\n",
+				use_strategies.items[i].path);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = &use_strategies.items[i];
+
+		ret = try_merge_strategy(use_strategies.items[i].path,
+			common, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy =
+						&use_strategies.items[i];
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok)
+		return finish_automerge(common, result_tree, wt_strategy);
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state();
+		if (use_strategies.nr > 1)
+			fprintf(stderr,
+				"No merge strategy handled the merge.\n");
+		else
+			fprintf(stderr, "Merge with strategy %s failed.\n",
+				use_strategies.items[0].path);
+		return 2;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf("Rewinding the tree to pristine...\n");
+		restore_state();
+		printf("Using the %s to prepare resolving by hand.\n",
+			best_strategy->path);
+		try_merge_strategy(best_strategy->path, common, head_arg);
+	}
+
+	if (squash)
+		finish(NULL, NULL);
+	else {
+		int fd;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next)
+			strbuf_addf(&buf, "%s\n",
+				sha1_to_hex(j->item->object.sha1));
+		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing",
+				git_path("MERGE_HEAD"));
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_HEAD"));
+		close(fd);
+		strbuf_addch(&merge_msg, '\n');
+		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MSG"));
+		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+			merge_msg.len)
+			die("Could not write to %s", git_path("MERGE_MSG"));
+		close(fd);
+	}
+
+	if (merge_was_ok) {
+		fprintf(stderr, "Automatic merge went well; "
+			"stopped before committing as requested\n");
+		return 0;
+	} else
+		return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
-- 
1.5.6.1
Junio C Hamano· Jul 1, 2008, 23:55 UTC · re: Miklos Vajna · lore

Re: [PATCH 14/14] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 10 quoted lines
> Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
> ---
>
> Interdiff: git diff 01a7dae..7709df7
>
> Individual commits (there is only one change this time): git log 7709df7
>
> Ah and please don't forget to apply the testsuite fix I sent in
> $gmane/86981. (Or is it easier if I resend the whole series?)
Are you sure about that?  I've been deliberately fixing that patch.
As far as I understand it, your series:
 - starts out building infrastructure;
 - adds tests;
 - finally replaces git-merge with the new implementation.

and the test in question comes when git-merge built from that version is still the scripted version with limitation.

So if you build and test after applying individual steps (you sure do that, don't you?), the test has to start with expecting failure (from the scripted git-merge that has the octopus limitation) and then later changed to expect success when git-merge is replaced.

Have you looked at the series I fixed up before pushing out in 'pu' last night, specifically 5948e2a and 01a7dae? If you actually built and tested 5948e2a, you'd notice that the test in that commit *must* expect failure.

Miklos Vajna· Jul 2, 2008, 07:43 UTC · re: Junio C Hamano · lore

Re: [PATCH 14/14] Build in merge

On Tue, Jul 01, 2008 at 04:55:21PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Have you looked at the series I fixed up before pushing out in 'pu' last
> night, specifically 5948e2a and 01a7dae?  If you actually built and tested
> 5948e2a, you'd notice that the test in that commit *must* expect failure.
Aah. Now I see the point, thanks for explaining! :-)
Junio C Hamano· Jul 6, 2008, 08:50 UTC · re: Miklos Vajna · lore

Re: [PATCH 14/14] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 37 quoted lines
> diff --git a/builtin-merge.c b/builtin-merge.c
> new file mode 100644
> index 0000000..b261993
> --- /dev/null
> +++ b/builtin-merge.c
> @@ -0,0 +1,1158 @@
> +/*
> + * Builtin "git merge"
> + *
> + * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
> + *
> + * Based on git-merge.sh by Junio C Hamano.
> + */
> +
> +#include "cache.h"
> +#include "parse-options.h"
> +#include "builtin.h"
> +#include "run-command.h"
> +#include "path-list.h"
> +#include "diff.h"
> +#include "refs.h"
> +#include "commit.h"
> +#include "diffcore.h"
> +#include "revision.h"
> +#include "unpack-trees.h"
> +#include "cache-tree.h"
> +#include "dir.h"
> +#include "utf8.h"
> +#include "log-tree.h"
> +#include "color.h"
> +
> +enum strategy {
> +	DEFAULT_TWOHEAD = 1,
> +	DEFAULT_OCTOPUS = 2,
> +	NO_FAST_FORWARD = 4,
> +	NO_TRIVIAL = 8
> +};

Usually "enum foo" consists of possible values of "foo". But this is not a list of strategies. These are possible attributes to strategies.

Show 26 quoted lines
> +static const char * const builtin_merge_usage[] = {
> +	"git-merge [options] <remote>...",
> +	"git-merge [options] <msg> HEAD <remote>",
> +	NULL
> +};
> +
> +static int show_diffstat = 1, option_log, squash;
> +static int option_commit = 1, allow_fast_forward = 1;
> +static int allow_trivial = 1, have_message;
> +static struct strbuf merge_msg;
> +static struct commit_list *remoteheads;
> +static unsigned char head[20], stash[20];
> +static struct path_list use_strategies;
> +static const char *branch;
> +
> +static struct path_list_item strategy_items[] = {
> +	{ "recur",      (void *)NO_TRIVIAL },
> +	{ "recursive",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },
> +	{ "octopus",    (void *)DEFAULT_OCTOPUS },
> +	{ "resolve",    (void *)0 },
> +	{ "stupid",     (void *)0 },
> +	{ "ours",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
> +	{ "subtree",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
> +};
> +static struct path_list strategies = { strategy_items,
> +	ARRAY_SIZE(strategy_items), 0, 0 };
This declaration is funnily line-wrapped.
        static struct path_list strategies = {
                strategy_items, ARRAY_SIZE(strategy_items), 0, 0,
        };

But more problematic is that a path_list is inherently a dynamic data structure (you can add and it reallocs), and this use of relying on the knowledge that you happen to never add anything (nor subtract anything) from the list is a mere hack. If on the other hand you (and more importantly other people who touch this implementation later) will never add or remove items from this "strategies" array, you should make sure at the interface level that nobody can -- one way to do so is not to abuse path_list for something like this.

Come to think of it, wasn't the reason why the earlier "Why do you need such distracting casts all over the place?" issue came up in another patch because of this kind of (ab)use of path_list, which is an inappropriate data structure for the job?

You would perhaps define:
	#define DEFAULT_TWOHEAD (1<<0)
	#define DEFAULT_OCTOPUS (1<<1)
	#define NO_FAST_FORWARD (1<<2)
	#define NO_TRIVIAL	(1<<3)
	static struct strategy {
        	char *name;
                unsigned attr;
	} all_strategy[] = {
                { "octopus",    DEFAULT_OCTOPUS },
                { "ours",       (NO_FAST_FORWARD | NO_TRIVIAL) },
                { "recur",      NO_TRIVIAL },
                { "recursive",  (DEFAULT_TWOHEAD | NO_TRIVIAL) },
                { "resolve",    0 },
                { "stupid",     0 },
                { "subtree",    (NO_FAST_FORWARD | NO_TRIVIAL) },
        };

And "unsorted_path_list_lookup()" can now become much more natural, perhaps:

	static struct strategy *get_strategy(const char *name);
which has a more natural function signature and much better name.

Then, you would keep an array of pointers into all_strategy[] array to represent the list of "-s strategy" given by the user:

	static struct strategy *use_strategy;
	static int use_strategy_alloc, use_strategy_nr;

and have a function that use s the standard ALLOC_GROW() and friends to grow this. The function will be named and written more naturally (i.e. path_list_append_strategy() can go) --- this does not have anything to do with path_list, but it is about "merge strategy".

But 99.9% of the time you would not have more than one elements ;-).
Junio C Hamano· Jul 6, 2008, 09:43 UTC · re: Junio C Hamano · lore

Re: [PATCH 14/14] Build in merge

Junio C Hamano <gitster@pobox.com> writes:
Show 32 quoted lines
> You would perhaps define:
>
> 	#define DEFAULT_TWOHEAD (1<<0)
> 	#define DEFAULT_OCTOPUS (1<<1)
> 	#define NO_FAST_FORWARD (1<<2)
> 	#define NO_TRIVIAL	(1<<3)
>
> 	static struct strategy {
>         	char *name;
>                 unsigned attr;
> 	} all_strategy[] = {
>                 { "octopus",    DEFAULT_OCTOPUS },
>                 { "ours",       (NO_FAST_FORWARD | NO_TRIVIAL) },
>                 { "recur",      NO_TRIVIAL },
>                 { "recursive",  (DEFAULT_TWOHEAD | NO_TRIVIAL) },
>                 { "resolve",    0 },
>                 { "stupid",     0 },
>                 { "subtree",    (NO_FAST_FORWARD | NO_TRIVIAL) },
>         };
>
> And "unsorted_path_list_lookup()" can now become much more natural,
> perhaps:
>
> 	static struct strategy *get_strategy(const char *name);
>
> which has a more natural function signature and much better name.
>
> Then, you would keep an array of pointers into all_strategy[] array to
> represent the list of "-s strategy" given by the user:
>
> 	static struct strategy *use_strategy;
> 	static int use_strategy_alloc, use_strategy_nr;

Sorry, I have an obvious typo here. "use_strategy" will be dynamic array of pointers into all_strategy[] so its definition would be:

	static struct strategy **use_strategy;
> and have a function that use s the standard ALLOC_GROW() and friends to
> grow this.  The function will be named and written more naturally
> (i.e. path_list_append_strategy() can go) --- this does not have anything
> to do with path_list, but it is about "merge strategy".
Miklos Vajna· Jul 7, 2008, 17:17 UTC · re: Junio C Hamano · lore

Re: [PATCH 14/14] Build in merge

On Sun, Jul 06, 2008 at 02:43:41AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 10 quoted lines
> > Then, you would keep an array of pointers into all_strategy[] array to
> > represent the list of "-s strategy" given by the user:
> >
> > 	static struct strategy *use_strategy;
> > 	static int use_strategy_alloc, use_strategy_nr;
> 
> Sorry, I have an obvious typo here.  "use_strategy" will be dynamic array
> of pointers into all_strategy[] so its definition would be:
> 
> 	static struct strategy **use_strategy;
I think there are two possibilities here:
1) Append custom strategies to all_strategy and have only pointers in
use_strategy.
This is what you suggest in your second mail.
2) Copy the names and attributes from all_strategy to use_strategies and
append custom strategies there.
This is what I do at the moment.

I think it's better not to modify all_strategy, it serves as a reference, for example later this would allow us to check if the used merge strategy is a predefined or a custom one.

Or is there any strong reason introducing all_strategy_alloc, all_strategy_nr and using ALLOC_GROW() with all_strategy instead of with use_strategy?

Thanks.
Junio C Hamano· Jul 7, 2008, 18:15 UTC · re: Miklos Vajna · lore

Re: [PATCH 14/14] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
> I think it's better not to modify all_strategy, it serves as a
> reference, for example later this would allow us to check if the used
> merge strategy is a predefined or a custom one.
I do not get you on this point.  Which one is nicer?
 (1) Have two lists, perhaps all_* and user_*.  The logic that finds a
     strategy searches in two lists.  The logic that checks if a given
     strategy is built-in checks if it is on all_* list.
 (2) Have a single list, but add a boolean "unsigned is_builtin:1" to each
     element of it.  The logic that finds a strategy looks in this single
     list.  The logic that checks if a given strategy is built-in looks at
     the strategy instance and it has the bit already.
You seem to be advocating (1) but I do not understand why...
Miklos Vajna· Jul 7, 2008, 23:42 UTC · re: Junio C Hamano · lore

[PATCH] Build in merge

Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
On Mon, Jul 07, 2008 at 11:15:09AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 15 quoted lines
> I do not get you on this point.  Which one is nicer?
>
>  (1) Have two lists, perhaps all_* and user_*.  The logic that finds a
>      strategy searches in two lists.  The logic that checks if a given
>      strategy is built-in checks if it is on all_* list.
>
>  (2) Have a single list, but add a boolean "unsigned is_builtin:1" to
>  each
>      element of it.  The logic that finds a strategy looks in this
>      single
>      list.  The logic that checks if a given strategy is built-in
>      looks at
>      the strategy instance and it has the bit already.
>
> You seem to be advocating (1) but I do not understand why...

Ah, OK. For now, I just added an "unsigned enabled:1;". Later we can add an "unsigned is_buildin:1;" as well, but currently we die with earlier with a "Could not find merge strategy" error message, so is_builtin would be always true.

So here is a version, this time without the use_strategies list.
Interdiff: git diff 638c51f..1e74660
 Makefile                                      |    2 +-
 builtin-merge.c                               | 1142 +++++++++++++++++++++++++
 builtin.h                                     |    1 +
 git-merge.sh => contrib/examples/git-merge.sh |    0
 git.c                                         |    1 +
 t/t7602-merge-octopus-many.sh                 |    2 +-
 6 files changed, 1146 insertions(+), 2 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)
Show changes to 6 files +1146 −2

Makefile, builtin-merge.c, builtin.h, git-merge.sh, git.c, t/t7602-merge-octopus-many.sh

diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
-SCRIPT_SH += git-merge.sh
 SCRIPT_SH += git-merge-stupid.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-parse-remote.sh
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
 BUILTIN_OBJS += builtin-ls-tree.o
 BUILTIN_OBJS += builtin-mailinfo.o
 BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
 BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..6d801dd
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1142 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL      (1<<3)
+
+struct strategy {
+	const char *name;
+	unsigned attr;
+	unsigned enabled:1;
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git-merge [options] <remote>...",
+	"git-merge [options] <msg> HEAD <remote>",
+	NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+static struct commit_list *remoteheads;
+static unsigned char head[20], stash[20];
+static const char *branch;
+
+static struct strategy all_strategy[] = {
+	{ "recur",      NO_TRIVIAL, 0 },
+	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL, 0 },
+	{ "octopus",    DEFAULT_OCTOPUS, 0 },
+	{ "resolve",    0, 0 },
+	{ "stupid",     0, 0 },
+	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL, 0 },
+	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL, 0 },
+};
+/*
+ * This contains the number of enabled variables, so you don't have to
+ * scan through all_strategies all the time.
+ */
+static int enabled_strategies;
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+	const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addf(buf, "%s\n\n", arg);
+		have_message = 1;
+	}
+	return 0;
+}
+
+static void enable_strategy(const char *name)
+{
+	int i;
+	struct strbuf err;
+
+	enabled_strategies++;
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (!strcmp(all_strategy[i].name, name)) {
+			all_strategy[i].enabled = 1;
+			return;
+		}
+
+	strbuf_init(&err, 0);
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		strbuf_addf(&err, " %s", all_strategy[i].name);
+	fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+	fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+	exit(1);
+}
+
+static int option_parse_strategy(const struct option *opt,
+	const char *arg, int unset)
+{
+	if (unset)
+		return 0;
+
+	enable_strategy(arg);
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+		const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	OPT_BOOLEAN(0, "log", &option_log,
+		"add list of one-line log to merge commit message"),
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast forward (default)"),
+	OPT_CALLBACK('s', "strategy", &all_strategy, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"message to be used for the merge commit (if any)",
+		option_parse_message),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die("could not run stash.");
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die("stash failed");
+	else if (!len)
+		return;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+	struct strbuf sb;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	strbuf_init(&sb, 0);
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	drop_save();
+}
+
+static void squash_message(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf out;
+	struct commit_list *j;
+	int fd;
+
+	printf("Squash commit -- not updating HEAD\n");
+	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die("Could not write to %s", git_path("SQUASH_MSG"));
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit = lookup_commit(head);
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	strbuf_init(&out, 0);
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+			NULL, NULL, rev.date_mode, 0);
+	}
+	write(fd, out.buf, out.len);
+	close(fd);
+	strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+	env[0] = index;
+	env[1] = NULL;
+
+	if (squash)
+		argv[1] = "1";
+	else
+		argv[1] = "0";
+	argv[2] = NULL;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message;
+
+	strbuf_init(&reflog_message, 0);
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message();
+	} else {
+		if (!merge_msg.len)
+			printf("No merge message -- not updating HEAD\n");
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_use_color_default > 0)
+			DIFF_OPT_SET(&opts, COLOR_DIFF);
+		if (diff_setup_done(&opts) < 0)
+			die("diff_setup_done failed");
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook("post-merge");
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct object *remote_head;
+	unsigned char branch_head[20], buf_sha[20];
+	struct strbuf buf;
+	const char *ptr;
+	int len, early;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+	if (!remote_head)
+		die("'%s' does not point to a commit", remote);
+
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, remote);
+	resolve_ref(buf.buf, branch_head, 0, 0);
+
+	if (!hashcmp(remote_head->sha1, branch_head)) {
+		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+			sha1_to_hex(branch_head), remote);
+		return;
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, len+11);
+		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    sha1_to_hex(remote_head->sha1),
+				    truname.buf,
+				    (early ? " (early part)" : ""));
+			return;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		FILE *fp;
+		struct strbuf line;
+		char *ptr;
+
+		strbuf_init(&line, 0);
+		fp = fopen(git_path("FETCH_HEAD"), "r");
+		if (!fp)
+			die("could not open %s for reading: %s",
+				git_path("FETCH_HEAD"), strerror(errno));
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		return;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		const char **argv;
+		int argc;
+		char *buf;
+
+		buf = xstrdup(v);
+		argc = split_cmdline(buf, &argv);
+		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+		argc++;
+		parse_options(argc, argv, builtin_merge_options,
+			      builtin_merge_usage, 0);
+		free(buf);
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+	unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+	const char *head_arg)
+{
+	const char **args;
+	int i = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf;
+
+	args = xmalloc((4 + commit_list_count(common) +
+			commit_list_count(remoteheads)) * sizeof(char *));
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remoteheads; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remoteheads; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	const struct index_state *state = &the_index;
+	int i, ret = 0;
+
+	for (i = 0; i < state->cache_nr; i++)
+		if (ce_stage(state->cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	if (read_cache_unmerged())
+		die("you need to resolve your current index first");
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	dir.show_ignored = 1;
+	dir.exclude_per_dir = ".gitignore";
+	opts.dir = &dir;
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die("unable to write new index file");
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct strategy **list,
+		int *nr, int *alloc)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+	struct strategy *list = NULL;
+	int list_alloc = 0, list_nr = 0, i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list, &list_nr, &list_alloc);
+	if (list != NULL) {
+		for (i = 0; i < list_nr; i++)
+			enable_strategy(list[i].name);
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (all_strategy[i].attr & attr)
+			enable_strategy(all_strategy[i].name);
+}
+
+static int merge_trivial(void)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list parent;
+
+	write_tree_trivial(result_tree);
+	printf("Wonderful.\n");
+	parent.item = remoteheads->item;
+	parent.next = NULL;
+	commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+	finish(result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+	unsigned char *result_tree,
+	const char *wt_strategy)
+{
+	struct commit_list *parents = NULL, *j;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	if (allow_fast_forward) {
+		parents = remoteheads;
+		commit_list_insert(lookup_commit(head), &parents);
+		parents = reduce_heads(parents);
+	} else {
+		struct commit_list **pptr = &parents;
+
+		pptr = &commit_list_insert(lookup_commit(head),
+				pptr)->next;
+		for (j = remoteheads; j; j = j->next)
+			pptr = &commit_list_insert(j->item, pptr)->next;
+	}
+	free_commit_list(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
+	finish(result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	FILE *fp;
+	int pos;
+
+	fp = fopen(git_path("MERGE_MSG"), "a");
+	if (!fp)
+		die("Could open %s for writing", git_path("MERGE_MSG"));
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere();
+	printf("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n");
+	return 1;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+	struct commit *second_token = NULL;
+	if (argc > 1) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die("'%s' is not a commit", argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	if (read_cache() < 0)
+		die("failed to read the cache");
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	struct strbuf buf;
+	const char *head_arg;
+	int flag, head_invalid = 0, i, first_strategy = 1;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	const char *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	setup_work_tree();
+	if (unmerged_cache())
+		die("You are in the middle of a conflicted merge.");
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = resolve_ref("HEAD", head, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
+	if (is_null_sha1(head))
+		head_invalid = 1;
+
+	git_config(git_merge_config, NULL);
+
+	/* for color.ui */
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
+	argc = parse_options(argc, argv, builtin_merge_options,
+			builtin_merge_usage, 0);
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die("You cannot combine --squash with --no-ff.");
+		option_commit = 0;
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+	strbuf_init(&buf, 0);
+
+	if (!have_message && is_old_style_invocation(argc, argv)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+	} else if (head_invalid) {
+		struct object *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die("Can merge only exactly one commit into "
+				"empty head");
+		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+		if (!remote_head)
+			die("%s - not something we can merge", argv[0]);
+		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+				DIE_ON_ERR);
+		reset_hard(remote_head->sha1, 0);
+		return 0;
+	} else {
+		struct strbuf msg;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged;
+		 * prepare the standard merge summary message to
+		 * be appended to the given message.  If remote
+		 * is invalid we will die later in the common
+		 * codepath so we discard the error in this
+		 * loop.
+		 */
+		strbuf_init(&msg, 0);
+		for (i = 0; i < argc; i++)
+			merge_name(argv[i], &msg);
+		fmt_merge_msg(option_log, &msg, &merge_msg);
+		if (merge_msg.len)
+			strbuf_setlen(&merge_msg, merge_msg.len-1);
+	}
+
+	if (head_invalid || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (i = 0; i < argc; i++)
+		strbuf_addf(&buf, " %s", argv[i]);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (i = 0; i < argc; i++) {
+		struct object *o;
+
+		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+		if (!o)
+			die("%s - not something we can merge", argv[i]);
+		remotes = &commit_list_insert(lookup_commit(o->sha1),
+			remotes)->next;
+
+		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+		setenv(buf.buf, argv[i], 1);
+		strbuf_reset(&buf);
+	}
+
+	if (!enabled_strategies) {
+		if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++) {
+		if (!all_strategy[i].enabled)
+			continue;
+		if (all_strategy[i].attr & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (all_strategy[i].attr & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads->next)
+		common = get_merge_bases(lookup_commit(head),
+				remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(lookup_commit(head), &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+		DIE_ON_ERR);
+
+	if (!common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads->next && !common->next &&
+			common->item == remoteheads->item) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		return 0;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg;
+		struct object *o;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+
+		printf("Updating %s..%s\n",
+			hex,
+			find_unique_abbrev(remoteheads->item->object.sha1,
+			DEFAULT_ABBREV));
+		refresh_cache(REFRESH_QUIET);
+		strbuf_init(&msg, 0);
+		strbuf_addstr(&msg, "Fast forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+			0, NULL, OBJ_COMMIT);
+		if (!o)
+			return 1;
+
+		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+			return 1;
+
+		finish(o->sha1, msg.buf);
+		drop_save();
+		return 0;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_ERROR_ON_NO_NAME);
+			printf("Trying really trivial in-index merge...\n");
+			if (!read_tree_trivial(common->item->object.sha1,
+					head, remoteheads->item->object.sha1))
+				return merge_trivial();
+			printf("Nope.\n");
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(lookup_commit(head),
+				j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			return 0;
+		}
+	}
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (enabled_strategies != 1) {
+		/*
+		 * Stash away the local changes so that we can try more
+		 * than one.
+		 */
+		save_state();
+	} else {
+		memcpy(stash, null_sha1, 20);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++) {
+		int ret;
+		if (!all_strategy[i].enabled)
+			continue;
+		if (first_strategy) {
+			first_strategy = 0;
+			printf("Rewinding the tree to pristine...\n");
+			restore_state();
+		}
+		if (enabled_strategies != 1)
+			printf("Trying merge strategy %s...\n",
+				all_strategy[i].name);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = all_strategy[i].name;
+
+		ret = try_merge_strategy(all_strategy[i].name,
+			common, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy = all_strategy[i].name;
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok)
+		return finish_automerge(common, result_tree, wt_strategy);
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state();
+		if (enabled_strategies > 1)
+			fprintf(stderr,
+				"No merge strategy handled the merge.\n");
+		else
+			fprintf(stderr, "Merge with strategy %s failed.\n",
+				wt_strategy);
+		return 2;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf("Rewinding the tree to pristine...\n");
+		restore_state();
+		printf("Using the %s to prepare resolving by hand.\n",
+			best_strategy);
+		try_merge_strategy(best_strategy, common, head_arg);
+	}
+
+	if (squash)
+		finish(NULL, NULL);
+	else {
+		int fd;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next)
+			strbuf_addf(&buf, "%s\n",
+				sha1_to_hex(j->item->object.sha1));
+		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing",
+				git_path("MERGE_HEAD"));
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_HEAD"));
+		close(fd);
+		strbuf_addch(&merge_msg, '\n');
+		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MSG"));
+		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+			merge_msg.len)
+			die("Could not write to %s", git_path("MERGE_MSG"));
+		close(fd);
+	}
+
+	if (merge_was_ok) {
+		fprintf(stderr, "Automatic merge went well; "
+			"stopped before committing as requested\n");
+		return 0;
+	} else
+		return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index f3a4bb2..fcb8285 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -23,7 +23,7 @@ test_expect_success 'setup' '
 	done
 '
 
-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
 	git reset --hard c1 &&
 	i=2 &&
 	refs="" &&
-- 
1.5.6.1.322.ge904b.dirty
Junio C Hamano· Jul 8, 2008, 00:32 UTC · re: Miklos Vajna · lore

Re: [PATCH] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 27 quoted lines
> Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
> ---
>
> On Mon, Jul 07, 2008 at 11:15:09AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
>> I do not get you on this point.  Which one is nicer?
>>
>>  (1) Have two lists, perhaps all_* and user_*.  The logic that finds a
>>      strategy searches in two lists.  The logic that checks if a given
>>      strategy is built-in checks if it is on all_* list.
>>
>>  (2) Have a single list, but add a boolean "unsigned is_builtin:1" to
>>  each
>>      element of it.  The logic that finds a strategy looks in this
>>      single
>>      list.  The logic that checks if a given strategy is built-in
>>      looks at
>>      the strategy instance and it has the bit already.
>>
>> You seem to be advocating (1) but I do not understand why...
>
> Ah, OK. For now, I just added an "unsigned enabled:1;". Later we can add
> an "unsigned is_buildin:1;" as well, but currently we die with earlier
> with a "Could not find merge strategy" error message, so is_builtin
> would be always true.
>
> So here is a version, this time without the use_strategies list.

That is not what I meant. I am afraid perhaps I misunderstood what you were talking about.

When/if you allow user defined new strategies, then you have a choice:
 (1) find "git-merge-*" in path, add them to the single all_strategies[]
     list (but you will do the ALLOC_GROW() business so you would need to
     use the one you currently have as static form to prime the real list),
     and look for "foo" strategy when "-s foo" is given from that single
     list, or
 (2) find "git-merge-*" in path, add them to a separate user_strategies[]
     list, and look for "foo" strategy when "-s foo" is given from the
     user_strategies[] list and all_strategies[] list (all_strategies[]
     should perhaps be renamed to builtin_strategies[] if you go that
     route).

The comparison I gave was between the above two. But the change you are talking about is completely different, isn't it?

The part that records which strategies were specified from the command line *in what order* via "-s foo" switches should remain list of pointers into "struct strategy", which is called "struct strategy **use_strategies" in the code and corresponds to the $use_strategies variable in the scripted version. The order of these is important, as that defines in which order the strategies are tried [*1*]. If you go route (1), these pointers will all be pointing at elements in all_strategies[]; with route (2) they may be pointing at either all_strageties[] element or user_strategies[] element.

If you are never going to say "available strategies are these" after you start supporting user-defined strategy, then you do not necessarily need to do the "find 'git-merge-*' in path, add them to ..." step above, in which case it would be Ok not to scan the path and add them to all_strategies[] (in route (1)) nor user_strategies[] (in route (2)). Instead, you would just create a new "struct strategy" instance lazily when the user gave "-s foo" and "foo" is not one of the built-in strategy. You would put that at the tail of "struct strategy **use_strategy" array, and iterate over use_strategy in the order they are given on the command line.

[Footnote]

*1* Personally, I find the importance of this dubious in practice, as I said earlier, I do not think it would work well to try different strategies and pick the best one --- evaluating which result is the *best* is difficult. If you want to stay compatible with the scripted version, however, you cannot just mark entries in all_strategies[] with boolean and iterate over them in the order that all_strageties[] define them. You need to try them in the order the user specified.

Junio C Hamano· Jul 8, 2008, 00:53 UTC · re: Junio C Hamano · lore

Re: [PATCH] Build in merge

Junio C Hamano <gitster@pobox.com> writes:
Show 11 quoted lines
> That is not what I meant.  I am afraid perhaps I misunderstood what you
> were talking about.
> ...
> The comparison I gave was between the above two.  But the change you are
> talking about is completely different, isn't it?
>
> The part that records which strategies were specified from the command
> line *in what order* via "-s foo" switches should remain list of pointers
> into "struct strategy", which is called "struct strategy **use_strategies"
> in the code and corresponds to the $use_strategies variable in the
> scripted version.

Here is what I meant to suggest; sorry for confusing you with an initial typo of having only one pointer in front of use_strategies "array of pointers". Applies on top of your previous round.

--
 builtin-merge.c |   58 +++++++++++++++++++++++++++---------------------------
 1 files changed, 29 insertions(+), 29 deletions(-)
Show changes to builtin-merge.c +29 −29
diff --git a/builtin-merge.c b/builtin-merge.c
index 7312997..b2e702a 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -44,7 +44,7 @@ static int allow_trivial = 1, have_message;
 static struct strbuf merge_msg;
 static struct commit_list *remoteheads;
 static unsigned char head[20], stash[20];
-static struct strategy *use_strategies;
+static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char *branch;
 
@@ -74,44 +74,44 @@ static int option_parse_message(const struct option *opt,
 	return 0;
 }
 
-static int strategy_lookup(const char *path)
+static struct strategy *get_strategy(const char *name)
 {
 	int i;
 
-	if (!path)
-		return -1;
+	if (!name)
+		return NULL;
 
 	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-		if (!strcmp(path, all_strategy[i].name))
-			return all_strategy[i].attr;
-	return -1;
+		if (!strcmp(name, all_strategy[i].name))
+			return &all_strategy[i];
+	return NULL;
 }
 
-static inline void append_strategy(const char *name, unsigned attr)
+static void append_strategy(struct strategy *s)
 {
 	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
-	use_strategies[use_strategies_nr].name = name;
-	use_strategies[use_strategies_nr++].attr = attr;
+	use_strategies[use_strategies_nr++] = s;
 }
 
 static int option_parse_strategy(const struct option *opt,
-	const char *arg, int unset)
+				 const char *name, int unset)
 {
-	int i, attr;
+	int i;
+	struct strategy *s;
 
 	if (unset)
 		return 0;
 
-	attr = strategy_lookup(arg);
+	s = get_strategy(name);
 
-	if (attr >= 0)
-		append_strategy(arg, attr);
+	if (s)
+		append_strategy(s);
 	else {
 		struct strbuf err;
 		strbuf_init(&err, 0);
 		for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
 			strbuf_addf(&err, " %s", all_strategy[i].name);
-		fprintf(stderr, "Could not find merge strategy '%s'.\n", arg);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
 		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
 		exit(1);
 	}
@@ -643,18 +643,18 @@ static void add_strategies(const char *string, unsigned attr)
 	split_merge_strategies(string, &list, &list_nr, &list_alloc);
 	if (list != NULL) {
 		for (i = 0; i < list_nr; i++) {
-			int attr;
+			struct strategy *s;
 
-			attr = strategy_lookup(list[i].name);
-			if (attr >= 0)
-				append_strategy(list[i].name, attr);
+			s = get_strategy(list[i].name);
+			if (s)
+				append_strategy(s);
 		}
 		return;
 	}
 	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
 		if (all_strategy[i].attr & attr)
-			append_strategy(all_strategy[i].name,
-				all_strategy[i].attr);
+			append_strategy(&all_strategy[i]);
+
 }
 
 static int merge_trivial(void)
@@ -903,9 +903,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 
 	for (i = 0; i < use_strategies_nr; i++) {
-		if (use_strategies[i].attr & NO_FAST_FORWARD)
+		if (use_strategies[i]->attr & NO_FAST_FORWARD)
 			allow_fast_forward = 0;
-		if (use_strategies[i].attr & NO_TRIVIAL)
+		if (use_strategies[i]->attr & NO_TRIVIAL)
 			allow_trivial = 0;
 	}
 
@@ -1043,14 +1043,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		}
 		if (use_strategies_nr != 1)
 			printf("Trying merge strategy %s...\n",
-				use_strategies[i].name);
+				use_strategies[i]->name);
 		/*
 		 * Remember which strategy left the state in the working
 		 * tree.
 		 */
-		wt_strategy = use_strategies[i].name;
+		wt_strategy = use_strategies[i]->name;
 
-		ret = try_merge_strategy(use_strategies[i].name,
+		ret = try_merge_strategy(use_strategies[i]->name,
 			common, head_arg);
 		if (!option_commit && !ret) {
 			merge_was_ok = 1;
@@ -1072,7 +1072,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 				int cnt = evaluate_result();
 
 				if (best_cnt <= 0 || cnt <= best_cnt) {
-					best_strategy = use_strategies[i].name;
+					best_strategy = use_strategies[i]->name;
 					best_cnt = cnt;
 				}
 			}
@@ -1106,7 +1106,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 				"No merge strategy handled the merge.\n");
 		else
 			fprintf(stderr, "Merge with strategy %s failed.\n",
-				use_strategies[0].name);
+				use_strategies[0]->name);
 		return 2;
 	} else if (best_strategy == wt_strategy)
 		; /* We already have its result in the working tree. */
Miklos Vajna· Jul 8, 2008, 01:18 UTC · re: Junio C Hamano · lore

[PATCH] Build in merge

Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
On Mon, Jul 07, 2008 at 05:53:23PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Here is what I meant to suggest; sorry for confusing you with an
> initial
> typo of having only one pointer in front of use_strategies "array of
> pointers".  Applies on top of your previous round.

Funny enough, I just did almost the same before reading your mail, except that I did not do the suggested strategy_lookup() -> get_strategy() rename and such.

Here is a squashed patch. (Previous round + your patch.)
 Makefile                                      |    2 +-
 builtin-merge.c                               | 1153 +++++++++++++++++++++++++
 builtin.h                                     |    1 +
 git-merge.sh => contrib/examples/git-merge.sh |    0
 git.c                                         |    1 +
 t/t7602-merge-octopus-many.sh                 |    2 +-
 6 files changed, 1157 insertions(+), 2 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)
Show changes to 6 files +1157 −2

Makefile, builtin-merge.c, builtin.h, git-merge.sh, git.c, t/t7602-merge-octopus-many.sh

diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
-SCRIPT_SH += git-merge.sh
 SCRIPT_SH += git-merge-stupid.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-parse-remote.sh
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
 BUILTIN_OBJS += builtin-ls-tree.o
 BUILTIN_OBJS += builtin-mailinfo.o
 BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
 BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..d6bd144
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1153 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL      (1<<3)
+
+struct strategy {
+	const char *name;
+	unsigned attr;
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git-merge [options] <remote>...",
+	"git-merge [options] <msg> HEAD <remote>",
+	NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+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 *branch;
+
+static struct strategy all_strategy[] = {
+	{ "recur",      NO_TRIVIAL },
+	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
+	{ "octopus",    DEFAULT_OCTOPUS },
+	{ "resolve",    0 },
+	{ "stupid",     0 },
+	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
+	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
+};
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+	const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addf(buf, "%s\n\n", arg);
+		have_message = 1;
+	}
+	return 0;
+}
+
+static struct strategy *get_strategy(const char *name)
+{
+	int i;
+
+	if (!name)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (!strcmp(name, all_strategy[i].name))
+			return &all_strategy[i];
+	return NULL;
+}
+
+static void append_strategy(struct strategy *s)
+{
+	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
+	use_strategies[use_strategies_nr++] = s;
+}
+
+static int option_parse_strategy(const struct option *opt,
+				 const char *name, int unset)
+{
+	int i;
+	struct strategy *s;
+
+	if (unset)
+		return 0;
+
+	s = get_strategy(name);
+
+	if (s)
+		append_strategy(s);
+	else {
+		struct strbuf err;
+		strbuf_init(&err, 0);
+		for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+			strbuf_addf(&err, " %s", all_strategy[i].name);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+		exit(1);
+	}
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+		const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	OPT_BOOLEAN(0, "log", &option_log,
+		"add list of one-line log to merge commit message"),
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast forward (default)"),
+	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"message to be used for the merge commit (if any)",
+		option_parse_message),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die("could not run stash.");
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die("stash failed");
+	else if (!len)
+		return;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+	struct strbuf sb;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	strbuf_init(&sb, 0);
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	drop_save();
+}
+
+static void squash_message(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf out;
+	struct commit_list *j;
+	int fd;
+
+	printf("Squash commit -- not updating HEAD\n");
+	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die("Could not write to %s", git_path("SQUASH_MSG"));
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit = lookup_commit(head);
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	strbuf_init(&out, 0);
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+			NULL, NULL, rev.date_mode, 0);
+	}
+	write(fd, out.buf, out.len);
+	close(fd);
+	strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+	env[0] = index;
+	env[1] = NULL;
+
+	if (squash)
+		argv[1] = "1";
+	else
+		argv[1] = "0";
+	argv[2] = NULL;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message;
+
+	strbuf_init(&reflog_message, 0);
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message();
+	} else {
+		if (!merge_msg.len)
+			printf("No merge message -- not updating HEAD\n");
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_use_color_default > 0)
+			DIFF_OPT_SET(&opts, COLOR_DIFF);
+		if (diff_setup_done(&opts) < 0)
+			die("diff_setup_done failed");
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook("post-merge");
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct object *remote_head;
+	unsigned char branch_head[20], buf_sha[20];
+	struct strbuf buf;
+	const char *ptr;
+	int len, early;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+	if (!remote_head)
+		die("'%s' does not point to a commit", remote);
+
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, remote);
+	resolve_ref(buf.buf, branch_head, 0, 0);
+
+	if (!hashcmp(remote_head->sha1, branch_head)) {
+		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+			sha1_to_hex(branch_head), remote);
+		return;
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, len+11);
+		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    sha1_to_hex(remote_head->sha1),
+				    truname.buf,
+				    (early ? " (early part)" : ""));
+			return;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		FILE *fp;
+		struct strbuf line;
+		char *ptr;
+
+		strbuf_init(&line, 0);
+		fp = fopen(git_path("FETCH_HEAD"), "r");
+		if (!fp)
+			die("could not open %s for reading: %s",
+				git_path("FETCH_HEAD"), strerror(errno));
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		return;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		const char **argv;
+		int argc;
+		char *buf;
+
+		buf = xstrdup(v);
+		argc = split_cmdline(buf, &argv);
+		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+		argc++;
+		parse_options(argc, argv, builtin_merge_options,
+			      builtin_merge_usage, 0);
+		free(buf);
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+	unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+	const char *head_arg)
+{
+	const char **args;
+	int i = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf;
+
+	args = xmalloc((4 + commit_list_count(common) +
+			commit_list_count(remoteheads)) * sizeof(char *));
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remoteheads; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remoteheads; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	const struct index_state *state = &the_index;
+	int i, ret = 0;
+
+	for (i = 0; i < state->cache_nr; i++)
+		if (ce_stage(state->cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	if (read_cache_unmerged())
+		die("you need to resolve your current index first");
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	dir.show_ignored = 1;
+	dir.exclude_per_dir = ".gitignore";
+	opts.dir = &dir;
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die("unable to write new index file");
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct strategy **list,
+		int *nr, int *alloc)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+	struct strategy *list = NULL;
+	int list_alloc = 0, list_nr = 0, i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list, &list_nr, &list_alloc);
+	if (list != NULL) {
+		for (i = 0; i < list_nr; i++) {
+			struct strategy *s;
+
+			s = get_strategy(list[i].name);
+			if (s)
+				append_strategy(s);
+		}
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (all_strategy[i].attr & attr)
+			append_strategy(&all_strategy[i]);
+
+}
+
+static int merge_trivial(void)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list parent;
+
+	write_tree_trivial(result_tree);
+	printf("Wonderful.\n");
+	parent.item = remoteheads->item;
+	parent.next = NULL;
+	commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+	finish(result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+	unsigned char *result_tree,
+	const char *wt_strategy)
+{
+	struct commit_list *parents = NULL, *j;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	if (allow_fast_forward) {
+		parents = remoteheads;
+		commit_list_insert(lookup_commit(head), &parents);
+		parents = reduce_heads(parents);
+	} else {
+		struct commit_list **pptr = &parents;
+
+		pptr = &commit_list_insert(lookup_commit(head),
+				pptr)->next;
+		for (j = remoteheads; j; j = j->next)
+			pptr = &commit_list_insert(j->item, pptr)->next;
+	}
+	free_commit_list(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
+	finish(result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	FILE *fp;
+	int pos;
+
+	fp = fopen(git_path("MERGE_MSG"), "a");
+	if (!fp)
+		die("Could open %s for writing", git_path("MERGE_MSG"));
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere();
+	printf("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n");
+	return 1;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+	struct commit *second_token = NULL;
+	if (argc > 1) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die("'%s' is not a commit", argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	if (read_cache() < 0)
+		die("failed to read the cache");
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	struct strbuf buf;
+	const char *head_arg;
+	int flag, head_invalid = 0, i;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	const char *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	setup_work_tree();
+	if (unmerged_cache())
+		die("You are in the middle of a conflicted merge.");
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = resolve_ref("HEAD", head, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
+	if (is_null_sha1(head))
+		head_invalid = 1;
+
+	git_config(git_merge_config, NULL);
+
+	/* for color.ui */
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
+	argc = parse_options(argc, argv, builtin_merge_options,
+			builtin_merge_usage, 0);
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die("You cannot combine --squash with --no-ff.");
+		option_commit = 0;
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+	strbuf_init(&buf, 0);
+
+	if (!have_message && is_old_style_invocation(argc, argv)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+	} else if (head_invalid) {
+		struct object *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die("Can merge only exactly one commit into "
+				"empty head");
+		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+		if (!remote_head)
+			die("%s - not something we can merge", argv[0]);
+		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+				DIE_ON_ERR);
+		reset_hard(remote_head->sha1, 0);
+		return 0;
+	} else {
+		struct strbuf msg;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged;
+		 * prepare the standard merge summary message to
+		 * be appended to the given message.  If remote
+		 * is invalid we will die later in the common
+		 * codepath so we discard the error in this
+		 * loop.
+		 */
+		strbuf_init(&msg, 0);
+		for (i = 0; i < argc; i++)
+			merge_name(argv[i], &msg);
+		fmt_merge_msg(option_log, &msg, &merge_msg);
+		if (merge_msg.len)
+			strbuf_setlen(&merge_msg, merge_msg.len-1);
+	}
+
+	if (head_invalid || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (i = 0; i < argc; i++)
+		strbuf_addf(&buf, " %s", argv[i]);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (i = 0; i < argc; i++) {
+		struct object *o;
+
+		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+		if (!o)
+			die("%s - not something we can merge", argv[i]);
+		remotes = &commit_list_insert(lookup_commit(o->sha1),
+			remotes)->next;
+
+		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+		setenv(buf.buf, argv[i], 1);
+		strbuf_reset(&buf);
+	}
+
+	if (!use_strategies) {
+		if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		if (use_strategies[i]->attr & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (use_strategies[i]->attr & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads->next)
+		common = get_merge_bases(lookup_commit(head),
+				remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(lookup_commit(head), &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+		DIE_ON_ERR);
+
+	if (!common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads->next && !common->next &&
+			common->item == remoteheads->item) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		return 0;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg;
+		struct object *o;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+
+		printf("Updating %s..%s\n",
+			hex,
+			find_unique_abbrev(remoteheads->item->object.sha1,
+			DEFAULT_ABBREV));
+		refresh_cache(REFRESH_QUIET);
+		strbuf_init(&msg, 0);
+		strbuf_addstr(&msg, "Fast forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+			0, NULL, OBJ_COMMIT);
+		if (!o)
+			return 1;
+
+		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+			return 1;
+
+		finish(o->sha1, msg.buf);
+		drop_save();
+		return 0;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_ERROR_ON_NO_NAME);
+			printf("Trying really trivial in-index merge...\n");
+			if (!read_tree_trivial(common->item->object.sha1,
+					head, remoteheads->item->object.sha1))
+				return merge_trivial();
+			printf("Nope.\n");
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(lookup_commit(head),
+				j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			return 0;
+		}
+	}
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies_nr != 1) {
+		/*
+		 * Stash away the local changes so that we can try more
+		 * than one.
+		 */
+		save_state();
+	} else {
+		memcpy(stash, null_sha1, 20);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		int ret;
+		if (i) {
+			printf("Rewinding the tree to pristine...\n");
+			restore_state();
+		}
+		if (use_strategies_nr != 1)
+			printf("Trying merge strategy %s...\n",
+				use_strategies[i]->name);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = use_strategies[i]->name;
+
+		ret = try_merge_strategy(use_strategies[i]->name,
+			common, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy = use_strategies[i]->name;
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok)
+		return finish_automerge(common, result_tree, wt_strategy);
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state();
+		if (use_strategies_nr > 1)
+			fprintf(stderr,
+				"No merge strategy handled the merge.\n");
+		else
+			fprintf(stderr, "Merge with strategy %s failed.\n",
+				use_strategies[0]->name);
+		return 2;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf("Rewinding the tree to pristine...\n");
+		restore_state();
+		printf("Using the %s to prepare resolving by hand.\n",
+			best_strategy);
+		try_merge_strategy(best_strategy, common, head_arg);
+	}
+
+	if (squash)
+		finish(NULL, NULL);
+	else {
+		int fd;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next)
+			strbuf_addf(&buf, "%s\n",
+				sha1_to_hex(j->item->object.sha1));
+		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing",
+				git_path("MERGE_HEAD"));
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_HEAD"));
+		close(fd);
+		strbuf_addch(&merge_msg, '\n');
+		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MSG"));
+		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+			merge_msg.len)
+			die("Could not write to %s", git_path("MERGE_MSG"));
+		close(fd);
+	}
+
+	if (merge_was_ok) {
+		fprintf(stderr, "Automatic merge went well; "
+			"stopped before committing as requested\n");
+		return 0;
+	} else
+		return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index f3a4bb2..fcb8285 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -23,7 +23,7 @@ test_expect_success 'setup' '
 	done
 '
 
-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
 	git reset --hard c1 &&
 	i=2 &&
 	refs="" &&
-- 
1.5.6.1.322.ge904b.dirty
Miklos Vajna· Jul 8, 2008, 01:00 UTC · re: Junio C Hamano · lore

Re: [PATCH] Build in merge

On Mon, Jul 07, 2008 at 05:32:50PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 18 quoted lines
> > So here is a version, this time without the use_strategies list.
> 
> That is not what I meant.  I am afraid perhaps I misunderstood what you
> were talking about.
> 
> When/if you allow user defined new strategies, then you have a choice:
> 
>  (1) find "git-merge-*" in path, add them to the single all_strategies[]
>      list (but you will do the ALLOC_GROW() business so you would need to
>      use the one you currently have as static form to prime the real list),
>      and look for "foo" strategy when "-s foo" is given from that single
>      list, or
> 
>  (2) find "git-merge-*" in path, add them to a separate user_strategies[]
>      list, and look for "foo" strategy when "-s foo" is given from the
>      user_strategies[] list and all_strategies[] list (all_strategies[]
>      should perhaps be renamed to builtin_strategies[] if you go that
>      route).

OK, I see now. Actually I think the primary problem with a custom strategy is that we do not know its flags, IOW if it handles an octopus, etc. So I think there are two questions here:

1) How to tell git-merge the flags of a custom strategy? Or: is it
necessary at all?
I could imagine the following situations:
1) A project has code in a repo, some documentation and po files.

The first two can be merged with builtin strategies, the later probably needs a custom merge driver.

So, in most cases recursive is fine, but sometimes the maintainer wants to say 'git pull -s po'.

In this case the flags of 'po' does not really matter.
2) Someone is not happy with the current recursive strategy and writes
from scratch a new one. He/she puts it to pull.twohead, so it will be
tried before recursive.

To sum up: I am not sure what would be the benefit of being able to specify flags for strategies. However, if we want so, it would be good to discuss how it should be done.

Show 9 quoted lines
> The part that records which strategies were specified from the command
> line *in what order* via "-s foo" switches should remain list of pointers
> into "struct strategy", which is called "struct strategy **use_strategies"
> in the code and corresponds to the $use_strategies variable in the
> scripted version.  The order of these is important, as that defines in
> which order the strategies are tried [*1*].  If you go route (1), these
> pointers will all be pointing at elements in all_strategies[]; with route
> (2) they may be pointing at either all_strageties[] element or
> user_strategies[] element.
I see the problem, I lost the order at the moment.
Show 10 quoted lines
> If you are never going to say "available strategies are these" after you
> start supporting user-defined strategy, then you do not necessarily need
> to do the "find 'git-merge-*' in path, add them to ..." step above, in
> which case it would be Ok not to scan the path and add them to
> all_strategies[] (in route (1)) nor user_strategies[] (in route (2)).
> Instead, you would just create a new "struct strategy" instance lazily
> when the user gave "-s foo" and "foo" is not one of the built-in strategy.
> You would put that at the tail of "struct strategy **use_strategy" array,
> and iterate over use_strategy in the order they are given on the command
> line.

I guess it would be nice to adjust the error message and scanning path, but that's something - I guess - that should be done after the rewrite is complete and there are no more issues with it.

Junio C Hamano· Jul 8, 2008, 01:05 UTC · re: Miklos Vajna · lore

Re: [PATCH] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 6 quoted lines
> I could imagine the following situations:
>
> 1) A project has code in a repo, some documentation and po files.
>
> The first two can be merged with builtin strategies, the later probably
> needs a custom merge driver.

Per-path merge is probably better handled with custom ll-merge driver anyway. See gitattributes(5).

> 2) Someone is not happy with the current recursive strategy and writes
> from scratch a new one. He/she puts it to pull.twohead, so it will be
> tried before recursive.
That is fine.
> To sum up: I am not sure what would be the benefit of being able to
> specify flags for strategies. However, if we want so, it would be good
> to discuss how it should be done.
It wasn't *me* ;-) who wanted to add these "flags".

I think it does not matter what "my-strategy" does unless "-s my-strategy" (or pull.twohead) is explicitly given by the user, and at that time, DEFAULT_* options should not matter. It probably is Ok to allow fast forward and trivial merges to them. We'll see.

Show 11 quoted lines
>> The part that records which strategies were specified from the command
>> line *in what order* via "-s foo" switches should remain list of pointers
>> into "struct strategy", which is called "struct strategy **use_strategies"
>> in the code and corresponds to the $use_strategies variable in the
>> scripted version.  The order of these is important, as that defines in
>> which order the strategies are tried [*1*].  If you go route (1), these
>> pointers will all be pointing at elements in all_strategies[]; with route
>> (2) they may be pointing at either all_strageties[] element or
>> user_strategies[] element.
>
> I see the problem, I lost the order at the moment.
That's Ok.  See the other patch on top of your previous one.
Miklos Vajna· Jul 8, 2008, 01:41 UTC · re: Junio C Hamano · lore

Re: [PATCH] Build in merge

On Mon, Jul 07, 2008 at 06:05:43PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 7 quoted lines
> > 1) A project has code in a repo, some documentation and po files.
> >
> > The first two can be merged with builtin strategies, the later probably
> > needs a custom merge driver.
> 
> Per-path merge is probably better handled with custom ll-merge driver
> anyway.  See gitattributes(5).
Ah, thanks. I did not know ll-merge supports custom drivers as well.
Show 6 quoted lines
> It wasn't *me* ;-) who wanted to add these "flags".
> 
> I think it does not matter what "my-strategy" does unless "-s my-strategy"
> (or pull.twohead) is explicitly given by the user, and at that time,
> DEFAULT_* options should not matter.  It probably is Ok to allow fast
> forward and trivial merges to them.  We'll see.

OK, so at first round I think we could avoid flags for custom strategies.

For the error message, I think the output could be something like git help --all, which splits commands based on being in GIT_EXEC_PATH or somewhere else in PATH.

Also, currently (the shell version and the C one as well) we silently ignore the config setting if it's set to semething invalid. What about changing that to a similar error message as well?

Johannes Schindelin· Jul 6, 2008, 12:38 UTC · re: Junio C Hamano · lore

Re: [PATCH 14/14] Build in merge

Hi,
On Sun, 6 Jul 2008, Junio C Hamano wrote:
Show 42 quoted lines
> Miklos Vajna <vmiklos@frugalware.org> writes:
> 
> > diff --git a/builtin-merge.c b/builtin-merge.c
> > new file mode 100644
> > index 0000000..b261993
> > --- /dev/null
> > +++ b/builtin-merge.c
> > @@ -0,0 +1,1158 @@
> > +/*
> > + * Builtin "git merge"
> > + *
> > + * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
> > + *
> > + * Based on git-merge.sh by Junio C Hamano.
> > + */
> > +
> > +#include "cache.h"
> > +#include "parse-options.h"
> > +#include "builtin.h"
> > +#include "run-command.h"
> > +#include "path-list.h"
> > +#include "diff.h"
> > +#include "refs.h"
> > +#include "commit.h"
> > +#include "diffcore.h"
> > +#include "revision.h"
> > +#include "unpack-trees.h"
> > +#include "cache-tree.h"
> > +#include "dir.h"
> > +#include "utf8.h"
> > +#include "log-tree.h"
> > +#include "color.h"
> > +
> > +enum strategy {
> > +	DEFAULT_TWOHEAD = 1,
> > +	DEFAULT_OCTOPUS = 2,
> > +	NO_FAST_FORWARD = 4,
> > +	NO_TRIVIAL = 8
> > +};
> 
> Usually "enum foo" consists of possible values of "foo".  But this is
> not a list of strategies.  These are possible attributes to strategies.
My fault.  I avoid ugly #defines, so I suggested using an enum.
Show 46 quoted lines
> > +static const char * const builtin_merge_usage[] = {
> > +	"git-merge [options] <remote>...",
> > +	"git-merge [options] <msg> HEAD <remote>",
> > +	NULL
> > +};
> > +
> > +static int show_diffstat = 1, option_log, squash;
> > +static int option_commit = 1, allow_fast_forward = 1;
> > +static int allow_trivial = 1, have_message;
> > +static struct strbuf merge_msg;
> > +static struct commit_list *remoteheads;
> > +static unsigned char head[20], stash[20];
> > +static struct path_list use_strategies;
> > +static const char *branch;
> > +
> > +static struct path_list_item strategy_items[] = {
> > +	{ "recur",      (void *)NO_TRIVIAL },
> > +	{ "recursive",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },
> > +	{ "octopus",    (void *)DEFAULT_OCTOPUS },
> > +	{ "resolve",    (void *)0 },
> > +	{ "stupid",     (void *)0 },
> > +	{ "ours",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
> > +	{ "subtree",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
> > +};
> > +static struct path_list strategies = { strategy_items,
> > +	ARRAY_SIZE(strategy_items), 0, 0 };
> 
> This declaration is funnily line-wrapped.
> 
>         static struct path_list strategies = {
>                 strategy_items, ARRAY_SIZE(strategy_items), 0, 0,
>         };
> 
> But more problematic is that a path_list is inherently a dynamic data 
> structure (you can add and it reallocs), and this use of relying on the 
> knowledge that you happen to never add anything (nor subtract anything) 
> from the list is a mere hack.  If on the other hand you (and more 
> importantly other people who touch this implementation later) will never 
> add or remove items from this "strategies" array, you should make sure 
> at the interface level that nobody can -- one way to do so is not to 
> abuse path_list for something like this.
> 
> Come to think of it, wasn't the reason why the earlier "Why do you need 
> such distracting casts all over the place?" issue came up in another 
> patch because of this kind of (ab)use of path_list, which is an 
> inappropriate data structure for the job?

Well, it is not really an abuse if you think of path_list as a string_list, which it really is, and which should have happened a long time ago, but I gave up.

Anyway, the use of string_list here was originally to make lookup slightly more convenient, using an API, instead of reinventing the wheel over and over and over again, as can be seen nicely in some parts of Git's source code.

Given that we want to be able to add other strategies, I would have rather suggested fixing string_list to heed "alloc == 0" and _not_ realloc() in that case.

But given that you seem so sick and tired of string_list, and rather have a code duplication, I will not argue to that end anymore.

Tired, Dscho

Junio C Hamano· Jul 6, 2008, 19:39 UTC · re: Johannes Schindelin · lore

Re: [PATCH 14/14] Build in merge

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> But given that you seem so sick and tired of string_list, and rather have 
> a code duplication, I will not argue to that end anymore.

You are probably very confused if you think I am saying I'd rather have duplication.

Look at "unsorted_path_list_lookup()" in builtin-merge.c and think again. Look at "path_list_append_strategy()" in builtin-merge.c and think again.

If you are adding the same amount of code _anyway_, why not write using more appropriate data structure for the job?

The path_list has its uses. It's wonderful when you have existing structures that you would need to keep in core anyway and being able to look them up via string keys. But that does not mean it is (nor should be "improved" to be) a good match for other forms of (ab)uses.

The way it was used by initializing with pointer to a static array location is clearly a misuse. When using ->util field to store things other than pointers to preexisting structures, the use of the API becomes clunky and we discussed this issue about another patch. That's all I was saying.

Miklos Vajna· Jul 7, 2008, 17:24 UTC · re: Junio C Hamano · lore

[PATCH] Build in merge

Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---
On Sun, Jul 06, 2008 at 01:50:05AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 21 quoted lines
> But more problematic is that a path_list is inherently a dynamic data
> structure (you can add and it reallocs), and this use of relying on
> the
> knowledge that you happen to never add anything (nor subtract
> anything)
> from the list is a mere hack.  If on the other hand you (and more
> importantly other people who touch this implementation later) will
> never
> add or remove items from this "strategies" array, you should make sure
> at
> the interface level that nobody can -- one way to do so is not to
> abuse
> path_list for something like this.
>
> Come to think of it, wasn't the reason why the earlier "Why do you
> need
> such distracting casts all over the place?" issue came up in another
> patch
> because of this kind of (ab)use of path_list, which is an
> inappropriate
> data structure for the job?
Here is an updated version without using path_list at all.
 Makefile                                      |    2 +-
 builtin-merge.c                               | 1153 +++++++++++++++++++++++++
 builtin.h                                     |    1 +
 git-merge.sh => contrib/examples/git-merge.sh |    0
 git.c                                         |    1 +
 t/t7602-merge-octopus-many.sh                 |    2 +-
 6 files changed, 1157 insertions(+), 2 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)
Show changes to 6 files +1157 −2

Makefile, builtin-merge.c, builtin.h, git-merge.sh, git.c, t/t7602-merge-octopus-many.sh

diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
-SCRIPT_SH += git-merge.sh
 SCRIPT_SH += git-merge-stupid.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-parse-remote.sh
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
 BUILTIN_OBJS += builtin-ls-tree.o
 BUILTIN_OBJS += builtin-mailinfo.o
 BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
 BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..7312997
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1153 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL      (1<<3)
+
+struct strategy {
+	const char *name;
+	unsigned attr;
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git-merge [options] <remote>...",
+	"git-merge [options] <msg> HEAD <remote>",
+	NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+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 *branch;
+
+static struct strategy all_strategy[] = {
+	{ "recur",      NO_TRIVIAL },
+	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
+	{ "octopus",    DEFAULT_OCTOPUS },
+	{ "resolve",    0 },
+	{ "stupid",     0 },
+	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
+	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
+};
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+	const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addf(buf, "%s\n\n", arg);
+		have_message = 1;
+	}
+	return 0;
+}
+
+static int strategy_lookup(const char *path)
+{
+	int i;
+
+	if (!path)
+		return -1;
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (!strcmp(path, all_strategy[i].name))
+			return all_strategy[i].attr;
+	return -1;
+}
+
+static inline void append_strategy(const char *name, unsigned attr)
+{
+	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
+	use_strategies[use_strategies_nr].name = name;
+	use_strategies[use_strategies_nr++].attr = attr;
+}
+
+static int option_parse_strategy(const struct option *opt,
+	const char *arg, int unset)
+{
+	int i, attr;
+
+	if (unset)
+		return 0;
+
+	attr = strategy_lookup(arg);
+
+	if (attr >= 0)
+		append_strategy(arg, attr);
+	else {
+		struct strbuf err;
+		strbuf_init(&err, 0);
+		for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+			strbuf_addf(&err, " %s", all_strategy[i].name);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", arg);
+		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+		exit(1);
+	}
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+		const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	OPT_BOOLEAN(0, "log", &option_log,
+		"add list of one-line log to merge commit message"),
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast forward (default)"),
+	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"message to be used for the merge commit (if any)",
+		option_parse_message),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die("could not run stash.");
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die("stash failed");
+	else if (!len)
+		return;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+	struct strbuf sb;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	strbuf_init(&sb, 0);
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	drop_save();
+}
+
+static void squash_message(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf out;
+	struct commit_list *j;
+	int fd;
+
+	printf("Squash commit -- not updating HEAD\n");
+	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die("Could not write to %s", git_path("SQUASH_MSG"));
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit = lookup_commit(head);
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	strbuf_init(&out, 0);
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+			NULL, NULL, rev.date_mode, 0);
+	}
+	write(fd, out.buf, out.len);
+	close(fd);
+	strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+	env[0] = index;
+	env[1] = NULL;
+
+	if (squash)
+		argv[1] = "1";
+	else
+		argv[1] = "0";
+	argv[2] = NULL;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message;
+
+	strbuf_init(&reflog_message, 0);
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message();
+	} else {
+		if (!merge_msg.len)
+			printf("No merge message -- not updating HEAD\n");
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_use_color_default > 0)
+			DIFF_OPT_SET(&opts, COLOR_DIFF);
+		if (diff_setup_done(&opts) < 0)
+			die("diff_setup_done failed");
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook("post-merge");
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct object *remote_head;
+	unsigned char branch_head[20], buf_sha[20];
+	struct strbuf buf;
+	const char *ptr;
+	int len, early;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+	if (!remote_head)
+		die("'%s' does not point to a commit", remote);
+
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, remote);
+	resolve_ref(buf.buf, branch_head, 0, 0);
+
+	if (!hashcmp(remote_head->sha1, branch_head)) {
+		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+			sha1_to_hex(branch_head), remote);
+		return;
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, len+11);
+		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    sha1_to_hex(remote_head->sha1),
+				    truname.buf,
+				    (early ? " (early part)" : ""));
+			return;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		FILE *fp;
+		struct strbuf line;
+		char *ptr;
+
+		strbuf_init(&line, 0);
+		fp = fopen(git_path("FETCH_HEAD"), "r");
+		if (!fp)
+			die("could not open %s for reading: %s",
+				git_path("FETCH_HEAD"), strerror(errno));
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		return;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		const char **argv;
+		int argc;
+		char *buf;
+
+		buf = xstrdup(v);
+		argc = split_cmdline(buf, &argv);
+		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+		argc++;
+		parse_options(argc, argv, builtin_merge_options,
+			      builtin_merge_usage, 0);
+		free(buf);
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+	unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+	const char *head_arg)
+{
+	const char **args;
+	int i = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf;
+
+	args = xmalloc((4 + commit_list_count(common) +
+			commit_list_count(remoteheads)) * sizeof(char *));
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remoteheads; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remoteheads; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	const struct index_state *state = &the_index;
+	int i, ret = 0;
+
+	for (i = 0; i < state->cache_nr; i++)
+		if (ce_stage(state->cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	if (read_cache_unmerged())
+		die("you need to resolve your current index first");
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	dir.show_ignored = 1;
+	dir.exclude_per_dir = ".gitignore";
+	opts.dir = &dir;
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die("unable to write new index file");
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct strategy **list,
+		int *nr, int *alloc)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+	struct strategy *list = NULL;
+	int list_alloc = 0, list_nr = 0, i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list, &list_nr, &list_alloc);
+	if (list != NULL) {
+		for (i = 0; i < list_nr; i++) {
+			int attr;
+
+			attr = strategy_lookup(list[i].name);
+			if (attr >= 0)
+				append_strategy(list[i].name, attr);
+		}
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (all_strategy[i].attr & attr)
+			append_strategy(all_strategy[i].name,
+				all_strategy[i].attr);
+}
+
+static int merge_trivial(void)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list parent;
+
+	write_tree_trivial(result_tree);
+	printf("Wonderful.\n");
+	parent.item = remoteheads->item;
+	parent.next = NULL;
+	commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+	finish(result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+	unsigned char *result_tree,
+	const char *wt_strategy)
+{
+	struct commit_list *parents = NULL, *j;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	if (allow_fast_forward) {
+		parents = remoteheads;
+		commit_list_insert(lookup_commit(head), &parents);
+		parents = reduce_heads(parents);
+	} else {
+		struct commit_list **pptr = &parents;
+
+		pptr = &commit_list_insert(lookup_commit(head),
+				pptr)->next;
+		for (j = remoteheads; j; j = j->next)
+			pptr = &commit_list_insert(j->item, pptr)->next;
+	}
+	free_commit_list(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
+	finish(result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	FILE *fp;
+	int pos;
+
+	fp = fopen(git_path("MERGE_MSG"), "a");
+	if (!fp)
+		die("Could open %s for writing", git_path("MERGE_MSG"));
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere();
+	printf("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n");
+	return 1;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+	struct commit *second_token = NULL;
+	if (argc > 1) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die("'%s' is not a commit", argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	if (read_cache() < 0)
+		die("failed to read the cache");
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	struct strbuf buf;
+	const char *head_arg;
+	int flag, head_invalid = 0, i;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	const char *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	setup_work_tree();
+	if (unmerged_cache())
+		die("You are in the middle of a conflicted merge.");
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = resolve_ref("HEAD", head, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
+	if (is_null_sha1(head))
+		head_invalid = 1;
+
+	git_config(git_merge_config, NULL);
+
+	/* for color.ui */
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
+	argc = parse_options(argc, argv, builtin_merge_options,
+			builtin_merge_usage, 0);
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die("You cannot combine --squash with --no-ff.");
+		option_commit = 0;
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+	strbuf_init(&buf, 0);
+
+	if (!have_message && is_old_style_invocation(argc, argv)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+	} else if (head_invalid) {
+		struct object *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die("Can merge only exactly one commit into "
+				"empty head");
+		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+		if (!remote_head)
+			die("%s - not something we can merge", argv[0]);
+		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+				DIE_ON_ERR);
+		reset_hard(remote_head->sha1, 0);
+		return 0;
+	} else {
+		struct strbuf msg;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged;
+		 * prepare the standard merge summary message to
+		 * be appended to the given message.  If remote
+		 * is invalid we will die later in the common
+		 * codepath so we discard the error in this
+		 * loop.
+		 */
+		strbuf_init(&msg, 0);
+		for (i = 0; i < argc; i++)
+			merge_name(argv[i], &msg);
+		fmt_merge_msg(option_log, &msg, &merge_msg);
+		if (merge_msg.len)
+			strbuf_setlen(&merge_msg, merge_msg.len-1);
+	}
+
+	if (head_invalid || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (i = 0; i < argc; i++)
+		strbuf_addf(&buf, " %s", argv[i]);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (i = 0; i < argc; i++) {
+		struct object *o;
+
+		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+		if (!o)
+			die("%s - not something we can merge", argv[i]);
+		remotes = &commit_list_insert(lookup_commit(o->sha1),
+			remotes)->next;
+
+		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+		setenv(buf.buf, argv[i], 1);
+		strbuf_reset(&buf);
+	}
+
+	if (!use_strategies) {
+		if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		if (use_strategies[i].attr & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (use_strategies[i].attr & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads->next)
+		common = get_merge_bases(lookup_commit(head),
+				remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(lookup_commit(head), &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+		DIE_ON_ERR);
+
+	if (!common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads->next && !common->next &&
+			common->item == remoteheads->item) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		return 0;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg;
+		struct object *o;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+
+		printf("Updating %s..%s\n",
+			hex,
+			find_unique_abbrev(remoteheads->item->object.sha1,
+			DEFAULT_ABBREV));
+		refresh_cache(REFRESH_QUIET);
+		strbuf_init(&msg, 0);
+		strbuf_addstr(&msg, "Fast forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+			0, NULL, OBJ_COMMIT);
+		if (!o)
+			return 1;
+
+		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+			return 1;
+
+		finish(o->sha1, msg.buf);
+		drop_save();
+		return 0;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_ERROR_ON_NO_NAME);
+			printf("Trying really trivial in-index merge...\n");
+			if (!read_tree_trivial(common->item->object.sha1,
+					head, remoteheads->item->object.sha1))
+				return merge_trivial();
+			printf("Nope.\n");
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(lookup_commit(head),
+				j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			return 0;
+		}
+	}
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+	/*
+	 * At this point, we need a real merge.  No matter what strategy
+	 * we use, it would operate on the index, possibly affecting the
+	 * working tree, and when resolved cleanly, have the desired
+	 * tree in the index -- this means that the index must be in
+	 * sync with the head commit.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies_nr != 1) {
+		/*
+		 * Stash away the local changes so that we can try more
+		 * than one.
+		 */
+		save_state();
+	} else {
+		memcpy(stash, null_sha1, 20);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		int ret;
+		if (i) {
+			printf("Rewinding the tree to pristine...\n");
+			restore_state();
+		}
+		if (use_strategies_nr != 1)
+			printf("Trying merge strategy %s...\n",
+				use_strategies[i].name);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = use_strategies[i].name;
+
+		ret = try_merge_strategy(use_strategies[i].name,
+			common, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy = use_strategies[i].name;
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok)
+		return finish_automerge(common, result_tree, wt_strategy);
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state();
+		if (use_strategies_nr > 1)
+			fprintf(stderr,
+				"No merge strategy handled the merge.\n");
+		else
+			fprintf(stderr, "Merge with strategy %s failed.\n",
+				use_strategies[0].name);
+		return 2;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf("Rewinding the tree to pristine...\n");
+		restore_state();
+		printf("Using the %s to prepare resolving by hand.\n",
+			best_strategy);
+		try_merge_strategy(best_strategy, common, head_arg);
+	}
+
+	if (squash)
+		finish(NULL, NULL);
+	else {
+		int fd;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next)
+			strbuf_addf(&buf, "%s\n",
+				sha1_to_hex(j->item->object.sha1));
+		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing",
+				git_path("MERGE_HEAD"));
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_HEAD"));
+		close(fd);
+		strbuf_addch(&merge_msg, '\n');
+		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MSG"));
+		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+			merge_msg.len)
+			die("Could not write to %s", git_path("MERGE_MSG"));
+		close(fd);
+	}
+
+	if (merge_was_ok) {
+		fprintf(stderr, "Automatic merge went well; "
+			"stopped before committing as requested\n");
+		return 0;
+	} else
+		return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index f3a4bb2..fcb8285 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -23,7 +23,7 @@ test_expect_success 'setup' '
 	done
 '
 
-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
 	git reset --hard c1 &&
 	i=2 &&
 	refs="" &&
-- 
1.5.6.1.322.ge904b.dirty
Miklos Vajna· Jul 7, 2008, 17:35 UTC · re: Miklos Vajna · lore

Re: [PATCH] Build in merge

On Mon, Jul 07, 2008 at 07:24:20PM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:
> Here is an updated version without using path_list at all.
Sorry forgot the interdiff and the log:
- interdiff: git diff 2ed1884..10d5724
- log: git log c255d12 (1 commit)

$ git grep -c path.list builtin-merge.c 0

Junio C Hamano· Jul 1, 2008, 07:27 UTC · re: Miklos Vajna · lore

Re: [PATCH 14/14] Build in merge

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 20 quoted lines
> +	/* See if remote matches <name>~<number>, or <name>^ */
> +	ptr = strrchr(remote, '^');
> +	if (ptr && ptr[1] == '\0') {
> +		for (len = 0, ptr = remote + strlen(remote);
> +				remote < ptr && ptr[-1] == '^';
> +				ptr--)
> +			len++;
> +	}
> +	else {
> +		ptr = strrchr(remote, '~');
> +		if (ptr && ptr[1] != '0' && isdigit(ptr[1])) {
> +			len = ptr-remote;
> +			ptr++;
> +			for (ptr++; *ptr; ptr++)
> +				if (!isdigit(*ptr)) {
> +					len = 0;
> +					break;
> +				}
> +		}
> +	}
I still have problems with the above.  I'd write it this way:
	int len, early;
	...
	/* See if remote matches <name>^^^.. or <name>~<number> */
	for (len = 0, ptr = remote + strlen(remote);
	     remote < ptr && ptr[-1] == '^';
	     ptr--)
		len++;
	if (len)
		early = 1;
	else {
		early = 0;
		ptr = strrchr(remote, '~');
		if (ptr) {
			int seen_nonzero = 0;
			len++; /* count ~ */
			while (*++ptr && isdigit(*ptr)) {
				seen_nonzero |= (*ptr != '0');
				len++;
			}
			if (*ptr)
				len = 0; /* not ...~<number> */
			else if (seen_nonzero)
				early = 1;
			else if (len == 1)
				early = 1; /* "name~" is "name~1"! */
		}
	}
	if (len) {
		struct strbuf truname = STRBUF_INIT;
		strbuf_addstr(&truname, "refs/heads/");
		strbuf_addstr(&truname, remote);
		strbuf_setlen(&truname, len+11);
		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
			strbuf_addf(msg,
				    "%s\t\tbranch '%s'%s of .\n",
				    sha1_to_hex(remote_head->sha1),
				    truname.buf,
				    (early ? " (early part)" : ""));
			return;
		}
	}

The first loop is obvious. If the tail end is ^, we set "len" and see if the remainder is a branch name (and if that is the case we are always talking about an early part of it of the branch).

Otherwise, we do want to say "early part" if "$name~<number>" is given, and another special case is "$name~" which is "$name~1" these days. As long as number is not zero we would want to say "early part". Otherwise we would want to say it is a branch itself, not its early part.

I'll queue the fixed-up result in 'pu', but I have to tend to other topics before I can actually publish. Together with the fix to "head_invalid" confusion I mentioned in another message squashed in to this commit, all the tests now finally seem to pass on the topic branch.

Oh, by the way, you sent this and the previous round without marking them as RFC nor WIP, even though they obviously did not even pass the test suite. For example, without the head_invalid fix, anything that runs merge on detached head, most notably "git rebase -i", would not work at all.

Miklos Vajna· Jul 1, 2008, 12:55 UTC · re: Junio C Hamano · lore

Re: [PATCH 14/14] Build in merge

On Tue, Jul 01, 2008 at 12:27:48AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
Show 10 quoted lines
> I'll queue the fixed-up result in 'pu', but I have to tend to other topics
> before I can actually publish.  Together with the fix to "head_invalid"
> confusion I mentioned in another message squashed in to this commit, all
> the tests now finally seem to pass on the topic branch.
> 
> Oh, by the way, you sent this and the previous round without marking them
> as RFC nor WIP, even though they obviously did not even pass the test
> suite.  For example, without the head_invalid fix, anything that runs
> merge on detached head, most notably "git rebase -i", would not work at
> all.

Thanks for pointing that out. I remember I used to run 'make test' before sending a patch, but that took a lot of time and then I used to run only t*merge*.sh, which - it turns out - was a bad idea, since I haven't noticed breaking t3404.

I'm now running a full 'make test' before I send the patch.

← back to recent threads