Commit 67b1d019a8ad93201c86d98d96782b13ba28fbbe

Edward Thomson 2021-09-14T09:49:31

email: include renames by default `git format-patch` includes diffs with rename detection enabled by default when creating emails. Match this behavior.

diff --git a/include/git2/email.h b/include/git2/email.h
index 4b2d70e..b56be5d 100644
--- a/include/git2/email.h
+++ b/include/git2/email.h
@@ -32,6 +32,9 @@ typedef enum {
 	 * patch is for a single commit (1/1).
 	 */
 	GIT_EMAIL_CREATE_ALWAYS_NUMBER = (1u << 1),
+
+	/** Do not perform rename or similarity detection. */
+	GIT_EMAIL_CREATE_NO_RENAMES = (1u << 2),
 } git_email_create_flags_t;
 
 /**
@@ -46,6 +49,9 @@ typedef struct {
 	/** Options to use when creating diffs */
 	git_diff_options diff_opts;
 
+	/** Options for finding similarities within diffs */
+	git_diff_find_options diff_find_opts;
+
 	/**
 	 * The subject prefix, by default "PATCH".  If set to an empty
 	 * string ("") then only the patch numbers will be shown in the
@@ -65,14 +71,16 @@ typedef struct {
 } git_email_create_options;
 
 /*
- * By default, our options include binary diffs to match `git format-patch`.
+ * By default, our options include rename detection and binary
+ * diffs to match `git format-patch`.
  */
 #define GIT_EMAIL_CREATE_OPTIONS_VERSION 1
 #define GIT_EMAIL_CREATE_OPTIONS_INIT \
 { \
 	GIT_EMAIL_CREATE_OPTIONS_VERSION, \
 	GIT_EMAIL_CREATE_DEFAULT, \
-	{ GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 } \
+	{ GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 }, \
+	GIT_DIFF_FIND_OPTIONS_INIT \
 }
 
 /**
diff --git a/src/email.c b/src/email.c
index 8b6e133..8957d9a 100644
--- a/src/email.c
+++ b/src/email.c
@@ -258,9 +258,10 @@ int git_email_create_from_commit(
 	const git_email_create_options *given_opts)
 {
 	git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
-	const git_diff_options *diff_opts;
 	git_diff *diff = NULL;
 	git_repository *repo;
+	git_diff_options *diff_opts;
+	git_diff_find_options *find_opts;
 	const git_signature *author;
 	const char *summary, *body;
 	const git_oid *commit_id;
@@ -282,10 +283,15 @@ int git_email_create_from_commit(
 	body = git_commit_body(commit);
 	commit_id = git_commit_id(commit);
 	diff_opts = &opts.diff_opts;
+	find_opts = &opts.diff_find_opts;
 
 	if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
 		goto done;
 
+	if ((opts.flags & GIT_EMAIL_CREATE_NO_RENAMES) == 0 &&
+	    (error = git_diff_find_similar(diff, find_opts)) < 0)
+		goto done;
+
 	error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, &opts);
 
 done:
diff --git a/tests/email/create.c b/tests/email/create.c
index 8bbde20..ccf79c2 100644
--- a/tests/email/create.c
+++ b/tests/email/create.c
@@ -69,7 +69,7 @@ static void assert_subject_match(
 
 void test_email_create__commit(void)
 {
-	const char *email =
+	const char *expected =
 	"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
 	"From: Jacques Germishuys <jacquesg@striata.com>\n" \
 	"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
@@ -109,7 +109,120 @@ void test_email_create__commit(void)
 	"\n";
 
 	assert_email_match(
-		email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL);
+		expected, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL);
+}
+
+void test_email_create__rename(void)
+{
+	const char *expected =
+	"From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+	"From: Jacques Germishuys <jacquesg@striata.com>\n" \
+	"Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+	"Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+	"\n" \
+	"---\n" \
+	" file1.txt => file1.txt.renamed | 4 ++--\n" \
+	" 1 file changed, 2 insertions(+), 2 deletions(-)\n" \
+	"\n" \
+	"diff --git a/file1.txt b/file1.txt.renamed\n" \
+	"similarity index 86%\n" \
+	"rename from file1.txt\n" \
+	"rename to file1.txt.renamed\n" \
+	"index af8f41d..a97157a 100644\n" \
+	"--- a/file1.txt\n" \
+	"+++ b/file1.txt.renamed\n" \
+	"@@ -3,13 +3,13 @@ file1.txt\n" \
+	" _file1.txt_\n" \
+	" file1.txt\n" \
+	" file1.txt\n" \
+	"-file1.txt\n" \
+	"+file1.txt_renamed\n" \
+	" file1.txt\n" \
+	" \n" \
+	" \n" \
+	" file1.txt\n" \
+	" file1.txt\n" \
+	"-file1.txt\n" \
+	"+file1.txt_renamed\n" \
+	" file1.txt\n" \
+	" file1.txt\n" \
+	" _file1.txt_\n" \
+	"--\n" \
+	"libgit2 " LIBGIT2_VERSION "\n" \
+	"\n";
+
+	assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", NULL);
+}
+
+void test_email_create__rename_as_add_delete(void)
+{
+	const char *expected =
+	"From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+	"From: Jacques Germishuys <jacquesg@striata.com>\n" \
+	"Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+	"Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+	"\n" \
+	"---\n" \
+	" file1.txt         | 17 -----------------\n" \
+	" file1.txt.renamed | 17 +++++++++++++++++\n" \
+	" 2 files changed, 17 insertions(+), 17 deletions(-)\n" \
+	" delete mode 100644 file1.txt\n" \
+	" create mode 100644 file1.txt.renamed\n" \
+	"\n" \
+	"diff --git a/file1.txt b/file1.txt\n" \
+	"deleted file mode 100644\n" \
+	"index af8f41d..0000000\n" \
+	"--- a/file1.txt\n" \
+	"+++ /dev/null\n" \
+	"@@ -1,17 +0,0 @@\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-_file1.txt_\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-\n" \
+	"-\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-file1.txt\n" \
+	"-_file1.txt_\n" \
+	"-_file1.txt_\n" \
+	"-file1.txt\n" \
+	"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
+	"new file mode 100644\n" \
+	"index 0000000..a97157a\n" \
+	"--- /dev/null\n" \
+	"+++ b/file1.txt.renamed\n" \
+	"@@ -0,0 +1,17 @@\n" \
+	"+file1.txt\n" \
+	"+file1.txt\n" \
+	"+_file1.txt_\n" \
+	"+file1.txt\n" \
+	"+file1.txt\n" \
+	"+file1.txt_renamed\n" \
+	"+file1.txt\n" \
+	"+\n" \
+	"+\n" \
+	"+file1.txt\n" \
+	"+file1.txt\n" \
+	"+file1.txt_renamed\n" \
+	"+file1.txt\n" \
+	"+file1.txt\n" \
+	"+_file1.txt_\n" \
+	"+_file1.txt_\n" \
+	"+file1.txt\n" \
+	"--\n" \
+	"libgit2 " LIBGIT2_VERSION "\n" \
+	"\n";
+
+	git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+	opts.flags |= GIT_EMAIL_CREATE_NO_RENAMES;
+
+	assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts);
 }
 
 void test_email_create__binary(void)