git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 2/2] format-patch: warn if commit msg contains a patch delimiter

From
Matheus Tavares <matheus.bernardino@usp.br>
Date
Sep 7, 2022, 14:44 UTC
Message-ID
<a2c4514aa03657f3b1d822efe3dd630713287ee6.1662559356.git.matheus.bernardino@usp.br>
In-Reply-To
<cover.1662559356.git.matheus.bernardino@usp.br>

When applying a patch, `git am` looks for special delimiter strings (such as "---") to know where the message ends and the actual diff starts. If one of these strings appears in the commit message itself, `am` might get confused and fail to apply the patch properly. This has already caused inconveniences in the past [1][2]. To help avoid such problem, let's make `git format-patch` warn on commit messages containing one of the said strings.

[1]: https://lore.kernel.org/git/20210113085846-mutt-send-email-mst@kernel.org/ [2]: https://lore.kernel.org/git/16297305.cDA1TJNmNo@earendil/

Signed-off-by: Matheus Tavares <matheus.bernardino@usp.br>
---
 builtin/log.c           |  1 +
 log-tree.c              |  1 +
 mailinfo.c              |  4 ++--
 mailinfo.h              |  3 +++
 pretty.c                | 16 +++++++++++++++-
 pretty.h                |  3 ++-
 revision.h              |  3 ++-
 t/t4014-format-patch.sh | 26 ++++++++++++++++++++++++++
 8 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/builtin/log.c b/builtin/log.c
index 56e2d95e86..edc84abaef 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1973,6 +1973,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	rev.diffopt.flags.recursive = 1;
 	rev.diffopt.no_free = 1;
 	rev.subject_prefix = fmt_patch_subject_prefix;
+	rev.check_in_body_patch_breaks = 1;
 	memset(&s_r_opt, 0, sizeof(s_r_opt));
 	s_r_opt.def = "HEAD";
 	s_r_opt.revarg_opt = REVARG_COMMITTISH;
diff --git a/log-tree.c b/log-tree.c
index 3e8c70ddcf..25ed5452b1 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -766,6 +766,7 @@ void show_log(struct rev_info *opt)
 	ctx.after_subject = extra_headers;
 	ctx.preserve_subject = opt->preserve_subject;
 	ctx.encode_email_headers = opt->encode_email_headers;
+	ctx.check_in_body_patch_breaks = opt->check_in_body_patch_breaks;
 	ctx.reflog_info = opt->reflog_info;
 	ctx.fmt = opt->commit_format;
 	ctx.mailmap = opt->mailmap;
diff --git a/mailinfo.c b/mailinfo.c
index f0a690b6e8..d227397f1c 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -646,7 +646,7 @@ static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 	free(ret);
 }
 
-static inline int patchbreak(const char *buf, size_t len)
+int patchbreak(const char *buf, size_t len)
 {
 	/* Beginning of a "diff -" header? */
 	if (skip_prefix_mem(buf, len, "diff -", &buf, &len))
@@ -680,7 +680,7 @@ static inline int patchbreak(const char *buf, size_t len)
 	return 0;
 }
 
