Code

Merge branch 'nd/maint-ignore-exclude' into nd/ignore-might-be-precious
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Nov 2011 18:41:43 +0000 (10:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Nov 2011 18:41:43 +0000 (10:41 -0800)
* nd/maint-ignore-exclude:
  checkout,merge: loosen overwriting untracked file check based on info/exclude

1  2 
builtin/checkout.c
builtin/merge.c

diff --combined builtin/checkout.c
index 2a8077242500d54ac50d5829a86b14803fc69126,2b90e6b5e21dc64132e68c66e4f7430c49724441..51840b9784f0daec21087ec101304eaa6cbf73cd
@@@ -19,7 -19,6 +19,7 @@@
  #include "ll-merge.h"
  #include "resolve-undo.h"
  #include "submodule.h"
 +#include "argv-array.h"
  
  static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@@ -31,7 -30,6 +31,7 @@@ struct checkout_opts 
        int quiet;
        int merge;
        int force;
 +      int force_detach;
        int writeout_stage;
        int writeout_error;
  
@@@ -72,7 -70,7 +72,7 @@@ static int update_some(const unsigned c
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, base, baselen);
        memcpy(ce->name + baselen, pathname, len - baselen);
 -      ce->ce_flags = create_ce_flags(len, 0);
 +      ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE;
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
  
  static int read_tree_some(struct tree *tree, const char **pathspec)
  {
 -      read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
 +      struct pathspec ps;
 +      init_pathspec(&ps, pathspec);
 +      read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
 +      free_pathspec(&ps);
  
        /* update the index with the given tree's info
         * for all args, expanding wildcards, and exit
@@@ -108,10 -103,9 +108,10 @@@ static int check_stage(int stage, struc
                        return 0;
                pos++;
        }
 -      return error("path '%s' does not have %s version",
 -                   ce->name,
 -                   (stage == 2) ? "our" : "their");
 +      if (stage == 2)
 +              return error(_("path '%s' does not have our version"), ce->name);
 +      else
 +              return error(_("path '%s' does not have their version"), ce->name);
  }
  
  static int check_all_stages(struct cache_entry *ce, int pos)
            ce_stage(active_cache[pos+1]) != 2 ||
            strcmp(active_cache[pos+2]->name, ce->name) ||
            ce_stage(active_cache[pos+2]) != 3)
 -              return error("path '%s' does not have all three versions",
 +              return error(_("path '%s' does not have all three versions"),
                             ce->name);
        return 0;
  }
@@@ -136,10 -130,9 +136,10 @@@ static int checkout_stage(int stage, st
                        return checkout_entry(active_cache[pos], state, NULL);
                pos++;
        }
 -      return error("path '%s' does not have %s version",
 -                   ce->name,
 -                   (stage == 2) ? "our" : "their");
 +      if (stage == 2)
 +              return error(_("path '%s' does not have our version"), ce->name);
 +      else
 +              return error(_("path '%s' does not have their version"), ce->name);
  }
  
  static int checkout_merged(int pos, struct checkout *state)
            ce_stage(active_cache[pos+1]) != 2 ||
            strcmp(active_cache[pos+2]->name, path) ||
            ce_stage(active_cache[pos+2]) != 3)
 -              return error("path '%s' does not have all 3 versions", path);
 +              return error(_("path '%s' does not have all 3 versions"), path);
  
        read_mmblob(&ancestor, active_cache[pos]->sha1);
        read_mmblob(&ours, active_cache[pos+1]->sha1);
         * merge.renormalize set, too
         */
        status = ll_merge(&result_buf, path, &ancestor, "base",
 -                        &ours, "ours", &theirs, "theirs", 0);
 +                        &ours, "ours", &theirs, "theirs", NULL);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
        if (status < 0 || !result_buf.ptr) {
                free(result_buf.ptr);
 -              return error("path '%s': cannot merge", path);
 +              return error(_("path '%s': cannot merge"), path);
        }
  
        /*
         */
        if (write_sha1_file(result_buf.ptr, result_buf.size,
                            blob_type, sha1))
 -              die("Unable to add merge result for '%s'", path);
 +              die(_("Unable to add merge result for '%s'"), path);
        ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
                              sha1,
                              path, 2, 0);
        if (!ce)
 -              die("make_cache_entry failed for path '%s'", path);
 +              die(_("make_cache_entry failed for path '%s'"), path);
        status = checkout_entry(ce, state, NULL);
        return status;
  }
  
  static int checkout_paths(struct tree *source_tree, const char **pathspec,
 -                        struct checkout_opts *opts)
 +                        const char *prefix, struct checkout_opts *opts)
  {
        int pos;
        struct checkout state;
  
        newfd = hold_locked_index(lock_file, 1);
        if (read_cache_preload(pathspec) < 0)
 -              return error("corrupt index file");
 +              return error(_("corrupt index file"));
  
        if (source_tree)
                read_tree_some(source_tree, pathspec);
  
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 +              if (source_tree && !(ce->ce_flags & CE_UPDATE))
 +                      continue;
                match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
        }
  
 -      if (report_path_error(ps_matched, pathspec, 0))
 +      if (report_path_error(ps_matched, pathspec, prefix))
                return 1;
  
        /* "checkout -m path" to recreate conflicted state */
                        if (!ce_stage(ce))
                                continue;
                        if (opts->force) {
 -                              warning("path '%s' is unmerged", ce->name);
 +                              warning(_("path '%s' is unmerged"), ce->name);
                        } else if (stage) {
                                errs |= check_stage(stage, ce, pos);
                        } else if (opts->merge) {
                                errs |= check_all_stages(ce, pos);
                        } else {
                                errs = 1;
 -                              error("path '%s' is unmerged", ce->name);
 +                              error(_("path '%s' is unmerged"), ce->name);
                        }
                        pos = skip_same_name(ce, pos) - 1;
                }
        state.refresh_cache = 1;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 +              if (source_tree && !(ce->ce_flags & CE_UPDATE))
 +                      continue;
                if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
                        if (!ce_stage(ce)) {
                                errs |= checkout_entry(ce, &state, NULL);
  
        if (write_cache(newfd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
 -              die("unable to write new index file");
 +              die(_("unable to write new index file"));
  
        resolve_ref("HEAD", rev, 0, &flag);
        head = lookup_commit_reference_gently(rev, 1);
@@@ -303,16 -292,17 +303,16 @@@ static void show_local_changes(struct o
        rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        if (diff_setup_done(&rev.diffopt) < 0)
 -              die("diff_setup_done failed");
 +              die(_("diff_setup_done failed"));
        add_pending_object(&rev, head, NULL);
        run_diff_index(&rev, 0);
  }
  
 -static void describe_detached_head(char *msg, struct commit *commit)
 +static void describe_detached_head(const char *msg, struct commit *commit)
  {
        struct strbuf sb = STRBUF_INIT;
 -      struct pretty_print_context ctx = {0};
        parse_commit(commit);
 -      pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx);
 +      pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        fprintf(stderr, "%s %s... %s\n", msg,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
@@@ -376,7 -366,7 +376,7 @@@ static int merge_working_tree(struct ch
        int newfd = hold_locked_index(lock_file, 1);
  
        if (read_cache_preload(NULL) < 0)
 -              return error("corrupt index file");
 +              return error(_("corrupt index file"));
  
        resolve_undo_clear();
        if (opts->force) {
                refresh_cache(REFRESH_QUIET);
  
                if (unmerged_cache()) {
 -                      error("you need to resolve your current index first");
 +                      error(_("you need to resolve your current index first"));
                        return 1;
                }
  
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
-               topts.dir->exclude_per_dir = ".gitignore";
+               setup_standard_excludes(topts.dir);
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
 -                                         (unsigned char *)EMPTY_TREE_SHA1_BIN);
 +                                         EMPTY_TREE_SHA1_BIN);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                tree = parse_tree_indirect(new->commit->object.sha1);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
        if (write_cache(newfd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
 -              die("unable to write new index file");
 +              die(_("unable to write new index file"));
  
        if (!opts->force && !opts->quiet)
                show_local_changes(&new->commit->object, &opts->diff_options);
@@@ -529,7 -519,7 +529,7 @@@ static void update_refs_for_switch(stru
                                temp = log_all_ref_updates;
                                log_all_ref_updates = 1;
                                if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
 -                                      fprintf(stderr, "Can not do reflog for '%s'\n",
 +                                      fprintf(stderr, _("Can not do reflog for '%s'\n"),
                                            opts->new_orphan_branch);
                                        log_all_ref_updates = temp;
                                        return;
        strbuf_addf(&msg, "checkout: moving from %s to %s",
                    old_desc ? old_desc : "(invalid)", new->name);
  
 -      if (new->path) {
 +      if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
 +              /* Nothing to do. */
 +      } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
 +              update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
 +                         REF_NODEREF, DIE_ON_ERR);
 +              if (!opts->quiet) {
 +                      if (old->path && advice_detached_head)
 +                              detach_advice(old->path, new->name);
 +                      describe_detached_head(_("HEAD is now at"), new->commit);
 +              }
 +      } else if (new->path) { /* Switch branches. */
                create_symref("HEAD", new->path, msg.buf);
                if (!opts->quiet) {
 -                      if (old->path && !strcmp(new->path, old->path))
 -                              fprintf(stderr, "Already on '%s'\n",
 -                                      new->name);
 -                      else if (opts->new_branch)
 -                              fprintf(stderr, "Switched to%s branch '%s'\n",
 -                                      opts->branch_exists ? " and reset" : " a new",
 +                      if (old->path && !strcmp(new->path, old->path)) {
 +                              fprintf(stderr, _("Already on '%s'\n"),
                                        new->name);
 -                      else
 -                              fprintf(stderr, "Switched to branch '%s'\n",
 +                      } else if (opts->new_branch) {
 +                              if (opts->branch_exists)
 +                                      fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
 +                              else
 +                                      fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name);
 +                      } else {
 +                              fprintf(stderr, _("Switched to branch '%s'\n"),
                                        new->name);
 +                      }
                }
                if (old->path && old->name) {
                        char log_file[PATH_MAX], ref_file[PATH_MAX];
                        if (!file_exists(ref_file) && file_exists(log_file))
                                remove_path(log_file);
                }
 -      } else if (strcmp(new->name, "HEAD")) {
 -              update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
 -                         REF_NODEREF, DIE_ON_ERR);
 -              if (!opts->quiet) {
 -                      if (old->path && advice_detached_head)
 -                              detach_advice(old->path, new->name);
 -                      describe_detached_head("HEAD is now at", new->commit);
 -              }
        }
        remove_branch_state();
        strbuf_release(&msg);
 -      if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
 +      if (!opts->quiet &&
 +          (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
                report_tracking(new);
  }
  
 +static int add_pending_uninteresting_ref(const char *refname,
 +                                       const unsigned char *sha1,
 +                                       int flags, void *cb_data)
 +{
 +      add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
 +      return 0;
 +}
 +
 +static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 +{
 +      parse_commit(commit);
 +      strbuf_addstr(sb, "  ");
 +      strbuf_addstr(sb,
 +              find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 +      strbuf_addch(sb, ' ');
 +      pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
 +      strbuf_addch(sb, '\n');
 +}
 +
 +#define ORPHAN_CUTOFF 4
 +static void suggest_reattach(struct commit *commit, struct rev_info *revs)
 +{
 +      struct commit *c, *last = NULL;
 +      struct strbuf sb = STRBUF_INIT;
 +      int lost = 0;
 +      while ((c = get_revision(revs)) != NULL) {
 +              if (lost < ORPHAN_CUTOFF)
 +                      describe_one_orphan(&sb, c);
 +              last = c;
 +              lost++;
 +      }
 +      if (ORPHAN_CUTOFF < lost) {
 +              int more = lost - ORPHAN_CUTOFF;
 +              if (more == 1)
 +                      describe_one_orphan(&sb, last);
 +              else
 +                      strbuf_addf(&sb, _(" ... and %d more.\n"), more);
 +      }
 +
 +      fprintf(stderr,
 +              Q_(
 +              /* The singular version */
 +              "Warning: you are leaving %d commit behind, "
 +              "not connected to\n"
 +              "any of your branches:\n\n"
 +              "%s\n",
 +              /* The plural version */
 +              "Warning: you are leaving %d commits behind, "
 +              "not connected to\n"
 +              "any of your branches:\n\n"
 +              "%s\n",
 +              /* Give ngettext() the count */
 +              lost),
 +              lost,
 +              sb.buf);
 +      strbuf_release(&sb);
 +
 +      if (advice_detached_head)
 +              fprintf(stderr,
 +                      _(
 +                      "If you want to keep them by creating a new branch, "
 +                      "this may be a good time\nto do so with:\n\n"
 +                      " git branch new_branch_name %s\n\n"),
 +                      sha1_to_hex(commit->object.sha1));
 +}
 +
 +/*
 + * We are about to leave commit that was at the tip of a detached
 + * HEAD.  If it is not reachable from any ref, this is the last chance
 + * for the user to do so without resorting to reflog.
 + */
 +static void orphaned_commit_warning(struct commit *commit)
 +{
 +      struct rev_info revs;
 +      struct object *object = &commit->object;
 +      struct object_array refs;
 +
 +      init_revisions(&revs, NULL);
 +      setup_revisions(0, NULL, &revs, NULL);
 +
 +      object->flags &= ~UNINTERESTING;
 +      add_pending_object(&revs, object, sha1_to_hex(object->sha1));
 +
 +      for_each_ref(add_pending_uninteresting_ref, &revs);
 +
 +      refs = revs.pending;
 +      revs.leak_pending = 1;
 +
 +      if (prepare_revision_walk(&revs))
 +              die(_("internal error in revision walk"));
 +      if (!(commit->object.flags & UNINTERESTING))
 +              suggest_reattach(commit, &revs);
 +      else
 +              describe_detached_head(_("Previous HEAD position was"), commit);
 +
 +      clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
 +      free(refs.objects);
 +}
 +
  static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
  {
        int ret = 0;
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
 -      old.path = resolve_ref("HEAD", rev, 0, &flag);
 +      old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
        old.commit = lookup_commit_reference_gently(rev, 1);
 -      if (!(flag & REF_ISSYMREF))
 +      if (!(flag & REF_ISSYMREF)) {
 +              free((char *)old.path);
                old.path = NULL;
 +      }
  
        if (old.path && !prefixcmp(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
                new->name = "HEAD";
                new->commit = old.commit;
                if (!new->commit)
 -                      die("You are on a branch yet to be born");
 +                      die(_("You are on a branch yet to be born"));
                parse_commit(new->commit);
        }
  
        if (ret)
                return ret;
  
 -      /*
 -       * If we were on a detached HEAD, but have now moved to
 -       * a new commit, we want to mention the old commit once more
 -       * to remind the user that it might be lost.
 -       */
        if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 -              describe_detached_head("Previous HEAD position was", old.commit);
 +              orphaned_commit_warning(old.commit);
  
        update_refs_for_switch(opts, &old, new);
  
        ret = post_checkout_hook(old.commit, new->commit, 1);
 +      free((char *)old.path);
        return ret || opts->writeout_error;
  }
  
@@@ -787,145 -675,32 +787,145 @@@ static const char *unique_tracking_name
        return NULL;
  }
  
 +static int parse_branchname_arg(int argc, const char **argv,
 +                              int dwim_new_local_branch_ok,
 +                              struct branch_info *new,
 +                              struct tree **source_tree,
 +                              unsigned char rev[20],
 +                              const char **new_branch)
 +{
 +      int argcount = 0;
 +      unsigned char branch_rev[20];
 +      const char *arg;
 +      int has_dash_dash;
 +
 +      /*
 +       * case 1: git checkout <ref> -- [<paths>]
 +       *
 +       *   <ref> must be a valid tree, everything after the '--' must be
 +       *   a path.
 +       *
 +       * case 2: git checkout -- [<paths>]
 +       *
 +       *   everything after the '--' must be paths.
 +       *
 +       * case 3: git checkout <something> [<paths>]
 +       *
 +       *   With no paths, if <something> is a commit, that is to
 +       *   switch to the branch or detach HEAD at it.  As a special case,
 +       *   if <something> is A...B (missing A or B means HEAD but you can
 +       *   omit at most one side), and if there is a unique merge base
 +       *   between A and B, A...B names that merge base.
 +       *
 +       *   With no paths, if <something> is _not_ a commit, no -t nor -b
 +       *   was given, and there is a tracking branch whose name is
 +       *   <something> in one and only one remote, then this is a short-hand
 +       *   to fork local <something> from that remote-tracking branch.
 +       *
 +       *   Otherwise <something> shall not be ambiguous.
 +       *   - If it's *only* a reference, treat it like case (1).
 +       *   - If it's only a path, treat it like case (2).
 +       *   - else: fail.
 +       *
 +       */
 +      if (!argc)
 +              return 0;
 +
 +      if (!strcmp(argv[0], "--"))     /* case (2) */
 +              return 1;
 +
 +      arg = argv[0];
 +      has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
 +
 +      if (!strcmp(arg, "-"))
 +              arg = "@{-1}";
 +
 +      if (get_sha1_mb(arg, rev)) {
 +              if (has_dash_dash)          /* case (1) */
 +                      die(_("invalid reference: %s"), arg);
 +              if (dwim_new_local_branch_ok &&
 +                  !check_filename(NULL, arg) &&
 +                  argc == 1) {
 +                      const char *remote = unique_tracking_name(arg);
 +                      if (!remote || get_sha1(remote, rev))
 +                              return argcount;
 +                      *new_branch = arg;
 +                      arg = remote;
 +                      /* DWIMmed to create local branch */
 +              } else {
 +                      return argcount;
 +              }
 +      }
 +
 +      /* we can't end up being in (2) anymore, eat the argument */
 +      argcount++;
 +      argv++;
 +      argc--;
 +
 +      new->name = arg;
 +      setup_branch_path(new);
 +
 +      if (!check_refname_format(new->path, 0) &&
 +          resolve_ref(new->path, branch_rev, 1, NULL))
 +              hashcpy(rev, branch_rev);
 +      else
 +              new->path = NULL; /* not an existing branch */
 +
 +      new->commit = lookup_commit_reference_gently(rev, 1);
 +      if (!new->commit) {
 +              /* not a commit */
 +              *source_tree = parse_tree_indirect(rev);
 +      } else {
 +              parse_commit(new->commit);
 +              *source_tree = new->commit->tree;
 +      }
 +
 +      if (!*source_tree)                   /* case (1): want a tree */
 +              die(_("reference is not a tree: %s"), arg);
 +      if (!has_dash_dash) {/* case (3 -> 1) */
 +              /*
 +               * Do not complain the most common case
 +               *      git checkout branch
 +               * even if there happen to be a file called 'branch';
 +               * it would be extremely annoying.
 +               */
 +              if (argc)
 +                      verify_non_filename(NULL, arg);
 +      } else {
 +              argcount++;
 +              argv++;
 +              argc--;
 +      }
 +
 +      return argcount;
 +}
 +
  int cmd_checkout(int argc, const char **argv, const char *prefix)
  {
        struct checkout_opts opts;
        unsigned char rev[20];
 -      const char *arg;
        struct branch_info new;
        struct tree *source_tree = NULL;
        char *conflict_style = NULL;
        int patch_mode = 0;
        int dwim_new_local_branch = 1;
        struct option options[] = {
 -              OPT__QUIET(&opts.quiet),
 +              OPT__QUIET(&opts.quiet, "suppress progress reporting"),
                OPT_STRING('b', NULL, &opts.new_branch, "branch",
                           "create and checkout a new branch"),
                OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
                           "create/reset and checkout a branch"),
 -              OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
 -              OPT_SET_INT('t', "track",  &opts.track, "track",
 +              OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
 +              OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"),
 +              OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
 -              OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
 +              OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files",
                            2),
 -              OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
 +              OPT_SET_INT('3', "theirs", &opts.writeout_stage, "checkout their version for unmerged files",
                            3),
 -              OPT_BOOLEAN('f', "force", &opts.force, "force"),
 -              OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
 +              OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
 +              OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
                OPT_STRING(0, "conflict", &conflict_style, "style",
                           "conflict style (merge or diff3)"),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
                OPT_END(),
        };
 -      int has_dash_dash;
  
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
  
        /* we can assume from now on new_branch = !new_branch_force */
        if (opts.new_branch && opts.new_branch_force)
 -              die("-B cannot be used with -b");
 +              die(_("-B cannot be used with -b"));
  
        /* copy -B over to -b, so that we can just check the latter */
        if (opts.new_branch_force)
                opts.new_branch = opts.new_branch_force;
  
        if (patch_mode && (opts.track > 0 || opts.new_branch
 -                         || opts.new_branch_log || opts.merge || opts.force))
 -              die ("--patch is incompatible with all other options");
 +                         || opts.new_branch_log || opts.merge || opts.force
 +                         || opts.force_detach))
 +              die (_("--patch is incompatible with all other options"));
 +
 +      if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
 +              die(_("--detach cannot be used with -b/-B/--orphan"));
 +      if (opts.force_detach && 0 < opts.track)
 +              die(_("--detach cannot be used with -t"));
  
        /* --track without -b should DWIM */
        if (0 < opts.track && !opts.new_branch) {
                const char *argv0 = argv[0];
                if (!argc || !strcmp(argv0, "--"))
 -                      die ("--track needs a branch name");
 +                      die (_("--track needs a branch name"));
                if (!prefixcmp(argv0, "refs/"))
                        argv0 += 5;
                if (!prefixcmp(argv0, "remotes/"))
                        argv0 += 8;
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
 -                      die ("Missing branch name; try -b");
 +                      die (_("Missing branch name; try -b"));
                opts.new_branch = argv0 + 1;
        }
  
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
 -                      die("--orphan and -b|-B are mutually exclusive");
 +                      die(_("--orphan and -b|-B are mutually exclusive"));
                if (opts.track > 0)
 -                      die("--orphan cannot be used with -t");
 +                      die(_("--orphan cannot be used with -t"));
                opts.new_branch = opts.new_orphan_branch;
        }
  
        }
  
        if (opts.force && opts.merge)
 -              die("git checkout: -f and -m are incompatible");
 +              die(_("git checkout: -f and -m are incompatible"));
  
        /*
 -       * case 1: git checkout <ref> -- [<paths>]
 +       * Extract branch name from command line arguments, so
 +       * all that is left is pathspecs.
         *
 -       *   <ref> must be a valid tree, everything after the '--' must be
 -       *   a path.
 +       * Handle
         *
 -       * case 2: git checkout -- [<paths>]
 -       *
 -       *   everything after the '--' must be paths.
 -       *
 -       * case 3: git checkout <something> [<paths>]
 -       *
 -       *   With no paths, if <something> is a commit, that is to
 -       *   switch to the branch or detach HEAD at it.  As a special case,
 -       *   if <something> is A...B (missing A or B means HEAD but you can
 -       *   omit at most one side), and if there is a unique merge base
 -       *   between A and B, A...B names that merge base.
 -       *
 -       *   With no paths, if <something> is _not_ a commit, no -t nor -b
 -       *   was given, and there is a tracking branch whose name is
 -       *   <something> in one and only one remote, then this is a short-hand
 -       *   to fork local <something> from that remote tracking branch.
 -       *
 -       *   Otherwise <something> shall not be ambiguous.
 -       *   - If it's *only* a reference, treat it like case (1).
 -       *   - If it's only a path, treat it like case (2).
 -       *   - else: fail.
 +       *  1) git checkout <tree> -- [<paths>]
 +       *  2) git checkout -- [<paths>]
 +       *  3) git checkout <something> [<paths>]
         *
 +       * including "last branch" syntax and DWIM-ery for names of
 +       * remote branches, erroring out for invalid or ambiguous cases.
         */
        if (argc) {
 -              if (!strcmp(argv[0], "--")) {       /* case (2) */
 -                      argv++;
 -                      argc--;
 -                      goto no_reference;
 -              }
 -
 -              arg = argv[0];
 -              has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
 -
 -              if (!strcmp(arg, "-"))
 -                      arg = "@{-1}";
 -
 -              if (get_sha1_mb(arg, rev)) {
 -                      if (has_dash_dash)          /* case (1) */
 -                              die("invalid reference: %s", arg);
 -                      if (!patch_mode &&
 -                          dwim_new_local_branch &&
 -                          opts.track == BRANCH_TRACK_UNSPECIFIED &&
 -                          !opts.new_branch &&
 -                          !check_filename(NULL, arg) &&
 -                          argc == 1) {
 -                              const char *remote = unique_tracking_name(arg);
 -                              if (!remote || get_sha1(remote, rev))
 -                                      goto no_reference;
 -                              opts.new_branch = arg;
 -                              arg = remote;
 -                              /* DWIMmed to create local branch */
 -                      }
 -                      else
 -                              goto no_reference;
 -              }
 -
 -              /* we can't end up being in (2) anymore, eat the argument */
 -              argv++;
 -              argc--;
 -
 -              new.name = arg;
 -              if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
 -                      setup_branch_path(&new);
 -
 -                      if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
 -                          resolve_ref(new.path, rev, 1, NULL))
 -                              ;
 -                      else
 -                              new.path = NULL;
 -                      parse_commit(new.commit);
 -                      source_tree = new.commit->tree;
 -              } else
 -                      source_tree = parse_tree_indirect(rev);
 -
 -              if (!source_tree)                   /* case (1): want a tree */
 -                      die("reference is not a tree: %s", arg);
 -              if (!has_dash_dash) {/* case (3 -> 1) */
 -                      /*
 -                       * Do not complain the most common case
 -                       *      git checkout branch
 -                       * even if there happen to be a file called 'branch';
 -                       * it would be extremely annoying.
 -                       */
 -                      if (argc)
 -                              verify_non_filename(NULL, arg);
 -              }
 -              else {
 -                      argv++;
 -                      argc--;
 -              }
 +              int dwim_ok =
 +                      !patch_mode &&
 +                      dwim_new_local_branch &&
 +                      opts.track == BRANCH_TRACK_UNSPECIFIED &&
 +                      !opts.new_branch;
 +              int n = parse_branchname_arg(argc, argv, dwim_ok,
 +                              &new, &source_tree, rev, &opts.new_branch);
 +              argv += n;
 +              argc -= n;
        }
  
 -no_reference:
 -
        if (opts.track == BRANCH_TRACK_UNSPECIFIED)
                opts.track = git_branch_track;
  
                const char **pathspec = get_pathspec(prefix, argv);
  
                if (!pathspec)
 -                      die("invalid path specification");
 +                      die(_("invalid path specification"));
  
                if (patch_mode)
                        return interactive_checkout(new.name, pathspec, &opts);
                /* Checkout paths */
                if (opts.new_branch) {
                        if (argc == 1) {
 -                              die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
 +                              die(_("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?"), argv[0]);
                        } else {
 -                              die("git checkout: updating paths is incompatible with switching branches.");
 +                              die(_("git checkout: updating paths is incompatible with switching branches."));
                        }
                }
  
 +              if (opts.force_detach)
 +                      die(_("git checkout: --detach does not take a path argument"));
 +
                if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
 -                      die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
 +                      die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
  
 -              return checkout_paths(source_tree, pathspec, &opts);
 +              return checkout_paths(source_tree, pathspec, prefix, &opts);
        }
  
        if (patch_mode)
  
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
 -              if (strbuf_check_branch_ref(&buf, opts.new_branch))
 -                      die("git checkout: we do not like '%s' as a branch name.",
 -                          opts.new_branch);
 -              if (!get_sha1(buf.buf, rev)) {
 -                      opts.branch_exists = 1;
 -                      if (!opts.new_branch_force)
 -                              die("git checkout: branch %s already exists",
 -                                  opts.new_branch);
 -              }
 +
 +              opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
 +                                                           !!opts.new_branch_force, 0);
 +
                strbuf_release(&buf);
        }
  
        if (new.name && !new.commit) {
 -              die("Cannot switch branch to a non-commit.");
 +              die(_("Cannot switch branch to a non-commit."));
        }
        if (opts.writeout_stage)
 -              die("--ours/--theirs is incompatible with switching branches.");
 +              die(_("--ours/--theirs is incompatible with switching branches."));
  
        return switch_branches(&opts, &new);
  }
