Merge branch 'rs/cocci'
authorJunio C Hamano <gitster@pobox.com>
Thu, 6 Oct 2016 21:53:12 +0000 (14:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 6 Oct 2016 21:53:12 +0000 (14:53 -0700)
Code clean-up with help from coccinelle tool continues.

* rs/cocci:
coccicheck: make transformation for strbuf_addf(sb, "...") more precise
use strbuf_add_unique_abbrev() for adding short hashes, part 2
use strbuf_addstr() instead of strbuf_addf() with "%s", part 2
gitignore: ignore output files of coccicheck make target

1  2 
builtin/submodule--helper.c
diff.c
submodule.c
wt-status.c
index e3fdc0aa7883e1050f4fe8a165ec6245ab9abc9e,dbe5699fe688faeec3bdad899f229746b96be57f..444ec06c2a31d1ec4981ba7475147c3154cd1b18
@@@ -296,8 -296,7 +296,8 @@@ static int module_list(int argc, const 
                if (ce_stage(ce))
                        printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
                else
 -                      printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
 +                      printf("%06o %s %d\t", ce->ce_mode,
 +                             oid_to_hex(&ce->oid), ce_stage(ce));
  
                utf8_fprintf(stdout, "%s\n", ce->name);
        }
@@@ -443,8 -442,7 +443,8 @@@ static int module_name(int argc, const 
  }
  
  static int clone_submodule(const char *path, const char *gitdir, const char *url,
 -                         const char *depth, const char *reference, int quiet)
 +                         const char *depth, struct string_list *reference,
 +                         int quiet, int progress)
  {
        struct child_process cp = CHILD_PROCESS_INIT;
  
        argv_array_push(&cp.args, "--no-checkout");
        if (quiet)
                argv_array_push(&cp.args, "--quiet");
 +      if (progress)
 +              argv_array_push(&cp.args, "--progress");
        if (depth && *depth)
                argv_array_pushl(&cp.args, "--depth", depth, NULL);
 -      if (reference && *reference)
 -              argv_array_pushl(&cp.args, "--reference", reference, NULL);
 +      if (reference->nr) {
 +              struct string_list_item *item;
 +              for_each_string_list_item(item, reference)
 +                      argv_array_pushl(&cp.args, "--reference",
 +                                       item->string, NULL);
 +      }
        if (gitdir && *gitdir)
                argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
  
        return run_command(&cp);
  }
  
 +struct submodule_alternate_setup {
 +      const char *submodule_name;
 +      enum SUBMODULE_ALTERNATE_ERROR_MODE {
 +              SUBMODULE_ALTERNATE_ERROR_DIE,
 +              SUBMODULE_ALTERNATE_ERROR_INFO,
 +              SUBMODULE_ALTERNATE_ERROR_IGNORE
 +      } error_mode;
 +      struct string_list *reference;
 +};
 +#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
 +      SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
 +
 +static int add_possible_reference_from_superproject(
 +              struct alternate_object_database *alt, void *sas_cb)
 +{
 +      struct submodule_alternate_setup *sas = sas_cb;
 +
 +      /* directory name, minus trailing slash */
 +      size_t namelen = alt->name - alt->base - 1;
 +      struct strbuf name = STRBUF_INIT;
 +      strbuf_add(&name, alt->base, namelen);
 +
 +      /*
 +       * If the alternate object store is another repository, try the
 +       * standard layout with .git/modules/<name>/objects
 +       */
 +      if (ends_with(name.buf, ".git/objects")) {
 +              char *sm_alternate;
 +              struct strbuf sb = STRBUF_INIT;
 +              struct strbuf err = STRBUF_INIT;
 +              strbuf_add(&sb, name.buf, name.len - strlen("objects"));
 +              /*
 +               * We need to end the new path with '/' to mark it as a dir,
 +               * otherwise a submodule name containing '/' will be broken
 +               * as the last part of a missing submodule reference would
 +               * be taken as a file name.
 +               */
 +              strbuf_addf(&sb, "modules/%s/", sas->submodule_name);
 +
 +              sm_alternate = compute_alternate_path(sb.buf, &err);
 +              if (sm_alternate) {
 +                      string_list_append(sas->reference, xstrdup(sb.buf));
 +                      free(sm_alternate);
 +              } else {
 +                      switch (sas->error_mode) {
 +                      case SUBMODULE_ALTERNATE_ERROR_DIE:
 +                              die(_("submodule '%s' cannot add alternate: %s"),
 +                                  sas->submodule_name, err.buf);
 +                      case SUBMODULE_ALTERNATE_ERROR_INFO:
 +                              fprintf(stderr, _("submodule '%s' cannot add alternate: %s"),
 +                                      sas->submodule_name, err.buf);
 +                      case SUBMODULE_ALTERNATE_ERROR_IGNORE:
 +                              ; /* nothing */
 +                      }
 +              }
 +              strbuf_release(&sb);
 +      }
 +
 +      strbuf_release(&name);
 +      return 0;
 +}
 +
 +static void prepare_possible_alternates(const char *sm_name,
 +              struct string_list *reference)
 +{
 +      char *sm_alternate = NULL, *error_strategy = NULL;
 +      struct submodule_alternate_setup sas = SUBMODULE_ALTERNATE_SETUP_INIT;
 +
 +      git_config_get_string("submodule.alternateLocation", &sm_alternate);
 +      if (!sm_alternate)
 +              return;
 +
 +      git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
 +
 +      if (!error_strategy)
 +              error_strategy = xstrdup("die");
 +
 +      sas.submodule_name = sm_name;
 +      sas.reference = reference;
 +      if (!strcmp(error_strategy, "die"))
 +              sas.error_mode = SUBMODULE_ALTERNATE_ERROR_DIE;
 +      else if (!strcmp(error_strategy, "info"))
 +              sas.error_mode = SUBMODULE_ALTERNATE_ERROR_INFO;
 +      else if (!strcmp(error_strategy, "ignore"))
 +              sas.error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE;
 +      else
 +              die(_("Value '%s' for submodule.alternateErrorStrategy is not recognized"), error_strategy);
 +
 +      if (!strcmp(sm_alternate, "superproject"))
 +              foreach_alt_odb(add_possible_reference_from_superproject, &sas);
 +      else if (!strcmp(sm_alternate, "no"))
 +              ; /* do nothing */
 +      else
 +              die(_("Value '%s' for submodule.alternateLocation is not recognized"), sm_alternate);
 +
 +      free(sm_alternate);
 +      free(error_strategy);
 +}
 +
  static int module_clone(int argc, const char **argv, const char *prefix)
  {
 -      const char *name = NULL, *url = NULL;
 -      const char *reference = NULL, *depth = NULL;
 +      const char *name = NULL, *url = NULL, *depth = NULL;
        int quiet = 0;
 +      int progress = 0;
        FILE *submodule_dot_git;
        char *p, *path = NULL, *sm_gitdir;
        struct strbuf rel_path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
 +      struct string_list reference = STRING_LIST_INIT_NODUP;
  
        struct option module_clone_options[] = {
                OPT_STRING(0, "prefix", &prefix,
                OPT_STRING(0, "url", &url,
                           N_("string"),
                           N_("url where to clone the submodule from")),
 -              OPT_STRING(0, "reference", &reference,
 -                         N_("string"),
 +              OPT_STRING_LIST(0, "reference", &reference,
 +                         N_("repo"),
                           N_("reference repository")),
                OPT_STRING(0, "depth", &depth,
                           N_("string"),
                           N_("depth for shallow clones")),
                OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
 +              OPT_BOOL(0, "progress", &progress,
 +                         N_("force cloning progress")),
                OPT_END()
        };
  
        if (!file_exists(sm_gitdir)) {
                if (safe_create_leading_directories_const(sm_gitdir) < 0)
                        die(_("could not create directory '%s'"), sm_gitdir);
 -              if (clone_submodule(path, sm_gitdir, url, depth, reference, quiet))
 +
 +              prepare_possible_alternates(name, &reference);
 +
 +              if (clone_submodule(path, sm_gitdir, url, depth, &reference,
 +                                  quiet, progress))
                        die(_("clone of '%s' into submodule path '%s' failed"),
                            url, path);
        } else {
@@@ -691,10 -577,9 +691,10 @@@ struct submodule_update_clone 
        struct submodule_update_strategy update;
  
        /* configuration parameters which are passed on to the children */
 +      int progress;
        int quiet;
        int recommend_shallow;
 -      const char *reference;
 +      struct string_list references;
        const char *depth;
        const char *recursive_prefix;
        const char *prefix;
        int failed_clones_nr, failed_clones_alloc;
  };
  #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
 -      SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \
 +      SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
 +      NULL, NULL, NULL, \
        STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
  
  
@@@ -753,7 -637,7 +753,7 @@@ static int prepare_to_clone_next_submod
                if (suc->recursive_prefix)
                        strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
                else
-                       strbuf_addf(&sb, "%s", ce->name);
+                       strbuf_addstr(&sb, ce->name);
                strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
                strbuf_addch(out, '\n');
                goto cleanup;
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
 -                      sha1_to_hex(ce->sha1), ce_stage(ce),
 +                      oid_to_hex(&ce->oid), ce_stage(ce),
                        needs_cloning, ce->name);
        string_list_append(&suc->projectlines, sb.buf);
  
        child->err = -1;
        argv_array_push(&child->args, "submodule--helper");
        argv_array_push(&child->args, "clone");
 +      if (suc->progress)
 +              argv_array_push(&child->args, "--progress");
        if (suc->quiet)
                argv_array_push(&child->args, "--quiet");
        if (suc->prefix)
        argv_array_pushl(&child->args, "--path", sub->path, NULL);
        argv_array_pushl(&child->args, "--name", sub->name, NULL);
        argv_array_pushl(&child->args, "--url", url, NULL);
 -      if (suc->reference)
 -              argv_array_push(&child->args, suc->reference);
 +      if (suc->references.nr) {
 +              struct string_list_item *item;
 +              for_each_string_list_item(item, &suc->references)
 +                      argv_array_pushl(&child->args, "--reference", item->string, NULL);
 +      }
        if (suc->depth)
                argv_array_push(&child->args, suc->depth);
  
@@@ -951,7 -830,7 +951,7 @@@ static int update_clone(int argc, cons
                OPT_STRING(0, "update", &update,
                           N_("string"),
                           N_("rebase, merge, checkout or none")),
 -              OPT_STRING(0, "reference", &suc.reference, N_("repo"),
 +              OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
                           N_("reference repository")),
                OPT_STRING(0, "depth", &suc.depth, "<depth>",
                           N_("Create a shallow clone truncated to the "
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
                OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
 +              OPT_BOOL(0, "progress", &suc.progress,
 +                          N_("force cloning progress")),
                OPT_END()
        };
  
diff --combined diff.c
index a178ed39bc77988dcc6282d252002f5446305628,ceaac826e21b7afe6c90b41f2338c29da2320d4d..be11e4ef2b516aa5348f01a64af8a0d9da96027f
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -18,7 -18,6 +18,7 @@@
  #include "ll-merge.h"
  #include "string-list.h"
  #include "argv-array.h"
 +#include "graph.h"
  
  #ifdef NO_FAST_WORKING_DIRECTORY
  #define FAST_WORKING_DIRECTORY 0
@@@ -27,7 -26,6 +27,7 @@@
  #endif
  
  static int diff_detect_rename_default;
 +static int diff_indent_heuristic; /* experimental */
  static int diff_compaction_heuristic; /* experimental */
  static int diff_rename_limit_default = 400;
  static int diff_suppress_blank_empty;
@@@ -56,11 -54,6 +56,11 @@@ static char diff_colors[][COLOR_MAXLEN
        GIT_COLOR_NORMAL,       /* FUNCINFO */
  };
  
 +static NORETURN void die_want_option(const char *option_name)
 +{
 +      die(_("option '%s' requires a value"), option_name);
 +}
 +
  static int parse_diff_color_slot(const char *var)
  {
        if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
@@@ -138,11 -131,9 +138,11 @@@ static int parse_dirstat_params(struct 
  static int parse_submodule_params(struct diff_options *options, const char *value)
  {
        if (!strcmp(value, "log"))
 -              DIFF_OPT_SET(options, SUBMODULE_LOG);
 +              options->submodule_format = DIFF_SUBMODULE_LOG;
        else if (!strcmp(value, "short"))
 -              DIFF_OPT_CLR(options, SUBMODULE_LOG);
 +              options->submodule_format = DIFF_SUBMODULE_SHORT;
 +      else if (!strcmp(value, "diff"))
 +              options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
        else
                return -1;
        return 0;
@@@ -183,21 -174,6 +183,21 @@@ void init_diff_ui_defaults(void
        diff_detect_rename_default = 1;
  }
  
 +int git_diff_heuristic_config(const char *var, const char *value, void *cb)
 +{
 +      if (!strcmp(var, "diff.indentheuristic")) {
 +              diff_indent_heuristic = git_config_bool(var, value);
 +              if (diff_indent_heuristic)
 +                      diff_compaction_heuristic = 0;
 +      }
 +      if (!strcmp(var, "diff.compactionheuristic")) {
 +              diff_compaction_heuristic = git_config_bool(var, value);
 +              if (diff_compaction_heuristic)
 +                      diff_indent_heuristic = 0;
 +      }
 +      return 0;
 +}
 +
  int git_diff_ui_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
        }
 -      if (!strcmp(var, "diff.compactionheuristic")) {
 -              diff_compaction_heuristic = git_config_bool(var, value);
 -              return 0;
 -      }
        if (!strcmp(var, "diff.autorefreshindex")) {
                diff_auto_refresh_index = git_config_bool(var, value);
                return 0;
                return 0;
        }
  
 +      if (git_diff_heuristic_config(var, value, cb) < 0)
 +              return -1;
        if (git_color_config(var, value, cb) < 0)
                return -1;
  
@@@ -376,6 -354,7 +376,6 @@@ struct emit_callback 
        const char **label_path;
        struct diff_words_data *diff_words;
        struct diff_options *opt;
 -      int *found_changesp;
        struct strbuf *header;
  };
  
@@@ -743,6 -722,7 +743,6 @@@ static void emit_rewrite_diff(const cha
  
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = want_color(o->use_color);
 -      ecbdata.found_changesp = &o->found_changes;
        ecbdata.ws_rule = whitespace_rule(name_b);
        ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
@@@ -971,8 -951,7 +971,8 @@@ static int find_word_boundaries(mmfile_
  {
        if (word_regex && *begin < buffer->size) {
                regmatch_t match[1];
 -              if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
 +              if (!regexec_buf(word_regex, buffer->ptr + *begin,
 +                               buffer->size - *begin, 1, match, 0)) {
                        char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
                                        '\n', match[0].rm_eo - match[0].rm_so);
                        *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
@@@ -1237,13 -1216,12 +1237,13 @@@ static void fn_out_consume(void *priv, 
        struct diff_options *o = ecbdata->opt;
        const char *line_prefix = diff_line_prefix(o);
  
 +      o->found_changes = 1;
 +
        if (ecbdata->header) {
 -              fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
 +              fprintf(o->file, "%s", ecbdata->header->buf);
                strbuf_reset(ecbdata->header);
                ecbdata->header = NULL;
        }
 -      *(ecbdata->found_changesp) = 1;
  
        if (ecbdata->label_path[0]) {
                const char *name_a_tab, *name_b_tab;
                name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
                name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
  
 -              fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
 +              fprintf(o->file, "%s%s--- %s%s%s\n",
                        line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
 -              fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
 +              fprintf(o->file, "%s%s+++ %s%s%s\n",
                        line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
 -                      putc('\n', ecbdata->opt->file);
 -              return;
 -      }
 -
 -      if (len < 1) {
 -              emit_line(ecbdata->opt, reset, reset, line, len);
 -              if (ecbdata->diff_words
 -                  && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
 -                      fputs("~\n", ecbdata->opt->file);
 +                      putc('\n', o->file);
                return;
        }
  
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
 -                      emit_line(ecbdata->opt, context, reset, line, len);
 -                      fputs("~\n", ecbdata->opt->file);
 +                      emit_line(o, context, reset, line, len);
 +                      fputs("~\n", o->file);
                } else {
                        /*
                         * Skip the prefix character, if any.  With
                              line++;
                              len--;
                        }
 -                      emit_line(ecbdata->opt, context, reset, line, len);
 +                      emit_line(o, context, reset, line, len);
                }
                return;
        }
        default:
                /* incomplete line at the end */
                ecbdata->lno_in_preimage++;
 -              emit_line(ecbdata->opt,
 -                        diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
 +              emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
                          reset, line, len);
                break;
        }
@@@ -1638,7 -1625,7 +1638,7 @@@ static void show_stats(struct diffstat_
         */
  
        if (options->stat_width == -1)
 -              width = term_columns() - options->output_prefix_length;
 +              width = term_columns() - strlen(line_prefix);
        else
                width = options->stat_width ? options->stat_width : 80;
        number_width = decimal_width(max_change) > number_width ?
@@@ -2312,37 -2299,17 +2312,37 @@@ static void builtin_diff(const char *na
        struct strbuf header = STRBUF_INIT;
        const char *line_prefix = diff_line_prefix(o);
  
 -      if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
 -                      (!one->mode || S_ISGITLINK(one->mode)) &&
 -                      (!two->mode || S_ISGITLINK(two->mode))) {
 +      diff_set_mnemonic_prefix(o, "a/", "b/");
 +      if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
 +              a_prefix = o->b_prefix;
 +              b_prefix = o->a_prefix;
 +      } else {
 +              a_prefix = o->a_prefix;
 +              b_prefix = o->b_prefix;
 +      }
 +
 +      if (o->submodule_format == DIFF_SUBMODULE_LOG &&
 +          (!one->mode || S_ISGITLINK(one->mode)) &&
 +          (!two->mode || S_ISGITLINK(two->mode))) {
                const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
                const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
                show_submodule_summary(o->file, one->path ? one->path : two->path,
                                line_prefix,
 -                              one->oid.hash, two->oid.hash,
 +                              &one->oid, &two->oid,
                                two->dirty_submodule,
                                meta, del, add, reset);
                return;
 +      } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
 +                 (!one->mode || S_ISGITLINK(one->mode)) &&
 +                 (!two->mode || S_ISGITLINK(two->mode))) {
 +              const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
 +              const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
 +              show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
 +                              line_prefix,
 +                              &one->oid, &two->oid,
 +                              two->dirty_submodule,
 +                              meta, del, add, reset, o);
 +              return;
        }
  
        if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
                textconv_two = get_textconv(two);
        }
  
 -      diff_set_mnemonic_prefix(o, "a/", "b/");
 -      if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
 -              a_prefix = o->b_prefix;
 -              b_prefix = o->a_prefix;
 -      } else {
 -              a_prefix = o->a_prefix;
 -              b_prefix = o->b_prefix;
 -      }
 -
        /* Never use a non-valid filename anywhere if at all possible */
        name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
        name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
                memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
 -              ecbdata.found_changesp = &o->found_changes;
                ecbdata.ws_rule = whitespace_rule(name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
@@@ -2723,7 -2700,7 +2723,7 @@@ static int reuse_worktree_file(const ch
         * This is not the sha1 we are looking for, or
         * unreusable because it is not a regular file.
         */
 -      if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
 +      if (hashcmp(sha1, ce->oid.hash) || !S_ISREG(ce->ce_mode))
                return 0;
  
        /*
@@@ -3109,7 -3086,7 +3109,7 @@@ static void fill_metainfo(struct strbu
                }
                strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
                            find_unique_abbrev(one->oid.hash, abbrev));
-               strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
+               strbuf_add_unique_abbrev(msg, two->oid.hash, abbrev);
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@@ -3316,9 -3293,7 +3316,9 @@@ void diff_setup(struct diff_options *op
        options->use_color = diff_use_color_default;
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
 -      if (diff_compaction_heuristic)
 +      if (diff_indent_heuristic)
 +              DIFF_XDL_SET(options, INDENT_HEURISTIC);
 +      else if (diff_compaction_heuristic)
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
  
        options->orderfile = diff_order_file_cfg;
@@@ -3347,7 -3322,7 +3347,7 @@@ void diff_setup_done(struct diff_option
        if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
                count++;
        if (count > 1)
 -              die("--name-only, --name-status, --check and -s are mutually exclusive");
 +              die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
  
        /*
         * Most of the time we can say "there are changes"
@@@ -3543,7 -3518,7 +3543,7 @@@ static int stat_opt(struct diff_option
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-width' requires a value");
 +                              die_want_option("--stat-width");
                        else if (!*arg) {
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-name-width' requires a value");
 +                              die_want_option("--stat-name-width");
                        else if (!*arg) {
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-graph-width' requires a value");
 +                              die_want_option("--stat-graph-width");
                        else if (!*arg) {
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-count' requires a value");
 +                              die_want_option("--stat-count");
                        else if (!*arg) {
                                count = strtoul(av[1], &end, 10);
                                argcount = 2;
@@@ -3840,15 -3815,9 +3840,15 @@@ int diff_opt_parse(struct diff_options 
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
 -      else if (!strcmp(arg, "--compaction-heuristic"))
 +      else if (!strcmp(arg, "--indent-heuristic")) {
 +              DIFF_XDL_SET(options, INDENT_HEURISTIC);
 +              DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
 +      } else if (!strcmp(arg, "--no-indent-heuristic"))
 +              DIFF_XDL_CLR(options, INDENT_HEURISTIC);
 +      else if (!strcmp(arg, "--compaction-heuristic")) {
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
 -      else if (!strcmp(arg, "--no-compaction-heuristic"))
 +              DIFF_XDL_CLR(options, INDENT_HEURISTIC);
 +      } else if (!strcmp(arg, "--no-compaction-heuristic"))
                DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg);
        } else if (!strcmp(arg, "--submodule"))
 -              DIFF_OPT_SET(options, SUBMODULE_LOG);
 +              options->submodule_format = DIFF_SUBMODULE_LOG;
        else if (skip_prefix(arg, "--submodule=", &arg))
                return parse_submodule_opt(options, arg);
        else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
                options->a_prefix = optarg;
                return argcount;
        }
 +      else if ((argcount = parse_long_opt("line-prefix", av, &optarg))) {
 +              options->line_prefix = optarg;
 +              options->line_prefix_length = strlen(options->line_prefix);
 +              graph_setup_line_prefix(options);
 +              return argcount;
 +      }
        else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
                options->b_prefix = optarg;
                return argcount;
diff --combined submodule.c
index aba94dd07dbd5c8d1e9cad5b3980bb4a9759e6cf,1e28437a5ae21d4f8a861278c974841a9b93571e..2de06a33516d40ab169a9a031df4fb547f9094fd
@@@ -127,9 -127,7 +127,9 @@@ static int add_submodule_odb(const cha
        int ret = 0;
        size_t alloc;
  
 -      strbuf_git_path_submodule(&objects_directory, path, "objects/");
 +      ret = strbuf_git_path_submodule(&objects_directory, path, "objects/");
 +      if (ret)
 +              goto done;
        if (!is_directory(objects_directory.buf)) {
                ret = -1;
                goto done;
@@@ -280,9 -278,9 +280,9 @@@ void handle_ignore_submodules_arg(struc
  
  static int prepare_submodule_summary(struct rev_info *rev, const char *path,
                struct commit *left, struct commit *right,
 -              int *fast_forward, int *fast_backward)
 +              struct commit_list *merge_bases)
  {
 -      struct commit_list *merge_bases, *list;
 +      struct commit_list *list;
  
        init_revisions(rev, NULL);
        setup_revisions(0, NULL, rev, NULL);
        left->object.flags |= SYMMETRIC_LEFT;
        add_pending_object(rev, &left->object, path);
        add_pending_object(rev, &right->object, path);
 -      merge_bases = get_merge_bases(left, right);
 -      if (merge_bases) {
 -              if (merge_bases->item == left)
 -                      *fast_forward = 1;
 -              else if (merge_bases->item == right)
 -                      *fast_backward = 1;
 -      }
        for (list = merge_bases; list; list = list->next) {
                list->item->object.flags |= UNINTERESTING;
                add_pending_object(rev, &list->item->object,
@@@ -328,23 -333,31 +328,23 @@@ static void print_submodule_summary(str
        strbuf_release(&sb);
  }
  
 -void show_submodule_summary(FILE *f, const char *path,
 +/* Helper function to display the submodule header line prior to the full
 + * summary output. If it can locate the submodule objects directory it will
 + * attempt to lookup both the left and right commits and put them into the
 + * left and right pointers.
 + */
 +static void show_submodule_header(FILE *f, const char *path,
                const char *line_prefix,
 -              unsigned char one[20], unsigned char two[20],
 +              struct object_id *one, struct object_id *two,
                unsigned dirty_submodule, const char *meta,
 -              const char *del, const char *add, const char *reset)
 +              const char *reset,
 +              struct commit **left, struct commit **right,
 +              struct commit_list **merge_bases)
  {
 -      struct rev_info rev;
 -      struct commit *left = NULL, *right = NULL;
        const char *message = NULL;
        struct strbuf sb = STRBUF_INIT;
        int fast_forward = 0, fast_backward = 0;
  
 -      if (is_null_sha1(two))
 -              message = "(submodule deleted)";
 -      else if (add_submodule_odb(path))
 -              message = "(not checked out)";
 -      else if (is_null_sha1(one))
 -              message = "(new submodule)";
 -      else if (!(left = lookup_commit_reference(one)) ||
 -               !(right = lookup_commit_reference(two)))
 -              message = "(commits not present)";
 -      else if (prepare_submodule_summary(&rev, path, left, right,
 -                                         &fast_forward, &fast_backward))
 -              message = "(revision walker failed)";
 -
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
                fprintf(f, "%sSubmodule %s contains untracked content\n",
                        line_prefix, path);
                fprintf(f, "%sSubmodule %s contains modified content\n",
                        line_prefix, path);
  
 -      if (!hashcmp(one, two)) {
 +      if (is_null_oid(one))
 +              message = "(new submodule)";
 +      else if (is_null_oid(two))
 +              message = "(submodule deleted)";
 +
 +      if (add_submodule_odb(path)) {
 +              if (!message)
 +                      message = "(not initialized)";
 +              goto output_header;
 +      }
 +
 +      /*
 +       * Attempt to lookup the commit references, and determine if this is
 +       * a fast forward or fast backwards update.
 +       */
 +      *left = lookup_commit_reference(one->hash);
 +      *right = lookup_commit_reference(two->hash);
 +
 +      /*
 +       * Warn about missing commits in the submodule project, but only if
 +       * they aren't null.
 +       */
 +      if ((!is_null_oid(one) && !*left) ||
 +           (!is_null_oid(two) && !*right))
 +              message = "(commits not present)";
 +
 +      *merge_bases = get_merge_bases(*left, *right);
 +      if (*merge_bases) {
 +              if ((*merge_bases)->item == *left)
 +                      fast_forward = 1;
 +              else if ((*merge_bases)->item == *right)
 +                      fast_backward = 1;
 +      }
 +
 +      if (!oidcmp(one, two)) {
                strbuf_release(&sb);
                return;
        }
  
 +output_header:
        strbuf_addf(&sb, "%s%sSubmodule %s %s..", line_prefix, meta, path,
 -                      find_unique_abbrev(one, DEFAULT_ABBREV));
 +                      find_unique_abbrev(one->hash, DEFAULT_ABBREV));
        if (!fast_backward && !fast_forward)
                strbuf_addch(&sb, '.');
-       strbuf_addf(&sb, "%s", find_unique_abbrev(two->hash, DEFAULT_ABBREV));
 -      strbuf_add_unique_abbrev(&sb, two, DEFAULT_ABBREV);
++      strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
        if (message)
                strbuf_addf(&sb, " %s%s\n", message, reset);
        else
                strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
        fwrite(sb.buf, sb.len, 1, f);
  
 -      if (!message) /* only NULL if we succeeded in setting up the walk */
 -              print_submodule_summary(&rev, f, line_prefix, del, add, reset);
 +      strbuf_release(&sb);
 +}
 +
 +void show_submodule_summary(FILE *f, const char *path,
 +              const char *line_prefix,
 +              struct object_id *one, struct object_id *two,
 +              unsigned dirty_submodule, const char *meta,
 +              const char *del, const char *add, const char *reset)
 +{
 +      struct rev_info rev;
 +      struct commit *left = NULL, *right = NULL;
 +      struct commit_list *merge_bases = NULL;
 +
 +      show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
 +                            meta, reset, &left, &right, &merge_bases);
 +
 +      /*
 +       * If we don't have both a left and a right pointer, there is no
 +       * reason to try and display a summary. The header line should contain
 +       * all the information the user needs.
 +       */
 +      if (!left || !right)
 +              goto out;
 +
 +      /* Treat revision walker failure the same as missing commits */
 +      if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
 +              fprintf(f, "%s(revision walker failed)\n", line_prefix);
 +              goto out;
 +      }
 +
 +      print_submodule_summary(&rev, f, line_prefix, del, add, reset);
 +
 +out:
 +      if (merge_bases)
 +              free_commit_list(merge_bases);
 +      clear_commit_marks(left, ~0);
 +      clear_commit_marks(right, ~0);
 +}
 +
 +void show_submodule_inline_diff(FILE *f, const char *path,
 +              const char *line_prefix,
 +              struct object_id *one, struct object_id *two,
 +              unsigned dirty_submodule, const char *meta,
 +              const char *del, const char *add, const char *reset,
 +              const struct diff_options *o)
 +{
 +      const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
 +      struct commit *left = NULL, *right = NULL;
 +      struct commit_list *merge_bases = NULL;
 +      struct strbuf submodule_dir = STRBUF_INIT;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +
 +      show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
 +                            meta, reset, &left, &right, &merge_bases);
 +
 +      /* We need a valid left and right commit to display a difference */
 +      if (!(left || is_null_oid(one)) ||
 +          !(right || is_null_oid(two)))
 +              goto done;
 +
 +      if (left)
 +              old = one;
 +      if (right)
 +              new = two;
 +
 +      fflush(f);
 +      cp.git_cmd = 1;
 +      cp.dir = path;
 +      cp.out = dup(fileno(f));
 +      cp.no_stdin = 1;
 +
 +      /* TODO: other options may need to be passed here. */
 +      argv_array_push(&cp.args, "diff");
 +      argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
 +      if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
 +              argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
 +                               o->b_prefix, path);
 +              argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
 +                               o->a_prefix, path);
 +      } else {
 +              argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
 +                               o->a_prefix, path);
 +              argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
 +                               o->b_prefix, path);
 +      }
 +      argv_array_push(&cp.args, oid_to_hex(old));
 +      /*
 +       * If the submodule has modified content, we will diff against the
 +       * work tree, under the assumption that the user has asked for the
 +       * diff format and wishes to actually see all differences even if they
 +       * haven't yet been committed to the submodule yet.
 +       */
 +      if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
 +              argv_array_push(&cp.args, oid_to_hex(new));
 +
 +      if (run_command(&cp))
 +              fprintf(f, "(diff failed)\n");
 +
 +done:
 +      strbuf_release(&submodule_dir);
 +      if (merge_bases)
 +              free_commit_list(merge_bases);
        if (left)
                clear_commit_marks(left, ~0);
        if (right)
                clear_commit_marks(right, ~0);
 -
 -      strbuf_release(&sb);
  }
  
  void set_config_fetch_recurse_submodules(int value)
@@@ -728,10 -608,9 +728,10 @@@ void check_for_new_submodule_commits(un
        sha1_array_append(&ref_tips_after_fetch, new_sha1);
  }
  
 -static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
 +static int add_sha1_to_argv(const unsigned char sha1[20], void *data)
  {
        argv_array_push(data, sha1_to_hex(sha1));
 +      return 0;
  }
  
  static void calculate_changed_submodule_paths(void)
@@@ -1281,5 -1160,4 +1281,5 @@@ void prepare_submodule_repo_env(struct 
                if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
                        argv_array_push(out, *var);
        }
 +      argv_array_push(out, "GIT_DIR=.git");
  }
diff --combined wt-status.c
index 9628c1d5d75ca3ff4d3bc6ccc66152f371d9483e,fed83e5c824886c15217a454953076aac5f2d185..99d1b0a8180f34bb928ad92612b6a11922907e27
@@@ -139,7 -139,7 +139,7 @@@ void wt_status_prepare(struct wt_statu
        s->display_comment_prefix = 0;
  }
  
 -static void wt_status_print_unmerged_header(struct wt_status *s)
 +static void wt_longstatus_print_unmerged_header(struct wt_status *s)
  {
        int i;
        int del_mod_conflict = 0;
        status_printf_ln(s, c, "%s", "");
  }
  
 -static void wt_status_print_cached_header(struct wt_status *s)
 +static void wt_longstatus_print_cached_header(struct wt_status *s)
  {
        const char *c = color(WT_STATUS_HEADER, s);
  
        status_printf_ln(s, c, "%s", "");
  }
  
 -static void wt_status_print_dirty_header(struct wt_status *s,
 -                                       int has_deleted,
 -                                       int has_dirty_submodules)
 +static void wt_longstatus_print_dirty_header(struct wt_status *s,
 +                                           int has_deleted,
 +                                           int has_dirty_submodules)
  {
        const char *c = color(WT_STATUS_HEADER, s);
  
        status_printf_ln(s, c, "%s", "");
  }
  
 -static void wt_status_print_other_header(struct wt_status *s,
 -                                       const char *what,
 -                                       const char *how)
 +static void wt_longstatus_print_other_header(struct wt_status *s,
 +                                           const char *what,
 +                                           const char *how)
  {
        const char *c = color(WT_STATUS_HEADER, s);
        status_printf_ln(s, c, "%s:", what);
        status_printf_ln(s, c, "%s", "");
  }
  
 -static void wt_status_print_trailer(struct wt_status *s)
 +static void wt_longstatus_print_trailer(struct wt_status *s)
  {
        status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
  }
@@@ -304,8 -304,8 +304,8 @@@ static int maxwidth(const char *(*label
        return result;
  }
  
 -static void wt_status_print_unmerged_data(struct wt_status *s,
 -                                        struct string_list_item *it)
 +static void wt_longstatus_print_unmerged_data(struct wt_status *s,
 +                                            struct string_list_item *it)
  {
        const char *c = color(WT_STATUS_UNMERGED, s);
        struct wt_status_change_data *d = it->util;
        strbuf_release(&onebuf);
  }
  
 -static void wt_status_print_change_data(struct wt_status *s,
 -                                      int change_type,
 -                                      struct string_list_item *it)
 +static void wt_longstatus_print_change_data(struct wt_status *s,
 +                                          int change_type,
 +                                          struct string_list_item *it)
  {
        struct wt_status_change_data *d = it->util;
        const char *c = color(change_type, s);
                status = d->worktree_status;
                break;
        default:
 -              die("BUG: unhandled change_type %d in wt_status_print_change_data",
 +              die("BUG: unhandled change_type %d in wt_longstatus_print_change_data",
                    change_type);
        }
  
@@@ -434,31 -434,6 +434,31 @@@ static void wt_status_collect_changed_c
                if (S_ISGITLINK(p->two->mode))
                        d->new_submodule_commits = !!oidcmp(&p->one->oid,
                                                            &p->two->oid);
 +
 +              switch (p->status) {
 +              case DIFF_STATUS_ADDED:
 +                      die("BUG: worktree status add???");
 +                      break;
 +
 +              case DIFF_STATUS_DELETED:
 +                      d->mode_index = p->one->mode;
 +                      oidcpy(&d->oid_index, &p->one->oid);
 +                      /* mode_worktree is zero for a delete. */
 +                      break;
 +
 +              case DIFF_STATUS_MODIFIED:
 +              case DIFF_STATUS_TYPE_CHANGED:
 +              case DIFF_STATUS_UNMERGED:
 +                      d->mode_index = p->one->mode;
 +                      d->mode_worktree = p->two->mode;
 +                      oidcpy(&d->oid_index, &p->one->oid);
 +                      break;
 +
 +              case DIFF_STATUS_UNKNOWN:
 +                      die("BUG: worktree status unknown???");
 +                      break;
 +              }
 +
        }
  }
  
@@@ -504,36 -479,12 +504,36 @@@ static void wt_status_collect_updated_c
                if (!d->index_status)
                        d->index_status = p->status;
                switch (p->status) {
 +              case DIFF_STATUS_ADDED:
 +                      /* Leave {mode,oid}_head zero for an add. */
 +                      d->mode_index = p->two->mode;
 +                      oidcpy(&d->oid_index, &p->two->oid);
 +                      break;
 +              case DIFF_STATUS_DELETED:
 +                      d->mode_head = p->one->mode;
 +                      oidcpy(&d->oid_head, &p->one->oid);
 +                      /* Leave {mode,oid}_index zero for a delete. */
 +                      break;
 +
                case DIFF_STATUS_COPIED:
                case DIFF_STATUS_RENAMED:
                        d->head_path = xstrdup(p->one->path);
 +                      d->score = p->score * 100 / MAX_SCORE;
 +                      /* fallthru */
 +              case DIFF_STATUS_MODIFIED:
 +              case DIFF_STATUS_TYPE_CHANGED:
 +                      d->mode_head = p->one->mode;
 +                      d->mode_index = p->two->mode;
 +                      oidcpy(&d->oid_head, &p->one->oid);
 +                      oidcpy(&d->oid_index, &p->two->oid);
                        break;
                case DIFF_STATUS_UNMERGED:
                        d->stagemask = unmerged_mask(p->two->path);
 +                      /*
 +                       * Don't bother setting {mode,oid}_{head,index} since the print
 +                       * code will output the stage values directly and not use the
 +                       * values in these fields.
 +                       */
                        break;
                }
        }
