Re: What's cooking in git.git (Apr 2012, #05; Thu, 12)

7 messages, 2 authors, 2016-06-15 · open the first message on its own page

Re: What's cooking in git.git (Apr 2012, #05; Thu, 12)

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:53:36

Linus Torvalds [off-list ref] writes:
quoted hunk
Trivially tested with

    git merge HEAD^ HEAD^^

which did the wrong thing before, and now works.

                       Linus

---
 builtin/merge.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/builtin/merge.c b/builtin/merge.c
index 016a4dbee3b5..28fb5c9d6ada 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1370,8 +1370,7 @@ int cmd_merge(int argc, const char **argv, const
char *prefix)

        if (!common)
                ; /* No common ancestors found. We need a real merge. */
-       else if (!remoteheads->next && !common->next &&
-                       common->item == remoteheads->item) {
+       else if (!parents->next && parents->item == head_commit) {
When everybody in remote_heads is an ancestor of the current HEAD,
finalize_parents() would have reduced parents to a single element list
with HEAD on it, and we are "already up-to-date".  Ok.

I wonder if use of remoteheads later in the same function are correct,
though.  We equate "!remoteheads->next" and "We are not doing octopus",
for example.

Re: What's cooking in git.git (Apr 2012, #05; Thu, 12)

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:53:36

On Mon, Apr 16, 2012 at 10:29 AM, Junio C Hamano [off-list ref] wrote:
I wonder if use of remoteheads later in the same function are correct,
though.  We equate "!remoteheads->next" and "We are not doing octopus",
for example.
I do think it would generally be a great idea to never use
"remoteheads" at all. An octopus merge that has been simplified to
just two parents isn't really an octopus merge any more.

So I think you're probably right - we should try to avoid using
remoteheads entirely, and any use is suspect

                 Linus

[PATCH 0/4] merge: reduce set of parents consistently

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:53:37

So this is my attempt to address "Eek, why does your Octopus record many
irrelevant parents?!?!?!" issue from Linus and Michał (but without reusing
any code from them, except for the initial test).

In the original code, the variable remoteheads held the list of parents
read from the command line, and it was used throughout the code to see how
many parents the resulting merge will have (used to see if we use twohead
or octopus strategy), except that right before we come up with the set of
parents to record in the resulting merge commit, we discarded redundant
ones.

The updated code instead reduces the parents right after we read them.
This even detects the case where our HEAD is an ancestor of one of the
commits being merged, in which case HEAD will not be recorded unless we
are deliberately recording a fast-forward case as a real merge.

Junio C Hamano (3):
  builtin/merge.c: remove "remoteheads" global variable
  builtin/merge.c: collect other parents early
  builtin/merge.c: reduce parents early

Michał Kiedrowicz (1):
  git-merge: test octopus with redundant parents

 builtin/merge.c               |  142 +++++++++++++++++++++++++----------------
 t/t6028-merge-up-to-date.sh   |   17 ++++-
 t/t7602-merge-octopus-many.sh |   10 ++-
 3 files changed, 108 insertions(+), 61 deletions(-)

-- 
1.7.10.332.g1863c

[PATCH 1/4] git-merge: test octopus with redundant parents

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:53:37

From: Michał Kiedrowicz <redacted>

This happens when git merge is run to merge multiple commits that are
descendants of current HEAD (or are HEAD).  We've hit this while updating
master to origin/master but accidentaly we called (while being on master):

	$ git merge master origin/master

Here is a minimal testcase:

	$ git init a && cd a
	$ echo a >a && git add a
	$ git commit -minitial
	$ echo b >a && git add a
	$ git commit -msecond
	$ git checkout master^

	$ git merge master master
	Fast-forwarding to: master
	Already up-to-date with master
	Merge made by the 'octopus' strategy.
	 a |    2 +-
	  1 files changed, 1 insertions(+), 1 deletions(-)

	$ git cat-file commit HEAD
	tree eebfed94e75e7760540d1485c740902590a00332
	parent bd679e85202280b263e20a57639a142fa14c2c64
	author Michał Kiedrowicz [off-list ref] 1329132996 +0100
	committer Michał Kiedrowicz [off-list ref] 1329132996 +0100

	Merge branches 'master' and 'master' into HEAD

Signed-off-by: Michał Kiedrowicz <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
 t/t6028-merge-up-to-date.sh |   17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
index a91644e..824fca5 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6028-merge-up-to-date.sh
@@ -16,7 +16,12 @@ test_expect_success setup '
 	test_tick &&
 	git commit -m second &&
 	git tag c1 &&
-	git branch test
+	git branch test &&
+	echo third >file &&
+	git add file &&
+	test_tick &&
+	git commit -m third &&
+	git tag c2
 '
 
 test_expect_success 'merge -s recursive up-to-date' '
@@ -74,4 +79,14 @@ test_expect_success 'merge -s subtree up-to-date' '
 
 '
 
+test_expect_failure 'merge fast-forward octopus' '
+
+	git reset --hard c0 &&
+	test_tick &&
+	git merge c1 c2
+	expect=$(git rev-parse c2) &&
+	current=$(git rev-parse HEAD) &&
+	test "$expect" = "$current"
+'
+
 test_done
-- 
1.7.10.332.g1863c

[PATCH 2/4] builtin/merge.c: remove "remoteheads" global variable

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:53:37

Instead pass it around starting from the toplevel cmd_merge()
as an explicit parameter.

Signed-off-by: Junio C Hamano <redacted>
---
 builtin/merge.c |   51 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/builtin/merge.c b/builtin/merge.c
index 5126443..c5ca70b 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -52,7 +52,6 @@ static int fast_forward_only, option_edit = -1;
 static int allow_trivial = 1, have_message;
 static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
-static struct commit_list *remoteheads;
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char **xopts;
@@ -318,7 +317,7 @@ static void finish_up_to_date(const char *msg)
 	drop_save();
 }
 