diff --combined builtin/merge.c
index 2870a6af6fbc868455f2961483cfbfdb065a1a14,29c43979bd2b5f24403433077ef43a980aef88a3..138737624807389ccb903a5cd03840d63b7cbf6f
@@@ -25,7 -25,6 +25,7 @@@
  #include "help.h"
  #include "merge-recursive.h"
  #include "resolve-undo.h"
 +#include "remote.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -38,30 -37,26 +38,30 @@@ struct strategy 
  };
  
  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, 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 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 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 },
@@@ -84,7 -79,7 +84,7 @@@ static int option_parse_message(const s
                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;
  }
  
@@@ -121,13 -116,13 +121,13 @@@ static struct strategy *get_strategy(co
                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:");
 +              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");
@@@ -182,15 -177,12 +182,15 @@@ static struct option builtin_merge_opti
        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_END()
  };
  
@@@ -218,7 -207,7 +218,7 @@@ static void drop_save(void
        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);
        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)
  {
        remote = bname.buf;
  
        memset(branch_head, 0, sizeof(branch_head));
 -      remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
 +      remote_head = want_commit(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/")) {
                        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;
                }
  
        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");
@@@ -520,34 -476,26 +520,34 @@@ cleanup
        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)
  {
        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: %s", branch,
 -                          split_cmdline_strerror(argc));
 -              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.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")) {
 +              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;
 +      }
        return git_diff_ui_config(k, v, cb);
  }
  