@@@ -614,17 -565,9 +614,17 @@@ static void wt_status_collect_changes_i
                if (ce_stage(ce)) {
                        d->index_status = DIFF_STATUS_UNMERGED;
                        d->stagemask |= (1 << (ce_stage(ce) - 1));
 -              }
 -              else
 +                      /*
 +                       * Don't bother setting {mode,oid}_{head,index} since the print
 +                       * code will output the stage values directly and not use the
 +                       * values in these fields.
 +                       */
 +              } else {
                        d->index_status = DIFF_STATUS_ADDED;
 +                      /* Leave {mode,oid}_head zero for adds. */
 +                      d->mode_index = ce->ce_mode;
 +                      hashcpy(d->oid_index.hash, ce->oid.hash);
 +              }
        }
  }
  
@@@ -684,7 -627,7 +684,7 @@@ void wt_status_collect(struct wt_statu
        wt_status_collect_untracked(s);
  }
  
 -static void wt_status_print_unmerged(struct wt_status *s)
 +static void wt_longstatus_print_unmerged(struct wt_status *s)
  {
        int shown_header = 0;
        int i;
                if (!d->stagemask)
                        continue;
                if (!shown_header) {
 -                      wt_status_print_unmerged_header(s);
 +                      wt_longstatus_print_unmerged_header(s);
                        shown_header = 1;
                }
 -              wt_status_print_unmerged_data(s, it);
 +              wt_longstatus_print_unmerged_data(s, it);
        }
        if (shown_header)
 -              wt_status_print_trailer(s);
 +              wt_longstatus_print_trailer(s);
  
  }
  
 -static void wt_status_print_updated(struct wt_status *s)
 +static void wt_longstatus_print_updated(struct wt_status *s)
  {
        int shown_header = 0;
        int i;
                    d->index_status == DIFF_STATUS_UNMERGED)
                        continue;
                if (!shown_header) {
 -                      wt_status_print_cached_header(s);
 +                      wt_longstatus_print_cached_header(s);
                        s->commitable = 1;
                        shown_header = 1;
                }
 -              wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
 +              wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
        }
        if (shown_header)
 -              wt_status_print_trailer(s);
 +              wt_longstatus_print_trailer(s);
  }
  
  /*
@@@ -760,7 -703,7 +760,7 @@@ static int wt_status_check_worktree_cha
        return changes;
  }
  
 -static void wt_status_print_changed(struct wt_status *s)
 +static void wt_longstatus_print_changed(struct wt_status *s)
  {
        int i, dirty_submodules;
        int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
        if (!worktree_changes)
                return;
  
 -      wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
 +      wt_longstatus_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
  
        for (i = 0; i < s->change.nr; i++) {
                struct wt_status_change_data *d;
                if (!d->worktree_status ||
                    d->worktree_status == DIFF_STATUS_UNMERGED)
                        continue;
 -              wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
 +              wt_longstatus_print_change_data(s, WT_STATUS_CHANGED, it);
        }
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
 -static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
 +static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted)
  {
        struct child_process sm_summary = CHILD_PROCESS_INIT;
        struct strbuf cmd_stdout = STRBUF_INIT;
        strbuf_release(&summary);
  }
  
 -static void wt_status_print_other(struct wt_status *s,
 -                                struct string_list *l,
 -                                const char *what,
 -                                const char *how)
 +static void wt_longstatus_print_other(struct wt_status *s,
 +                                    struct string_list *l,
 +                                    const char *what,
 +                                    const char *how)
  {
        int i;
        struct strbuf buf = STRBUF_INIT;
        if (!l->nr)
                return;
  
 -      wt_status_print_other_header(s, what, how);
 +      wt_longstatus_print_other_header(s, what, how);
  
        for (i = 0; i < l->nr; i++) {
                struct string_list_item *it;
@@@ -902,7 -845,7 +902,7 @@@ void wt_status_add_cut_line(FILE *fp
        strbuf_release(&buf);
  }
  
 -static void wt_status_print_verbose(struct wt_status *s)
 +static void wt_longstatus_print_verbose(struct wt_status *s)
  {
        struct rev_info rev;
        struct setup_revision_opt opt;
        if (s->verbose > 1 && s->commitable) {
                /* print_updated() printed a header, so do we */
                if (s->fp != stdout)
 -                      wt_status_print_trailer(s);
 +                      wt_longstatus_print_trailer(s);
                status_printf_ln(s, c, _("Changes to be committed:"));
                rev.diffopt.a_prefix = "c/";
                rev.diffopt.b_prefix = "i/";
        }
  }
  
 -static void wt_status_print_tracking(struct wt_status *s)
 +static void wt_longstatus_print_tracking(struct wt_status *s)
  {
        struct strbuf sb = STRBUF_INIT;
        const char *cp, *ep, *branch_name;
@@@ -1019,7 -962,7 +1019,7 @@@ static void show_merge_in_progress(stru
                        status_printf_ln(s, color,
                                _("  (use \"git commit\" to conclude merge)"));
        }
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
  static void show_am_in_progress(struct wt_status *s,
                status_printf_ln(s, color,
                        _("  (use \"git am --abort\" to restore the original branch)"));
        }
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
  static char *read_line_from_git_path(const char *filename)
@@@ -1264,7 -1207,7 +1264,7 @@@ static void show_rebase_in_progress(str
                                _("  (use \"git rebase --continue\" once you are satisfied with your changes)"));
                }
        }
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
  static void show_cherry_pick_in_progress(struct wt_status *s,
                status_printf_ln(s, color,
                        _("  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
        }
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
  static void show_revert_in_progress(struct wt_status *s,
                status_printf_ln(s, color,
                        _("  (use \"git revert --abort\" to cancel the revert operation)"));
        }
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
  static void show_bisect_in_progress(struct wt_status *s,
        if (s->hints)
                status_printf_ln(s, color,
                        _("  (use \"git bisect reset\" to get back to the original branch)"));
 -      wt_status_print_trailer(s);
 +      wt_longstatus_print_trailer(s);
  }
  
  /*
@@@ -1383,8 -1326,7 +1383,7 @@@ static int grab_1st_switch(unsigned cha
        if (!strcmp(cb->buf.buf, "HEAD")) {
                /* HEAD is relative. Resolve it to the right reflog entry. */
                strbuf_reset(&cb->buf);
-               strbuf_addstr(&cb->buf,
-                             find_unique_abbrev(nsha1, DEFAULT_ABBREV));
+               strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV);
        }
        return 1;
  }