-static void squash_message(struct commit *commit)
+static void squash_message(struct commit *commit, struct commit_list *remoteheads)
 {
 	struct rev_info rev;
 	struct strbuf out = STRBUF_INIT;
@@ -366,6 +365,7 @@ static void squash_message(struct commit *commit)
 }
 
 static void finish(struct commit *head_commit,
+		   struct commit_list *remoteheads,
 		   const unsigned char *new_head, const char *msg)
 {
 	struct strbuf reflog_message = STRBUF_INIT;
@@ -380,7 +380,7 @@ static void finish(struct commit *head_commit,
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
 	if (squash) {
-		squash_message(head_commit);
+		squash_message(head_commit, remoteheads);
 	} else {
 		if (verbosity >= 0 && !merge_msg.len)
 			printf(_("No merge message -- not updating HEAD\n"));
@@ -681,6 +681,7 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
 }
 
 static int try_merge_strategy(const char *strategy, struct commit_list *common,
+			      struct commit_list *remoteheads,
 			      struct commit *head, const char *head_arg)
 {
 	int index_fd;
@@ -874,14 +875,14 @@ static void read_merge_msg(struct strbuf *msg)
 		die_errno(_("Could not read from '%s'"), filename);
 }
 
-static void write_merge_state(void);
-static void abort_commit(const char *err_msg)
+static void write_merge_state(struct commit_list *);
+static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
 {
 	if (err_msg)
 		error("%s", err_msg);
 	fprintf(stderr,
 		_("Not committing merge; use 'git commit' to complete the merge.\n"));
-	write_merge_state();
+	write_merge_state(remoteheads);
 	exit(1);
 }
 
@@ -892,7 +893,7 @@ N_("Please enter a commit message to explain why this merge is necessary,\n"
    "Lines starting with '#' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
-static void prepare_to_commit(void)
+static void prepare_to_commit(struct commit_list *remoteheads)
 {
 	struct strbuf msg = STRBUF_INIT;
 	const char *comment = _(merge_editor_comment);
@@ -905,18 +906,18 @@ static void prepare_to_commit(void)
 		 git_path("MERGE_MSG"), "merge", NULL, NULL);
 	if (option_edit) {
 		if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
-			abort_commit(NULL);
+			abort_commit(remoteheads, NULL);
 	}
 	read_merge_msg(&msg);
 	stripspace(&msg, option_edit);
 	if (!msg.len)
-		abort_commit(_("Empty commit message."));
+		abort_commit(remoteheads, _("Empty commit message."));
 	strbuf_release(&merge_msg);
 	strbuf_addbuf(&merge_msg, &msg);
 	strbuf_release(&msg);
 }
 
-static int merge_trivial(struct commit *head)
+static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 {
 	unsigned char result_tree[20], result_commit[20];
 	struct commit_list *parent = xmalloc(sizeof(*parent));
@@ -927,17 +928,18 @@ static int merge_trivial(struct commit *head)
 	parent->next = xmalloc(sizeof(*parent->next));
 	parent->next->item = remoteheads->item;
 	parent->next->next = NULL;
-	prepare_to_commit();
+	prepare_to_commit(remoteheads);
 	if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
 			sign_commit))
 		die(_("failed to write commit object"));
-	finish(head, result_commit, "In-index merge");
+	finish(head, remoteheads, result_commit, "In-index merge");
 	drop_save();
 	return 0;
 }
 
 static int finish_automerge(struct commit *head,
 			    struct commit_list *common,
+			    struct commit_list *remoteheads,
 			    unsigned char *result_tree,
 			    const char *wt_strategy)
 {
@@ -959,13 +961,13 @@ static int finish_automerge(struct commit *head,
 			pptr = &commit_list_insert(j->item, pptr)->next;
 	}
 	strbuf_addch(&merge_msg, '\n');
-	prepare_to_commit();
+	prepare_to_commit(remoteheads);
 	free_commit_list(remoteheads);
 	if (commit_tree(&merge_msg, result_tree, parents, result_commit,
 			NULL, sign_commit))
 		die(_("failed to write commit object"));
 	strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
-	finish(head, result_commit, buf.buf);
+	finish(head, remoteheads, result_commit, buf.buf);
 	strbuf_release(&buf);
 	drop_save();
 	return 0;
@@ -1070,7 +1072,7 @@ static int setup_with_upstream(const char ***argv)
 	return i;
 }
 
