Merge branch 'tr/void-diff-setup-done' into maint-1.7.11
authorJunio C Hamano <gitster@pobox.com>
Tue, 11 Sep 2012 17:53:40 +0000 (10:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 11 Sep 2012 17:53:40 +0000 (10:53 -0700)
* tr/void-diff-setup-done:
diff_setup_done(): return void

12 files changed:
1  2 
builtin/blame.c
builtin/checkout.c
builtin/diff.c
builtin/merge.c
diff-no-index.c
diff.c
diff.h
merge-recursive.c
notes-merge.c
revision.c
submodule.c
tree-diff.c
diff --combined builtin/blame.c
index a9705d06a5364e881eb894c5b401e6a3b953202d,c0e26a0b178c0441bf52bc1ea220408c6cb13265..cad4111a36a5b880d9d851463ef5f554f56d27d3
@@@ -88,20 -88,6 +88,20 @@@ struct origin 
        char path[FLEX_ARRAY];
  };
  
 +static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
 +                    xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
 +{
 +      xpparam_t xpp = {0};
 +      xdemitconf_t xecfg = {0};
 +      xdemitcb_t ecb = {NULL};
 +
 +      xpp.flags = xdl_opts;
 +      xecfg.ctxlen = ctxlen;
 +      xecfg.hunk_func = hunk_func;
 +      ecb.priv = cb_data;
 +      return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
 +}
 +
  /*
   * Prepare diff_filespec and convert it using diff textconv API
   * if the textconv driver exists.
  int textconv_object(const char *path,
                    unsigned mode,
                    const unsigned char *sha1,
 +                  int sha1_valid,
                    char **buf,
                    unsigned long *buf_size)
  {
        struct userdiff_driver *textconv;
  
        df = alloc_filespec(path);
 -      fill_filespec(df, sha1, mode);
 +      fill_filespec(df, sha1, sha1_valid, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@@ -143,7 -128,7 +143,7 @@@ static void fill_origin_blob(struct dif
  
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                  textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
 +                  textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@@ -407,8 -392,7 +407,7 @@@ static struct origin *find_origin(struc
        paths[1] = NULL;
  
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
  
        if (is_null_sha1(origin->commit->object.sha1))
                do_diff_cache(parent->tree->object.sha1, &diff_opts);
@@@ -494,8 -478,7 +493,7 @@@ static struct origin *find_rename(struc
        diff_opts.single_follow = origin->path;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
  
        if (is_null_sha1(origin->commit->object.sha1))
                do_diff_cache(parent->tree->object.sha1, &diff_opts);
@@@ -774,14 -757,12 +772,14 @@@ struct blame_chunk_cb_data 
        long tlno;
  };
  
 -static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
 +static int blame_chunk_cb(long start_a, long count_a,
 +                        long start_b, long count_b, void *data)
  {
        struct blame_chunk_cb_data *d = data;
 -      blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
 -      d->plno = p_next;
 -      d->tlno = t_next;
 +      blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent);
 +      d->plno = start_a + count_a;
 +      d->tlno = start_b + count_b;
 +      return 0;
  }
  
  /*
@@@ -796,7 -777,8 +794,7 @@@ static int pass_blame_to_parent(struct 
        int last_in_target;
        mmfile_t file_p, file_o;
        struct blame_chunk_cb_data d;
 -      xpparam_t xpp;
 -      xdemitconf_t xecfg;
 +
        memset(&d, 0, sizeof(d));
        d.sb = sb; d.target = target; d.parent = parent;
        last_in_target = find_last_in_target(sb, target);
        fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
  
 -      memset(&xpp, 0, sizeof(xpp));
 -      xpp.flags = xdl_opts;
 -      memset(&xecfg, 0, sizeof(xecfg));
 -      xecfg.ctxlen = 0;
 -      xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
 +      diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
        /* The rest (i.e. anything after tlno) are the same as the parent */
        blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
  
@@@ -911,15 -897,12 +909,15 @@@ struct handle_split_cb_data 
        long tlno;
  };
  
 -static void handle_split_cb(void *data, long same, long p_next, long t_next)
 +static int handle_split_cb(long start_a, long count_a,
 +                         long start_b, long count_b, void *data)
  {
        struct handle_split_cb_data *d = data;
 -      handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
 -      d->plno = p_next;
 -      d->tlno = t_next;
 +      handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
 +                   d->split);
 +      d->plno = start_a + count_a;
 +      d->tlno = start_b + count_b;
 +      return 0;
  }
  
  /*
@@@ -937,7 -920,8 +935,7 @@@ static void find_copy_in_blob(struct sc
        int cnt;
        mmfile_t file_o;
        struct handle_split_cb_data d;
 -      xpparam_t xpp;
 -      xdemitconf_t xecfg;
 +
        memset(&d, 0, sizeof(d));
        d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
        /*
         * file_o is a part of final image we are annotating.
         * file_p partially may match that image.
         */
 -      memset(&xpp, 0, sizeof(xpp));
 -      xpp.flags = xdl_opts;
 -      memset(&xecfg, 0, sizeof(xecfg));
 -      xecfg.ctxlen = 1;
        memset(split, 0, sizeof(struct blame_entry [3]));
 -      xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
 +      diff_hunks(file_p, &file_o, 1, handle_split_cb, &d);
        /* remainder, if any, all match the preimage */
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
  }
@@@ -1075,8 -1063,7 +1073,7 @@@ static int find_copy_in_parent(struct s
  
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
  
        /* Try "find copies harder" on new path if requested;
         * we do not want to use diffcore_rename() actually to
@@@ -1838,14 -1825,16 +1835,14 @@@ static int read_ancestry(const char *gr
        return 0;
  }
  
 -/*
 - * How many columns do we need to show line numbers in decimal?
 - */
 -static int lineno_width(int lines)
 +static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
  {
 -      int i, width;
 -
 -      for (width = 1, i = 10; i <= lines; width++)
 -              i *= 10;
 -      return width;
 +      const char *uniq = find_unique_abbrev(suspect->commit->object.sha1,
 +                                            auto_abbrev);
 +      int len = strlen(uniq);
 +      if (auto_abbrev < len)
 +              return len;
 +      return auto_abbrev;
  }
  
  /*
@@@ -1858,16 -1847,12 +1855,16 @@@ static void find_alignment(struct score
        int longest_dst_lines = 0;
        unsigned largest_score = 0;
        struct blame_entry *e;
 +      int compute_auto_abbrev = (abbrev < 0);
 +      int auto_abbrev = default_abbrev;
  
        for (e = sb->ent; e; e = e->next) {
                struct origin *suspect = e->suspect;
                struct commit_info ci;
                int num;
  
 +              if (compute_auto_abbrev)
 +                      auto_abbrev = update_auto_abbrev(auto_abbrev, suspect);
                if (strcmp(suspect->path, sb->path))
                        *option |= OUTPUT_SHOW_NAME;
                num = strlen(suspect->path);
                if (largest_score < ent_score(sb, e))
                        largest_score = ent_score(sb, e);
        }
 -      max_orig_digits = lineno_width(longest_src_lines);
 -      max_digits = lineno_width(longest_dst_lines);
 -      max_score_digits = lineno_width(largest_score);
 +      max_orig_digits = decimal_width(longest_src_lines);
 +      max_digits = decimal_width(longest_dst_lines);
 +      max_score_digits = decimal_width(largest_score);
 +
 +      if (compute_auto_abbrev)
 +              /* one more abbrev length is needed for the boundary commit */
 +              abbrev = auto_abbrev + 1;
  }
  
  /*
@@@ -2066,8 -2047,14 +2063,8 @@@ static int git_blame_config(const char 
                return 0;
        }
  
 -      switch (userdiff_config(var, value)) {
 -      case 0:
 -              break;
 -      case -1:
 +      if (userdiff_config(var, value) < 0)
                return -1;
 -      default:
 -              return 0;
 -      }
  
        return git_default_config(var, value, cb);
  }
@@@ -2124,7 -2111,7 +2121,7 @@@ static struct commit *fake_working_tree
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                          textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
 +                          textconv_object(read_from, mode, null_sha1, 0, &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);
@@@ -2330,7 -2317,6 +2327,7 @@@ int cmd_blame(int argc, const char **ar
                OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
                OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
 +              OPT_BIT(0, "minimal", &xdl_opts, "Spend extra cycles to find better match", XDF_NEED_MINIMAL),
                OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
                OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
                { OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
  parse_done:
        argc = parse_options_end(&ctx);
  
 -      if (abbrev == -1)
 -              abbrev = default_abbrev;
 -      /* one more abbrev length is needed for the boundary commit */
 -      abbrev++;
 +      if (0 < abbrev)
 +              /* one more abbrev length is needed for the boundary commit */
 +              abbrev++;
  
        if (revs_file && read_ancestry(revs_file))
                die_errno("reading graft file '%s' failed", revs_file);
                        die("no such path %s in %s", path, final_commit_name);
  
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
 -                  textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
 +                  textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
diff --combined builtin/checkout.c
index e060efb2a281865afbc6e5ae23d3f0fe2351dab0,fc581d8bbbea9e411ad61b2c207adfca498c3691..45aa0a87019c4f51802ea896edead95379eab5fb
@@@ -315,8 -315,7 +315,7 @@@ static void show_local_changes(struct o
        init_revisions(&rev, NULL);
        rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
-       if (diff_setup_done(&rev.diffopt) < 0)
-               die(_("diff_setup_done failed"));
+       diff_setup_done(&rev.diffopt);
        add_pending_object(&rev, head, NULL);
        run_diff_index(&rev, 0);
  }
@@@ -343,7 -342,7 +342,7 @@@ static int reset_tree(struct tree *tree
        opts.reset = 1;
        opts.merge = 1;
        opts.fn = oneway_merge;
 -      opts.verbose_update = !o->quiet;
 +      opts.verbose_update = !o->quiet && isatty(2);
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
        parse_tree(tree);
@@@ -420,7 -419,7 +419,7 @@@ static int merge_working_tree(struct ch
                topts.update = 1;
                topts.merge = 1;
                topts.gently = opts->merge && old->commit;
 -              topts.verbose_update = !opts->quiet;
 +              topts.verbose_update = !opts->quiet && isatty(2);
                topts.fn = twoway_merge;
                if (opts->overwrite_ignore) {
                        topts.dir = xcalloc(1, sizeof(*topts.dir));
@@@ -514,6 -513,20 +513,6 @@@ static void report_tracking(struct bran
        strbuf_release(&sb);
  }
  
 -static void detach_advice(const char *old_path, const char *new_name)
 -{
 -      const char fmt[] =
 -      "Note: checking out '%s'.\n\n"
 -      "You are in 'detached HEAD' state. You can look around, make experimental\n"
 -      "changes and commit them, and you can discard any commits you make in this\n"
 -      "state without impacting any branches by performing another checkout.\n\n"
 -      "If you want to create a new branch to retain commits you create, you may\n"
 -      "do so (now or later) by using -b with the checkout command again. Example:\n\n"
 -      "  git checkout -b new_branch_name\n\n";
 -
 -      fprintf(stderr, fmt, new_name);
 -}
 -
  static void update_refs_for_switch(struct checkout_opts *opts,
                                   struct branch_info *old,
                                   struct branch_info *new)
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
                                      opts->new_branch_force ? 1 : 0,
 +                                    opts->quiet,
                                      opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
                           REF_NODEREF, DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path && advice_detached_head)
 -                              detach_advice(old->path, new->name);
 +                              detach_advice(new->name);
                        describe_detached_head(_("HEAD is now at"), new->commit);
                }
        } else if (new->path) { /* Switch branches. */
@@@ -605,7 -617,7 +604,7 @@@ static int add_pending_uninteresting_re
                                         const unsigned char *sha1,
                                         int flags, void *cb_data)
  {
 -      add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
 +      add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
        return 0;
  }
  
