OSDN Git Service

Merge branch 'jc/merge-drop-old-syntax'
authorJunio C Hamano <gitster@pobox.com>
Thu, 30 Mar 2017 21:07:13 +0000 (14:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 30 Mar 2017 21:07:13 +0000 (14:07 -0700)
Stop supporting "git merge <message> HEAD <commit>" syntax that has
been deprecated since October 2007, and issues a deprecation
warning message since v2.5.0.

* jc/merge-drop-old-syntax:
  merge: drop 'git merge <message> HEAD <commit>' syntax

1  2 
Documentation/git-merge.txt
builtin/merge.c

@@@ -10,12 -10,9 +10,11 @@@ SYNOPSI
  --------
  [verse]
  'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
 -      [-s <strategy>] [-X <strategy-option>] [-S[<key-id>]]
 +      [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
 +      [--[no-]allow-unrelated-histories]
        [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
- 'git merge' <msg> HEAD <commit>...
  'git merge' --abort
 +'git merge' --continue
  
  DESCRIPTION
  -----------
@@@ -46,11 -43,7 +45,7 @@@ a log message from the user describing 
      D---E---F---G---H master
  ------------
  
- The second syntax (<msg> `HEAD` <commit>...) is supported for
- historical reasons.  Do not use it from the command line or in
- new scripts.  It is the same as `git merge -m <msg> <commit>...`.
- The third syntax ("`git merge --abort`") can only be run after the
+ The second syntax ("`git merge --abort`") can only be run after the
  merge has resulted in conflicts. 'git merge --abort' will abort the
  merge process and try to reconstruct the pre-merge state. However,
  if there were uncommitted changes when the merge started (and
@@@ -62,8 -55,6 +57,8 @@@ reconstruct the original (pre-merge) ch
  discouraged: while possible, it may leave you in a state that is hard to
  back out of in the case of a conflict.
  
 +The fourth syntax ("`git merge --continue`") can only be run after the
 +merge has resulted in conflicts.
  
  OPTIONS
  -------
@@@ -71,9 -62,7 +66,9 @@@ include::merge-options.txt[
  
  -S[<keyid>]::
  --gpg-sign[=<keyid>]::
 -      GPG-sign the resulting merge commit.
 +      GPG-sign the resulting merge commit. The `keyid` argument is
 +      optional and defaults to the committer identity; if specified,
 +      it must be stuck to the option without a space.
  
  -m <msg>::
        Set the commit message to be used for the merge commit (in
@@@ -84,7 -73,7 +79,7 @@@ will be appended to the specified messa
  +
  The 'git fmt-merge-msg' command can be
  used to give a good default for automated 'git merge'
 -invocations.
 +invocations. The automated message can include the branch description.
  
  --[no-]rerere-autoupdate::
        Allow the rerere mechanism to update the index with the
@@@ -102,11 -91,6 +97,11 @@@ commit or stash your changes before run
  'git merge --abort' is equivalent to 'git reset --merge' when
  `MERGE_HEAD` is present.
  
 +--continue::
 +      After a 'git merge' stops due to conflicts you can conclude the
 +      merge by running 'git merge --continue' (see "HOW TO RESOLVE
 +      CONFLICTS" section below).
 +
  <commit>...::
        Commits, usually other branch heads, to merge into our branch.
        Specifying more than one commit will create a merge with
@@@ -247,7 -231,7 +242,7 @@@ Barbie's remark on your side.  The onl
  side wants to say it is hard and you'd prefer to go shopping, while the
  other side wants to claim it is easy.
  
 -An alternative style can be used by setting the "merge.conflictstyle"
 +An alternative style can be used by setting the "merge.conflictStyle"
  configuration variable to "diff3".  In "diff3" style, the above conflict
  may look like this:
  
@@@ -344,7 -328,7 +339,7 @@@ CONFIGURATIO
  -------------
  include::merge-config.txt[]
  
 -branch.<name>.mergeoptions::
 +branch.<name>.mergeOptions::
        Sets default options for merging into branch <name>. The syntax and
        supported options are the same as those of 'git merge', but option
        values containing whitespace characters are currently not supported.
diff --combined builtin/merge.c
@@@ -29,8 -29,6 +29,8 @@@
  #include "remote.h"
  #include "fmt-merge-msg.h"
  #include "gpg-interface.h"
 +#include "sequencer.h"
 +#include "string-list.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -43,10 -41,8 +43,9 @@@ struct strategy 
  };
  
  static const char * const builtin_merge_usage[] = {
 -      N_("git merge [options] [<commit>...]"),
 +      N_("git merge [<options>] [<commit>...]"),
-       N_("git merge [<options>] <msg> HEAD <commit>"),
        N_("git merge --abort"),
 +      N_("git merge --continue"),
        NULL
  };
  
@@@ -66,8 -62,6 +65,8 @@@ static int option_renormalize
  static int verbosity;
  static int allow_rerere_auto;
  static int abort_current_merge;
 +static int continue_current_merge;
 +static int allow_unrelated_histories;
  static int show_progress = -1;
  static int default_to_upstream = 1;
  static const char *sign_commit;
@@@ -214,7 -208,7 +213,7 @@@ static struct option builtin_merge_opti
                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY },
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_BOOL(0, "verify-signatures", &verify_signatures,
 -              N_("Verify that the named commit has a valid GPG signature")),
 +              N_("verify that the named commit has a valid GPG signature")),
        OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
                N_("merge strategy to use"), option_parse_strategy),
        OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
 +      OPT_BOOL(0, "continue", &continue_current_merge,
 +              N_("continue the current in-progress merge")),
 +      OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
 +               N_("allow merging unrelated histories")),
        OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
        { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
  /* 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"));
 +      unlink(git_path_merge_head());
 +      unlink(git_path_merge_msg());
 +      unlink(git_path_merge_mode());
  }
  
 -static int save_state(unsigned char *stash)
 +static int save_state(struct object_id *stash)
  {
        int len;
        struct child_process cp = CHILD_PROCESS_INIT;
        else if (!len)          /* no changes */
                return -1;
        strbuf_setlen(&buffer, buffer.len-1);
 -      if (get_sha1(buffer.buf, stash))
 +      if (get_oid(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
        return 0;
  }
@@@ -305,18 -295,18 +304,18 @@@ static void reset_hard(unsigned const c
                die(_("read-tree failed"));
  }
  
 -static void restore_state(const unsigned char *head,
 -                        const unsigned char *stash)
 +static void restore_state(const struct object_id *head,
 +                        const struct object_id *stash)
  {
        struct strbuf sb = STRBUF_INIT;
        const char *args[] = { "stash", "apply", NULL, NULL };
  
 -      if (is_null_sha1(stash))
 +      if (is_null_oid(stash))
                return;
  
 -      reset_hard(head, 1);
 +      reset_hard(head->hash, 1);
  
 -      args[2] = sha1_to_hex(stash);
 +      args[2] = oid_to_hex(stash);
  
        /*
         * It is OK to ignore error here, for example when there was
@@@ -341,9 -331,15 +340,9 @@@ static void squash_message(struct commi
        struct rev_info rev;
        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"));
 -      filename = git_path("SQUASH_MSG");
 -      fd = open(filename, O_WRONLY | O_CREAT, 0666);
 -      if (fd < 0)
 -              die_errno(_("Could not write to '%s'"), filename);
  
        init_revisions(&rev, NULL);
        rev.ignore_merges = 1;
        while ((commit = get_revision(&rev)) != NULL) {
                strbuf_addch(&out, '\n');
                strbuf_addf(&out, "commit %s\n",
 -                      sha1_to_hex(commit->object.sha1));
 +                      oid_to_hex(&commit->object.oid));
                pretty_print_commit(&ctx, commit, &out);
        }
 -      if (write_in_full(fd, out.buf, out.len) != out.len)
 -              die_errno(_("Writing SQUASH_MSG"));
 -      if (close(fd))
 -              die_errno(_("Finishing SQUASH_MSG"));
 +      write_file_buf(git_path_squash_msg(), out.buf, out.len);
        strbuf_release(&out);
  }
  
  static void finish(struct commit *head_commit,
                   struct commit_list *remoteheads,
 -                 const unsigned char *new_head, const char *msg)
 +                 const struct object_id *new_head, const char *msg)
  {
        struct strbuf reflog_message = STRBUF_INIT;
 -      const unsigned char *head = head_commit->object.sha1;
 +      const struct object_id *head = &head_commit->object.oid;
  
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
                else {
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
                        update_ref(reflog_message.buf, "HEAD",
 -                              new_head, head, 0,
 +                              new_head->hash, head->hash, 0,
                                UPDATE_REFS_DIE_ON_ERR);
                        /*
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
                         */
 +                      close_all_packs();
                        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
                }
        }
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
 -              diff_tree_sha1(head, new_head, "", &opts);
 +              diff_tree_sha1(head->hash, new_head->hash, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
        }
  static void merge_name(const char *remote, struct strbuf *msg)
  {
        struct commit *remote_head;
 -      unsigned char branch_head[20];
 +      struct object_id branch_head;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
        const char *ptr;
        char *found_ref;
        int len, early;
  
 -      strbuf_branchname(&bname, remote);
 +      strbuf_branchname(&bname, remote, 0);
        remote = bname.buf;
  
 -      memset(branch_head, 0, sizeof(branch_head));
 +      oidclr(&branch_head);
        remote_head = get_merge_parent(remote);
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
  
 -      if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
 +      if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) {
                if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
 -                                  sha1_to_hex(branch_head), remote);
 +                                  oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
                if (starts_with(found_ref, "refs/tags/")) {
                        strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
 -                                  sha1_to_hex(branch_head), remote);
 +                                  oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
                if (starts_with(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
 -                                  sha1_to_hex(branch_head), remote);
 +                                  oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
        }
                if (ref_exists(truname.buf)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
 -                                  sha1_to_hex(remote_head->object.sha1),
 +                                  oid_to_hex(&remote_head->object.oid),
                                    truname.buf + 11,
                                    (early ? " (early part)" : ""));
                        strbuf_release(&truname);
                desc = merge_remote_util(remote_head);
                if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
                        strbuf_addf(msg, "%s\t\t%s '%s'\n",
 -                                  sha1_to_hex(desc->obj->sha1),
 +                                  oid_to_hex(&desc->obj->oid),
                                    typename(desc->obj->type),
                                    remote);
                        goto cleanup;
        }
  
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
 -              sha1_to_hex(remote_head->object.sha1), remote);
 +              oid_to_hex(&remote_head->object.oid), remote);
  cleanup:
        strbuf_release(&buf);
        strbuf_release(&bname);