-static void write_merge_state(void)
+static void write_merge_state(struct commit_list *remoteheads)
 {
 	const char *filename;
 	int fd;
@@ -1148,6 +1150,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	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 *remoteheads = NULL;
 	struct commit_list **remotes = &remoteheads;
 	void *branch_to_free;
 
@@ -1400,7 +1403,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			goto done;
 		}
 
-		finish(head_commit, commit->object.sha1, msg.buf);
+		finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
 		drop_save();
 		goto done;
 	} else if (!remoteheads->next && common->next)
@@ -1422,7 +1425,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			if (!read_tree_trivial(common->item->object.sha1,
 					       head_commit->object.sha1,
 					       remoteheads->item->object.sha1)) {
-				ret = merge_trivial(head_commit);
+				ret = merge_trivial(head_commit, remoteheads);
 				goto done;
 			}
 			printf(_("Nope.\n"));
@@ -1493,7 +1496,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		wt_strategy = use_strategies[i]->name;
 
 		ret = try_merge_strategy(use_strategies[i]->name,
-					 common, head_commit, head_arg);
+					 common, remoteheads,
+					 head_commit, head_arg);
 		if (!option_commit && !ret) {
 			merge_was_ok = 1;
 			/*
@@ -1535,8 +1539,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * auto resolved the merge cleanly.
 	 */
 	if (automerge_was_ok) {
-		ret = finish_automerge(head_commit, common, result_tree,
-				       wt_strategy);
+		ret = finish_automerge(head_commit, common, remoteheads,
+				       result_tree, wt_strategy);
 		goto done;
 	}
 
@@ -1561,13 +1565,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		restore_state(head_commit->object.sha1, stash);
 		printf(_("Using the %s to prepare resolving by hand.\n"),
 			best_strategy);
-		try_merge_strategy(best_strategy, common, head_commit, head_arg);
+		try_merge_strategy(best_strategy, common, remoteheads,
+				   head_commit, head_arg);
 	}
 
 	if (squash)
