Merge branch 'ci/commit--interactive-atomic'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 May 2011 23:47:10 +0000 (16:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 May 2011 23:47:10 +0000 (16:47 -0700)
* ci/commit--interactive-atomic:
Test atomic git-commit --interactive
Add commit to list of config.singlekey commands
Add support for -p/--patch to git-commit
Allow git commit --interactive with paths
t7501.8: feed a meaningful command
Use a temporary index for git commit --interactive

1  2 
Documentation/config.txt
builtin/add.c
builtin/commit.c
commit.h
t/t7501-commit.sh
diff --combined Documentation/config.txt
index 285c7f73ca1b7a2a63b549a6cacec4cc77ad5ad7,1c7336ccb82c7ea8e3f39fd39c27f47298701ee0..1a060ecbc8b137bff3df2c689d7e803937c66d18
@@@ -442,6 -442,8 +442,6 @@@ for most projects as source code and ot
  be delta compressed, but larger binary media files won't be.
  +
  Common unit suffixes of 'k', 'm', or 'g' are supported.
 -+
 -Currently only linkgit:git-fast-import[1] honors this setting.
  
  core.excludesfile::
        In addition to '.gitignore' (per-directory) and
@@@ -587,8 -589,6 +587,8 @@@ it will be treated as a shell command
  "gitk --all --not ORIG_HEAD".  Note that shell commands will be
  executed from the top-level directory of a repository, which may
  not necessarily be the current directory.
 +'GIT_PREFIX' is set as returned by running 'git rev-parse --show-prefix'
 +from the original current directory. See linkgit:git-rev-parse[1].
  
  am.keepcr::
        If true, git-am will call git-mailsplit for patches in mbox format
@@@ -643,7 -643,7 +643,7 @@@ branch.<name>.remote:
  
  branch.<name>.merge::
        Defines, together with branch.<name>.remote, the upstream branch
 -      for the given branch. It tells 'git fetch'/'git pull' which
 +      for the given branch. It tells 'git fetch'/'git pull'/'git rebase' which
        branch to merge and can also affect 'git push' (see push.default).
        When in branch <name>, it tells 'git fetch' the default
        refspec to be marked for merging in FETCH_HEAD. The value is
@@@ -708,16 -708,9 +708,16 @@@ second is the background.  The positio
  doesn't matter.
  
  color.diff::
 -      When set to `always`, always use colors in patch.
 -      When false (or `never`), never.  When set to `true` or `auto`, use
 -      colors only when the output is to the terminal. Defaults to false.
 +      Whether to use ANSI escape sequences to add color to patches.
 +      If this is set to `always`, linkgit:git-diff[1],
 +      linkgit:git-log[1], and linkgit:git-show[1] will use color
 +      for all patches.  If it is set to `true` or `auto`, those
 +      commands will only use color when output is to the terminal.
 +      Defaults to false.
 ++
 +This does not affect linkgit:git-format-patch[1] nor the
 +'git-diff-{asterisk}' plumbing commands.  Can be overridden on the
 +command line with the `--color[=<when>]` option.
  
  color.diff.<slot>::
        Use customized color for diff colorization.  `<slot>` specifies
@@@ -803,15 -796,11 +803,15 @@@ color.status.<slot>:
        color.branch.<slot>.
  
  color.ui::
 -      When set to `always`, always use colors in all git commands which
 -      are capable of colored output. When false (or `never`), never. When
 -      set to `true` or `auto`, use colors only when the output is to the
 -      terminal. When more specific variables of color.* are set, they always
 -      take precedence over this setting. Defaults to false.
 +      This variable determines the default value for variables such
 +      as `color.diff` and `color.grep` that control the use of color
 +      per command family. Its scope will expand as more commands learn
 +      configuration to set a default for the `--color` option.  Set it
 +      to `always` if you want all output not intended for machine
 +      consumption to use color, to `true` or `auto` if you want such
 +      output to use color when written to the terminal, or to `false` or
 +      `never` if you prefer git commands not to use color unless enabled
 +      explicitly with some other configuration or the `--color` option.
  
  commit.status::
        A boolean to enable/disable inclusion of status information in the
@@@ -823,7 -812,68 +823,7 @@@ commit.template:
        "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
        specified user's home directory.
  
 -diff.autorefreshindex::
 -      When using 'git diff' to compare with work tree
 -      files, do not consider stat-only change as changed.
 -      Instead, silently run `git update-index --refresh` to
 -      update the cached stat information for paths whose
 -      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.external::
 -      If this config variable is set, diff generation is not
 -      performed using the internal diff machinery, but using the
 -      given command.  Can be overridden with the `GIT_EXTERNAL_DIFF'
 -      environment variable.  The command is called with parameters
 -      as described under "git Diffs" in linkgit:git[1].  Note: if
 -      you want to use an external diff program only on a subset of
 -      your files, you might want to use linkgit:gitattributes[5] instead.
 -
 -diff.mnemonicprefix::
 -      If set, 'git diff' uses a prefix pair that is different from the
 -      standard "a/" and "b/" depending on what is being compared.  When
 -      this configuration is in effect, reverse diff output also swaps
 -      the order of the prefixes:
 -`git diff`;;
 -      compares the (i)ndex and the (w)ork tree;
 -`git diff HEAD`;;
 -       compares a (c)ommit and the (w)ork tree;
 -`git diff --cached`;;
 -      compares a (c)ommit and the (i)ndex;
 -`git diff HEAD:file1 file2`;;
 -      compares an (o)bject and a (w)ork tree entity;
 -`git diff --no-index a b`;;
 -      compares two non-git things (1) and (2).
 -
 -diff.noprefix::
 -      If set, 'git diff' does not show any source or destination prefix.
 -
 -diff.renameLimit::
 -      The number of files to consider when performing the copy/rename
 -      detection; equivalent to the 'git diff' option '-l'.
 -
 -diff.renames::
 -      Tells git to detect renames.  If set to any boolean value, it
 -      will enable basic rename detection.  If set to "copies" or
 -      "copy", it will detect copies, as well.
 -
 -diff.ignoreSubmodules::
 -      Sets the default value of --ignore-submodules. Note that this
 -      affects only 'git diff' Porcelain, and not lower level 'diff'
 -      commands such as 'git diff-files'. 'git checkout' also honors
 -      this setting when reporting uncommitted changes.
 -
 -diff.suppressBlankEmpty::
 -      A boolean to inhibit the standard behavior of printing a space
 -      before each empty output line. Defaults to false.
 -
 -diff.tool::
 -      Controls which diff tool is used.  `diff.tool` overrides
 -      `merge.tool` when used by linkgit:git-difftool[1] and has
 -      the same valid values as `merge.tool` minus "tortoisemerge"
 -      and plus "kompare".
 +include::diff-config.txt[]
  
  difftool.<tool>.path::
        Override the path for the given tool.  This is useful in case
@@@ -927,16 -977,6 +927,16 @@@ format.signoff:
      the rights to submit this work under the same open source license.
      Please see the 'SubmittingPatches' document for further discussion.
  
 +filter.<driver>.clean::
 +      The command which is used to convert the content of a worktree
 +      file to a blob upon checkin.  See linkgit:gitattributes[5] for
 +      details.
 +
 +filter.<driver>.smudge::
 +      The command which is used to convert the content of a blob
 +      object to a worktree file upon checkout.  See
 +      linkgit:gitattributes[5] for details.
 +
  gc.aggressiveWindow::
        The window size parameter used in the delta compression
        algorithm used by 'git gc --aggressive'.  This defaults
@@@ -1310,9 -1350,10 +1310,10 @@@ interactive.singlekey:
        In interactive commands, allow the user to provide one-letter
        input with a single key (i.e., without hitting enter).
        Currently this is used by the `\--patch` mode of
-       linkgit:git-add[1], linkgit:git-reset[1], linkgit:git-stash[1] and
-       linkgit:git-checkout[1]. Note that this setting is silently
-       ignored if portable keystroke input is not available.
+       linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
+       linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
+       setting is silently ignored if portable keystroke input
+       is not available.
  
  log.date::
        Set the default date-time mode for the 'log' command.
diff --combined builtin/add.c
index e57abddf5297432d62bc20b8e3bde2e5eab474c4,f02524bae0a610708e25a3d573ee4c00c701fe80..c59b0c98fefefc413c8330715fffcc83142d5b2d
@@@ -26,27 -26,6 +26,27 @@@ struct update_callback_data 
        int add_errors;
  };
  
 +static int fix_unmerged_status(struct diff_filepair *p,
 +                             struct update_callback_data *data)
 +{
 +      if (p->status != DIFF_STATUS_UNMERGED)
 +              return p->status;
 +      if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
 +              /*
 +               * This is not an explicit add request, and the
 +               * path is missing from the working tree (deleted)
 +               */
 +              return DIFF_STATUS_DELETED;
 +      else
 +              /*
 +               * Either an explicit add request, or path exists
 +               * in the working tree.  An attempt to explicitly
 +               * add a path that does not exist in the working tree
 +               * will be caught as an error by the caller immediately.
 +               */
 +              return DIFF_STATUS_MODIFIED;
 +}
 +
  static void update_callback(struct diff_queue_struct *q,
                            struct diff_options *opt, void *cbdata)
  {
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                const char *path = p->one->path;
 -              switch (p->status) {
 +              switch (fix_unmerged_status(p, data)) {
                default:
                        die(_("unexpected diff status %c"), p->status);
 -              case DIFF_STATUS_UNMERGED:
 -                      /*
 -                       * ADD_CACHE_IGNORE_REMOVAL is unset if "git
 -                       * add -u" is calling us, In such a case, a
 -                       * missing work tree file needs to be removed
 -                       * if there is an unmerged entry at stage #2,
 -                       * but such a diff record is followed by
 -                       * another with DIFF_STATUS_DELETED (and if
 -                       * there is no stage #2, we won't see DELETED
 -                       * nor MODIFIED).  We can simply continue
 -                       * either way.
 -                       */
 -                      if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
 -                              continue;
 -                      /*
 -                       * Otherwise, it is "git add path" is asking
 -                       * to explicitly add it; we fall through.  A
 -                       * missing work tree file is an error and is
 -                       * caught by add_file_to_index() in such a
 -                       * case.
 -                       */
                case DIFF_STATUS_MODIFIED:
                case DIFF_STATUS_TYPE_CHANGED:
                        if (add_file_to_index(&the_index, path, data->flags)) {
@@@ -91,7 -91,6 +91,7 @@@ int add_files_to_cache(const char *pref
        data.flags = flags;
        data.add_errors = 0;
        rev.diffopt.format_callback_data = &data;
 +      rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
        return !!data.add_errors;
  }
@@@ -242,7 -241,7 +242,7 @@@ int run_add_interactive(const char *rev
        return status;
  }
  
- int interactive_add(int argc, const char **argv, const char *prefix)
+ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
  {
        const char **pathspec = NULL;
  
        }
  
        return run_add_interactive(NULL,
-                                  patch_interactive ? "--patch" : NULL,
+                                  patch ? "--patch" : NULL,
                                   pathspec);
  }
  
@@@ -331,8 -330,8 +331,8 @@@ static struct option builtin_add_option
  
  static int add_config(const char *var, const char *value, void *cb)
  {
 -      if (!strcasecmp(var, "add.ignoreerrors") ||
 -          !strcasecmp(var, "add.ignore-errors")) {
 +      if (!strcmp(var, "add.ignoreerrors") ||
 +          !strcmp(var, "add.ignore-errors")) {
                ignore_add_errors = git_config_bool(var, value);
                return 0;
        }
@@@ -378,7 -377,7 +378,7 @@@ int cmd_add(int argc, const char **argv
        if (patch_interactive)
                add_interactive = 1;
        if (add_interactive)
-               exit(interactive_add(argc - 1, argv + 1, prefix));
+               exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
  
        if (edit_interactive)
                return(edit_patch(argc, argv, prefix));
diff --combined builtin/commit.c
index 411d5e41531adabe679df2d1381dbf923c5e925b,008c1ec8380af17de62452dbf580c0c81912bf8e..5286432f39b87e03a08cc86bb622b6ca3b911365
@@@ -83,7 -83,7 +83,7 @@@ static const char *template_file
  static const char *author_message, *author_message_buffer;
  static char *edit_message, *use_message;
  static char *fixup_message, *squash_message;
- static int all, edit_flag, also, interactive, only, amend, signoff;
+ static int all, edit_flag, also, interactive, patch_interactive, only, amend, signoff;
  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
  static int no_post_rewrite, allow_empty_message;
  static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
@@@ -152,6 -152,7 +152,7 @@@ static struct option builtin_commit_opt
        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"),
@@@ -336,18 -337,11 +337,11 @@@ static char *prepare_index(int argc, co
        int fd;
        struct string_list partial;
        const char **pathspec = NULL;
+       char *old_index_env = NULL;
        int refresh_flags = REFRESH_QUIET;
  
        if (is_status)
                refresh_flags |= REFRESH_UNMERGED;
-       if (interactive) {
-               if (interactive_add(argc, argv, prefix) != 0)
-                       die(_("interactive add failed"));
-               if (read_cache_preload(NULL) < 0)
-                       die(_("index file corrupt"));
-               commit_style = COMMIT_AS_IS;
-               return get_index_file();
-       }
  
        if (*argv)
                pathspec = get_pathspec(prefix, argv);
        if (read_cache_preload(pathspec) < 0)
                die(_("index file corrupt"));
  
+       if (interactive) {
+               fd = hold_locked_index(&index_lock, 1);
+               refresh_cache_or_die(refresh_flags);
+               if (write_cache(fd, active_cache, active_nr) ||
+                   close_lock_file(&index_lock))
+                       die(_("unable to create temporary index"));
+               old_index_env = getenv(INDEX_ENVIRONMENT);
+               setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
+               if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
+                       die(_("interactive add failed"));
+               if (old_index_env && *old_index_env)
+                       setenv(INDEX_ENVIRONMENT, old_index_env, 1);
+               else
+                       unsetenv(INDEX_ENVIRONMENT);
+               discard_cache();
+               read_cache_from(index_lock.filename);
+               commit_style = COMMIT_NORMAL;
+               return index_lock.filename;
+       }
        /*
         * Non partial, non as-is commit.
         *
@@@ -615,7 -636,6 +636,7 @@@ static int prepare_to_commit(const cha
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
        int ident_shown = 0;
 +      int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
  
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
                if (strbuf_read_file(&sb, template_file, 0) < 0)
                        die_errno(_("could not read '%s'"), template_file);
                hook_arg1 = "template";
 +              clean_message_contents = 0;
        }
  
        /*
        if (s->fp == NULL)
                die_errno(_("could not open '%s'"), git_path(commit_editmsg));
  
 -      if (cleanup_mode != CLEANUP_NONE)
 +      if (clean_message_contents)
                stripspace(&sb, 0);
  
        if (signoff) {
@@@ -1043,8 -1062,11 +1064,11 @@@ static int parse_and_validate_options(i
                author_message_buffer = read_commit_message(author_message);
        }
  
+       if (patch_interactive)
+               interactive = 1;
        if (!!also + !!only + !!all + !!interactive > 1)
-               die(_("Only one of --include/--only/--all/--interactive can be used."));
+               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."));
        if (argc == 0 && only && amend)
  
        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;
diff --combined commit.h
index b3c3bb70c5e737ed18df7ca665c85e339e1f3292,b432642b997b36c742a4d4d6ff0b92497d192ee4..43940e2bddcc34d2b74d2ddb76c045939c413c52
+++ b/commit.h
@@@ -90,8 -90,6 +90,8 @@@ extern char *logmsg_reencode(const stru
  extern char *reencode_commit_message(const struct commit *commit,
                                     const char **encoding_p);
  extern void get_commit_format(const char *arg, struct rev_info *);
 +extern const char *format_subject(struct strbuf *sb, const char *msg,
 +                                const char *line_separator);
  extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
  extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
@@@ -161,7 -159,7 +161,7 @@@ extern struct commit_list *get_shallow_
  int is_descendant_of(struct commit *, struct commit_list *);
  int in_merge_bases(struct commit *, struct commit **, int);
  
- extern int interactive_add(int argc, const char **argv, const char *prefix);
+ extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
  extern int run_add_interactive(const char *revision, const char *patch_mode,
                               const char **pathspec);
  
diff --combined t/t7501-commit.sh
index 7f7f7c7b95475b3b0673de00b68914a08098aead,73e19c50b0d8f57cf36b2d377f0f7b9eb4ce4072..3ad04363b5920497617f806a18a3a5a0083ac1b9
@@@ -16,10 -16,9 +16,10 @@@ test_expect_success 
        "echo 'bongo bongo' >file &&
         git add file"
  
 -test_expect_success C_LOCALE_OUTPUT \
 -      "Constructing initial commit" \
 -      "git status | grep 'Initial commit'"
 +test_expect_success "Constructing initial commit" '
 +      git status >actual &&
 +      test_i18ngrep "Initial commit" actual
 +'
  
  test_expect_success \
        "fail initial amend" \
@@@ -42,10 -41,13 +42,13 @@@ test_expect_success 
        "echo King of the bongo >file &&
        test_must_fail git commit -m foo -a file"
  
- test_expect_success PERL \
-       "using paths with --interactive" \
-       "echo bong-o-bong >file &&
-       ! (echo 7 | git commit -m foo --interactive file)"
+ test_expect_success PERL 'can use paths with --interactive' '
+       echo bong-o-bong >file &&
+       # 2: update, 1:st path, that is all, 7: quit
+       ( echo 2; echo 1; echo; echo 7 ) |
+       git commit -m foo --interactive file &&
+       git reset --hard HEAD^
+ '
  
  test_expect_success \
        "using invalid commit with -C" \
@@@ -130,6 -132,16 +133,16 @@@ test_expect_success 
  test_expect_success PERL \
        "interactive add" \
        "echo 7 | git commit --interactive | grep 'What now'"
+ test_expect_success PERL \
+       "commit --interactive doesn't change index if editor aborts" \
+       "echo zoo >file &&
+       test_must_fail git diff --exit-code >diff1 &&
+       (echo u ; echo '*' ; echo q) |
+       (EDITOR=: && export EDITOR &&
+        test_must_fail git commit --interactive) &&
+       git diff >diff2 &&
+       test_cmp diff1 diff2"
  
  test_expect_success \
        "showing committed revisions" \