Merge branch 'jn/maint-commit-missing-template'
authorJunio C Hamano <gitster@pobox.com>
Fri, 4 Mar 2011 23:02:27 +0000 (15:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 4 Mar 2011 23:02:27 +0000 (15:02 -0800)
* jn/maint-commit-missing-template:
commit: error out for missing commit message template

1  2 
builtin/commit.c
t/t7500-commit.sh
diff --combined builtin/commit.c
index 355b2cbca7460d40b013380c38801686e61b74f1,cc16e3f292403814e137078fd46927eeaecf0d36..d71e1e0c9c27c4d03cd02b4deaacec67af33917a
@@@ -69,7 -69,6 +69,7 @@@ static enum 
  static const char *logfile, *force_author;
  static const char *template_file;
  static char *edit_message, *use_message;
 +static char *fixup_message, *squash_message;
  static int all, edit_flag, also, interactive, only, amend, signoff;
  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
  static int no_post_rewrite, allow_empty_message;
@@@ -114,18 -113,16 +114,18 @@@ static int opt_parse_m(const struct opt
  }
  
  static struct option builtin_commit_options[] = {
 -      OPT__QUIET(&quiet),
 -      OPT__VERBOSE(&verbose),
 +      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 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_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"),
                    STATUS_FORMAT_SHORT),
        OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
 -                  "show porcelain output format", STATUS_FORMAT_PORCELAIN),
 +                  "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" },
 +      { 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,
@@@ -576,25 -573,6 +576,25 @@@ static int prepare_to_commit(const cha
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
  
 +      if (squash_message) {
 +              /*
 +               * Insert the proper subject line before other commit
 +               * message options add their content.
 +               */
 +              if (use_message && !strcmp(use_message, squash_message))
 +                      strbuf_addstr(&sb, "squash! ");
 +              else {
 +                      struct pretty_print_context ctx = {0};
 +                      struct commit *c;
 +                      c = lookup_commit_reference_by_name(squash_message);
 +                      if (!c)
 +                              die("could not lookup commit %s", squash_message);
 +                      ctx.output_encoding = get_commit_output_encoding();
 +                      format_commit_message(c, "squash! %s\n\n", &sb,
 +                                            &ctx);
 +              }
 +      }
 +
        if (message.len) {
                strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
                strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
 +      } else if (fixup_message) {
 +              struct pretty_print_context ctx = {0};
 +              struct commit *commit;
 +              commit = lookup_commit_reference_by_name(fixup_message);
 +              if (!commit)
 +                      die("could not lookup commit %s", fixup_message);
 +              ctx.output_encoding = get_commit_output_encoding();
 +              format_commit_message(commit, "fixup! %s\n\n",
 +                                    &sb, &ctx);
 +              hook_arg1 = "message";
        } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
                        die_errno("could not read MERGE_MSG");
                if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
                        die_errno("could not read SQUASH_MSG");
                hook_arg1 = "squash";
-       } else if (template_file && !stat(template_file, &statbuf)) {
+       } else if (template_file) {
                if (strbuf_read_file(&sb, template_file, 0) < 0)
                        die_errno("could not read '%s'", template_file);
                hook_arg1 = "template";
        else if (in_merge)
                hook_arg1 = "merge";
  
 +      if (squash_message) {
 +              /*
 +               * If squash_commit was used for the commit subject,
 +               * then we're possibly hijacking other commit log options.
 +               * Reset the hook args to tell the real story.
 +               */
 +              hook_arg1 = "message";
 +              hook_arg2 = "";
 +      }
 +
        fp = fopen(git_path(commit_editmsg), "w");
        if (fp == NULL)
                die_errno("could not open '%s'", git_path(commit_editmsg));
@@@ -914,7 -872,7 +914,7 @@@ static int parse_and_validate_options(i
        if (force_author && renew_authorship)
                die("Using both --reset-author and --author does not make sense");
  
 -      if (logfile || message.len || use_message)
 +      if (logfile || message.len || use_message || fixup_message)
                use_editor = 0;
        if (edit_flag)
                use_editor = 1;
                die("You have nothing to amend.");
        if (amend && in_merge)
                die("You are in the middle of a merge -- cannot amend.");
 -
 +      if (fixup_message && squash_message)
 +              die("Options --squash and --fixup cannot be used together");
        if (use_message)
                f++;
        if (edit_message)
                f++;
 +      if (fixup_message)
 +              f++;
        if (logfile)
                f++;
        if (f > 1)
 -              die("Only one of -c/-C/-F can be used.");
 +              die("Only one of -c/-C/-F/--fixup can be used.");
        if (message.len && f > 0)
 -              die("Option -m cannot be combined with -c/-C/-F.");
 +              die("Option -m cannot be combined with -c/-C/-F/--fixup.");
        if (edit_message)
                use_message = edit_message;
 -      if (amend && !use_message)
 +      if (amend && !use_message && !fixup_message)
                use_message = "HEAD";
        if (!use_message && renew_authorship)
                die("--reset-author can be used only with -C, -c or --amend.");
        if (use_message) {
 -              unsigned char sha1[20];
 -              static char utf8[] = "UTF-8";
                const char *out_enc;
 -              char *enc, *end;
                struct commit *commit;
  
 -              if (get_sha1(use_message, sha1))
 +              commit = lookup_commit_reference_by_name(use_message);
 +              if (!commit)
                        die("could not lookup commit %s", use_message);
 -              commit = lookup_commit_reference(sha1);
 -              if (!commit || parse_commit(commit))
 -                      die("could not parse commit %s", use_message);
 -
 -              enc = strstr(commit->buffer, "\nencoding");
 -              if (enc) {
 -                      end = strchr(enc + 10, '\n');
 -                      enc = xstrndup(enc + 10, end - (enc + 10));
 -              } else {
 -                      enc = utf8;
 -              }
 -              out_enc = git_commit_encoding ? git_commit_encoding : utf8;
 -
 -              if (strcmp(out_enc, enc))
 -                      use_message_buffer =
 -                              reencode_string(commit->buffer, out_enc, enc);
 +              out_enc = get_commit_output_encoding();
 +              use_message_buffer = logmsg_reencode(commit, out_enc);
  
                /*
                 * If we failed to reencode the buffer, just copy it
                 */
                if (use_message_buffer == NULL)
                        use_message_buffer = xstrdup(commit->buffer);
 -              if (enc != utf8)
 -                      free(enc);
        }
  
        if (!!also + !!only + !!all + !!interactive > 1)
@@@ -1020,8 -993,6 +1020,8 @@@ static int parse_status_slot(const cha
  {
        if (!strcasecmp(var+offset, "header"))
                return WT_STATUS_HEADER;
 +      if (!strcasecmp(var+offset, "branch"))
 +              return WT_STATUS_ONBRANCH;
        if (!strcasecmp(var+offset, "updated")
                || !strcasecmp(var+offset, "added"))
                return WT_STATUS_UPDATED;
@@@ -1086,13 -1057,13 +1086,13 @@@ int cmd_status(int argc, const char **a
        int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
 -              OPT__VERBOSE(&verbose),
 +              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,
                            "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
 -                          "show porcelain output format",
 +                          "machine-readable output",
                            STATUS_FORMAT_PORCELAIN),
                OPT_BOOLEAN('z', "null", &null_termination,
                            "terminate entries with NUL"),
                OPT_END(),
        };
  
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
 +              usage_with_options(builtin_status_usage, builtin_status_options);
 +
        if (null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
  
@@@ -1297,9 -1265,6 +1297,9 @@@ int cmd_commit(int argc, const char **a
        int allow_fast_forward = 1;
        struct wt_status s;
  
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
 +              usage_with_options(builtin_commit_usage, builtin_commit_options);
 +
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
        in_merge = file_exists(git_path("MERGE_HEAD"));
diff --combined t/t7500-commit.sh
index d551b77ce6d94acce80069f2197f0169ee59e6d5,1590877c425c4ae4b884770ca812de7f1a5f359f..5976f598fc7bf48120865eca3ceef74a151e717a
@@@ -10,12 -10,7 +10,12 @@@ Tests for selected commit options.
  . ./test-lib.sh
  
  commit_msg_is () {
 -      test "`git log --pretty=format:%s%b -1`" = "$1"
 +      expect=commit_msg_is.expect
 +      actual=commit_msg_is.actual
 +
 +      printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
 +      printf "%s" "$1" >$actual &&
 +      test_cmp $expect $actual
  }
  
  # A sanity check to see if commit is working at all.
@@@ -28,13 -23,21 +28,21 @@@ test_expect_success 'a basic commit in 
  test_expect_success 'nonexistent template file should return error' '
        echo changes >> foo &&
        git add foo &&
-       test_must_fail git commit --template "$PWD"/notexist
+       (
+               GIT_EDITOR="echo hello >\"\$1\"" &&
+               export GIT_EDITOR &&
+               test_must_fail git commit --template "$PWD"/notexist
+       )
  '
  
  test_expect_success 'nonexistent template file in config should return error' '
        git config commit.template "$PWD"/notexist &&
-       test_must_fail git commit &&
-       git config --unset commit.template
+       test_when_finished "git config --unset commit.template" &&
+       (
+               GIT_EDITOR="echo hello >\"\$1\"" &&
+               export GIT_EDITOR &&
+               test_must_fail git commit
+       )
  '
  
  # From now on we'll use a template file that exists.
@@@ -220,84 -223,4 +228,84 @@@ test_expect_success 'Commit a message w
        commit_msg_is "hello there"
  '
  
 +commit_for_rebase_autosquash_setup () {
 +      echo "first content line" >>foo &&
 +      git add foo &&
 +      cat >log <<EOF &&
 +target message subject line
 +
 +target message body line 1
 +target message body line 2
 +EOF
 +      git commit -F log &&
 +      echo "second content line" >>foo &&
 +      git add foo &&
 +      git commit -m "intermediate commit" &&
 +      echo "third content line" >>foo &&
 +      git add foo
 +}
 +
 +test_expect_success 'commit --fixup provides correct one-line commit message' '
 +      commit_for_rebase_autosquash_setup &&
 +      git commit --fixup HEAD~1 &&
 +      commit_msg_is "fixup! target message subject line"
 +'
 +
 +test_expect_success 'commit --squash works with -F' '
 +      commit_for_rebase_autosquash_setup &&
 +      echo "log message from file" >msgfile &&
 +      git commit --squash HEAD~1 -F msgfile  &&
 +      commit_msg_is "squash! target message subject linelog message from file"
 +'
 +
 +test_expect_success 'commit --squash works with -m' '
 +      commit_for_rebase_autosquash_setup &&
 +      git commit --squash HEAD~1 -m "foo bar\nbaz" &&
 +      commit_msg_is "squash! target message subject linefoo bar\nbaz"
 +'
 +
 +test_expect_success 'commit --squash works with -C' '
 +      commit_for_rebase_autosquash_setup &&
 +      git commit --squash HEAD~1 -C HEAD &&
 +      commit_msg_is "squash! target message subject lineintermediate commit"
 +'
 +
 +test_expect_success 'commit --squash works with -c' '
 +      commit_for_rebase_autosquash_setup &&
 +      test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
 +      git commit --squash HEAD~1 -c HEAD &&
 +      commit_msg_is "squash! target message subject lineedited commit"
 +'
 +
 +test_expect_success 'commit --squash works with -C for same commit' '
 +      commit_for_rebase_autosquash_setup &&
 +      git commit --squash HEAD -C HEAD &&
 +      commit_msg_is "squash! intermediate commit"
 +'
 +
 +test_expect_success 'commit --squash works with -c for same commit' '
 +      commit_for_rebase_autosquash_setup &&
 +      test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
 +      git commit --squash HEAD -c HEAD &&
 +      commit_msg_is "squash! edited commit"
 +'
 +
 +test_expect_success 'commit --squash works with editor' '
 +      commit_for_rebase_autosquash_setup &&
 +      test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
 +      git commit --squash HEAD~1 &&
 +      commit_msg_is "squash! target message subject linecommit message"
 +'
 +
 +test_expect_success 'invalid message options when using --fixup' '
 +      echo changes >>foo &&
 +      echo "message" >log &&
 +      git add foo &&
 +      test_must_fail git commit --fixup HEAD~1 --squash HEAD~2 &&
 +      test_must_fail git commit --fixup HEAD~1 -C HEAD~2 &&
 +      test_must_fail git commit --fixup HEAD~1 -c HEAD~2 &&
 +      test_must_fail git commit --fixup HEAD~1 -m "cmdline message" &&
 +      test_must_fail git commit --fixup HEAD~1 -F log
 +'
 +
  test_done