@@@ -590,8 -588,8 +589,8 @@@ static int git_merge_config(const char 
        return git_diff_ui_config(k, v, cb);
  }
  
 -static int read_tree_trivial(unsigned char *common, unsigned char *head,
 -                           unsigned char *one)
 +static int read_tree_trivial(struct object_id *common, struct object_id *head,
 +                           struct object_id *one)
  {
        int i, nr_trees = 0;
        struct tree *trees[MAX_UNPACK_TREES];
        opts.verbose_update = 1;
        opts.trivial_merges_only = 1;
        opts.merge = 1;
 -      trees[nr_trees] = parse_tree_indirect(common);
 +      trees[nr_trees] = parse_tree_indirect(common->hash);
        if (!trees[nr_trees++])
                return -1;
 -      trees[nr_trees] = parse_tree_indirect(head);
 +      trees[nr_trees] = parse_tree_indirect(head->hash);
        if (!trees[nr_trees++])
                return -1;
 -      trees[nr_trees] = parse_tree_indirect(one);
 +      trees[nr_trees] = parse_tree_indirect(one->hash);
        if (!trees[nr_trees++])
                return -1;
        opts.fn = threeway_merge;
        return 0;
  }
  
 -static void write_tree_trivial(unsigned char *sha1)
 +static void write_tree_trivial(struct object_id *oid)
  {
 -      if (write_cache_as_tree(sha1, 0, NULL))
 +      if (write_cache_as_tree(oid->hash, 0, NULL))
                die(_("git write-tree failed to write a tree"));
  }
  
  static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
