Merge branch 'maint-1.7.7' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Dec 2011 05:58:51 +0000 (21:58 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Dec 2011 05:58:51 +0000 (21:58 -0800)
* maint-1.7.7:
Git 1.7.7.5
Git 1.7.6.5
blame: don't overflow time buffer
fetch: create status table using strbuf
checkout,merge: loosen overwriting untracked file check based on info/exclude
cast variable in call to free() in builtin/diff.c and submodule.c
apply: get rid of useless x < 0 comparison on a size_t type

Conflicts:
Documentation/git.txt
GIT-VERSION-GEN
RelNotes
builtin/fetch.c

1  2 
Documentation/git.txt
builtin/apply.c
builtin/blame.c
builtin/checkout.c
builtin/fetch.c
builtin/merge.c
submodule.c
diff --combined Documentation/git.txt
index e869032fc0703aed18d3cb1417eda4341c29e093,5c313e52144fded5fa83a7b11252e0024b4f2628..da7d48787e7a00e7e14f9d9a0bf236ffe088bd8b
@@@ -44,20 -44,20 +44,25 @@@ unreleased) version of git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
- * link:v1.7.7.1/git.html[documentation for release 1.7.7.1]
 +* link:v1.7.8/git.html[documentation for release 1.7.8]
 +
 +* release notes for
 +  link:RelNotes/1.7.8.txt[1.7.8].
 +
+ * link:v1.7.7.5/git.html[documentation for release 1.7.7.5]
  
  * release notes for
+   link:RelNotes/1.7.7.5.txt[1.7.7.5],
+   link:RelNotes/1.7.7.4.txt[1.7.7.4],
+   link:RelNotes/1.7.7.3.txt[1.7.7.3],
+   link:RelNotes/1.7.7.2.txt[1.7.7.2],
    link:RelNotes/1.7.7.1.txt[1.7.7.1],
    link:RelNotes/1.7.7.txt[1.7.7].
  
- * link:v1.7.6.4/git.html[documentation for release 1.7.6.4]
+ * link:v1.7.6.5/git.html[documentation for release 1.7.6.5]
  
  * release notes for
+   link:RelNotes/1.7.6.5.txt[1.7.6.5],
    link:RelNotes/1.7.6.4.txt[1.7.6.4],
    link:RelNotes/1.7.6.3.txt[1.7.6.3],
    link:RelNotes/1.7.6.2.txt[1.7.6.2],
