Unverified Commit ba01547d by Edward Thomson Committed by GitHub

Merge pull request #6061 from libgit2/ethomson/email

Introduce `git_email_create`; deprecate `git_diff_format_email`
parents 0a872862 ba3595af
......@@ -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"
......
......@@ -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
......
......@@ -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
......
/*
* 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
/*
* 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
......@@ -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);
......
......@@ -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 */
......
......@@ -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;
......
/*
* 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;
}
/*
* 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
......@@ -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
}
#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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment