Merge branch 'mm/status-without-comment-char'
authorJunio C Hamano <gitster@pobox.com>
Fri, 20 Sep 2013 19:29:01 +0000 (12:29 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 20 Sep 2013 19:29:01 +0000 (12:29 -0700)
"git status" now omits the prefix to make its output a comment in a
commit log editor, which is not necessary for human consumption.

We may want to tighten the output to omit unnecessary trailing blank
lines, but that does not have to be in the scope of this series.

* mm/status-without-comment-char:
t7508: avoid non-portable sed expression
status: add missing blank line after list of "other" files
tests: don't set status.displayCommentPrefix file-wide
status: disable display of '#' comment prefix by default
submodule summary: ignore --for-status option
wt-status: use argv_array API
builtin/stripspace.c: fix broken indentation

1  2 
Documentation/config.txt
builtin/commit.c
wt-status.c
wt-status.h
diff --combined Documentation/config.txt
index 44e7873521b05b78e0c5655e39a64c7b06cddb7e,60c6bc99a6ad8ec5fd6d7b5d34fdd6193324cda8..b320b63b914714671eaedf5c641b5fb1287c00f2
@@@ -170,8 -170,8 +170,8 @@@ advice.*:
        pushNeedsForce::
                Shown when linkgit:git-push[1] rejects an update that
                tries to overwrite a remote ref that points at an
 -              object that is not a committish, or make the remote
 -              ref point at an object that is not a committish.
 +              object that is not a commit-ish, or make the remote
 +              ref point at an object that is not a commit-ish.
        statusHints::
                Show directions on how to proceed from the current
                state in the output of linkgit:git-status[1], in
@@@ -553,20 -553,22 +553,20 @@@ sequence.editor:
        When not configured the default commit message editor is used instead.
  
  core.pager::
 -      The command that Git will use to paginate output.  Can
 -      be overridden with the `GIT_PAGER` environment
 -      variable.  Note that Git sets the `LESS` environment
 -      variable to `FRSX` if it is unset when it runs the
 -      pager.  One can change these settings by setting the
 -      `LESS` variable to some other value.  Alternately,
 -      these settings can be overridden on a project or
 -      global basis by setting the `core.pager` option.
 -      Setting `core.pager` has no effect on the `LESS`
 -      environment variable behaviour above, so if you want
 -      to override Git's default settings this way, you need
 -      to be explicit.  For example, to disable the S option
 -      in a backward compatible manner, set `core.pager`
 -      to `less -+S`.  This will be passed to the shell by
 -      Git, which will translate the final command to
 -      `LESS=FRSX less -+S`.
 +      Text viewer for use by Git commands (e.g., 'less').  The value
 +      is meant to be interpreted by the shell.  The order of preference
 +      is the `$GIT_PAGER` environment variable, then `core.pager`
 +      configuration, then `$PAGER`, and then the default chosen at
 +      compile time (usually 'less').
 ++
 +When the `LESS` environment variable is unset, Git sets it to `FRSX`
 +(if `LESS` environment variable is set, Git does not change it at
 +all).  If you want to selectively override Git's default setting
 +for `LESS`, you can set `core.pager` to e.g. `less -+S`.  This will
 +be passed to the shell by Git, which will translate the final
 +command to `LESS=FRSX less -+S`. The environment tells the command
 +to set the `S` option to chop long lines but the command line
 +resets it to the default to fold long lines.
  
  core.whitespace::
        A comma separated list of common whitespace problems to
@@@ -763,10 -765,6 +763,10 @@@ branch.<name>.rebase:
        instead of merging the default branch from the default remote when
        "git pull" is run. See "pull.rebase" for doing this in a non
        branch-specific manner.
 ++
 +      When preserve, also pass `--preserve-merges` along to 'git rebase'
 +      so that locally committed merge commits will not be flattened
 +      by running 'git pull'.
  +
  *NOTE*: this is a possibly dangerous operation; do *not* use
  it unless you understand the implications (see linkgit:git-rebase[1]
@@@ -789,8 -787,8 +789,8 @@@ browser.<tool>.path:
        working repository in gitweb (see linkgit:git-instaweb[1]).
  
  clean.requireForce::
 -      A boolean to make git-clean do nothing unless given -f
 -      or -n.   Defaults to true.
 +      A boolean to make git-clean do nothing unless given -f,
 +      -i or -n.   Defaults to true.
  
  color.branch::
        A boolean to enable/disable color in the output of
@@@ -1063,10 -1061,6 +1063,10 @@@ fetch.unpackLimit:
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
  
 +fetch.prune::
 +      If true, fetch will automatically behave as if the `--prune`
 +      option was given on the command line.  See also `remote.<name>.prune`.
 +
  format.attach::
        Enable multipart/mixed attachments as the default for
        'format-patch'.  The value can also be a double quoted string
@@@ -1451,11 -1445,7 +1451,11 @@@ http.cookiefile:
        of the file to read cookies from should be plain HTTP headers or
        the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
        NOTE that the file specified with http.cookiefile is only used as
 -      input. No cookies will be stored in the file.
 +      input unless http.saveCookies is set.
 +
 +http.savecookies::
 +      If set, store cookies received during requests to the file specified by
 +      http.cookiefile. Has no effect if http.cookiefile is unset.
  
  http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
@@@ -1535,51 -1525,6 +1535,51 @@@ http.useragent:
        of common USER_AGENT strings (but not including those like git/1.7.1).
        Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
  
 +http.<url>.*::
 +      Any of the http.* options above can be applied selectively to some urls.
 +      For a config key to match a URL, each element of the config key is
 +      compared to that of the URL, in the following order:
 ++
 +--
 +. Scheme (e.g., `https` in `https://example.com/`). This field
 +  must match exactly between the config key and the URL.
 +
 +. Host/domain name (e.g., `example.com` in `https://example.com/`).
 +  This field must match exactly between the config key and the URL.
 +
 +. Port number (e.g., `8080` in `http://example.com:8080/`).
 +  This field must match exactly between the config key and the URL.
 +  Omitted port numbers are automatically converted to the correct
 +  default for the scheme before matching.
 +
 +. Path (e.g., `repo.git` in `https://example.com/repo.git`). The
 +  path field of the config key must match the path field of the URL
 +  either exactly or as a prefix of slash-delimited path elements.  This means
 +  a config key with path `foo/` matches URL path `foo/bar`.  A prefix can only
 +  match on a slash (`/`) boundary.  Longer matches take precedence (so a config
 +  key with path `foo/bar` is a better match to URL path `foo/bar` than a config
 +  key with just path `foo/`).
 +
 +. User name (e.g., `user` in `https://user@example.com/repo.git`). If
 +  the config key has a user name it must match the user name in the
 +  URL exactly. If the config key does not have a user name, that
 +  config key will match a URL with any user name (including none),
 +  but at a lower precedence than a config key with a user name.
 +--
 ++
 +The list above is ordered by decreasing precedence; a URL that matches
 +a config key's path is preferred to one that matches its user name. For example,
 +if the URL is `https://user@example.com/foo/bar` a config key match of
 +`https://example.com/foo` will be preferred over a config key match of
 +`https://user@example.com`.
 ++
 +All URLs are normalized before attempting any matching (the password part,
 +if embedded in the URL, is always ignored for matching purposes) so that
 +equivalent urls that are simply spelled differently will match properly.
 +Environment variable settings always override any matches.  The urls that are
 +matched against are those given directly to Git commands.  This means any URLs
 +visited as a result of a redirection do not participate in matching.
 +
  i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
        does not care per se, but this information is necessary e.g. when
@@@ -1880,10 -1825,6 +1880,10 @@@ pull.rebase:
        of merging the default branch from the default remote when "git
        pull" is run. See "branch.<name>.rebase" for setting this on a
        per-branch basis.
 ++
 +      When preserve, also pass `--preserve-merges` along to 'git rebase'
 +      so that locally committed merge commits will not be flattened
 +      by running 'git pull'.
  +
  *NOTE*: this is a possibly dangerous operation; do *not* use
  it unless you understand the implications (see linkgit:git-rebase[1]
@@@ -2083,12 -2024,6 +2083,12 @@@ remote.<name>.vcs:
        Setting this to a value <vcs> will cause Git to interact with
        the remote with the git-remote-<vcs> helper.
  
 +remote.<name>.prune::
 +      When set to true, fetching from this remote by default will also
 +      remove any remote-tracking branches which no longer exist on the
 +      remote (as if the `--prune` option was give on the command line).
 +      Overrides `fetch.prune` settings, if any.
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
@@@ -2183,6 -2118,13 +2183,13 @@@ status.branch:
        Set to true to enable --branch by default in linkgit:git-status[1].
        The option --no-branch takes precedence over this variable.
  
+ status.displayCommentPrefix::
+       If set to true, linkgit:git-status[1] will insert a comment
+       prefix before each output line (starting with
+       `core.commentChar`, i.e. `#` by default). This was the
+       behavior of linkgit:git-status[1] in Git 1.8.4 and previous.
+       Defaults to false.
  status.showUntrackedFiles::
        By default, linkgit:git-status[1] and linkgit:git-commit[1] show
        files which are not currently tracked by Git. Directories which
diff --combined builtin/commit.c
index 084d70fd4da5d38196cbde1f692bd2d70c0c3800,61975ad1df0bfa79225a28f267f5dde660e2737c..d0d8bc1b30fe4d084e32beaf4b8ee9a24bb9ac94
@@@ -30,7 -30,6 +30,7 @@@
  #include "column.h"
  #include "sequencer.h"
  #include "notes-utils.h"
 +#include "mailmap.h"
  
  static const char * const builtin_commit_usage[] = {
        N_("git commit [options] [--] <pathspec>..."),
@@@ -203,15 -202,17 +203,15 @@@ static int commit_index_files(void
   * and return the paths that match the given pattern in list.
   */
  static int list_paths(struct string_list *list, const char *with_tree,
 -                    const char *prefix, const char **pattern)
 +                    const char *prefix, const struct pathspec *pattern)
  {
        int i;
        char *m;
  
 -      if (!pattern)
 +      if (!pattern->nr)
                return 0;
  
 -      for (i = 0; pattern[i]; i++)
 -              ;
 -      m = xcalloc(1, i);
 +      m = xcalloc(1, pattern->nr);
  
        if (with_tree) {
                char *max_prefix = common_prefix(pattern);
  
                if (ce->ce_flags & CE_UPDATE)
                        continue;
 -              if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
 +              if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@@ -297,17 -298,17 +297,17 @@@ static char *prepare_index(int argc, co
  {
        int fd;
        struct string_list partial;
 -      const char **pathspec = NULL;
 +      struct pathspec pathspec;
        char *old_index_env = NULL;
        int refresh_flags = REFRESH_QUIET;
  
        if (is_status)
                refresh_flags |= REFRESH_UNMERGED;
 +      parse_pathspec(&pathspec, 0,
 +                     PATHSPEC_PREFER_FULL,
 +                     prefix, argv);
  
 -      if (*argv)
 -              pathspec = get_pathspec(prefix, argv);
 -
 -      if (read_cache_preload(pathspec) < 0)
 +      if (read_cache_preload(&pathspec) < 0)
                die(_("index file corrupt"));
  
        if (interactive) {
         * (A) if all goes well, commit the real index;
         * (B) on failure, rollback the real index.
         */
 -      if (all || (also && pathspec && *pathspec)) {
 +      if (all || (also && pathspec.nr)) {
                fd = hold_locked_index(&index_lock, 1);
 -              add_files_to_cache(also ? prefix : NULL, pathspec, 0);
 +              add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
                if (write_cache(fd, active_cache, active_nr) ||
         * and create commit from the_index.
         * We still need to refresh the index here.
         */
 -      if (!only && (!pathspec || !*pathspec)) {
 +      if (!only && !pathspec.nr) {
                fd = hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed) {
  
        memset(&partial, 0, sizeof(partial));
        partial.strdup_strings = 1;
 -      if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
 +      if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
                exit(1);
  
        discard_cache();
@@@ -598,6 -599,7 +598,7 @@@ static int prepare_to_commit(const cha
        const char *hook_arg2 = NULL;
        int ident_shown = 0;
        int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+       int old_display_comment_prefix;
  
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
        if (s->fp == NULL)
                die_errno(_("could not open '%s'"), git_path(commit_editmsg));
  
+       /* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
+       old_display_comment_prefix = s->display_comment_prefix;
+       s->display_comment_prefix = 1;
        if (clean_message_contents)
                stripspace(&sb, 0);
  
         */
        if (!commitable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(current_head))) {
+               s->display_comment_prefix = old_display_comment_prefix;
                run_status(stdout, index_file, prefix, 0, s);
                if (amend)
                        fputs(_(empty_amend_advice), stderr);
@@@ -934,7 -941,6 +940,7 @@@ static const char *find_author_by_nickn
        struct rev_info revs;
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
 +      struct string_list mailmap = STRING_LIST_INIT_NODUP;
        const char *av[20];
        int ac = 0;
  
        av[++ac] = buf.buf;
        av[++ac] = NULL;
        setup_revisions(ac, av, &revs, NULL);
 +      revs.mailmap = &mailmap;
 +      read_mailmap(revs.mailmap, NULL);
 +
        prepare_revision_walk(&revs);
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = DATE_NORMAL;
                strbuf_release(&buf);
 -              format_commit_message(commit, "%an <%ae>", &buf, &ctx);
 +              format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 +              clear_mailmap(&mailmap);
                return strbuf_detach(&buf, NULL);
        }
        die(_("No existing author found with '%s'"), name);
@@@ -1095,7 -1097,7 +1101,7 @@@ static int parse_and_validate_options(i
        if (patch_interactive)
                interactive = 1;
  
 -      if (!!also + !!only + !!all + !!interactive > 1)
 +      if (also + only + all + interactive > 1)
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
        if (argc == 0 && (also || (only && !amend)))
                die(_("No paths with --include/--only does not make sense."));
@@@ -1186,6 -1188,10 +1192,10 @@@ static int git_status_config(const cha
                s->use_color = git_config_colorbool(k, v);
                return 0;
        }
+       if (!strcmp(k, "status.displaycommentprefix")) {
+               s->display_comment_prefix = git_config_bool(k, v);
+               return 0;
+       }
        if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                if (slot < 0)
@@@ -1232,14 -1238,14 +1242,14 @@@ int cmd_status(int argc, const char **a
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
 -              OPT_BOOLEAN('z', "null", &s.null_termination,
 -                          N_("terminate entries with NUL")),
 +              OPT_BOOL('z', "null", &s.null_termination,
 +                       N_("terminate entries with NUL")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
                  N_("mode"),
                  N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 -              OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
 -                          N_("show ignored files")),
 +              OPT_BOOL(0, "ignored", &show_ignored_in_status,
 +                       N_("show ignored files")),
                { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
                  N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
                s.show_ignored_files = 1;
 -      if (*argv)
 -              s.pathspec = get_pathspec(prefix, argv);
 +      parse_pathspec(&s.pathspec, 0,
 +                     PATHSPEC_PREFER_FULL,
 +                     prefix, argv);
  
 -      read_cache_preload(s.pathspec);
 -      refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
 +      read_cache_preload(&s.pathspec);
 +      refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
  
        fd = hold_locked_index(&index_lock, 0);
        if (0 <= fd)
@@@ -1439,24 -1444,24 +1449,24 @@@ int cmd_commit(int argc, const char **a
                OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
                OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
                OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
 -              OPT_BOOLEAN(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
 -              OPT_BOOLEAN('s', "signoff", &signoff, N_("add Signed-off-by:")),
 +              OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
 +              OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
                OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
 -              OPT_BOOLEAN(0, "status", &include_status, N_("include status in commit message template")),
 +              OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                /* end commit message options */
  
                OPT_GROUP(N_("Commit contents options")),
 -              OPT_BOOLEAN('a', "all", &all, N_("commit all changed files")),
 -              OPT_BOOLEAN('i', "include", &also, N_("add specified files to index for commit")),
 -              OPT_BOOLEAN(0, "interactive", &interactive, N_("interactively add files")),
 -              OPT_BOOLEAN('p', "patch", &patch_interactive, N_("interactively add changes")),
 -              OPT_BOOLEAN('o', "only", &only, N_("commit only specified files")),
 -              OPT_BOOLEAN('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
 -              OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
 +              OPT_BOOL('a', "all", &all, N_("commit all changed files")),
 +              OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
 +              OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
 +              OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
 +              OPT_BOOL('o', "only", &only, N_("commit only specified files")),
 +              OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
 +              OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
                OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
 -              OPT_BOOLEAN('z', "null", &s.null_termination,
 -                          N_("terminate entries with NUL")),
 -              OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")),
 -              OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
 +              OPT_BOOL('z', "null", &s.null_termination,
 +                       N_("terminate entries with NUL")),
 +              OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
 +              OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("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,
 -                N_("ok to record an empty change"),
 -                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 -              { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
 -                N_("ok to record a change with an empty message"),
 -                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 +              OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
 +                              N_("ok to record an empty change")),
 +              OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message,
 +                              N_("ok to record a change with an empty message")),
  
                OPT_END()
        };
diff --combined wt-status.c
index c8c2d7752470fbea4fd4c8f80e2fa2d7bc50eeda,2a9ca0fbfe324edee38b27e73730f039cbe68601..706a6f77a2fd95893cd64c703b7bf407a874547e
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "pathspec.h"
  #include "wt-status.h"
  #include "object.h"
  #include "dir.h"
@@@ -9,6 -8,7 +9,7 @@@
  #include "diffcore.h"
  #include "quote.h"
  #include "run-command.h"
+ #include "argv-array.h"
  #include "remote.h"
  #include "refs.h"
  #include "submodule.h"
@@@ -46,9 -46,11 +47,11 @@@ static void status_vprintf(struct wt_st
  
        strbuf_vaddf(&sb, fmt, ap);
        if (!sb.len) {
-               strbuf_addch(&sb, comment_line_char);
-               if (!trail)
-                       strbuf_addch(&sb, ' ');
+               if (s->display_comment_prefix) {
+                       strbuf_addch(&sb, comment_line_char);
+                       if (!trail)
+                               strbuf_addch(&sb, ' ');
+               }
                color_print_strbuf(s->fp, color, &sb);
                if (trail)
                        fprintf(s->fp, "%s", trail);
@@@ -59,7 -61,7 +62,7 @@@
                eol = strchr(line, '\n');
  
                strbuf_reset(&linebuf);
-               if (at_bol) {
+               if (at_bol && s->display_comment_prefix) {
                        strbuf_addch(&linebuf, comment_line_char);
                        if (*line != '\n' && *line != '\t')
                                strbuf_addch(&linebuf, ' ');
@@@ -129,6 -131,7 +132,7 @@@ void wt_status_prepare(struct wt_statu
        s->untracked.strdup_strings = 1;
        s->ignored.strdup_strings = 1;
        s->show_branch = -1;  /* unspecified */
+       s->display_comment_prefix = 0;
  }
  
  static void wt_status_print_unmerged_header(struct wt_status *s)
@@@ -439,7 -442,7 +443,7 @@@ static void wt_status_collect_changes_w
        }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
        rev.diffopt.format_callback_data = s;
 -      init_pathspec(&rev.prune_data, s->pathspec);
 +      copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_files(&rev, 0);
  }
  
@@@ -464,20 -467,22 +468,20 @@@ static void wt_status_collect_changes_i
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 200;
        rev.diffopt.break_opt = 0;
 -      init_pathspec(&rev.prune_data, s->pathspec);
 +      copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_index(&rev, 1);
  }
  
  static void wt_status_collect_changes_initial(struct wt_status *s)
  {
 -      struct pathspec pathspec;
        int i;
  
 -      init_pathspec(&pathspec, s->pathspec);
        for (i = 0; i < active_nr; i++) {
                struct string_list_item *it;
                struct wt_status_change_data *d;
                const struct cache_entry *ce = active_cache[i];
  
 -              if (!ce_path_match(ce, &pathspec))
 +              if (!ce_path_match(ce, &s->pathspec))
                        continue;
                it = string_list_insert(&s->change, ce->name);
                d = it->util;
                else
                        d->index_status = DIFF_STATUS_ADDED;
        }
 -      free_pathspec(&pathspec);
  }
  
  static void wt_status_collect_untracked(struct wt_status *s)
                dir.flags |= DIR_SHOW_IGNORED_TOO;
        setup_standard_excludes(&dir);
  
 -      fill_directory(&dir, s->pathspec);
 +      fill_directory(&dir, &s->pathspec);
  
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
                if (cache_name_is_other(ent->name, ent->len) &&
 -                  match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
 +                  match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
                        string_list_insert(&s->untracked, ent->name);
                free(ent);
        }
        for (i = 0; i < dir.ignored_nr; i++) {
                struct dir_entry *ent = dir.ignored[i];
                if (cache_name_is_other(ent->name, ent->len) &&
 -                  match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
 +                  match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
                        string_list_insert(&s->ignored, ent->name);
                free(ent);
        }
@@@ -661,29 -667,57 +665,57 @@@ static void wt_status_print_submodule_s
        char summary_limit[64];
        char index[PATH_MAX];
        const char *env[] = { NULL, NULL };
-       const char *argv[8];
-       env[0] =        index;
-       argv[0] =       "submodule";
-       argv[1] =       "summary";
-       argv[2] =       uncommitted ? "--files" : "--cached";
-       argv[3] =       "--for-status";
-       argv[4] =       "--summary-limit";
-       argv[5] =       summary_limit;
-       argv[6] =       uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD");
-       argv[7] =       NULL;
+       struct argv_array argv = ARGV_ARRAY_INIT;
+       struct strbuf cmd_stdout = STRBUF_INIT;
+       struct strbuf summary = STRBUF_INIT;
+       char *summary_content;
+       size_t len;
  
        sprintf(summary_limit, "%d", s->submodule_summary);
        snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
  
+       env[0] = index;
+       argv_array_push(&argv, "submodule");
+       argv_array_push(&argv, "summary");
+       argv_array_push(&argv, uncommitted ? "--files" : "--cached");
+       argv_array_push(&argv, "--for-status");
+       argv_array_push(&argv, "--summary-limit");
+       argv_array_push(&argv, summary_limit);
+       if (!uncommitted)
+               argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
        memset(&sm_summary, 0, sizeof(sm_summary));
-       sm_summary.argv = argv;
+       sm_summary.argv = argv.argv;
        sm_summary.env = env;
        sm_summary.git_cmd = 1;
        sm_summary.no_stdin = 1;
        fflush(s->fp);
-       sm_summary.out = dup(fileno(s->fp));    /* run_command closes it */
+       sm_summary.out = -1;
        run_command(&sm_summary);
+       argv_array_clear(&argv);
+       len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
+       /* prepend header, only if there's an actual output */
+       if (len) {
+               if (uncommitted)
+                       strbuf_addstr(&summary, _("Submodules changed but not updated:"));
+               else
+                       strbuf_addstr(&summary, _("Submodule changes to be committed:"));
+               strbuf_addstr(&summary, "\n\n");
+       }
+       strbuf_addbuf(&summary, &cmd_stdout);
+       strbuf_release(&cmd_stdout);
+       if (s->display_comment_prefix) {
+               summary_content = strbuf_detach(&summary, &len);
+               strbuf_add_commented_lines(&summary, summary_content, len);
+               free(summary_content);
+       }
+       fputs(summary.buf, s->fp);
+       strbuf_release(&summary);
  }
  
  static void wt_status_print_other(struct wt_status *s,
  
        strbuf_release(&buf);
        if (!column_active(s->colopts))
-               return;
+               goto conclude;
  
-       strbuf_addf(&buf, "%s#\t%s",
+       strbuf_addf(&buf, "%s%s\t%s",
                    color(WT_STATUS_HEADER, s),
+                   s->display_comment_prefix ? "#" : "",
                    color(WT_STATUS_UNTRACKED, s));
        memset(&copts, 0, sizeof(copts));
        copts.padding = 1;
        print_columns(&output, s->colopts, &copts);
        string_list_clear(&output, 0);
        strbuf_release(&buf);
+ conclude:
+       status_printf_ln(s, GIT_COLOR_NORMAL, "");
  }
  
  static void wt_status_print_verbose(struct wt_status *s)
@@@ -764,6 -801,8 +799,8 @@@ static void wt_status_print_tracking(st
        struct strbuf sb = STRBUF_INIT;
        const char *cp, *ep;
        struct branch *branch;
+       char comment_line_string[3];
+       int i;
  
        assert(s->branch && !s->is_initial);
        if (prefixcmp(s->branch, "refs/heads/"))
        if (!format_tracking_info(branch, &sb))
                return;
  
+       i = 0;
+       if (s->display_comment_prefix) {
+               comment_line_string[i++] = comment_line_char;
+               comment_line_string[i++] = ' ';
+       }
+       comment_line_string[i] = '\0';
        for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
-                                "%c %.*s", comment_line_char,
+                                "%s%.*s", comment_line_string,
                                 (int)(ep - cp), cp);
-       color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
-                        comment_line_char);
+       if (s->display_comment_prefix)
+               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
+                                comment_line_char);
+       else
+               fprintf_ln(s->fp, "");
  }
  
  static int has_unmerged(struct wt_status *s)
@@@ -1363,7 -1412,6 +1410,7 @@@ static void wt_shortstatus_print_tracki
        const char *base;
        const char *branch_name;
        int num_ours, num_theirs;
 +      int upstream_is_gone = 0;
  
        color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
  
        branch = branch_get(s->branch + 11);
        if (s->is_initial)
                color_fprintf(s->fp, header_color, _("Initial commit on "));
 -      if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
 -              color_fprintf(s->fp, branch_color_local, "%s", branch_name);
 +
 +      color_fprintf(s->fp, branch_color_local, "%s", branch_name);
 +
 +      switch (stat_tracking_info(branch, &num_ours, &num_theirs)) {
 +      case 0:
 +              /* no base */
                fputc(s->null_termination ? '\0' : '\n', s->fp);
                return;
 +      case -1:
 +              /* with "gone" base */
 +              upstream_is_gone = 1;
 +              break;
 +      default:
 +              /* with base */
 +              break;
        }
  
        base = branch->merge[0]->dst;
        base = shorten_unambiguous_ref(base, 0);
 -      color_fprintf(s->fp, branch_color_local, "%s", branch_name);
        color_fprintf(s->fp, header_color, "...");
        color_fprintf(s->fp, branch_color_remote, "%s", base);
  
 +      if (!upstream_is_gone && !num_ours && !num_theirs) {
 +              fputc(s->null_termination ? '\0' : '\n', s->fp);
 +              return;
 +      }
 +
        color_fprintf(s->fp, header_color, " [");
 -      if (!num_ours) {
 +      if (upstream_is_gone) {
 +              color_fprintf(s->fp, header_color, _("gone"));
 +      } else if (!num_ours) {
                color_fprintf(s->fp, header_color, _("behind "));
                color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
        } else if (!num_theirs) {
diff --combined wt-status.h
index 9966c13deb678857d5fdb0c3648865fa7189686b,fac16cadca4e481304b3afaf65bd6979d66fdd78..009c954c73461366434ce7ab51d03fe0dd15f505
@@@ -44,12 -44,13 +44,13 @@@ struct wt_status 
        int is_initial;
        char *branch;
        const char *reference;
 -      const char **pathspec;
 +      struct pathspec pathspec;
        int verbose;
        int amend;
        enum commit_whence whence;
        int nowarn;
        int use_color;
+       int display_comment_prefix;
        int relative_paths;
        int submodule_summary;
        int show_ignored_files;