sequencer: prepare for rebase -i's commit functionality

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. Also, in "interactive rebase" mode we want to skip reading the options in the state directory of the cherry-pick/revert commands. Finally, 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.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Oct 21, 2016 at 14:25 UTC b5a670452c742345918e12b1d002b3502fe1de96
1 file changed +89 -10
sequencer.c
+89 -10
@@ -27,6 +27,19 @@ static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
27 static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
28 static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
29
30 +/*
31 + * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
32 + * GIT_AUTHOR_DATE that will be used for the commit that is currently
33 + * being rebased.
34 + */
35 +static GIT_PATH_FUNC(rebase_path_author_script, "rebase-merge/author-script")
36 +
37 +/* We will introduce the 'interactive rebase' mode later */
38 +static inline int is_rebase_i(const struct replay_opts *opts)
39 +{
40 + return 0;
41 +}
42 +
43 static const char *get_dir(const struct replay_opts *opts)
44 {
45 return git_path_seq_dir();
@@ -369,20 +382,80 @@ static int is_index_unchanged(void)
382 return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
383 }
384
385 +/*
386 + * Read the author-script file into an environment block, ready for use in
387 + * run_command(), that can be free()d afterwards.
388 + */
389 +static char **read_author_script(void)
390 +{
391 + struct strbuf script = STRBUF_INIT;
392 + int i, count = 0;
393 + char *p, *p2, **env;
394 + size_t env_size;
395 +
396 + if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
397 + return NULL;
398 +
399 + for (p = script.buf; *p; p++)
400 + if (skip_prefix(p, "'\\\\''", (const char **)&p2))
401 + strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
402 + else if (*p == '\'')
403 + strbuf_splice(&script, p-- - script.buf, 1, "", 0);
404 + else if (*p == '\n') {
405 + *p = '\0';
406 + count++;
407 + }
408 +
409 + env_size = (count + 1) * sizeof(*env);
410 + strbuf_grow(&script, env_size);
411 + memmove(script.buf + env_size, script.buf, script.len);
412 + p = script.buf + env_size;
413 + env = (char **)strbuf_detach(&script, NULL);
414 +
415 + for (i = 0; i < count; i++) {
416 + env[i] = p;
417 + p += strlen(p) + 1;
418 + }
419 + env[count] = NULL;
420 +
421 + return env;
422 +}
423 +
424 /*
425 * If we are cherry-pick, and if the merge did not result in
426 * hand-editing, we will hit this commit and inherit the original
427 * author date and name.
428 + *
429 * If we are revert, or if our cherry-pick results in a hand merge,
430 * we had better say that the current user is responsible for that.
431 + *
432 + * An exception is when run_git_commit() is called during an
433 + * interactive rebase: in that case, we will want to retain the
434 + * author metadata.
435 */
436 static int run_git_commit(const char *defmsg, struct replay_opts *opts,
437 int allow_empty)
438 {
439 + char **env = NULL;
440 struct argv_array array;
441 int rc;
442 const char *value;
443
444 + if (is_rebase_i(opts)) {
445 + env = read_author_script();
446 + if (!env)
447 + return error("You have staged changes in your working "
448 + "tree. If these changes are meant to be\n"
449 + "squashed into the previous commit, run:\n\n"
450 + " git commit --amend $gpg_sign_opt_quoted\n\n"
451 + "If they are meant to go into a new commit, "
452 + "run:\n\n"
453 + " git commit $gpg_sign_opt_quoted\n\n"
454 + "In both cases, once you're done, continue "
455 + "with:\n\n"
456 + " git rebase --continue\n");
457 + }
458 +
459 argv_array_init(&array);
460 argv_array_push(&array, "commit");
461 argv_array_push(&array, "-n");
@@ -391,14 +464,13 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
464 argv_array_pushf(&array, "-S%s", opts->gpg_sign);
465 if (opts->signoff)
466 argv_array_push(&array, "-s");
394 - if (!opts->edit) {
395 - argv_array_push(&array, "-F");
396 - argv_array_push(&array, defmsg);
397 - if (!opts->signoff &&
398 - !opts->record_origin &&
399 - git_config_get_value("commit.cleanup", &value))
400 - argv_array_push(&array, "--cleanup=verbatim");
401 - }
467 + if (defmsg)
468 + argv_array_pushl(&array, "-F", defmsg, NULL);
469 + if (opts->edit)
470 + argv_array_push(&array, "-e");
471 + else if (!opts->signoff && !opts->record_origin &&
472 + git_config_get_value("commit.cleanup", &value))
473 + argv_array_push(&array, "--cleanup=verbatim");
474
475 if (allow_empty)
476 argv_array_push(&array, "--allow-empty");
@@ -406,8 +478,11 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
478 if (opts->allow_empty_message)
479 argv_array_push(&array, "--allow-empty-message");
480
409 - rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
481 + rc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,
482 + (const char *const *)env);
483 argv_array_clear(&array);
484 + free(env);
485 +
486 return rc;
487 }
488
@@ -657,7 +732,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
732 goto leave;
733 }
734 if (!opts->no_commit)
660 - res = run_git_commit(git_path_merge_msg(), opts, allow);
735 + res = run_git_commit(opts->edit ? NULL : git_path_merge_msg(),
736 + opts, allow);
737
738 leave:
739 free_message(commit, &msg);
@@ -879,6 +955,9 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
955
956 static int read_populate_opts(struct replay_opts *opts)
957 {
958 + if (is_rebase_i(opts))
959 + return 0;
960 +
961 if (!file_exists(git_path_opts_file()))
962 return 0;
963 /*