Merge branch 'jk/maint-status-porcelain-z-b' into HEAD
authorJeff King <peff@peff.net>
Tue, 8 May 2012 08:55:35 +0000 (04:55 -0400)
committerJeff King <peff@peff.net>
Tue, 8 May 2012 08:55:35 +0000 (04:55 -0400)
* jk/maint-status-porcelain-z-b:
  status: respect "-b" for porcelain format
  status: fix null termination with "-b"
  status: refactor null_termination option
  commit: refactor option parsing

Conflicts:
wt-status.h

1  2 
Documentation/git-status.txt
builtin/commit.c
t/t7508-status.sh
wt-status.c
wt-status.h

@@@ -77,13 -77,6 +77,13 @@@ configuration variable documented in li
        Terminate entries with NUL, instead of LF.  This implies
        the `--porcelain` output format if no other format is given.
  
 +--column[=<options>]::
 +--no-column::
 +      Display untracked files in columns. See configuration variable
 +      column.status for option syntax.`--column` and `--no-column`
 +      without options are equivalent to 'always' and 'never'
 +      respectively.
 +
  
  OUTPUT
  ------
@@@ -105,12 -98,12 +105,12 @@@ In the short-format, the status of eac
  
        XY PATH1 -> PATH2
  
 -where `PATH1` is the path in the `HEAD`, and the ` \-> PATH2` part is
 +where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
  shown only when `PATH1` corresponds to a different path in the
  index/worktree (i.e. the file is renamed). The 'XY' is a two-letter
  status code.
  
 -The fields (including the `\->`) are separated from each other by a
 +The fields (including the `->`) are separated from each other by a
  single space. If a filename contains whitespace or other nonprintable
  characters, that field will be quoted in the manner of a C string
  literal: surrounded by ASCII double quote (34) characters, and with
