Merge branch 'jh/commit-status'
authorJunio C Hamano <gitster@pobox.com>
Mon, 18 Jan 2010 00:00:07 +0000 (16:00 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 18 Jan 2010 00:00:07 +0000 (16:00 -0800)
* jh/commit-status:
t7502: test commit.status, --status and --no-status
commit: support commit.status, --status, and --no-status

Conflicts:
Documentation/git-commit.txt
builtin-commit.c

1  2 
Documentation/config.txt
Documentation/git-commit.txt
builtin-commit.c
diff --combined Documentation/config.txt
index 8acb613ec3a56dbe76459075a31fd4e66c218991,5561560a0fb5cef87de72ec42aa27c93fa6ac632..2d6775c135b466ce8a95db3fd7c22c04151f8d42
@@@ -297,24 -297,17 +297,24 @@@ false), while all other repositories ar
  = true).
  
  core.worktree::
 -      Set the path to the working tree.  The value will not be
 -      used in combination with repositories found automatically in
 -      a .git directory (i.e. $GIT_DIR is not set).
 +      Set the path to the root of the work tree.
        This can be overridden by the GIT_WORK_TREE environment
        variable and the '--work-tree' command line option. It can be
 -      a absolute path or relative path to the directory specified by
 -      --git-dir or GIT_DIR.
 -      Note: If --git-dir or GIT_DIR are specified but none of
 +      an absolute path or a relative path to the .git directory,
 +      either specified by --git-dir or GIT_DIR, or automatically
 +      discovered.
 +      If --git-dir or GIT_DIR are specified but none of
        --work-tree, GIT_WORK_TREE and core.worktree is specified,
 -      the current working directory is regarded as the top directory
 -      of your working tree.
 +      the current working directory is regarded as the root of the
 +      work tree.
 ++
 +Note that this variable is honored even when set in a configuration
 +file in a ".git" subdirectory of a directory, and its value differs
 +from the latter directory (e.g. "/path/to/.git/config" has
 +core.worktree set to "/different/path"), which is most likely a
 +misconfiguration.  Running git commands in "/path/to" directory will
 +still use "/different/path" as the root of the work tree and can cause
 +great confusion to the users.
  
  core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@@ -502,10 -495,6 +502,10 @@@ notes should be printed
  This setting defaults to "refs/notes/commits", and can be overridden by
  the `GIT_NOTES_REF` environment variable.
  
 +core.sparseCheckout::
 +      Enable "sparse checkout" feature. See section "Sparse checkout" in
 +      linkgit:git-read-tree[1] for more information.
 +
  add.ignore-errors::
        Tells 'git-add' to continue adding files when some files cannot be
        added due to indexing errors. Equivalent to the '--ignore-errors'
@@@ -541,7 -530,7 +541,7 @@@ apply.whitespace:
        as the '--whitespace' option. See linkgit:git-apply[1].
  
  branch.autosetupmerge::
 -      Tells 'git-branch' and 'git-checkout' to setup new branches
 +      Tells 'git-branch' and 'git-checkout' to set up new branches
        so that linkgit:git-pull[1] will appropriately merge from the
        starting point branch. Note that even if this option is not set,
        this behavior can be chosen per-branch using the `--track`
@@@ -716,6 -705,11 +716,11 @@@ color.ui:
        terminal. When more specific variables of color.* are set, they always
        take precedence over this setting. Defaults to false.
  
+ commit.status
+       A boolean to enable/disable inclusion of status information in the
+       commit message template when using an editor to prepare the commit
+       message.  Defaults to true.
  commit.template::
        Specify a file to use as the template for new commit messages.
        "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
@@@ -729,7 -723,7 +734,7 @@@ diff.autorefreshindex:
        contents in the work tree match the contents in the
        index.  This option defaults to true.  Note that this
        affects only 'git-diff' Porcelain, and not lower level
 -      'diff' commands, such as 'git-diff-files'.
 +      'diff' commands such as 'git-diff-files'.
  
  diff.external::
        If this config variable is set, diff generation is not
@@@ -845,8 -839,8 +850,8 @@@ format.pretty:
  
  format.thread::
        The default threading style for 'git-format-patch'.  Can be
 -      either a boolean value, `shallow` or `deep`.  `shallow`
 -      threading makes every mail a reply to the head of the series,
 +      a boolean value, or `shallow` or `deep`.  `shallow` threading
 +      makes every mail a reply to the head of the series,
        where the head is chosen from the cover letter, the
        `\--in-reply-to`, and the first patch mail, in this order.
        `deep` threading makes every mail a reply to the previous one.