-                             struct commit *head, const char *head_arg)
+                             struct commit *head)
  {
        static struct lock_file lock;
+       const char *head_arg = "HEAD";
  
 -      hold_locked_index(&lock, 1);
 +      hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
            write_locked_index(&the_index, &lock, COMMIT_LOCK))
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
  
 -              hold_locked_index(&lock, 1);
 +              hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
 +              if (clean < 0)
 +                      exit(128);
                if (active_cache_changed &&
                    write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
@@@ -710,17 -707,42 +710,17 @@@ static int count_unmerged_entries(void
        return ret;
  }
  
 -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));
 +      int i;
 +
 +      if (string) {
 +              struct string_list list = STRING_LIST_INIT_DUP;
 +              struct string_list_item *item;
 +              string_list_split(&list, string, ' ', -1);
 +              for_each_string_list_item(item, &list)
 +                      append_strategy(get_strategy(item->string));
 +              string_list_clear(&list, 0);
                return;
        }
        for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
  
  }
  
 -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");
 +      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);
@@@ -762,16 -796,16 +762,16 @@@ static void prepare_to_commit(struct co
        strbuf_addch(&msg, '\n');
        if (0 < option_edit)
                strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
 -      write_merge_msg(&msg);
 +      write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
 -                          git_path("MERGE_MSG"), "merge", NULL))
 +                          git_path_merge_msg(), "merge", NULL))
                abort_commit(remoteheads, NULL);
        if (0 < option_edit) {
 -              if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
 +              if (launch_editor(git_path_merge_msg(), NULL, NULL))
                        abort_commit(remoteheads, NULL);
        }
        read_merge_msg(&msg);
 -      stripspace(&msg, 0 < option_edit);
 +      strbuf_stripspace(&msg, 0 < option_edit);
        if (!msg.len)
                abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
  
  static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
  {
 -      unsigned char result_tree[20], result_commit[20];
 +      struct object_id result_tree, result_commit;
        struct commit_list *parents, **pptr = &parents;
 +      static struct lock_file lock;
  
 -      write_tree_trivial(result_tree);
 +      hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
 +      refresh_cache(REFRESH_QUIET);
 +      if (active_cache_changed &&
 +          write_locked_index(&the_index, &lock, COMMIT_LOCK))
 +              return error(_("Unable to write index."));
 +      rollback_lock_file(&lock);
 +
 +      write_tree_trivial(&result_tree);
        printf(_("Wonderful.\n"));
        pptr = commit_list_append(head, pptr);
        pptr = commit_list_append(remoteheads->item, pptr);
        prepare_to_commit(remoteheads);
 -      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
 -                      result_commit, NULL, sign_commit))
 +      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents,
 +                      result_commit.hash, NULL, sign_commit))
                die(_("failed to write commit object"));
 -      finish(head, remoteheads, result_commit, "In-index merge");
 +      finish(head, remoteheads, &result_commit, "In-index merge");
        drop_save();
        return 0;
  }