-		finish(head_commit, NULL, NULL);
+		finish(head_commit, remoteheads, NULL, NULL);
 	else
-		write_merge_state();
+		write_merge_state(remoteheads);
 
 	if (merge_was_ok)
 		fprintf(stderr, _("Automatic merge went well; "
-- 
1.7.10.332.g1863c

[PATCH 3/4] builtin/merge.c: collect other parents early

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:53:37

Move the code around to populate remoteheads list early in the process
before any decision regarding twohead vs octopus and fast-forwardness is
made.

Signed-off-by: Junio C Hamano <redacted>
---
 builtin/merge.c |   40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)
diff --git a/builtin/merge.c b/builtin/merge.c
index c5ca70b..2cef2f6 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1137,6 +1137,21 @@ static int default_edit_option(void)
 		st_stdin.st_mode == st_stdout.st_mode);
 }
 
+static struct commit_list *collect_parents(int argc, const char **argv)
+{
+	int i;
+	struct commit_list *remoteheads = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	for (i = 0; i < argc; i++) {
+		struct commit *commit = get_merge_parent(argv[i]);
+		if (!commit)
+			die(_("%s - not something we can merge"), argv[i]);
+		remotes = &commit_list_insert(commit, remotes)->next;
+	}
+	*remotes = NULL;
+	return remoteheads;
+}
 
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
@@ -1150,8 +1165,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	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 *remoteheads = NULL;
-	struct commit_list **remotes = &remoteheads;
+	struct commit_list *remoteheads, *p;
 	void *branch_to_free;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1256,6 +1270,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		head_arg = argv[1];
 		argv += 2;
 		argc -= 2;