@@@ -879,12 -873,15 +884,12 @@@ gc.autopacklimit:
        default value is 50.  Setting this to 0 disables it.
  
  gc.packrefs::
 -      'git-gc' does not run `git pack-refs` in a bare repository by
 -      default so that older dumb-transport clients can still fetch
 -      from the repository.  Setting this to `true` lets 'git-gc'
 -      to run `git pack-refs`.  Setting this to `false` tells
 -      'git-gc' never to run `git pack-refs`. The default setting is
 -      `notbare`. Enable it only when you know you do not have to
 -      support such clients.  The default setting will change to `true`
 -      at some stage, and setting this to `false` will continue to
 -      prevent `git pack-refs` from being run from 'git-gc'.
 +      Running `git pack-refs` in a repository renders it
 +      unclonable by Git versions prior to 1.5.1.2 over dumb
 +      transports such as HTTP.  This variable determines whether
 +      'git gc' runs `git pack-refs`. This can be set to "nobare"
 +      to enable it within all non-bare repos or it can be set to a
 +      boolean value.  The default is `true`.
  
  gc.pruneexpire::
        When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
@@@ -1140,12 -1137,6 +1145,12 @@@ http.maxRequests:
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
  
 +http.minSessions::
 +      The number of curl sessions (counted across slots) to be kept across
 +      requests. They will not be ended with curl_easy_cleanup() until
 +      http_cleanup() is invoked. If USE_CURL_MULTI is not defined, this
 +      value will be capped at 1. Defaults to 1.
 +
  http.postBuffer::
        Maximum size in bytes of the buffer used by smart HTTP
        transports when POSTing data to the remote system.
@@@ -1475,10 -1466,6 +1480,10 @@@ remote.<name>.tagopt:
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>
  
 +remote.<name>.vcs::
 +      Setting this to a value <vcs> will cause git to interact with
 +      the remote with the git-remote-<vcs> helper.
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
index 5fb43f9320a71a50cdc36d3d5ce59ac1641dfd91,0e535184b1ad61eaa9bb75e16c2734bd650825de..d3a2dec21eb0323b535dfc696bceac3d6b66b2c7
@@@ -11,7 -11,8 +11,8 @@@ SYNOPSI
  'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
           [(-c | -C) <commit>] [-F <file> | -m <msg>] [--reset-author]
           [--allow-empty] [--no-verify] [-e] [--author=<author>]
-          [--date=<date>] [--cleanup=<mode>] [--] [[-i | -o ]<file>...]
 -         [--cleanup=<mode>] [--status | --no-status] [--]
++         [--date=<date>] [--cleanup=<mode>] [--status | --no-status] [--]
+          [[-i | -o ]<file>...]
  
  DESCRIPTION
  -----------
@@@ -74,20 -75,6 +75,20 @@@ OPTION
        authorship of the resulting commit now belongs of the committer.
        This also renews the author timestamp.
  
 +--short::
 +      When doing a dry-run, give the output in the short-format. See
 +      linkgit:git-status[1] for details. Implies `--dry-run`.
 +
 +--porcelain::
 +      When doing a dry-run, give the output in a porcelain-ready
 +      format. See linkgit:git-status[1] for details. Implies
 +      `--dry-run`.
 +
 +-z::
 +      When showing `short` or `porcelain` status output, terminate
 +      entries in the status output with NUL, instead of LF. If no
 +      format is given, implies the `--porcelain` output format.
 +
  -F <file>::
  --file=<file>::
        Take the commit message from the given file.  Use '-' to
        an existing commit that matches the given string and its author
        name is used.
  
 +--date=<date>::
 +      Override the author date used in the commit.
 +
  -m <msg>::
  --message=<msg>::
        Use the given <msg> as the commit message.
@@@ -224,6 -208,17 +225,17 @@@ specified
        to be committed, paths with local changes that will be left
        uncommitted and paths that are untracked.
  
+ --status::
+       Include the output of linkgit:git-status[1] in the commit
+       message template when using an editor to prepare the commit
+       message.  Defaults to on, but can be used to override
+       configuration variable commit.status.
+ --no-status::
+       Do not include the output of linkgit:git-status[1] in the
+       commit message template when using an editor to prepare the
+       default commit message.
  \--::
        Do not interpret any more arguments as options.
  
        these files are also staged for the next commit on top
        of what have been staged before.
  
 +:git-commit: 1
 +include::date-formats.txt[]
  
  EXAMPLES
  --------
diff --combined builtin-commit.c
index 592b10396d1d6aa25d5d1c0aa27556c93c219877,095c1869e83dcb40d10ff0ac7931fe078a4a6763..e64487121059b1b4a618828095375c1a42e4e93e
@@@ -24,7 -24,6 +24,7 @@@
  #include "string-list.h"
  #include "rerere.h"
  #include "unpack-trees.h"
 +#include "quote.h"
  
  static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@@ -36,7 -35,7 +36,7 @@@ static const char * const builtin_statu
        NULL
  };
  
 -static unsigned char head_sha1[20], merge_head_sha1[20];
 +static unsigned char head_sha1[20];
  static char *use_message_buffer;
  static const char commit_editmsg[] = "COMMIT_EDITMSG";
  static struct lock_file index_lock; /* real index */