diff --combined builtin/apply.c
index 84a8a0b52136c4d1e43ec10f9ef5ed76b7d3c12f,082fe8f3e3e6088adcce4b54ae351c4ec1f03786..b3b59db534ff0763c822ae9da571d5643a8b709a
@@@ -250,9 -250,6 +250,6 @@@ static int fuzzy_matchlines(const char 
        const char *last2 = s2 + n2 - 1;
        int result = 0;
  
-       if (n1 < 0 || n2 < 0)
-               return 0;
        /* ignore line endings */
        while ((*last1 == '\r') || (*last1 == '\n'))
                last1--;
@@@ -3841,6 -3838,7 +3838,6 @@@ int cmd_apply(int argc, const char **ar
        int i;
        int errs = 0;
        int is_not_gitdir = !startup_info->have_repository;
 -      int binary;
        int force_apply = 0;
  
        const char *whitespace_option = NULL;
                        "ignore additions made by the patch"),
                OPT_BOOLEAN(0, "stat", &diffstat,
                        "instead of applying the patch, output diffstat for the input"),
 -              { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
 -                NULL, "old option, now no-op",
 -                PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
 -              { OPTION_BOOLEAN, 0, "binary", &binary,
 -                NULL, "old option, now no-op",
 -                PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
 +              OPT_NOOP_NOARG(0, "allow-binary-replacement"),
 +              OPT_NOOP_NOARG(0, "binary"),
                OPT_BOOLEAN(0, "numstat", &numstat,
                        "shows number of added and deleted lines in decimal notation"),
                OPT_BOOLEAN(0, "summary", &summary,
diff --combined builtin/blame.c
index 80febbe420db1c75bbcf7eda6a733e7e66549790,3e1f7e1e453f6ecc28fb910686c6580e77929556..5a67c202f06abeaa90a7547d78b536f7f2b9db24
@@@ -1598,7 -1598,7 +1598,7 @@@ static const char *format_time(unsigne
        int tz;
  
        if (show_raw_time) {
-               sprintf(time_buf, "%lu %s", time, tz_str);
+               snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
        }
        else {
                tz = atoi(tz_str);
@@@ -2096,7 -2096,6 +2096,7 @@@ static struct commit *fake_working_tree
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
 +              char *buf_ptr;
                unsigned long buf_len;
  
                if (contents_from) {
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                          textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
 -                              buf.len = buf_len;
 +                          textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
 +                              strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
                        break;
diff --combined builtin/checkout.c
index 2a8077242500d54ac50d5829a86b14803fc69126,7ea9f29aa6d24edf44431161208a306798e69623..51840b9784f0daec21087ec101304eaa6cbf73cd
@@@ -411,7 -411,7 +411,7 @@@ static int merge_working_tree(struct ch
                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 :
                                           EMPTY_TREE_SHA1_BIN);
@@@ -593,11 -593,23 +593,11 @@@ static void update_refs_for_switch(stru
                report_tracking(new);
  }
  
 -static int add_one_ref_to_rev_list_arg(const char *refname,
 -                                     const unsigned char *sha1,
 -                                     int flags,
 -                                     void *cb_data)
 +static int add_pending_uninteresting_ref(const char *refname,
 +                                       const unsigned char *sha1,
 +                                       int flags, void *cb_data)
  {
 -      argv_array_push(cb_data, refname);
 -      return 0;
 -}
 -
 -static int clear_commit_marks_from_one_ref(const char *refname,
 -                                    const unsigned char *sha1,
 -                                    int flags,
 -                                    void *cb_data)
 -{
 -      struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 -      if (commit)
 -              clear_commit_marks(commit, -1);
 +      add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
        return 0;
  }
  
@@@ -666,21 -678,18 +666,21 @@@ static void suggest_reattach(struct com
   */
  static void orphaned_commit_warning(struct commit *commit)
  {
 -      struct argv_array args = ARGV_ARRAY_INIT;
        struct rev_info revs;
 -
 -      argv_array_push(&args, "(internal)");
 -      argv_array_push(&args, sha1_to_hex(commit->object.sha1));
 -      argv_array_push(&args, "--not");
 -      for_each_ref(add_one_ref_to_rev_list_arg, &args);
 -      argv_array_push(&args, "--");
 +      struct object *object = &commit->object;
 +      struct object_array refs;
  
        init_revisions(&revs, NULL);
 -      if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
 -              die(_("internal error: only -- alone should have been left"));
 +      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))
        else
                describe_detached_head(_("Previous HEAD position was"), commit);
  
 -      argv_array_clear(&args);
 -      clear_commit_marks(commit, -1);
 -      for_each_ref(clear_commit_marks_from_one_ref, NULL);
 +      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)
@@@ -865,7 -875,7 +865,7 @@@ static int parse_branchname_arg(int arg
        new->name = arg;
        setup_branch_path(new);
  
 -      if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
 +      if (!check_refname_format(new->path, 0) &&
            resolve_ref(new->path, branch_rev, 1, NULL))
                hashcpy(rev, branch_rev);
        else
diff --combined builtin/fetch.c
index 91731b909aeb22bf8d4e366b8b92281ac0f9ac0c,59dfba5180d2373f0e52563f4db928a25708822d..8761a33b491ae8a9437d78247ef0808c0686486e
@@@ -13,7 -13,6 +13,7 @@@
  #include "sigchain.h"
  #include "transport.h"
  #include "submodule.h"
 +#include "connected.h"
  
  static const char * const builtin_fetch_usage[] = {
        "git fetch [<options>] [<repository> [<refspec>...]]",
@@@ -240,23 -239,23 +240,23 @@@ static int s_update_ref(const char *act
  
  static int update_local_ref(struct ref *ref,
                            const char *remote,
-                           char *display)
+                           struct strbuf *display)
  {
        struct commit *current = NULL, *updated;
        enum object_type type;
        struct branch *current_branch = branch_get(NULL);
        const char *pretty_ref = prettify_refname(ref->name);
  
-       *display = 0;
        type = sha1_object_info(ref->new_sha1, NULL);
        if (type < 0)
                die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
  
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
                if (verbosity > 0)
-                       sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
-                               _("[up to date]"), REFCOL_WIDTH, remote,
-                               pretty_ref);
+                       strbuf_addf(display, "= %-*s %-*s -> %s",
+                                   TRANSPORT_SUMMARY_WIDTH,
+                                   _("[up to date]"), REFCOL_WIDTH,
+                                   remote, pretty_ref);
                return 0;
        }
  
                 * If this is the head, and it's not okay to update
                 * the head, and the old value of the head isn't empty...
                 */
-               sprintf(display, _("! %-*s %-*s -> %s  (can't fetch in current branch)"),
-                       TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
-                       pretty_ref);
+               strbuf_addf(display,
+                           _("! %-*s %-*s -> %s  (can't fetch in current branch)"),
+                           TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+                           REFCOL_WIDTH, remote, pretty_ref);
                return 1;
        }
  
            !prefixcmp(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
-               sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
-                       TRANSPORT_SUMMARY_WIDTH, _("[tag update]"), REFCOL_WIDTH, remote,
-                       pretty_ref, r ? _("  (unable to update local ref)") : "");
+               strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+                           r ? '!' : '-',
+                           TRANSPORT_SUMMARY_WIDTH, _("[tag update]"),
+                           REFCOL_WIDTH, remote, pretty_ref,
+                           r ? _("  (unable to update local ref)") : "");
                return r;
        }
  
                }
  
                r = s_update_ref(msg, ref, 0);
-               sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
-                       TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
-                       r ? _("  (unable to update local ref)") : "");
+               strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+                           r ? '!' : '*',
+                           TRANSPORT_SUMMARY_WIDTH, what,
+                           REFCOL_WIDTH, remote, pretty_ref,
+                           r ? _("  (unable to update local ref)") : "");
                return r;
        }
  
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref("fast-forward", ref, 1);
-               sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
-                       TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
-                       pretty_ref, r ? _("  (unable to update local ref)") : "");
+               strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+                           r ? '!' : ' ',
+                           TRANSPORT_SUMMARY_WIDTH, quickref,
+                           REFCOL_WIDTH, remote, pretty_ref,
+                           r ? _("  (unable to update local ref)") : "");
                return r;
        } else if (force || ref->force) {
                char quickref[84];
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref("forced-update", ref, 1);
-               sprintf(display, "%c %-*s %-*s -> %s  (%s)", r ? '!' : '+',
-                       TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
-                       pretty_ref,
-                       r ? _("unable to update local ref") : _("forced update"));
+               strbuf_addf(display, "%c %-*s %-*s -> %s  (%s)",
+                           r ? '!' : '+',
+                           TRANSPORT_SUMMARY_WIDTH, quickref,
+                           REFCOL_WIDTH, remote, pretty_ref,
+                           r ? _("unable to update local ref") : _("forced update"));
                return r;
        } else {
-               sprintf(display, "! %-*s %-*s -> %s  %s",
-                       TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
-                       pretty_ref, _("(non-fast-forward)"));
+               strbuf_addf(display, "! %-*s %-*s -> %s  %s",
+                           TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+                           REFCOL_WIDTH, remote, pretty_ref,
+                           _("(non-fast-forward)"));
                return 1;
        }
  }
  
 +static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
 +{
 +      struct ref **rm = cb_data;
 +      struct ref *ref = *rm;
 +
 +      if (!ref)
 +              return -1; /* end of the list */
 +      *rm = ref->next;
 +      hashcpy(sha1, ref->old_sha1);
 +      return 0;
 +}
 +
  static int store_updated_refs(const char *raw_url, const char *remote_name,
                struct ref *ref_map)
  {
        FILE *fp;
        struct commit *commit;
-       int url_len, i, note_len, shown_url = 0, rc = 0;
-       char note[1024];
+       int url_len, i, shown_url = 0, rc = 0;
+       struct strbuf note = STRBUF_INIT;
        const char *what, *kind;
        struct ref *rm;
        char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
                url = transport_anonymize_url(raw_url);
        else
                url = xstrdup("foreign");
 +
 +      rm = ref_map;
 +      if (check_everything_connected(iterate_ref_map, 0, &rm)) {
 +              rc = error(_("%s did not send all necessary objects\n"), url);
 +              goto abort;
 +      }
 +
        for (rm = ref_map; rm; rm = rm->next) {
                struct ref *ref = NULL;
  
                if (4 < i && !strncmp(".git", url + i - 3, 4))
                        url_len = i - 3;
  
-               note_len = 0;
+               strbuf_reset(&note);
                if (*what) {
                        if (*kind)
-                               note_len += sprintf(note + note_len, "%s ",
-                                                   kind);
-                       note_len += sprintf(note + note_len, "'%s' of ", what);
+                               strbuf_addf(&note, "%s ", kind);
+                       strbuf_addf(&note, "'%s' of ", what);
                }
-               note[note_len] = '\0';
                fprintf(fp, "%s\t%s\t%s",
                        sha1_to_hex(commit ? commit->object.sha1 :
                                    rm->old_sha1),
                        rm->merge ? "" : "not-for-merge",
-                       note);
+                       note.buf);
                for (i = 0; i < url_len; ++i)
                        if ('\n' == url[i])
                                fputs("\\n", fp);
                                fputc(url[i], fp);
                fputc('\n', fp);
  
+               strbuf_reset(&note);
                if (ref) {
-                       rc |= update_local_ref(ref, what, note);
+                       rc |= update_local_ref(ref, what, &note);
                        free(ref);
                } else
-                       sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
-                               TRANSPORT_SUMMARY_WIDTH, *kind ? kind : "branch",
-                                REFCOL_WIDTH, *what ? what : "HEAD");
-               if (*note) {
+                       strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
+                                   TRANSPORT_SUMMARY_WIDTH,
+                                   *kind ? kind : "branch",
+                                   REFCOL_WIDTH,
+                                   *what ? what : "HEAD");
+               if (note.len) {
                        if (verbosity >= 0 && !shown_url) {
                                fprintf(stderr, _("From %.*s\n"),
                                                url_len, url);
                                shown_url = 1;
                        }
                        if (verbosity >= 0)
-                               fprintf(stderr, " %s\n", note);
+                               fprintf(stderr, " %s\n", note.buf);
                }
        }
 -      free(url);
 -      fclose(fp);
 +
        if (rc & STORE_REF_ERROR_DF_CONFLICT)
                error(_("some local refs could not be updated; try running\n"
                      " 'git remote prune %s' to remove any old, conflicting "
                      "branches"), remote_name);
 +
 + abort:
+       strbuf_release(&note);
 +      free(url);
 +      fclose(fp);
        return rc;
  }
  
   * We would want to bypass the object transfer altogether if
   * everything we are going to fetch already exists and is connected
   * locally.
 - *
 - * The refs we are going to fetch are in ref_map.  If running
 - *
 - *  $ git rev-list --objects --stdin --not --all
 - *
 - * (feeding all the refs in ref_map on its standard input)
 - * does not error out, that means everything reachable from the
 - * refs we are going to fetch exists and is connected to some of
 - * our existing refs.
   */
  static int quickfetch(struct ref *ref_map)
  {
 -      struct child_process revlist;
 -      struct ref *ref;
 -      int err;
 -      const char *argv[] = {"rev-list",
 -              "--quiet", "--objects", "--stdin", "--not", "--all", NULL};
 +      struct ref *rm = ref_map;
  
        /*
         * If we are deepening a shallow clone we already have these
         */
        if (depth)
                return -1;
 -
 -      if (!ref_map)
 -              return 0;
 -
 -      memset(&revlist, 0, sizeof(revlist));
 -      revlist.argv = argv;
 -      revlist.git_cmd = 1;
 -      revlist.no_stdout = 1;
 -      revlist.no_stderr = 1;
 -      revlist.in = -1;
 -
 -      err = start_command(&revlist);
 -      if (err) {
 -              error(_("could not run rev-list"));
 -              return err;
 -      }
 -
 -      /*
 -       * If rev-list --stdin encounters an unknown commit, it terminates,
 -       * which will cause SIGPIPE in the write loop below.
 -       */
 -      sigchain_push(SIGPIPE, SIG_IGN);
 -
 -      for (ref = ref_map; ref; ref = ref->next) {
 -              if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
 -                  write_str_in_full(revlist.in, "\n") < 0) {
 -                      if (errno != EPIPE && errno != EINVAL)
 -                              error(_("failed write to rev-list: %s"), strerror(errno));
 -                      err = -1;
 -                      break;
 -              }
 -      }
 -
 -      if (close(revlist.in)) {
 -              error(_("failed to close rev-list's stdin: %s"), strerror(errno));
 -              err = -1;
 -      }
 -
 -      sigchain_pop(SIGPIPE);
 -
 -      return finish_command(&revlist) || err;
 +      return check_everything_connected(iterate_ref_map, 1, &rm);
  }
  
  static int fetch_refs(struct transport *transport, struct ref *ref_map)
