Merge branch 'rs/cocci'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:21 +0000 (13:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:21 +0000 (13:25 -0700)
Code cleanup.

* rs/cocci:
use strbuf_add_unique_abbrev() for adding short hashes, part 3
remove unnecessary NULL check before free(3)

1  2 
merge-recursive.c
parse-options-cb.c
pretty.c
submodule.c
wt-status.c
diff --combined merge-recursive.c
index 5200d5ccf8e10b9d5726f1c814f43dfebf32531d,aa92e30f639027b5e4a661d3691493be5713f631..9041c2f149c01134ce02119354455894533e713c
@@@ -202,9 -202,9 +202,9 @@@ static void output_commit_title(struct 
                strbuf_addf(&o->obuf, "virtual %s\n",
                        merge_remote_util(commit)->name);
        else {
-               strbuf_addf(&o->obuf, "%s ",
-                       find_unique_abbrev(commit->object.oid.hash,
-                               DEFAULT_ABBREV));
+               strbuf_add_unique_abbrev(&o->obuf, commit->object.oid.hash,
+                                        DEFAULT_ABBREV);
+               strbuf_addch(&o->obuf, ' ');
                if (parse_commit(commit) != 0)
                        strbuf_addstr(&o->obuf, _("(bad commit)\n"));
                else {
@@@ -382,7 -382,7 +382,7 @@@ static struct string_list *get_unmerged
                }
                e = item->util;
                e->stages[ce_stage(ce)].mode = ce->ce_mode;
 -              hashcpy(e->stages[ce_stage(ce)].oid.hash, ce->sha1);
 +              oidcpy(&e->stages[ce_stage(ce)].oid, &ce->oid);
        }
  
        return unmerged;
@@@ -910,9 -910,9 +910,9 @@@ static int merge_3way(struct merge_opti
                name2 = mkpathdup("%s", branch2);
        }
  
 -      read_mmblob(&orig, one->oid.hash);
 -      read_mmblob(&src1, a->oid.hash);
 -      read_mmblob(&src2, b->oid.hash);
 +      read_mmblob(&orig, &one->oid);
 +      read_mmblob(&src1, &a->oid);
 +      read_mmblob(&src2, &b->oid);
  
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
                                &src1, name1, &src2, name2, &ll_opts);
diff --combined parse-options-cb.c
index b5d920914e2a3ba3a46017f89f9952d08b8e5b58,16818830e97e099286f3aa259378de1b8e9fc322..b7d8f7dcb2c3ae7568542832accadab84c7ae314
@@@ -158,18 -158,6 +158,18 @@@ int parse_opt_noop_cb(const struct opti
        return 0;
  }
  
 +/**
 + * Report that the option is unknown, so that other code can handle
 + * it. This can be used as a callback together with
 + * OPTION_LOWLEVEL_CALLBACK to allow an option to be documented in the
 + * "-h" output even if it's not being handled directly by
 + * parse_options().
 + */
 +int parse_opt_unknown_cb(const struct option *opt, const char *arg, int unset)
 +{
 +      return -2;
 +}
 +
  /**
   * Recreates the command-line option in the strbuf.
   */
@@@ -211,8 -199,7 +211,7 @@@ int parse_opt_passthru(const struct opt
        if (recreate_opt(&sb, opt, arg, unset) < 0)
                return -1;
  
-       if (*opt_value)
-               free(*opt_value);
+       free(*opt_value);
  
        *opt_value = strbuf_detach(&sb, NULL);
  
diff --combined pretty.c
index 25efbcac9206e82343f954405176fbf477055903,19089b35a97c0657a312c5fffe20e56cff120406..0c3149524059bd46c3c6d70bd774df0aaa65f976
+++ b/pretty.c
@@@ -544,15 -544,13 +544,13 @@@ static void add_merge_info(const struc
        strbuf_addstr(sb, "Merge:");
  
        while (parent) {
-               struct commit *p = parent->item;
-               const char *hex = NULL;
+               struct object_id *oidp = &parent->item->object.oid;
+               strbuf_addch(sb, ' ');
                if (pp->abbrev)
-                       hex = find_unique_abbrev(p->object.oid.hash, pp->abbrev);
-               if (!hex)
-                       hex = oid_to_hex(&p->object.oid);
+                       strbuf_add_unique_abbrev(sb, oidp->hash, pp->abbrev);
+               else
+                       strbuf_addstr(sb, oid_to_hex(oidp));
                parent = parent->next;
-               strbuf_addf(sb, " %s", hex);
        }
        strbuf_addch(sb, '\n');
  }
@@@ -1065,15 -1063,13 +1063,15 @@@ static size_t format_commit_one(struct 
        const struct commit *commit = c->commit;
        const char *msg = c->message;
        struct commit_list *p;
 -      int h1, h2;
 +      int ch;
  
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = want_color(c->pretty_ctx->color);
 +                      if (c->auto_color && sb->len)
 +                              strbuf_addstr(sb, GIT_COLOR_RESET);
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
                        int ret = parse_color(sb, placeholder, c);
                return 1;
        case 'x':
                /* %x00 == NUL, %x0a == LF, etc. */
 -              if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) &&
 -                  h1 <= 16 &&
 -                  0 <= (h2 = hexval_table[0xff & placeholder[2]]) &&
 -                  h2 <= 16) {
 -                      strbuf_addch(sb, (h1<<4)|h2);
 -                      return 3;
 -              } else
 +              ch = hex2chr(placeholder + 1);
 +              if (ch < 0)
                        return 0;
 +              strbuf_addch(sb, ch);
 +              return 3;
        case 'w':
                if (placeholder[1] == '(') {
                        unsigned long width = 0, indent1 = 0, indent2 = 0;
diff --combined submodule.c
index 733332035b3c19279885175e0689665a34dc13ef,f2ba4a2dc2a4894d76006d4ef8ba5a9bd64f153d..6f7d883de950af8ba6427537d09e021a4bed36bd
@@@ -123,16 -123,35 +123,16 @@@ void stage_updated_gitmodules(void
  static int add_submodule_odb(const char *path)
  {
        struct strbuf objects_directory = STRBUF_INIT;
 -      struct alternate_object_database *alt_odb;
        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;
        }
 -      /* avoid adding it twice */
 -      prepare_alt_odb();
 -      for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
 -              if (alt_odb->name - alt_odb->base == objects_directory.len &&
 -                              !strncmp(alt_odb->base, objects_directory.buf,
 -                                      objects_directory.len))
 -                      goto done;
 -
 -      alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */
 -      alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc));
 -      alt_odb->next = alt_odb_list;
 -      xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
 -      alt_odb->name = alt_odb->base + objects_directory.len;
 -      alt_odb->name[2] = '/';
 -      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);
 +      add_to_alternates_memory(objects_directory.buf);
  done:
        strbuf_release(&objects_directory);
        return ret;
