In interactive rebases, we commit a little bit differently than the
sequencer did so far: we heed the "author-script", the "message" and
the "amend" files in the .git/rebase-merge/ subdirectory.

Likewise, we may want to edit the commit message *even* when providing
a file containing the suggested commit message. Therefore we change the
code to not even provide a default message when we do not want any, and
to call the editor explicitly.

As interactive rebase's GPG settings are configured differently from
how cherry-pick (and therefore sequencer) handles them, we will leave
support for that to the next commit.

Signed-off-by: Johannes Schindelin <johannes.schinde...@gmx.de>
---
 sequencer.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 sequencer.h |  3 ++
 2 files changed, 83 insertions(+), 12 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 3398774..b124980 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -27,6 +27,16 @@ static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
 static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
 static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
 
+/*
+ * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
+ * GIT_AUTHOR_DATE that will be used for the commit that is currently
+ * being rebased.
+ */
+static GIT_PATH_FUNC(rebase_path_author_script, "rebase-merge/author-script")
+
+/* We will introduce the 'interactive rebase' mode later */
+#define IS_REBASE_I() 0
+
 static const char *get_dir(const struct replay_opts *opts)
 {
        return git_path_seq_dir();
@@ -377,20 +387,72 @@ static int is_index_unchanged(void)
        return !hashcmp(active_cache_tree->sha1, 
head_commit->tree->object.oid.hash);
 }
 
+static char **read_author_script(void)
+{
+       struct strbuf script = STRBUF_INIT;
+       int i, count = 0;
+       char *p, *p2, **env;
+       size_t env_size;
+
+       if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
+               return NULL;
+
+       for (p = script.buf; *p; p++)
+               if (skip_prefix(p, "'\\\\''", (const char **)&p2))
+                       strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
+               else if (*p == '\'')
+                       strbuf_splice(&script, p-- - script.buf, 1, "", 0);
+               else if (*p == '\n') {
+                       *p = '\0';
+                       count++;
+               }
+
+       env_size = (count + 1) * sizeof(*env);
+       strbuf_grow(&script, env_size);
+       memmove(script.buf + env_size, script.buf, script.len);
+       p = script.buf + env_size;
+       env = (char **)strbuf_detach(&script, NULL);
+
+       for (i = 0; i < count; i++) {
+               env[i] = p;
+               p += strlen(p) + 1;
+       }
+       env[count] = NULL;
+
+       return env;
+}
+
 /*
  * If we are cherry-pick, and if the merge did not result in
  * hand-editing, we will hit this commit and inherit the original
  * author date and name.
  * If we are revert, or if our cherry-pick results in a hand merge,
- * we had better say that the current user is responsible for that.
+ * we had better say that the current user is responsible for that
+ * (except, of course, while running an interactive rebase).
  */
-static int run_git_commit(const char *defmsg, struct replay_opts *opts,
+int sequencer_commit(const char *defmsg, struct replay_opts *opts,
                          int allow_empty)
 {
+       char **env = NULL;
        struct argv_array array;
        int rc;
        const char *value;
 
+       if (IS_REBASE_I()) {
+               env = read_author_script();
+               if (!env)
+                       return error("You have staged changes in your working "
+                               "tree. If these changes are meant to be\n"
+                               "squashed into the previous commit, run:\n\n"
+                               "  git commit --amend $gpg_sign_opt_quoted\n\n"
+                               "If they are meant to go into a new commit, "
+                               "run:\n\n"
+                               "  git commit $gpg_sign_opt_quoted\n\n"
+                               "In both case, once you're done, continue "
+                               "with:\n\n"
+                               "  git rebase --continue\n");
+       }
+
        argv_array_init(&array);
        argv_array_push(&array, "commit");
        argv_array_push(&array, "-n");
@@ -399,14 +461,13 @@ static int run_git_commit(const char *defmsg, struct 
replay_opts *opts,
                argv_array_pushf(&array, "-S%s", opts->gpg_sign);
        if (opts->signoff)
                argv_array_push(&array, "-s");
-       if (!opts->edit) {
-               argv_array_push(&array, "-F");
-               argv_array_push(&array, defmsg);
-               if (!opts->signoff &&
-                   !opts->record_origin &&
-                   git_config_get_value("commit.cleanup", &value))
-                       argv_array_push(&array, "--cleanup=verbatim");
-       }
+       if (defmsg)
+               argv_array_pushl(&array, "-F", defmsg, NULL);
+       if (opts->edit)
+               argv_array_push(&array, "-e");
+       else if (!opts->signoff && !opts->record_origin &&
+                git_config_get_value("commit.cleanup", &value))
+               argv_array_push(&array, "--cleanup=verbatim");
 
        if (allow_empty)
                argv_array_push(&array, "--allow-empty");
@@ -414,8 +475,11 @@ static int run_git_commit(const char *defmsg, struct 
replay_opts *opts,
        if (opts->allow_empty_message)
                argv_array_push(&array, "--allow-empty-message");
 
-       rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+       rc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,
+                       (const char *const *)env);
        argv_array_clear(&array);
+       free(env);
+
        return rc;
 }
 
@@ -664,7 +728,8 @@ static int do_pick_commit(enum todo_command command, struct 
commit *commit,
                goto leave;
        }
        if (!opts->no_commit)
-               res = run_git_commit(git_path_merge_msg(), opts, allow);
+               res = sequencer_commit(opts->edit ? NULL : git_path_merge_msg(),
+                       opts, allow);
 
 leave:
        free_message(commit, &msg);
@@ -859,6 +924,9 @@ static int populate_opts_cb(const char *key, const char 
*value, void *data)
 
 static int read_populate_opts(struct replay_opts *opts)
 {
+       if (IS_REBASE_I())
+               return 0;
+
        if (!file_exists(git_path_opts_file()))
                return 0;
        if (git_config_from_file(populate_opts_cb, git_path_opts_file(), opts) 
< 0)
diff --git a/sequencer.h b/sequencer.h
index 674f11e..9f63c31 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -49,6 +49,9 @@ int sequencer_continue(struct replay_opts *opts);
 int sequencer_rollback(struct replay_opts *opts);
 int sequencer_remove_state(struct replay_opts *opts);
 
+int sequencer_commit(const char *defmsg, struct replay_opts *opts,
+                         int allow_empty);
+
 extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
-- 
2.10.0.rc1.114.g2bd6b38


Reply via email to