summaryrefslogtreecommitdiff
path: root/builtin/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/commit.c')
-rw-r--r--builtin/commit.c155
1 files changed, 83 insertions, 72 deletions
diff --git a/builtin/commit.c b/builtin/commit.c
index fa5982c..646e845 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -60,15 +60,18 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
"\n");
static const char empty_cherry_pick_advice_single[] =
-N_("Otherwise, please use 'git reset'\n");
+N_("Otherwise, please use 'git cherry-pick --skip'\n");
static const char empty_cherry_pick_advice_multi[] =
-N_("If you wish to skip this commit, use:\n"
+N_("and then use:\n"
"\n"
-" git reset\n"
+" git cherry-pick --continue\n"
"\n"
-"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
-"the remaining commits.\n");
+"to resume cherry-picking the remaining commits.\n"
+"If you wish to skip this commit, use:\n"
+"\n"
+" git cherry-pick --skip\n"
+"\n");
static const char *color_status_slots[] = {
[WT_STATUS_HEADER] = "header",
@@ -104,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
-static int no_post_rewrite, allow_empty_message;
+static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit;
+static char *sign_commit, *pathspec_from_file;
/*
* The default commit message cleanup mode will remove the lines
@@ -235,7 +238,7 @@ static int commit_index_files(void)
* and return the paths that match the given pattern in list.
*/
static int list_paths(struct string_list *list, const char *with_tree,
- const char *prefix, const struct pathspec *pattern)
+ const struct pathspec *pattern)
{
int i, ret;
char *m;
@@ -264,7 +267,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
item->util = item; /* better a valid pointer than a fake one */
}
- ret = report_path_error(m, pattern, prefix);
+ ret = report_path_error(m, pattern);
free(m);
return ret;
}
@@ -340,6 +343,26 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
PATHSPEC_PREFER_FULL,
prefix, argv);
+ if (pathspec_from_file) {
+ if (interactive)
+ die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+
+ if (all)
+ die(_("--pathspec-from-file with -a does not make sense"));
+
+ if (pathspec.nr)
+ die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+ parse_pathspec_file(&pathspec, 0,
+ PATHSPEC_PREFER_FULL,
+ prefix, pathspec_from_file, pathspec_file_nul);
+ } else if (pathspec_file_nul) {
+ die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ }
+
+ if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
+ die(_("No paths with --include/--only does not make sense."));
+
if (read_cache_preload(&pathspec) < 0)
die(_("index file corrupt"));
@@ -454,7 +477,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
die(_("cannot do a partial commit during a cherry-pick."));
}
- if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
+ if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
exit(1);
discard_cache();
@@ -507,7 +530,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->nowarn = nowarn;
s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
if (!s->is_initial)
- hashcpy(s->sha1_commit, oid.hash);
+ oidcpy(&s->oid_commit, &oid);
s->status_format = status_format;
s->ignore_submodule_arg = ignore_submodule_arg;
@@ -534,7 +557,7 @@ static void export_one(const char *var, const char *s, const char *e, int hack)
struct strbuf buf = STRBUF_INIT;
if (hack)
strbuf_addch(&buf, hack);
- strbuf_addf(&buf, "%.*s", (int)(e - s), s);
+ strbuf_add(&buf, s, e - s);
setenv(var, buf.buf, 1);
strbuf_release(&buf);
}
@@ -668,6 +691,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
const char *hook_arg2 = NULL;
int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
int old_display_comment_prefix;
+ int merge_contains_scissors = 0;
/* This checks and barfs if author is badly specified */
determine_author_info(author_ident);
@@ -728,6 +752,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
strbuf_addbuf(&sb, &message);
hook_arg1 = "message";
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
+ size_t merge_msg_start;
+
/*
* prepend SQUASH_MSG here if it exists and a
* "merge --squash" was originally performed
@@ -738,8 +764,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
hook_arg1 = "squash";
} else
hook_arg1 = "merge";
+
+ merge_msg_start = sb.len;
if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
die_errno(_("could not read MERGE_MSG"));
+
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+ wt_status_locate_end(sb.buf + merge_msg_start,
+ sb.len - merge_msg_start) <
+ sb.len - merge_msg_start)
+ merge_contains_scissors = 1;
} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
die_errno(_("could not read SQUASH_MSG"));
@@ -807,7 +841,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct ident_split ci, ai;
if (whence != FROM_COMMIT) {
- if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+ !merge_contains_scissors)
wt_status_add_cut_line(s->fp);
status_printf_ln(s, GIT_COLOR_NORMAL,
whence == FROM_MERGE
@@ -832,10 +867,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
_("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 == COMMIT_MSG_CLEANUP_SCISSORS &&
- whence == FROM_COMMIT)
- wt_status_add_cut_line(s->fp);
- else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
+ else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+ if (whence == FROM_COMMIT && !merge_contains_scissors)
+ wt_status_add_cut_line(s->fp);
+ } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
status_printf(s, GIT_COLOR_NORMAL,
_("Please enter the commit message for your changes."
" Lines starting\n"
@@ -929,6 +964,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
*/
if (!committable && whence != FROM_MERGE && !allow_empty &&
!(amend && is_a_merge(current_head))) {
+ s->hints = advice_status_hints;
s->display_comment_prefix = old_display_comment_prefix;
run_status(stdout, index_file, prefix, 0, s);
if (amend)
@@ -1066,9 +1102,11 @@ static const char *read_commit_message(const char *name)
static struct status_deferred_config {
enum wt_status_format status_format;
int show_branch;
+ enum ahead_behind_flags ahead_behind;
} status_deferred_config = {
STATUS_FORMAT_UNSPECIFIED,
- -1 /* unspecified */
+ -1, /* unspecified */
+ AHEAD_BEHIND_UNSPECIFIED,
};
static void finalize_deferred_config(struct wt_status *s)
@@ -1095,6 +1133,17 @@ static void finalize_deferred_config(struct wt_status *s)
if (s->show_branch < 0)
s->show_branch = 0;
+ /*
+ * If the user did not give a "--[no]-ahead-behind" command
+ * line argument *AND* we will print in a human-readable format
+ * (short, long etc.) then we inherit from the status.aheadbehind
+ * config setting. In all other cases (and porcelain V[12] formats
+ * in particular), we inherit _FULL for backwards compatibility.
+ */
+ if (use_deferred_config &&
+ s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
+ s->ahead_behind_flags = status_deferred_config.ahead_behind;
+
if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
s->ahead_behind_flags = AHEAD_BEHIND_FULL;
}
@@ -1170,31 +1219,13 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (also + only + all + interactive > 1)
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
- 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 ? COMMIT_MSG_CLEANUP_ALL :
- COMMIT_MSG_CLEANUP_SPACE;
- else if (!strcmp(cleanup_arg, "verbatim"))
- cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
- else if (!strcmp(cleanup_arg, "whitespace"))
- cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
- else if (!strcmp(cleanup_arg, "strip"))
- cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
- else if (!strcmp(cleanup_arg, "scissors"))
- cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
- COMMIT_MSG_CLEANUP_SPACE;
- /*
- * Please update _git_commit() in git-completion.bash when you
- * add new options.
- */
- else
- die(_("Invalid cleanup mode %s"), cleanup_arg);
+ cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
handle_untracked_files_arg(s);
if (all && argc > 0)
- die(_("Paths with -a does not make sense."));
+ die(_("paths '%s ...' with -a does not make sense"),
+ argv[0]);
if (status_format != STATUS_FORMAT_NONE)
dry_run = 1;
@@ -1250,6 +1281,10 @@ static int git_status_config(const char *k, const char *v, void *cb)
status_deferred_config.show_branch = git_config_bool(k, v);
return 0;
}
+ if (!strcmp(k, "status.aheadbehind")) {
+ status_deferred_config.ahead_behind = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(k, "status.showstash")) {
s->show_stash = git_config_bool(k, v);
return 0;
@@ -1390,7 +1425,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
if (!s.is_initial)
- hashcpy(s.sha1_commit, oid.hash);
+ oidcpy(&s.oid_commit, &oid);
s.ignore_submodule_arg = ignore_submodule_arg;
s.status_format = status_format;
@@ -1447,28 +1482,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
return git_status_config(k, v, s);
}
-int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
-{
- struct argv_array hook_env = ARGV_ARRAY_INIT;
- va_list args;
- int ret;
-
- argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
-
- /*
- * Let the hook know that no editor will be launched.
- */
- if (!editor_is_used)
- argv_array_push(&hook_env, "GIT_EDITOR=:");
-
- va_start(args, name);
- ret = run_hook_ve(hook_env.argv,name, args);
- va_end(args);
- argv_array_clear(&hook_env);
-
- return ret;
-}
-
int cmd_commit(int argc, const char **argv, const char *prefix)
{
const char *argv_gc_auto[] = {"gc", "--auto", NULL};
@@ -1490,7 +1503,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
- OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
+ OPT_CLEANUP(&cleanup_arg),
OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
@@ -1519,6 +1532,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
/* end commit contents options */
OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
@@ -1626,11 +1641,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die(_("could not read commit message: %s"), strerror(saved_errno));
}
- if (verbose || /* Truncate the message just before the diff, if any. */
- cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
- strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
- if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
- strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+ cleanup_message(&sb, cleanup_mode, verbose);
if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
rollback_index_files();
@@ -1666,8 +1677,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die("%s", err.buf);
}
- unlink(git_path_cherry_pick_head(the_repository));
- unlink(git_path_revert_head(the_repository));
+ sequencer_post_commit_cleanup(the_repository, 0);
unlink(git_path_merge_head(the_repository));
unlink(git_path_merge_msg(the_repository));
unlink(git_path_merge_mode(the_repository));
@@ -1678,8 +1688,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"new_index file. Check that disk is not full and quota is\n"
"not exceeded, and then \"git restore --staged :/\" to recover."));
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
- write_commit_graph_reachable(get_object_directory(), 0, 0);
+ if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+ write_commit_graph_reachable(get_object_directory(), 0, NULL))
+ return 1;
repo_rerere(the_repository, 0);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);