-static int is_scissors_line(const char *line, size_t len)
+int is_scissors_line(const char *line, size_t len)
 {
 	const char *c;
 	int scissors = 0, gap = 0;
diff --git a/mailinfo.h b/mailinfo.h
index f2ffd0349e..347eefe856 100644
--- a/mailinfo.h
+++ b/mailinfo.h
@@ -53,4 +53,7 @@ void setup_mailinfo(struct mailinfo *);
 int mailinfo(struct mailinfo *, const char *msg, const char *patch);
 void clear_mailinfo(struct mailinfo *);
 
+int patchbreak(const char *line, size_t len);
+int is_scissors_line(const char *line, size_t len);
+
 #endif /* MAILINFO_H */
diff --git a/pretty.c b/pretty.c
index 6d819103fb..913d974b3a 100644
--- a/pretty.c
+++ b/pretty.c
@@ -5,6 +5,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "string-list.h"
+#include "mailinfo.h"
 #include "mailmap.h"
 #include "log-tree.h"
 #include "notes.h"
@@ -2097,7 +2098,7 @@ void pp_remainder(struct pretty_print_context *pp,
 		  int indent)
 {
 	struct grep_opt *opt = pp->rev ? &pp->rev->grep_filter : NULL;
-	int first = 1;
+	int first = 1, found_delimiter = 0;
 
 	for (;;) {
 		const char *line = *msg_p;
@@ -2107,6 +2108,14 @@ void pp_remainder(struct pretty_print_context *pp,
 		if (!linelen)
 			break;
 
+		if (pp->check_in_body_patch_breaks &&
+		    (patchbreak(line, linelen) || is_scissors_line(line, linelen))) {
+			warning(_("commit message has a patch delimiter: '%.*s'"),
+				line[linelen - 1] == '\n' ? linelen - 1 : linelen,
+				line);
+			found_delimiter = 1;
+		}
+
 		if (is_blank_line(line, &linelen)) {
 			if (first)
 				continue;
@@ -2133,6 +2142,11 @@ void pp_remainder(struct pretty_print_context *pp,
 		}
 		strbuf_addch(sb, '\n');
 	}
+
+	if (found_delimiter) {
+		warning(_("git am might fail to apply this patch. "
+			  "Consider indenting the offending lines."));
+	}
 }
 
 void pretty_print_commit(struct pretty_print_context *pp,
diff --git a/pretty.h b/pretty.h
index f34e24c53a..12df2f4a39 100644
--- a/pretty.h
+++ b/pretty.h
@@ -49,7 +49,8 @@ struct pretty_print_context {
 	struct string_list *mailmap;
 	int color;
 	struct ident_split *from_ident;
-	unsigned encode_email_headers:1;
+	unsigned encode_email_headers:1,
+		 check_in_body_patch_breaks:1;
 	struct pretty_print_describe_status *describe_status;
 
 	/*
diff --git a/revision.h b/revision.h
index 61a9b1316b..f384ab716f 100644
--- a/revision.h
+++ b/revision.h
@@ -230,7 +230,8 @@ struct rev_info {
 			date_mode_explicit:1,
 			preserve_subject:1,
 			encode_email_headers:1,
-			include_header:1;
+			include_header:1,
+			check_in_body_patch_breaks:1;
 	unsigned int	disable_stdin:1;
 	/* --show-linear-break */
 	unsigned int	track_linear:1,
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index fbec8ad2ef..4bbf1156e9 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -2329,4 +2329,30 @@ test_expect_success 'interdiff: solo-patch' '
 	test_cmp expect actual
 '
 
+test_expect_success 'warn if commit message contains patch delimiter' '
+	>delim &&
+	git add delim &&
+	cat >msg <<-\EOF &&
+	title
+
+	---
+	EOF
+	git commit -F msg &&
+	git format-patch -1 2>stderr &&
+	grep "warning: commit message has a patch delimiter" stderr
+'
+
+test_expect_success 'warn if commit message contains scissors' '
+	>scissors &&
+	git add scissors &&
+	cat >msg <<-\EOF &&
+	title
+
+	-- >8 --
+	EOF
+	git commit -F msg &&
+	git format-patch -1 2>stderr &&
+	grep "warning: commit message has a patch delimiter" stderr
+'
+
 test_done
-- 
2.37.2
Previous: Matheus TavaresNext: Phillip Wood
Message 5 of 13 in “format-patch: warn if commit msg contains a patch delimiter”
  1. format-patch: warn if commit msg contains a patch delimiterMatheus Tavares, Sep 4, 2022
  2. Ævar Arnfjörð BjarmasonSep 5, 2022
  3. René ScharfeSep 5, 2022
  4. 0/2 format-patch: warn if commit msg contains a patch delimiterMatheus Tavares, Sep 7, 2022
  5. 2/2 format-patch: warn if commit msg contains a patch delimiterMatheus Tavares, Sep 7, 2022
  6. Phillip WoodSep 7, 2022
  7. Junio C HamanoSep 7, 2022
  8. Matheus TavaresSep 9, 2022
  9. Junio C HamanoSep 9, 2022
  10. 1/2 patchbreak(), is_scissors_line(): work with a buf/len pairMatheus Tavares, Sep 7, 2022
  11. Phillip WoodSep 7, 2022
  12. Eric SunshineSep 8, 2022
  13. René ScharfeSep 7, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.