Code

Merge branch 'jk/commit-die-on-bogus-ident'
authorJunio C Hamano <gitster@pobox.com>
Wed, 22 Dec 2010 22:41:14 +0000 (14:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 22 Dec 2010 22:41:14 +0000 (14:41 -0800)
* jk/commit-die-on-bogus-ident:
  commit: die before asking to edit the log message
  ident: die on bogus date format

Conflicts:
builtin/commit.c

1  2 
builtin/commit.c

diff --combined builtin/commit.c
index 6c09857a602193857cf8907c1c1c2574053e29e1,3bcb4b7ec040c4896593b75b3e574951891d016f..22ba54f9bb2ea9f9f9f01528e8a155723da7a4da
@@@ -69,8 -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 *author_name, *author_email, *author_date;
 +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;
@@@ -115,8 -113,8 +114,8 @@@ 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_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_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"),
@@@ -462,7 -458,7 +461,7 @@@ static int is_a_merge(const unsigned ch
  
  static const char sign_off_header[] = "Signed-off-by: ";
  
- static void determine_author_info(void)
+ static void determine_author_info(struct strbuf *author_ident)
  {
        char *name, *email, *date;
  
  
        if (force_date)
                date = force_date;
-       author_name = name;
-       author_email = email;
-       author_date = date;
+       strbuf_addstr(author_ident, fmt_ident(name, email, date,
+                                             IDENT_ERROR_ON_NO_NAME));
  }
  
  static int ends_rfc2822_footer(struct strbuf *sb)
        return 1;
  }
  
+ static char *cut_ident_timestamp_part(char *string)
+ {
+       char *ket = strrchr(string, '>');
+       if (!ket || ket[1] != ' ')
+               die("Malformed ident string: '%s'", string);
+       *++ket = '\0';
+       return ket;
+ }
  static int prepare_to_commit(const char *index_file, const char *prefix,
-                            struct wt_status *s)
+                            struct wt_status *s,
+                            struct strbuf *author_ident)
  {
        struct stat statbuf;
+       struct strbuf committer_ident = STRBUF_INIT;
        int commitable, saved_color_setting;
        struct strbuf sb = STRBUF_INIT;
        char *buffer;
        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");
        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));
  
        strbuf_release(&sb);
  
-       determine_author_info();
+       /* This checks and barfs if author is badly specified */
+       determine_author_info(author_ident);
  
        /* This checks if committer ident is explicitly given */
-       git_committer_info(0);
+       strbuf_addstr(&committer_ident, git_committer_info(0));
        if (use_editor && include_status) {
-               char *author_ident;
-               const char *committer_ident;
+               char *ai_tmp, *ci_tmp;
                if (in_merge)
                        fprintf(fp,
                                "#\n"
                if (only_include_assumed)
                        fprintf(fp, "# %s\n", only_include_assumed);
  
-               author_ident = xstrdup(fmt_name(author_name, author_email));
-               committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
-                                          getenv("GIT_COMMITTER_EMAIL"));
-               if (strcmp(author_ident, committer_ident))
+               ai_tmp = cut_ident_timestamp_part(author_ident->buf);
+               ci_tmp = cut_ident_timestamp_part(committer_ident.buf);
+               if (strcmp(author_ident->buf, committer_ident.buf))
                        fprintf(fp,
                                "%s"
                                "# Author:    %s\n",
                                ident_shown++ ? "" : "#\n",
-                               author_ident);
-               free(author_ident);
+                               author_ident->buf);
  
                if (!user_ident_sufficiently_given())
                        fprintf(fp,
                                "%s"
                                "# Committer: %s\n",
                                ident_shown++ ? "" : "#\n",
-                               committer_ident);
+                               committer_ident.buf);
  
                if (ident_shown)
                        fprintf(fp, "#\n");
                s->use_color = 0;
                commitable = run_status(fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
+               *ai_tmp = ' ';
+               *ci_tmp = ' ';
        } else {
                unsigned char sha1[20];
                const char *parent = "HEAD";
                else
                        commitable = index_differs_from(parent, 0);
        }
+       strbuf_release(&committer_ident);
  
        fclose(fp);
  
@@@ -905,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)
@@@ -1011,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;
@@@ -1077,7 -1057,7 +1086,7 @@@ 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,
                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;
  
@@@ -1278,6 -1255,7 +1287,7 @@@ static int run_rewrite_hook(const unsig
  int cmd_commit(int argc, const char **argv, const char *prefix)
  {
        struct strbuf sb = STRBUF_INIT;
+       struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl, *p;
        unsigned char commit_sha1[20];
        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"));
  
        /* Set up everything for writing the commit object.  This includes
           running hooks, writing the trees, and interacting with the user.  */
-       if (!prepare_to_commit(index_file, prefix, &s)) {
+       if (!prepare_to_commit(index_file, prefix, &s, &author_ident)) {
                rollback_index_files();
                return 1;
        }
        }
  
        if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
-                       fmt_ident(author_name, author_email, author_date,
-                               IDENT_ERROR_ON_NO_NAME))) {
+                       author_ident.buf)) {
                rollback_index_files();
                die("failed to write commit object");
        }
+       strbuf_release(&author_ident);
  
        ref_lock = lock_any_ref_for_update("HEAD",
                                           initial_commit ? NULL : head_sha1,