#include "help.h"
#include "merge-recursive.h"
#include "resolve-undo.h"
+#include "remote.h"
+#include "fmt-merge-msg.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
};
static const char * const builtin_merge_usage[] = {
- "git merge [options] <remote>...",
- "git merge [options] <msg> HEAD <remote>",
+ "git merge [options] [<commit>...]",
+ "git merge [options] <msg> HEAD <commit>",
+ "git merge --abort",
NULL
};
-static int show_diffstat = 1, option_log, squash;
+static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only;
+static int fast_forward_only, option_edit;
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 unsigned char head[20], stash[20];
static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
static const char **xopts;
static size_t xopts_nr, xopts_alloc;
static const char *branch;
+static char *branch_mergeoptions;
+static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
+static int abort_current_merge;
+static int show_progress = -1;
+static int default_to_upstream;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg);
have_message = 1;
} else
- return error("switch `m' requires a value");
+ return error(_("switch `m' requires a value"));
return 0;
}
exclude_cmds(&main_cmds, ¬_strategies);
}
if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
- fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
- fprintf(stderr, "Available strategies are:");
+ fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name);
+ fprintf(stderr, _("Available strategies are:"));
for (i = 0; i < main_cmds.cnt; i++)
fprintf(stderr, " %s", main_cmds.names[i]->name);
fprintf(stderr, ".\n");
if (other_cmds.cnt) {
- fprintf(stderr, "Available custom strategies are:");
+ fprintf(stderr, _("Available custom strategies are:"));
for (i = 0; i < other_cmds.cnt; i++)
fprintf(stderr, " %s", other_cmds.names[i]->name);
fprintf(stderr, ".\n");
ret = xcalloc(1, sizeof(struct strategy));
ret->name = xstrdup(name);
+ ret->attr = NO_TRIVIAL;
return ret;
}
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"),
+ { OPTION_INTEGER, 0, "log", &shortlog_len, "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"),
OPT_BOOLEAN(0, "commit", &option_commit,
"perform a commit if the merge succeeds (default)"),
+ OPT_BOOLEAN('e', "edit", &option_edit,
+ "edit message before committing"),
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
"allow fast-forward (default)"),
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
"option for selected merge strategy", option_parse_x),
OPT_CALLBACK('m', "message", &merge_msg, "message",
- "message to be used for the merge commit (if any)",
+ "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),
+ OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
OPT_END()
};
unlink(git_path("MERGE_MODE"));
}
-static void save_state(void)
+static int save_state(unsigned char *stash)
{
int len;
struct child_process cp;
cp.git_cmd = 1;
if (start_command(&cp))
- die("could not run stash.");
+ 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;
+ die(_("stash failed"));
+ else if (!len) /* no changes */
+ return -1;
strbuf_setlen(&buffer, buffer.len-1);
if (get_sha1(buffer.buf, stash))
- die("not a valid object: %s", buffer.buf);
+ die(_("not a valid object: %s"), buffer.buf);
+ return 0;
+}
+
+static void read_empty(unsigned const char *sha1, int verbose)
+{
+ int i = 0;
+ const char *args[7];
+
+ args[i++] = "read-tree";
+ if (verbose)
+ args[i++] = "-v";
+ args[i++] = "-m";
+ args[i++] = "-u";
+ args[i++] = EMPTY_TREE_SHA1_HEX;
+ args[i++] = sha1_to_hex(sha1);
+ args[i] = NULL;
+
+ if (run_command_v_opt(args, RUN_GIT_CMD))
+ die(_("read-tree failed"));
}
static void reset_hard(unsigned const char *sha1, int verbose)
args[i] = NULL;
if (run_command_v_opt(args, RUN_GIT_CMD))
- die("read-tree failed");
+ die(_("read-tree failed"));
}
-static void restore_state(void)
+static void restore_state(const unsigned char *head,
+ const unsigned char *stash)
{
struct strbuf sb = STRBUF_INIT;
const char *args[] = { "stash", "apply", NULL, NULL };
static void finish_up_to_date(const char *msg)
{
if (verbosity >= 0)
- printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+ printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
drop_save();
}
-static void squash_message(void)
+static void squash_message(struct commit *commit)
{
struct rev_info rev;
- struct commit *commit;
struct strbuf out = STRBUF_INIT;
struct commit_list *j;
+ const char *filename;
int fd;
struct pretty_print_context ctx = {0};
- printf("Squash commit -- not updating HEAD\n");
- fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+ printf(_("Squash commit -- not updating HEAD\n"));
+ filename = git_path("SQUASH_MSG");
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno("Could not write to '%s'", git_path("SQUASH_MSG"));
+ die_errno(_("Could not write to '%s'"), filename);
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);
setup_revisions(0, NULL, &rev, NULL);
if (prepare_revision_walk(&rev))
- die("revision walk setup failed");
+ die(_("revision walk setup failed"));
ctx.abbrev = rev.abbrev;
ctx.date_mode = rev.date_mode;
+ ctx.fmt = rev.commit_format;
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, &ctx);
+ pretty_print_commit(&ctx, commit, &out);
}
if (write(fd, out.buf, out.len) < 0)
- die_errno("Writing SQUASH_MSG");
+ die_errno(_("Writing SQUASH_MSG"));
if (close(fd))
- die_errno("Finishing SQUASH_MSG");
+ die_errno(_("Finishing SQUASH_MSG"));
strbuf_release(&out);
}
-static void finish(const unsigned char *new_head, const char *msg)
+static void finish(struct commit *head_commit,
+ const unsigned char *new_head, const char *msg)
{
struct strbuf reflog_message = STRBUF_INIT;
+ const unsigned char *head = head_commit->object.sha1;
if (!msg)
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
getenv("GIT_REFLOG_ACTION"), msg);
}
if (squash) {
- squash_message();
+ squash_message(head_commit);
} else {
if (verbosity >= 0 && !merge_msg.len)
- printf("No merge message -- not updating HEAD\n");
+ printf(_("No merge message -- not updating HEAD\n"));
else {
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
update_ref(reflog_message.buf, "HEAD",
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");
+ die(_("diff_setup_done failed"));
diff_tree_sha1(head, new_head, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
/* 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;
remote = bname.buf;
memset(branch_head, 0, sizeof(branch_head));
- remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+ remote_head = get_merge_parent(remote);
if (!remote_head)
- die("'%s' does not point to a commit", remote);
+ die(_("'%s' does not point to a commit"), remote);
if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
if (!prefixcmp(found_ref, "refs/heads/")) {
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 branch '%s' of .\n",
+ strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
sha1_to_hex(branch_head), remote);
goto cleanup;
}
strbuf_addstr(&truname, "refs/heads/");
strbuf_addstr(&truname, remote);
strbuf_setlen(&truname, truname.len - len);
- if (resolve_ref(truname.buf, buf_sha, 0, 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);
if (!strcmp(remote, "FETCH_HEAD") &&
!access(git_path("FETCH_HEAD"), R_OK)) {
+ const char *filename;
FILE *fp;
struct strbuf line = STRBUF_INIT;
char *ptr;
- fp = fopen(git_path("FETCH_HEAD"), "r");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "r");
if (!fp)
- die_errno("could not open '%s' for reading",
- git_path("FETCH_HEAD"));
+ die_errno(_("could not open '%s' for reading"),
+ filename);
strbuf_getline(&line, fp, '\n');
fclose(fp);
ptr = strstr(line.buf, "\tnot-for-merge\t");
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);
}
+static void parse_branch_merge_options(char *bmo)
+{
+ const char **argv;
+ int argc;
+
+ if (!bmo)
+ return;
+ argc = split_cmdline(bmo, &argv);
+ if (argc < 0)
+ die(_("Bad branch.%s.mergeoptions string: %s"), branch,
+ split_cmdline_strerror(argc));
+ argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+ memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+ argc++;
+ argv[0] = "branch.*.mergeoptions";
+ parse_options(argc, argv, NULL, builtin_merge_options,
+ builtin_merge_usage, 0);
+ free(argv);
+}
+
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")) {
- const char **argv;
- int argc;
- char *buf;
-
- buf = xstrdup(v);
- argc = split_cmdline(buf, &argv);
- if (argc < 0)
- die("Bad branch.%s.mergeoptions string", branch);
- argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
- memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
- argc++;
- parse_options(argc, argv, NULL, builtin_merge_options,
- builtin_merge_usage, 0);
- free(buf);
+ free(branch_mergeoptions);
+ branch_mergeoptions = xstrdup(v);
+ return 0;
}
if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
return git_config_string(&pull_twohead, k, v);
else if (!strcmp(k, "pull.octopus"))
return git_config_string(&pull_octopus, k, v);
- else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
- option_log = git_config_bool(k, v);
+ else if (!strcmp(k, "merge.renormalize"))
+ option_renormalize = git_config_bool(k, v);
+ else if (!strcmp(k, "merge.ff")) {
+ int boolval = git_config_maybe_bool(k, v);
+ if (0 <= boolval) {
+ allow_fast_forward = boolval;
+ } else if (v && !strcmp(v, "only")) {
+ allow_fast_forward = 1;
+ fast_forward_only = 1;
+ } /* do not barf on values from future versions of git */
+ return 0;
+ } else if (!strcmp(k, "merge.defaulttoupstream")) {
+ default_to_upstream = git_config_bool(k, v);
+ return 0;
+ }
+ status = fmt_merge_msg_config(k, v, cb);
+ if (status)
+ return status;
return git_diff_ui_config(k, v, cb);
}
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");
+ 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)
+static const char *merge_argument(struct commit *commit)
+{
+ if (commit)
+ return sha1_to_hex(commit->object.sha1);
+ else
+ return EMPTY_TREE_SHA1_HEX;
+}
+
+int try_merge_command(const char *strategy, size_t xopts_nr,
+ const char **xopts, struct commit_list *common,
+ const char *head_arg, struct commit_list *remotes)
{
const char **args;
int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
+
+ args = xmalloc((4 + xopts_nr + commit_list_count(common) +
+ commit_list_count(remotes)) * sizeof(char *));
+ strbuf_addf(&buf, "merge-%s", strategy);
+ args[i++] = buf.buf;
+ for (x = 0; x < xopts_nr; x++) {
+ char *s = xmalloc(strlen(xopts[x])+2+1);
+ strcpy(s, "--");
+ strcpy(s+2, xopts[x]);
+ args[i++] = s;
+ }
+ for (j = common; j; j = j->next)
+ args[i++] = xstrdup(merge_argument(j->item));
+ args[i++] = "--";
+ args[i++] = head_arg;
+ for (j = remotes; j; j = j->next)
+ args[i++] = xstrdup(merge_argument(j->item));
+ args[i] = NULL;
+ ret = run_command_v_opt(args, RUN_GIT_CMD);
+ strbuf_release(&buf);
+ i = 1;
+ for (x = 0; x < xopts_nr; x++)
+ free((void *)args[i++]);
+ for (j = common; j; j = j->next)
+ free((void *)args[i++]);
+ i += 2;
+ for (j = remotes; j; j = j->next)
+ free((void *)args[i++]);
+ free(args);
+ discard_cache();
+ if (read_cache() < 0)
+ die(_("failed to read the cache"));
+ resolve_undo_clear();
+
+ return ret;
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+ struct commit *head, const char *head_arg)
+{
int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
if (active_cache_changed &&
(write_cache(index_fd, active_cache, active_nr) ||
commit_locked_index(lock)))
- return error("Unable to write index.");
+ return error(_("Unable to write index."));
rollback_lock_file(lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
- int clean;
+ int clean, x;
struct commit *result;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct commit_list *reversed = NULL;
struct merge_options o;
+ struct commit_list *j;
if (remoteheads->next) {
- error("Not handling anything other than two heads merge.");
+ error(_("Not handling anything other than two heads merge."));
return 2;
}
if (!strcmp(strategy, "subtree"))
o.subtree_shift = "";
- for (x = 0; x < xopts_nr; x++) {
- if (!strcmp(xopts[x], "ours"))
- o.recursive_variant = MERGE_RECURSIVE_OURS;
- else if (!strcmp(xopts[x], "theirs"))
- o.recursive_variant = MERGE_RECURSIVE_THEIRS;
- else if (!strcmp(xopts[x], "subtree"))
- o.subtree_shift = "";
- else if (!prefixcmp(xopts[x], "subtree="))
- o.subtree_shift = xopts[x]+8;
- else
- die("Unknown option for merge-recursive: -X%s", xopts[x]);
- }
+ o.renormalize = option_renormalize;
+ o.show_rename_progress =
+ show_progress == -1 ? isatty(2) : show_progress;
+
+ for (x = 0; x < xopts_nr; x++)
+ if (parse_merge_opt(&o, xopts[x]))
+ 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);
index_fd = hold_locked_index(lock, 1);
- clean = merge_recursive(&o, lookup_commit(head),
+ clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
if (active_cache_changed &&
(write_cache(index_fd, active_cache, active_nr) ||
commit_locked_index(lock)))
- die ("unable to write %s", get_index_file());
+ die (_("unable to write %s"), get_index_file());
rollback_lock_file(lock);
return clean ? 0 : 1;
} else {
- args = xmalloc((4 + xopts_nr + commit_list_count(common) +
- commit_list_count(remoteheads)) * sizeof(char *));
- strbuf_addf(&buf, "merge-%s", strategy);
- args[i++] = buf.buf;
- for (x = 0; x < xopts_nr; x++) {
- char *s = xmalloc(strlen(xopts[x])+2+1);
- strcpy(s, "--");
- strcpy(s+2, xopts[x]);
- args[i++] = s;
- }
- 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 (x = 0; x < xopts_nr; x++)
- free((void *)args[i++]);
- 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);
- discard_cache();
- if (read_cache() < 0)
- die("failed to read the cache");
- resolve_undo_clear();
- return ret;
+ return try_merge_command(strategy, xopts_nr, xopts,
+ common, head_arg, remoteheads);
}
}
return ret;
}
-static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+int checkout_fast_forward(const unsigned char *head, const unsigned char *remote)
{
struct tree *trees[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
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;
opts.verbose_update = 1;
opts.merge = 1;
opts.fn = twoway_merge;
- opts.msgs = get_porcelain_error_msgs();
+ setup_unpack_trees_porcelain(&opts, "merge");
trees[nr_trees] = parse_tree_indirect(head);
if (!trees[nr_trees++])
return -1;
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(lock_file))
- die("unable to write new index file");
+ die(_("unable to write new index file"));
return 0;
}
}
-static int merge_trivial(void)
+static void write_merge_msg(struct strbuf *msg)
+{
+ const char *filename = git_path("MERGE_MSG");
+ int fd = open(filename, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ die_errno(_("Could not open '%s' for writing"),
+ filename);
+ if (write_in_full(fd, msg->buf, msg->len) != msg->len)
+ die_errno(_("Could not write to '%s'"), filename);
+ close(fd);
+}
+
+static void read_merge_msg(struct strbuf *msg)
+{
+ const char *filename = git_path("MERGE_MSG");
+ strbuf_reset(msg);
+ if (strbuf_read_file(msg, filename, 0) < 0)
+ die_errno(_("Could not read from '%s'"), filename);
+}
+
+static void write_merge_state(void);
+static void abort_commit(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();
+ exit(1);
+}
+
+static void prepare_to_commit(void)
+{
+ struct strbuf msg = STRBUF_INIT;
+ strbuf_addbuf(&msg, &merge_msg);
+ strbuf_addch(&msg, '\n');
+ write_merge_msg(&msg);
+ run_hook(get_index_file(), "prepare-commit-msg",
+ git_path("MERGE_MSG"), "merge", NULL, NULL);
+ if (option_edit) {
+ if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
+ abort_commit(NULL);
+ }
+ read_merge_msg(&msg);
+ stripspace(&msg, option_edit);
+ if (!msg.len)
+ abort_commit(_("Empty commit message."));
+ strbuf_release(&merge_msg);
+ strbuf_addbuf(&merge_msg, &msg);
+ strbuf_release(&msg);
+}
+
+static int merge_trivial(struct commit *head)
{
unsigned char result_tree[20], result_commit[20];
struct commit_list *parent = xmalloc(sizeof(*parent));
write_tree_trivial(result_tree);
- printf("Wonderful.\n");
- parent->item = lookup_commit(head);
+ printf(_("Wonderful.\n"));
+ parent->item = head;
parent->next = xmalloc(sizeof(*parent->next));
parent->next->item = remoteheads->item;
parent->next->next = NULL;
- commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
- finish(result_commit, "In-index merge");
+ prepare_to_commit();
+ if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL))
+ die(_("failed to write commit object"));
+ finish(head, result_commit, "In-index merge");
drop_save();
return 0;
}
-static int finish_automerge(struct commit_list *common,
+static int finish_automerge(struct commit *head,
+ struct commit_list *common,
unsigned char *result_tree,
const char *wt_strategy)
{
free_commit_list(common);
if (allow_fast_forward) {
parents = remoteheads;
- commit_list_insert(lookup_commit(head), &parents);
+ commit_list_insert(head, &parents);
parents = reduce_heads(parents);
} else {
struct commit_list **pptr = &parents;
- pptr = &commit_list_insert(lookup_commit(head),
+ pptr = &commit_list_insert(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, NULL);
- strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
- finish(result_commit, buf.buf);
+ prepare_to_commit();
+ free_commit_list(remoteheads);
+ if (commit_tree(&merge_msg, result_tree, parents, result_commit, NULL))
+ die(_("failed to write commit object"));
+ strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
+ finish(head, result_commit, buf.buf);
strbuf_release(&buf);
drop_save();
return 0;
}
-static int suggest_conflicts(void)
+static int suggest_conflicts(int renormalizing)
{
+ const char *filename;
FILE *fp;
int pos;
- fp = fopen(git_path("MERGE_MSG"), "a");
+ filename = git_path("MERGE_MSG");
+ fp = fopen(filename, "a");
if (!fp)
- die_errno("Could not open '%s' for writing",
- git_path("MERGE_MSG"));
+ die_errno(_("Could not open '%s' for writing"), filename);
fprintf(fp, "\nConflicts:\n");
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
}
fclose(fp);
rerere(allow_rerere_auto);
- printf("Automatic merge failed; "
- "fix conflicts and then commit the result.\n");
+ 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)
+static struct commit *is_old_style_invocation(int argc, const char **argv,
+ const unsigned char *head)
{
struct commit *second_token = NULL;
if (argc > 2) {
return NULL;
second_token = lookup_commit_reference_gently(second_sha1, 0);
if (!second_token)
- die("'%s' is not a commit", argv[1]);
+ die(_("'%s' is not a commit"), argv[1]);
if (hashcmp(second_token->object.sha1, head))
return NULL;
}
return cnt;
}
+/*
+ * Pretend as if the user told us to merge with the tracking
+ * branch we have for the upstream of the current branch
+ */
+static int setup_with_upstream(const char ***argv)
+{
+ struct branch *branch = branch_get(NULL);
+ int i;
+ const char **args;
+
+ if (!branch)
+ die(_("No current branch."));
+ if (!branch->remote)
+ die(_("No remote for the current branch."));
+ if (!branch->merge_nr)
+ die(_("No default upstream defined for the current branch."));
+
+ args = xcalloc(branch->merge_nr + 1, sizeof(char *));
+ for (i = 0; i < branch->merge_nr; i++) {
+ if (!branch->merge[i]->dst)
+ die(_("No remote tracking branch for %s from %s"),
+ branch->merge[i]->src, branch->remote_name);
+ args[i] = branch->merge[i]->dst;
+ }
+ args[i] = NULL;
+ *argv = args;
+ return i;
+}
+
+static void write_merge_state(void)
+{
+ const char *filename;
+ int fd;
+ struct commit_list *j;
+ struct strbuf buf = STRBUF_INIT;
+
+ 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)
+ die_errno(_("Could not open '%s' for writing"), filename);
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+ die_errno(_("Could not write to '%s'"), filename);
+ close(fd);
+ strbuf_addch(&merge_msg, '\n');
+ write_merge_msg(&merge_msg);
+
+ filename = git_path("MERGE_MODE");
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ die_errno(_("Could not open '%s' for writing"), filename);
+ strbuf_reset(&buf);
+ if (!allow_fast_forward)
+ strbuf_addf(&buf, "no-ff");
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+ die_errno(_("Could not write to '%s'"), filename);
+ close(fd);
+}
+
int cmd_merge(int argc, const char **argv, const char *prefix)
{
unsigned char result_tree[20];
+ unsigned char stash[20];
+ unsigned char head_sha1[20];
+ struct commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, head_invalid = 0, i;
+ int flag, i, ret = 0;
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;
+ void *branch_to_free;
- if (read_cache_unmerged()) {
- die_resolve_conflict("merge");
- }
- if (file_exists(git_path("MERGE_HEAD"))) {
- /*
- * There is no unmerged entry, don't advise 'git
- * add/rm <file>', just 'git commit'.
- */
- if (advice_resolve_conflict)
- die("You have not concluded your merge (MERGE_HEAD exists).\n"
- "Please, commit your changes before you can merge.");
- else
- die("You have not concluded your merge (MERGE_HEAD exists).");
- }
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage_with_options(builtin_merge_usage, builtin_merge_options);
- resolve_undo_clear();
/*
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = resolve_ref("HEAD", head, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
if (branch && !prefixcmp(branch, "refs/heads/"))
branch += 11;
- if (is_null_sha1(head))
- head_invalid = 1;
+ if (!branch || is_null_sha1(head_sha1))
+ head_commit = NULL;
+ else
+ head_commit = lookup_commit_or_die(head_sha1, "HEAD");
git_config(git_merge_config, NULL);
- /* for color.ui */
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
+ if (branch_mergeoptions)
+ 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;
+
+ if (abort_current_merge) {
+ int nargc = 2;
+ const char *nargv[] = {"reset", "--merge", NULL};
+
+ if (!file_exists(git_path("MERGE_HEAD")))
+ die(_("There is no merge to abort (MERGE_HEAD missing)."));
+
+ /* Invoke 'git reset --merge' */
+ ret = cmd_reset(nargc, nargv, prefix);
+ goto done;
+ }
+
+ if (read_cache_unmerged())
+ die_resolve_conflict("merge");
+
+ if (file_exists(git_path("MERGE_HEAD"))) {
+ /*
+ * There is no unmerged entry, don't advise 'git
+ * add/rm <file>', just 'git commit'.
+ */
+ if (advice_resolve_conflict)
+ die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
+ "Please, commit your changes before you can merge."));
+ else
+ die(_("You have not concluded your merge (MERGE_HEAD exists)."));
+ }
+ if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
+ if (advice_resolve_conflict)
+ die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+ "Please, commit your changes before you can merge."));
+ else
+ die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
+ }
+ resolve_undo_clear();
+
if (verbosity < 0)
show_diffstat = 0;
if (squash) {
if (!allow_fast_forward)
- die("You cannot combine --squash with --no-ff.");
+ die(_("You cannot combine --squash with --no-ff."));
option_commit = 0;
}
if (!allow_fast_forward && fast_forward_only)
- die("You cannot combine --no-ff with --ff-only.");
+ die(_("You cannot combine --no-ff with --ff-only."));
+ if (!abort_current_merge) {
+ 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)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
* additional safety measure to check for it.
*/
- if (!have_message && is_old_style_invocation(argc, argv)) {
+ if (!have_message && head_commit &&
+ is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
strbuf_addstr(&merge_msg, argv[0]);
head_arg = argv[1];
argv += 2;
argc -= 2;
- } else if (head_invalid) {
- struct object *remote_head;
+ } else if (!head_commit) {
+ 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.
* We do the same for "git pull".
*/
if (argc != 1)
- die("Can merge only exactly one commit into "
- "empty head");
+ die(_("Can merge only exactly one commit into "
+ "empty head"));
if (squash)
- die("Squash commit into empty head not supported yet");
+ die(_("Squash commit into empty head not supported yet"));
if (!allow_fast_forward)
- die("Non-fast-forward commit does not make sense into "
- "an empty head");
- remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+ die(_("Non-fast-forward commit does not make sense into "
+ "an empty head"));
+ remote_head = get_merge_parent(argv[0]);
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;
+ die(_("%s - not something we can merge"), argv[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 msg = STRBUF_INIT;
+ struct strbuf merge_names = STRBUF_INIT;
/* 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.
+ * All the rest are the commits being merged; prepare
+ * the standard merge summary message to be appended
+ * to the given message.
*/
- if (!have_message) {
- for (i = 0; i < argc; i++)
- merge_name(argv[i], &msg);
- fmt_merge_msg(option_log, &msg, &merge_msg);
+ for (i = 0; i < argc; i++)
+ merge_name(argv[i], &merge_names);
+
+ if (!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);
+ strbuf_setlen(&merge_msg, merge_msg.len - 1);
}
}
- if (head_invalid || !argc)
+ if (!head_commit || !argc)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
strbuf_reset(&buf);
for (i = 0; i < argc; i++) {
- struct object *o;
- struct commit *commit;
-
- o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
- if (!o)
- die("%s - not something we can merge", argv[i]);
- commit = lookup_commit(o->sha1);
- commit->util = (void *)argv[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;
-
- strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+ strbuf_addf(&buf, "GITHEAD_%s",
+ sha1_to_hex(commit->object.sha1));
setenv(buf.buf, argv[i], 1);
strbuf_reset(&buf);
+ if (merge_remote_util(commit) &&
+ merge_remote_util(commit)->obj &&
+ merge_remote_util(commit)->obj->type == OBJ_TAG) {
+ option_edit = 1;
+ allow_fast_forward = 0;
+ }
}
if (!use_strategies) {
}
if (!remoteheads->next)
- common = get_merge_bases(lookup_commit(head),
- remoteheads->item, 1);
+ common = get_merge_bases(head_commit, remoteheads->item, 1);
else {
struct commit_list *list = remoteheads;
- commit_list_insert(lookup_commit(head), &list);
+ commit_list_insert(head_commit, &list);
common = get_octopus_merge_bases(list);
free(list);
}
- update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
- DIE_ON_ERR);
+ update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
+ NULL, 0, DIE_ON_ERR);
if (!common)
; /* No common ancestors found. We need a real merge. */
* 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)) {
+ !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, DEFAULT_ABBREV));
+ strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
if (verbosity >= 0)
- printf("Updating %s..%s\n",
+ printf(_("Updating %s..%s\n"),
hex,
find_unique_abbrev(remoteheads->item->object.sha1,
DEFAULT_ABBREV));
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;
+ commit = remoteheads->item;
+ if (!commit) {
+ ret = 1;
+ goto done;
+ }
- if (checkout_fast_forward(head, remoteheads->item->object.sha1))
- return 1;
+ if (checkout_fast_forward(head_commit->object.sha1,
+ commit->object.sha1)) {
+ ret = 1;
+ goto done;
+ }
- finish(o->sha1, msg.buf);
+ finish(head_commit, commit->object.sha1, msg.buf);
drop_save();
- return 0;
+ goto done;
} else if (!remoteheads->next && common->next)
;
/*
if (allow_trivial && !fast_forward_only) {
/* See if it is really trivial. */
git_committer_info(IDENT_ERROR_ON_NO_NAME);
- printf("Trying really trivial in-index merge...\n");
+ 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");
+ head_commit->object.sha1,
+ remoteheads->item->object.sha1)) {
+ ret = merge_trivial(head_commit);
+ goto done;
+ }
+ printf(_("Nope.\n"));
}
} else {
/*
* merge_bases again, otherwise "git merge HEAD^
* HEAD^^" would be missed.
*/
- common_one = get_merge_bases(lookup_commit(head),
- j->item, 1);
+ common_one = get_merge_bases(head_commit, j->item, 1);
if (hashcmp(common_one->item->object.sha1,
j->item->object.sha1)) {
up_to_date = 0;
}
if (up_to_date) {
finish_up_to_date("Already up-to-date. Yeeah!");
- return 0;
+ goto done;
}
}
if (fast_forward_only)
- die("Not possible to fast-forward, aborting.");
+ die(_("Not possible to fast-forward, aborting."));
/* We are going to make a new commit. */
git_committer_info(IDENT_ERROR_ON_NO_NAME);
* 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);
- }
+ if (use_strategies_nr == 1 ||
+ /*
+ * Stash away the local changes so that we can try more than one.
+ */
+ save_state(stash))
+ hashcpy(stash, null_sha1);
for (i = 0; i < use_strategies_nr; i++) {
int ret;
if (i) {
- printf("Rewinding the tree to pristine...\n");
- restore_state();
+ printf(_("Rewinding the tree to pristine...\n"));
+ restore_state(head_commit->object.sha1, stash);
}
if (use_strategies_nr != 1)
- printf("Trying merge strategy %s...\n",
+ printf(_("Trying merge strategy %s...\n"),
use_strategies[i]->name);
/*
* Remember which strategy left the state in the working
wt_strategy = use_strategies[i]->name;
ret = try_merge_strategy(use_strategies[i]->name,
- common, head_arg);
+ common, head_commit, head_arg);
if (!option_commit && !ret) {
merge_was_ok = 1;
/*
* 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);
+ if (automerge_was_ok) {
+ ret = finish_automerge(head_commit, common, result_tree,
+ wt_strategy);
+ goto done;
+ }
/*
* Pick the result from the best strategy and have the user fix
* it up.
*/
if (!best_strategy) {
- restore_state();
+ restore_state(head_commit->object.sha1, stash);
if (use_strategies_nr > 1)
fprintf(stderr,
- "No merge strategy handled the merge.\n");
+ _("No merge strategy handled the merge.\n"));
else
- fprintf(stderr, "Merge with strategy %s failed.\n",
+ 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 {
- printf("Rewinding the tree to pristine...\n");
- restore_state();
- printf("Using the %s to prepare resolving by hand.\n",
+ printf(_("Rewinding the tree to pristine...\n"));
+ 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_arg);
+ try_merge_strategy(best_strategy, common, head_commit, 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_errno("Could not open '%s' for writing",
- git_path("MERGE_HEAD"));
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno("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_errno("Could not open '%s' for writing",
- git_path("MERGE_MSG"));
- if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
- merge_msg.len)
- die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
- close(fd);
- fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (fd < 0)
- die_errno("Could not open '%s' for writing",
- git_path("MERGE_MODE"));
- strbuf_reset(&buf);
- if (!allow_fast_forward)
- strbuf_addf(&buf, "no-ff");
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno("Could not write to '%s'", git_path("MERGE_MODE"));
- close(fd);
- }
-
- if (merge_was_ok) {
- fprintf(stderr, "Automatic merge went well; "
- "stopped before committing as requested\n");
- return 0;
- } else
- return suggest_conflicts();
+ finish(head_commit, NULL, NULL);
+ else
+ write_merge_state();
+
+ if (merge_was_ok)
+ fprintf(stderr, _("Automatic merge went well; "
+ "stopped before committing as requested\n"));
+ else
+ ret = suggest_conflicts(option_renormalize);
+
+done:
+ free(branch_to_free);
+ return ret;
}