@@@ -53,7 -52,7 +53,7 @@@ static char *edit_message, *use_message
  static char *author_name, *author_email, *author_date;
  static int all, edit_flag, also, interactive, only, amend, signoff;
  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 -static char *untracked_files_arg;
 +static char *untracked_files_arg, *force_date;
  /*
   * The default commit message cleanup mode will remove the lines
   * beginning with # (shell comments) and leading and trailing
@@@ -68,17 -67,10 +68,17 @@@ static enum 
  } cleanup_mode;
  static char *cleanup_arg;
  
- static int use_editor = 1, initial_commit, in_merge;
+ static int use_editor = 1, initial_commit, in_merge, include_status = 1;
  static const char *only_include_assumed;
  static struct strbuf message;
  
 +static int null_termination;
 +static enum {
 +      STATUS_FORMAT_LONG,
 +      STATUS_FORMAT_SHORT,
 +      STATUS_FORMAT_PORCELAIN,
 +} status_format = STATUS_FORMAT_LONG;
 +
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
        struct strbuf *buf = opt->value;
  static struct option builtin_commit_options[] = {
        OPT__QUIET(&quiet),
        OPT__VERBOSE(&verbose),
 -      OPT_GROUP("Commit message options"),
  
 +      OPT_GROUP("Commit message options"),
        OPT_FILENAME('F', "file", &logfile, "read log 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", "specify 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_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
        OPT_BOOLEAN('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"),
 +      /* end commit message options */
  
        OPT_GROUP("Commit contents options"),
        OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
        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_SET_INT(0, "porcelain", &status_format,
 +                  "show porcelain output format", STATUS_FORMAT_PORCELAIN),
 +      OPT_BOOLEAN('z', "null", &null_termination,
 +                  "terminate entries with NUL"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        { 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" },
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
 -      OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 +      /* end commit contents options */
  
        OPT_END()
  };
@@@ -183,15 -167,11 +184,15 @@@ static int list_paths(struct string_lis
  
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
 +              struct string_list_item *item;
 +
                if (ce->ce_flags & CE_UPDATE)
                        continue;
                if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
                        continue;
 -              string_list_insert(ce->name, list);
 +              item = string_list_insert(ce->name, list);
 +              if (ce_skip_worktree(ce))
 +                      item->util = item; /* better a valid pointer than a fake one */
        }
  
        return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
@@@ -204,10 -184,6 +205,10 @@@ static void add_remove_files(struct str
                struct stat st;
                struct string_list_item *p = &(list->items[i]);
  
 +              /* p->util is skip-worktree */
 +              if (p->util)
 +                      continue;
 +
                if (!lstat(p->string, &st)) {
                        if (add_to_cache(p->string, &st, 0))
                                die("updating files failed");
@@@ -331,7 -307,7 +332,7 @@@ static char *prepare_index(int argc, co
         */
        commit_style = COMMIT_PARTIAL;
  
 -      if (file_exists(git_path("MERGE_HEAD")))
 +      if (in_merge)
                die("cannot do a partial commit during a merge.");
  
        memset(&partial, 0, sizeof(partial));
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
                      struct wt_status *s)
  {
 +      unsigned char sha1[20];
 +
        if (s->relative_paths)
                s->prefix = prefix;
  
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
 +      s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
  
 -      wt_status_print(s);
 +      wt_status_collect(s);
 +
 +      switch (status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(s, null_termination);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(s, null_termination);
 +              break;
 +      case STATUS_FORMAT_LONG:
 +              wt_status_print(s);
 +              break;
 +      }
  
        return s->commitable;
  }
@@@ -450,9 -411,6 +451,9 @@@ static void determine_author_info(void
                email = xstrndup(lb + 2, rb - (lb + 2));
        }
  
 +      if (force_date)
 +              date = force_date;
 +
        author_name = name;
        author_email = email;
        author_date = date;
@@@ -590,7 -548,7 +591,7 @@@ static int prepare_to_commit(const cha
  
        /* This checks if committer ident is explicitly given */
        git_committer_info(0);
-       if (use_editor) {
+       if (use_editor && include_status) {
                char *author_ident;
                const char *committer_ident;
  
@@@ -778,21 -736,6 +779,21 @@@ static const char *find_author_by_nickn
        die("No existing author found with '%s'", name);
  }
  
 +
 +static void handle_untracked_files_arg(struct wt_status *s)
 +{
 +      if (!untracked_files_arg)
 +              ; /* default already initialized */
 +      else if (!strcmp(untracked_files_arg, "no"))
 +              s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
 +      else if (!strcmp(untracked_files_arg, "normal"))
 +              s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 +      else if (!strcmp(untracked_files_arg, "all"))
 +              s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 +      else
 +              die("Invalid untracked files mode '%s'", untracked_files_arg);
 +}
 +
  static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
                                      const char *prefix,
        if (get_sha1("HEAD", head_sha1))
                initial_commit = 1;
  
 -      if (!get_sha1("MERGE_HEAD", merge_head_sha1))
 -              in_merge = 1;
 -
        /* Sanity check options */
        if (amend && initial_commit)
                die("You have nothing to amend.");
        else
                die("Invalid cleanup mode %s", cleanup_arg);
  
 -      if (!untracked_files_arg)
 -              ; /* default already initialized */
 -      else if (!strcmp(untracked_files_arg, "no"))
 -              s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
 -      else if (!strcmp(untracked_files_arg, "normal"))
 -              s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 -      else if (!strcmp(untracked_files_arg, "all"))
 -              s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 -      else
 -              die("Invalid untracked files mode '%s'", untracked_files_arg);
 +      handle_untracked_files_arg(s);
  
        if (all && argc > 0)
                die("Paths with -a does not make sense.");
        else if (interactive && argc > 0)
                die("Paths with --interactive does not make sense.");
  
 +      if (null_termination && status_format == STATUS_FORMAT_LONG)
 +              status_format = STATUS_FORMAT_PORCELAIN;
 +      if (status_format != STATUS_FORMAT_LONG)
 +              dry_run = 1;
 +
        return argc;
  }
  
@@@ -941,7 -891,7 +942,7 @@@ static int parse_status_slot(const cha
                return WT_STATUS_NOBRANCH;
        if (!strcasecmp(var+offset, "unmerged"))
                return WT_STATUS_UNMERGED;
 -      die("bad config variable '%s'", var);
 +      return -1;
  }
  
  static int git_status_config(const char *k, const char *v, void *cb)
        }
        if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
 +              if (slot < 0)
 +                      return 0;
                if (!v)
                        return config_error_nonbool(k);
                color_parse(v, k, s->color_palette[slot]);
  int cmd_status(int argc, const char **argv, const char *prefix)
  {
        struct wt_status s;
 +      unsigned char sha1[20];
 +      static struct option builtin_status_options[] = {
 +              OPT__VERBOSE(&verbose),
 +              OPT_SET_INT('s', "short", &status_format,
 +                          "show status concisely", STATUS_FORMAT_SHORT),
 +              OPT_SET_INT(0, "porcelain", &status_format,
 +                          "show porcelain output format",
 +                          STATUS_FORMAT_PORCELAIN),
 +              OPT_BOOLEAN('z', "null", &null_termination,
 +                          "terminate entries with NUL"),
 +              { 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" },
 +              OPT_END(),
 +      };
 +
 +      if (null_termination && status_format == STATUS_FORMAT_LONG)
 +              status_format = STATUS_FORMAT_PORCELAIN;
  
        wt_status_prepare(&s);
        git_config(git_status_config, &s);
 +      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      argc = parse_options(argc, argv, prefix,
 +                           builtin_status_options,
 +                           builtin_status_usage, 0);
 +      handle_untracked_files_arg(&s);
 +
 +      if (*argv)
 +              s.pathspec = get_pathspec(prefix, argv);
 +
 +      read_cache();
 +      refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
 +      s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 +      s.in_merge = in_merge;
 +      wt_status_collect(&s);
 +
 +      if (s.relative_paths)
 +              s.prefix = prefix;
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
  
 -      argc = parse_and_validate_options(argc, argv, builtin_status_usage,
 -                                        prefix, &s);
 -      return dry_run_commit(argc, argv, prefix, &s);
 +      switch (status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(&s, null_termination);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(&s, null_termination);
 +              break;
 +      case STATUS_FORMAT_LONG:
 +              s.verbose = verbose;
 +              wt_status_print(&s);
 +              break;
 +      }
 +      return 0;
  }
  
  static void print_summary(const char *prefix, const unsigned char *sha1)
@@@ -1105,6 -1007,10 +1106,10 @@@ static int git_commit_config(const cha
  
        if (!strcmp(k, "commit.template"))
                return git_config_pathname(&template_file, k, v);
+       if (!strcmp(k, "commit.status")) {
+               include_status = git_config_bool(k, v);
+               return 0;
+       }
  
        return git_status_config(k, v, s);
  }
@@@ -1123,11 -1029,10 +1128,11 @@@ int cmd_commit(int argc, const char **a
  
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
 +      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      s.in_merge = in_merge;
  
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
 -
        argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
                                          prefix, &s);
        if (dry_run) {