@@@ -621,19 -550,10 +621,19 @@@ static int read_tree_trivial(unsigned c
  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 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, struct commit_list *common,
 +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;
                args[i++] = s;
        }
        for (j = common; j; j = j->next)
 -              args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
 +              args[i++] = xstrdup(merge_argument(j->item));
        args[i++] = "--";
        args[i++] = head_arg;
        for (j = remotes; j; j = j->next)
 -              args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
 +              args[i++] = xstrdup(merge_argument(j->item));
        args[i] = NULL;
        ret = run_command_v_opt(args, RUN_GIT_CMD);
        strbuf_release(&buf);
        free(args);
        discard_cache();
        if (read_cache() < 0)
 -              die("failed to read the cache");
 +              die(_("failed to read the cache"));
        resolve_undo_clear();
  
        return ret;
  }
  
  static int try_merge_strategy(const char *strategy, struct commit_list *common,
 -                            const char *head_arg)
 +                            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")) {
                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;
                }
  
                        o.subtree_shift = "";
  
                o.renormalize = option_renormalize;
 +              o.show_rename_progress =
 +                      show_progress == -1 ? isatty(2) : show_progress;
  
 -              /*
 -               * NEEDSWORK: merge with table in builtin/merge-recursive
 -               */
 -              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 if (!strcmp(xopts[x], "renormalize"))
 -                              o.renormalize = 1;
 -                      else if (!strcmp(xopts[x], "no-renormalize"))
 -                              o.renormalize = 0;
 -                      else
 -                              die("Unknown option for merge-recursive: -X%s", xopts[x]);
 -              }
 +              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;
                        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 {
 -              return try_merge_command(strategy, common, head_arg, remoteheads);
 +              return try_merge_command(strategy, xopts_nr, xopts,
 +                                              common, head_arg, remoteheads);
        }
  }
  
