]> asedeno.scripts.mit.edu Git - git.git/blobdiff - builtin-commit.c
git-commit: Allow to amend a merge commit that does not change the tree
[git.git] / builtin-commit.c
index e779db8ca3a7493a5353549b7ed562b68679a0ed..6c2dc390c451d5942521ddef9f046c5f98097dc3 100644 (file)
@@ -27,6 +27,11 @@ static const char * const builtin_commit_usage[] = {
        NULL
 };
 
+static const char * const builtin_status_usage[] = {
+       "git-status [options] [--] <filepattern>...",
+       NULL
+};
+
 static unsigned char head_sha1[20], merge_head_sha1[20];
 static char *use_message_buffer;
 static const char commit_editmsg[] = "COMMIT_EDITMSG";
@@ -336,7 +341,7 @@ static int prepare_log_message(const char *index_file, const char *prefix)
 
        fp = fopen(git_path(commit_editmsg), "w");
        if (fp == NULL)
-               die("could not open %s\n", git_path(commit_editmsg));
+               die("could not open %s", git_path(commit_editmsg));
 
        stripspace(&sb, 0);
 
@@ -346,11 +351,9 @@ static int prepare_log_message(const char *index_file, const char *prefix)
 
                strbuf_init(&sob, 0);
                strbuf_addstr(&sob, sign_off_header);
-               strbuf_addstr(&sob, fmt_ident(getenv("GIT_COMMITTER_NAME"),
-                                             getenv("GIT_COMMITTER_EMAIL"),
-                                             "", 1));
+               strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
+                                            getenv("GIT_COMMITTER_EMAIL")));
                strbuf_addch(&sob, '\n');
-
                for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
                        ; /* do nothing */
                if (prefixcmp(sb.buf + i, sob.buf)) {
@@ -362,11 +365,32 @@ static int prepare_log_message(const char *index_file, const char *prefix)
        }
 
        if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
-               die("could not write commit template: %s\n",
-                   strerror(errno));
+               die("could not write commit template: %s", strerror(errno));
 
        strbuf_release(&sb);
 
+       if (no_edit) {
+               struct rev_info rev;
+               unsigned char sha1[40];
+
+               fclose(fp);
+
+               if (!active_nr && read_cache() < 0)
+                       die("Cannot read index");
+
+               if (get_sha1("HEAD", sha1) != 0)
+                       return !!active_nr;
+
+               init_revisions(&rev, "");
+               rev.abbrev = 0;
+               setup_revisions(0, NULL, &rev, "HEAD");
+               DIFF_OPT_SET(&rev.diffopt, QUIET);
+               DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+               run_diff_index(&rev, 1 /* cached */);
+
+               return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+       }
+
        if (in_merge && !no_edit)
                fprintf(fp,
                        "#\n"
@@ -448,13 +472,13 @@ static void determine_author_info(struct strbuf *sb)
 
                a = strstr(use_message_buffer, "\nauthor ");
                if (!a)
-                       die("invalid commit: %s\n", use_message);
+                       die("invalid commit: %s", use_message);
 
                lb = strstr(a + 8, " <");
                rb = strstr(a + 8, "> ");
                eol = strchr(a + 8, '\n');
                if (!lb || !rb || !eol)
-                       die("invalid commit: %s\n", use_message);
+                       die("invalid commit: %s", use_message);
 
                name = xstrndup(a + 8, lb - (a + 8));
                email = xstrndup(lb + 2, rb - (lb + 2));
@@ -466,7 +490,7 @@ static void determine_author_info(struct strbuf *sb)
                const char *rb = strchr(force_author, '>');
 
                if (!lb || !rb)
-                       die("malformed --author parameter\n");
+                       die("malformed --author parameter");
                name = xstrndup(force_author, lb - force_author);
                email = xstrndup(lb + 2, rb - (lb + 2));
        }
@@ -474,12 +498,12 @@ static void determine_author_info(struct strbuf *sb)
        strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, 1));
 }
 
-static int parse_and_validate_options(int argc, const char *argv[])
+static int parse_and_validate_options(int argc, const char *argv[],
+                                     const char * const usage[])
 {
        int f = 0;
 
-       argc = parse_options(argc, argv, builtin_commit_options,
-                            builtin_commit_usage, 0);
+       argc = parse_options(argc, argv, builtin_commit_options, usage, 0);
 
        if (logfile || message.len || use_message)
                no_edit = 1;
@@ -496,7 +520,7 @@ static int parse_and_validate_options(int argc, const char *argv[])
        if (amend && initial_commit)
                die("You have nothing to amend.");
        if (amend && in_merge)
-               die("You are in the middle of a merger -- cannot amend.");
+               die("You are in the middle of a merge -- cannot amend.");
 
        if (use_message)
                f++;
@@ -576,7 +600,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        git_config(git_status_config);
 
-       argc = parse_and_validate_options(argc, argv);
+       argc = parse_and_validate_options(argc, argv, builtin_status_usage);
 
        index_file = prepare_index(argv, prefix);
 
@@ -619,7 +643,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 
        commit = lookup_commit(sha1);
        if (!commit)
-               die("couldn't look up newly created commit\n");
+               die("couldn't look up newly created commit");
        if (!commit || parse_commit(commit))
                die("could not parse newly created commit");
 
@@ -652,6 +676,14 @@ int git_commit_config(const char *k, const char *v)
        return git_status_config(k, v);
 }
 
+static int is_a_merge(const unsigned char *sha1)
+{
+       struct commit *commit = lookup_commit(sha1);
+       if (!commit || parse_commit(commit))
+               die("could not parse HEAD commit");
+       return !!(commit->parents && commit->parents->next);
+}
+
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
@@ -662,13 +694,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        int header_len;
        struct strbuf sb;
        const char *index_file, *reflog_msg;
-       char *nl;
+       char *nl, *p;
        unsigned char commit_sha1[20];
        struct ref_lock *ref_lock;
 
        git_config(git_commit_config);
 
-       argc = parse_and_validate_options(argc, argv);
+       argc = parse_and_validate_options(argc, argv, builtin_commit_usage);
 
        index_file = prepare_index(argv, prefix);
 
@@ -677,7 +709,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                return 1;
        }
 
-       if (!prepare_log_message(index_file, prefix) && !in_merge) {
+       if (!prepare_log_message(index_file, prefix) && !in_merge &&
+           !(amend && is_a_merge(head_sha1))) {
                run_status(stdout, index_file, prefix);
                rollback_index_files();
                unlink(commit_editmsg);
@@ -748,20 +781,29 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        /* Get the commit message and validate it */
        header_len = sb.len;
-       if (!no_edit)
-               launch_editor(git_path(commit_editmsg), &sb);
-       else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+       if (!no_edit) {
+               char index[PATH_MAX];
+               const char *env[2] = { index, NULL };
+               snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+               launch_editor(git_path(commit_editmsg), &sb, env);
+       } else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
                rollback_index_files();
-               die("could not read commit message\n");
+               die("could not read commit message");
        }
        if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
                rollback_index_files();
                exit(1);
        }
+
+       /* Truncate the message just before the diff, if any. */
+       p = strstr(sb.buf, "\ndiff --git a/");
+       if (p != NULL)
+               strbuf_setlen(&sb, p - sb.buf + 1);
+
        stripspace(&sb, 1);
        if (sb.len < header_len || message_is_empty(&sb, header_len)) {
                rollback_index_files();
-               die("no commit message?  aborting commit.");
+               die("no commit message?  aborting commit.");
        }
        strbuf_addch(&sb, '\0');
        if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))