summaryrefslogtreecommitdiff
path: root/builtin/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/commit.c')
-rw-r--r--builtin/commit.c144
1 files changed, 115 insertions, 29 deletions
diff --git a/builtin/commit.c b/builtin/commit.c
index 9cfef6c6cc..5e2221c8e8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -526,10 +526,29 @@ static int sane_ident_split(struct ident_split *person)
return 1;
}
+static int parse_force_date(const char *in, char *out, int len)
+{
+ if (len < 1)
+ return -1;
+ *out++ = '@';
+ len--;
+
+ if (parse_date(in, out, len) < 0) {
+ int errors = 0;
+ unsigned long t = approxidate_careful(in, &errors);
+ if (errors)
+ return -1;
+ snprintf(out, len, "%lu", t);
+ }
+
+ return 0;
+}
+
static void determine_author_info(struct strbuf *author_ident)
{
char *name, *email, *date;
struct ident_split author;
+ char date_buf[64];
name = getenv("GIT_AUTHOR_NAME");
email = getenv("GIT_AUTHOR_EMAIL");
@@ -574,8 +593,12 @@ static void determine_author_info(struct strbuf *author_ident)
email = xstrndup(lb + 2, rb - (lb + 2));
}
- if (force_date)
- date = force_date;
+ if (force_date) {
+ if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
+ die(_("invalid date format: %s"), force_date);
+ date = date_buf;
+ }
+
strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
if (!split_ident_line(&author, author_ident->buf, author_ident->len) &&
sane_ident_split(&author)) {
@@ -585,13 +608,46 @@ static void determine_author_info(struct strbuf *author_ident)
}
}
-static char *cut_ident_timestamp_part(char *string)
+static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
+{
+ if (split_ident_line(id, buf->buf, buf->len) ||
+ !sane_ident_split(id))
+ die(_("Malformed ident string: '%s'"), buf->buf);
+}
+
+static int author_date_is_interesting(void)
{
- char *ket = strrchr(string, '>');
- if (!ket || ket[1] != ' ')
- die(_("Malformed ident string: '%s'"), string);
- *++ket = '\0';
- return ket;
+ return author_message || force_date;
+}
+
+static void adjust_comment_line_char(const struct strbuf *sb)
+{
+ char candidates[] = "#;@!$%^&|:";
+ char *candidate;
+ const char *p;
+
+ comment_line_char = candidates[0];
+ if (!memchr(sb->buf, comment_line_char, sb->len))
+ return;
+
+ p = sb->buf;
+ candidate = strchr(candidates, *p);
+ if (candidate)
+ *candidate = ' ';
+ for (p = sb->buf; *p; p++) {
+ if ((p[0] == '\n' || p[0] == '\r') && p[1]) {
+ candidate = strchr(candidates, p[1]);
+ if (candidate)
+ *candidate = ' ';
+ }
+ }
+
+ for (p = candidates; *p == ' '; p++)
+ ;
+ if (!*p)
+ die(_("unable to select a comment character that is not used\n"
+ "in the current commit message"));
+ comment_line_char = *p;
}
static int prepare_to_commit(const char *index_file, const char *prefix,
@@ -650,9 +706,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
} else if (use_message) {
char *buffer;
buffer = strstr(use_message_buffer, "\n\n");
- if (!use_editor && (!buffer || buffer[2] == '\0'))
- die(_("commit has empty message"));
- strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+ if (buffer)
+ strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
hook_arg1 = "commit";
hook_arg2 = use_message;
} else if (fixup_message) {
@@ -748,6 +803,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
die_errno(_("could not write commit template"));
+ if (auto_comment_line_char)
+ adjust_comment_line_char(&sb);
strbuf_release(&sb);
/* This checks if committer ident is explicitly given */
@@ -755,7 +812,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (use_editor && include_status) {
int ident_shown = 0;
int saved_color_setting;
- char *ai_tmp, *ci_tmp;
+ struct ident_split ci, ai;
+
if (whence != FROM_COMMIT) {
if (cleanup_mode == CLEANUP_SCISSORS)
wt_status_add_cut_line(s->fp);
@@ -795,32 +853,39 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
status_printf_ln(s, GIT_COLOR_NORMAL,
"%s", only_include_assumed);
- ai_tmp = cut_ident_timestamp_part(author_ident->buf);
- ci_tmp = cut_ident_timestamp_part(committer_ident.buf);
- if (strcmp(author_ident->buf, committer_ident.buf))
+ split_ident_or_die(&ai, author_ident);
+ split_ident_or_die(&ci, &committer_ident);
+
+ if (ident_cmp(&ai, &ci))
+ status_printf_ln(s, GIT_COLOR_NORMAL,
+ _("%s"
+ "Author: %.*s <%.*s>"),
+ ident_shown++ ? "" : "\n",
+ (int)(ai.name_end - ai.name_begin), ai.name_begin,
+ (int)(ai.mail_end - ai.mail_begin), ai.mail_begin);
+
+ if (author_date_is_interesting())
status_printf_ln(s, GIT_COLOR_NORMAL,
_("%s"
- "Author: %s"),
+ "Date: %s"),
ident_shown++ ? "" : "\n",
- author_ident->buf);
+ show_ident_date(&ai, DATE_NORMAL));
if (!committer_ident_sufficiently_given())
status_printf_ln(s, GIT_COLOR_NORMAL,
_("%s"
- "Committer: %s"),
+ "Committer: %.*s <%.*s>"),
ident_shown++ ? "" : "\n",
- committer_ident.buf);
+ (int)(ci.name_end - ci.name_begin), ci.name_begin,
+ (int)(ci.mail_end - ci.mail_begin), ci.mail_begin);
if (ident_shown)
- status_printf_ln(s, GIT_COLOR_NORMAL, "");
+ status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
saved_color_setting = s->use_color;
s->use_color = 0;
commitable = run_status(s->fp, index_file, prefix, 1, s);
s->use_color = saved_color_setting;
-
- *ai_tmp = ' ';
- *ci_tmp = ' ';
} else {
unsigned char sha1[20];
const char *parent = "HEAD";
@@ -833,8 +898,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (get_sha1(parent, sha1))
commitable = !!active_nr;
- else
- commitable = index_differs_from(parent, 0);
+ else {
+ /*
+ * Unless the user did explicitly request a submodule
+ * ignore mode by passing a command line option we do
+ * not ignore any changed submodule SHA-1s when
+ * comparing index and parent, no matter what is
+ * configured. Otherwise we won't commit any
+ * submodules which were manually staged, which would
+ * be really confusing.
+ */
+ int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ if (ignore_submodule_arg &&
+ !strcmp(ignore_submodule_arg, "all"))
+ diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
+ commitable = index_differs_from(parent, diff_flags);
+ }
}
strbuf_release(&committer_ident);
@@ -1356,6 +1435,13 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
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);
@@ -1672,6 +1758,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
? NULL
: current_head->object.sha1,
0, NULL);
+ if (!ref_lock) {
+ rollback_index_files();
+ die(_("cannot lock HEAD ref"));
+ }
nl = strchr(sb.buf, '\n');
if (nl)
@@ -1681,10 +1771,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
- if (!ref_lock) {
- rollback_index_files();
- die(_("cannot lock HEAD ref"));
- }
if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
rollback_index_files();
die(_("cannot update HEAD ref"));