diff options
-rw-r--r-- | include/git2.h | 1 | ||||
-rw-r--r-- | include/git2/deprecated.h | 96 | ||||
-rw-r--r-- | include/git2/diff.h | 93 | ||||
-rw-r--r-- | include/git2/email.h | 127 | ||||
-rw-r--r-- | include/git2/sys/email.h | 45 | ||||
-rw-r--r-- | src/buffer.c | 7 | ||||
-rw-r--r-- | src/buffer.h | 1 | ||||
-rw-r--r-- | src/diff.c | 214 | ||||
-rw-r--r-- | src/email.c | 300 | ||||
-rw-r--r-- | src/email.h | 25 | ||||
-rw-r--r-- | tests/diff/format_email.c | 21 | ||||
-rw-r--r-- | tests/email/create.c | 363 |
12 files changed, 1026 insertions, 267 deletions
diff --git a/include/git2.h b/include/git2.h index f39d7fbe2..2961cc3e5 100644 --- a/include/git2.h +++ b/include/git2.h @@ -26,6 +26,7 @@ #include "git2/deprecated.h" #include "git2/describe.h" #include "git2/diff.h" +#include "git2/email.h" #include "git2/errors.h" #include "git2/filter.h" #include "git2/global.h" diff --git a/include/git2/deprecated.h b/include/git2/deprecated.h index a2b117f53..6b268eb79 100644 --- a/include/git2/deprecated.h +++ b/include/git2/deprecated.h @@ -294,6 +294,102 @@ typedef git_configmap git_cvar_map; /**@}*/ +/** @name Deprecated Diff Functions and Constants + * + * These functions and enumeration values are retained for backward + * compatibility. The newer versions of these functions and values + * should be preferred in all new code. + * + * There is no plan to remove these backward compatibility values at + * this time. + */ +/**@{*/ + +/** + * 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; + + /** see `git_diff_format_email_flags_t` above */ + uint32_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; + + /** Commit message's body */ + const char *body; + + /** 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, NULL} + +/** + * Create an e-mail ready patch from a diff. + * + * @deprecated git_email_create_from_diff + * @see git_email_create_from_diff + */ +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. + * + * @deprecated git_email_create_from_commit + * @see git_email_create_from_commit + */ +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, + uint32_t flags, + const git_diff_options *diff_opts); + +/** + * Initialize git_diff_format_email_options structure + * + * 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_blame_options` struct to initialize. + * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_diff_format_email_options_init( + git_diff_format_email_options *opts, + unsigned int version); + +/**@}*/ + /** @name Deprecated Error Functions and Constants * * These functions and enumeration values are retained for backward diff --git a/include/git2/diff.h b/include/git2/diff.h index 9497793c3..a0a15e76f 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -1377,99 +1377,6 @@ GIT_EXTERN(int) git_diff_stats_to_buf( 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; - - /** see `git_diff_format_email_flags_t` above */ - uint32_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; - - /** Commit message's body */ - const char *body; - - /** 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, 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, - uint32_t flags, - const git_diff_options *diff_opts); - -/** - * Initialize git_diff_format_email_options structure - * - * 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_blame_options` struct to initialize. - * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. - * @return Zero on success; -1 on failure. - */ -GIT_EXTERN(int) git_diff_format_email_options_init( - git_diff_format_email_options *opts, - unsigned int version); - -/** * Patch ID options structure * * Initialize with `GIT_PATCHID_OPTIONS_INIT`. Alternatively, you can diff --git a/include/git2/email.h b/include/git2/email.h new file mode 100644 index 000000000..b56be5d0e --- /dev/null +++ b/include/git2/email.h @@ -0,0 +1,127 @@ +/* + * 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. + */ +#ifndef INCLUDE_git_email_h__ +#define INCLUDE_git_email_h__ + +#include "common.h" + +/** + * @file git2/email.h + * @brief Git email formatting and application routines. + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Formatting options for diff e-mail generation + */ +typedef enum { + /** Normal patch, the default */ + GIT_EMAIL_CREATE_DEFAULT = 0, + + /** Do not include patch numbers in the subject prefix. */ + GIT_EMAIL_CREATE_OMIT_NUMBERS = (1u << 0), + + /** + * Include numbers in the subject prefix even when the + * patch is for a single commit (1/1). + */ + GIT_EMAIL_CREATE_ALWAYS_NUMBER = (1u << 1), + + /** Do not perform rename or similarity detection. */ + GIT_EMAIL_CREATE_NO_RENAMES = (1u << 2), +} git_email_create_flags_t; + +/** + * Options for controlling the formatting of the generated e-mail. + */ +typedef struct { + unsigned int version; + + /** see `git_email_create_flags_t` above */ + uint32_t flags; + + /** Options to use when creating diffs */ + git_diff_options diff_opts; + + /** Options for finding similarities within diffs */ + git_diff_find_options diff_find_opts; + + /** + * The subject prefix, by default "PATCH". If set to an empty + * string ("") then only the patch numbers will be shown in the + * prefix. If the subject_prefix is empty and patch numbers + * are not being shown, the prefix will be omitted entirely. + */ + const char *subject_prefix; + + /** + * The starting patch number; this cannot be 0. By default, + * this is 1. + */ + size_t start_number; + + /** The "re-roll" number. By default, there is no re-roll. */ + size_t reroll_number; +} git_email_create_options; + +/* + * By default, our options include rename detection and binary + * diffs to match `git format-patch`. + */ +#define GIT_EMAIL_CREATE_OPTIONS_VERSION 1 +#define GIT_EMAIL_CREATE_OPTIONS_INIT \ +{ \ + GIT_EMAIL_CREATE_OPTIONS_VERSION, \ + GIT_EMAIL_CREATE_DEFAULT, \ + { GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 }, \ + GIT_DIFF_FIND_OPTIONS_INIT \ +} + +/** + * Create a diff for a commit in mbox format for sending via email. + * + * @param out buffer to store the e-mail patch in + * @param diff the changes to include in the email + * @param patch_idx the patch index + * @param patch_count the total number of patches that will be included + * @param commit_id the commit id for this change + * @param summary the commit message for this change + * @param body optional text to include above the diffstat + * @param author the person who authored this commit + * @param opts email creation options + */ +GIT_EXTERN(int) git_email_create_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *opts); + +/** + * Create a diff for a commit in mbox format for sending via email. + * The commit must not be a merge commit. + * + * @param out buffer to store the e-mail patch in + * @param commit commit to create a patch for + * @param opts email creation options + */ +GIT_EXTERN(int) git_email_create_from_commit( + git_buf *out, + git_commit *commit, + const git_email_create_options *opts); + +GIT_END_DECL + +/** @} */ + +#endif diff --git a/include/git2/sys/email.h b/include/git2/sys/email.h new file mode 100644 index 000000000..6f4a28662 --- /dev/null +++ b/include/git2/sys/email.h @@ -0,0 +1,45 @@ +/* + * 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. + */ +#ifndef INCLUDE_sys_git_email_h__ +#define INCLUDE_sys_git_email_h__ + +/** + * @file git2/sys/email.h + * @brief Advanced git email creation routines + * @defgroup git_email Advanced git email creation routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Create a diff for a commit in mbox format for sending via email. + * + * @param out buffer to store the e-mail patch in + * @param diff the changes to include in the email + * @param patch_idx the patch index + * @param patch_count the total number of patches that will be included + * @param commit_id the commit id for this change + * @param summary the commit message for this change + * @param body optional text to include above the diffstat + * @param author the person who authored this commit + * @param opts email creation options + */ +GIT_EXTERN(int) git_email_create_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *opts); + +/** @} */ +GIT_END_DECL +#endif diff --git a/src/buffer.c b/src/buffer.c index ab2a6139a..a57df1284 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -600,6 +600,13 @@ void git_buf_shorten(git_buf *buf, size_t amount) git_buf_clear(buf); } +void git_buf_truncate_at_char(git_buf *buf, char separator) +{ + ssize_t idx = git_buf_find(buf, separator); + if (idx >= 0) + git_buf_truncate(buf, (size_t)idx); +} + void git_buf_rtruncate_at_char(git_buf *buf, char separator) { ssize_t idx = git_buf_rfind_next(buf, separator); diff --git a/src/buffer.h b/src/buffer.h index 7faa9fdc8..a356bebea 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -171,6 +171,7 @@ void git_buf_consume_bytes(git_buf *buf, size_t len); void git_buf_consume(git_buf *buf, const char *end); void git_buf_truncate(git_buf *buf, size_t len); void git_buf_shorten(git_buf *buf, size_t amount); +void git_buf_truncate_at_char(git_buf *buf, char separator); void git_buf_rtruncate_at_char(git_buf *path, char separator); /** General join with separator */ diff --git a/src/diff.c b/src/diff.c index 4085c5f78..30b9f647a 100644 --- a/src/diff.c +++ b/src/diff.c @@ -7,11 +7,15 @@ #include "diff.h" -#include "git2/version.h" -#include "diff_generate.h" +#include "common.h" #include "patch.h" +#include "email.h" #include "commit.h" #include "index.h" +#include "diff_generate.h" + +#include "git2/version.h" +#include "git2/email.h" struct patch_id_args { git_hash_ctx ctx; @@ -150,97 +154,14 @@ int git_diff_foreach( return error; } -static int diff_format_email_append_header_tobuf( - git_buf *out, - const git_oid *id, - const git_signature *author, - const char *summary, - const char *body, - 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); - - if (body) { - git_buf_puts(out, body); - - if (out->ptr[out->size - 1] != '\n') - git_buf_putc(out, '\n'); - } - - return error; -} - -static int 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; -} +#ifndef GIT_DEPRECATE_HARD 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; - size_t allocsize; + git_email_create_options email_create_opts = GIT_EMAIL_CREATE_OPTIONS_INIT; int error; GIT_ASSERT_ARG(out); @@ -251,64 +172,13 @@ int git_diff_format_email( GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options"); - ignore_marker = (opts->flags & - GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0; - - if (!ignore_marker) { - if (opts->patch_no > opts->total_patches) { - git_error_set(GIT_ERROR_INVALID, - "patch %"PRIuZ" out of range. max %"PRIuZ, - opts->patch_no, opts->total_patches); - return -1; - } + if ((opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0) + email_create_opts.subject_prefix = ""; - if (opts->patch_no == 0) { - git_error_set(GIT_ERROR_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) { - git_error_set(GIT_ERROR_INVALID, "summary is empty"); - error = -1; - goto on_error; - } - - GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, offset, 1); - summary = git__calloc(allocsize, sizeof(char)); - GIT_ERROR_CHECK_ALLOC(summary); - - strncpy(summary, opts->summary, offset); - } - - error = diff_format_email_append_header_tobuf(out, - opts->id, opts->author, summary == NULL ? opts->summary : summary, - opts->body, 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)) < 0 || - (error = git_buf_putc(out, '\n')) < 0 || - (error = 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); + error = git_email__append_from_diff(out, diff, opts->patch_no, + opts->total_patches, opts->id, opts->summary, opts->body, + opts->author, &email_create_opts); return error; } @@ -323,60 +193,43 @@ int git_diff_commit_as_email( const git_diff_options *diff_opts) { git_diff *diff = NULL; - git_diff_format_email_options opts = - GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + const git_oid *commit_id; + const char *summary, *body; + const git_signature *author; int error; GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(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.body = git_commit_body(commit); - opts.author = git_commit_author(commit); + commit_id = git_commit_id(commit); + summary = git_commit_summary(commit); + body = git_commit_body(commit); + author = git_commit_author(commit); + + if ((flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0) + opts.subject_prefix = ""; if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) return error; - error = git_diff_format_email(out, diff, &opts); + error = git_email_create_from_diff(out, diff, patch_no, total_patches, commit_id, summary, body, author, &opts); git_diff_free(diff); return error; } -int git_diff_options_init(git_diff_options *opts, unsigned int version) -{ - GIT_INIT_STRUCTURE_FROM_TEMPLATE( - opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT); - return 0; -} - -#ifndef GIT_DEPRECATE_HARD int git_diff_init_options(git_diff_options *opts, unsigned int version) { return git_diff_options_init(opts, version); } -#endif -int git_diff_find_options_init( - git_diff_find_options *opts, unsigned int version) -{ - GIT_INIT_STRUCTURE_FROM_TEMPLATE( - opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT); - return 0; -} - -#ifndef GIT_DEPRECATE_HARD int git_diff_find_init_options( git_diff_find_options *opts, unsigned int version) { return git_diff_find_options_init(opts, version); } -#endif int git_diff_format_email_options_init( git_diff_format_email_options *opts, unsigned int version) @@ -387,14 +240,29 @@ int git_diff_format_email_options_init( return 0; } -#ifndef GIT_DEPRECATE_HARD int git_diff_format_email_init_options( git_diff_format_email_options *opts, unsigned int version) { return git_diff_format_email_options_init(opts, version); } + #endif +int git_diff_options_init(git_diff_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT); + return 0; +} + +int git_diff_find_options_init( + git_diff_find_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT); + return 0; +} + static int flush_hunk(git_oid *result, git_hash_ctx *ctx) { git_oid hash; diff --git a/src/email.c b/src/email.c new file mode 100644 index 000000000..8957d9a38 --- /dev/null +++ b/src/email.c @@ -0,0 +1,300 @@ +/* + * 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 "email.h" + +#include "buffer.h" +#include "common.h" +#include "diff_generate.h" + +#include "git2/email.h" +#include "git2/patch.h" +#include "git2/version.h" + +/* + * Git uses a "magic" timestamp to indicate that an email message + * is from `git format-patch` (or our equivalent). + */ +#define EMAIL_TIMESTAMP "Mon Sep 17 00:00:00 2001" + +GIT_INLINE(int) include_prefix( + size_t patch_count, + git_email_create_options *opts) +{ + return ((!opts->subject_prefix || *opts->subject_prefix) || + (opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 || + opts->reroll_number || + (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))); +} + +static int append_prefix( + git_buf *out, + size_t patch_idx, + size_t patch_count, + git_email_create_options *opts) +{ + const char *subject_prefix = opts->subject_prefix ? + opts->subject_prefix : "PATCH"; + + git_buf_putc(out, '['); + + if (*subject_prefix) + git_buf_puts(out, subject_prefix); + + if (opts->reroll_number) { + if (*subject_prefix) + git_buf_putc(out, ' '); + + git_buf_printf(out, "v%" PRIuZ, opts->reroll_number); + } + + if ((opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 || + (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))) { + size_t start_number = opts->start_number ? + opts->start_number : 1; + + if (*subject_prefix || opts->reroll_number) + git_buf_putc(out, ' '); + + git_buf_printf(out, "%" PRIuZ "/%" PRIuZ, + patch_idx + (start_number - 1), + patch_count + (start_number - 1)); + } + + git_buf_puts(out, "]"); + + return git_buf_oom(out) ? -1 : 0; +} + +static int append_subject( + git_buf *out, + size_t patch_idx, + size_t patch_count, + const char *summary, + git_email_create_options *opts) +{ + bool prefix = include_prefix(patch_count, opts); + size_t summary_len = summary ? strlen(summary) : 0; + int error; + + if (summary_len) { + const char *nl = strchr(summary, '\n'); + + if (nl) + summary_len = (nl - summary); + } + + if ((error = git_buf_puts(out, "Subject: ")) < 0) + return error; + + if (prefix && + (error = append_prefix(out, patch_idx, patch_count, opts)) < 0) + return error; + + if (prefix && summary_len && (error = git_buf_putc(out, ' ')) < 0) + return error; + + if (summary_len && + (error = git_buf_put(out, summary, summary_len)) < 0) + return error; + + return git_buf_putc(out, '\n'); +} + +static int append_header( + git_buf *out, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const git_signature *author, + git_email_create_options *opts) +{ + char id[GIT_OID_HEXSZ]; + char date[GIT_DATE_RFC2822_SZ]; + int error; + + if ((error = git_oid_fmt(id, commit_id)) < 0 || + (error = git_buf_printf(out, "From %.*s %s\n", GIT_OID_HEXSZ, id, EMAIL_TIMESTAMP)) < 0 || + (error = git_buf_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 || + (error = git__date_rfc2822_fmt(date, sizeof(date), &author->when)) < 0 || + (error = git_buf_printf(out, "Date: %s\n", date)) < 0 || + (error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0) + return error; + + if ((error = git_buf_putc(out, '\n')) < 0) + return error; + + return 0; +} + +static int append_body(git_buf *out, const char *body) +{ + size_t body_len; + int error; + + if (!body) + return 0; + + body_len = strlen(body); + + if ((error = git_buf_puts(out, body)) < 0) + return error; + + if (body_len && body[body_len - 1] != '\n') + error = git_buf_putc(out, '\n'); + + return error; +} + +static int append_diffstat(git_buf *out, git_diff *diff) +{ + git_diff_stats *stats = NULL; + unsigned int format_flags; + int error; + + format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY; + + if ((error = git_diff_get_stats(&stats, diff)) == 0 && + (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) == 0) + error = git_buf_putc(out, '\n'); + + git_diff_stats_free(stats); + return error; +} + +static int append_patches(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_email__append_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *given_opts) +{ + git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + int error; + + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(diff); + GIT_ASSERT_ARG(!patch_idx || patch_idx <= patch_count); + GIT_ASSERT_ARG(commit_id); + GIT_ASSERT_ARG(author); + + GIT_ERROR_CHECK_VERSION(given_opts, + GIT_EMAIL_CREATE_OPTIONS_VERSION, + "git_email_create_options"); + + if (given_opts) + memcpy(&opts, given_opts, sizeof(git_email_create_options)); + + git_buf_sanitize(out); + git_buf_clear(out); + + if ((error = append_header(out, patch_idx, patch_count, commit_id, summary, author, &opts)) == 0 && + (error = append_body(out, body)) == 0 && + (error = git_buf_puts(out, "---\n")) == 0 && + (error = append_diffstat(out, diff)) == 0 && + (error = append_patches(out, diff)) == 0) + error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n"); + + return error; +} + +int git_email_create_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *given_opts) +{ + int error; + + git_buf_sanitize(out); + git_buf_clear(out); + + error = git_email__append_from_diff(out, diff, patch_idx, + patch_count, commit_id, summary, body, author, + given_opts); + + return error; +} + +int git_email_create_from_commit( + git_buf *out, + git_commit *commit, + const git_email_create_options *given_opts) +{ + git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + git_diff *diff = NULL; + git_repository *repo; + git_diff_options *diff_opts; + git_diff_find_options *find_opts; + const git_signature *author; + const char *summary, *body; + const git_oid *commit_id; + int error = -1; + + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(commit); + + GIT_ERROR_CHECK_VERSION(given_opts, + GIT_EMAIL_CREATE_OPTIONS_VERSION, + "git_email_create_options"); + + if (given_opts) + memcpy(&opts, given_opts, sizeof(git_email_create_options)); + + repo = git_commit_owner(commit); + author = git_commit_author(commit); + summary = git_commit_summary(commit); + body = git_commit_body(commit); + commit_id = git_commit_id(commit); + diff_opts = &opts.diff_opts; + find_opts = &opts.diff_find_opts; + + if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) + goto done; + + if ((opts.flags & GIT_EMAIL_CREATE_NO_RENAMES) == 0 && + (error = git_diff_find_similar(diff, find_opts)) < 0) + goto done; + + error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, &opts); + +done: + git_diff_free(diff); + return error; +} diff --git a/src/email.h b/src/email.h new file mode 100644 index 000000000..7aeb462ab --- /dev/null +++ b/src/email.h @@ -0,0 +1,25 @@ +/* + * 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. + */ +#ifndef INCLUDE_email_h__ +#define INCLUDE_email_h__ + +#include "common.h" + +#include "git2/email.h" + +extern int git_email__append_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *given_opts); + +#endif diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c index bdfc4cac3..ea7aa070f 100644 --- a/tests/diff/format_email.c +++ b/tests/diff/format_email.c @@ -18,6 +18,7 @@ void test_diff_format_email__cleanup(void) cl_git_sandbox_cleanup(); } +#ifndef GIT_DEPRECATE_HARD static void assert_email_match( const char *expected, const char *oidstr, @@ -51,9 +52,11 @@ static void assert_email_match( git_commit_free(commit); git_buf_dispose(&buf); } +#endif void test_diff_format_email__simple(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ @@ -96,10 +99,12 @@ void test_diff_format_email__simple(void) assert_email_match( email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +#endif } void test_diff_format_email__with_message(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \ "From: Patrick Steinhardt <ps@pks.im>\n" \ @@ -136,11 +141,13 @@ void test_diff_format_email__with_message(void) assert_email_match( email, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270", &opts); +#endif } void test_diff_format_email__multiple(void) { +#ifndef GIT_DEPRECATE_HARD git_oid oid; git_commit *commit = NULL; git_diff *diff = NULL; @@ -256,10 +263,12 @@ void test_diff_format_email__multiple(void) git_diff_free(diff); git_commit_free(commit); git_buf_dispose(&buf); +#endif } void test_diff_format_email__exclude_marker(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ @@ -304,10 +313,12 @@ void test_diff_format_email__exclude_marker(void) assert_email_match( email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +#endif } void test_diff_format_email__invalid_no(void) { +#ifndef GIT_DEPRECATE_HARD git_oid oid; git_commit *commit = NULL; git_diff *diff = NULL; @@ -327,15 +338,16 @@ void test_diff_format_email__invalid_no(void) 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_dispose(&buf); +#endif } void test_diff_format_email__mode_change(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \ @@ -357,10 +369,12 @@ void test_diff_format_email__mode_change(void) assert_email_match( email, "7ade76dd34bba4733cf9878079f9fd4a456a9189", &opts); +#endif } void test_diff_format_email__rename_add_remove(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \ @@ -427,10 +441,12 @@ void test_diff_format_email__rename_add_remove(void) assert_email_match( email, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts); +#endif } void test_diff_format_email__multiline_summary(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ @@ -475,10 +491,12 @@ void test_diff_format_email__multiline_summary(void) assert_email_match( email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +#endif } void test_diff_format_email__binary(void) { +#ifndef GIT_DEPRECATE_HARD git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; const char *email = "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \ @@ -501,5 +519,6 @@ void test_diff_format_email__binary(void) assert_email_match( email, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts); +#endif } diff --git a/tests/email/create.c b/tests/email/create.c new file mode 100644 index 000000000..ccf79c2aa --- /dev/null +++ b/tests/email/create.c @@ -0,0 +1,363 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "diff_generate.h" + +static git_repository *repo; + +void test_email_create__initialize(void) +{ + repo = cl_git_sandbox_init("diff_format_email"); +} + +void test_email_create__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void email_for_commit( + git_buf *out, + const char *commit_id, + git_email_create_options *opts) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + + git_oid_fromstr(&oid, commit_id); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + cl_git_pass(git_email_create_from_commit(out, commit, opts)); + + git_diff_free(diff); + git_commit_free(commit); +} + +static void assert_email_match( + const char *expected, + const char *commit_id, + git_email_create_options *opts) +{ + git_buf buf = GIT_BUF_INIT; + + email_for_commit(&buf, commit_id, opts); + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + + git_buf_dispose(&buf); +} + +static void assert_subject_match( + const char *expected, + const char *commit_id, + git_email_create_options *opts) +{ + git_buf buf = GIT_BUF_INIT; + const char *loc; + + email_for_commit(&buf, commit_id, opts); + + cl_assert((loc = strstr(buf.ptr, "\nSubject: ")) != NULL); + git_buf_consume(&buf, (loc + 10)); + git_buf_truncate_at_char(&buf, '\n'); + + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + + git_buf_dispose(&buf); +} + +void test_email_create__commit(void) +{ + const char *expected = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "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"; + + assert_email_match( + expected, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL); +} + +void test_email_create__rename(void) +{ + const char *expected = + "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \ + "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \ + "\n" \ + "---\n" \ + " file1.txt => file1.txt.renamed | 4 ++--\n" \ + " 1 file changed, 2 insertions(+), 2 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt.renamed\n" \ + "similarity index 86%\n" \ + "rename from file1.txt\n" \ + "rename to file1.txt.renamed\n" \ + "index af8f41d..a97157a 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt.renamed\n" \ + "@@ -3,13 +3,13 @@ file1.txt\n" \ + " _file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "+file1.txt_renamed\n" \ + " file1.txt\n" \ + " \n" \ + " \n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "+file1.txt_renamed\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " _file1.txt_\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", NULL); +} + +void test_email_create__rename_as_add_delete(void) +{ + const char *expected = + "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \ + "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \ + "\n" \ + "---\n" \ + " file1.txt | 17 -----------------\n" \ + " file1.txt.renamed | 17 +++++++++++++++++\n" \ + " 2 files changed, 17 insertions(+), 17 deletions(-)\n" \ + " delete mode 100644 file1.txt\n" \ + " create mode 100644 file1.txt.renamed\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "deleted file mode 100644\n" \ + "index af8f41d..0000000\n" \ + "--- a/file1.txt\n" \ + "+++ /dev/null\n" \ + "@@ -1,17 +0,0 @@\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-_file1.txt_\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-\n" \ + "-\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-_file1.txt_\n" \ + "-_file1.txt_\n" \ + "-file1.txt\n" \ + "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \ + "new file mode 100644\n" \ + "index 0000000..a97157a\n" \ + "--- /dev/null\n" \ + "+++ b/file1.txt.renamed\n" \ + "@@ -0,0 +1,17 @@\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+_file1.txt_\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+file1.txt_renamed\n" \ + "+file1.txt\n" \ + "+\n" \ + "+\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+file1.txt_renamed\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + "+file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + opts.flags |= GIT_EMAIL_CREATE_NO_RENAMES; + + assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts); +} + +void test_email_create__binary(void) +{ + const char *expected = + "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 -> 5 bytes\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + "\n" \ + "diff --git a/binary.bin b/binary.bin\n" \ + "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \ + "GIT binary patch\n" \ + "literal 5\n" \ + "Mc${NkU}WL~000&M4gdfE\n" \ + "\n" \ + "literal 3\n" \ + "Kc${Nk-~s>u4FC%O\n" \ + "\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + assert_email_match(expected, "8d7523f6fcb2404257889abe0d96f093d9f524f9", NULL); +} + +void test_email_create__binary_not_included(void) +{ + const char *expected = + "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 -> 5 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_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + opts.diff_opts.flags &= ~GIT_DIFF_SHOW_BINARY; + + assert_email_match(expected, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts); +} + +void test_email_create__custom_summary_and_body(void) +{ + const char *expected = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \ + "From: Patrick Steinhardt <ps@pks.im>\n" \ + "Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \ + "Subject: [PPPPPATCH 2/4] This is a subject\n" \ + "\n" \ + "Modify content of file3.txt by appending a new line. Make this\n" \ + "commit message somewhat longer to test behavior with newlines\n" \ + "embedded in the message body.\n" \ + "\n" \ + "Also test if new paragraphs are included correctly.\n" \ + "---\n" \ + " file3.txt | 1 +\n" \ + " 1 file changed, 1 insertion(+)\n" \ + "\n" \ + "diff --git a/file3.txt b/file3.txt\n" \ + "index 9a2d780..7309653 100644\n" \ + "--- a/file3.txt\n" \ + "+++ b/file3.txt\n" \ + "@@ -3,3 +3,4 @@ file3!\n" \ + " file3\n" \ + " file3\n" \ + " file3\n" \ + "+file3\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + const char *summary = "This is a subject\nwith\nnewlines"; + const char *body = "Modify content of file3.txt by appending a new line. Make this\n" \ + "commit message somewhat longer to test behavior with newlines\n" \ + "embedded in the message body.\n" \ + "\n" \ + "Also test if new paragraphs are included correctly."; + + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_buf buf = GIT_BUF_INIT; + git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + + opts.subject_prefix = "PPPPPATCH"; + + git_oid_fromstr(&oid, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270"); + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_email_create_from_diff(&buf, diff, 2, 4, &oid, summary, body, git_commit_author(commit), &opts)); + + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_dispose(&buf); +} + +void test_email_create__commit_subjects(void) +{ + git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT; + + assert_subject_match("[PATCH] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.reroll_number = 42; + assert_subject_match("[PATCH v42] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.flags |= GIT_EMAIL_CREATE_ALWAYS_NUMBER; + assert_subject_match("[PATCH v42 1/1] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.start_number = 9; + assert_subject_match("[PATCH v42 9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.subject_prefix = ""; + assert_subject_match("[v42 9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.reroll_number = 0; + assert_subject_match("[9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.start_number = 0; + assert_subject_match("[1/1] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); + + opts.flags = GIT_EMAIL_CREATE_OMIT_NUMBERS; + assert_subject_match("Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +} |