@@@ -775,7 -708,7 +775,7 @@@ int checkout_fast_forward(const unsigne
        memset(&t, 0, sizeof(t));
        memset(&dir, 0, sizeof(dir));
        dir.flags |= DIR_SHOW_IGNORED;
-       dir.exclude_per_dir = ".gitignore";
+       setup_standard_excludes(&dir);
        opts.dir = &dir;
  
        opts.head_idx = 1;
                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;
  }
  
@@@ -849,78 -782,24 +849,78 @@@ static void add_strategies(const char *
  
  }
  
 -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;
 +      prepare_to_commit();
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
 -      finish(result_commit, "In-index merge");
 +      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');
 +      prepare_to_commit();
 +      free_commit_list(remoteheads);
        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_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(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;
        }
@@@ -1022,158 -898,63 +1022,158 @@@ static int evaluate_result(void
        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)
 +              strbuf_addf(&buf, "%s\n",
 +                      sha1_to_hex(j->item->object.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;
        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).");
 -      }
 +      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 = resolve_ref("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 (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' */
 +              return cmd_reset(nargc, nargv, prefix);
 +      }
 +
 +      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 && default_to_upstream)
 +                      argc = setup_with_upstream(&argv);
 +              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) {
 +      } else if (!head_commit) {
                struct object *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                 * 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 = want_commit(argv[0]);
                if (!remote_head)
 -                      die("%s - not something we can merge", argv[0]);
 +                      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);
 -              reset_hard(remote_head->sha1, 0);
                return 0;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
  
 -              if (have_message && option_log)
 -                      fmt_merge_msg_shortlog(&merge_names, &merge_msg);
 -              else if (!have_message)
 -                      fmt_merge_msg(option_log, &merge_names, &merge_msg);
 -
 -
 -              if (!(have_message && !option_log) && merge_msg.len)
 -                      strbuf_setlen(&merge_msg, merge_msg.len-1);
 +              if (!have_message || shortlog_len) {
 +                      fmt_merge_msg(&merge_names, &merge_msg, !have_message,
 +                                    shortlog_len);
 +                      if (merge_msg.len)
 +                              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);
  
                struct object *o;
                struct commit *commit;
  
 -              o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
 +              o = want_commit(argv[i]);
                if (!o)
 -                      die("%s - not something we can merge", argv[i]);
 +                      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;
        }
  
        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. */
                return 0;
        } 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;
                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);
 +              o = want_commit(sha1_to_hex(remoteheads->item->object.sha1));
                if (!o)
                        return 1;
  
 -              if (checkout_fast_forward(head, remoteheads->item->object.sha1))
 +              if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1))
                        return 1;
  
 -              finish(o->sha1, msg.buf);
 +              finish(head_commit, o->sha1, msg.buf);
                drop_save();
                return 0;
        } 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))
 +                              return merge_trivial(head_commit);
 +                      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 (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;
                        /*
         * auto resolved the merge cleanly.
         */
        if (automerge_was_ok)
 -              return finish_automerge(common, result_tree, wt_strategy);
 +              return finish_automerge(head_commit, common, result_tree,
 +                                      wt_strategy);
  
        /*
         * 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;
        } 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);
 -      }
 +              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");
 +              fprintf(stderr, _("Automatic merge went well; "
 +                      "stopped before committing as requested\n"));
                return 0;
        } else
                return suggest_conflicts(option_renormalize);