summaryrefslogtreecommitdiff
path: root/builtin/merge.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/merge.c')
-rw-r--r--builtin/merge.c390
1 files changed, 252 insertions, 138 deletions
diff --git a/builtin/merge.c b/builtin/merge.c
index 2870a6af6f..dd50a0c57b 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -26,6 +26,8 @@
#include "merge-recursive.h"
#include "resolve-undo.h"
#include "remote.h"
+#include "fmt-merge-msg.h"
+#include "gpg-interface.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -44,12 +46,12 @@ static const char * const builtin_merge_usage[] = {
NULL
};
-static int show_diffstat = 1, shortlog_len, squash;
+static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only, option_edit;
+static int fast_forward_only, option_edit = -1;
static int allow_trivial = 1, have_message;
-static struct strbuf merge_msg;
-static struct commit_list *remoteheads;
+static int overwrite_ignore = 1;
+static struct strbuf merge_msg = STRBUF_INIT;
static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
static const char **xopts;
@@ -62,6 +64,7 @@ static int allow_rerere_auto;
static int abort_current_merge;
static int show_progress = -1;
static int default_to_upstream;
+static const char *sign_commit;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -189,7 +192,7 @@ static struct option builtin_merge_options[] = {
"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('e', "edit", &option_edit,
+ OPT_BOOL('e', "edit", &option_edit,
"edit message before committing"),
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
"allow fast-forward (default)"),
@@ -207,6 +210,9 @@ static struct option builtin_merge_options[] = {
OPT_BOOLEAN(0, "abort", &abort_current_merge,
"abort the current in-progress merge"),
OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
+ { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+ "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
OPT_END()
};
@@ -311,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;
@@ -359,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;
@@ -373,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"));
@@ -392,6 +399,8 @@ static void finish(struct commit *head_commit,
if (new_head && show_diffstat) {
struct diff_options opts;
diff_setup(&opts);
+ opts.stat_width = -1; /* use full terminal width */
+ opts.stat_graph_width = -1; /* respect statGraphWidth config */
opts.output_format |=
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
opts.detect_rename = DIFF_DETECT_RENAME;
@@ -408,21 +417,11 @@ static void finish(struct commit *head_commit,
strbuf_release(&reflog_message);
}
-static struct object *want_commit(const char *name)
-{
- struct object *obj;
- unsigned char sha1[20];
- if (get_sha1(name, sha1))
- return NULL;
- obj = parse_object(sha1);
- return peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
/* 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 commit *remote_head;
+ unsigned char branch_head[20];
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
const char *ptr;
@@ -433,7 +432,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
remote = bname.buf;
memset(branch_head, 0, sizeof(branch_head));
- remote_head = want_commit(remote);
+ remote_head = get_merge_parent(remote);
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
@@ -443,6 +442,11 @@ static void merge_name(const char *remote, struct strbuf *msg)
sha1_to_hex(branch_head), remote);
goto cleanup;
}
+ if (!prefixcmp(found_ref, "refs/tags/")) {
+ strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
+ sha1_to_hex(branch_head), remote);
+ goto cleanup;
+ }
if (!prefixcmp(found_ref, "refs/remotes/")) {
strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
sha1_to_hex(branch_head), remote);
@@ -481,10 +485,10 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_addstr(&truname, "refs/heads/");
strbuf_addstr(&truname, remote);
strbuf_setlen(&truname, truname.len - len);
- if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
+ if (ref_exists(truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
- sha1_to_hex(remote_head->sha1),
+ sha1_to_hex(remote_head->object.sha1),
truname.buf + 11,
(early ? " (early part)" : ""));
strbuf_release(&truname);
@@ -514,7 +518,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
goto cleanup;
}
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
- sha1_to_hex(remote_head->sha1), remote);
+ sha1_to_hex(remote_head->object.sha1), remote);
cleanup:
strbuf_release(&buf);
strbuf_release(&bname);
@@ -542,6 +546,8 @@ static void parse_branch_merge_options(char *bmo)
static int git_merge_config(const char *k, const char *v, void *cb)
{
+ int status;
+
if (branch && !prefixcmp(k, "branch.") &&
!prefixcmp(k + 7, branch) &&
!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
@@ -558,15 +564,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return git_config_string(&pull_octopus, k, v);
else if (!strcmp(k, "merge.renormalize"))
option_renormalize = git_config_bool(k, v);
- else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
- int is_bool;
- shortlog_len = git_config_bool_or_int(k, v, &is_bool);
- if (!is_bool && shortlog_len < 0)
- return error(_("%s: negative length %s"), k, v);
- if (is_bool && shortlog_len)
- shortlog_len = DEFAULT_MERGE_LOG_LEN;
- return 0;
- } else if (!strcmp(k, "merge.ff")) {
+ else if (!strcmp(k, "merge.ff")) {
int boolval = git_config_maybe_bool(k, v);
if (0 <= boolval) {
allow_fast_forward = boolval;
@@ -579,6 +577,13 @@ static int git_merge_config(const char *k, const char *v, void *cb)
default_to_upstream = git_config_bool(k, v);
return 0;
}
+
+ status = fmt_merge_msg_config(k, v, cb);
+ if (status)
+ return status;
+ status = git_gpg_config(k, v, NULL);
+ if (status)
+ return status;
return git_diff_ui_config(k, v, cb);
}
@@ -678,6 +683,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;
@@ -718,7 +724,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
o.branch1 = head_arg;
- o.branch2 = remoteheads->item->util;
+ o.branch2 = merge_remote_util(remoteheads->item)->name;
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
@@ -773,10 +779,12 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
memset(&trees, 0, sizeof(trees));
memset(&opts, 0, sizeof(opts));
memset(&t, 0, sizeof(t));
- memset(&dir, 0, sizeof(dir));
- dir.flags |= DIR_SHOW_IGNORED;
- dir.exclude_per_dir = ".gitignore";
- opts.dir = &dir;
+ if (overwrite_ignore) {
+ memset(&dir, 0, sizeof(dir));
+ dir.flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(&dir);
+ opts.dir = &dir;
+ }
opts.head_idx = 1;
opts.src_index = &the_index;
@@ -869,39 +877,49 @@ 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);
}
-static void prepare_to_commit(void)
+static const char merge_editor_comment[] =
+N_("Please enter a commit message to explain why this merge is necessary,\n"
+ "especially if it merges an updated upstream into a topic branch.\n"
+ "\n"
+ "Lines starting with '#' will be ignored, and an empty message aborts\n"
+ "the commit.\n");
+
+static void prepare_to_commit(struct commit_list *remoteheads)
{
struct strbuf msg = STRBUF_INIT;
+ const char *comment = _(merge_editor_comment);
strbuf_addbuf(&msg, &merge_msg);
strbuf_addch(&msg, '\n');
+ if (0 < option_edit)
+ strbuf_add_lines(&msg, "# ", comment, strlen(comment));
write_merge_msg(&msg);
run_hook(get_index_file(), "prepare-commit-msg",
git_path("MERGE_MSG"), "merge", NULL, NULL);
- if (option_edit) {
+ if (0 < 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);
+ stripspace(&msg, 0 < 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));
@@ -912,41 +930,37 @@ 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();
- commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
- finish(head, result_commit, "In-index merge");
+ 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, remoteheads, result_commit, "In-index merge");
drop_save();
return 0;
}
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();
- free_commit_list(remoteheads);
- commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
+ prepare_to_commit(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;
@@ -1051,16 +1065,23 @@ 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;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
- for (j = remoteheads; j; j = j->next)
- strbuf_addf(&buf, "%s\n",
- sha1_to_hex(j->item->object.sha1));
+ for (j = remoteheads; j; j = j->next) {
+ unsigned const char *sha1;
+ struct commit *c = j->item;
+ if (c->util && merge_remote_util(c)->obj) {
+ sha1 = merge_remote_util(c)->obj->sha1;
+ } else {
+ sha1 = c->object.sha1;
+ }
+ strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+ }
filename = git_path("MERGE_HEAD");
fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
@@ -1083,6 +1104,66 @@ static void write_merge_state(void)
close(fd);
}
+static int default_edit_option(void)
+{
+ static const char name[] = "GIT_MERGE_AUTOEDIT";
+ const char *e = getenv(name);
+ struct stat st_stdin, st_stdout;
+
+ if (have_message)
+ /* an explicit -m msg without --[no-]edit */
+ return 0;
+
+ if (e) {
+ int v = git_config_maybe_bool(name, e);
+ if (v < 0)
+ die("Bad value '%s' in environment '%s'", e, name);
+ return v;
+ }
+
+ /* Use editor if stdin and stdout are the same and is a tty */
+ return (!fstat(0, &st_stdin) &&
+ !fstat(1, &st_stdout) &&
+ isatty(0) && isatty(1) &&
+ st_stdin.st_dev == st_stdout.st_dev &&
+ st_stdin.st_ino == st_stdout.st_ino &&
+ st_stdin.st_mode == st_stdout.st_mode);
+}
+
+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, *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)
+ die(_("%s - not something we can merge"), argv[i]);
+ 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;
+}
+
int cmd_merge(int argc, const char **argv, const char *prefix)
{
unsigned char result_tree[20];
@@ -1091,11 +1172,12 @@ 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;
+ 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;
- struct commit_list **remotes = &remoteheads;
+ struct commit_list *remoteheads, *p;
+ void *branch_to_free;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1104,7 +1186,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = resolve_ref("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
if (branch && !prefixcmp(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1118,6 +1200,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
parse_branch_merge_options(branch_mergeoptions);
argc = parse_options(argc, argv, prefix, builtin_merge_options,
builtin_merge_usage, 0);
+ if (shortlog_len < 0)
+ shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
if (verbosity < 0 && show_progress == -1)
show_progress = 0;
@@ -1130,7 +1214,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("There is no merge to abort (MERGE_HEAD missing)."));
/* Invoke 'git reset --merge' */
- return cmd_reset(nargc, nargv, prefix);
+ ret = cmd_reset(nargc, nargv, prefix);
+ goto done;
}
if (read_cache_unmerged())
@@ -1169,9 +1254,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("You cannot combine --no-ff with --ff-only."));
if (!abort_current_merge) {
- if (!argc && default_to_upstream)
- argc = setup_with_upstream(&argv);
- else if (argc == 1 && !strcmp(argv[0], "-"))
+ if (!argc) {
+ if (default_to_upstream)
+ argc = setup_with_upstream(&argv);
+ else
+ die(_("No commit specified and merge.defaultToUpstream not set."));
+ } else if (argc == 1 && !strcmp(argv[0], "-"))
argv[0] = "@{-1}";
}
if (!argc)
@@ -1193,8 +1281,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
head_arg = argv[1];
argv += 2;
argc -= 2;
+ remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
} else if (!head_commit) {
- struct object *remote_head;
+ struct commit *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.
@@ -1208,13 +1297,14 @@ 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 = want_commit(argv[0]);
+ 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]);
- read_empty(remote_head->sha1, 0);
- update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
- DIE_ON_ERR);
- return 0;
+ read_empty(remote_head->object.sha1, 0);
+ update_ref("initial pull", "HEAD", remote_head->object.sha1,
+ NULL, 0, DIE_ON_ERR);
+ goto done;
} else {
struct strbuf merge_names = STRBUF_INIT;
@@ -1222,19 +1312,21 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
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.
+ * All the rest are the commits being merged; prepare
+ * 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(head_commit, &head_subsumed, argc, argv);
+ for (p = remoteheads; p; p = p->next)
+ merge_name(merge_remote_util(p->item)->name, &merge_names);
if (!have_message || shortlog_len) {
- fmt_merge_msg(&merge_names, &merge_msg, !have_message,
- shortlog_len);
+ struct fmt_merge_msg_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.add_title = !have_message;
+ opts.shortlog_len = shortlog_len;
+
+ fmt_merge_msg(&merge_names, &merge_msg, &opts);
if (merge_msg.len)
strbuf_setlen(&merge_msg, merge_msg.len - 1);
}
@@ -1245,29 +1337,31 @@ 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 object *o;
- struct commit *commit;
-
- o = want_commit(argv[i]);
- if (!o)
- die(_("%s - not something we can merge"), argv[i]);
- commit = lookup_commit(o->sha1);
- commit->util = (void *)argv[i];
- remotes = &commit_list_insert(commit, remotes)->next;
-
- strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
- setenv(buf.buf, argv[i], 1);
+ 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, merge_remote_util(commit)->name, 1);
strbuf_reset(&buf);
+ if (!fast_forward_only &&
+ merge_remote_util(commit) &&
+ merge_remote_util(commit)->obj &&
+ merge_remote_util(commit)->obj->type == OBJ_TAG)
+ allow_fast_forward = 0;
}
+ if (option_edit < 0)
+ option_edit = default_edit_option();
+
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);
@@ -1280,7 +1374,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;
@@ -1292,22 +1388,23 @@ 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.
*/
finish_up_to_date("Already up-to-date.");
- return 0;
+ goto done;
} else if (allow_fast_forward && !remoteheads->next &&
!common->next &&
!hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
/* Again the most common case of merging one remote. */
struct strbuf msg = STRBUF_INIT;
- struct object *o;
+ struct commit *commit;
char hex[41];
strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
@@ -1321,16 +1418,21 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (have_message)
strbuf_addstr(&msg,
" (no commit created; -m option ignored)");
- o = want_commit(sha1_to_hex(remoteheads->item->object.sha1));
- if (!o)
- return 1;
+ commit = remoteheads->item;
+ if (!commit) {
+ ret = 1;
+ goto done;
+ }
- if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1))
- return 1;
+ if (checkout_fast_forward(head_commit->object.sha1,
+ commit->object.sha1)) {
+ ret = 1;
+ goto done;
+ }
- finish(head_commit, o->sha1, msg.buf);
+ finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
drop_save();
- return 0;
+ goto done;
} else if (!remoteheads->next && common->next)
;
/*
@@ -1345,11 +1447,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
refresh_cache(REFRESH_QUIET);
if (allow_trivial && !fast_forward_only) {
/* See if it is really trivial. */
- git_committer_info(IDENT_ERROR_ON_NO_NAME);
+ git_committer_info(IDENT_STRICT);
printf(_("Trying really trivial in-index merge...\n"));
if (!read_tree_trivial(common->item->object.sha1,
- head_commit->object.sha1, remoteheads->item->object.sha1))
- return merge_trivial(head_commit);
+ head_commit->object.sha1,
+ remoteheads->item->object.sha1)) {
+ ret = merge_trivial(head_commit, remoteheads);
+ goto done;
+ }
printf(_("Nope.\n"));
}
} else {
@@ -1377,7 +1482,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (up_to_date) {
finish_up_to_date("Already up-to-date. Yeeah!");
- return 0;
+ goto done;
}
}
@@ -1385,7 +1490,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("Not possible to fast-forward, aborting."));
/* We are going to make a new commit. */
- git_committer_info(IDENT_ERROR_ON_NO_NAME);
+ git_committer_info(IDENT_STRICT);
/*
* At this point, we need a real merge. No matter what strategy
@@ -1418,7 +1523,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;
/*
@@ -1459,9 +1565,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* If we have a resulting tree, that means the strategy module
* auto resolved the merge cleanly.
*/
- if (automerge_was_ok)
- return finish_automerge(head_commit, common, result_tree,
- wt_strategy);
+ if (automerge_was_ok) {
+ ret = finish_automerge(head_commit, head_subsumed,
+ common, remoteheads,
+ result_tree, wt_strategy);
+ goto done;
+ }
/*
* Pick the result from the best strategy and have the user fix
@@ -1475,7 +1584,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
fprintf(stderr, _("Merge with strategy %s failed.\n"),
use_strategies[0]->name);
- return 2;
+ ret = 2;
+ goto done;
} else if (best_strategy == wt_strategy)
; /* We already have its result in the working tree. */
else {
@@ -1483,18 +1593,22 @@ 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) {
+ if (merge_was_ok)
fprintf(stderr, _("Automatic merge went well; "
"stopped before committing as requested\n"));
- return 0;
- } else
- return suggest_conflicts(option_renormalize);
+ else
+ ret = suggest_conflicts(option_renormalize);
+
+done:
+ free(branch_to_free);
+ return ret;
}