Commit 6aa34966 by Edward Thomson

email: introduce `git_email_create_from_diff`

Introduce a function to create an email from a diff and multiple inputs
about the source of the diff.

Creating an email from a diff requires many more inputs, and should be
discouraged in favor of building directly from a commit, and is thus in
the `sys` namespace.
parent 75d4676a
......@@ -73,30 +73,39 @@ typedef struct {
/**
* 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 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_commit(
GIT_EXTERN(int) git_email_create_from_diff(
git_buf *out,
git_commit *commit,
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 an mbox format diff for the given commits in the revision
* spec, excluding merge commits.
* 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 patches in
* @param commits the array of commits to create patches for
* @param len the length of the `commits` array
* @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_commits(
git_strarray *out,
git_commit **commits,
size_t len,
GIT_EXTERN(int) git_email_create_from_commit(
git_buf *out,
git_commit *commit,
const git_email_create_options *opts);
GIT_END_DECL
......
/*
* 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
......@@ -38,9 +38,6 @@ static int append_prefix(
const char *subject_prefix = opts->subject_prefix ?
opts->subject_prefix : "PATCH";
if (!include_prefix(patch_count, opts))
return 0;
git_buf_putc(out, '[');
if (*subject_prefix)
......@@ -66,47 +63,65 @@ static int append_prefix(
patch_count + (start_number - 1));
}
git_buf_puts(out, "] ");
git_buf_puts(out, "]");
return git_buf_oom(out) ? -1 : 0;
}
static int append_subject(
git_buf *out,
git_commit *commit,
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 ((error = git_buf_puts(out, "Subject: ")) < 0 ||
(error = append_prefix(out, patch_idx, patch_count, opts)) < 0 ||
(error = git_buf_puts(out, git_commit_summary(commit))) < 0 ||
(error = git_buf_putc(out, '\n')) < 0)
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;
return 0;
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,
git_commit *commit,
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)
{
const git_signature *author = git_commit_author(commit);
char id[GIT_OID_HEXSZ];
char date[GIT_DATE_RFC2822_SZ];
int error;
if ((error = git_oid_fmt(id, git_commit_id(commit))) < 0 ||
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, commit, patch_idx, patch_count, opts)) < 0)
(error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0)
return error;
if ((error = git_buf_putc(out, '\n')) < 0)
......@@ -115,9 +130,8 @@ static int append_header(
return 0;
}
static int append_body(git_buf *out, git_commit *commit)
static int append_body(git_buf *out, const char *body)
{
const char *body = git_commit_body(commit);
size_t body_len;
int error;
......@@ -173,18 +187,25 @@ static int append_patches(git_buf *out, git_diff *diff)
return error;
}
int git_email_create_from_commit(
int git_email_create_from_diff(
git_buf *out,
git_commit *commit,
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_diff *diff = NULL;
git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
git_repository *repo;
int error = 0;
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(commit);
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,
......@@ -196,16 +217,49 @@ int git_email_create_from_commit(
git_buf_sanitize(out);
git_buf_clear(out);
repo = git_commit_owner(commit);
if ((error = git_diff__commit(&diff, repo, commit, &opts.diff_opts)) == 0 &&
(error = append_header(out, commit, 1, 1, &opts)) == 0 &&
(error = append_body(out, commit)) == 0 &&
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_commit(
git_buf *out,
git_commit *commit,
const git_email_create_options *opts)
{
const git_diff_options *diff_opts;
git_diff *diff = NULL;
git_repository *repo;
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(opts,
GIT_EMAIL_CREATE_OPTIONS_VERSION,
"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 ? &opts->diff_opts : NULL;
if ((error = git_diff__commit(&diff, repo, commit, diff_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;
}
......@@ -2,6 +2,7 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "diff_generate.h"
static git_repository *repo;
......@@ -111,27 +112,60 @@ void test_email_create__commit(void)
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL);
}
void test_email_create__mode_change(void)
void test_email_create__custom_summary_and_body(void)
{
const char *expected =
"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" \
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" \
" file1.txt.renamed | 0\n" \
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
" mode change 100644 => 100755 file1.txt.renamed\n" \
" file3.txt | 1 +\n" \
" 1 file changed, 1 insertion(+)\n" \
"\n" \
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
"old mode 100644\n" \
"new mode 100755\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";
assert_email_match(expected, "7ade76dd34bba4733cf9878079f9fd4a456a9189", NULL);
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)
......
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