@@@ -672,10 -684,10 +671,10 @@@ static void suggest_reattach(struct com
   * HEAD.  If it is not reachable from any ref, this is the last chance
   * for the user to do so without resorting to reflog.
   */
 -static void orphaned_commit_warning(struct commit *commit)
 +static void orphaned_commit_warning(struct commit *old, struct commit *new)
  {
        struct rev_info revs;
 -      struct object *object = &commit->object;
 +      struct object *object = &old->object;
        struct object_array refs;
  
        init_revisions(&revs, NULL);
        add_pending_object(&revs, object, sha1_to_hex(object->sha1));
  
        for_each_ref(add_pending_uninteresting_ref, &revs);
 +      add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING);
  
        refs = revs.pending;
        revs.leak_pending = 1;
  
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
 -      if (!(commit->object.flags & UNINTERESTING))
 -              suggest_reattach(commit, &revs);
 +      if (!(old->object.flags & UNINTERESTING))
 +              suggest_reattach(old, &revs);
        else
 -              describe_detached_head(_("Previous HEAD position was"), commit);
 +              describe_detached_head(_("Previous HEAD position was"), old);
  
        clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
        free(refs.objects);
@@@ -732,7 -743,7 +731,7 @@@ static int switch_branches(struct check
        }
  
        if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 -              orphaned_commit_warning(old.commit);
 +              orphaned_commit_warning(old.commit, new->commit);
  
        update_refs_for_switch(opts, &old, new);
  
@@@ -915,8 -926,6 +914,8 @@@ static int switch_unborn_to_new_branch(
        int status;
        struct strbuf branch_ref = STRBUF_INIT;
  
 +      if (!opts->new_branch)
 +              die(_("You are on a branch yet to be born"));
        strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
        status = create_symref("HEAD", branch_ref.buf, "checkout -b");
        strbuf_release(&branch_ref);
@@@ -1094,7 -1103,7 +1093,7 @@@ int cmd_checkout(int argc, const char *
        if (opts.writeout_stage)
                die(_("--ours/--theirs is incompatible with switching branches."));
  
 -      if (!new.commit) {
 +      if (!new.commit && opts.new_branch) {
                unsigned char rev[20];
                int flag;
  
diff --combined builtin/diff.c
index bf722987526f58f507acda2ea2f8da06da83d6ca,a2c45dbdc632598e718db48c27880bde6ac7ded9..9650be2c50e6f79dbe033fae39f490d49c6318da
@@@ -29,8 -29,6 +29,8 @@@ static void stuff_change(struct diff_op
                         unsigned old_mode, unsigned new_mode,
                         const unsigned char *old_sha1,
                         const unsigned char *new_sha1,
 +                       int old_sha1_valid,
 +                       int new_sha1_valid,
                         const char *old_name,
                         const char *new_name)
  {
@@@ -56,8 -54,8 +56,8 @@@
  
        one = alloc_filespec(old_name);
        two = alloc_filespec(new_name);
 -      fill_filespec(one, old_sha1, old_mode);
 -      fill_filespec(two, new_sha1, new_mode);
 +      fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
 +      fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
  
        diff_queue(&diff_queued_diff, one, two);
  }
@@@ -86,7 -84,6 +86,7 @@@ static int builtin_diff_b_f(struct rev_
        stuff_change(&revs->diffopt,
                     blob[0].mode, canon_mode(st.st_mode),
                     blob[0].sha1, null_sha1,
 +                   1, 0,
                     path, path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
@@@ -111,7 -108,6 +111,7 @@@ static int builtin_diff_blobs(struct re
        stuff_change(&revs->diffopt,
                     blob[0].mode, blob[1].mode,
                     blob[0].sha1, blob[1].sha1,
 +                   1, 1,
                     blob[0].name, blob[1].name);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
@@@ -289,10 -285,6 +289,10 @@@ int cmd_diff(int argc, const char **arg
        /* Otherwise, we are doing the usual "git" diff */
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
  
 +      /* Scale to real terminal size and respect statGraphWidth config */
 +      rev.diffopt.stat_width = -1;
 +      rev.diffopt.stat_graph_width = -1;
 +
        /* Default to let external and textconv be used */
        DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
        DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format) {
                rev.diffopt.output_format = DIFF_FORMAT_PATCH;
-               if (diff_setup_done(&rev.diffopt) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&rev.diffopt);
        }
  
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
  
 -      /*
 -       * If the user asked for our exit code then don't start a
 -       * pager or we would end up reporting its exit code instead.
 -       */
 -      if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) &&
 -          check_pager_config("diff") != 0)
 -              setup_pager();
 +      setup_diff_pager(&rev.diffopt);
  
        /*
         * Do we have --cached and not have a pending object, then
                                add_head_to_pending(&rev);
                                if (!rev.pending.nr) {
                                        struct tree *tree;
 -                                      tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN);
 +                                      tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
                                        add_pending_object(&rev, &tree->object, "HEAD");
                                }
                                break;
                refresh_index_quietly();
        return result;
  }
 +
 +void setup_diff_pager(struct diff_options *opt)
 +{
 +      /*
 +       * If the user asked for our exit code, then either they want --quiet
 +       * or --exit-code. We should definitely not bother with a pager in the
 +       * former case, as we will generate no output. Since we still properly
 +       * report our exit code even when a pager is run, we _could_ run a
 +       * pager with --exit-code. But since we have not done so historically,
 +       * and because it is easy to find people oneline advising "git diff
 +       * --exit-code" in hooks and other scripts, we do not do so.
 +       */
 +      if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
 +          check_pager_config("diff") != 0)
 +              setup_pager();
 +}
diff --combined builtin/merge.c
index dd50a0c57b4d73d03cc84afd90594a56616d4953,99825d63c97012989a117e813602668cbdaf37f6..e81fde6d79e8896235c4a3901bc303f6094a9e55
@@@ -52,6 -52,7 +52,6 @@@ static int fast_forward_only, option_ed
  static int allow_trivial = 1, have_message;
  static int overwrite_ignore = 1;
  static struct strbuf merge_msg = STRBUF_INIT;
 -static struct commit_list *remoteheads;
  static struct strategy **use_strategies;
  static size_t use_strategies_nr, use_strategies_alloc;
  static const char **xopts;
@@@ -317,7 -318,7 +317,7 @@@ static void finish_up_to_date(const cha
        drop_save();
  }
  
 -static void squash_message(struct commit *commit)
 +static void squash_message(struct commit *commit, struct commit_list *remoteheads)
  {
        struct rev_info rev;
        struct strbuf out = STRBUF_INIT;
  }
  
  static void finish(struct commit *head_commit,
 +                 struct commit_list *remoteheads,
                   const unsigned char *new_head, const char *msg)
  {
        struct strbuf reflog_message = STRBUF_INIT;
                        getenv("GIT_REFLOG_ACTION"), msg);
        }
        if (squash) {
 -              squash_message(head_commit);
 +              squash_message(head_commit, remoteheads);
        } else {
                if (verbosity >= 0 && !merge_msg.len)
                        printf(_("No merge message -- not updating HEAD\n"));
        if (new_head && show_diffstat) {
                struct diff_options opts;
                diff_setup(&opts);
 +              opts.stat_width = -1; /* use full terminal width */
 +              opts.stat_graph_width = -1; /* respect statGraphWidth config */
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
-               if (diff_setup_done(&opts) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&opts);
                diff_tree_sha1(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
@@@ -683,7 -680,6 +682,7 @@@ int try_merge_command(const char *strat
  }
  
  static int try_merge_strategy(const char *strategy, struct commit_list *common,
 +                            struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
  {
        int index_fd;
@@@ -877,14 -873,14 +876,14 @@@ static void read_merge_msg(struct strbu
                die_errno(_("Could not read from '%s'"), filename);
  }
  
 -static void write_merge_state(void);
 -static void abort_commit(const char *err_msg)
 +static void write_merge_state(struct commit_list *);
 +static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
  {
        if (err_msg)
                error("%s", err_msg);
        fprintf(stderr,
                _("Not committing merge; use 'git commit' to complete the merge.\n"));
 -      write_merge_state();
 +      write_merge_state(remoteheads);
        exit(1);
  }
  
@@@ -895,7 -891,7 +894,7 @@@ N_("Please enter a commit message to ex
     "Lines starting with '#' will be ignored, and an empty message aborts\n"
     "the commit.\n");
  
 -static void prepare_to_commit(void)
 +static void prepare_to_commit(struct commit_list *remoteheads)
  {
        struct strbuf msg = STRBUF_INIT;
        const char *comment = _(merge_editor_comment);
        write_merge_msg(&msg);
        run_hook(get_index_file(), "prepare-commit-msg",
                 git_path("MERGE_MSG"), "merge", NULL, NULL);
 -      if (option_edit) {
 +      if (0 < option_edit) {
                if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
 -                      abort_commit(NULL);
 +                      abort_commit(remoteheads, NULL);
        }
        read_merge_msg(&msg);
 -      stripspace(&msg, option_edit);
 +      stripspace(&msg, 0 < option_edit);
        if (!msg.len)
 -              abort_commit(_("Empty commit message."));
 +              abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
        strbuf_addbuf(&merge_msg, &msg);
        strbuf_release(&msg);
  }
  
 -static int merge_trivial(struct commit *head)
 +static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
  {
        unsigned char result_tree[20], result_commit[20];
        struct commit_list *parent = xmalloc(sizeof(*parent));
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
 -      prepare_to_commit();
 +      prepare_to_commit(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
                        sign_commit))
                die(_("failed to write commit object"));
 -      finish(head, result_commit, "In-index merge");
 +      finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
        return 0;
  }
  
  static int finish_automerge(struct commit *head,
 +                          int head_subsumed,
                            struct commit_list *common,
 +                          struct commit_list *remoteheads,
                            unsigned char *result_tree,
                            const char *wt_strategy)
  {
 -      struct commit_list *parents = NULL, *j;
 +      struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
        unsigned char result_commit[20];
  
        free_commit_list(common);
 -      if (allow_fast_forward) {
 -              parents = remoteheads;
 +      parents = remoteheads;
 +      if (!head_subsumed || !allow_fast_forward)
                commit_list_insert(head, &parents);
 -              parents = reduce_heads(parents);
 -      } else {
 -              struct commit_list **pptr = &parents;
 -
 -              pptr = &commit_list_insert(head,
 -                              pptr)->next;
 -              for (j = remoteheads; j; j = j->next)
 -                      pptr = &commit_list_insert(j->item, pptr)->next;
 -      }
        strbuf_addch(&merge_msg, '\n');
 -      prepare_to_commit();
 -      free_commit_list(remoteheads);
 +      prepare_to_commit(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parents, result_commit,
                        NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
 -      finish(head, result_commit, buf.buf);
 +      finish(head, remoteheads, result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
@@@ -1065,7 -1069,7 +1064,7 @@@ static int setup_with_upstream(const ch
        return i;
  }
  
 -static void write_merge_state(void)
 +static void write_merge_state(struct commit_list *remoteheads)
  {
        const char *filename;
        int fd;
@@@ -1130,39 -1134,6 +1129,39 @@@ static int default_edit_option(void
                st_stdin.st_mode == st_stdout.st_mode);
  }
  
 +static struct commit_list *collect_parents(struct commit *head_commit,
 +                                         int *head_subsumed,
 +                                         int argc, const char **argv)
 +{
 +      int i;
 +      struct commit_list *remoteheads = NULL, *parents, *next;
 +      struct commit_list **remotes = &remoteheads;
 +
 +      if (head_commit)
 +              remotes = &commit_list_insert(head_commit, remotes)->next;
 +      for (i = 0; i < argc; i++) {
 +              struct commit *commit = get_merge_parent(argv[i]);
 +              if (!commit)
 +                      die(_("%s - not something we can merge"), argv[i]);
 +              remotes = &commit_list_insert(commit, remotes)->next;
 +      }
 +      *remotes = NULL;
 +
 +      parents = reduce_heads(remoteheads);
 +
 +      *head_subsumed = 1; /* we will flip this to 0 when we find it */
 +      for (remoteheads = NULL, remotes = &remoteheads;
 +           parents;
 +           parents = next) {
 +              struct commit *commit = parents->item;
 +              next = parents->next;
 +              if (commit == head_commit)
 +                      *head_subsumed = 0;
 +              else
 +                      remotes = &commit_list_insert(commit, remotes)->next;
 +      }
 +      return remoteheads;
 +}
  
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
 -      int flag, i, ret = 0;
 +      int flag, 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 **remotes = &remoteheads;
 +      struct commit_list *remoteheads, *p;
        void *branch_to_free;
  
        if (argc == 2 && !strcmp(argv[1], "-h"))
                head_arg = argv[1];
                argv += 2;
                argc -= 2;
 +              remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
        } else if (!head_commit) {
                struct commit *remote_head;
                /*
                if (!allow_fast_forward)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
 -              remote_head = get_merge_parent(argv[0]);
 +              remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
 +              remote_head = remoteheads->item;
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
                read_empty(remote_head->object.sha1, 0);
                 * the standard merge summary message to be appended
                 * to the given message.
                 */
 -              for (i = 0; i < argc; i++)
 -                      merge_name(argv[i], &merge_names);
 +              remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
 +              for (p = remoteheads; p; p = p->next)
 +                      merge_name(merge_remote_util(p->item)->name, &merge_names);
  
                if (!have_message || shortlog_len) {
                        struct fmt_merge_msg_opts opts;
                        builtin_merge_options);
  
        strbuf_addstr(&buf, "merge");
 -      for (i = 0; i < argc; i++)
 -              strbuf_addf(&buf, " %s", argv[i]);
 +      for (p = remoteheads; p; p = p->next)
 +              strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
        setenv("GIT_REFLOG_ACTION", buf.buf, 0);
        strbuf_reset(&buf);
  
 -      for (i = 0; i < argc; i++) {
 -              struct commit *commit = get_merge_parent(argv[i]);
 -              if (!commit)
 -                      die(_("%s - not something we can merge"), argv[i]);
 -              remotes = &commit_list_insert(commit, remotes)->next;
 +      for (p = remoteheads; p; p = p->next) {
 +              struct commit *commit = p->item;
                strbuf_addf(&buf, "GITHEAD_%s",
                            sha1_to_hex(commit->object.sha1));
 -              setenv(buf.buf, argv[i], 1);
 +              setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
                if (!fast_forward_only &&
                    merge_remote_util(commit) &&
                    merge_remote_util(commit)->obj &&
 -                  merge_remote_util(commit)->obj->type == OBJ_TAG) {
 -                      if (option_edit < 0)
 -                              option_edit = default_edit_option();
 +                  merge_remote_util(commit)->obj->type == OBJ_TAG)
                        allow_fast_forward = 0;
 -              }
        }
  
        if (option_edit < 0)
 -              option_edit = 0;
 +              option_edit = default_edit_option();
  
        if (!use_strategies) {
 -              if (!remoteheads->next)
 +              if (!remoteheads)
 +                      ; /* already up-to-date */
 +              else if (!remoteheads->next)
                        add_strategies(pull_twohead, DEFAULT_TWOHEAD);
                else
                        add_strategies(pull_octopus, DEFAULT_OCTOPUS);
                        allow_trivial = 0;
        }
  
 -      if (!remoteheads->next)
 +      if (!remoteheads)
 +              ; /* already up-to-date */
 +      else if (!remoteheads->next)
                common = get_merge_bases(head_commit, remoteheads->item, 1);
        else {
                struct commit_list *list = remoteheads;
        update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
                   NULL, 0, DIE_ON_ERR);
  
 -      if (!common)
 +      if (remoteheads && !common)
                ; /* No common ancestors found. We need a real merge. */
 -      else if (!remoteheads->next && !common->next &&
 -                      common->item == remoteheads->item) {
 +      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.
                        goto done;
                }
  
 -              finish(head_commit, commit->object.sha1, msg.buf);
 +              finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
                refresh_cache(REFRESH_QUIET);
                if (allow_trivial && !fast_forward_only) {
                        /* See if it is really trivial. */
 -                      git_committer_info(IDENT_ERROR_ON_NO_NAME);
 +                      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)) {
 -                              ret = merge_trivial(head_commit);
 +                              ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
                        printf(_("Nope.\n"));
                die(_("Not possible to fast-forward, aborting."));
  
        /* We are going to make a new commit. */
 -      git_committer_info(IDENT_ERROR_ON_NO_NAME);
 +      git_committer_info(IDENT_STRICT);
  
        /*
         * At this point, we need a real merge.  No matter what strategy
                wt_strategy = use_strategies[i]->name;
  
                ret = try_merge_strategy(use_strategies[i]->name,
 -                                       common, head_commit, head_arg);
 +                                       common, remoteheads,
 +                                       head_commit, head_arg);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
         * auto resolved the merge cleanly.
         */
        if (automerge_was_ok) {
 -              ret = finish_automerge(head_commit, common, result_tree,
 -                                     wt_strategy);
 +              ret = finish_automerge(head_commit, head_subsumed,
 +                                     common, remoteheads,
 +                                     result_tree, wt_strategy);
                goto done;
        }
  
                restore_state(head_commit->object.sha1, stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
 -              try_merge_strategy(best_strategy, common, head_commit, head_arg);
 +              try_merge_strategy(best_strategy, common, remoteheads,
 +                                 head_commit, head_arg);
        }
  
        if (squash)
 -              finish(head_commit, NULL, NULL);
 +              finish(head_commit, remoteheads, NULL, NULL);
        else
 -              write_merge_state();
 +              write_merge_state(remoteheads);
  
        if (merge_was_ok)
                fprintf(stderr, _("Automatic merge went well; "
diff --combined diff-no-index.c
index 0b46a0f79f1376a935e4a0a408a224de711b60cb,b245ca8ad84732c8a5c9a227bd341b970ce8a62a..74da659368e4db67715a9c81dc7a5e0edcd3bcce
@@@ -32,13 -32,6 +32,13 @@@ static int read_directory(const char *p
        return 0;
  }
  
 +/*
 + * This should be "(standard input)" or something, but it will
 + * probably expose many more breakages in the way no-index code
 + * is bolted onto the diff callchain.
 + */
 +static const char file_from_standard_input[] = "-";
 +
  static int get_mode(const char *path, int *mode)
  {
        struct stat st;
@@@ -49,7 -42,7 +49,7 @@@
        else if (!strcasecmp(path, "nul"))
                *mode = 0;
  #endif
 -      else if (!strcmp(path, "-"))
 +      else if (path == file_from_standard_input)
                *mode = create_ce_mode(0666);
        else if (lstat(path, &st))
                return error("Could not access '%s'", path);
        return 0;
  }
  
 +static int populate_from_stdin(struct diff_filespec *s)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      size_t size = 0;
 +
 +      if (strbuf_read(&buf, 0, 0) < 0)
 +              return error("error while reading from stdin %s",
 +                                   strerror(errno));
 +
 +      s->should_munmap = 0;
 +      s->data = strbuf_detach(&buf, &size);
 +      s->size = size;
 +      s->should_free = 1;
 +      s->is_stdin = 1;
 +      return 0;
 +}
 +
 +static struct diff_filespec *noindex_filespec(const char *name, int mode)
 +{
 +      struct diff_filespec *s;
 +
 +      if (!name)
 +              name = "/dev/null";
 +      s = alloc_filespec(name);
 +      fill_filespec(s, null_sha1, 0, mode);
 +      if (name == file_from_standard_input)
 +              populate_from_stdin(s);
 +      return s;
 +}
 +
  static int queue_diff(struct diff_options *o,
 -              const char *name1, const char *name2)
 +                    const char *name1, const char *name2)
  {
        int mode1 = 0, mode2 = 0;
  
                return error("file/directory conflict: %s, %s", name1, name2);
  
        if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
 -              char buffer1[PATH_MAX], buffer2[PATH_MAX];
 +              struct strbuf buffer1 = STRBUF_INIT;
 +              struct strbuf buffer2 = STRBUF_INIT;
                struct string_list p1 = STRING_LIST_INIT_DUP;
                struct string_list p2 = STRING_LIST_INIT_DUP;
 -              int len1 = 0, len2 = 0, i1, i2, ret = 0;
 +              int i1, i2, ret = 0;
 +              size_t len1 = 0, len2 = 0;
  
                if (name1 && read_directory(name1, &p1))
                        return -1;
                }
  
                if (name1) {
 -                      len1 = strlen(name1);
 -                      if (len1 > 0 && name1[len1 - 1] == '/')
 -                              len1--;
 -                      memcpy(buffer1, name1, len1);
 -                      buffer1[len1++] = '/';
 +                      strbuf_addstr(&buffer1, name1);
 +                      if (buffer1.len && buffer1.buf[buffer1.len - 1] != '/')
 +                              strbuf_addch(&buffer1, '/');
 +                      len1 = buffer1.len;
                }
  
                if (name2) {
 -                      len2 = strlen(name2);
 -                      if (len2 > 0 && name2[len2 - 1] == '/')
 -                              len2--;
 -                      memcpy(buffer2, name2, len2);
 -                      buffer2[len2++] = '/';
 +                      strbuf_addstr(&buffer2, name2);
 +                      if (buffer2.len && buffer2.buf[buffer2.len - 1] != '/')
 +                              strbuf_addch(&buffer2, '/');
 +                      len2 = buffer2.len;
                }
  
                for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
                        const char *n1, *n2;
                        int comp;
  
 +                      strbuf_setlen(&buffer1, len1);
 +                      strbuf_setlen(&buffer2, len2);
 +
                        if (i1 == p1.nr)
                                comp = 1;
                        else if (i2 == p2.nr)
                                comp = -1;
                        else
 -                              comp = strcmp(p1.items[i1].string,
 -                                      p2.items[i2].string);
 +                              comp = strcmp(p1.items[i1].string, p2.items[i2].string);
  
                        if (comp > 0)
                                n1 = NULL;
                        else {
 -                              n1 = buffer1;
 -                              strncpy(buffer1 + len1, p1.items[i1++].string,
 -                                              PATH_MAX - len1);
 +                              strbuf_addstr(&buffer1, p1.items[i1++].string);
 +                              n1 = buffer1.buf;
                        }
  
                        if (comp < 0)
                                n2 = NULL;
                        else {
 -                              n2 = buffer2;
 -                              strncpy(buffer2 + len2, p2.items[i2++].string,
 -                                              PATH_MAX - len2);
 +                              strbuf_addstr(&buffer2, p2.items[i2++].string);
 +                              n2 = buffer2.buf;
                        }
  
                        ret = queue_diff(o, n1, n2);
                }
                string_list_clear(&p1, 0);
                string_list_clear(&p2, 0);
 +              strbuf_release(&buffer1);
 +              strbuf_release(&buffer2);
  
                return ret;
        } else {
                        tmp_c = name1; name1 = name2; name2 = tmp_c;
                }
  
 -              if (!name1)
 -                      name1 = "/dev/null";
 -              if (!name2)
 -                      name2 = "/dev/null";
 -              d1 = alloc_filespec(name1);
 -              d2 = alloc_filespec(name2);
 -              fill_filespec(d1, null_sha1, mode1);
 -              fill_filespec(d2, null_sha1, mode2);
 -
 +              d1 = noindex_filespec(name1, mode1);
 +              d2 = noindex_filespec(name2, mode2);
                diff_queue(&diff_queued_diff, d1, d2);
                return 0;
        }
  }
  
 -static int path_outside_repo(const char *path)
 -{
 -      const char *work_tree;
 -      size_t len;
 -
 -      if (!is_absolute_path(path))
 -              return 0;
 -      work_tree = get_git_work_tree();
 -      if (!work_tree)
 -              return 1;
 -      len = strlen(work_tree);
 -      if (strncmp(path, work_tree, len) ||
 -          (path[len] != '\0' && path[len] != '/'))
 -              return 1;
 -      return 0;
 -}
 -
  void diff_no_index(struct rev_info *revs,
                   int argc, const char **argv,
                   int nongit, const char *prefix)
  {
 -      int i;
 +      int i, prefixlen;
        int no_index = 0;
        unsigned options = 0;
 +      const char *paths[2];
  
        /* Were we asked to do --no-index explicitly? */
        for (i = 1; i < argc; i++) {
                 * a colourful "diff" replacement.
                 */
                if ((argc != i + 2) ||
 -                  (!path_outside_repo(argv[i]) &&
 -                   !path_outside_repo(argv[i+1])))
 +                  (path_inside_repo(prefix, argv[i]) &&
 +                   path_inside_repo(prefix, argv[i+1])))
                        return;
        }
        if (argc != i + 2)
                }
        }
  
 -      /*
 -       * If the user asked for our exit code then don't start a
 -       * pager or we would end up reporting its exit code instead.
 -       */
 -      if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
 -              setup_pager();
 -
 -      if (prefix) {
 -              int len = strlen(prefix);
 -              const char *paths[3];
 -              memset(paths, 0, sizeof(paths));
 -
 -              for (i = 0; i < 2; i++) {
 -                      const char *p = argv[argc - 2 + i];
 +      prefixlen = prefix ? strlen(prefix) : 0;
 +      for (i = 0; i < 2; i++) {
 +              const char *p = argv[argc - 2 + i];
 +              if (!strcmp(p, "-"))
                        /*
 -                       * stdin should be spelled as '-'; if you have
 -                       * path that is '-', spell it as ./-.
 +                       * stdin should be spelled as "-"; if you have
 +                       * path that is "-", spell it as "./-".
                         */
 -                      p = (strcmp(p, "-")
 -                           ? xstrdup(prefix_filename(prefix, len, p))
 -                           : p);
 -                      paths[i] = p;
 -              }
 -              diff_tree_setup_paths(paths, &revs->diffopt);
 +                      p = file_from_standard_input;
 +              else if (prefixlen)
 +                      p = xstrdup(prefix_filename(prefix, prefixlen, p));
 +              paths[i] = p;
        }
 -      else
 -              diff_tree_setup_paths(argv + argc - 2, &revs->diffopt);
        revs->diffopt.skip_stat_unmatch = 1;
        if (!revs->diffopt.output_format)
                revs->diffopt.output_format = DIFF_FORMAT_PATCH;
  
 -      DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
        DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
  
        revs->max_count = -2;
-       if (diff_setup_done(&revs->diffopt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&revs->diffopt);
  
 -      if (queue_diff(&revs->diffopt, revs->diffopt.pathspec.raw[0],
 -                     revs->diffopt.pathspec.raw[1]))
 +      setup_diff_pager(&revs->diffopt);
 +      DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
 +
 +      if (queue_diff(&revs->diffopt, paths[0], paths[1]))
                exit(1);
        diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
        diffcore_std(&revs->diffopt);
         * The return code for --no-index imitates diff(1):
         * 0 = no changes, 1 = changes, else error
         */
 -      exit(revs->diffopt.found_changes);
 +      exit(diff_result_code(&revs->diffopt, 0));
  }
diff --combined diff.c
index 15125e8894c61f895c8f054cac0e8890210d3b7b,df18447920a6a463d3a04d59c926a636fa0d07f1..37bf00270023d6f65084508ee711a21430b10371
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -31,7 -31,6 +31,7 @@@ static const char *external_diff_cmd_cf
  int diff_auto_refresh_index = 1;
  static int diff_mnemonic_prefix;
  static int diff_no_prefix;
 +static int diff_stat_graph_width;
  static int diff_dirstat_permille_default = 30;
  static struct diff_options default_diff_options;
  
@@@ -157,10 -156,6 +157,10 @@@ int git_diff_ui_config(const char *var
                diff_no_prefix = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "diff.statgraphwidth")) {
 +              diff_stat_graph_width = git_config_int(var, value);
 +              return 0;
 +      }
        if (!strcmp(var, "diff.external"))
                return git_config_string(&external_diff_cmd_cfg, var, value);
        if (!strcmp(var, "diff.wordregex"))
@@@ -182,8 -177,11 +182,8 @@@ int git_diff_basic_config(const char *v
                return 0;
        }
  
 -      switch (userdiff_config(var, value)) {
 -              case 0: break;
 -              case -1: return -1;
 -              default: return 0;
 -      }
 +      if (userdiff_config(var, value) < 0)
 +              return -1;
  
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
@@@ -989,74 -987,10 +989,74 @@@ static void diff_words_flush(struct emi
                diff_words_show(ecbdata->diff_words);
  }
  
 +static void diff_filespec_load_driver(struct diff_filespec *one)
 +{
 +      /* Use already-loaded driver */
 +      if (one->driver)
 +              return;
 +
 +      if (S_ISREG(one->mode))
 +              one->driver = userdiff_find_by_path(one->path);
 +
 +      /* Fallback to default settings */
 +      if (!one->driver)
 +              one->driver = userdiff_find_by_name("default");
 +}
 +
 +static const char *userdiff_word_regex(struct diff_filespec *one)
 +{
 +      diff_filespec_load_driver(one);
 +      return one->driver->word_regex;
 +}
 +
 +static void init_diff_words_data(struct emit_callback *ecbdata,
 +                               struct diff_options *orig_opts,
 +                               struct diff_filespec *one,
 +                               struct diff_filespec *two)
 +{
 +      int i;
 +      struct diff_options *o = xmalloc(sizeof(struct diff_options));
 +      memcpy(o, orig_opts, sizeof(struct diff_options));
 +
 +      ecbdata->diff_words =
 +              xcalloc(1, sizeof(struct diff_words_data));
 +      ecbdata->diff_words->type = o->word_diff;
 +      ecbdata->diff_words->opt = o;
 +      if (!o->word_regex)
 +              o->word_regex = userdiff_word_regex(one);
 +      if (!o->word_regex)
 +              o->word_regex = userdiff_word_regex(two);
 +      if (!o->word_regex)
 +              o->word_regex = diff_word_regex_cfg;
 +      if (o->word_regex) {
 +              ecbdata->diff_words->word_regex = (regex_t *)
 +                      xmalloc(sizeof(regex_t));
 +              if (regcomp(ecbdata->diff_words->word_regex,
 +                          o->word_regex,
 +                          REG_EXTENDED | REG_NEWLINE))
 +                      die ("Invalid regular expression: %s",
 +                           o->word_regex);
 +      }
 +      for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
 +              if (o->word_diff == diff_words_styles[i].type) {
 +                      ecbdata->diff_words->style =
 +                              &diff_words_styles[i];
 +                      break;
 +              }
 +      }
 +      if (want_color(o->use_color)) {
 +              struct diff_words_style *st = ecbdata->diff_words->style;
 +              st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
 +              st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
 +              st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
 +      }
 +}
 +
  static void free_diff_words_data(struct emit_callback *ecbdata)
  {
        if (ecbdata->diff_words) {
                diff_words_flush(ecbdata);
 +              free (ecbdata->diff_words->opt);
                free (ecbdata->diff_words->minus.text.ptr);
                free (ecbdata->diff_words->minus.orig);
                free (ecbdata->diff_words->plus.text.ptr);
@@@ -1443,8 -1377,8 +1443,8 @@@ static void show_stats(struct diffstat_
  {
        int i, len, add, del, adds = 0, dels = 0;
        uintmax_t max_change = 0, max_len = 0;
 -      int total_files = data->nr;
 -      int width, name_width, count;
 +      int total_files = data->nr, count;
 +      int width, name_width, graph_width, number_width = 0, bin_width = 0;
        const char *reset, *add_c, *del_c;
        const char *line_prefix = "";
        int extra_shown = 0;
                line_prefix = msg->buf;
        }
  
 -      width = options->stat_width ? options->stat_width : 80;
 -      name_width = options->stat_name_width ? options->stat_name_width : 50;
        count = options->stat_count ? options->stat_count : data->nr;
  
 -      /* Sanity: give at least 5 columns to the graph,
 -       * but leave at least 10 columns for the name.
 -       */
 -      if (width < 25)
 -              width = 25;
 -      if (name_width < 10)
 -              name_width = 10;
 -      else if (width < name_width + 15)
 -              name_width = width - 15;
 -
 -      /* Find the longest filename and max number of changes */
        reset = diff_get_color_opt(options, DIFF_RESET);
        add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
        del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
  
 +      /*
 +       * Find the longest filename and max number of changes
 +       */
        for (i = 0; (i < count) && (i < data->nr); i++) {
                struct diffstat_file *file = data->files[i];
                uintmax_t change = file->added + file->deleted;
                if (max_len < len)
                        max_len = len;
  
 -              if (file->is_binary || file->is_unmerged)
 +              if (file->is_unmerged) {
 +                      /* "Unmerged" is 8 characters */
 +                      bin_width = bin_width < 8 ? 8 : bin_width;
 +                      continue;
 +              }
 +              if (file->is_binary) {
 +                      /* "Bin XXX -> YYY bytes" */
 +                      int w = 14 + decimal_width(file->added)
 +                              + decimal_width(file->deleted);
 +                      bin_width = bin_width < w ? w : bin_width;
 +                      /* Display change counts aligned with "Bin" */
 +                      number_width = 3;
                        continue;
 +              }
 +
                if (max_change < change)
                        max_change = change;
        }
        count = i; /* min(count, data->nr) */
  
 -      /* Compute the width of the graph part;
 -       * 10 is for one blank at the beginning of the line plus
 -       * " | count " between the name and the graph.
 +      /*
 +       * We have width = stat_width or term_columns() columns total.
 +       * We want a maximum of min(max_len, stat_name_width) for the name part.
 +       * We want a maximum of min(max_change, stat_graph_width) for the +- part.
 +       * We also need 1 for " " and 4 + decimal_width(max_change)
 +       * for " | NNNN " and one the empty column at the end, altogether
 +       * 6 + decimal_width(max_change).
 +       *
 +       * If there's not enough space, we will use the smaller of
 +       * stat_name_width (if set) and 5/8*width for the filename,
 +       * and the rest for constant elements + graph part, but no more
 +       * than stat_graph_width for the graph part.
 +       * (5/8 gives 50 for filename and 30 for the constant parts + graph
 +       * for the standard terminal size).
         *
 -       * From here on, name_width is the width of the name area,
 -       * and width is the width of the graph area.
 +       * In other words: stat_width limits the maximum width, and
 +       * stat_name_width fixes the maximum width of the filename,
 +       * and is also used to divide available columns if there
 +       * aren't enough.
 +       *
 +       * Binary files are displayed with "Bin XXX -> YYY bytes"
 +       * instead of the change count and graph. This part is treated
 +       * similarly to the graph part, except that it is not
 +       * "scaled". If total width is too small to accomodate the
 +       * guaranteed minimum width of the filename part and the
 +       * separators and this message, this message will "overflow"
 +       * making the line longer than the maximum width.
         */
 -      name_width = (name_width < max_len) ? name_width : max_len;
 -      if (width < (name_width + 10) + max_change)
 -              width = width - (name_width + 10);
 +
 +      if (options->stat_width == -1)
 +              width = term_columns() - options->output_prefix_length;
        else
 -              width = max_change;
 +              width = options->stat_width ? options->stat_width : 80;
 +      number_width = decimal_width(max_change) > number_width ?
 +              decimal_width(max_change) : number_width;
 +
 +      if (options->stat_graph_width == -1)
 +              options->stat_graph_width = diff_stat_graph_width;
  
 +      /*
 +       * Guarantee 3/8*16==6 for the graph part
 +       * and 5/8*16==10 for the filename part
 +       */
 +      if (width < 16 + 6 + number_width)
 +              width = 16 + 6 + number_width;
 +
 +      /*
 +       * First assign sizes that are wanted, ignoring available width.
 +       * strlen("Bin XXX -> YYY bytes") == bin_width, and the part
 +       * starting from "XXX" should fit in graph_width.
 +       */
 +      graph_width = max_change + 4 > bin_width ? max_change : bin_width - 4;
 +      if (options->stat_graph_width &&
 +          options->stat_graph_width < graph_width)
 +              graph_width = options->stat_graph_width;
 +
 +      name_width = (options->stat_name_width > 0 &&
 +                    options->stat_name_width < max_len) ?
 +              options->stat_name_width : max_len;
 +
 +      /*
 +       * Adjust adjustable widths not to exceed maximum width
 +       */
 +      if (name_width + number_width + 6 + graph_width > width) {
 +              if (graph_width > width * 3/8 - number_width - 6) {
 +                      graph_width = width * 3/8 - number_width - 6;
 +                      if (graph_width < 6)
 +                              graph_width = 6;
 +              }
 +
 +              if (options->stat_graph_width &&
 +                  graph_width > options->stat_graph_width)
 +                      graph_width = options->stat_graph_width;
 +              if (name_width > width - number_width - 6 - graph_width)
 +                      name_width = width - number_width - 6 - graph_width;
 +              else
 +                      graph_width = width - number_width - 6 - name_width;
 +      }
 +
 +      /*
 +       * From here name_width is the width of the name area,
 +       * and graph_width is the width of the graph area.
 +       * max_change is used to scale graph properly.
 +       */
        for (i = 0; i < count; i++) {
                const char *prefix = "";
                char *name = data->files[i]->print_name;
                if (data->files[i]->is_binary) {
                        fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
 -                      fprintf(options->file, "  Bin ");
 -                      fprintf(options->file, "%s%"PRIuMAX"%s",
 +                      fprintf(options->file, " %*s", number_width, "Bin");
 +                      if (!added && !deleted) {
 +                              putc('\n', options->file);
 +                              continue;
 +                      }
 +                      fprintf(options->file, " %s%"PRIuMAX"%s",
                                del_c, deleted, reset);
                        fprintf(options->file, " -> ");
                        fprintf(options->file, "%s%"PRIuMAX"%s",
                else if (data->files[i]->is_unmerged) {
                        fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
 -                      fprintf(options->file, "  Unmerged\n");
 +                      fprintf(options->file, " Unmerged\n");
                        continue;
                }
  
                adds += add;
                dels += del;
  
 -              if (width <= max_change) {
 +              if (graph_width <= max_change) {
                        int total = add + del;
  
 -                      total = scale_linear(add + del, width, max_change);
 +                      total = scale_linear(add + del, graph_width, max_change);
                        if (total < 2 && add && del)
                                /* width >= 2 due to the sanity check */
                                total = 2;
                        if (add < del) {
 -                              add = scale_linear(add, width, max_change);
 +                              add = scale_linear(add, graph_width, max_change);
                                del = total - add;
                        } else {
 -                              del = scale_linear(del, width, max_change);
 +                              del = scale_linear(del, graph_width, max_change);
                                add = total - del;
                        }
                }
                fprintf(options->file, "%s", line_prefix);
                show_name(options->file, prefix, name, len);
 -              fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
 -                              added + deleted ? " " : "");
 +              fprintf(options->file, " %*"PRIuMAX"%s",
 +                      number_width, added + deleted,
 +                      added + deleted ? " " : "");
                show_graph(options->file, '+', add, add_c, reset);
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
@@@ -1693,16 -1548,17 +1693,16 @@@ static void show_shortstats(struct diff
                return;
  
        for (i = 0; i < data->nr; i++) {
 -              if (!data->files[i]->is_binary &&
 -                  !data->files[i]->is_unmerged) {
 -                      int added = data->files[i]->added;
 -                      int deleted= data->files[i]->deleted;
 -                      if (!data->files[i]->is_renamed &&
 -                          (added + deleted == 0)) {
 -                              total_files--;
 -                      } else {
 -                              adds += added;
 -                              dels += deleted;
 -                      }
 +              int added = data->files[i]->added;
 +              int deleted= data->files[i]->deleted;
 +
 +              if (data->files[i]->is_unmerged)
 +                      continue;
 +              if (!data->files[i]->is_renamed && (added + deleted == 0)) {
 +                      total_files--;
 +              } else if (!data->files[i]->is_binary) { /* don't count bytes */
 +                      adds += added;
 +                      dels += deleted;
                }
        }
        if (options->output_prefix) {
@@@ -2160,6 -2016,20 +2160,6 @@@ static void emit_binary_diff(FILE *file
        emit_binary_diff_body(file, two, one, prefix);
  }
  
 -static void diff_filespec_load_driver(struct diff_filespec *one)
 -{
 -      /* Use already-loaded driver */
 -      if (one->driver)
 -              return;
 -
 -      if (S_ISREG(one->mode))
 -              one->driver = userdiff_find_by_path(one->path);
 -
 -      /* Fallback to default settings */
 -      if (!one->driver)
 -              one->driver = userdiff_find_by_name("default");
 -}
 -
  int diff_filespec_is_binary(struct diff_filespec *one)
  {
        if (one->is_binary == -1) {
@@@ -2185,6 -2055,12 +2185,6 @@@ static const struct userdiff_funcname *
        return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
  }
  
 -static const char *userdiff_word_regex(struct diff_filespec *one)
 -{
 -      diff_filespec_load_driver(one);
 -      return one->driver->word_regex;
 -}
 -
  void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
  {
        if (!options->a_prefix)
@@@ -2371,8 -2247,42 +2371,8 @@@ static void builtin_diff(const char *na
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
                else if (!prefixcmp(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
 -              if (o->word_diff) {
 -                      int i;
 -
 -                      ecbdata.diff_words =
 -                              xcalloc(1, sizeof(struct diff_words_data));
 -                      ecbdata.diff_words->type = o->word_diff;
 -                      ecbdata.diff_words->opt = o;
 -                      if (!o->word_regex)
 -                              o->word_regex = userdiff_word_regex(one);
 -                      if (!o->word_regex)
 -                              o->word_regex = userdiff_word_regex(two);
 -                      if (!o->word_regex)
 -                              o->word_regex = diff_word_regex_cfg;
 -                      if (o->word_regex) {
 -                              ecbdata.diff_words->word_regex = (regex_t *)
 -                                      xmalloc(sizeof(regex_t));
 -                              if (regcomp(ecbdata.diff_words->word_regex,
 -                                              o->word_regex,
 -                                              REG_EXTENDED | REG_NEWLINE))
 -                                      die ("Invalid regular expression: %s",
 -                                                      o->word_regex);
 -                      }
 -                      for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
 -                              if (o->word_diff == diff_words_styles[i].type) {
 -                                      ecbdata.diff_words->style =
 -                                              &diff_words_styles[i];
 -                                      break;
 -                              }
 -                      }
 -                      if (want_color(o->use_color)) {
 -                              struct diff_words_style *st = ecbdata.diff_words->style;
 -                              st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
 -                              st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
 -                              st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
 -                      }
 -              }
 +              if (o->word_diff)
 +                      init_diff_words_data(&ecbdata, o, one, two);
                xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
                              &xpp, &xecfg);
                if (o->word_diff)
@@@ -2402,7 -2312,6 +2402,7 @@@ static void builtin_diffstat(const cha
  {
        mmfile_t mf1, mf2;
        struct diffstat_file *data;
 +      int same_contents;
  
        data = diffstat_add(diffstat, name_a, name_b);
  
                return;
        }
  
 +      same_contents = !hashcmp(one->sha1, two->sha1);
 +
        if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
                data->is_binary = 1;
 -              data->added = diff_filespec_size(two);
 -              data->deleted = diff_filespec_size(one);
 +              if (same_contents) {
 +                      data->added = 0;
 +                      data->deleted = 0;
 +              } else {
 +                      data->added = diff_filespec_size(two);
 +                      data->deleted = diff_filespec_size(one);
 +              }
        }
  
        else if (complete_rewrite) {
                data->added = count_lines(two->data, two->size);
        }
  
 -      else {
 +      else if (!same_contents) {
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
                xdemitconf_t xecfg;
@@@ -2541,12 -2443,12 +2541,12 @@@ void free_filespec(struct diff_filespe
  }
  
  void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
 -                 unsigned short mode)
 +                 int sha1_valid, unsigned short mode)
  {
        if (mode) {
                spec->mode = canon_mode(mode);
                hashcpy(spec->sha1, sha1);
 -              spec->sha1_valid = !is_null_sha1(sha1);
 +              spec->sha1_valid = sha1_valid;
        }
  }
  
@@@ -2619,6 -2521,22 +2619,6 @@@ static int reuse_worktree_file(const ch
        return 0;
  }
  
 -static int populate_from_stdin(struct diff_filespec *s)
 -{
 -      struct strbuf buf = STRBUF_INIT;
 -      size_t size = 0;
 -
 -      if (strbuf_read(&buf, 0, 0) < 0)
 -              return error("error while reading from stdin %s",
 -                                   strerror(errno));
 -
 -      s->should_munmap = 0;
 -      s->data = strbuf_detach(&buf, &size);
 -      s->size = size;
 -      s->should_free = 1;
 -      return 0;
 -}
 -
  static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
  {
        int len;
@@@ -2668,6 -2586,9 +2668,6 @@@ int diff_populate_filespec(struct diff_
                struct stat st;
                int fd;
  
 -              if (!strcmp(s->path, "-"))
 -                      return populate_from_stdin(s);
 -
                if (lstat(s->path, &st) < 0) {
                        if (errno == ENOENT) {
                        err_empty:
@@@ -2992,8 -2913,9 +2992,8 @@@ static void run_diff_cmd(const char *pg
        int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
        int must_show_header = 0;
  
 -      if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
 -              pgm = NULL;
 -      else {
 +
 +      if (DIFF_OPT_TST(o, ALLOW_EXTERNAL)) {
                struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
                if (drv && drv->external)
                        pgm = drv->external;
@@@ -3028,7 -2950,7 +3028,7 @@@ static void diff_fill_sha1_info(struct 
        if (DIFF_FILE_VALID(one)) {
                if (!one->sha1_valid) {
                        struct stat st;
 -                      if (!strcmp(one->path, "-")) {
 +                      if (one->is_stdin) {
                                hashcpy(one->sha1, null_sha1);
                                return;
                        }
@@@ -3073,9 -2995,6 +3073,9 @@@ static void run_diff(struct diff_filepa
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
  
 +      if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
 +              pgm = NULL;
 +
        if (DIFF_PAIR_UNMERGED(p)) {
                run_diff_cmd(pgm, name, NULL, attr_path,
                             NULL, NULL, NULL, o, p);
@@@ -3172,7 -3091,6 +3172,7 @@@ void diff_setup(struct diff_options *op
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = 3;
 +      DIFF_OPT_SET(options, RENAME_EMPTY);
  
        options->change = diff_change;
        options->add_remove = diff_addremove;
        }
  }
  
int diff_setup_done(struct diff_options *options)
void diff_setup_done(struct diff_options *options)
  {
        int count = 0;
  
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
-       return 0;
  }
  
  static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@@ -3384,7 -3300,6 +3382,7 @@@ static int stat_opt(struct diff_option
        char *end;
        int width = options->stat_width;
        int name_width = options->stat_name_width;
 +      int graph_width = options->stat_graph_width;
        int count = options->stat_count;
        int argcount = 1;
  
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
 +              } else if (!prefixcmp(arg, "-graph-width")) {
 +                      arg += strlen("-graph-width");
 +                      if (*arg == '=')
 +                              graph_width = strtoul(arg + 1, &end, 10);
 +                      else if (!*arg && !av[1])
 +                              die("Option '--stat-graph-width' requires a value");
 +                      else if (!*arg) {
 +                              graph_width = strtoul(av[1], &end, 10);
 +                              argcount = 2;
 +                      }
                } else if (!prefixcmp(arg, "-count")) {
                        arg += strlen("-count");
                        if (*arg == '=')
                return 0;
        options->output_format |= DIFF_FORMAT_DIFFSTAT;
        options->stat_name_width = name_width;
 +      options->stat_graph_width = graph_width;
        options->stat_width = width;
        options->stat_count = count;
        return argcount;
@@@ -3543,10 -3447,6 +3541,10 @@@ int diff_opt_parse(struct diff_options 
        }
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
 +      else if (!strcmp(arg, "--rename-empty"))
 +              DIFF_OPT_SET(options, RENAME_EMPTY);
 +      else if (!strcmp(arg, "--no-rename-empty"))
 +              DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
        else if (!prefixcmp(arg, "--relative=")) {
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--patience"))
 -              DIFF_XDL_SET(options, PATIENCE_DIFF);
 +              options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
 -              DIFF_XDL_SET(options, HISTOGRAM_DIFF);
 +              options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
  
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
@@@ -4440,12 -4340,6 +4438,12 @@@ void diff_flush(struct diff_options *op
  
        if (output_format & DIFF_FORMAT_PATCH) {
                if (separator) {
 +                      if (options->output_prefix) {
 +                              struct strbuf *msg = NULL;
 +                              msg = options->output_prefix(options,
 +                                      options->output_prefix_data);
 +                              fwrite(msg->buf, msg->len, 1, stdout);
 +                      }
                        putc(options->line_termination, options->file);
                        if (options->stat_sep) {
                                /* attach patch instead of inline */
@@@ -4693,7 -4587,6 +4691,7 @@@ static int is_submodule_ignored(const c
  void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
 +                  int sha1_valid,
                    const char *concatpath, unsigned dirty_submodule)
  {
        struct diff_filespec *one, *two;
        two = alloc_filespec(concatpath);
  
        if (addremove != '+')
 -              fill_filespec(one, sha1, mode);
 +              fill_filespec(one, sha1, sha1_valid, mode);
        if (addremove != '-') {
 -              fill_filespec(two, sha1, mode);
 +              fill_filespec(two, sha1, sha1_valid, mode);
                two->dirty_submodule = dirty_submodule;
        }
  
@@@ -4740,7 -4633,6 +4738,7 @@@ void diff_change(struct diff_options *o
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
 +               int old_sha1_valid, int new_sha1_valid,
                 const char *concatpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
                const unsigned char *tmp_c;
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
 +              tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid;
 +                      new_sha1_valid = tmp;
                tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
                        new_dirty_submodule = tmp;
        }
  
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
 -      fill_filespec(one, old_sha1, old_mode);
 -      fill_filespec(two, new_sha1, new_mode);
 +      fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
 +      fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
  
diff --combined diff.h
index 815dd7af5766937ae1b553631a8e7be4d609d8d3,1d0a808bd45daa663bd42000950e0c2edc56ac28..e25addb806a190f138a14ca64190fa5d2f311aaa
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -19,14 -19,12 +19,14 @@@ typedef void (*change_fn_t)(struct diff
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
 +               int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule);
  
  typedef void (*add_remove_fn_t)(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
 +                  int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule);
  
  typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
@@@ -62,7 -60,7 +62,7 @@@ typedef struct strbuf *(*diff_prefix_fn
  #define DIFF_OPT_SILENT_ON_REMOVE    (1 <<  5)
  #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
  #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
 -/* (1 <<  8) unused */
 +#define DIFF_OPT_RENAME_EMPTY        (1 <<  8)
  /* (1 <<  9) unused */
  #define DIFF_OPT_HAS_CHANGES         (1 << 10)
  #define DIFF_OPT_QUICK               (1 << 11)
@@@ -84,7 -82,6 +84,7 @@@
  #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
  #define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)
  #define DIFF_OPT_FUNCCONTEXT         (1 << 29)
 +#define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
  
  #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
  #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
@@@ -93,8 -90,6 +93,8 @@@
  #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
  #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
  
 +#define DIFF_WITH_ALG(opts, flag)   (((opts)->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | XDF_##flag)
 +
  enum diff_words_type {
        DIFF_WORDS_NONE = 0,
        DIFF_WORDS_PORCELAIN,
@@@ -134,7 -129,6 +134,7 @@@ struct diff_options 
  
        int stat_width;
        int stat_name_width;
 +      int stat_graph_width;
        int stat_count;
        const char *word_regex;
        enum diff_words_type word_diff;
        diff_format_fn_t format_callback;
        void *format_callback_data;
        diff_prefix_fn_t output_prefix;
 +      int output_prefix_length;
        void *output_prefix_data;
  };
  
@@@ -216,15 -209,12 +216,15 @@@ extern void diff_addremove(struct diff_
                           int addremove,
                           unsigned mode,
                           const unsigned char *sha1,
 +                         int sha1_valid,
                           const char *fullpath, unsigned dirty_submodule);
  
  extern void diff_change(struct diff_options *,
                        unsigned mode1, unsigned mode2,
                        const unsigned char *sha1,
                        const unsigned char *sha2,
 +                      int sha1_valid,
 +                      int sha2_valid,
                        const char *fullpath,
                        unsigned dirty_submodule1, unsigned dirty_submodule2);
  
@@@ -246,7 -236,7 +246,7 @@@ extern int git_diff_ui_config(const cha
  extern int diff_use_color_default;
  extern void diff_setup(struct diff_options *);
  extern int diff_opt_parse(struct diff_options *, const char **, int);
- extern int diff_setup_done(struct diff_options *);
+ extern void diff_setup_done(struct diff_options *);
  
  #define DIFF_DETECT_RENAME    1
  #define DIFF_DETECT_COPY      2
diff --combined merge-recursive.c
index 8801b9d2f1eeb0710ac40df0058bd36ab123f104,3fcc327581165647d0d44f4bb81a3b77338cdb25..e02da3556d08f02abf87cb1ece75a2e1d0c8ab78
@@@ -264,7 -264,7 +264,7 @@@ struct tree *write_tree_from_memory(str
  
        if (!cache_tree_fully_valid(active_cache_tree) &&
            cache_tree_update(active_cache_tree,
 -                            active_cache, active_nr, 0, 0, 0) < 0)
 +                            active_cache, active_nr, 0) < 0)
                die("error building trees");
  
        result = lookup_tree(active_cache_tree->sha1);
@@@ -485,7 -485,6 +485,7 @@@ static struct string_list *get_renames(
        renames = xcalloc(1, sizeof(struct string_list));
        diff_setup(&opts);
        DIFF_OPT_SET(&opts, RECURSIVE);
 +      DIFF_OPT_CLR(&opts, RENAME_EMPTY);
        opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
        opts.rename_score = o->rename_score;
        opts.show_rename_progress = o->show_rename_progress;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opts) < 0)
-               die("diff setup failed");
+       diff_setup_done(&opts);
        diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
        diffcore_std(&opts);
        if (opts.needed_rename_limit > o->needed_rename_limit)
@@@ -614,6 -612,23 +613,6 @@@ static char *unique_path(struct merge_o
        return newpath;
  }
  
 -static void flush_buffer(int fd, const char *buf, unsigned long size)
 -{
 -      while (size > 0) {
 -              long ret = write_in_full(fd, buf, size);
 -              if (ret < 0) {
 -                      /* Ignore epipe */
 -                      if (errno == EPIPE)
 -                              break;
 -                      die_errno("merge-recursive");
 -              } else if (!ret) {
 -                      die("merge-recursive: disk full?");
 -              }
 -              size -= ret;
 -              buf += ret;
 -      }
 -}
 -
  static int dir_in_way(const char *path, int check_working_copy)
  {
        int pos, pathlen = strlen(path);
@@@ -772,7 -787,7 +771,7 @@@ static void update_file_flags(struct me
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
                        if (fd < 0)
                                die_errno("failed to open '%s'", path);
 -                      flush_buffer(fd, buf, size);
 +                      write_in_full(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
                        char *lnk = xmemdupz(buf, size);
@@@ -1898,7 -1913,7 +1897,7 @@@ int merge_recursive(struct merge_option
                /* if there is no common ancestor, use an empty tree */
                struct tree *tree;
  
 -              tree = lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
 +              tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
                merged_common_ancestors = make_virtual_commit(tree, "ancestor");
        }
  
@@@ -2052,9 -2067,9 +2051,9 @@@ int parse_merge_opt(struct merge_option
        else if (!prefixcmp(s, "subtree="))
                o->subtree_shift = s + strlen("subtree=");
        else if (!strcmp(s, "patience"))
 -              o->xdl_opts |= XDF_PATIENCE_DIFF;
 +              o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
 -              o->xdl_opts |= XDF_HISTOGRAM_DIFF;
 +              o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
        else if (!strcmp(s, "ignore-space-change"))
                o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
        else if (!strcmp(s, "ignore-all-space"))
diff --combined notes-merge.c
index 74aa77ce4be2bf23387a32e42cbdc72154c529c2,66b1da9e8a241cc7658b2699ebf595bd92f97626..7d62904f619fcd775f7ed7e7cff00d44005f3a5b
@@@ -126,8 -126,7 +126,7 @@@ static struct notes_merge_pair *diff_tr
        diff_setup(&opt);
        DIFF_OPT_SET(&opt, RECURSIVE);
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&opt);
        diff_tree_sha1(base, remote, "", &opt);
        diffcore_std(&opt);
  
@@@ -190,8 -189,7 +189,7 @@@ static void diff_tree_local(struct note
        diff_setup(&opt);
        DIFF_OPT_SET(&opt, RECURSIVE);
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&opt);
        diff_tree_sha1(base, local, "", &opt);
        diffcore_std(&opt);
  
@@@ -267,8 -265,7 +265,8 @@@ static void check_notes_merge_worktree(
                 * Must establish NOTES_MERGE_WORKTREE.
                 * Abort if NOTES_MERGE_WORKTREE already exists
                 */
 -              if (file_exists(git_path(NOTES_MERGE_WORKTREE))) {
 +              if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
 +                  !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
                        if (advice_resolve_conflict)
                                die("You have not concluded your previous "
                                    "notes merge (%s exists).\nPlease, use "
@@@ -688,60 -685,51 +686,60 @@@ int notes_merge_commit(struct notes_mer
  {
        /*
         * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
 -       * found notes to 'partial_tree'. Write the updates notes tree to
 +       * found notes to 'partial_tree'. Write the updated notes tree to
         * the DB, and commit the resulting tree object while reusing the
         * commit message and parents from 'partial_commit'.
         * Finally store the new commit object SHA1 into 'result_sha1'.
         */
 -      struct dir_struct dir;
 -      char *path = xstrdup(git_path(NOTES_MERGE_WORKTREE "/"));
 -      int path_len = strlen(path), i;
 +      DIR *dir;
 +      struct dirent *e;
 +      struct strbuf path = STRBUF_INIT;
        char *msg = strstr(partial_commit->buffer, "\n\n");
        struct strbuf sb_msg = STRBUF_INIT;
 +      int baselen;
  
 +      strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
        if (o->verbosity >= 3)
 -              printf("Committing notes in notes merge worktree at %.*s\n",
 -                      path_len - 1, path);
 +              printf("Committing notes in notes merge worktree at %s\n",
 +                      path.buf);
  
        if (!msg || msg[2] == '\0')
                die("partial notes commit has empty message");
        msg += 2;
  
 -      memset(&dir, 0, sizeof(dir));
 -      read_directory(&dir, path, path_len, NULL);
 -      for (i = 0; i < dir.nr; i++) {
 -              struct dir_entry *ent = dir.entries[i];
 +      dir = opendir(path.buf);
 +      if (!dir)
 +              die_errno("could not open %s", path.buf);
 +
 +      strbuf_addch(&path, '/');
 +      baselen = path.len;
 +      while ((e = readdir(dir)) != NULL) {
                struct stat st;
 -              const char *relpath = ent->name + path_len;
                unsigned char obj_sha1[20], blob_sha1[20];
  
 -              if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
 +              if (is_dot_or_dotdot(e->d_name))
 +                      continue;
 +
 +              if (strlen(e->d_name) != 40 || get_sha1_hex(e->d_name, obj_sha1)) {
                        if (o->verbosity >= 3)
 -                              printf("Skipping non-SHA1 entry '%s'\n",
 -                                                              ent->name);
 +                              printf("Skipping non-SHA1 entry '%s%s'\n",
 +                                      path.buf, e->d_name);
                        continue;
                }
  
 +              strbuf_addstr(&path, e->d_name);
                /* write file as blob, and add to partial_tree */
 -              if (stat(ent->name, &st))
 -                      die_errno("Failed to stat '%s'", ent->name);
 -              if (index_path(blob_sha1, ent->name, &st, HASH_WRITE_OBJECT))
 -                      die("Failed to write blob object from '%s'", ent->name);
 +              if (stat(path.buf, &st))
 +                      die_errno("Failed to stat '%s'", path.buf);
 +              if (index_path(blob_sha1, path.buf, &st, HASH_WRITE_OBJECT))
 +                      die("Failed to write blob object from '%s'", path.buf);
                if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
                        die("Failed to add resolved note '%s' to notes tree",
 -                          ent->name);
 +                          path.buf);
                if (o->verbosity >= 4)
                        printf("Added resolved note for object %s: %s\n",
                                sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
 +              strbuf_setlen(&path, baselen);
        }
  
        strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1);
        if (o->verbosity >= 4)
                printf("Finalized notes merge commit: %s\n",
                        sha1_to_hex(result_sha1));
 -      free(path);
 +      strbuf_release(&path);
 +      closedir(dir);
        return 0;
  }
  
  int notes_merge_abort(struct notes_merge_options *o)
  {
 -      /* Remove .git/NOTES_MERGE_WORKTREE directory and all files within */
 +      /*
 +       * Remove all files within .git/NOTES_MERGE_WORKTREE. We do not remove
 +       * the .git/NOTES_MERGE_WORKTREE directory itself, since it might be
 +       * the current working directory of the user.
 +       */
        struct strbuf buf = STRBUF_INIT;
        int ret;
  
        strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
        if (o->verbosity >= 3)
 -              printf("Removing notes merge worktree at %s\n", buf.buf);
 -      ret = remove_dir_recursively(&buf, 0);
 +              printf("Removing notes merge worktree at %s/*\n", buf.buf);
 +      ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
        strbuf_release(&buf);
        return ret;
  }
diff --combined revision.c
index 74c484ca84867c5edfb1ac569ac63efef1a893e6,86d17c6d7a66b75dc2d5a5e70bb3e57d0aef02f1..6d21ac7ee1ccb1aa014dc977955d7fce6d92ede2
@@@ -139,32 -139,11 +139,32 @@@ void mark_tree_uninteresting(struct tre
  
  void mark_parents_uninteresting(struct commit *commit)
  {
 -      struct commit_list *parents = commit->parents;
 +      struct commit_list *parents = NULL, *l;
 +
 +      for (l = commit->parents; l; l = l->next)
 +              commit_list_insert(l->item, &parents);
  
        while (parents) {
                struct commit *commit = parents->item;
 -              if (!(commit->object.flags & UNINTERESTING)) {
 +              l = parents;
 +              parents = parents->next;
 +              free(l);
 +
 +              while (commit) {
 +                      /*
 +                       * A missing commit is ok iff its parent is marked
 +                       * uninteresting.
 +                       *
 +                       * We just mark such a thing parsed, so that when
 +                       * it is popped next time around, we won't be trying
 +                       * to parse it and get an error.
 +                       */
 +                      if (!has_sha1_file(commit->object.sha1))
 +                              commit->object.parsed = 1;
 +
 +                      if (commit->object.flags & UNINTERESTING)
 +                              break;
 +
                        commit->object.flags |= UNINTERESTING;
  
                        /*
                         * wasn't uninteresting), in which case we need
                         * to mark its parents recursively too..
                         */
 -                      if (commit->parents)
 -                              mark_parents_uninteresting(commit);
 -              }
 +                      if (!commit->parents)
 +                              break;
  
 -              /*
 -               * A missing commit is ok iff its parent is marked
 -               * uninteresting.
 -               *
 -               * We just mark such a thing parsed, so that when
 -               * it is popped next time around, we won't be trying
 -               * to parse it and get an error.
 -               */
 -              if (!has_sha1_file(commit->object.sha1))
 -                      commit->object.parsed = 1;
 -              parents = parents->next;
 +                      for (l = commit->parents->next; l; l = l->next)
 +                              commit_list_insert(l->item, &parents);
 +                      commit = commit->parents->item;
 +              }
        }
  }
  
@@@ -345,7 -332,6 +345,7 @@@ static int tree_difference = REV_TREE_S
  static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
 +                  int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
@@@ -359,7 -345,6 +359,7 @@@ static void file_change(struct diff_opt
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
 +               int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
@@@ -1360,13 -1345,11 +1360,13 @@@ static int handle_revision_opt(struct r
                revs->topo_order = 1;
        } else if (!strcmp(arg, "--simplify-merges")) {
                revs->simplify_merges = 1;
 +              revs->topo_order = 1;
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->limited = 1;
        } else if (!strcmp(arg, "--simplify-by-decoration")) {
                revs->simplify_merges = 1;
 +              revs->topo_order = 1;
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->simplify_by_decoration = 1;
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
 +              DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
@@@ -1719,21 -1701,17 +1719,21 @@@ int setup_revisions(int argc, const cha
                submodule = opt->submodule;
  
        /* First, search for "--" */
 -      seen_dashdash = 0;
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 -              if (strcmp(arg, "--"))
 -                      continue;
 -              argv[i] = NULL;
 -              argc = i;
 -              if (argv[i + 1])
 -                      append_prune_data(&prune_data, argv + i + 1);
 +      if (opt && opt->assume_dashdash) {
                seen_dashdash = 1;
 -              break;
 +      } else {
 +              seen_dashdash = 0;
 +              for (i = 1; i < argc; i++) {
 +                      const char *arg = argv[i];
 +                      if (strcmp(arg, "--"))
 +                              continue;
 +                      argv[i] = NULL;
 +                      argc = i;
 +                      if (argv[i + 1])
 +                              append_prune_data(&prune_data, argv + i + 1);
 +                      seen_dashdash = 1;
 +                      break;
 +              }
        }
  
        /* Second, deal with arguments and options */
                         * but the latter we have checked in the main loop.
                         */
                        for (j = i; j < argc; j++)
 -                              verify_filename(revs->prefix, argv[j]);
 +                              verify_filename(revs->prefix, argv[j], j == i);
  
                        append_prune_data(&prune_data, argv + i);
                        break;
        if (revs->combine_merges)
                revs->ignore_merges = 0;
        revs->diffopt.abbrev = revs->abbrev;
-       if (diff_setup_done(&revs->diffopt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&revs->diffopt);
  
        compile_grep_patterns(&revs->grep_filter);
  
@@@ -1951,9 -1928,8 +1950,9 @@@ static struct commit_list **simplify_on
        }
  
        /*
 -       * Do we know what commit all of our parents should be rewritten to?
 -       * Otherwise we are not ready to rewrite this one yet.
 +       * Do we know what commit all of our parents that matter
 +       * should be rewritten to?  Otherwise we are not ready to
 +       * rewrite this one yet.
         */
        for (cnt = 0, p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
                        tail = &commit_list_insert(p->item, tail)->next;
                        cnt++;
                }
 +              if (revs->first_parent_only)
 +                      break;
        }
        if (cnt) {
                tail = &commit_list_insert(commit, tail)->next;
        for (p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
                p->item = pst->simplified;
 +              if (revs->first_parent_only)
 +                      break;
        }
 -      cnt = remove_duplicate_parents(commit);
 +      if (!revs->first_parent_only)
 +              cnt = remove_duplicate_parents(commit);
 +      else
 +              cnt = 1;
  
        /*
         * It is possible that we are a merge and one side branch
  
  static void simplify_merges(struct rev_info *revs)
  {
 -      struct commit_list *list;
 +      struct commit_list *list, *next;
        struct commit_list *yet_to_do, **tail;
 +      struct commit *commit;
  
 -      if (!revs->topo_order)
 -              sort_in_topological_order(&revs->commits, revs->lifo);
        if (!revs->prune)
                return;
  
        /* feed the list reversed */
        yet_to_do = NULL;
 -      for (list = revs->commits; list; list = list->next)
 -              commit_list_insert(list->item, &yet_to_do);
 +      for (list = revs->commits; list; list = next) {
 +              commit = list->item;
 +              next = list->next;
 +              /*
 +               * Do not free(list) here yet; the original list
 +               * is used later in this function.
 +               */
 +              commit_list_insert(commit, &yet_to_do);
 +      }
        while (yet_to_do) {
                list = yet_to_do;
                yet_to_do = NULL;
                tail = &yet_to_do;
                while (list) {
 -                      struct commit *commit = list->item;
 -                      struct commit_list *next = list->next;
 +                      commit = list->item;
 +                      next = list->next;
                        free(list);
                        list = next;
                        tail = simplify_one(revs, commit, tail);
        revs->commits = NULL;
        tail = &revs->commits;
        while (list) {
 -              struct commit *commit = list->item;
 -              struct commit_list *next = list->next;
                struct merge_simplify_state *st;
 +
 +              commit = list->item;
 +              next = list->next;
                free(list);
                list = next;
                st = locate_simplify_state(revs, commit);
@@@ -2085,16 -2047,10 +2084,16 @@@ static void set_children(struct rev_inf
        }
  }
  
 +void reset_revision_walk(void)
 +{
 +      clear_object_flags(SEEN | ADDED | SHOWN);
 +}
 +
  int prepare_revision_walk(struct rev_info *revs)
  {
        int nr = revs->pending.nr;
        struct object_array_entry *e, *list;
 +      struct commit_list **next = &revs->commits;
  
        e = list = revs->pending.objects;
        revs->pending.nr = 0;
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
 -                              commit_list_insert_by_date(commit, &revs->commits);
 +                              next = commit_list_append(commit, next);
                        }
                }
                e++;
        }
 +      commit_list_sort_by_date(&revs->commits);
        if (!revs->leak_pending)
                free(list);
  
diff --combined submodule.c
index 959d349ea7426344289020ee4216785fa65ec1e6,191b43240ff192fd17d7cf5060d1482e3217bcd8..19dc6a6c0d5cf7d9a4c80c61e160290b4462665d
@@@ -63,9 -63,6 +63,9 @@@ static int add_submodule_odb(const cha
        alt_odb->name[40] = '\0';
        alt_odb->name[41] = '\0';
        alt_odb_list = alt_odb;
 +
 +      /* add possible alternates from the submodule */
 +      read_info_alternates(objects_directory.buf, 0);
        prepare_alt_odb();
  done:
        strbuf_release(&objects_directory);
@@@ -360,19 -357,21 +360,19 @@@ static void collect_submodules_from_dif
                                         void *data)
  {
        int i;
 -      int *needs_pushing = data;
 +      struct string_list *needs_pushing = data;
  
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                if (!S_ISGITLINK(p->two->mode))
                        continue;
 -              if (submodule_needs_pushing(p->two->path, p->two->sha1)) {
 -                      *needs_pushing = 1;
 -                      break;
 -              }
 +              if (submodule_needs_pushing(p->two->path, p->two->sha1))
 +                      string_list_insert(needs_pushing, p->two->path);
        }
  }
  
 -
 -static void commit_need_pushing(struct commit *commit, int *needs_pushing)
 +static void find_unpushed_submodule_commits(struct commit *commit,
 +              struct string_list *needs_pushing)
  {
        struct rev_info rev;
  
        diff_tree_combined_merge(commit, 1, &rev);
  }
  
 -int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
 +int find_unpushed_submodules(unsigned char new_sha1[20],
 +              const char *remotes_name, struct string_list *needs_pushing)
  {
        struct rev_info rev;
        struct commit *commit;
        const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
        int argc = ARRAY_SIZE(argv) - 1;
        char *sha1_copy;
 -      int needs_pushing = 0;
 +
        struct strbuf remotes_arg = STRBUF_INIT;
  
        strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
  
 -      while ((commit = get_revision(&rev)) && !needs_pushing)
 -              commit_need_pushing(commit, &needs_pushing);
 +      while ((commit = get_revision(&rev)) != NULL)
 +              find_unpushed_submodule_commits(commit, needs_pushing);
  
 +      reset_revision_walk();
        free(sha1_copy);
        strbuf_release(&remotes_arg);
  
 -      return needs_pushing;
 +      return needs_pushing->nr;
 +}
 +
 +static int push_submodule(const char *path)
 +{
 +      if (add_submodule_odb(path))
 +              return 1;
 +
 +      if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 +              struct child_process cp;
 +              const char *argv[] = {"push", NULL};
 +
 +              memset(&cp, 0, sizeof(cp));
 +              cp.argv = argv;
 +              cp.env = local_repo_env;
 +              cp.git_cmd = 1;
 +              cp.no_stdin = 1;
 +              cp.dir = path;
 +              if (run_command(&cp))
 +                      return 0;
 +              close(cp.out);
 +      }
 +
 +      return 1;
 +}
 +
 +int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
 +{
 +      int i, ret = 1;
 +      struct string_list needs_pushing;
 +
 +      memset(&needs_pushing, 0, sizeof(struct string_list));
 +      needs_pushing.strdup_strings = 1;
 +
 +      if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
 +              return 1;
 +
 +      for (i = 0; i < needs_pushing.nr; i++) {
 +              const char *path = needs_pushing.items[i].string;
 +              fprintf(stderr, "Pushing submodule '%s'\n", path);
 +              if (!push_submodule(path)) {
 +                      fprintf(stderr, "Unable to push submodule '%s'\n", path);
 +                      ret = 0;
 +              }
 +      }
 +
 +      string_list_clear(&needs_pushing, 0);
 +
 +      return ret;
  }
  
  static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
@@@ -574,8 -523,7 +574,7 @@@ static void calculate_changed_submodule
                        DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
-                       if (diff_setup_done(&diff_opts) < 0)
-                               die("diff_setup_done failed");
+                       diff_setup_done(&diff_opts);
                        diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
                        diffcore_std(&diff_opts);
                        diff_flush(&diff_opts);
@@@ -792,7 -740,6 +791,7 @@@ static int find_first_merges(struct obj
                if (in_merge_bases(b, &commit, 1))
                        add_object_array(o, NULL, &merges);
        }
 +      reset_revision_walk();
  
        /* Now we've got all merges that contain a and b. Prune all
         * merges that contain another found merge and save them in
diff --combined tree-diff.c
index 7e5483366e611df02238656a10749f9070ce41c6,f760740a1174820349ddb0ac9c583e7e726dcfb5..ba01563a0241041cb401cfb03495e6067847248e
@@@ -49,12 -49,12 +49,12 @@@ static int compare_tree_entry(struct tr
        if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
                        opt->change(opt, mode1, mode2,
 -                                  sha1, sha2, base->buf, 0, 0);
 +                                  sha1, sha2, 1, 1, base->buf, 0, 0);
                }
                strbuf_addch(base, '/');
                diff_tree_sha1(sha1, sha2, base->buf, opt);
        } else {
 -              opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
 +              opt->change(opt, mode1, mode2, sha1, sha2, 1, 1, base->buf, 0, 0);
        }
        strbuf_setlen(base, old_baselen);
        return 0;
@@@ -100,7 -100,7 +100,7 @@@ static void show_entry(struct diff_opti
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
  
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE))
 -                      opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0);
 +                      opt->add_remove(opt, *prefix, mode, sha1, 1, base->buf, 0);
  
                strbuf_addch(base, '/');
  
                show_tree(opt, prefix, &inner, base);
                free(tree);
        } else
 -              opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0);
 +              opt->add_remove(opt, prefix[0], mode, sha1, 1, base->buf, 0);
  
        strbuf_setlen(base, old_baselen);
  }
@@@ -212,8 -212,7 +212,7 @@@ static void try_to_follow_renames(struc
        diff_opts.rename_score = opt->rename_score;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("unable to set up diff options to follow renames");
+       diff_setup_done(&diff_opts);
        diff_tree(t1, t2, base, &diff_opts);
        diffcore_std(&diff_opts);
        diff_tree_release_paths(&diff_opts);