};
static const char * const builtin_merge_usage[] = {
- "git merge [options] [<commit>...]",
- "git merge [options] <msg> HEAD <commit>",
- "git merge --abort",
+ N_("git merge [options] [<commit>...]"),
+ N_("git merge [options] <msg> HEAD <commit>"),
+ N_("git merge --abort"),
NULL
};
static struct option builtin_merge_options[] = {
{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
- "do not show a diffstat at the end of the merge",
+ N_("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)"),
- { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
- "add (at most <n>) entries from shortlog to merge commit message",
+ N_("show a diffstat at the end of the merge")),
+ OPT_BOOLEAN(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
+ { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+ N_("add (at most <n>) entries from shortlog to merge commit message"),
PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
OPT_BOOLEAN(0, "squash", &squash,
- "create a single commit instead of doing a merge"),
+ N_("create a single commit instead of doing a merge")),
OPT_BOOLEAN(0, "commit", &option_commit,
- "perform a commit if the merge succeeds (default)"),
+ N_("perform a commit if the merge succeeds (default)")),
OPT_BOOL('e', "edit", &option_edit,
- "edit message before committing"),
+ N_("edit message before committing")),
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
- "allow fast-forward (default)"),
+ N_("allow fast-forward (default)")),
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
- "abort if fast-forward is not possible"),
+ N_("abort if fast-forward is not possible")),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
- OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
- "merge strategy to use", option_parse_strategy),
- OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
- "option for selected merge strategy", option_parse_x),
- OPT_CALLBACK('m', "message", &merge_msg, "message",
- "merge commit message (for a non-fast-forward merge)",
+ OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
+ N_("merge strategy to use"), option_parse_strategy),
+ OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
+ N_("option for selected merge strategy"), option_parse_x),
+ OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
+ N_("merge commit message (for a non-fast-forward merge)"),
option_parse_message),
OPT__VERBOSITY(&verbosity),
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)"),
+ N_("abort the current in-progress merge")),
+ OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
+ { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
+ N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
OPT_END()
};
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;
- if (diff_setup_done(&opts) < 0)
- die(_("diff_setup_done failed"));
+ diff_setup_done(&opts);
diff_tree_sha1(head, new_head, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
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(remoteheads, NULL);
}
read_merge_msg(&msg);
- stripspace(&msg, option_edit);
+ stripspace(&msg, 0 < option_edit);
if (!msg.len)
abort_commit(remoteheads, _("Empty commit message."));
strbuf_release(&merge_msg);
}
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"));
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)
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;
}
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;
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;
/*
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]);
* 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);
if (!fast_forward_only &&
merge_remote_util(commit) &&
merge_remote_util(commit)->obj &&
- merge_remote_util(commit)->obj->type == OBJ_TAG) {
- if (option_edit < 0)
- option_edit = default_edit_option();
+ merge_remote_util(commit)->obj->type == OBJ_TAG)
allow_fast_forward = 0;
- }
}
if (option_edit < 0)
- 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);
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;
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.
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,
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
* 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;
}