summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <vicent@github.com>2014-04-16 19:09:35 +0200
committerVicent Marti <vicent@github.com>2014-04-16 19:09:35 +0200
commitc5cacc4ec2c13e93164b8e3013b0d18c8c6d820c (patch)
tree19ee82e4327f0d30a4e75bc5d7997430fbb2becf
parent3b2d14a7b83e0db272c84046f90b53b7b773180e (diff)
parent39206ca256409edb7102edf0d30a6ced2e5f16e3 (diff)
downloadlibgit2-c5cacc4ec2c13e93164b8e3013b0d18c8c6d820c.tar.gz
Merge pull request #2261 from jacquesg/format-patch
Support for format-patch
-rw-r--r--include/git2/diff.h173
-rw-r--r--include/git2/patch.h6
-rw-r--r--src/buffer.c9
-rw-r--r--src/buffer.h1
-rw-r--r--src/date.c28
-rw-r--r--src/diff.c222
-rw-r--r--src/diff.h3
-rw-r--r--src/diff_patch.c6
-rw-r--r--src/diff_stats.c343
-rw-r--r--src/util.h12
-rw-r--r--tests/date/rfc2822.c40
-rw-r--r--tests/diff/format_email.c556
-rw-r--r--tests/diff/stats.c428
-rw-r--r--tests/resources/diff_format_email/.gitted/HEAD1
-rw-r--r--tests/resources/diff_format_email/.gitted/config7
-rw-r--r--tests/resources/diff_format_email/.gitted/indexbin0 -> 256 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/info/exclude6
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664ccebin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508bin0 -> 176 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8bin0 -> 34 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06abbin0 -> 155 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235ebin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5bin0 -> 162 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20bin0 -> 31 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6bin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234bin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8abin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264bin0 -> 155 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3bin0 -> 162 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74dbin0 -> 171 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8ebin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6bin0 -> 120 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257dfbin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882dbin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4bin0 -> 166 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fabin0 -> 40 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169cbin0 -> 179 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4fbin0 -> 173 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1ebin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261bin0 -> 123 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8dbin0 -> 41 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaabin0 -> 123 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5bin0 -> 35 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b0674302
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4cbin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551fbin0 -> 167 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd931
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86bin0 -> 38 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d2312
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a91893
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7bin0 -> 26 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6bin0 -> 179 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373bin0 -> 181 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c1
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f91
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76bin0 -> 120 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92bin0 -> 181 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31abin0 -> 24 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03bin0 -> 62 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911bin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44bin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63bin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4bin0 -> 20 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4bbin0 -> 22 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b1
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1fbin0 -> 40 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961bin0 -> 62 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13bin0 -> 50 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235bin0 -> 37 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1bin0 -> 18 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ffbin0 -> 35 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df3
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2bin0 -> 180 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8bin0 -> 163 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8abin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e1
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297abin0 -> 156 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2ccbin0 -> 175 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0bin0 -> 51 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1bin0 -> 24 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c22
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a31
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e0851
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514bin0 -> 176 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcbbin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/binary1
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/master1
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/multihunk1
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/rename1
-rwxr-xr-xtests/resources/diff_format_email/file1.txt.renamed17
-rw-r--r--tests/resources/diff_format_email/file2.txt5
-rw-r--r--tests/resources/diff_format_email/file3.txt5
102 files changed, 1893 insertions, 6 deletions
diff --git a/include/git2/diff.h b/include/git2/diff.h
index f855f52ba..a0cfbc918 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -1072,6 +1072,179 @@ GIT_EXTERN(int) git_diff_buffers(
git_diff_line_cb line_cb,
void *payload);
+/**
+ * This is an opaque structure which is allocated by `git_diff_get_stats`.
+ * You are responsible for releasing the object memory when done, using the
+ * `git_diff_stats_free()` function.
+ */
+typedef struct git_diff_stats git_diff_stats;
+
+/**
+ * Formatting options for diff stats
+ */
+typedef enum {
+ /** No stats*/
+ GIT_DIFF_STATS_NONE = 0,
+
+ /** Full statistics, equivalent of `--stat` */
+ GIT_DIFF_STATS_FULL = (1u << 0),
+
+ /** Short statistics, equivalent of `--shortstat` */
+ GIT_DIFF_STATS_SHORT = (1u << 1),
+
+ /** Number statistics, equivalent of `--numstat` */
+ GIT_DIFF_STATS_NUMBER = (1u << 2),
+
+ /** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */
+ GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3),
+} git_diff_stats_format_t;
+
+/**
+ * Accumlate diff statistics for all patches.
+ *
+ * @param out Structure containg the diff statistics.
+ * @param diff A git_diff generated by one of the above functions.
+ * @return 0 on success; non-zero on error
+ */
+GIT_EXTERN(int) git_diff_get_stats(
+ git_diff_stats **out,
+ git_diff *diff);
+
+/**
+ * Get the total number of files changed in a diff
+ *
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @return total number of files changed in the diff
+ */
+GIT_EXTERN(size_t) git_diff_stats_files_changed(
+ const git_diff_stats *stats);
+
+/**
+ * Get the total number of insertions in a diff
+ *
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @return total number of insertions in the diff
+ */
+GIT_EXTERN(size_t) git_diff_stats_insertions(
+ const git_diff_stats *stats);
+
+/**
+ * Get the total number of deletions in a diff
+ *
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @return total number of deletions in the diff
+ */
+GIT_EXTERN(size_t) git_diff_stats_deletions(
+ const git_diff_stats *stats);
+
+/**
+ * Print diff statistics to a `git_buf`.
+ *
+ * @param out buffer to store the formatted diff statistics in.
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @param format Formatting option.
+ * @return 0 on success; non-zero on error
+ */
+GIT_EXTERN(int) git_diff_stats_to_buf(
+ git_buf *out,
+ const git_diff_stats *stats,
+ git_diff_stats_format_t format);
+
+/**
+ * Deallocate a `git_diff_stats`.
+ *
+ * @param stats The previously created statistics object;
+ * cannot be used after free.
+ */
+GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats);
+
+/**
+ * Formatting options for diff e-mail generation
+ */
+typedef enum {
+ /** Normal patch, the default */
+ GIT_DIFF_FORMAT_EMAIL_NONE = 0,
+
+ /** Don't insert "[PATCH]" in the subject header*/
+ GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0),
+
+} git_diff_format_email_flags_t;
+
+/**
+ * Options for controlling the formatting of the generated e-mail.
+ */
+typedef struct {
+ unsigned int version;
+
+ git_diff_format_email_flags_t flags;
+
+ /** This patch number */
+ size_t patch_no;
+
+ /** Total number of patches in this series */
+ size_t total_patches;
+
+ /** id to use for the commit */
+ const git_oid *id;
+
+ /** Summary of the change */
+ const char *summary;
+
+ /** Author of the change */
+ const git_signature *author;
+} git_diff_format_email_options;
+
+#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
+#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL}
+
+/**
+ * Create an e-mail ready patch from a diff.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param diff containing the commit
+ * @param opts structure with options to influence content and formatting.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_diff_format_email(
+ git_buf *out,
+ git_diff *diff,
+ const git_diff_format_email_options *opts);
+
+/**
+ * Create an e-mail ready patch for a commit.
+ *
+ * Does not support creating patches for merge commits (yet).
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param repo containing the commit
+ * @param commit pointer to up commit
+ * @param patch_no patch number of the commit
+ * @param total_patches total number of patches in the patch set
+ * @param flags determines the formatting of the e-mail
+ * @param diff_opts structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_diff_commit_as_email(
+ git_buf *out,
+ git_repository *repo,
+ git_commit *commit,
+ size_t patch_no,
+ size_t total_patches,
+ git_diff_format_email_flags_t flags,
+ const git_diff_options *diff_opts);
+
+/**
+* Initializes a `git_diff_format_email_options` with default values. Equivalent to
+* creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
+*
+* @param opts the `git_diff_format_email_options` instance to initialize.
+* @param version the version of the struct; you should pass
+* `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION` here.
+* @return Zero on success; -1 on failure.
+*/
+GIT_EXTERN(int) git_diff_format_email_init_options(
+ git_diff_format_email_options *opts,
+ int version);
GIT_END_DECL
diff --git a/include/git2/patch.h b/include/git2/patch.h
index f5ec682c6..47c395669 100644
--- a/include/git2/patch.h
+++ b/include/git2/patch.h
@@ -141,12 +141,12 @@ GIT_EXTERN(void) git_patch_free(git_patch *patch);
/**
* Get the delta associated with a patch
*/
-GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch);
+GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch);
/**
* Get the number of hunks in a patch
*/
-GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch);
+GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch);
/**
* Get line counts of each type in a patch.
@@ -197,7 +197,7 @@ GIT_EXTERN(int) git_patch_get_hunk(
* @return Number of lines in hunk or -1 if invalid hunk index
*/
GIT_EXTERN(int) git_patch_num_lines_in_hunk(
- git_patch *patch,
+ const git_patch *patch,
size_t hunk_idx);
/**
diff --git a/src/buffer.c b/src/buffer.c
index 83960e912..f6e34a445 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -148,6 +148,15 @@ int git_buf_putc(git_buf *buf, char c)
return 0;
}
+int git_buf_putcn(git_buf *buf, char c, size_t len)
+{
+ ENSURE_SIZE(buf, buf->size + len + 1);
+ memset(buf->ptr + buf->size, c, len);
+ buf->size += len;
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
diff --git a/src/buffer.h b/src/buffer.h
index dba594d97..398aec9b7 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -88,6 +88,7 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
*/
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
+int git_buf_putcn(git_buf *buf, char c, size_t len);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
diff --git a/src/date.c b/src/date.c
index 7849c2f02..0e1b31aee 100644
--- a/src/date.c
+++ b/src/date.c
@@ -874,3 +874,31 @@ int git__date_parse(git_time_t *out, const char *date)
*out = approxidate_str(date, time_sec, &error_ret);
return error_ret;
}
+
+int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date)
+{
+ int written;
+ struct tm gmt;
+ time_t t;
+
+ assert(out && date);
+
+ t = (time_t) (date->time + date->offset * 60);
+
+ if (p_gmtime_r (&t, &gmt) == NULL)
+ return -1;
+
+ written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d",
+ weekday_names[gmt.tm_wday],
+ gmt.tm_mday,
+ month_names[gmt.tm_mon],
+ gmt.tm_year + 1900,
+ gmt.tm_hour, gmt.tm_min, gmt.tm_sec,
+ date->offset / 60, date->offset % 60);
+
+ if (written < 0 || (written > (int) len - 1))
+ return -1;
+
+ return 0;
+}
+
diff --git a/src/diff.c b/src/diff.c
index e62f45c22..cb05a5faf 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1427,6 +1427,216 @@ int git_diff__paired_foreach(
return error;
}
+int git_diff__commit(
+ git_diff **diff,
+ git_repository *repo,
+ const git_commit *commit,
+ const git_diff_options *opts)
+{
+ git_commit *parent = NULL;
+ git_diff *commit_diff = NULL;
+ git_tree *old_tree = NULL, *new_tree = NULL;
+ size_t parents;
+ int error = 0;
+
+ if ((parents = git_commit_parentcount(commit)) > 1) {
+ char commit_oidstr[GIT_OID_HEXSZ + 1];
+
+ error = -1;
+ giterr_set(GITERR_INVALID, "Commit %s is a merge commit",
+ git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
+ goto on_error;
+ }
+
+ if (parents > 0)
+ if ((error = git_commit_parent(&parent, commit, 0)) < 0 ||
+ (error = git_commit_tree(&old_tree, parent)) < 0)
+ goto on_error;
+
+ if ((error = git_commit_tree(&new_tree, commit)) < 0 ||
+ (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0)
+ goto on_error;
+
+ *diff = commit_diff;
+
+on_error:
+ git_tree_free(new_tree);
+ git_tree_free(old_tree);
+ git_commit_free(parent);
+
+ return error;
+}
+
+int git_diff_format_email__append_header_tobuf(
+ git_buf *out,
+ const git_oid *id,
+ const git_signature *author,
+ const char *summary,
+ size_t patch_no,
+ size_t total_patches,
+ bool exclude_patchno_marker)
+{
+ char idstr[GIT_OID_HEXSZ + 1];
+ char date_str[GIT_DATE_RFC2822_SZ];
+ int error = 0;
+
+ git_oid_fmt(idstr, id);
+ idstr[GIT_OID_HEXSZ] = '\0';
+
+ if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str), &author->when)) < 0)
+ return error;
+
+ error = git_buf_printf(out,
+ "From %s Mon Sep 17 00:00:00 2001\n" \
+ "From: %s <%s>\n" \
+ "Date: %s\n" \
+ "Subject: ",
+ idstr,
+ author->name, author->email,
+ date_str);
+
+ if (error < 0)
+ return error;
+
+ if (!exclude_patchno_marker) {
+ if (total_patches == 1) {
+ error = git_buf_puts(out, "[PATCH] ");
+ } else {
+ error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ", patch_no, total_patches);
+ }
+
+ if (error < 0)
+ return error;
+ }
+
+ error = git_buf_printf(out, "%s\n\n", summary);
+
+ return error;
+}
+
+int git_diff_format_email__append_patches_tobuf(
+ git_buf *out,
+ git_diff *diff)
+{
+ size_t i, deltas;
+ int error = 0;
+
+ deltas = git_diff_num_deltas(diff);
+
+ for (i = 0; i < deltas; ++i) {
+ git_patch *patch = NULL;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
+ error = git_patch_to_buf(out, patch);
+
+ git_patch_free(patch);
+
+ if (error < 0)
+ break;
+ }
+
+ return error;
+}
+
+int git_diff_format_email(
+ git_buf *out,
+ git_diff *diff,
+ const git_diff_format_email_options *opts)
+{
+ git_diff_stats *stats = NULL;
+ char *summary = NULL, *loc = NULL;
+ bool ignore_marker;
+ unsigned int format_flags = 0;
+ int error;
+
+ assert(out && diff && opts);
+ assert(opts->summary && opts->id && opts->author);
+
+ GITERR_CHECK_VERSION(opts, GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options");
+
+ if ((ignore_marker = opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) == false) {
+ if (opts->patch_no > opts->total_patches) {
+ giterr_set(GITERR_INVALID, "patch %"PRIuZ" out of range. max %"PRIuZ, opts->patch_no, opts->total_patches);
+ return -1;
+ }
+
+ if (opts->patch_no == 0) {
+ giterr_set(GITERR_INVALID, "invalid patch no %"PRIuZ". should be >0", opts->patch_no);
+ return -1;
+ }
+ }
+
+ /* the summary we receive may not be clean.
+ * it could potentially contain new line characters
+ * or not be set, sanitize, */
+ if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) {
+ size_t offset = 0;
+
+ if ((offset = (loc - opts->summary)) == 0) {
+ giterr_set(GITERR_INVALID, "summary is empty");
+ error = -1;
+ }
+
+ summary = git__calloc(offset + 1, sizeof(char));
+ GITERR_CHECK_ALLOC(summary);
+ strncpy(summary, opts->summary, offset);
+ }
+
+ error = git_diff_format_email__append_header_tobuf(out,
+ opts->id, opts->author, summary == NULL ? opts->summary : summary,
+ opts->patch_no, opts->total_patches, ignore_marker);
+
+ if (error < 0)
+ goto on_error;
+
+ format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
+
+ if ((error = git_buf_puts(out, "---\n")) < 0 ||
+ (error = git_diff_get_stats(&stats, diff)) < 0 ||
+ (error = git_diff_stats_to_buf(out, stats, format_flags)) < 0 ||
+ (error = git_diff_format_email__append_patches_tobuf(out, diff)) < 0)
+ goto on_error;
+
+ error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
+
+on_error:
+ git__free(summary);
+ git_diff_stats_free(stats);
+
+ return error;
+}
+
+int git_diff_commit_as_email(
+ git_buf *out,
+ git_repository *repo,
+ git_commit *commit,
+ size_t patch_no,
+ size_t total_patches,
+ git_diff_format_email_flags_t flags,
+ const git_diff_options *diff_opts)
+{
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ int error;
+
+ assert (out && repo && commit);
+
+ opts.flags = flags;
+ opts.patch_no = patch_no;
+ opts.total_patches = total_patches;
+ opts.id = git_commit_id(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.author = git_commit_author(commit);
+
+ if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
+ return error;
+
+ error = git_diff_format_email(out, diff, &opts);
+
+ git_diff_free(diff);
+ return error;
+}
+
int git_diff_init_options(git_diff_options* opts, int version)
{
if (version != GIT_DIFF_OPTIONS_VERSION) {
@@ -1450,3 +1660,15 @@ int git_diff_find_init_options(git_diff_find_options* opts, int version)
return 0;
}
}
+
+int git_diff_format_email_init_options(git_diff_format_email_options* opts, int version)
+{
+ if (version != GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION) {
+ giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_format_email_options", version);
+ return -1;
+ } else {
+ git_diff_format_email_options o = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ memcpy(opts, &o, sizeof(o));
+ return 0;
+ }
+}
diff --git a/src/diff.h b/src/diff.h
index c588f6301..aae8fbff1 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -116,6 +116,9 @@ extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
extern int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
+extern int git_diff__commit(
+ git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
+
/*
* Sometimes a git_diff_file will have a zero size; this attempts to
* fill in the size without loading the blob if possible. If that is
diff --git a/src/diff_patch.c b/src/diff_patch.c
index dd8b73938..38d5f4257 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -631,13 +631,13 @@ void git_patch_free(git_patch *patch)
GIT_REFCOUNT_DEC(patch, diff_patch_free);
}
-const git_diff_delta *git_patch_get_delta(git_patch *patch)
+const git_diff_delta *git_patch_get_delta(const git_patch *patch)
{
assert(patch);
return patch->delta;
}
-size_t git_patch_num_hunks(git_patch *patch)
+size_t git_patch_num_hunks(const git_patch *patch)
{
assert(patch);
return git_array_size(patch->hunks);
@@ -708,7 +708,7 @@ int git_patch_get_hunk(
return 0;
}
-int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx)
+int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
{
diff_patch_hunk *hunk;
assert(patch);
diff --git a/src/diff_stats.c b/src/diff_stats.c
new file mode 100644
index 000000000..bb436bf7b
--- /dev/null
+++ b/src/diff_stats.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "vector.h"
+#include "diff.h"
+#include "diff_patch.h"
+
+#define DIFF_RENAME_FILE_SEPARATOR " => "
+
+struct git_diff_stats {
+ git_vector patches;
+
+ size_t files_changed;
+ size_t insertions;
+ size_t deletions;
+};
+
+static size_t diff_get_filename_padding(
+ int has_renames,
+ const git_diff_stats *stats)
+{
+ const git_patch *patch = NULL;
+ size_t i, max_padding = 0;
+
+ if (has_renames) {
+ git_vector_foreach(&stats->patches, i, patch) {
+ const git_diff_delta *delta = NULL;
+ size_t len;
+
+ delta = git_patch_get_delta(patch);
+ if (strcmp(delta->old_file.path, delta->new_file.path) == 0)
+ continue;
+
+ if ((len = strlen(delta->old_file.path) + strlen(delta->new_file.path)) > max_padding)
+ max_padding = len;
+ }
+ }
+
+ git_vector_foreach(&stats->patches, i, patch) {
+ const git_diff_delta *delta = NULL;
+ size_t len;
+
+ delta = git_patch_get_delta(patch);
+ len = strlen(delta->new_file.path);
+
+ if (strcmp(delta->old_file.path, delta->new_file.path) != 0)
+ continue;
+
+ if (len > max_padding)
+ max_padding = len;
+ }
+
+ return max_padding;
+}
+
+int git_diff_file_stats__full_to_buf(
+ git_buf *out,
+ size_t max_padding,
+ int has_renames,
+ const git_patch *patch)
+{
+ const char *old_path = NULL, *new_path = NULL;
+ const git_diff_delta *delta = NULL;
+ size_t padding, old_size, new_size;
+ int error;
+
+ delta = git_patch_get_delta(patch);
+
+ old_path = delta->old_file.path;
+ new_path = delta->new_file.path;
+ old_size = delta->old_file.size;
+ new_size = delta->new_file.size;
+
+ if ((error = git_buf_printf(out, " %s", old_path)) < 0)
+ goto on_error;
+
+ if (strcmp(old_path, new_path) != 0) {
+ padding = max_padding - strlen(old_path) - strlen(new_path);
+
+ if ((error = git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path)) < 0)
+ goto on_error;
+ }
+ else {
+ padding = max_padding - strlen(old_path);
+
+ if (has_renames)
+ padding += strlen(DIFF_RENAME_FILE_SEPARATOR);
+ }
+
+ if ((error = git_buf_putcn(out, ' ', padding)) < 0 ||
+ (error = git_buf_puts(out, " | ")) < 0)
+ goto on_error;
+
+ if (delta->flags & GIT_DIFF_FLAG_BINARY) {
+ if ((error = git_buf_printf(out, "Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size)) < 0)
+ goto on_error;
+ }
+ else {
+ size_t insertions, deletions;
+
+ if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0)
+ goto on_error;
+
+ if ((error = git_buf_printf(out, "%" PRIuZ, insertions + deletions)) < 0)
+ goto on_error;
+
+ if (insertions || deletions) {
+ if ((error = git_buf_putc(out, ' ')) < 0 ||
+ (error = git_buf_putcn(out, '+', insertions)) < 0 ||
+ (error = git_buf_putcn(out, '-', deletions)) < 0)
+ goto on_error;
+ }
+ }
+
+ error = git_buf_putc(out, '\n');
+
+on_error:
+ return error;
+}
+
+int git_diff_file_stats__number_to_buf(
+ git_buf *out,
+ const git_patch *patch)
+{
+ const git_diff_delta *delta = NULL;
+ const char *path = NULL;
+ size_t insertions, deletions;
+ int error;
+
+ delta = git_patch_get_delta(patch);
+ path = delta->new_file.path;
+
+ if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0)
+ return error;
+
+ if (delta->flags & GIT_DIFF_FLAG_BINARY)
+ error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path);
+ else
+ error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n", insertions, deletions, path);
+
+ return error;
+}
+
+int git_diff_file_stats__summary_to_buf(
+ git_buf *out,
+ const git_patch *patch)
+{
+ const git_diff_delta *delta = NULL;
+
+ delta = git_patch_get_delta(patch);
+
+ if (delta->old_file.mode != delta->new_file.mode) {
+ if (delta->old_file.mode == 0) {
+ git_buf_printf(out, " create mode %06o %s\n",
+ delta->new_file.mode, delta->new_file.path);
+ }
+ else if (delta->new_file.mode == 0) {
+ git_buf_printf(out, " delete mode %06o %s\n",
+ delta->old_file.mode, delta->old_file.path);
+ }
+ else {
+ git_buf_printf(out, " mode change %06o => %06o %s\n",
+ delta->old_file.mode, delta->new_file.mode, delta->new_file.path);
+ }
+ }
+
+ return 0;
+}
+
+int git_diff_stats__has_renames(
+ const git_diff_stats *stats)
+{
+ git_patch *patch = NULL;
+ size_t i;
+
+ git_vector_foreach(&stats->patches, i, patch) {
+ const git_diff_delta *delta = git_patch_get_delta(patch);
+
+ if (strcmp(delta->old_file.path, delta->new_file.path) != 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int git_diff_stats__add_file_stats(
+ git_diff_stats *stats,
+ git_patch *patch)
+{
+ const git_diff_delta *delta = NULL;
+ int error = 0;
+
+ if ((delta = git_patch_get_delta(patch)) == NULL)
+ return -1;
+
+ if ((error = git_vector_insert(&stats->patches, patch)) < 0)
+ return error;
+
+ return error;
+}
+
+int git_diff_get_stats(
+ git_diff_stats **out,
+ git_diff *diff)
+{
+ size_t i, deltas;
+ size_t total_insertions = 0, total_deletions = 0;
+ git_diff_stats *stats = NULL;
+ int error = 0;
+
+ assert(out && diff);
+
+ stats = git__calloc(1, sizeof(git_diff_stats));
+ GITERR_CHECK_ALLOC(stats);
+
+ deltas = git_diff_num_deltas(diff);
+
+ for (i = 0; i < deltas; ++i) {
+ git_patch *patch = NULL;
+ size_t add, remove;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
+ goto on_error;
+
+ if ((error = git_patch_line_stats(NULL, &add, &remove, patch)) < 0 ||
+ (error = git_diff_stats__add_file_stats(stats, patch)) < 0) {
+ git_patch_free(patch);
+ goto on_error;
+ }
+
+ total_insertions += add;
+ total_deletions += remove;
+ }
+
+ stats->files_changed = deltas;
+ stats->insertions = total_insertions;
+ stats->deletions = total_deletions;
+
+ *out = stats;
+
+ goto done;
+
+on_error:
+ git_diff_stats_free(stats);
+
+done:
+ return error;
+}
+
+size_t git_diff_stats_files_changed(
+ const git_diff_stats *stats)
+{
+ assert(stats);
+
+ return stats->files_changed;
+}
+
+size_t git_diff_stats_insertions(
+ const git_diff_stats *stats)
+{
+ assert(stats);
+
+ return stats->insertions;
+}
+
+size_t git_diff_stats_deletions(
+ const git_diff_stats *stats)
+{
+ assert(stats);
+
+ return stats->deletions;
+}
+
+int git_diff_stats_to_buf(
+ git_buf *out,
+ const git_diff_stats *stats,
+ git_diff_stats_format_t format)
+{
+ git_patch *patch = NULL;
+ size_t i;
+ int has_renames = 0, error = 0;
+
+ assert(out && stats);
+
+ /* check if we have renames, it affects the padding */
+ has_renames = git_diff_stats__has_renames(stats);
+
+ git_vector_foreach(&stats->patches, i, patch) {
+ if (format & GIT_DIFF_STATS_FULL) {
+ size_t max_padding = diff_get_filename_padding(has_renames, stats);
+
+ error = git_diff_file_stats__full_to_buf(out, max_padding, has_renames, patch);
+ }
+ else if (format & GIT_DIFF_STATS_NUMBER) {
+ error = git_diff_file_stats__number_to_buf(out, patch);
+ }
+
+ if (error < 0)
+ return error;
+ }
+
+ if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) {
+ error = git_buf_printf(out, " %" PRIuZ " file%s changed, %" PRIuZ " insertions(+), %" PRIuZ " deletions(-)\n",
+ stats->files_changed, stats->files_changed > 1 ? "s" : "",
+ stats->insertions, stats->deletions);
+
+ if (error < 0)
+ return error;
+ }
+
+ if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) {
+ git_vector_foreach(&stats->patches, i, patch) {
+ if ((error = git_diff_file_stats__summary_to_buf(out, patch)) < 0)
+ return error;
+ }
+
+ if (git_vector_length(&stats->patches) > 0)
+ git_buf_putc(out, '\n');
+ }
+
+ return error;
+}
+
+void git_diff_stats_free(git_diff_stats *stats)
+{
+ size_t i;
+ git_patch *patch;
+
+ if (stats == NULL)
+ return;
+
+ git_vector_foreach(&stats->patches, i, patch)
+ git_patch_free(patch);
+
+ git_vector_free(&stats->patches);
+ git__free(stats);
+}
+
diff --git a/src/util.h b/src/util.h
index e378786d9..5c2c563d6 100644
--- a/src/util.h
+++ b/src/util.h
@@ -20,6 +20,8 @@
# define max(a,b) ((a) > (b) ? (a) : (b))
#endif
+#define GIT_DATE_RFC2822_SZ 32
+
/*
* Custom memory allocation wrappers
* that set error code and error message
@@ -329,6 +331,16 @@ extern int git__parse_bool(int *out, const char *value);
extern int git__date_parse(git_time_t *out, const char *date);
/*
+ * Format a git_time as a RFC2822 string
+ *
+ * @param out buffer to store formatted date; a '\\0' terminator will automatically be added.
+ * @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size;
+ * @param date the date to be formatted
+ * @return 0 if successful; -1 on error
+ */
+extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
+
+/*
* Unescapes a string in-place.
*
* Edge cases behavior:
diff --git a/tests/date/rfc2822.c b/tests/date/rfc2822.c
new file mode 100644
index 000000000..eda475ac9
--- /dev/null
+++ b/tests/date/rfc2822.c
@@ -0,0 +1,40 @@
+#include "clar_libgit2.h"
+
+#include "util.h"
+
+void test_date_rfc2822__format_rfc2822_no_offset(void)
+{
+ git_time t = {1397031663, 0};
+ char buf[GIT_DATE_RFC2822_SZ];
+
+ cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
+ cl_assert(strcmp(buf, "Wed, 9 Apr 2014 08:21:03 +0000") == 0);
+}
+
+void test_date_rfc2822__format_rfc2822_positive_offset(void)
+{
+ git_time t = {1397031663, 120};
+ char buf[GIT_DATE_RFC2822_SZ];
+
+ cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
+ cl_assert(strcmp(buf, "Wed, 9 Apr 2014 10:21:03 +0200") == 0);
+}
+
+void test_date_rfc2822__format_rfc2822_negative_offset(void)
+{
+ git_time t = {1397031663, -120};
+ char buf[GIT_DATE_RFC2822_SZ];
+
+ cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
+ cl_assert(strcmp(buf, "Wed, 9 Apr 2014 06:21:03 -0200") == 0);
+}
+
+void test_date_rfc2822__format_rfc2822_buffer_too_small(void)
+{
+ // "Wed, 10 Apr 2014 08:21:03 +0000"
+ git_time t = {1397031663 + 86400, 0};
+ char buf[GIT_DATE_RFC2822_SZ-1];
+
+ cl_git_fail(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
+}
+
diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c
new file mode 100644
index 000000000..3260fdea8
--- /dev/null
+++ b/tests/diff/format_email.c
@@ -0,0 +1,556 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "buffer.h"
+#include "commit.h"
+#include "diff.h"
+
+static git_repository *repo;
+
+void test_diff_format_email__initialize(void)
+{
+ repo = cl_git_sandbox_init("diff_format_email");
+}
+
+void test_diff_format_email__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_format_email__simple(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "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" \
+ "Subject: [PATCH] Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\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" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__multiple(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \
+ "Subject: [PATCH 1/2] Added file2.txt file3.txt\n" \
+ "\n" \
+ "---\n" \
+ " file2.txt | 5 +++++\n" \
+ " file3.txt | 5 +++++\n" \
+ " 2 files changed, 10 insertions(+), 0 deletions(-)\n" \
+ " create mode 100644 file2.txt\n" \
+ " create mode 100644 file3.txt\n" \
+ "\n" \
+ "diff --git a/file2.txt b/file2.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..e909123\n" \
+ "--- /dev/null\n" \
+ "+++ b/file2.txt\n" \
+ "@@ -0,0 +1,5 @@\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..9435022\n" \
+ "--- /dev/null\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -0,0 +1,5 @@\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n" \
+ "From 873806f6f27e631eb0b23e4b56bea2bfac14a373 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Thu, 10 Apr 2014 19:37:36 +0200\n" \
+ "Subject: [PATCH 2/2] Modified file2.txt, file3.txt\n" \
+ "\n" \
+ "---\n" \
+ " file2.txt | 2 +-\n" \
+ " file3.txt | 2 +-\n" \
+ " 2 files changed, 2 insertions(+), 2 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file2.txt b/file2.txt\n" \
+ "index e909123..7aff11d 100644\n" \
+ "--- a/file2.txt\n" \
+ "+++ b/file2.txt\n" \
+ "@@ -1,5 +1,5 @@\n" \
+ " file2\n" \
+ " file2\n" \
+ " file2\n" \
+ "-file2\n" \
+ "+file2!\n" \
+ " file2\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "index 9435022..9a2d780 100644\n" \
+ "--- a/file3.txt\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -1,5 +1,5 @@\n" \
+ " file3\n" \
+ "-file3\n" \
+ "+file3!\n" \
+ " file3\n" \
+ " file3\n" \
+ " file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508");
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.patch_no = 1;
+ opts.total_patches = 2;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ diff = NULL;
+ commit = NULL;
+
+ git_oid_fromstr(&oid, "873806f6f27e631eb0b23e4b56bea2bfac14a373");
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.patch_no = 2;
+ opts.total_patches = 2;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__exclude_marker(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "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" \
+ "Subject: Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\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" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+
+ opts.flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1,
+ GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER, NULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__invalid_no(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.patch_no = 2;
+ opts.total_patches = 1;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_fail(git_diff_format_email(&buf, diff, &opts));
+ cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL));
+ cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 0, 0, 0, NULL));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__mode_change(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \
+ "Subject: [PATCH] Update permissions\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt.renamed | 0\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ " mode change 100644 => 100755 file1.txt.renamed\n" \
+ "\n" \
+ "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
+ "old mode 100644\n" \
+ "new mode 100755\n" \
+ "index a97157a..a97157a\n" \
+ "--- a/file1.txt.renamed\n" \
+ "+++ b/file1.txt.renamed\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__rename_add_remove(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "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_oid_fromstr(&oid, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__multiline_summary(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "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" \
+ "Subject: [PATCH] Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\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" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = "Modify some content\nSome extra stuff here";
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_format_email__binary(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */
+ const char *email =
+ "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
+ "Subject: [PATCH] Modified binary file\n" \
+ "\n" \
+ "---\n" \
+ " binary.bin | Bin 3 -> 0 bytes\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ "\n" \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2..9ac35ff 100644\n" \
+ "Binary files a/binary.bin and b/binary.bin differ\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = "Modified binary file";
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+ cl_assert(strcmp(git_buf_cstr(&buf), email) == 0);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
diff --git a/tests/diff/stats.c b/tests/diff/stats.c
new file mode 100644
index 000000000..131b7681d
--- /dev/null
+++ b/tests/diff/stats.c
@@ -0,0 +1,428 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "buffer.h"
+#include "commit.h"
+#include "diff.h"
+
+static git_repository *repo;
+
+void test_diff_stats__initialize(void)
+{
+ repo = cl_git_sandbox_init("diff_format_email");
+}
+
+void test_diff_stats__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_stats__stat(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 1);
+ cl_assert(git_diff_stats_insertions(stats) == 5);
+ cl_assert(git_diff_stats_deletions(stats) == 3);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__multiple_hunks(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt | 5 +++--\n" \
+ " file3.txt | 6 ++++--\n" \
+ " 2 files changed, 7 insertions(+), 4 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 2);
+ cl_assert(git_diff_stats_insertions(stats) == 7);
+ cl_assert(git_diff_stats_deletions(stats) == 4);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__numstat(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ "3 2 file2.txt\n"
+ "4 2 file3.txt\n";
+
+ git_oid_fromstr(&oid, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_NUMBER));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__shortstat(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 1);
+ cl_assert(git_diff_stats_insertions(stats) == 5);
+ cl_assert(git_diff_stats_deletions(stats) == 3);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_SHORT));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__rename(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt => file2.txt.renamed | 1 +\n"
+ " file3.txt => file3.txt.renamed | 4 +++-\n"
+ " 2 files changed, 4 insertions(+), 1 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_find_similar(diff, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 2);
+ cl_assert(git_diff_stats_insertions(stats) == 4);
+ cl_assert(git_diff_stats_deletions(stats) == 1);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__rename_nochanges(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt.renamed => file2.txt.renamed2 | 0\n"
+ " file3.txt.renamed => file3.txt.renamed2 | 0\n"
+ " 2 files changed, 0 insertions(+), 0 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_find_similar(diff, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 2);
+ cl_assert(git_diff_stats_insertions(stats) == 0);
+ cl_assert(git_diff_stats_deletions(stats) == 0);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__rename_and_modifiy(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt.renamed2 | 2 +-\n"
+ " file3.txt.renamed2 => file3.txt.renamed | 0\n"
+ " 2 files changed, 1 insertions(+), 1 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "4ca10087e696d2ba78d07b146a118e9a7096ed4f");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_find_similar(diff, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 2);
+ cl_assert(git_diff_stats_insertions(stats) == 1);
+ cl_assert(git_diff_stats_deletions(stats) == 1);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__rename_no_find(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt | 5 -----\n"
+ " file2.txt.renamed | 6 ++++++\n"
+ " file3.txt | 5 -----\n"
+ " file3.txt.renamed | 7 +++++++\n"
+ " 4 files changed, 13 insertions(+), 10 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 4);
+ cl_assert(git_diff_stats_insertions(stats) == 13);
+ cl_assert(git_diff_stats_deletions(stats) == 10);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__rename_nochanges_no_find(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt.renamed | 6 ------\n"
+ " file2.txt.renamed2 | 6 ++++++\n"
+ " file3.txt.renamed | 7 -------\n"
+ " file3.txt.renamed2 | 7 +++++++\n"
+ " 4 files changed, 13 insertions(+), 13 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 4);
+ cl_assert(git_diff_stats_insertions(stats) == 13);
+ cl_assert(git_diff_stats_deletions(stats) == 13);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__rename_and_modifiy_no_find(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file2.txt.renamed2 | 2 +-\n"
+ " file3.txt.renamed | 7 +++++++\n"
+ " file3.txt.renamed2 | 7 -------\n"
+ " 3 files changed, 8 insertions(+), 8 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "4ca10087e696d2ba78d07b146a118e9a7096ed4f");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 3);
+ cl_assert(git_diff_stats_insertions(stats) == 8);
+ cl_assert(git_diff_stats_deletions(stats) == 8);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__binary(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */
+ const char *stat =
+ " binary.bin | Bin 3 -> 0 bytes\n"
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n";
+
+ git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+ cl_assert(git_diff_stats_files_changed(stats) == 1);
+ cl_assert(git_diff_stats_insertions(stats) == 0);
+ cl_assert(git_diff_stats_deletions(stats) == 0);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__binary_numstat(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ "- - binary.bin\n";
+
+ git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_NUMBER));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
+
+void test_diff_stats__mode_change(void)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_stats *stats = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *stat =
+ " file1.txt.renamed | 0\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ " mode change 100644 => 100755 file1.txt.renamed\n" \
+ "\n";
+
+ git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189");
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+
+ cl_git_pass(git_diff_get_stats(&stats, diff));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+
+ git_diff_stats_free(stats);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_free(&buf);
+}
diff --git a/tests/resources/diff_format_email/.gitted/HEAD b/tests/resources/diff_format_email/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/diff_format_email/.gitted/config b/tests/resources/diff_format_email/.gitted/config
new file mode 100644
index 000000000..6c9406b7d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/diff_format_email/.gitted/index b/tests/resources/diff_format_email/.gitted/index
new file mode 100644
index 000000000..f73027e56
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/index
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/info/exclude b/tests/resources/diff_format_email/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce b/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce
new file mode 100644
index 000000000..1ece99cde
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 b/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508
new file mode 100644
index 000000000..90313feec
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 b/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8
new file mode 100644
index 000000000..360dc3bc7
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab b/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab
new file mode 100644
index 000000000..603aa496a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e b/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e
new file mode 100644
index 000000000..b6f04538e
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 b/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5
new file mode 100644
index 000000000..be85c78ba
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 b/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20
new file mode 100644
index 000000000..e8145edfd
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 b/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6
new file mode 100644
index 000000000..3ae87cfa9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 b/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234
new file mode 100644
index 000000000..f80b3612c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a b/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a
new file mode 100644
index 000000000..190c3f272
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 b/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264
new file mode 100644
index 000000000..42f49ae8a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 b/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3
new file mode 100644
index 000000000..e1ede9ae9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d b/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d
new file mode 100644
index 000000000..1b8d68951
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e b/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e
new file mode 100644
index 000000000..10c34009d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 b/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6
new file mode 100644
index 000000000..689f5b9a1
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df b/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df
new file mode 100644
index 000000000..6af5dda9d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d b/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d
new file mode 100644
index 000000000..d2b911d0e
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 b/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4
new file mode 100644
index 000000000..69d213dcb
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf b/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf
new file mode 100644
index 000000000..e5014565b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf
@@ -0,0 +1,2 @@
+xJ0ay_IIqwo0Lo+$&[zU&FY,:YCT8B UsH,BL)(r.ca }+^Y>mp՚_ sG)&k}GhzRļi*
+Rrs X \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa b/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa
new file mode 100644
index 000000000..b855408e8
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c b/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c
new file mode 100644
index 000000000..65740b42c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f b/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f
new file mode 100644
index 000000000..b05e7d634
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e b/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e
new file mode 100644
index 000000000..57a8dfed1
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 b/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87
new file mode 100644
index 000000000..0f31b97d6
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 b/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509
new file mode 100644
index 000000000..5b96aa5ea
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 b/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261
new file mode 100644
index 000000000..8db9090bc
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d b/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d
new file mode 100644
index 000000000..4bbc0ea43
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa b/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa
new file mode 100644
index 000000000..680e48762
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 b/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5
new file mode 100644
index 000000000..86a38289b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 b/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448
new file mode 100644
index 000000000..81b606f4e
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 b/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430
new file mode 100644
index 000000000..aa9d7b0cd
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430
@@ -0,0 +1,2 @@
+x;
+1a X)$sD3{.+sšsj޵ 'fg=Cm⤄A <p&m5\H:0 ThaCJ[;̱<^)mY \xupŒS]\s01dTžrJq_rIム/PK \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c b/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c
new file mode 100644
index 000000000..49e9d65f4
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f b/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f
new file mode 100644
index 000000000..e32471ba9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d b/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d
new file mode 100644
index 000000000..26ee22429
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d
@@ -0,0 +1,2 @@
+xAN0 EYޏ8` fĒtDܞ"O,!uU1գ*C2G,2pt+{7)JcE,JT 1IC88tx
+Oۼ^Zd(K;'Θ N*q/zM`0~W@ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93 b/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93
new file mode 100644
index 000000000..ee55d64d7
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93
@@ -0,0 +1 @@
+x+)JMU01e040031QHI5+(+JKMMaXYB،|-a'{"̊w \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86 b/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86
new file mode 100644
index 000000000..1bae4a6d6
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231 b/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231
new file mode 100644
index 000000000..db037caf9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231
@@ -0,0 +1,2 @@
+xAj1 E)/
+$<N<EoZatU@)3C" "J-z 9'2O H%J(5I2:| ﹐L\tqon$MW^p~:L> LqGh~u$+g+H[^`ȏQW \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 b/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189
new file mode 100644
index 000000000..cf9bdaa5f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189
@@ -0,0 +1,3 @@
+xA
+0E]d4M"`m5IޢGp>X4aWn3 y ,[FHoD-e
+:ёcz0FbCÊ:_HNcwRHqN'6xF {4j[j$0U_y*&R \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 b/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7
new file mode 100644
index 000000000..d8c9934f7
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 b/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6
new file mode 100644
index 000000000..df4cbb322
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 b/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373
new file mode 100644
index 000000000..890abcd4a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd b/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd
new file mode 100644
index 000000000..d4018bf8e
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd
@@ -0,0 +1,2 @@
+xAj0E)/ь,Jw.{8 ;Eo_C{nWZnYҩ7:8\u!Ni@0qaWR4Foڰt$
+S8"C'-ΫD1쇖}Gv߿7{y۬]eb"sc|bъL \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c b/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c
new file mode 100644
index 000000000..1dce143b7
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c
@@ -0,0 +1 @@
+xAN0 @Q9Hȵ$Y!q 't1g$-~ֵ FWb[hqXY)9Q[ '͘9d&e‘us׶__yGo2lLD3HաsZ!;X"Pq \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 b/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9
new file mode 100644
index 000000000..903ec751c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9
@@ -0,0 +1 @@
+xAJ0a9IӤw!f&_Utno#nF7JZLT$%BQVfe,,9+ElL$a$KAu|ެm>zO/@SNOfG!c+6+ ]ER \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 b/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76
new file mode 100644
index 000000000..b5e08f901
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 b/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92
new file mode 100644
index 000000000..75b047a64
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a b/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a
new file mode 100644
index 000000000..a5286bc68
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 b/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03
new file mode 100644
index 000000000..9e26e34cd
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 b/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911
new file mode 100644
index 000000000..5fc167e79
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 b/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44
new file mode 100644
index 000000000..be48c275e
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 b/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63
new file mode 100644
index 000000000..f322a7ce4
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 b/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4
new file mode 100644
index 000000000..75cd71426
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b b/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b
new file mode 100644
index 000000000..d84ab8dcc
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b b/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b
new file mode 100644
index 000000000..a693df5c8
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b
@@ -0,0 +1 @@
+xMJ1@a9ETDf7 xJRD5^nm]ihbSK5p)RD.V|ۄԢEBR"rPKi6|6!;e}^yK7KC H桏)uҠ;hQK \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 b/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238
new file mode 100644
index 000000000..f0483943b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f b/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f
new file mode 100644
index 000000000..5c1faf009
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 b/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961
new file mode 100644
index 000000000..3baf494be
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 b/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13
new file mode 100644
index 000000000..f0dcaa9ac
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 b/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235
new file mode 100644
index 000000000..28e148f41
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 b/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9
new file mode 100644
index 000000000..0c74e7696
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 b/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1
new file mode 100644
index 000000000..2dc82087b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff b/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff
new file mode 100644
index 000000000..af0232aa1
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df b/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df
new file mode 100644
index 000000000..e03c74935
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df
@@ -0,0 +1,3 @@
+xK
+0FaYE悤Cq&8t7[![p NĹIM~* }ʬ'lilxqԤw6kK
+cX̱ ^0Wy^+ja,㲴:rCYvttZUOrRbmᯈc$M \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 b/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2
new file mode 100644
index 000000000..155b45273
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 b/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8
new file mode 100644
index 000000000..fd9363612
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a b/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a
new file mode 100644
index 000000000..5863cec1b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e b/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e
new file mode 100644
index 000000000..a5d4d78e9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e
@@ -0,0 +1 @@
+x+)JMU01e040075UHI5+(+JKMMaXYB،|-a'{"z \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a b/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a
new file mode 100644
index 000000000..85eb8141c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc b/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc
new file mode 100644
index 000000000..8e250ece2
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 b/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0
new file mode 100644
index 000000000..92a89a2cf
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 b/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1
new file mode 100644
index 000000000..43a006231
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 b/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2
new file mode 100644
index 000000000..ee1edad9a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2
@@ -0,0 +1,2 @@
+xM
+0@a9Ed3+o)-x4Imp*9Su$\0Nuؐ1^%u/X$e,BPLK# 9FpPĵ S^+/u,˰~y|~^Z-CƓHh^iĦj"R,HrK \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 b/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3
new file mode 100644
index 000000000..62d747cf6
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3
@@ -0,0 +1 @@
+xA @Qלb&fhi)1bA1b#L&=ۿx?̥dN*3x`C]p-&H]ScpYk|[LB0Yq4Qu] ;KlHB \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 b/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085
new file mode 100644
index 000000000..d858a87b5
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085
@@ -0,0 +1 @@
+xMN0 @a98HCBNvL~ܞ8omkЫ*pń31M6L1'4'rJ\!bk=Kts zgd!6mmir[u.^<Z_9 "YPR \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc b/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc
new file mode 100644
index 000000000..b9919c225
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc
@@ -0,0 +1,2 @@
+xM
+0@a9ELL"G[<oxq,ehݴ9 HH`\{jMwфHYX Hǒ9 JvH|-y\0ߗϬݎs4ű4-Zu]m: eR_K \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514 b/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514
new file mode 100644
index 000000000..776221d21
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb b/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb
new file mode 100644
index 000000000..92964252a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/binary b/tests/resources/diff_format_email/.gitted/refs/heads/binary
new file mode 100644
index 000000000..7e563c957
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/binary
@@ -0,0 +1 @@
+a3ac918e3a6604294b239cb956363e83d71abb3b
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/master b/tests/resources/diff_format_email/.gitted/refs/heads/master
new file mode 100644
index 000000000..f0f3f932a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/master
@@ -0,0 +1 @@
+873806f6f27e631eb0b23e4b56bea2bfac14a373
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/multihunk b/tests/resources/diff_format_email/.gitted/refs/heads/multihunk
new file mode 100644
index 000000000..41bd37f39
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/multihunk
@@ -0,0 +1 @@
+cd471f0d8770371e1bc78bcbb38db4c7e4106bd2
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/rename b/tests/resources/diff_format_email/.gitted/refs/heads/rename
new file mode 100644
index 000000000..3025fbc64
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/rename
@@ -0,0 +1 @@
+4ca10087e696d2ba78d07b146a118e9a7096ed4f
diff --git a/tests/resources/diff_format_email/file1.txt.renamed b/tests/resources/diff_format_email/file1.txt.renamed
new file mode 100755
index 000000000..a97157a0d
--- /dev/null
+++ b/tests/resources/diff_format_email/file1.txt.renamed
@@ -0,0 +1,17 @@
+file1.txt
+file1.txt
+_file1.txt_
+file1.txt
+file1.txt
+file1.txt_renamed
+file1.txt
+
+
+file1.txt
+file1.txt
+file1.txt_renamed
+file1.txt
+file1.txt
+_file1.txt_
+_file1.txt_
+file1.txt
diff --git a/tests/resources/diff_format_email/file2.txt b/tests/resources/diff_format_email/file2.txt
new file mode 100644
index 000000000..7aff11da9
--- /dev/null
+++ b/tests/resources/diff_format_email/file2.txt
@@ -0,0 +1,5 @@
+file2
+file2
+file2
+file2!
+file2
diff --git a/tests/resources/diff_format_email/file3.txt b/tests/resources/diff_format_email/file3.txt
new file mode 100644
index 000000000..9a2d780ac
--- /dev/null
+++ b/tests/resources/diff_format_email/file3.txt
@@ -0,0 +1,5 @@
+file3
+file3!
+file3
+file3
+file3