@@@ -184,7 -177,7 +184,7 @@@ order is reversed (e.g 'from \-> to' be
  and the terminating newline (but a space still separates the status
  field from the first filename).  Third, filenames containing special
  characters are not specially formatted; no quoting or
- backslash-escaping is performed. Fourth, there is no branch line.
+ backslash-escaping is performed.
  
  CONFIGURATION
  -------------
diff --combined builtin/commit.c
@@@ -27,7 -27,6 +27,7 @@@
  #include "quote.h"
  #include "submodule.h"
  #include "gpg-interface.h"
 +#include "column.h"
  
  static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@@ -89,7 -88,6 +89,7 @@@ static int quiet, verbose, no_verify, a
  static int no_post_rewrite, allow_empty_message;
  static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
  static char *sign_commit;
 +static unsigned int colopts;
  
  /*
   * The default commit message cleanup mode will remove the lines
@@@ -111,13 -109,11 +111,11 @@@ static int show_ignored_in_status
  static const char *only_include_assumed;
  static struct strbuf message = STRBUF_INIT;
  
- static int null_termination;
  static enum {
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
        STATUS_FORMAT_PORCELAIN
  } status_format = STATUS_FORMAT_LONG;
- static int status_show_branch;
  
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
        return 0;
  }
  
- static struct option builtin_commit_options[] = {
-       OPT__QUIET(&quiet, "suppress summary after successful commit"),
-       OPT__VERBOSE(&verbose, "show diff in commit message template"),
-       OPT_GROUP("Commit message options"),
-       OPT_FILENAME('F', "file", &logfile, "read message from file"),
-       OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
-       OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
-       OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
-       OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
-       OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
-       OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
-       OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
-       OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
-       OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-       OPT_FILENAME('t', "template", &template_file, "use specified template file"),
-       OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
-       OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
-       OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
-       { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
-         "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-       /* end commit message options */
-       OPT_GROUP("Commit contents options"),
-       OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
-       OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
-       OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
-       OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
-       OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
-       OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
-       OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
-       OPT_SET_INT(0, "short", &status_format, "show status concisely",
-                   STATUS_FORMAT_SHORT),
-       OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
-       OPT_SET_INT(0, "porcelain", &status_format,
-                   "machine-readable output", STATUS_FORMAT_PORCELAIN),
-       OPT_BOOLEAN('z', "null", &null_termination,
-                   "terminate entries with NUL"),
-       OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
-       OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
-       { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
-       /* end commit contents options */
-       { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
-         "ok to record an empty change",
-         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
-         "ok to record a change with an empty message",
-         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       OPT_END()
- };
  static void determine_whence(struct wt_status *s)
  {
        if (file_exists(git_path("MERGE_HEAD")))
                s->whence = whence;
  }
  
 -static const char *whence_s(void)
 -{
 -      const char *s = "";
 -
 -      switch (whence) {
 -      case FROM_COMMIT:
 -              break;
 -      case FROM_MERGE:
 -              s = _("merge");
 -              break;
 -      case FROM_CHERRY_PICK:
 -              s = _("cherry-pick");
 -              break;
 -      }
 -
 -      return s;
 -}
 -
  static void rollback_index_files(void)
  {
        switch (commit_style) {
@@@ -437,12 -398,8 +382,12 @@@ static char *prepare_index(int argc, co
         */
        commit_style = COMMIT_PARTIAL;
  
 -      if (whence != FROM_COMMIT)
 -              die(_("cannot do a partial commit during a %s."), whence_s());
 +      if (whence != FROM_COMMIT) {
 +              if (whence == FROM_MERGE)
 +                      die(_("cannot do a partial commit during a merge."));
 +              else if (whence == FROM_CHERRY_PICK)
 +                      die(_("cannot do a partial commit during a cherry-pick."));
 +      }
  
        memset(&partial, 0, sizeof(partial));
        partial.strdup_strings = 1;
@@@ -501,10 -458,10 +446,10 @@@ static int run_status(FILE *fp, const c
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(s, null_termination, status_show_branch);
+               wt_shortstatus_print(s);
                break;
        case STATUS_FORMAT_PORCELAIN:
-               wt_porcelain_print(s, null_termination);
+               wt_porcelain_print(s);
                break;
        case STATUS_FORMAT_LONG:
                wt_status_print(s);
@@@ -784,31 -741,28 +729,31 @@@ static int prepare_to_commit(const cha
                char *ai_tmp, *ci_tmp;
                if (whence != FROM_COMMIT)
                        status_printf_ln(s, GIT_COLOR_NORMAL,
 -                              _("\n"
 -                              "It looks like you may be committing a %s.\n"
 -                              "If this is not correct, please remove the file\n"
 -                              "       %s\n"
 -                              "and try again.\n"
 -                              ""),
 -                              whence_s(),
 +                          whence == FROM_MERGE
 +                              ? _("\n"
 +                                      "It looks like you may be committing a merge.\n"
 +                                      "If this is not correct, please remove the file\n"
 +                                      "       %s\n"
 +                                      "and try again.\n")
 +                              : _("\n"
 +                                      "It looks like you may be committing a cherry-pick.\n"
 +                                      "If this is not correct, please remove the file\n"
 +                                      "       %s\n"
 +                                      "and try again.\n"),
                                git_path(whence == FROM_MERGE
                                         ? "MERGE_HEAD"
                                         : "CHERRY_PICK_HEAD"));
  
                fprintf(s->fp, "\n");
 -              status_printf(s, GIT_COLOR_NORMAL,
 -                      _("Please enter the commit message for your changes."));
                if (cleanup_mode == CLEANUP_ALL)
 -                      status_printf_more(s, GIT_COLOR_NORMAL,
 -                              _(" Lines starting\n"
 -                              "with '#' will be ignored, and an empty"
 +                      status_printf(s, GIT_COLOR_NORMAL,
 +                              _("Please enter the commit message for your changes."
 +                              " Lines starting\nwith '#' will be ignored, and an empty"
                                " message aborts the commit.\n"));
                else /* CLEANUP_SPACE, that is. */
 -                      status_printf_more(s, GIT_COLOR_NORMAL,
 -                              _(" Lines starting\n"
 +                      status_printf(s, GIT_COLOR_NORMAL,
 +                              _("Please enter the commit message for your changes."
 +                              " Lines starting\n"
                                "with '#' will be kept; you may remove them"
                                " yourself if you want to.\n"
                                "An empty message aborts the commit.\n"));
@@@ -1037,6 -991,7 +982,7 @@@ static const char *read_commit_message(
  }
  
  static int parse_and_validate_options(int argc, const char *argv[],
+                                     const struct option *options,
                                      const char * const usage[],
                                      const char *prefix,
                                      struct commit *current_head,
  {
        int f = 0;
  
-       argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
-                            0);
+       argc = parse_options(argc, argv, prefix, options, usage, 0);
  
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
        /* Sanity check options */
        if (amend && !current_head)
                die(_("You have nothing to amend."));
 -      if (amend && whence != FROM_COMMIT)
 -              die(_("You are in the middle of a %s -- cannot amend."), whence_s());
 +      if (amend && whence != FROM_COMMIT) {
 +              if (whence == FROM_MERGE)
 +                      die(_("You are in the middle of a merge -- cannot amend."));
 +              else if (whence == FROM_CHERRY_PICK)
 +                      die(_("You are in the middle of a cherry-pick -- cannot amend."));
 +      }
        if (fixup_message && squash_message)
                die(_("Options --squash and --fixup cannot be used together"));
        if (use_message)
        if (all && argc > 0)
                die(_("Paths with -a does not make sense."));
  
-       if (null_termination && status_format == STATUS_FORMAT_LONG)
+       if (s->null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
        if (status_format != STATUS_FORMAT_LONG)
                dry_run = 1;
@@@ -1175,8 -1125,6 +1120,8 @@@ static int git_status_config(const cha
  {
        struct wt_status *s = cb;
  
 +      if (!prefixcmp(k, "column."))
 +              return git_column_config(k, v, "status", &colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
                s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
  
  int cmd_status(int argc, const char **argv, const char *prefix)
  {
-       struct wt_status s;
+       static struct wt_status s;
        int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose, "be verbose"),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN('b', "branch", &status_show_branch,
+               OPT_BOOLEAN('b', "branch", &s.show_branch,
                            "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
                            "machine-readable output",
                            STATUS_FORMAT_PORCELAIN),
-               OPT_BOOLEAN('z', "null", &null_termination,
+               OPT_BOOLEAN('z', "null", &s.null_termination,
                            "terminate entries with NUL"),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
                  "mode",
                { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
                  "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 +              OPT_COLUMN(0, "column", &colopts, "list untracked files in columns"),
                OPT_END(),
        };
  
        argc = parse_options(argc, argv, prefix,
                             builtin_status_options,
                             builtin_status_usage, 0);
 +      finalize_colopts(&colopts, -1);
 +      s.colopts = colopts;
  
-       if (null_termination && status_format == STATUS_FORMAT_LONG)
+       if (s.null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
  
        handle_untracked_files_arg(&s);
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(&s, null_termination, status_show_branch);
+               wt_shortstatus_print(&s);
                break;
        case STATUS_FORMAT_PORCELAIN:
-               wt_porcelain_print(&s, null_termination);
+               wt_porcelain_print(&s);
                break;
        case STATUS_FORMAT_LONG:
                s.verbose = verbose;
@@@ -1422,6 -1367,60 +1367,60 @@@ static int run_rewrite_hook(const unsig
  
  int cmd_commit(int argc, const char **argv, const char *prefix)
  {
+       static struct wt_status s;
+       static struct option builtin_commit_options[] = {
+               OPT__QUIET(&quiet, "suppress summary after successful commit"),
+               OPT__VERBOSE(&verbose, "show diff in commit message template"),
+               OPT_GROUP("Commit message options"),
+               OPT_FILENAME('F', "file", &logfile, "read message from file"),
+               OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
+               OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
+               OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
+               OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
+               OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
+               OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
+               OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
+               OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
+               OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
+               OPT_FILENAME('t', "template", &template_file, "use specified template file"),
+               OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
+               OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+               OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+               { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+                 "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+               /* end commit message options */
+               OPT_GROUP("Commit contents options"),
+               OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+               OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+               OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+               OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
+               OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
+               OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+               OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
+               OPT_SET_INT(0, "short", &status_format, "show status concisely",
+                           STATUS_FORMAT_SHORT),
+               OPT_BOOLEAN(0, "branch", &s.show_branch, "show branch information"),
+               OPT_SET_INT(0, "porcelain", &status_format,
+                           "machine-readable output", STATUS_FORMAT_PORCELAIN),
+               OPT_BOOLEAN('z', "null", &s.null_termination,
+                           "terminate entries with NUL"),
+               OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+               OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
+               { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+               /* end commit contents options */
+               { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+                 "ok to record an empty change",
+                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+               { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+                 "ok to record a change with an empty message",
+                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+               OPT_END()
+       };
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        struct commit_list *parents = NULL, **pptr = &parents;
        struct stat statbuf;
        int allow_fast_forward = 1;
-       struct wt_status s;
        struct commit *current_head = NULL;
        struct commit_extra_header *extra = NULL;
  
                if (!current_head || parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
-       argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+       argc = parse_and_validate_options(argc, argv, builtin_commit_options,
+                                         builtin_commit_usage,
                                          prefix, current_head, &s);
        if (dry_run)
                return dry_run_commit(argc, argv, prefix, current_head, &s);
diff --combined t/t7508-status.sh
@@@ -59,30 -59,6 +59,30 @@@ test_expect_success 'status (1)' 
        test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
  '
  
 +test_expect_success 'status --column' '
 +      COLUMNS=50 git status --column="column dense" >output &&
 +      cat >expect <<\EOF &&
 +# On branch master
 +# Changes to be committed:
 +#   (use "git reset HEAD <file>..." to unstage)
 +#
 +#     new file:   dir2/added
 +#
 +# Changes not staged for commit:
 +#   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
 +#
 +#     modified:   dir1/modified
 +#
 +# Untracked files:
 +#   (use "git add <file>..." to include in what will be committed)
 +#
 +#     dir1/untracked dir2/untracked untracked
 +#     dir2/modified  output
 +EOF
 +      test_cmp expect output
 +'
 +
  cat >expect <<\EOF
  # On branch master
  # Changes to be committed:
@@@ -295,6 -271,15 +295,15 @@@ test_expect_success 'status -s -b' 
  
  '
  
+ test_expect_success 'status -s -z -b' '
+       tr "\\n" Q <expect >expect.q &&
+       mv expect.q expect &&
+       git status -s -z -b >output &&
+       nul_to_q <output >output.q &&
+       mv output.q output &&
+       test_cmp expect output
+ '
  test_expect_success 'setup dir3' '
        mkdir dir3 &&
        : >dir3/untracked1 &&
@@@ -671,9 -656,14 +680,14 @@@ test_expect_success 'status --porcelai
  git config --unset color.status
  git config --unset color.ui
  
- test_expect_success 'status --porcelain ignores -b' '
+ test_expect_success 'status --porcelain respects -b' '
  
        git status --porcelain -b >output &&
+       {
+               echo "## master" &&
+               cat expect
+       } >tmp &&
+       mv tmp expect &&
        test_cmp expect output
  
  '
diff --combined wt-status.c
@@@ -11,7 -11,6 +11,7 @@@
  #include "remote.h"
  #include "refs.h"
  #include "submodule.h"
 +#include "column.h"
  
  static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@@ -642,8 -641,6 +642,8 @@@ static void wt_status_print_other(struc
  {
        int i;
        struct strbuf buf = STRBUF_INIT;
 +      static struct string_list output = STRING_LIST_INIT_DUP;
 +      struct column_options copts;
  
        if (!l->nr)
                return;
  
        for (i = 0; i < l->nr; i++) {
                struct string_list_item *it;
 +              const char *path;
                it = &(l->items[i]);
 +              path = quote_path(it->string, strlen(it->string),
 +                                &buf, s->prefix);
 +              if (column_active(s->colopts)) {
 +                      string_list_append(&output, path);
 +                      continue;
 +              }
                status_printf(s, color(WT_STATUS_HEADER, s), "\t");
                status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
 -                      "%s\n", quote_path(it->string, strlen(it->string),
 -                                          &buf, s->prefix));
 +                                 "%s\n", path);
        }
 +
 +      strbuf_release(&buf);
 +      if (!column_active(s->colopts))
 +              return;
 +
 +      strbuf_addf(&buf, "%s#\t%s",
 +                  color(WT_STATUS_HEADER, s),
 +                  color(WT_STATUS_UNTRACKED, s));
 +      memset(&copts, 0, sizeof(copts));
 +      copts.padding = 1;
 +      copts.indent = buf.buf;
 +      if (want_color(s->use_color))
 +              copts.nl = GIT_COLOR_RESET "\n";
 +      print_columns(&output, s->colopts, &copts);
 +      string_list_clear(&output, 0);
        strbuf_release(&buf);
  }
  
@@@ -801,7 -777,7 +801,7 @@@ void wt_status_print(struct wt_status *
        }
  }
  
- static void wt_shortstatus_unmerged(int null_termination, struct string_list_item *it,
+ static void wt_shortstatus_unmerged(struct string_list_item *it,
                           struct wt_status *s)
  {
        struct wt_status_change_data *d = it->util;
        case 7: how = "UU"; break; /* both modified */
        }
        color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
-       if (null_termination) {
+       if (s->null_termination) {
                fprintf(stdout, " %s%c", it->string, 0);
        } else {
                struct strbuf onebuf = STRBUF_INIT;
        }
  }
  
- static void wt_shortstatus_status(int null_termination, struct string_list_item *it,
+ static void wt_shortstatus_status(struct string_list_item *it,
                         struct wt_status *s)
  {
        struct wt_status_change_data *d = it->util;
        else
                putchar(' ');
        putchar(' ');
-       if (null_termination) {
+       if (s->null_termination) {
                fprintf(stdout, "%s%c", it->string, 0);
                if (d->head_path)
                        fprintf(stdout, "%s%c", d->head_path, 0);
        }
  }
  
- static void wt_shortstatus_other(int null_termination, struct string_list_item *it,
+ static void wt_shortstatus_other(struct string_list_item *it,
                                 struct wt_status *s, const char *sign)
  {
-       if (null_termination) {
+       if (s->null_termination) {
                fprintf(stdout, "%s %s%c", sign, it->string, 0);
        } else {
                struct strbuf onebuf = STRBUF_INIT;
@@@ -913,8 -889,8 +913,8 @@@ static void wt_shortstatus_print_tracki
        if (s->is_initial)
                color_fprintf(s->fp, header_color, _("Initial commit on "));
        if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
-               color_fprintf_ln(s->fp, branch_color_local,
-                       "%s", branch_name);
+               color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+               fputc(s->null_termination ? '\0' : '\n', s->fp);
                return;
        }
  
                color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
        }
  
-       color_fprintf_ln(s->fp, header_color, "]");
+       color_fprintf(s->fp, header_color, "]");
+       fputc(s->null_termination ? '\0' : '\n', s->fp);
  }
  
- void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch)
+ void wt_shortstatus_print(struct wt_status *s)
  {
        int i;
  
-       if (show_branch)
+       if (s->show_branch)
                wt_shortstatus_print_tracking(s);
  
        for (i = 0; i < s->change.nr; i++) {
                it = &(s->change.items[i]);
                d = it->util;
                if (d->stagemask)
-                       wt_shortstatus_unmerged(null_termination, it, s);
+                       wt_shortstatus_unmerged(it, s);
                else
-                       wt_shortstatus_status(null_termination, it, s);
+                       wt_shortstatus_status(it, s);
        }
        for (i = 0; i < s->untracked.nr; i++) {
                struct string_list_item *it;
  
                it = &(s->untracked.items[i]);
-               wt_shortstatus_other(null_termination, it, s, "??");
+               wt_shortstatus_other(it, s, "??");
        }
        for (i = 0; i < s->ignored.nr; i++) {
                struct string_list_item *it;
  
                it = &(s->ignored.items[i]);
-               wt_shortstatus_other(null_termination, it, s, "!!");
+               wt_shortstatus_other(it, s, "!!");
        }
  }
  
- void wt_porcelain_print(struct wt_status *s, int null_termination)
+ void wt_porcelain_print(struct wt_status *s)
  {
        s->use_color = 0;
        s->relative_paths = 0;
        s->prefix = NULL;
-       wt_shortstatus_print(s, null_termination, 0);
+       wt_shortstatus_print(s);
  }
diff --combined wt-status.h
@@@ -56,7 -56,8 +56,9 @@@ struct wt_status 
        enum untracked_status_type show_untracked_files;
        const char *ignore_submodule_arg;
        char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
 +      int colopts;
+       int null_termination;
+       int show_branch;
  
        /* These are computed during processing of the individual sections */
        int commitable;
@@@ -73,8 -74,8 +75,8 @@@ void wt_status_prepare(struct wt_statu
  void wt_status_print(struct wt_status *s);
  void wt_status_collect(struct wt_status *s);
  
- void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);
- void wt_porcelain_print(struct wt_status *s, int null_termination);
+ void wt_shortstatus_print(struct wt_status *s);
+ void wt_porcelain_print(struct wt_status *s);
  
  void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)
        ;