Merge branch 'pw/sequencer-in-process-commit'
The sequencer infrastructure is shared across "git cherry-pick", "git rebase -i", etc., and has always spawned "git commit" when it needs to create a commit. It has been taught to do so internally, when able, by reusing the codepath "git commit" itself uses, which gives performance boost for a few tens of percents in some sample scenarios. * pw/sequencer-in-process-commit: sequencer: run 'prepare-commit-msg' hook t7505: add tests for cherry-pick and rebase -i/-p t7505: style fixes sequencer: assign only free()able strings to gpg_sign sequencer: improve config handling t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 sequencer: try to commit without forking 'git commit' sequencer: load commit related config sequencer: simplify adding Signed-off-by: trailer commit: move print_commit_summary() to libgit commit: move post-rewrite code to libgit Add a function to update HEAD after creating a commit commit: move empty message checks to libgit t3404: check intermediate squash messages
This commit is contained in:
291
builtin/commit.c
291
builtin/commit.c
@ -31,9 +31,7 @@
|
||||
#include "gpg-interface.h"
|
||||
#include "column.h"
|
||||
#include "sequencer.h"
|
||||
#include "notes-utils.h"
|
||||
#include "mailmap.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
static const char * const builtin_commit_usage[] = {
|
||||
N_("git commit [<options>] [--] <pathspec>..."),
|
||||
@ -45,31 +43,6 @@ static const char * const builtin_status_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char implicit_ident_advice_noconfig[] =
|
||||
N_("Your name and email address were configured automatically based\n"
|
||||
"on your username and hostname. Please check that they are accurate.\n"
|
||||
"You can suppress this message by setting them explicitly. Run the\n"
|
||||
"following command and follow the instructions in your editor to edit\n"
|
||||
"your configuration file:\n"
|
||||
"\n"
|
||||
" git config --global --edit\n"
|
||||
"\n"
|
||||
"After doing this, you may fix the identity used for this commit with:\n"
|
||||
"\n"
|
||||
" git commit --amend --reset-author\n");
|
||||
|
||||
static const char implicit_ident_advice_config[] =
|
||||
N_("Your name and email address were configured automatically based\n"
|
||||
"on your username and hostname. Please check that they are accurate.\n"
|
||||
"You can suppress this message by setting them explicitly:\n"
|
||||
"\n"
|
||||
" git config --global user.name \"Your Name\"\n"
|
||||
" git config --global user.email you@example.com\n"
|
||||
"\n"
|
||||
"After doing this, you may fix the identity used for this commit with:\n"
|
||||
"\n"
|
||||
" git commit --amend --reset-author\n");
|
||||
|
||||
static const char empty_amend_advice[] =
|
||||
N_("You asked to amend the most recent commit, but doing so would make\n"
|
||||
"it empty. You can repeat your command with --allow-empty, or you can\n"
|
||||
@ -93,8 +66,6 @@ N_("If you wish to skip this commit, use:\n"
|
||||
"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
|
||||
"the remaining commits.\n");
|
||||
|
||||
static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
|
||||
|
||||
static const char *use_message_buffer;
|
||||
static struct lock_file index_lock; /* real index */
|
||||
static struct lock_file false_lock; /* used only for partial commits */
|
||||
@ -128,12 +99,7 @@ static char *sign_commit;
|
||||
* if editor is used, and only the whitespaces if the message
|
||||
* is specified explicitly.
|
||||
*/
|
||||
static enum {
|
||||
CLEANUP_SPACE,
|
||||
CLEANUP_NONE,
|
||||
CLEANUP_SCISSORS,
|
||||
CLEANUP_ALL
|
||||
} cleanup_mode;
|
||||
static enum commit_msg_cleanup_mode cleanup_mode;
|
||||
static const char *cleanup_arg;
|
||||
|
||||
static enum commit_whence whence;
|
||||
@ -673,7 +639,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *hook_arg1 = NULL;
|
||||
const char *hook_arg2 = NULL;
|
||||
int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
|
||||
int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
|
||||
int old_display_comment_prefix;
|
||||
|
||||
/* This checks and barfs if author is badly specified */
|
||||
@ -814,7 +780,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
struct ident_split ci, ai;
|
||||
|
||||
if (whence != FROM_COMMIT) {
|
||||
if (cleanup_mode == CLEANUP_SCISSORS)
|
||||
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
|
||||
wt_status_add_cut_line(s->fp);
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL,
|
||||
whence == FROM_MERGE
|
||||
@ -834,14 +800,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
}
|
||||
|
||||
fprintf(s->fp, "\n");
|
||||
if (cleanup_mode == CLEANUP_ALL)
|
||||
if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
|
||||
status_printf(s, GIT_COLOR_NORMAL,
|
||||
_("Please enter the commit message for your changes."
|
||||
" Lines starting\nwith '%c' will be ignored, and an empty"
|
||||
" message aborts the commit.\n"), comment_line_char);
|
||||
else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
|
||||
else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
|
||||
whence == FROM_COMMIT)
|
||||
wt_status_add_cut_line(s->fp);
|
||||
else /* CLEANUP_SPACE, that is. */
|
||||
else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
|
||||
status_printf(s, GIT_COLOR_NORMAL,
|
||||
_("Please enter the commit message for your changes."
|
||||
" Lines starting\n"
|
||||
@ -986,65 +953,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rest_is_empty(struct strbuf *sb, int start)
|
||||
{
|
||||
int i, eol;
|
||||
const char *nl;
|
||||
|
||||
/* Check if the rest is just whitespace and Signed-off-by's. */
|
||||
for (i = start; i < sb->len; i++) {
|
||||
nl = memchr(sb->buf + i, '\n', sb->len - i);
|
||||
if (nl)
|
||||
eol = nl - sb->buf;
|
||||
else
|
||||
eol = sb->len;
|
||||
|
||||
if (strlen(sign_off_header) <= eol - i &&
|
||||
starts_with(sb->buf + i, sign_off_header)) {
|
||||
i = eol;
|
||||
continue;
|
||||
}
|
||||
while (i < eol)
|
||||
if (!isspace(sb->buf[i++]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out if the message in the strbuf contains only whitespace and
|
||||
* Signed-off-by lines.
|
||||
*/
|
||||
static int message_is_empty(struct strbuf *sb)
|
||||
{
|
||||
if (cleanup_mode == CLEANUP_NONE && sb->len)
|
||||
return 0;
|
||||
return rest_is_empty(sb, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* See if the user edited the message in the editor or left what
|
||||
* was in the template intact
|
||||
*/
|
||||
static int template_untouched(struct strbuf *sb)
|
||||
{
|
||||
struct strbuf tmpl = STRBUF_INIT;
|
||||
const char *start;
|
||||
|
||||
if (cleanup_mode == CLEANUP_NONE && sb->len)
|
||||
return 0;
|
||||
|
||||
if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
|
||||
return 0;
|
||||
|
||||
strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
|
||||
if (!skip_prefix(sb->buf, tmpl.buf, &start))
|
||||
start = sb->buf;
|
||||
strbuf_release(&tmpl);
|
||||
return rest_is_empty(sb, start - sb->buf);
|
||||
}
|
||||
|
||||
static const char *find_author_by_nickname(const char *name)
|
||||
{
|
||||
struct rev_info revs;
|
||||
@ -1229,15 +1137,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
||||
if (argc == 0 && (also || (only && !amend && !allow_empty)))
|
||||
die(_("No paths with --include/--only does not make sense."));
|
||||
if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
|
||||
cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
|
||||
cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
|
||||
COMMIT_MSG_CLEANUP_SPACE;
|
||||
else if (!strcmp(cleanup_arg, "verbatim"))
|
||||
cleanup_mode = CLEANUP_NONE;
|
||||
cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
|
||||
else if (!strcmp(cleanup_arg, "whitespace"))
|
||||
cleanup_mode = CLEANUP_SPACE;
|
||||
cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
|
||||
else if (!strcmp(cleanup_arg, "strip"))
|
||||
cleanup_mode = CLEANUP_ALL;
|
||||
cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
|
||||
else if (!strcmp(cleanup_arg, "scissors"))
|
||||
cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
|
||||
cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
|
||||
COMMIT_MSG_CLEANUP_SPACE;
|
||||
else
|
||||
die(_("Invalid cleanup mode %s"), cleanup_arg);
|
||||
|
||||
@ -1439,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *implicit_ident_advice(void)
|
||||
{
|
||||
char *user_config = expand_user_path("~/.gitconfig", 0);
|
||||
char *xdg_config = xdg_config_home("config");
|
||||
int config_exists = file_exists(user_config) || file_exists(xdg_config);
|
||||
|
||||
free(user_config);
|
||||
free(xdg_config);
|
||||
|
||||
if (config_exists)
|
||||
return _(implicit_ident_advice_config);
|
||||
else
|
||||
return _(implicit_ident_advice_noconfig);
|
||||
|
||||
}
|
||||
|
||||
static void print_summary(const char *prefix, const struct object_id *oid,
|
||||
int initial_commit)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct commit *commit;
|
||||
struct strbuf format = STRBUF_INIT;
|
||||
const char *head;
|
||||
struct pretty_print_context pctx = {0};
|
||||
struct strbuf author_ident = STRBUF_INIT;
|
||||
struct strbuf committer_ident = STRBUF_INIT;
|
||||
|
||||
commit = lookup_commit(oid);
|
||||
if (!commit)
|
||||
die(_("couldn't look up newly created commit"));
|
||||
if (parse_commit(commit))
|
||||
die(_("could not parse newly created commit"));
|
||||
|
||||
strbuf_addstr(&format, "format:%h] %s");
|
||||
|
||||
format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
|
||||
format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
|
||||
if (strbuf_cmp(&author_ident, &committer_ident)) {
|
||||
strbuf_addstr(&format, "\n Author: ");
|
||||
strbuf_addbuf_percentquote(&format, &author_ident);
|
||||
}
|
||||
if (author_date_is_interesting()) {
|
||||
struct strbuf date = STRBUF_INIT;
|
||||
format_commit_message(commit, "%ad", &date, &pctx);
|
||||
strbuf_addstr(&format, "\n Date: ");
|
||||
strbuf_addbuf_percentquote(&format, &date);
|
||||
strbuf_release(&date);
|
||||
}
|
||||
if (!committer_ident_sufficiently_given()) {
|
||||
strbuf_addstr(&format, "\n Committer: ");
|
||||
strbuf_addbuf_percentquote(&format, &committer_ident);
|
||||
if (advice_implicit_identity) {
|
||||
strbuf_addch(&format, '\n');
|
||||
strbuf_addstr(&format, implicit_ident_advice());
|
||||
}
|
||||
}
|
||||
strbuf_release(&author_ident);
|
||||
strbuf_release(&committer_ident);
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
|
||||
rev.diff = 1;
|
||||
rev.diffopt.output_format =
|
||||
DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
|
||||
|
||||
rev.verbose_header = 1;
|
||||
rev.show_root_diff = 1;
|
||||
get_commit_format(format.buf, &rev);
|
||||
rev.always_show_header = 0;
|
||||
rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
|
||||
rev.diffopt.break_opt = 0;
|
||||
diff_setup_done(&rev.diffopt);
|
||||
|
||||
head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
|
||||
if (!head)
|
||||
die_errno(_("unable to resolve HEAD after creating commit"));
|
||||
if (!strcmp(head, "HEAD"))
|
||||
head = _("detached HEAD");
|
||||
else
|
||||
skip_prefix(head, "refs/heads/", &head);
|
||||
printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
|
||||
|
||||
if (!log_tree_commit(&rev, commit)) {
|
||||
rev.always_show_header = 1;
|
||||
rev.use_terminator = 1;
|
||||
log_tree_commit(&rev, commit);
|
||||
}
|
||||
|
||||
strbuf_release(&format);
|
||||
}
|
||||
|
||||
static int git_commit_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
struct wt_status *s = cb;
|
||||
@ -1560,37 +1378,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
|
||||
return git_status_config(k, v, s);
|
||||
}
|
||||
|
||||
static int run_rewrite_hook(const struct object_id *oldoid,
|
||||
const struct object_id *newoid)
|
||||
{
|
||||
struct child_process proc = CHILD_PROCESS_INIT;
|
||||
const char *argv[3];
|
||||
int code;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
argv[0] = find_hook("post-rewrite");
|
||||
if (!argv[0])
|
||||
return 0;
|
||||
|
||||
argv[1] = "amend";
|
||||
argv[2] = NULL;
|
||||
|
||||
proc.argv = argv;
|
||||
proc.in = -1;
|
||||
proc.stdout_to_stderr = 1;
|
||||
|
||||
code = start_command(&proc);
|
||||
if (code)
|
||||
return code;
|
||||
strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
write_in_full(proc.in, sb.buf, sb.len);
|
||||
close(proc.in);
|
||||
strbuf_release(&sb);
|
||||
sigchain_pop(SIGPIPE);
|
||||
return finish_command(&proc);
|
||||
}
|
||||
|
||||
int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
|
||||
{
|
||||
struct argv_array hook_env = ARGV_ARRAY_INIT;
|
||||
@ -1673,13 +1460,11 @@ 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;
|
||||
struct object_id oid;
|
||||
struct commit_list *parents = NULL;
|
||||
struct stat statbuf;
|
||||
struct commit *current_head = NULL;
|
||||
struct commit_extra_header *extra = NULL;
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
if (argc == 2 && !strcmp(argv[1], "-h"))
|
||||
@ -1770,17 +1555,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (verbose || /* Truncate the message just before the diff, if any. */
|
||||
cleanup_mode == CLEANUP_SCISSORS)
|
||||
cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
|
||||
strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
|
||||
if (cleanup_mode != CLEANUP_NONE)
|
||||
strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
|
||||
if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
|
||||
strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
|
||||
|
||||
if (message_is_empty(&sb) && !allow_empty_message) {
|
||||
if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
|
||||
rollback_index_files();
|
||||
fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
|
||||
exit(1);
|
||||
}
|
||||
if (template_untouched(&sb) && !allow_empty_message) {
|
||||
if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
|
||||
rollback_index_files();
|
||||
fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
|
||||
exit(1);
|
||||
@ -1802,25 +1587,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
strbuf_release(&author_ident);
|
||||
free_commit_extra_headers(extra);
|
||||
|
||||
nl = strchr(sb.buf, '\n');
|
||||
if (nl)
|
||||
strbuf_setlen(&sb, nl + 1 - sb.buf);
|
||||
else
|
||||
strbuf_addch(&sb, '\n');
|
||||
strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
|
||||
strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
|
||||
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, "HEAD", &oid,
|
||||
current_head
|
||||
? ¤t_head->object.oid : &null_oid,
|
||||
0, sb.buf, &err) ||
|
||||
ref_transaction_commit(transaction, &err)) {
|
||||
if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
|
||||
&err)) {
|
||||
rollback_index_files();
|
||||
die("%s", err.buf);
|
||||
}
|
||||
ref_transaction_free(transaction);
|
||||
|
||||
unlink(git_path_cherry_pick_head());
|
||||
unlink(git_path_revert_head());
|
||||
@ -1837,17 +1608,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
rerere(0);
|
||||
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
|
||||
if (amend && !no_post_rewrite) {
|
||||
struct notes_rewrite_cfg *cfg;
|
||||
cfg = init_copy_notes_for_rewrite("amend");
|
||||
if (cfg) {
|
||||
/* we are amending, so current_head is not NULL */
|
||||
copy_note_for_rewrite(cfg, ¤t_head->object.oid, &oid);
|
||||
finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
|
||||
}
|
||||
run_rewrite_hook(¤t_head->object.oid, &oid);
|
||||
commit_post_rewrite(current_head, &oid);
|
||||
}
|
||||
if (!quiet) {
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (!current_head)
|
||||
flags |= SUMMARY_INITIAL_COMMIT;
|
||||
if (author_date_is_interesting())
|
||||
flags |= SUMMARY_SHOW_AUTHOR_DATE;
|
||||
print_commit_summary(prefix, &oid, flags);
|
||||
}
|
||||
if (!quiet)
|
||||
print_summary(prefix, &oid, !current_head);
|
||||
|
||||
UNLEAK(err);
|
||||
UNLEAK(sb);
|
||||
|
Reference in New Issue
Block a user