Merge branch 'dg/local-mod-error-messages'
authorJunio C Hamano <gitster@pobox.com>
Sat, 4 Sep 2010 05:23:49 +0000 (22:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 4 Sep 2010 05:23:49 +0000 (22:23 -0700)
* dg/local-mod-error-messages:
t7609-merge-co-error-msgs: test non-fast forward case too.
Move "show_all_errors = 1" to setup_unpack_trees_porcelain()
setup_unpack_trees_porcelain: take the whole options struct as parameter
Move set_porcelain_error_msgs to unpack-trees.c and rename it

Conflicts:
merge-recursive.c

1  2 
builtin/checkout.c
builtin/merge.c
merge-recursive.c
merge-recursive.h
unpack-trees.c
diff --combined builtin/checkout.c
index ff5ac1e0ff20ad8e5b30289cd3f24f0c64eb0627,f3dfb7b8046cc92e578303e00e54eae53b2bc054..aae80c34cf79fa4fe04a03e48887d690db98047c
@@@ -32,11 -32,7 +32,11 @@@ struct checkout_opts 
        int writeout_stage;
        int writeout_error;
  
 +      /* not set by parse_options */
 +      int branch_exists;
 +
        const char *new_branch;
 +      const char *new_branch_force;
        const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
@@@ -154,10 -150,6 +154,10 @@@ static int checkout_merged(int pos, str
        read_mmblob(&ours, active_cache[pos+1]->sha1);
        read_mmblob(&theirs, active_cache[pos+2]->sha1);
  
 +      /*
 +       * NEEDSWORK: re-create conflicts from merges with
 +       * merge.renormalize set, too
 +       */
        status = ll_merge(&result_buf, path, &ancestor, "base",
                          &ours, "ours", &theirs, "theirs", 0);
        free(ancestor.ptr);
@@@ -287,6 -279,7 +287,6 @@@ static void show_local_changes(struct o
        struct rev_info rev;
        /* I think we want full paths, even if we're in a subdirectory. */
        init_revisions(&rev, NULL);
 -      rev.abbrev = 0;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        if (diff_setup_done(&rev.diffopt) < 0)
                die("diff_setup_done failed");
@@@ -380,7 -373,7 +380,7 @@@ static int merge_working_tree(struct ch
                topts.src_index = &the_index;
                topts.dst_index = &the_index;
  
-               set_porcelain_error_msgs(topts.msgs, "checkout");
+               setup_unpack_trees_porcelain(&topts, "checkout");
  
                refresh_cache(REFRESH_QUIET);
  
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
                topts.dir->exclude_per_dir = ".gitignore";
-               topts.show_all_errors = 1;
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           (unsigned char *)EMPTY_TREE_SHA1_BIN);
                         */
  
                        add_files_to_cache(NULL, NULL, 0);
 +                      /*
 +                       * NEEDSWORK: carrying over local changes
 +                       * when branches have different end-of-line
 +                       * normalization (or clean+smudge rules) is
 +                       * a pain; plumb in an option to set
 +                       * o.renormalize?
 +                       */
                        init_merge_options(&o);
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
@@@ -526,8 -511,7 +525,8 @@@ static void update_refs_for_switch(stru
                        }
                }
                else
 -                      create_branch(old->name, opts->new_branch, new->name, 0,
 +                      create_branch(old->name, opts->new_branch, new->name,
 +                                    opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
                        if (old->path && !strcmp(new->path, old->path))
                                fprintf(stderr, "Already on '%s'\n",
                                        new->name);
 -                      else
 +                      else if (opts->new_branch)
                                fprintf(stderr, "Switched to%s branch '%s'\n",
 -                                      opts->new_branch ? " a new" : "",
 +                                      opts->branch_exists ? " and reset" : " a new",
 +                                      new->name);
 +                      else
 +                              fprintf(stderr, "Switched to branch '%s'\n",
                                        new->name);
                }
                if (old->path && old->name) {
@@@ -676,10 -657,7 +675,10 @@@ int cmd_checkout(int argc, const char *
        int dwim_new_local_branch = 1;
        struct option options[] = {
                OPT__QUIET(&opts.quiet),
 -              OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
 +              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",
                        BRANCH_TRACK_EXPLICIT),
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
  
 +      /* 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");
 +
 +      /* 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");
  
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
 -                      die("--orphan and -b are mutually exclusive");
 +                      die("--orphan and -b|-B are mutually exclusive");
                if (opts.track > 0)
                        die("--orphan cannot be used with -t");
                opts.new_branch = opts.new_orphan_branch;
@@@ -888,12 -858,8 +887,12 @@@ no_reference
                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))
 -                      die("git checkout: branch %s already exists", 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);
 +              }
                strbuf_release(&buf);
        }
  
diff --combined builtin/merge.c
index 4e4ec898e6cd8bed9f43e2cb8d8958cd6160e766,32b4d9d0228d205b1b1c45839aab8955bfad4cac..5f65c0c8a6eacd4c6d05602ad9de2821ec62a034
@@@ -54,7 -54,6 +54,7 @@@ static size_t use_strategies_nr, use_st
  static const char **xopts;
  static size_t xopts_nr, xopts_alloc;
  static const char *branch;
 +static int option_renormalize;
  static int verbosity;
  static int allow_rerere_auto;
  
@@@ -132,7 -131,6 +132,7 @@@ static struct strategy *get_strategy(co
  
        ret = xcalloc(1, sizeof(struct strategy));
        ret->name = xstrdup(name);
 +      ret->attr = NO_TRIVIAL;
        return ret;
  }
  
@@@ -439,7 -437,7 +439,7 @@@ static void merge_name(const char *remo
                strbuf_addstr(&truname, "refs/heads/");
                strbuf_addstr(&truname, remote);
                strbuf_setlen(&truname, truname.len - len);
 -              if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
 +              if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
                                    sha1_to_hex(remote_head->sha1),
@@@ -488,8 -486,7 +488,8 @@@ static int git_merge_config(const char 
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
                if (argc < 0)
 -                      die("Bad branch.%s.mergeoptions string", branch);
 +                      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++;
                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);
        return git_diff_ui_config(k, v, cb);
  }
  
@@@ -629,11 -624,6 +629,11 @@@ static int try_merge_strategy(const cha
                if (!strcmp(strategy, "subtree"))
                        o.subtree_shift = "";
  
 +              o.renormalize = option_renormalize;
 +
 +              /*
 +               * 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;
                                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]);
                }
@@@ -718,8 -704,7 +718,7 @@@ int checkout_fast_forward(const unsigne
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
-       opts.show_all_errors = 1;
-       set_porcelain_error_msgs(opts.msgs, "merge");
+       setup_unpack_trees_porcelain(&opts, "merge");
  
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
@@@ -831,7 -816,7 +830,7 @@@ static int finish_automerge(struct comm
        return 0;
  }
  
 -static int suggest_conflicts(void)
 +static int suggest_conflicts(int renormalizing)
  {
        FILE *fp;
        int pos;
@@@ -1316,5 -1301,5 +1315,5 @@@ int cmd_merge(int argc, const char **ar
                        "stopped before committing as requested\n");
                return 0;
        } else
 -              return suggest_conflicts();
 +              return suggest_conflicts(option_renormalize);
  }
diff --combined merge-recursive.c
index aadd48c4fc73d693979956735e601d969594107e,084f54b88c92c2edb48a5aaab2004ed3840d573e..20e1779428ee553a940592c09bbabe9ca9800a39
@@@ -20,7 -20,6 +20,7 @@@
  #include "attr.h"
  #include "merge-recursive.h"
  #include "dir.h"
 +#include "submodule.h"
  
  static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
@@@ -137,10 -136,16 +137,10 @@@ static void output_commit_title(struct 
                if (parse_commit(commit) != 0)
                        printf("(bad commit)\n");
                else {
 -                      const char *s;
 -                      int len;
 -                      for (s = commit->buffer; *s; s++)
 -                              if (*s == '\n' && s[1] == '\n') {
 -                                      s += 2;
 -                                      break;
 -                              }
 -                      for (len = 0; s[len] && '\n' != s[len]; len++)
 -                              ; /* do nothing */
 -                      printf("%.*s\n", len, s);
 +                      const char *title;
 +                      int len = find_commit_subject(commit->buffer, &title);
 +                      if (len)
 +                              printf("%.*s\n", len, title);
                }
        }
  }
@@@ -180,7 -185,7 +180,7 @@@ static int git_merge_trees(int index_on
        opts.fn = threeway_merge;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
-       set_porcelain_error_msgs(opts.msgs, "merge");
+       setup_unpack_trees_porcelain(&opts, "merge");
  
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@@ -520,15 -525,13 +520,15 @@@ static void update_file_flags(struct me
                void *buf;
                unsigned long size;
  
 -              if (S_ISGITLINK(mode))
 +              if (S_ISGITLINK(mode)) {
                        /*
                         * We may later decide to recursively descend into
                         * the submodule directory and update its index
                         * and/or work tree, but we do not do that now.
                         */
 +                      update_wd = 0;
                        goto update_index;
 +              }
  
                buf = read_sha1_file(sha, &type, &size);
                if (!buf)
@@@ -644,9 -647,7 +644,9 @@@ static int merge_3way(struct merge_opti
  
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
                                &src1, name1, &src2, name2,
 -                              (!!o->call_depth) | (favor << 1));
 +                              ((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
 +                               (o->renormalize ? LL_OPT_RENORMALIZE : 0) |
 +                               create_ll_flag(favor)));
  
        free(name1);
        free(name2);
@@@ -715,8 -716,8 +715,8 @@@ static struct merge_file_info merge_fil
                        free(result_buf.ptr);
                        result.clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
 -                      result.clean = 0;
 -                      hashcpy(result.sha, a->sha1);
 +                      result.clean = merge_submodule(result.sha, one->path, one->sha1,
 +                                                     a->sha1, b->sha1);
                } else if (S_ISLNK(a->mode)) {
                        hashcpy(result.sha, a->sha1);
  
@@@ -805,8 -806,7 +805,8 @@@ static int process_renames(struct merge
                           struct string_list *b_renames)
  {
        int clean_merge = 1, i, j;
 -      struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
 +      struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
 +      struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
        const struct rename *sre;
  
        for (i = 0; i < a_renames->nr; i++) {
  
                                if (mfi.clean &&
                                    sha_eq(mfi.sha, ren1->pair->two->sha1) &&
 -                                  mfi.mode == ren1->pair->two->mode)
 +                                  mfi.mode == ren1->pair->two->mode) {
                                        /*
 -                                       * This messaged is part of
 +                                       * This message is part of
                                         * t6022 test. If you change
                                         * it update the test too.
                                         */
                                        output(o, 3, "Skipped %s (merged same as existing)", ren1_dst);
 -                              else {
 +
 +                                      /* There may be higher stage entries left
 +                                       * in the index (e.g. due to a D/F
 +                                       * conflict) that need to be resolved.
 +                                       */
 +                                      if (!ren1->dst_entry->stages[2].mode !=
 +                                          !ren1->dst_entry->stages[3].mode)
 +                                              ren1->dst_entry->processed = 0;
 +                              } else {
                                        if (mfi.merge || !mfi.clean)
                                                output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
                                        if (mfi.merge)
@@@ -1064,53 -1056,6 +1064,53 @@@ static unsigned char *stage_sha(const u
        return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
  }
  
 +static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
 +{
 +      void *buf;
 +      enum object_type type;
 +      unsigned long size;
 +      buf = read_sha1_file(sha1, &type, &size);
 +      if (!buf)
 +              return error("cannot read object %s", sha1_to_hex(sha1));
 +      if (type != OBJ_BLOB) {
 +              free(buf);
 +              return error("object %s is not a blob", sha1_to_hex(sha1));
 +      }
 +      strbuf_attach(dst, buf, size, size + 1);
 +      return 0;
 +}
 +
 +static int blob_unchanged(const unsigned char *o_sha,
 +                        const unsigned char *a_sha,
 +                        int renormalize, const char *path)
 +{
 +      struct strbuf o = STRBUF_INIT;
 +      struct strbuf a = STRBUF_INIT;
 +      int ret = 0; /* assume changed for safety */
 +
 +      if (sha_eq(o_sha, a_sha))
 +              return 1;
 +      if (!renormalize)
 +              return 0;
 +
 +      assert(o_sha && a_sha);
 +      if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
 +              goto error_return;
 +      /*
 +       * Note: binary | is used so that both renormalizations are
 +       * performed.  Comparison can be skipped if both files are
 +       * unchanged since their sha1s have already been compared.
 +       */
 +      if (renormalize_buffer(path, o.buf, o.len, &o) |
 +          renormalize_buffer(path, a.buf, o.len, &a))
 +              ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
 +
 +error_return:
 +      strbuf_release(&o);
 +      strbuf_release(&a);
 +      return ret;
 +}
 +
  /* Per entry merge function */
  static int process_entry(struct merge_options *o,
                         const char *path, struct stage_data *entry)
        print_index_entry("\tpath: ", entry);
        */
        int clean_merge = 1;
 +      int normalize = o->renormalize;
        unsigned o_mode = entry->stages[1].mode;
        unsigned a_mode = entry->stages[2].mode;
        unsigned b_mode = entry->stages[3].mode;
        unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
        unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
  
 +      entry->processed = 1;
        if (o_sha && (!a_sha || !b_sha)) {
                /* Case A: Deleted in one */
                if ((!a_sha && !b_sha) ||
 -                  (sha_eq(a_sha, o_sha) && !b_sha) ||
 -                  (!a_sha && sha_eq(b_sha, o_sha))) {
 +                  (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
 +                  (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_sha)
        } else if ((!o_sha && a_sha && !b_sha) ||
                   (!o_sha && !a_sha && b_sha)) {
                /* Case B: Added in one. */
 -              const char *add_branch;
 -              const char *other_branch;
                unsigned mode;
                const unsigned char *sha;
 -              const char *conf;
  
                if (a_sha) {
 -                      add_branch = o->branch1;
 -                      other_branch = o->branch2;
                        mode = a_mode;
                        sha = a_sha;
 -                      conf = "file/directory";
                } else {
 -                      add_branch = o->branch2;
 -                      other_branch = o->branch1;
                        mode = b_mode;
                        sha = b_sha;
 -                      conf = "directory/file";
                }
                if (string_list_has_string(&o->current_directory_set, path)) {
 -                      const char *new_path = unique_path(o, path, add_branch);
 -                      clean_merge = 0;
 -                      output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
 -                             "Adding %s as %s",
 -                             conf, path, other_branch, path, new_path);
 -                      remove_file(o, 0, path, 0);
 -                      update_file(o, 0, sha, mode, new_path);
 +                      /* Handle D->F conflicts after all subfiles */
 +                      entry->processed = 0;
 +                      /* But get any file out of the way now, so conflicted
 +                       * entries below the directory of the same name can
 +                       * be put in the working directory.
 +                       */
 +                      if (a_sha)
 +                              output(o, 2, "Removing %s", path);
 +                      /* do not touch working file if it did not exist */
 +                      remove_file(o, 0, path, !a_sha);
 +                      return 1; /* Assume clean till processed */
                } else {
                        output(o, 2, "Adding %s", path);
                        update_file(o, 1, sha, mode, path);
        return clean_merge;
  }
  
- void set_porcelain_error_msgs(const char **msgs, const char *cmd)
- {
-       const char *msg;
-       char *tmp;
-       const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
-       if (advice_commit_before_merge)
-               msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
-                       "Please, commit your changes or stash them before you can %s.";
-       else
-               msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
-       sprintf(tmp, msg, cmd, cmd2);
-       msgs[ERROR_WOULD_OVERWRITE] = tmp;
-       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
-       msgs[ERROR_NOT_UPTODATE_DIR] =
-               "Updating the following directories would lose untracked files in it:\n%s";
-       if (advice_commit_before_merge)
-               msg = "The following untracked working tree files would be %s by %s:\n%%s"
-                       "Please move or remove them before you can %s.";
-       else
-               msg = "The following untracked working tree files would be %s by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "removed", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "overwritten", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
-       /*
-        * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
-        * cannot easily display it as a list.
-        */
-       msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
-       msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
-               "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
-       msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
-               "The following Working tree files would be overwritten by sparse checkout update:\n%s";
-       msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
-               "The following Working tree files would be removed by sparse checkout update:\n%s";
- }
 +/*
 + * Per entry merge function for D/F conflicts, to be called only after
 + * all files below dir have been processed.  We do this because in the
 + * cases we can cleanly resolve D/F conflicts, process_entry() can clean
 + * out all the files below the directory for us.
 + */
 +static int process_df_entry(struct merge_options *o,
 +                       const char *path, struct stage_data *entry)
 +{
 +      int clean_merge = 1;
 +      unsigned o_mode = entry->stages[1].mode;
 +      unsigned a_mode = entry->stages[2].mode;
 +      unsigned b_mode = entry->stages[3].mode;
 +      unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
 +      unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
 +      unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
 +      const char *add_branch;
 +      const char *other_branch;
 +      unsigned mode;
 +      const unsigned char *sha;
 +      const char *conf;
 +      struct stat st;
 +
 +      /* We currently only handle D->F cases */
 +      assert((!o_sha && a_sha && !b_sha) ||
 +             (!o_sha && !a_sha && b_sha));
 +
 +      entry->processed = 1;
 +
 +      if (a_sha) {
 +              add_branch = o->branch1;
 +              other_branch = o->branch2;
 +              mode = a_mode;
 +              sha = a_sha;
 +              conf = "file/directory";
 +      } else {
 +              add_branch = o->branch2;
 +              other_branch = o->branch1;
 +              mode = b_mode;
 +              sha = b_sha;
 +              conf = "directory/file";
 +      }
 +      if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
 +              const char *new_path = unique_path(o, path, add_branch);
 +              clean_merge = 0;
 +              output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
 +                     "Adding %s as %s",
 +                     conf, path, other_branch, path, new_path);
 +              remove_file(o, 0, path, 0);
 +              update_file(o, 0, sha, mode, new_path);
 +      } else {
 +              output(o, 2, "Adding %s", path);
 +              update_file(o, 1, sha, mode, path);
 +      }
 +
 +      return clean_merge;
 +}
 +
  int merge_trees(struct merge_options *o,
                struct tree *head,
                struct tree *merge,
                                && !process_entry(o, path, e))
                                clean = 0;
                }
 +              for (i = 0; i < entries->nr; i++) {
 +                      const char *path = entries->items[i].string;
 +                      struct stage_data *e = entries->items[i].util;
 +                      if (!e->processed
 +                              && !process_df_entry(o, path, e))
 +                              clean = 0;
 +              }
  
                string_list_clear(re_merge, 0);
                string_list_clear(re_head, 0);
@@@ -1575,7 -1414,6 +1531,7 @@@ void init_merge_options(struct merge_op
        o->buffer_output = 1;
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
 +      o->renormalize = 0;
        git_config(merge_recursive_config, o);
        if (getenv("GIT_MERGE_VERBOSITY"))
                o->verbosity =
diff --combined merge-recursive.h
index 196f0531062a095d57afc92c16c57a67ec93f31c,f79917cef4fe429ded36bd3465f23336db4cd4a2..34492dbd6ea22d715c98acbeeabe04033ff9a794
@@@ -14,7 -14,6 +14,7 @@@ struct merge_options 
        } recursive_variant;
        const char *subtree_shift;
        unsigned buffer_output : 1;
 +      unsigned renormalize : 1;
        int verbosity;
        int diff_rename_limit;
        int merge_rename_limit;
        struct string_list current_directory_set;
  };
  
- /*
-  * Sets the list of user-friendly error messages to be used by the
-  * command "cmd" (either merge or checkout)
-  */
- void set_porcelain_error_msgs(const char **msgs, const char *cmd);
  /* merge_trees() but with recursive ancestor consolidation */
  int merge_recursive(struct merge_options *o,
                    struct commit *h1,
diff --combined unpack-trees.c
index 3c7a7c9cde745c1b438f60aceea7e556dded81d7,d0c5d1c4d560e0f6b2a6eccd2d1cf0fe3a89fe70..803445aa7be140c3707bcebc72aaf6fc6af45e4b
@@@ -14,7 -14,7 +14,7 @@@
   * read-tree.  Non-scripted Porcelain is not required to use these messages
   * and in fact are encouraged to reword them to better suit their particular
   * situation better.  See how "git checkout" and "git merge" replaces
-  * them using set_porcelain_error_msgs(), for example.
+  * them using setup_unpack_trees_porcelain(), for example.
   */
  const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
        /* ERROR_WOULD_OVERWRITE */
          ? ((o)->msgs[(type)])      \
          : (unpack_plumbing_errors[(type)]) )
  
+ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
+                                 const char *cmd)
+ {
+       const char **msgs = opts->msgs;
+       const char *msg;
+       char *tmp;
+       const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+       if (advice_commit_before_merge)
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
+                       "Please, commit your changes or stash them before you can %s.";
+       else
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
+       sprintf(tmp, msg, cmd, cmd2);
+       msgs[ERROR_WOULD_OVERWRITE] = tmp;
+       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+       msgs[ERROR_NOT_UPTODATE_DIR] =
+               "Updating the following directories would lose untracked files in it:\n%s";
+       if (advice_commit_before_merge)
+               msg = "The following untracked working tree files would be %s by %s:\n%%s"
+                       "Please move or remove them before you can %s.";
+       else
+               msg = "The following untracked working tree files would be %s by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "overwritten", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+       /*
+        * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
+        * cannot easily display it as a list.
+        */
+       msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
+       msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
+               "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
+               "The following Working tree files would be overwritten by sparse checkout update:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
+               "The following Working tree files would be removed by sparse checkout update:\n%s";
+       opts->show_all_errors = 1;
+ }
  static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
        unsigned int set, unsigned int clear)
  {
  
        clear |= CE_HASHED | CE_UNHASHED;
  
 +      if (set & CE_REMOVE)
 +              set |= CE_WT_REMOVE;
 +
        memcpy(new, ce, size);
        new->next = NULL;
        new->ce_flags = (new->ce_flags & ~clear) | set;
@@@ -77,12 -122,6 +125,6 @@@ static int add_rejected_path(struct unp
                             const char *path)
  {
        struct rejected_paths_list *newentry;
-       int porcelain = o && (o)->msgs[e];
-       /*
-        * simply display the given error message if in plumbing mode
-        */
-       if (!porcelain)
-               o->show_all_errors = 0;
        if (!o->show_all_errors)
                return error(ERRORMSG(o, e), path);
  
@@@ -159,7 -198,7 +201,7 @@@ static int check_updates(struct unpack_
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
                        struct cache_entry *ce = index->cache[cnt];
 -                      if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
 +                      if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
                                total++;
                }
  
                                unlink_entry(ce);
                        continue;
                }
 -
 -              if (ce->ce_flags & CE_REMOVE) {
 -                      display_progress(progress, ++cnt);
 -                      if (o->update)
 -                              unlink_entry(ce);
 -              }
        }
        remove_marked_cache_entries(&o->result);
        remove_scheduled_dirs();
@@@ -207,6 -252,9 +249,6 @@@ static int will_have_skip_worktree(cons
  {
        const char *basename;
  
 -      if (ce_stage(ce))
 -              return 0;
 -
        basename = strrchr(ce->name, '/');
        basename = basename ? basename+1 : ce->name;
        return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
@@@ -216,36 -264,19 +258,36 @@@ static int apply_sparse_checkout(struc
  {
        int was_skip_worktree = ce_skip_worktree(ce);
  
 -      if (will_have_skip_worktree(ce, o))
 +      if (!ce_stage(ce) && will_have_skip_worktree(ce, o))
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
  
        /*
 -       * We only care about files getting into the checkout area
 -       * If merge strategies want to remove some, go ahead, this
 -       * flag will be removed eventually in unpack_trees() if it's
 -       * outside checkout area.
 +       * if (!was_skip_worktree && !ce_skip_worktree()) {
 +       *      This is perfectly normal. Move on;
 +       * }
         */
 -      if (ce->ce_flags & CE_REMOVE)
 -              return 0;
 +
 +      /*
 +       * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
 +       * area as a result of ce_skip_worktree() shortcuts in
 +       * verify_absent() and verify_uptodate().
 +       * Make sure they don't modify worktree if they are already
 +       * outside checkout area
 +       */
 +      if (was_skip_worktree && ce_skip_worktree(ce)) {
 +              ce->ce_flags &= ~CE_UPDATE;
 +
 +              /*
 +               * By default, when CE_REMOVE is on, CE_WT_REMOVE is also
 +               * on to get that file removed from both index and worktree.
 +               * If that file is already outside worktree area, don't
 +               * bother remove it.
 +               */
 +              if (ce->ce_flags & CE_REMOVE)
 +                      ce->ce_flags &= ~CE_WT_REMOVE;
 +      }
  
        if (!was_skip_worktree && ce_skip_worktree(ce)) {
                /*
@@@ -412,7 -443,6 +454,7 @@@ static int traverse_trees_recursive(in
  {
        int i, ret, bottom;
        struct tree_desc t[MAX_UNPACK_TREES];
 +      void *buf[MAX_UNPACK_TREES];
        struct traverse_info newinfo;
        struct name_entry *p;
  
                const unsigned char *sha1 = NULL;
                if (dirmask & 1)
                        sha1 = names[i].sha1;
 -              fill_tree_descriptor(t+i, sha1);
 +              buf[i] = fill_tree_descriptor(t+i, sha1);
        }
  
        bottom = switch_cache_bottom(&newinfo);
        ret = traverse_trees(n, t, &newinfo);
        restore_cache_bottom(&newinfo, bottom);
 +
 +      for (i = 0; i < n; i++)
 +              free(buf[i]);
 +
        return ret;
  }
  
@@@ -887,7 -913,14 +929,7 @@@ int unpack_trees(unsigned len, struct t
                                ret = -1;
                                goto done;
                        }
 -                      /*
 -                       * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
 -                       * area as a result of ce_skip_worktree() shortcuts in
 -                       * verify_absent() and verify_uptodate(). Clear them.
 -                       */
 -                      if (ce_skip_worktree(ce))
 -                              ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
 -                      else
 +                      if (!ce_skip_worktree(ce))
                                empty_worktree = 0;
  
                }
@@@ -1186,8 -1219,6 +1228,8 @@@ static int merged_entry(struct cache_en
        if (!old) {
                if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
 +              if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o))
 +                      update |= CE_SKIP_WORKTREE;
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*