From 4c57bd27402353f135250af91d1cc2646db73df0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 16 Apr 2012 16:15:13 -0700 Subject: builtin/merge.c: remove "remoteheads" global variable Instead pass it around starting from the toplevel cmd_merge() as an explicit parameter. Signed-off-by: Junio C Hamano --- builtin/merge.c | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) (limited to 'builtin/merge.c') diff --git a/builtin/merge.c b/builtin/merge.c index 5126443fd..c5ca70b5a 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; " -- cgit v1.2.1 From b5d887f90692515a6e9c99e7683533d12df3ade2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 17 Apr 2012 11:31:10 -0700 Subject: builtin/merge.c: collect other parents early 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 --- builtin/merge.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'builtin/merge.c') diff --git a/builtin/merge.c b/builtin/merge.c index c5ca70b5a..2cef2f6a2 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) && -- cgit v1.2.1 From e78cbf8cbb61edfbdef5d33262f9b8cf02a3afca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 17 Apr 2012 12:22:26 -0700 Subject: builtin/merge.c: reduce parents early 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 t6028 is now fixed. Signed-off-by: Junio C Hamano --- builtin/merge.c | 65 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 25 deletions(-) (limited to 'builtin/merge.c') diff --git a/builtin/merge.c b/builtin/merge.c index 2cef2f6a2..20aeca0b3 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; } -- cgit v1.2.1