+		remoteheads = collect_parents(argc, argv);
 	} else if (!head_commit) {
 		struct commit *remote_head;
 		/*
@@ -1271,7 +1286,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		if (!allow_fast_forward)
 			die(_("Non-fast-forward commit does not make sense into "
 			    "an empty head"));
-		remote_head = get_merge_parent(argv[0]);
+		remoteheads = collect_parents(argc, argv);
+		remote_head = remoteheads->item;
 		if (!remote_head)
 			die(_("%s - not something we can merge"), argv[0]);
 		read_empty(remote_head->object.sha1, 0);
@@ -1289,8 +1305,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		 * the standard merge summary message to be appended
 		 * to the given message.
 		 */
-		for (i = 0; i < argc; i++)
-			merge_name(argv[i], &merge_names);
+		remoteheads = collect_parents(argc, argv);
+		for (p = remoteheads; p; p = p->next)
+			merge_name(merge_remote_util(p->item)->name, &merge_names);
 
 		if (!have_message || shortlog_len) {
 			struct fmt_merge_msg_opts opts;
@@ -1309,19 +1326,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			builtin_merge_options);
 
 	strbuf_addstr(&buf, "merge");
-	for (i = 0; i < argc; i++)
-		strbuf_addf(&buf, " %s", argv[i]);
+	for (p = remoteheads; p; p = p->next)
+		strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
 	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
 	strbuf_reset(&buf);
 
-	for (i = 0; i < argc; i++) {
-		struct commit *commit = get_merge_parent(argv[i]);
-		if (!commit)
-			die(_("%s - not something we can merge"), argv[i]);
-		remotes = &commit_list_insert(commit, remotes)->next;
+	for (p = remoteheads; p; p = p->next) {
+		struct commit *commit = p->item;
 		strbuf_addf(&buf, "GITHEAD_%s",
 			    sha1_to_hex(commit->object.sha1));
-		setenv(buf.buf, argv[i], 1);
+		setenv(buf.buf, merge_remote_util(commit)->name, 1);
 		strbuf_reset(&buf);
 		if (!fast_forward_only &&
 		    merge_remote_util(commit) &&
-- 
1.7.10.332.g1863c

[PATCH 4/4] builtin/merge.c: reduce parents early

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:53:37

Instead of waiting until we record the parents of resulting merge, reduce
redundant parents (including our HEAD) immediately after reading them.

The change to t7602 illustrates the essence of the effect of this change.
The octopus merge strategy used to be fed with redundant commits only to
discard them as "up-to-date", but we no longer feed such redundant commits
to it and the affected test degenerates to a regular two-head merge.

And obviously the known-to-be-broken test in t7602 is now fixed.

Signed-off-by: Junio C Hamano <redacted>
---
 builtin/merge.c               |   65 +++++++++++++++++++++++++----------------
 t/t6028-merge-up-to-date.sh   |    2 +-
 t/t7602-merge-octopus-many.sh |   10 +++----
 3 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/builtin/merge.c b/builtin/merge.c
index 2cef2f6..20aeca0 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -938,31 +938,22 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 }
 
 static int finish_automerge(struct commit *head,
+			    int head_subsumed,
 			    struct commit_list *common,
 			    struct commit_list *remoteheads,
 			    unsigned char *result_tree,
 			    const char *wt_strategy)
 {
-	struct commit_list *parents = NULL, *j;
+	struct commit_list *parents = NULL;
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char result_commit[20];
 
 	free_commit_list(common);
-	if (allow_fast_forward) {
-		parents = remoteheads;
+	parents = remoteheads;
+	if (!head_subsumed || !allow_fast_forward)
 		commit_list_insert(head, &parents);
-		parents = reduce_heads(parents);
-	} else {
-		struct commit_list **pptr = &parents;
-
-		pptr = &commit_list_insert(head,
-				pptr)->next;
-		for (j = remoteheads; j; j = j->next)
-			pptr = &commit_list_insert(j->item, pptr)->next;
-	}
 	strbuf_addch(&merge_msg, '\n');
 	prepare_to_commit(remoteheads);
-	free_commit_list(remoteheads);
 	if (commit_tree(&merge_msg, result_tree, parents, result_commit,
 			NULL, sign_commit))
 		die(_("failed to write commit object"));
@@ -1137,12 +1128,16 @@ static int default_edit_option(void)
 		st_stdin.st_mode == st_stdout.st_mode);
 }
 
-static struct commit_list *collect_parents(int argc, const char **argv)
+static struct commit_list *collect_parents(struct commit *head_commit,
+					   int *head_subsumed,
+					   int argc, const char **argv)
 {
 	int i;
-	struct commit_list *remoteheads = NULL;
+	struct commit_list *remoteheads = NULL, *parents, *next;
 	struct commit_list **remotes = &remoteheads;
 
+	if (head_commit)
+		remotes = &commit_list_insert(head_commit, remotes)->next;
 	for (i = 0; i < argc; i++) {
 		struct commit *commit = get_merge_parent(argv[i]);
 		if (!commit)
@@ -1150,6 +1145,20 @@ static struct commit_list *collect_parents(int argc, const char **argv)
 		remotes = &commit_list_insert(commit, remotes)->next;
 	}
 	*remotes = NULL;
+
+	parents = reduce_heads(remoteheads);
+
+	*head_subsumed = 1; /* we will flip this to 0 when we find it */
+	for (remoteheads = NULL, remotes = &remoteheads;
+	     parents;
+	     parents = next) {
+		struct commit *commit = parents->item;
+		next = parents->next;
+		if (commit == head_commit)
+			*head_subsumed = 0;
+		else
+			remotes = &commit_list_insert(commit, remotes)->next;
+	}
 	return remoteheads;
 }
 
@@ -1161,7 +1170,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	struct commit *head_commit;
 	struct strbuf buf = STRBUF_INIT;
 	const char *head_arg;
-	int flag, i, ret = 0;
+	int flag, i, ret = 0, head_subsumed;
 	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;
@@ -1270,7 +1279,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		head_arg = argv[1];
 		argv += 2;
 		argc -= 2;
-		remoteheads = collect_parents(argc, argv);
+		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
 	} else if (!head_commit) {
 		struct commit *remote_head;
 		/*
@@ -1286,7 +1295,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		if (!allow_fast_forward)
 			die(_("Non-fast-forward commit does not make sense into "
 			    "an empty head"));
-		remoteheads = collect_parents(argc, argv);
+		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
 		remote_head = remoteheads->item;
 		if (!remote_head)
 			die(_("%s - not something we can merge"), argv[0]);
@@ -1305,7 +1314,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		 * the standard merge summary message to be appended
 		 * to the given message.
 		 */
-		remoteheads = collect_parents(argc, argv);
+		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
 		for (p = remoteheads; p; p = p->next)
 			merge_name(merge_remote_util(p->item)->name, &merge_names);
 
@@ -1351,7 +1360,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		option_edit = 0;
 
 	if (!use_strategies) {
-		if (!remoteheads->next)
+		if (!remoteheads)
+			; /* already up-to-date */
+		else if (!remoteheads->next)
 			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
 		else
 			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
@@ -1364,7 +1375,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			allow_trivial = 0;
 	}
 
-	if (!remoteheads->next)
+	if (!remoteheads)
+		; /* already up-to-date */
+	else if (!remoteheads->next)
 		common = get_merge_bases(head_commit, remoteheads->item, 1);
 	else {
 		struct commit_list *list = remoteheads;
@@ -1376,10 +1389,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
 		   NULL, 0, DIE_ON_ERR);
 
-	if (!common)
+	if (remoteheads && !common)
 		; /* No common ancestors found. We need a real merge. */
-	else if (!remoteheads->next && !common->next &&
-			common->item == remoteheads->item) {
+	else if (!remoteheads ||
+		 (!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.
@@ -1553,7 +1567,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * auto resolved the merge cleanly.
 	 */
 	if (automerge_was_ok) {
-		ret = finish_automerge(head_commit, common, remoteheads,
+		ret = finish_automerge(head_commit, head_subsumed,
+				       common, remoteheads,
 				       result_tree, wt_strategy);
 		goto done;
 	}
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
index 824fca5..c518e9c 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6028-merge-up-to-date.sh
@@ -79,7 +79,7 @@ test_expect_success 'merge -s subtree up-to-date' '
 
 '
 
-test_expect_failure 'merge fast-forward octopus' '
+test_expect_success 'merge fast-forward octopus' '
 
 	git reset --hard c0 &&
 	test_tick &&
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index 5783ebf..7117b57 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -70,17 +70,15 @@ test_expect_success 'merge output uses pretty names' '
 '
 
 cat >expected <<\EOF
-Already up-to-date with c4
-Trying simple merge with c5
-Merge made by the 'octopus' strategy.
+Merge made by the 'recursive' strategy.
  c5.c |    1 +
  1 file changed, 1 insertion(+)
  create mode 100644 c5.c
 EOF
 
-test_expect_success 'merge up-to-date output uses pretty names' '
-	git merge c4 c5 >actual &&
-	test_cmp actual expected
+test_expect_success 'merge reduces irrelevant remote heads' '
+	GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
+	test_cmp expected actual
 '
 
 cat >expected <<\EOF
-- 
1.7.10.332.g1863c
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help