@@@ -1489,8 -1431,8 +1488,8 @@@ void wt_status_get_state(struct wt_stat
                wt_status_get_detached_from(state);
  }
  
 -static void wt_status_print_state(struct wt_status *s,
 -                                struct wt_status_state *state)
 +static void wt_longstatus_print_state(struct wt_status *s,
 +                                    struct wt_status_state *state)
  {
        const char *state_color = color(WT_STATUS_HEADER, s);
        if (state->merge_in_progress)
                show_bisect_in_progress(s, state, state_color);
  }
  
 -void wt_status_print(struct wt_status *s)
 +static void wt_longstatus_print(struct wt_status *s)
  {
        const char *branch_color = color(WT_STATUS_ONBRANCH, s);
        const char *branch_status_color = color(WT_STATUS_HEADER, s);
                status_printf_more(s, branch_status_color, "%s", on_what);
                status_printf_more(s, branch_color, "%s\n", branch_name);
                if (!s->is_initial)
 -                      wt_status_print_tracking(s);
 +                      wt_longstatus_print_tracking(s);
        }
  
 -      wt_status_print_state(s, &state);
 +      wt_longstatus_print_state(s, &state);
        free(state.branch);
        free(state.onto);
        free(state.detached_from);
                status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
        }
  
 -      wt_status_print_updated(s);
 -      wt_status_print_unmerged(s);
 -      wt_status_print_changed(s);
 +      wt_longstatus_print_updated(s);
 +      wt_longstatus_print_unmerged(s);
 +      wt_longstatus_print_changed(s);
        if (s->submodule_summary &&
            (!s->ignore_submodule_arg ||
             strcmp(s->ignore_submodule_arg, "all"))) {
 -              wt_status_print_submodule_summary(s, 0);  /* staged */
 -              wt_status_print_submodule_summary(s, 1);  /* unstaged */
 +              wt_longstatus_print_submodule_summary(s, 0);  /* staged */
 +              wt_longstatus_print_submodule_summary(s, 1);  /* unstaged */
        }
        if (s->show_untracked_files) {
 -              wt_status_print_other(s, &s->untracked, _("Untracked files"), "add");
 +              wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
                if (s->show_ignored_files)
 -                      wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
 +                      wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
                if (advice_status_u_option && 2000 < s->untracked_in_ms) {
                        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                        ? _(" (use -u option to show untracked files)") : "");
  
        if (s->verbose)
 -              wt_status_print_verbose(s);
 +              wt_longstatus_print_verbose(s);
        if (!s->commitable) {
                if (s->amend)
                        status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
@@@ -1774,7 -1716,7 +1773,7 @@@ static void wt_shortstatus_print_tracki
        fputc(s->null_termination ? '\0' : '\n', s->fp);
  }
  
 -void wt_shortstatus_print(struct wt_status *s)
 +static void wt_shortstatus_print(struct wt_status *s)
  {
        int i;
  
        }
  }
  
 -void wt_porcelain_print(struct wt_status *s)
 +static void wt_porcelain_print(struct wt_status *s)
  {
        s->use_color = 0;
        s->relative_paths = 0;
        s->no_gettext = 1;
        wt_shortstatus_print(s);
  }
 +
 +/*
 + * Print branch information for porcelain v2 output.  These lines
 + * are printed when the '--branch' parameter is given.
 + *
 + *    # branch.oid <commit><eol>
 + *    # branch.head <head><eol>
 + *   [# branch.upstream <upstream><eol>
 + *   [# branch.ab +<ahead> -<behind><eol>]]
 + *
 + *      <commit> ::= the current commit hash or the the literal
 + *                   "(initial)" to indicate an initialized repo
 + *                   with no commits.
 + *
 + *        <head> ::= <branch_name> the current branch name or
 + *                   "(detached)" literal when detached head or
 + *                   "(unknown)" when something is wrong.
 + *
 + *    <upstream> ::= the upstream branch name, when set.
 + *
 + *       <ahead> ::= integer ahead value, when upstream set
 + *                   and the commit is present (not gone).
 + *
 + *      <behind> ::= integer behind value, when upstream set
 + *                   and commit is present.
 + *
 + *
 + * The end-of-line is defined by the -z flag.
 + *
 + *                 <eol> ::= NUL when -z,
 + *                           LF when NOT -z.
 + *
 + */
 +static void wt_porcelain_v2_print_tracking(struct wt_status *s)
 +{
 +      struct branch *branch;
 +      const char *base;
 +      const char *branch_name;
 +      struct wt_status_state state;
 +      int ab_info, nr_ahead, nr_behind;
 +      char eol = s->null_termination ? '\0' : '\n';
 +
 +      memset(&state, 0, sizeof(state));
 +      wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD"));
 +
 +      fprintf(s->fp, "# branch.oid %s%c",
 +                      (s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)),
 +                      eol);
 +
 +      if (!s->branch)
 +              fprintf(s->fp, "# branch.head %s%c", "(unknown)", eol);
 +      else {
 +              if (!strcmp(s->branch, "HEAD")) {
 +                      fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
 +
 +                      if (state.rebase_in_progress || state.rebase_interactive_in_progress)
 +                              branch_name = state.onto;
 +                      else if (state.detached_from)
 +                              branch_name = state.detached_from;
 +                      else
 +                              branch_name = "";
 +              } else {
 +                      branch_name = NULL;
 +                      skip_prefix(s->branch, "refs/heads/", &branch_name);
 +
 +                      fprintf(s->fp, "# branch.head %s%c", branch_name, eol);
 +              }
 +
 +              /* Lookup stats on the upstream tracking branch, if set. */
 +              branch = branch_get(branch_name);
 +              base = NULL;
 +              ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0);
 +              if (base) {
 +                      base = shorten_unambiguous_ref(base, 0);
 +                      fprintf(s->fp, "# branch.upstream %s%c", base, eol);
 +                      free((char *)base);
 +
 +                      if (ab_info)
 +                              fprintf(s->fp, "# branch.ab +%d -%d%c", nr_ahead, nr_behind, eol);
 +              }
 +      }
 +
 +      free(state.branch);
 +      free(state.onto);
 +      free(state.detached_from);
 +}
 +
 +/*
 + * Convert various submodule status values into a
 + * fixed-length string of characters in the buffer provided.
 + */
 +static void wt_porcelain_v2_submodule_state(
 +      struct wt_status_change_data *d,
 +      char sub[5])
 +{
 +      if (S_ISGITLINK(d->mode_head) ||
 +              S_ISGITLINK(d->mode_index) ||
 +              S_ISGITLINK(d->mode_worktree)) {
 +              sub[0] = 'S';
 +              sub[1] = d->new_submodule_commits ? 'C' : '.';
 +              sub[2] = (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) ? 'M' : '.';
 +              sub[3] = (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ? 'U' : '.';
 +      } else {
 +              sub[0] = 'N';
 +              sub[1] = '.';
 +              sub[2] = '.';
 +              sub[3] = '.';
 +      }
 +      sub[4] = 0;
 +}
 +
 +/*
 + * Fix-up changed entries before we print them.
 + */
 +static void wt_porcelain_v2_fix_up_changed(
 +      struct string_list_item *it,
 +      struct wt_status *s)
 +{
 +      struct wt_status_change_data *d = it->util;
 +
 +      if (!d->index_status) {
 +              /*
 +               * This entry is unchanged in the index (relative to the head).
 +               * Therefore, the collect_updated_cb was never called for this
 +               * entry (during the head-vs-index scan) and so the head column
 +               * fields were never set.
 +               *
 +               * We must have data for the index column (from the
 +               * index-vs-worktree scan (otherwise, this entry should not be
 +               * in the list of changes)).
 +               *
 +               * Copy index column fields to the head column, so that our
 +               * output looks complete.
 +               */
 +              assert(d->mode_head == 0);
 +              d->mode_head = d->mode_index;
 +              oidcpy(&d->oid_head, &d->oid_index);
 +      }
 +
 +      if (!d->worktree_status) {
 +              /*
 +               * This entry is unchanged in the worktree (relative to the index).
 +               * Therefore, the collect_changed_cb was never called for this entry
 +               * (during the index-vs-worktree scan) and so the worktree column
 +               * fields were never set.
 +               *
 +               * We must have data for the index column (from the head-vs-index
 +               * scan).
 +               *
 +               * Copy the index column fields to the worktree column so that
 +               * our output looks complete.
 +               *
 +               * Note that we only have a mode field in the worktree column
 +               * because the scan code tries really hard to not have to compute it.
 +               */
 +              assert(d->mode_worktree == 0);
 +              d->mode_worktree = d->mode_index;
 +      }
 +}
 +
 +/*
 + * Print porcelain v2 info for tracked entries with changes.
 + */
 +static void wt_porcelain_v2_print_changed_entry(
 +      struct string_list_item *it,
 +      struct wt_status *s)
 +{
 +      struct wt_status_change_data *d = it->util;
 +      struct strbuf buf_index = STRBUF_INIT;
 +      struct strbuf buf_head = STRBUF_INIT;
 +      const char *path_index = NULL;
 +      const char *path_head = NULL;
 +      char key[3];
 +      char submodule_token[5];
 +      char sep_char, eol_char;
 +
 +      wt_porcelain_v2_fix_up_changed(it, s);
 +      wt_porcelain_v2_submodule_state(d, submodule_token);
 +
 +      key[0] = d->index_status ? d->index_status : '.';
 +      key[1] = d->worktree_status ? d->worktree_status : '.';
 +      key[2] = 0;
 +
 +      if (s->null_termination) {
 +              /*
 +               * In -z mode, we DO NOT C-quote pathnames.  Current path is ALWAYS first.
 +               * A single NUL character separates them.
 +               */
 +              sep_char = '\0';
 +              eol_char = '\0';
 +              path_index = it->string;
 +              path_head = d->head_path;
 +      } else {
 +              /*
 +               * Path(s) are C-quoted if necessary. Current path is ALWAYS first.
 +               * The source path is only present when necessary.
 +               * A single TAB separates them (because paths can contain spaces
 +               * which are not escaped and C-quoting does escape TAB characters).
 +               */
 +              sep_char = '\t';
 +              eol_char = '\n';
 +              path_index = quote_path(it->string, s->prefix, &buf_index);
 +              if (d->head_path)
 +                      path_head = quote_path(d->head_path, s->prefix, &buf_head);
 +      }
 +
 +      if (path_head)
 +              fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c",
 +                              key, submodule_token,
 +                              d->mode_head, d->mode_index, d->mode_worktree,
 +                              oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
 +                              key[0], d->score,
 +                              path_index, sep_char, path_head, eol_char);
 +      else
 +              fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c",
 +                              key, submodule_token,
 +                              d->mode_head, d->mode_index, d->mode_worktree,
 +                              oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
 +                              path_index, eol_char);
 +
 +      strbuf_release(&buf_index);
 +      strbuf_release(&buf_head);
 +}
 +
 +/*
 + * Print porcelain v2 status info for unmerged entries.
 + */
 +static void wt_porcelain_v2_print_unmerged_entry(
 +      struct string_list_item *it,
 +      struct wt_status *s)
 +{
 +      struct wt_status_change_data *d = it->util;
 +      const struct cache_entry *ce;
 +      struct strbuf buf_index = STRBUF_INIT;
 +      const char *path_index = NULL;
 +      int pos, stage, sum;
 +      struct {
 +              int mode;
 +              struct object_id oid;
 +      } stages[3];
 +      char *key;
 +      char submodule_token[5];
 +      char unmerged_prefix = 'u';
 +      char eol_char = s->null_termination ? '\0' : '\n';
 +
 +      wt_porcelain_v2_submodule_state(d, submodule_token);
 +
 +      switch (d->stagemask) {
 +      case 1: key = "DD"; break; /* both deleted */
 +      case 2: key = "AU"; break; /* added by us */
 +      case 3: key = "UD"; break; /* deleted by them */
 +      case 4: key = "UA"; break; /* added by them */
 +      case 5: key = "DU"; break; /* deleted by us */
 +      case 6: key = "AA"; break; /* both added */
 +      case 7: key = "UU"; break; /* both modified */
 +      default:
 +              die("BUG: unhandled unmerged status %x", d->stagemask);
 +      }
 +
 +      /*
 +       * Disregard d.aux.porcelain_v2 data that we accumulated
 +       * for the head and index columns during the scans and
 +       * replace with the actual stage data.
 +       *
 +       * Note that this is a last-one-wins for each the individual
 +       * stage [123] columns in the event of multiple cache entries
 +       * for same stage.
 +       */
 +      memset(stages, 0, sizeof(stages));
 +      sum = 0;
 +      pos = cache_name_pos(it->string, strlen(it->string));
 +      assert(pos < 0);
 +      pos = -pos-1;
 +      while (pos < active_nr) {
 +              ce = active_cache[pos++];
 +              stage = ce_stage(ce);
 +              if (strcmp(ce->name, it->string) || !stage)
 +                      break;
 +              stages[stage - 1].mode = ce->ce_mode;
 +              hashcpy(stages[stage - 1].oid.hash, ce->oid.hash);
 +              sum |= (1 << (stage - 1));
 +      }
 +      if (sum != d->stagemask)
 +              die("BUG: observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
 +
 +      if (s->null_termination)
 +              path_index = it->string;
 +      else
 +              path_index = quote_path(it->string, s->prefix, &buf_index);
 +
 +      fprintf(s->fp, "%c %s %s %06o %06o %06o %06o %s %s %s %s%c",
 +                      unmerged_prefix, key, submodule_token,
 +                      stages[0].mode, /* stage 1 */
 +                      stages[1].mode, /* stage 2 */
 +                      stages[2].mode, /* stage 3 */
 +                      d->mode_worktree,
 +                      oid_to_hex(&stages[0].oid), /* stage 1 */
 +                      oid_to_hex(&stages[1].oid), /* stage 2 */
 +                      oid_to_hex(&stages[2].oid), /* stage 3 */
 +                      path_index,
 +                      eol_char);
 +
 +      strbuf_release(&buf_index);
 +}
 +
 +/*
 + * Print porcelain V2 status info for untracked and ignored entries.
 + */
 +static void wt_porcelain_v2_print_other(
 +      struct string_list_item *it,
 +      struct wt_status *s,
 +      char prefix)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      const char *path;
 +      char eol_char;
 +
 +      if (s->null_termination) {
 +              path = it->string;
 +              eol_char = '\0';
 +      } else {
 +              path = quote_path(it->string, s->prefix, &buf);
 +              eol_char = '\n';
 +      }
 +
 +      fprintf(s->fp, "%c %s%c", prefix, path, eol_char);
 +
 +      strbuf_release(&buf);
 +}
 +
 +/*
 + * Print porcelain V2 status.
 + *
 + * [<v2_branch>]
 + * [<v2_changed_items>]*
 + * [<v2_unmerged_items>]*
 + * [<v2_untracked_items>]*
 + * [<v2_ignored_items>]*
 + *
 + */
 +static void wt_porcelain_v2_print(struct wt_status *s)
 +{
 +      struct wt_status_change_data *d;
 +      struct string_list_item *it;
 +      int i;
 +
 +      if (s->show_branch)
 +              wt_porcelain_v2_print_tracking(s);
 +
 +      for (i = 0; i < s->change.nr; i++) {
 +              it = &(s->change.items[i]);
 +              d = it->util;
 +              if (!d->stagemask)
 +                      wt_porcelain_v2_print_changed_entry(it, s);
 +      }
 +
 +      for (i = 0; i < s->change.nr; i++) {
 +              it = &(s->change.items[i]);
 +              d = it->util;
 +              if (d->stagemask)
 +                      wt_porcelain_v2_print_unmerged_entry(it, s);
 +      }
 +
 +      for (i = 0; i < s->untracked.nr; i++) {
 +              it = &(s->untracked.items[i]);
 +              wt_porcelain_v2_print_other(it, s, '?');
 +      }
 +
 +      for (i = 0; i < s->ignored.nr; i++) {
 +              it = &(s->ignored.items[i]);
 +              wt_porcelain_v2_print_other(it, s, '!');
 +      }
 +}
 +
 +void wt_status_print(struct wt_status *s)
 +{
 +      switch (s->status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(s);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(s);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN_V2:
 +              wt_porcelain_v2_print(s);
 +              break;
 +      case STATUS_FORMAT_UNSPECIFIED:
 +              die("BUG: finalize_deferred_config() should have been called");
 +              break;
 +      case STATUS_FORMAT_NONE:
 +      case STATUS_FORMAT_LONG:
 +              wt_longstatus_print(s);
 +              break;
 +      }
 +}