summaryrefslogtreecommitdiff
path: root/builtin-merge.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-02-22 08:42:18 -0800
committerJunio C Hamano <gitster@pobox.com>2010-02-22 14:29:41 -0800
commit81b50f3ce40bfdd66e5d967bf82be001039a9a98 (patch)
tree7e86bb81e83c9fad73dcbdaa0ef33038137b4274 /builtin-merge.c
parent241b9254e1c8ff071d8054f8b6fbe1883b389d69 (diff)
downloadgit-81b50f3ce40bfdd66e5d967bf82be001039a9a98.tar.gz
Move 'builtin-*' into a 'builtin/' subdirectory
This shrinks the top-level directory a bit, and makes it much more pleasant to use auto-completion on the thing. Instead of [torvalds@nehalem git]$ em buil<tab> Display all 180 possibilities? (y or n) [torvalds@nehalem git]$ em builtin-sh builtin-shortlog.c builtin-show-branch.c builtin-show-ref.c builtin-shortlog.o builtin-show-branch.o builtin-show-ref.o [torvalds@nehalem git]$ em builtin-shor<tab> builtin-shortlog.c builtin-shortlog.o [torvalds@nehalem git]$ em builtin-shortlog.c you get [torvalds@nehalem git]$ em buil<tab> [type] builtin/ builtin.h [torvalds@nehalem git]$ em builtin [auto-completes to] [torvalds@nehalem git]$ em builtin/sh<tab> [type] shortlog.c shortlog.o show-branch.c show-branch.o show-ref.c show-ref.o [torvalds@nehalem git]$ em builtin/sho [auto-completes to] [torvalds@nehalem git]$ em builtin/shor<tab> [type] shortlog.c shortlog.o [torvalds@nehalem git]$ em builtin/shortlog.c which doesn't seem all that different, but not having that annoying break in "Display all 180 possibilities?" is quite a relief. NOTE! If you do this in a clean tree (no object files etc), or using an editor that has auto-completion rules that ignores '*.o' files, you won't see that annoying 'Display all 180 possibilities?' message - it will just show the choices instead. I think bash has some cut-off around 100 choices or something. So the reason I see this is that I'm using an odd editory, and thus don't have the rules to cut down on auto-completion. But you can simulate that by using 'ls' instead, or something similar. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'builtin-merge.c')
-rw-r--r--builtin-merge.c1292
1 files changed, 0 insertions, 1292 deletions
diff --git a/builtin-merge.c b/builtin-merge.c
deleted file mode 100644
index 3aaec7bed7..0000000000
--- a/builtin-merge.c
+++ /dev/null
@@ -1,1292 +0,0 @@
-/*
- * Builtin "git merge"
- *
- * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
- *
- * Based on git-merge.sh by Junio C Hamano.
- */
-
-#include "cache.h"
-#include "parse-options.h"
-#include "builtin.h"
-#include "run-command.h"
-#include "diff.h"
-#include "refs.h"
-#include "commit.h"
-#include "diffcore.h"
-#include "revision.h"
-#include "unpack-trees.h"
-#include "cache-tree.h"
-#include "dir.h"
-#include "utf8.h"
-#include "log-tree.h"
-#include "color.h"
-#include "rerere.h"
-#include "help.h"
-#include "merge-recursive.h"
-#include "resolve-undo.h"
-
-#define DEFAULT_TWOHEAD (1<<0)
-#define DEFAULT_OCTOPUS (1<<1)
-#define NO_FAST_FORWARD (1<<2)
-#define NO_TRIVIAL (1<<3)
-
-struct strategy {
- const char *name;
- unsigned attr;
-};
-
-static const char * const builtin_merge_usage[] = {
- "git merge [options] <remote>...",
- "git merge [options] <msg> HEAD <remote>",
- NULL
-};
-
-static int show_diffstat = 1, option_log, squash;
-static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only;
-static int allow_trivial = 1, have_message;
-static struct strbuf merge_msg;
-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 int verbosity;
-static int allow_rerere_auto;
-
-static struct strategy all_strategy[] = {
- { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
- { "octopus", DEFAULT_OCTOPUS },
- { "resolve", 0 },
- { "ours", NO_FAST_FORWARD | NO_TRIVIAL },
- { "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
-};
-
-static const char *pull_twohead, *pull_octopus;
-
-static int option_parse_message(const struct option *opt,
- const char *arg, int unset)
-{
- struct strbuf *buf = opt->value;
-
- if (unset)
- strbuf_setlen(buf, 0);
- else if (arg) {
- strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg);
- have_message = 1;
- } else
- return error("switch `m' requires a value");
- return 0;
-}
-
-static struct strategy *get_strategy(const char *name)
-{
- int i;
- struct strategy *ret;
- static struct cmdnames main_cmds, other_cmds;
- static int loaded;
-
- if (!name)
- return NULL;
-
- for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
- if (!strcmp(name, all_strategy[i].name))
- return &all_strategy[i];
-
- if (!loaded) {
- struct cmdnames not_strategies;
- loaded = 1;
-
- memset(&not_strategies, 0, sizeof(struct cmdnames));
- load_command_list("git-merge-", &main_cmds, &other_cmds);
- for (i = 0; i < main_cmds.cnt; i++) {
- int j, found = 0;
- struct cmdname *ent = main_cmds.names[i];
- for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
- if (!strncmp(ent->name, all_strategy[j].name, ent->len)
- && !all_strategy[j].name[ent->len])
- found = 1;
- if (!found)
- add_cmdname(&not_strategies, ent->name, ent->len);
- }
- exclude_cmds(&main_cmds, &not_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:");
- 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:");
- for (i = 0; i < other_cmds.cnt; i++)
- fprintf(stderr, " %s", other_cmds.names[i]->name);
- fprintf(stderr, ".\n");
- }
- exit(1);
- }
-
- ret = xcalloc(1, sizeof(struct strategy));
- ret->name = xstrdup(name);
- return ret;
-}
-
-static void append_strategy(struct strategy *s)
-{
- ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
- use_strategies[use_strategies_nr++] = s;
-}
-
-static int option_parse_strategy(const struct option *opt,
- const char *name, int unset)
-{
- if (unset)
- return 0;
-
- append_strategy(get_strategy(name));
- return 0;
-}
-
-static int option_parse_x(const struct option *opt,
- const char *arg, int unset)
-{
- if (unset)
- return 0;
-
- ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
- xopts[xopts_nr++] = xstrdup(arg);
- return 0;
-}
-
-static int option_parse_n(const struct option *opt,
- const char *arg, int unset)
-{
- show_diffstat = unset;
- return 0;
-}
-
-static struct option builtin_merge_options[] = {
- { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
- "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)"),
- OPT_BOOLEAN(0, "log", &option_log,
- "add list of one-line log to merge commit message"),
- 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(0, "ff", &allow_fast_forward,
- "allow fast-forward (default)"),
- OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
- "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",
- "message to be used for the merge commit (if any)",
- option_parse_message),
- OPT__VERBOSITY(&verbosity),
- OPT_END()
-};
-
-/* Cleans up metadata that is uninteresting after a succeeded merge. */
-static void drop_save(void)
-{
- unlink(git_path("MERGE_HEAD"));
- unlink(git_path("MERGE_MSG"));
- unlink(git_path("MERGE_MODE"));
-}
-
-static void save_state(void)
-{
- int len;
- struct child_process cp;
- struct strbuf buffer = STRBUF_INIT;
- const char *argv[] = {"stash", "create", NULL};
-
- memset(&cp, 0, sizeof(cp));
- cp.argv = argv;
- cp.out = -1;
- cp.git_cmd = 1;
-
- if (start_command(&cp))
- 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;
- strbuf_setlen(&buffer, buffer.len-1);
- if (get_sha1(buffer.buf, stash))
- die("not a valid object: %s", buffer.buf);
-}
-
-static void reset_hard(unsigned const char *sha1, int verbose)
-{
- int i = 0;
- const char *args[6];
-
- args[i++] = "read-tree";
- if (verbose)
- args[i++] = "-v";
- args[i++] = "--reset";
- args[i++] = "-u";
- args[i++] = sha1_to_hex(sha1);
- args[i] = NULL;
-
- if (run_command_v_opt(args, RUN_GIT_CMD))
- die("read-tree failed");
-}
-
-static void restore_state(void)
-{
- struct strbuf sb = STRBUF_INIT;
- const char *args[] = { "stash", "apply", NULL, NULL };
-
- if (is_null_sha1(stash))
- return;
-
- reset_hard(head, 1);
-
- args[2] = sha1_to_hex(stash);
-
- /*
- * It is OK to ignore error here, for example when there was
- * nothing to restore.
- */
- run_command_v_opt(args, RUN_GIT_CMD);
-
- strbuf_release(&sb);
- refresh_cache(REFRESH_QUIET);
-}
-
-/* This is called when no merge was necessary. */
-static void finish_up_to_date(const char *msg)
-{
- if (verbosity >= 0)
- printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
- drop_save();
-}
-
-static void squash_message(void)
-{
- struct rev_info rev;
- struct commit *commit;
- struct strbuf out = STRBUF_INIT;
- struct commit_list *j;
- 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);
- if (fd < 0)
- die_errno("Could not write to '%s'", git_path("SQUASH_MSG"));
-
- 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);
-
- for (j = remoteheads; j; j = j->next)
- add_pending_object(&rev, &j->item->object, NULL);
-
- setup_revisions(0, NULL, &rev, NULL);
- if (prepare_revision_walk(&rev))
- die("revision walk setup failed");
-
- ctx.abbrev = rev.abbrev;
- ctx.date_mode = rev.date_mode;
-
- 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);
- }
- if (write(fd, out.buf, out.len) < 0)
- die_errno("Writing SQUASH_MSG");
- if (close(fd))
- die_errno("Finishing SQUASH_MSG");
- strbuf_release(&out);
-}
-
-static void finish(const unsigned char *new_head, const char *msg)
-{
- struct strbuf reflog_message = STRBUF_INIT;
-
- if (!msg)
- strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
- else {
- if (verbosity >= 0)
- printf("%s\n", msg);
- strbuf_addf(&reflog_message, "%s: %s",
- getenv("GIT_REFLOG_ACTION"), msg);
- }
- if (squash) {
- squash_message();
- } else {
- if (verbosity >= 0 && !merge_msg.len)
- printf("No merge message -- not updating HEAD\n");
- else {
- const char *argv_gc_auto[] = { "gc", "--auto", NULL };
- update_ref(reflog_message.buf, "HEAD",
- new_head, head, 0,
- DIE_ON_ERR);
- /*
- * We ignore errors in 'gc --auto', since the
- * user should see them.
- */
- run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
- }
- }
- if (new_head && show_diffstat) {
- struct diff_options opts;
- diff_setup(&opts);
- 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");
- diff_tree_sha1(head, new_head, "", &opts);
- diffcore_std(&opts);
- diff_flush(&opts);
- }
-
- /* Run a post-merge hook */
- run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
-
- strbuf_release(&reflog_message);
-}
-
-/* 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 strbuf buf = STRBUF_INIT;
- struct strbuf bname = STRBUF_INIT;
- const char *ptr;
- char *found_ref;
- int len, early;
-
- strbuf_branchname(&bname, remote);
- remote = bname.buf;
-
- memset(branch_head, 0, sizeof(branch_head));
- remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
- if (!remote_head)
- 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/")) {
- strbuf_addf(msg, "%s\t\tbranch '%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",
- sha1_to_hex(branch_head), remote);
- goto cleanup;
- }
- }
-
- /* See if remote matches <name>^^^.. or <name>~<number> */
- for (len = 0, ptr = remote + strlen(remote);
- remote < ptr && ptr[-1] == '^';
- ptr--)
- len++;
- if (len)
- early = 1;
- else {
- early = 0;
- ptr = strrchr(remote, '~');
- if (ptr) {
- int seen_nonzero = 0;
-
- len++; /* count ~ */
- while (*++ptr && isdigit(*ptr)) {
- seen_nonzero |= (*ptr != '0');
- len++;
- }
- if (*ptr)
- len = 0; /* not ...~<number> */
- else if (seen_nonzero)
- early = 1;
- else if (len == 1)
- early = 1; /* "name~" is "name~1"! */
- }
- }
- if (len) {
- struct strbuf truname = STRBUF_INIT;
- strbuf_addstr(&truname, "refs/heads/");
- strbuf_addstr(&truname, remote);
- strbuf_setlen(&truname, truname.len - len);
- if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
- strbuf_addf(msg,
- "%s\t\tbranch '%s'%s of .\n",
- sha1_to_hex(remote_head->sha1),
- truname.buf + 11,
- (early ? " (early part)" : ""));
- strbuf_release(&truname);
- goto cleanup;
- }
- }
-
- if (!strcmp(remote, "FETCH_HEAD") &&
- !access(git_path("FETCH_HEAD"), R_OK)) {
- FILE *fp;
- struct strbuf line = STRBUF_INIT;
- char *ptr;
-
- fp = fopen(git_path("FETCH_HEAD"), "r");
- if (!fp)
- die_errno("could not open '%s' for reading",
- git_path("FETCH_HEAD"));
- strbuf_getline(&line, fp, '\n');
- fclose(fp);
- ptr = strstr(line.buf, "\tnot-for-merge\t");
- if (ptr)
- strbuf_remove(&line, ptr-line.buf+1, 13);
- strbuf_addbuf(msg, &line);
- strbuf_release(&line);
- goto cleanup;
- }
- strbuf_addf(msg, "%s\t\tcommit '%s'\n",
- sha1_to_hex(remote_head->sha1), remote);
-cleanup:
- strbuf_release(&buf);
- strbuf_release(&bname);
-}
-
-static int git_merge_config(const char *k, const char *v, void *cb)
-{
- 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);
- }
-
- if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
- show_diffstat = git_config_bool(k, v);
- else if (!strcmp(k, "pull.twohead"))
- 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);
- return git_diff_ui_config(k, v, cb);
-}
-
-static int read_tree_trivial(unsigned char *common, unsigned char *head,
- unsigned char *one)
-{
- int i, nr_trees = 0;
- struct tree *trees[MAX_UNPACK_TREES];
- struct tree_desc t[MAX_UNPACK_TREES];
- struct unpack_trees_options opts;
-
- memset(&opts, 0, sizeof(opts));
- opts.head_idx = 2;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
- opts.update = 1;
- opts.verbose_update = 1;
- opts.trivial_merges_only = 1;
- opts.merge = 1;
- trees[nr_trees] = parse_tree_indirect(common);
- if (!trees[nr_trees++])
- return -1;
- trees[nr_trees] = parse_tree_indirect(head);
- if (!trees[nr_trees++])
- return -1;
- trees[nr_trees] = parse_tree_indirect(one);
- if (!trees[nr_trees++])
- return -1;
- opts.fn = threeway_merge;
- cache_tree_free(&active_cache_tree);
- for (i = 0; i < nr_trees; i++) {
- parse_tree(trees[i]);
- init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
- }
- if (unpack_trees(nr_trees, t, &opts))
- return -1;
- return 0;
-}
-
-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");
-}
-
-static int try_merge_strategy(const char *strategy, struct commit_list *common,
- const char *head_arg)
-{
- const char **args;
- int i = 0, x = 0, ret;
- struct commit_list *j;
- struct strbuf buf = STRBUF_INIT;
- int index_fd;
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-
- index_fd = hold_locked_index(lock, 1);
- refresh_cache(REFRESH_QUIET);
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(lock)))
- return error("Unable to write index.");
- rollback_lock_file(lock);
-
- if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
- int clean;
- 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;
-
- if (remoteheads->next) {
- error("Not handling anything other than two heads merge.");
- return 2;
- }
-
- init_merge_options(&o);
- 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.branch1 = head_arg;
- o.branch2 = remoteheads->item->util;
-
- 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),
- 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());
- 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;
- }
-}
-
-static void count_diff_files(struct diff_queue_struct *q,
- struct diff_options *opt, void *data)
-{
- int *count = data;
-
- (*count) += q->nr;
-}
-
-static int count_unmerged_entries(void)
-{
- int i, ret = 0;
-
- for (i = 0; i < active_nr; i++)
- if (ce_stage(active_cache[i]))
- ret++;
-
- return ret;
-}
-
-static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
-{
- struct tree *trees[MAX_UNPACK_TREES];
- struct unpack_trees_options opts;
- struct tree_desc t[MAX_UNPACK_TREES];
- int i, fd, nr_trees = 0;
- struct dir_struct dir;
- struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-
- refresh_cache(REFRESH_QUIET);
-
- fd = hold_locked_index(lock_file, 1);
-
- 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;
-
- opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
- opts.update = 1;
- opts.verbose_update = 1;
- opts.merge = 1;
- opts.fn = twoway_merge;
- opts.msgs = get_porcelain_error_msgs();
-
- trees[nr_trees] = parse_tree_indirect(head);
- if (!trees[nr_trees++])
- return -1;
- trees[nr_trees] = parse_tree_indirect(remote);
- if (!trees[nr_trees++])
- return -1;
- for (i = 0; i < nr_trees; i++) {
- parse_tree(trees[i]);
- init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
- }
- if (unpack_trees(nr_trees, t, &opts))
- return -1;
- if (write_cache(fd, active_cache, active_nr) ||
- commit_locked_index(lock_file))
- die("unable to write new index file");
- return 0;
-}
-
-static void split_merge_strategies(const char *string, struct strategy **list,
- int *nr, int *alloc)
-{
- char *p, *q, *buf;
-
- if (!string)
- return;
-
- buf = xstrdup(string);
- q = buf;
- for (;;) {
- p = strchr(q, ' ');
- if (!p) {
- ALLOC_GROW(*list, *nr + 1, *alloc);
- (*list)[(*nr)++].name = xstrdup(q);
- free(buf);
- return;
- } else {
- *p = '\0';
- ALLOC_GROW(*list, *nr + 1, *alloc);
- (*list)[(*nr)++].name = xstrdup(q);
- q = ++p;
- }
- }
-}
-
-static void add_strategies(const char *string, unsigned attr)
-{
- struct strategy *list = NULL;
- int list_alloc = 0, list_nr = 0, i;
-
- memset(&list, 0, sizeof(list));
- split_merge_strategies(string, &list, &list_nr, &list_alloc);
- if (list) {
- for (i = 0; i < list_nr; i++)
- append_strategy(get_strategy(list[i].name));
- return;
- }
- for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
- if (all_strategy[i].attr & attr)
- append_strategy(&all_strategy[i]);
-
-}
-
-static int merge_trivial(void)
-{
- 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);
- 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");
- drop_save();
- return 0;
-}
-
-static int finish_automerge(struct commit_list *common,
- unsigned char *result_tree,
- const char *wt_strategy)
-{
- struct commit_list *parents = NULL, *j;
- struct strbuf buf = STRBUF_INIT;
- unsigned char result_commit[20];
-
- free_commit_list(common);
- if (allow_fast_forward) {
- parents = remoteheads;
- commit_list_insert(lookup_commit(head), &parents);
- parents = reduce_heads(parents);
- } else {
- struct commit_list **pptr = &parents;
-
- pptr = &commit_list_insert(lookup_commit(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);
- strbuf_release(&buf);
- drop_save();
- return 0;
-}
-
-static int suggest_conflicts(void)
-{
- FILE *fp;
- int pos;
-
- fp = fopen(git_path("MERGE_MSG"), "a");
- if (!fp)
- die_errno("Could not open '%s' for writing",
- git_path("MERGE_MSG"));
- fprintf(fp, "\nConflicts:\n");
- for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
-
- if (ce_stage(ce)) {
- fprintf(fp, "\t%s\n", ce->name);
- while (pos + 1 < active_nr &&
- !strcmp(ce->name,
- active_cache[pos + 1]->name))
- pos++;
- }
- }
- fclose(fp);
- rerere(allow_rerere_auto);
- 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)
-{
- struct commit *second_token = NULL;
- if (argc > 2) {
- unsigned char second_sha1[20];
-
- if (get_sha1(argv[1], second_sha1))
- return NULL;
- second_token = lookup_commit_reference_gently(second_sha1, 0);
- if (!second_token)
- die("'%s' is not a commit", argv[1]);
- if (hashcmp(second_token->object.sha1, head))
- return NULL;
- }
- return second_token;
-}
-
-static int evaluate_result(void)
-{
- int cnt = 0;
- struct rev_info rev;
-
- /* Check how many files differ. */
- init_revisions(&rev, "");
- setup_revisions(0, NULL, &rev, NULL);
- rev.diffopt.output_format |=
- DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = count_diff_files;
- rev.diffopt.format_callback_data = &cnt;
- run_diff_files(&rev, 0);
-
- /*
- * Check how many unmerged entries are
- * there.
- */
- cnt += count_unmerged_entries();
-
- return cnt;
-}
-
-int cmd_merge(int argc, const char **argv, const char *prefix)
-{
- unsigned char result_tree[20];
- struct strbuf buf = STRBUF_INIT;
- const char *head_arg;
- int flag, head_invalid = 0, i;
- 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;
-
- 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).");
- }
-
- 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);
- if (branch && !prefixcmp(branch, "refs/heads/"))
- branch += 11;
- if (is_null_sha1(head))
- head_invalid = 1;
-
- git_config(git_merge_config, NULL);
-
- /* for color.ui */
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
- argc = parse_options(argc, argv, prefix, builtin_merge_options,
- builtin_merge_usage, 0);
- if (verbosity < 0)
- show_diffstat = 0;
-
- if (squash) {
- if (!allow_fast_forward)
- 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.");
-
- if (!argc)
- usage_with_options(builtin_merge_usage,
- builtin_merge_options);
-
- /*
- * This could be traditional "merge <msg> HEAD <commit>..." and
- * the way we can tell it is to see if the second token is HEAD,
- * but some people might have misused the interface and used a
- * committish that is the same as HEAD there instead.
- * Traditional format never would have "-m" so it is an
- * additional safety measure to check for it.
- */
-
- if (!have_message && is_old_style_invocation(argc, argv)) {
- strbuf_addstr(&merge_msg, argv[0]);
- head_arg = argv[1];
- argv += 2;
- argc -= 2;
- } else if (head_invalid) {
- struct object *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");
- if (squash)
- 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);
- 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;
- } else {
- struct strbuf msg = 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.
- */
- if (!have_message) {
- for (i = 0; i < argc; i++)
- merge_name(argv[i], &msg);
- fmt_merge_msg(option_log, &msg, &merge_msg);
- if (merge_msg.len)
- strbuf_setlen(&merge_msg, merge_msg.len-1);
- }
- }
-
- if (head_invalid || !argc)
- usage_with_options(builtin_merge_usage,
- builtin_merge_options);
-
- strbuf_addstr(&buf, "merge");
- for (i = 0; i < argc; i++)
- strbuf_addf(&buf, " %s", argv[i]);
- setenv("GIT_REFLOG_ACTION", buf.buf, 0);
- 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];
- remotes = &commit_list_insert(commit, remotes)->next;
-
- strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
- setenv(buf.buf, argv[i], 1);
- strbuf_reset(&buf);
- }
-
- if (!use_strategies) {
- if (!remoteheads->next)
- add_strategies(pull_twohead, DEFAULT_TWOHEAD);
- else
- add_strategies(pull_octopus, DEFAULT_OCTOPUS);
- }
-
- for (i = 0; i < use_strategies_nr; i++) {
- if (use_strategies[i]->attr & NO_FAST_FORWARD)
- allow_fast_forward = 0;
- if (use_strategies[i]->attr & NO_TRIVIAL)
- allow_trivial = 0;
- }
-
- if (!remoteheads->next)
- common = get_merge_bases(lookup_commit(head),
- remoteheads->item, 1);
- else {
- struct commit_list *list = remoteheads;
- commit_list_insert(lookup_commit(head), &list);
- common = get_octopus_merge_bases(list);
- free(list);
- }
-
- update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
- DIE_ON_ERR);
-
- if (!common)
- ; /* No common ancestors found. We need a real merge. */
- else if (!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;
- } else if (allow_fast_forward && !remoteheads->next &&
- !common->next &&
- !hashcmp(common->item->object.sha1, head)) {
- /* Again the most common case of merging one remote. */
- struct strbuf msg = STRBUF_INIT;
- struct object *o;
- char hex[41];
-
- strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
-
- if (verbosity >= 0)
- printf("Updating %s..%s\n",
- hex,
- find_unique_abbrev(remoteheads->item->object.sha1,
- DEFAULT_ABBREV));
- strbuf_addstr(&msg, "Fast-forward");
- 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;
-
- if (checkout_fast_forward(head, remoteheads->item->object.sha1))
- return 1;
-
- finish(o->sha1, msg.buf);
- drop_save();
- return 0;
- } else if (!remoteheads->next && common->next)
- ;
- /*
- * We are not doing octopus and not fast-forward. Need
- * a real merge.
- */
- else if (!remoteheads->next && !common->next && option_commit) {
- /*
- * We are not doing octopus, not fast-forward, and have
- * only one common.
- */
- refresh_cache(REFRESH_QUIET);
- 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");
- if (!read_tree_trivial(common->item->object.sha1,
- head, remoteheads->item->object.sha1))
- return merge_trivial();
- printf("Nope.\n");
- }
- } else {
- /*
- * An octopus. If we can reach all the remote we are up
- * to date.
- */
- int up_to_date = 1;
- struct commit_list *j;
-
- for (j = remoteheads; j; j = j->next) {
- struct commit_list *common_one;
-
- /*
- * Here we *have* to calculate the individual
- * merge_bases again, otherwise "git merge HEAD^
- * HEAD^^" would be missed.
- */
- common_one = get_merge_bases(lookup_commit(head),
- j->item, 1);
- if (hashcmp(common_one->item->object.sha1,
- j->item->object.sha1)) {
- up_to_date = 0;
- break;
- }
- }
- if (up_to_date) {
- finish_up_to_date("Already up-to-date. Yeeah!");
- return 0;
- }
- }
-
- if (fast_forward_only)
- die("Not possible to fast-forward, aborting.");
-
- /* We are going to make a new commit. */
- git_committer_info(IDENT_ERROR_ON_NO_NAME);
-
- /*
- * At this point, we need a real merge. No matter what strategy
- * we use, it would operate on the index, possibly affecting the
- * working tree, and when resolved cleanly, have the desired
- * tree in the index -- this means that the index must be in
- * 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);
- }
-
- for (i = 0; i < use_strategies_nr; i++) {
- int ret;
- if (i) {
- printf("Rewinding the tree to pristine...\n");
- restore_state();
- }
- if (use_strategies_nr != 1)
- printf("Trying merge strategy %s...\n",
- use_strategies[i]->name);
- /*
- * Remember which strategy left the state in the working
- * tree.
- */
- wt_strategy = use_strategies[i]->name;
-
- ret = try_merge_strategy(use_strategies[i]->name,
- common, head_arg);
- if (!option_commit && !ret) {
- merge_was_ok = 1;
- /*
- * This is necessary here just to avoid writing
- * the tree, but later we will *not* exit with
- * status code 1 because merge_was_ok is set.
- */
- ret = 1;
- }
-
- if (ret) {
- /*
- * The backend exits with 1 when conflicts are
- * left to be resolved, with 2 when it does not
- * handle the given merge at all.
- */
- if (ret == 1) {
- int cnt = evaluate_result();
-
- if (best_cnt <= 0 || cnt <= best_cnt) {
- best_strategy = use_strategies[i]->name;
- best_cnt = cnt;
- }
- }
- if (merge_was_ok)
- break;
- else
- continue;
- }
-
- /* Automerge succeeded. */
- write_tree_trivial(result_tree);
- automerge_was_ok = 1;
- break;
- }
-
- /*
- * 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);
-
- /*
- * Pick the result from the best strategy and have the user fix
- * it up.
- */
- if (!best_strategy) {
- restore_state();
- if (use_strategies_nr > 1)
- fprintf(stderr,
- "No merge strategy handled the merge.\n");
- else
- fprintf(stderr, "Merge with strategy %s failed.\n",
- use_strategies[0]->name);
- return 2;
- } 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",
- best_strategy);
- try_merge_strategy(best_strategy, common, 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();
-}