Merge branch 'jc/commit-unedited-template' into maint

When "git commit --template F" errors out because the user did not
touch the message, it claimed that it aborts due to "empty message",
which was utterly wrong.

By Junio C Hamano (4) and Adam Monsen (1)
* jc/commit-unedited-template:
  Documentation/git-commit: rephrase the "initial-ness" of templates
  git-commit.txt: clarify -t requires editing message
  commit: rephrase the error when user did not touch templated log message
  commit: do not trigger bogus "has templated message edited" check
  t7501: test the right kind of breakage
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 5cc84a1..68abfca 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -132,11 +132,14 @@
 
 -t <file>::
 --template=<file>::
-	Use the contents of the given file as the initial version
-	of the commit message. The editor is invoked and you can
-	make subsequent changes. If a message is specified using
-	the `-m` or `-F` options, this option has no effect. This
-	overrides the `commit.template` configuration variable.
+	When editing the commit message, start the editor with the
+	contents in the given file.  The `commit.template` configuration
+	variable is often used to give this option implicitly to the
+	command.  This mechanism can be used by projects that want to
+	guide participants with some hints on what to write in the message
+	in what order.  If the user exits the editor without editing the
+	message, the commit is aborted.  This has no effect when a message
+	is given by other means, e.g. with the `-m` or `-F` options.
 
 -s::
 --signoff::
diff --git a/builtin/commit.c b/builtin/commit.c
index 7808be3..b257ae8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -921,27 +921,10 @@
 	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)
+static int rest_is_empty(struct strbuf *sb, int start)
 {
-	struct strbuf tmpl = STRBUF_INIT;
+	int i, eol;
 	const char *nl;
-	int eol, i, start = 0;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	/* See if the template is just a prefix of the message. */
-	if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
-		stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-		if (start + tmpl.len <= sb->len &&
-		    memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
-			start += tmpl.len;
-	}
-	strbuf_release(&tmpl);
 
 	/* Check if the rest is just whitespace and Signed-of-by's. */
 	for (i = start; i < sb->len; i++) {
@@ -964,6 +947,40 @@
 	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;
+	char *start;
+
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+	start = (char *)skip_prefix(sb->buf, tmpl.buf);
+	if (!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;
@@ -1071,6 +1088,8 @@
 		die(_("Only one of -c/-C/-F/--fixup can be used."));
 	if (message.len && f > 0)
 		die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
+	if (f || message.len)
+		template_file = NULL;
 	if (edit_message)
 		use_message = edit_message;
 	if (amend && !use_message && !fixup_message)
@@ -1510,6 +1529,11 @@
 
 	if (cleanup_mode != CLEANUP_NONE)
 		stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+	if (template_untouched(&sb) && !allow_empty_message) {
+		rollback_index_files();
+		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+		exit(1);
+	}
 	if (message_is_empty(&sb) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 8bb3833..b20ca0e 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -30,10 +30,12 @@
 '
 
 test_expect_success '-m and -F do not mix' '
+	git checkout HEAD file && echo >>file && git add file &&
 	test_must_fail git commit -m foo -m bar -F file
 '
 
 test_expect_success '-m and -C do not mix' '
+	git checkout HEAD file && echo >>file && git add file &&
 	test_must_fail git commit -C HEAD -m illegal
 '
 
@@ -79,7 +81,19 @@
 	test_must_fail git commit -F msg -a
 '
 
+test_expect_success 'template "emptyness" check does not kick in with -F' '
+	git checkout HEAD file && echo >>file && git add file &&
+	git commit -t file -F file
+'
+
+test_expect_success 'template "emptyness" check' '
+	git checkout HEAD file && echo >>file && git add file &&
+	test_must_fail git commit -t file 2>err &&
+	test_i18ngrep "did not edit" err
+'
+
 test_expect_success 'setup: commit message from file' '
+	git checkout HEAD file && echo >>file && git add file &&
 	echo this is the commit message, coming from a file >msg &&
 	git commit -F msg -a
 '