NULL
};
-static unsigned char head_sha1[20], merge_head_sha1[20];
+static unsigned char head_sha1[20];
static char *use_message_buffer;
static const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, no_verify, allow_empty, dry_run;
-static char *untracked_files_arg;
+static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static char *untracked_files_arg, *force_date;
/*
* The default commit message cleanup mode will remove the lines
* beginning with # (shell comments) and leading and trailing
} cleanup_mode;
static char *cleanup_arg;
-static int use_editor = 1, initial_commit, in_merge;
+static int use_editor = 1, initial_commit, in_merge, include_status = 1;
static const char *only_include_assumed;
static struct strbuf message;
static struct option builtin_commit_options[] = {
OPT__QUIET(&quiet),
OPT__VERBOSE(&verbose),
- OPT_GROUP("Commit message options"),
+ OPT_GROUP("Commit message options"),
OPT_FILENAME('F', "file", &logfile, "read log from file"),
OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
+ OPT_STRING(0, "date", &force_date, "DATE", "override date for commit"),
OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
- OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
+ OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
+ OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+ OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+ OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+ /* end commit message options */
OPT_GROUP("Commit contents options"),
OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
- OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+ /* end commit contents options */
OPT_END()
};
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
+ struct string_list_item *item;
+
if (ce->ce_flags & CE_UPDATE)
continue;
if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
continue;
- string_list_insert(ce->name, list);
+ item = string_list_insert(ce->name, list);
+ if (ce_skip_worktree(ce))
+ item->util = item; /* better a valid pointer than a fake one */
}
return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
struct stat st;
struct string_list_item *p = &(list->items[i]);
+ /* p->util is skip-worktree */
+ if (p->util)
+ continue;
+
if (!lstat(p->string, &st)) {
if (add_to_cache(p->string, &st, 0))
die("updating files failed");
*/
commit_style = COMMIT_PARTIAL;
- if (file_exists(git_path("MERGE_HEAD")))
+ if (in_merge)
die("cannot do a partial commit during a merge.");
memset(&partial, 0, sizeof(partial));
email = getenv("GIT_AUTHOR_EMAIL");
date = getenv("GIT_AUTHOR_DATE");
- if (use_message) {
+ if (use_message && !renew_authorship) {
const char *a, *lb, *rb, *eol;
a = strstr(use_message_buffer, "\nauthor ");
email = xstrndup(lb + 2, rb - (lb + 2));
}
+ if (force_date)
+ date = force_date;
+
author_name = name;
author_email = email;
author_date = date;
}
+static int ends_rfc2822_footer(struct strbuf *sb)
+{
+ int ch;
+ int hit = 0;
+ int i, j, k;
+ int len = sb->len;
+ int first = 1;
+ const char *buf = sb->buf;
+
+ for (i = len - 1; i > 0; i--) {
+ if (hit && buf[i] == '\n')
+ break;
+ hit = (buf[i] == '\n');
+ }
+
+ while (i < len - 1 && buf[i] == '\n')
+ i++;
+
+ for (; i < len; i = k) {
+ for (k = i; k < len && buf[k] != '\n'; k++)
+ ; /* do nothing */
+ k++;
+
+ if ((buf[k] == ' ' || buf[k] == '\t') && !first)
+ continue;
+
+ first = 0;
+
+ for (j = 0; i + j < len; j++) {
+ ch = buf[i + j];
+ if (ch == ':')
+ break;
+ if (isalnum(ch) ||
+ (ch == '-'))
+ continue;
+ return 0;
+ }
+ }
+ return 1;
+}
+
static int prepare_to_commit(const char *index_file, const char *prefix,
struct wt_status *s)
{
for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
; /* do nothing */
if (prefixcmp(sb.buf + i, sob.buf)) {
- if (prefixcmp(sb.buf + i, sign_off_header))
+ if (!i || !ends_rfc2822_footer(&sb))
strbuf_addch(&sb, '\n');
strbuf_addbuf(&sb, &sob);
}
/* This checks if committer ident is explicitly given */
git_committer_info(0);
- if (use_editor) {
+ if (use_editor && include_status) {
char *author_ident;
const char *committer_ident;
prepare_revision_walk(&revs);
commit = get_revision(&revs);
if (commit) {
+ struct pretty_print_context ctx = {0};
+ ctx.date_mode = DATE_NORMAL;
strbuf_release(&buf);
- format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
+ format_commit_message(commit, "%an <%ae>", &buf, &ctx);
return strbuf_detach(&buf, NULL);
}
die("No existing author found with '%s'", name);
if (force_author && !strchr(force_author, '>'))
force_author = find_author_by_nickname(force_author);
+ if (force_author && renew_authorship)
+ die("Using both --reset-author and --author does not make sense");
+
if (logfile || message.len || use_message)
use_editor = 0;
if (edit_flag)
if (get_sha1("HEAD", head_sha1))
initial_commit = 1;
- if (!get_sha1("MERGE_HEAD", merge_head_sha1))
- in_merge = 1;
-
/* Sanity check options */
if (amend && initial_commit)
die("You have nothing to amend.");
use_message = edit_message;
if (amend && !use_message)
use_message = "HEAD";
+ if (!use_message && renew_authorship)
+ die("--reset-author can be used only with -C, -c or --amend.");
if (use_message) {
unsigned char sha1[20];
static char utf8[] = "UTF-8";
return WT_STATUS_NOBRANCH;
if (!strcasecmp(var+offset, "unmerged"))
return WT_STATUS_UNMERGED;
- die("bad config variable '%s'", var);
+ return -1;
}
static int git_status_config(const char *k, const char *v, void *cb)
}
if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
int slot = parse_status_slot(k, 13);
+ if (slot < 0)
+ return 0;
if (!v)
return config_error_nonbool(k);
color_parse(v, k, s->color_palette[slot]);
wt_status_prepare(&s);
git_config(git_status_config, &s);
+ in_merge = file_exists(git_path("MERGE_HEAD"));
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
read_cache();
refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
+ s.in_merge = in_merge;
wt_status_collect(&s);
if (s.relative_paths)
initial_commit ? " (root-commit)" : "");
if (!log_tree_commit(&rev, commit)) {
+ struct pretty_print_context ctx = {0};
struct strbuf buf = STRBUF_INIT;
- format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
+ ctx.date_mode = DATE_NORMAL;
+ format_commit_message(commit, format + 7, &buf, &ctx);
printf("%s\n", buf.buf);
strbuf_release(&buf);
}
struct wt_status *s = cb;
if (!strcmp(k, "commit.template"))
- return git_config_string(&template_file, k, v);
+ return git_config_pathname(&template_file, k, v);
+ if (!strcmp(k, "commit.status")) {
+ include_status = git_config_bool(k, v);
+ return 0;
+ }
return git_status_config(k, v, s);
}
wt_status_prepare(&s);
git_config(git_commit_config, &s);
+ in_merge = file_exists(git_path("MERGE_HEAD"));
+ s.in_merge = in_merge;
if (s.use_color == -1)
s.use_color = git_use_color_default;
-
argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
prefix, &s);
if (dry_run) {