diff --combined builtin/merge.c
index 2870a6af6fbc868455f2961483cfbfdb065a1a14,b22a8422381e73fa410d0c27a70bb9ef6cdc9232..138737624807389ccb903a5cd03840d63b7cbf6f
@@@ -46,7 -46,7 +46,7 @@@ static const char * const builtin_merge
  
  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;
@@@ -189,8 -189,6 +189,8 @@@ static struct option builtin_merge_opti
                "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,
@@@ -316,15 -314,13 +316,15 @@@ 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"));
 -      fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
 +      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;
@@@ -408,16 -404,6 +408,16 @@@ static void finish(struct commit *head_
        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);
  
  
        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"));
 +                                filename);
                strbuf_getline(&line, fp, '\n');
                fclose(fp);
                ptr = strstr(line.buf, "\tnot-for-merge\t");
@@@ -775,7 -759,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;
@@@ -849,56 -833,30 +849,56 @@@ static void add_strategies(const char *
  
  }
  
 -static void write_merge_msg(void)
 +static void write_merge_msg(struct strbuf *msg)
  {
 -      int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
 +      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"),
 -                        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"));
 +                        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(void)
 +static void read_merge_msg(struct strbuf *msg)
  {
 -      strbuf_reset(&merge_msg);
 -      if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0)
 -              die_errno(_("Could not read from '%s'"), 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);
  }
  
 -static void run_prepare_commit_msg(void)
 +static void write_merge_state(void);
 +static void abort_commit(const char *err_msg)
  {
 -      write_merge_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);
 -      read_merge_msg();
 +      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)
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
 -      run_prepare_commit_msg();
 +      prepare_to_commit();
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
        finish(head, result_commit, "In-index merge");
        drop_save();