@@@ -809,12 -835,12 +809,12 @@@ static int finish_automerge(struct comm
                            int head_subsumed,
                            struct commit_list *common,
                            struct commit_list *remoteheads,
 -                          unsigned char *result_tree,
 +                          struct object_id *result_tree,
                            const char *wt_strategy)
  {
        struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
 -      unsigned char result_commit[20];
 +      struct object_id result_commit;
  
        free_commit_list(common);
        parents = remoteheads;
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
 -      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
 -                      result_commit, NULL, sign_commit))
 +      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents,
 +                      result_commit.hash, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
 -      finish(head, remoteheads, result_commit, buf.buf);
 +      finish(head, remoteheads, &result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
  }
  
 -static int suggest_conflicts(int renormalizing)
 +static int suggest_conflicts(void)
  {
        const char *filename;
        FILE *fp;
 -      int pos;
 +      struct strbuf msgbuf = STRBUF_INIT;
  
 -      filename = git_path("MERGE_MSG");
 +      filename = git_path_merge_msg();
        fp = fopen(filename, "a");
        if (!fp)
                die_errno(_("Could not open '%s' for writing"), filename);
 -      fprintf(fp, "\nConflicts:\n");
 -      for (pos = 0; pos < active_nr; pos++) {
 -              const 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++;
 -              }
 -      }
 +
 +      append_conflicts_hint(&msgbuf);
 +      fputs(msgbuf.buf, fp);
 +      strbuf_release(&msgbuf);
        fclose(fp);
        rerere(allow_rerere_auto);
        printf(_("Automatic merge failed; "
        return 1;
  }
  
- static struct commit *is_old_style_invocation(int argc, const char **argv,
-                                             const struct object_id *head)
- {
-       struct commit *second_token = NULL;
-       if (argc > 2) {
-               struct object_id second_oid;
-               if (get_oid(argv[1], &second_oid))
-                       return NULL;
-               second_token = lookup_commit_reference_gently(second_oid.hash, 0);
-               if (!second_token)
-                       die(_("'%s' is not a commit"), argv[1]);
-               if (oidcmp(&second_token->object.oid, head))
-                       return NULL;
-       }
-       return second_token;
- }
  static int evaluate_result(void)
  {
        int cnt = 0;
@@@ -906,12 -922,12 +888,12 @@@ static int setup_with_upstream(const ch
  
        if (!branch)
                die(_("No current branch."));
 -      if (!branch->remote)
 +      if (!branch->remote_name)
                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 *));
 +      args = xcalloc(st_add(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"),
  
  static void write_merge_state(struct commit_list *remoteheads)
  {
 -      const char *filename;
 -      int fd;
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
  
        for (j = remoteheads; j; j = j->next) {
 -              unsigned const char *sha1;
 +              struct object_id *oid;
                struct commit *c = j->item;
                if (c->util && merge_remote_util(c)->obj) {
 -                      sha1 = merge_remote_util(c)->obj->sha1;
 +                      oid = &merge_remote_util(c)->obj->oid;
                } else {
 -                      sha1 = c->object.sha1;
 +                      oid = &c->object.oid;
                }
 -              strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
 +              strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
 -      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);
 +      write_file_buf(git_path_merge_head(), buf.buf, buf.len);
        strbuf_addch(&merge_msg, '\n');
 -      write_merge_msg(&merge_msg);
 +      write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
  
 -      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 (fast_forward == FF_NO)
 -              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);
 +              strbuf_addstr(&buf, "no-ff");
 +      write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
  }
  
  static int default_edit_option(void)
        if (e) {
                int v = git_config_maybe_bool(name, e);
                if (v < 0)
 -                      die("Bad value '%s' in environment '%s'", e, name);
 +                      die(_("Bad value '%s' in environment '%s'"), e, name);
                return v;
        }
  
@@@ -978,7 -1008,7 +960,7 @@@ static struct commit_list *reduce_paren
                                          int *head_subsumed,
                                          struct commit_list *remoteheads)
  {
 -      struct commit_list *parents, *next, **remotes = &remoteheads;
 +      struct commit_list *parents, **remotes;
  
        /*
         * Is the current HEAD reachable from another commit being
        /* Find what parents to record by checking independent ones. */
        parents = reduce_heads(remoteheads);
  
 -      for (remoteheads = NULL, remotes = &remoteheads;
 -           parents;
 -           parents = next) {
 -              struct commit *commit = parents->item;
 -              next = parents->next;
 +      remoteheads = NULL;
 +      remotes = &remoteheads;
 +      while (parents) {
 +              struct commit *commit = pop_commit(&parents);
                if (commit == head_commit)
                        *head_subsumed = 0;
                else
                        remotes = &commit_list_insert(commit, remotes)->next;
 -              free(parents);
        }
        return remoteheads;
  }
@@@ -1027,7 -1059,7 +1009,7 @@@ static void handle_fetch_head(struct co
        if (!merge_names)
                merge_names = &fetch_head_file;
  
 -      filename = git_path("FETCH_HEAD");
 +      filename = git_path_fetch_head();
        fd = open(filename, O_RDONLY);
        if (fd < 0)
                die_errno(_("could not open '%s' for reading"), filename);
                die_errno(_("could not close '%s'"), filename);
  
        for (pos = 0; pos < merge_names->len; pos = npos) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                char *ptr;
                struct commit *commit;
  
                else
                        npos = merge_names->len;
  
 -              if (npos - pos < 40 + 2 ||
 -                  get_sha1_hex(merge_names->buf + pos, sha1))
 +              if (npos - pos < GIT_SHA1_HEXSZ + 2 ||
 +                  get_oid_hex(merge_names->buf + pos, &oid))
                        commit = NULL; /* bad */
 -              else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
 +              else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2))
                        continue; /* not-for-merge */
                else {
 -                      char saved = merge_names->buf[pos + 40];
 -                      merge_names->buf[pos + 40] = '\0';
 +                      char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ];
 +                      merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0';
                        commit = get_merge_parent(merge_names->buf + pos);
 -                      merge_names->buf[pos + 40] = saved;
 +                      merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved;
                }
                if (!commit) {
                        if (ptr)
                                *ptr = '\0';
 -                      die("not something we can merge in %s: %s",
 +                      die(_("not something we can merge in %s: %s"),
                            filename, merge_names->buf + pos);
                }
                remotes = &commit_list_insert(commit, remotes)->next;
@@@ -1096,7 -1128,7 +1078,7 @@@ static struct commit_list *collect_pare
                        struct commit *commit = get_merge_parent(argv[i]);
                        if (!commit)
                                help_unknown_ref(argv[i], "merge",
 -                                               "not something we can merge");
 +                                               _("not something we can merge"));
                        remotes = &commit_list_insert(commit, remotes)->next;
                }
                remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
  
  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 object_id result_tree, stash, head_oid;
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
-       const char *head_arg;
 -      int flag, i, ret = 0, head_subsumed;
 +      int i, ret = 0, head_subsumed;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list *remoteheads, *p;
        void *branch_to_free;
 +      int orig_argc = argc;
  
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_merge_usage, builtin_merge_options);
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
 -      branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
 +      branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
        if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
 -      if (!branch || is_null_sha1(head_sha1))
 +      if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
 -              head_commit = lookup_commit_or_die(head_sha1, "HEAD");
 +              head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
  
 +      init_diff_ui_defaults();
        git_config(git_merge_config, NULL);
  
        if (branch_mergeoptions)
                int nargc = 2;
                const char *nargv[] = {"reset", "--merge", NULL};
  
 -              if (!file_exists(git_path("MERGE_HEAD")))
 +              if (orig_argc != 2)
 +                      usage_msg_opt(_("--abort expects no arguments"),
 +                            builtin_merge_usage, builtin_merge_options);
 +
 +              if (!file_exists(git_path_merge_head()))
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
  
                /* Invoke 'git reset --merge' */
                goto done;
        }
  
 +      if (continue_current_merge) {
 +              int nargc = 1;
 +              const char *nargv[] = {"commit", NULL};
 +
 +              if (orig_argc != 2)
 +                      usage_msg_opt(_("--continue expects no arguments"),
 +                            builtin_merge_usage, builtin_merge_options);
 +
 +              if (!file_exists(git_path_merge_head()))
 +                      die(_("There is no merge in progress (MERGE_HEAD missing)."));
 +
 +              /* Invoke 'git commit' */
 +              ret = cmd_commit(nargc, nargv, prefix);
 +              goto done;
 +      }
 +
        if (read_cache_unmerged())
                die_resolve_conflict("merge");
  
 -      if (file_exists(git_path("MERGE_HEAD"))) {
 +      if (file_exists(git_path_merge_head())) {
                /*
                 * There is no unmerged entry, don't advise 'git
                 * add/rm <file>', just 'git commit'.
                else
                        die(_("You have not concluded your merge (MERGE_HEAD exists)."));
        }
 -      if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
 +      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 merge."));
                        builtin_merge_options);
  
        if (!head_commit) {
 -              struct commit *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                 * to forbid "git merge" into a branch yet to be born.
                 * We do the same for "git pull".
                 */
 +              struct object_id *remote_head_oid;
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
                if (fast_forward == FF_NO)
                            "an empty head"));
                remoteheads = collect_parents(head_commit, &head_subsumed,
                                              argc, argv, NULL);
 -              remote_head = remoteheads->item;
 -              if (!remote_head)
 +              if (!remoteheads)
                        die(_("%s - not something we can merge"), argv[0]);
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
 -              read_empty(remote_head->object.sha1, 0);
 -              update_ref("initial pull", "HEAD", remote_head->object.sha1,
 +              remote_head_oid = &remoteheads->item->object.oid;
 +              read_empty(remote_head_oid->hash, 0);
 +              update_ref("initial pull", "HEAD", remote_head_oid->hash,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        }
  
        /*
-        * 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
-        * commit-ish 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.
+        * All the rest are the commits being merged; prepare
+        * the standard merge summary message to be appended
+        * to the given message.
         */
-       if (!have_message &&
-           is_old_style_invocation(argc, argv, &head_commit->object.oid)) {
-               warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
-               strbuf_addstr(&merge_msg, argv[0]);
-               head_arg = argv[1];
-               argv += 2;
-               argc -= 2;
-               remoteheads = collect_parents(head_commit, &head_subsumed,
-                                             argc, argv, NULL);
-       } else {
-               /* 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.
-                */
-               remoteheads = collect_parents(head_commit, &head_subsumed,
-                                             argc, argv, &merge_msg);
-       }
+       remoteheads = collect_parents(head_commit, &head_subsumed,
+                                     argc, argv, &merge_msg);
  
        if (!head_commit || !argc)
                usage_with_options(builtin_merge_usage,
        if (verify_signatures) {
                for (p = remoteheads; p; p = p->next) {
                        struct commit *commit = p->item;
 -                      char hex[41];
 +                      char hex[GIT_SHA1_HEXSZ + 1];
                        struct signature_check signature_check;
                        memset(&signature_check, 0, sizeof(signature_check));
  
                        check_commit_signature(commit, &signature_check);
  
 -                      strcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 +                      find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV);
                        switch (signature_check.result) {
                        case 'G':
                                break;
        for (p = remoteheads; p; p = p->next) {
                struct commit *commit = p->item;
                strbuf_addf(&buf, "GITHEAD_%s",
 -                          sha1_to_hex(commit->object.sha1));
 +                          oid_to_hex(&commit->object.oid));
                setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
                if (fast_forward != FF_ONLY &&
        if (!remoteheads)
                ; /* already up-to-date */
        else if (!remoteheads->next)
 -              common = get_merge_bases(head_commit, remoteheads->item, 1);
 +              common = get_merge_bases(head_commit, remoteheads->item);
        else {
                struct commit_list *list = remoteheads;
                commit_list_insert(head_commit, &list);
                free(list);
        }
  
 -      update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
 +      update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
                   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
  
 -      if (remoteheads && !common)
 -              ; /* No common ancestors found. We need a real merge. */
 -      else if (!remoteheads ||
 +      if (remoteheads && !common) {
 +              /* No common ancestors found. */
 +              if (!allow_unrelated_histories)
 +                      die(_("refusing to merge unrelated histories"));
 +              /* otherwise, we need a real merge. */
 +      } else if (!remoteheads ||
                 (!remoteheads->next && !common->next &&
                  common->item == remoteheads->item)) {
                /*
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
                 */
 -              finish_up_to_date("Already up-to-date.");
 +              finish_up_to_date(_("Already up-to-date."));
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
 -                      !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
 +                      !oidcmp(&common->item->object.oid, &head_commit->object.oid)) {
                /* Again the most common case of merging one remote. */
                struct strbuf msg = STRBUF_INIT;
                struct commit *commit;
 -              char hex[41];
 -
 -              strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
  
 -              if (verbosity >= 0)
 +              if (verbosity >= 0) {
                        printf(_("Updating %s..%s\n"),
 -                              hex,
 -                              find_unique_abbrev(remoteheads->item->object.sha1,
 -                              DEFAULT_ABBREV));
 +                             find_unique_abbrev(head_commit->object.oid.hash,
 +                                                DEFAULT_ABBREV),
 +                             find_unique_abbrev(remoteheads->item->object.oid.hash,
 +                                                DEFAULT_ABBREV));
 +              }
                strbuf_addstr(&msg, "Fast-forward");
                if (have_message)
                        strbuf_addstr(&msg,
                        goto done;
                }
  
 -              if (checkout_fast_forward(head_commit->object.sha1,
 -                                        commit->object.sha1,
 +              if (checkout_fast_forward(head_commit->object.oid.hash,
 +                                        commit->object.oid.hash,
                                          overwrite_ignore)) {
                        ret = 1;
                        goto done;
                }
  
 -              finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
 +              finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
 -                      if (!read_tree_trivial(common->item->object.sha1,
 -                                             head_commit->object.sha1,
 -                                             remoteheads->item->object.sha1)) {
 +                      if (!read_tree_trivial(&common->item->object.oid,
 +                                             &head_commit->object.oid,
 +                                             &remoteheads->item->object.oid)) {
                                ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
                         * merge_bases again, otherwise "git merge HEAD^
                         * HEAD^^" would be missed.
                         */
 -                      common_one = get_merge_bases(head_commit, j->item, 1);
 -                      if (hashcmp(common_one->item->object.sha1,
 -                              j->item->object.sha1)) {
 +                      common_one = get_merge_bases(head_commit, j->item);
 +                      if (oidcmp(&common_one->item->object.oid, &j->item->object.oid)) {
                                up_to_date = 0;
                                break;
                        }
                }
                if (up_to_date) {
 -                      finish_up_to_date("Already up-to-date. Yeeah!");
 +                      finish_up_to_date(_("Already up-to-date. Yeeah!"));
                        goto done;
                }
        }
            /*
             * Stash away the local changes so that we can try more than one.
             */
 -          save_state(stash))
 -              hashcpy(stash, null_sha1);
 +          save_state(&stash))
 +              oidclr(&stash);
  
        for (i = 0; i < use_strategies_nr; i++) {
                int ret;
                if (i) {
                        printf(_("Rewinding the tree to pristine...\n"));
 -                      restore_state(head_commit->object.sha1, stash);
 +                      restore_state(&head_commit->object.oid, &stash);
                }
                if (use_strategies_nr != 1)
                        printf(_("Trying merge strategy %s...\n"),
  
                ret = try_merge_strategy(use_strategies[i]->name,
                                         common, remoteheads,
-                                        head_commit, head_arg);
+                                        head_commit);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
                }
  
                /* Automerge succeeded. */
 -              write_tree_trivial(result_tree);
 +              write_tree_trivial(&result_tree);
                automerge_was_ok = 1;
                break;
        }
        if (automerge_was_ok) {
                ret = finish_automerge(head_commit, head_subsumed,
                                       common, remoteheads,
 -                                     result_tree, wt_strategy);
 +                                     &result_tree, wt_strategy);
                goto done;
        }
  
         * it up.
         */
        if (!best_strategy) {
 -              restore_state(head_commit->object.sha1, stash);
 +              restore_state(&head_commit->object.oid, &stash);
                if (use_strategies_nr > 1)
                        fprintf(stderr,
                                _("No merge strategy handled the merge.\n"));
                ; /* We already have its result in the working tree. */
        else {
                printf(_("Rewinding the tree to pristine...\n"));
 -              restore_state(head_commit->object.sha1, stash);
 +              restore_state(&head_commit->object.oid, &stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
                try_merge_strategy(best_strategy, common, remoteheads,
-                                  head_commit, head_arg);
+                                  head_commit);
        }
  
        if (squash)
                fprintf(stderr, _("Automatic merge went well; "
                        "stopped before committing as requested\n"));
        else
 -              ret = suggest_conflicts(option_renormalize);
 +              ret = suggest_conflicts();
  
  done:
        free(branch_to_free);