@@@ -259,9 -278,9 +259,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,
@@@ -307,23 -333,31 +307,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;
        }
  
-       strbuf_addf(&sb, "%s%sSubmodule %s %s..", line_prefix, meta, path,
-                       find_unique_abbrev(one->hash, DEFAULT_ABBREV));
-       if (!fast_backward && !fast_forward)
-               strbuf_addch(&sb, '.');
 +output_header:
 -      strbuf_add_unique_abbrev(&sb, one, DEFAULT_ABBREV);
+       strbuf_addf(&sb, "%s%sSubmodule %s ", line_prefix, meta, path);
 -      strbuf_add_unique_abbrev(&sb, two, DEFAULT_ABBREV);
++      strbuf_add_unique_abbrev(&sb, one->hash, DEFAULT_ABBREV);
+       strbuf_addstr(&sb, (fast_backward || fast_forward) ? ".." : "...");
 +      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)
@@@ -707,10 -607,9 +706,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)
@@@ -1260,5 -1159,4 +1259,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 99d1b0a8180f34bb928ad92612b6a11922907e27,7004a2d588769b76e95c07c8471569b2d6bf6c56..ca5c45f94522f5487e3a93438cbccf8aecb65d5d
@@@ -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)
@@@ -1110,7 -1053,6 +1110,6 @@@ static void abbrev_sha1_in_line(struct 
        split = strbuf_split_max(line, ' ', 3);
        if (split[0] && split[1]) {
                unsigned char sha1[20];
-               const char *abbrev;
  
                /*
                 * strbuf_split_max left a space. Trim it and re-add
                 */
                strbuf_trim(split[1]);
                if (!get_sha1(split[1]->buf, sha1)) {
-                       abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
                        strbuf_reset(split[1]);
-                       strbuf_addf(split[1], "%s ", abbrev);
+                       strbuf_add_unique_abbrev(split[1], sha1,
+                                                DEFAULT_ABBREV);
+                       strbuf_addch(split[1], ' ');
                        strbuf_reset(line);
                        for (i = 0; split[i]; i++)
                                strbuf_addbuf(line, split[i]);
@@@ -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);
  }
  
  /*
@@@ -1343,10 -1286,8 +1343,8 @@@ static char *get_branch(const struct wo
        else if (starts_with(sb.buf, "refs/"))
                ;
        else if (!get_sha1_hex(sb.buf, sha1)) {
-               const char *abbrev;
-               abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
                strbuf_reset(&sb);
-               strbuf_addstr(&sb, abbrev);
+               strbuf_add_unique_abbrev(&sb, sha1, DEFAULT_ABBREV);
        } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
                goto got_nothing;
        else                    /* bisect */
@@@ -1488,8 -1429,8 +1486,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"));
@@@ -1773,7 -1714,7 +1771,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;
 +      }
 +}