@@@ -941,9 -899,9 +941,9 @@@ static int finish_automerge(struct comm
                for (j = remoteheads; j; j = j->next)
                        pptr = &commit_list_insert(j->item, pptr)->next;
        }
 -      free_commit_list(remoteheads);
        strbuf_addch(&merge_msg, '\n');
 -      run_prepare_commit_msg();
 +      prepare_to_commit();
 +      free_commit_list(remoteheads);
        commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, result_commit, buf.buf);
  
  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];
@@@ -1051,38 -1008,6 +1051,38 @@@ static int setup_with_upstream(const ch
        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];
                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);
 +              remote_head = want_commit(argv[0]);
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
                read_empty(remote_head->sha1, 0);
                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]);
                commit = lookup_commit(o->sha1);
                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 (squash)
                finish(head_commit, 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');
 -              write_merge_msg();
 -              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);
 -      }
 +      else
 +              write_merge_state();
  
        if (merge_was_ok) {
                fprintf(stderr, _("Automatic merge went well; "
diff --combined submodule.c
index 0fd10a0fdbf5d1af10819dea808b43f6b13b98a8,09a41b59befca75feb6fec95d6c98bb51e10b987..52cdcc6a6347a786deafad34efdb9dc4dc3670ff
@@@ -391,7 -391,7 +391,7 @@@ static void commit_need_pushing(struct 
        rev.diffopt.format_callback_data = needs_pushing;
        diff_tree_combined(commit->object.sha1, parents, n, 1, &rev);
  
-       free(parents);
+       free((void *)parents);
  }
  
  int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
@@@ -794,7 -794,7 +794,7 @@@ static void print_commit(struct commit 
  
  int merge_submodule(unsigned char result[20], const char *path,
                    const unsigned char base[20], const unsigned char a[20],
 -                  const unsigned char b[20])
 +                  const unsigned char b[20], int search)
  {
        struct commit *commit_base, *commit_a, *commit_b;
        int parent_count;
         * user needs to confirm the resolution.
         */
  
 +      /* Skip the search if makes no sense to the calling context.  */
 +      if (!search)
 +              return 0;
 +
        /* find commit which merges them */
        parent_count = find_first_merges(&merges, path, commit_a, commit_b);
        switch (parent_count) {