Skip to content

Commit 6aa3496

Browse files
committed
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.
1 parent 75d4676 commit 6aa3496

File tree

4 files changed

+195
-53
lines changed

4 files changed

+195
-53
lines changed

include/git2/email.h

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,30 +73,39 @@ typedef struct {
7373

7474
/**
7575
* Create a diff for a commit in mbox format for sending via email.
76-
* The commit must not be a merge commit.
7776
*
7877
* @param out buffer to store the e-mail patch in
79-
* @param commit commit to create a patch for
78+
* @param diff the changes to include in the email
79+
* @param patch_idx the patch index
80+
* @param patch_count the total number of patches that will be included
81+
* @param commit_id the commit id for this change
82+
* @param summary the commit message for this change
83+
* @param body optional text to include above the diffstat
84+
* @param author the person who authored this commit
8085
* @param opts email creation options
8186
*/
82-
GIT_EXTERN(int) git_email_create_from_commit(
87+
GIT_EXTERN(int) git_email_create_from_diff(
8388
git_buf *out,
84-
git_commit *commit,
89+
git_diff *diff,
90+
size_t patch_idx,
91+
size_t patch_count,
92+
const git_oid *commit_id,
93+
const char *summary,
94+
const char *body,
95+
const git_signature *author,
8596
const git_email_create_options *opts);
8697

8798
/**
88-
* Create an mbox format diff for the given commits in the revision
89-
* spec, excluding merge commits.
99+
* Create a diff for a commit in mbox format for sending via email.
100+
* The commit must not be a merge commit.
90101
*
91-
* @param out buffer to store the e-mail patches in
92-
* @param commits the array of commits to create patches for
93-
* @param len the length of the `commits` array
102+
* @param out buffer to store the e-mail patch in
103+
* @param commit commit to create a patch for
94104
* @param opts email creation options
95105
*/
96-
GIT_EXTERN(int) git_email_create_from_commits(
97-
git_strarray *out,
98-
git_commit **commits,
99-
size_t len,
106+
GIT_EXTERN(int) git_email_create_from_commit(
107+
git_buf *out,
108+
git_commit *commit,
100109
const git_email_create_options *opts);
101110

102111
GIT_END_DECL

include/git2/sys/email.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
#ifndef INCLUDE_sys_git_email_h__
8+
#define INCLUDE_sys_git_email_h__
9+
10+
/**
11+
* @file git2/sys/email.h
12+
* @brief Advanced git email creation routines
13+
* @defgroup git_email Advanced git email creation routines
14+
* @ingroup Git
15+
* @{
16+
*/
17+
GIT_BEGIN_DECL
18+
19+
/**
20+
* Create a diff for a commit in mbox format for sending via email.
21+
*
22+
* @param out buffer to store the e-mail patch in
23+
* @param diff the changes to include in the email
24+
* @param patch_idx the patch index
25+
* @param patch_count the total number of patches that will be included
26+
* @param commit_id the commit id for this change
27+
* @param summary the commit message for this change
28+
* @param body optional text to include above the diffstat
29+
* @param author the person who authored this commit
30+
* @param opts email creation options
31+
*/
32+
GIT_EXTERN(int) git_email_create_from_diff(
33+
git_buf *out,
34+
git_diff *diff,
35+
size_t patch_idx,
36+
size_t patch_count,
37+
const git_oid *commit_id,
38+
const char *summary,
39+
const char *body,
40+
const git_signature *author,
41+
const git_email_create_options *opts);
42+
43+
/** @} */
44+
GIT_END_DECL
45+
#endif

src/email.c

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ static int append_prefix(
3838
const char *subject_prefix = opts->subject_prefix ?
3939
opts->subject_prefix : "PATCH";
4040

41-
if (!include_prefix(patch_count, opts))
42-
return 0;
43-
4441
git_buf_putc(out, '[');
4542

4643
if (*subject_prefix)
@@ -66,47 +63,65 @@ static int append_prefix(
6663
patch_count + (start_number - 1));
6764
}
6865

69-
git_buf_puts(out, "] ");
66+
git_buf_puts(out, "]");
7067

7168
return git_buf_oom(out) ? -1 : 0;
7269
}
7370

7471
static int append_subject(
7572
git_buf *out,
76-
git_commit *commit,
7773
size_t patch_idx,
7874
size_t patch_count,
75+
const char *summary,
7976
git_email_create_options *opts)
8077
{
78+
bool prefix = include_prefix(patch_count, opts);
79+
size_t summary_len = summary ? strlen(summary) : 0;
8180
int error;
8281

83-
if ((error = git_buf_puts(out, "Subject: ")) < 0 ||
84-
(error = append_prefix(out, patch_idx, patch_count, opts)) < 0 ||
85-
(error = git_buf_puts(out, git_commit_summary(commit))) < 0 ||
86-
(error = git_buf_putc(out, '\n')) < 0)
82+
if (summary_len) {
83+
const char *nl = strchr(summary, '\n');
84+
85+
if (nl)
86+
summary_len = (nl - summary);
87+
}
88+
89+
if ((error = git_buf_puts(out, "Subject: ")) < 0)
8790
return error;
8891

89-
return 0;
92+
if (prefix &&
93+
(error = append_prefix(out, patch_idx, patch_count, opts)) < 0)
94+
return error;
95+
96+
if (prefix && summary_len && (error = git_buf_putc(out, ' ')) < 0)
97+
return error;
98+
99+
if (summary_len &&
100+
(error = git_buf_put(out, summary, summary_len)) < 0)
101+
return error;
102+
103+
return git_buf_putc(out, '\n');
90104
}
91105

92106
static int append_header(
93107
git_buf *out,
94-
git_commit *commit,
95108
size_t patch_idx,
96109
size_t patch_count,
110+
const git_oid *commit_id,
111+
const char *summary,
112+
const git_signature *author,
97113
git_email_create_options *opts)
98114
{
99-
const git_signature *author = git_commit_author(commit);
100115
char id[GIT_OID_HEXSZ];
101116
char date[GIT_DATE_RFC2822_SZ];
102117
int error;
103118

104-
if ((error = git_oid_fmt(id, git_commit_id(commit))) < 0 ||
119+
if ((error = git_oid_fmt(id, commit_id)) < 0 ||
105120
(error = git_buf_printf(out, "From %.*s %s\n", GIT_OID_HEXSZ, id, EMAIL_TIMESTAMP)) < 0 ||
106121
(error = git_buf_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 ||
107122
(error = git__date_rfc2822_fmt(date, sizeof(date), &author->when)) < 0 ||
108123
(error = git_buf_printf(out, "Date: %s\n", date)) < 0 ||
109-
(error = append_subject(out, commit, patch_idx, patch_count, opts)) < 0)
124+
(error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0)
110125
return error;
111126

112127
if ((error = git_buf_putc(out, '\n')) < 0)
@@ -115,9 +130,8 @@ static int append_header(
115130
return 0;
116131
}
117132

118-
static int append_body(git_buf *out, git_commit *commit)
133+
static int append_body(git_buf *out, const char *body)
119134
{
120-
const char *body = git_commit_body(commit);
121135
size_t body_len;
122136
int error;
123137

@@ -173,18 +187,25 @@ static int append_patches(git_buf *out, git_diff *diff)
173187
return error;
174188
}
175189

176-
int git_email_create_from_commit(
190+
int git_email_create_from_diff(
177191
git_buf *out,
178-
git_commit *commit,
192+
git_diff *diff,
193+
size_t patch_idx,
194+
size_t patch_count,
195+
const git_oid *commit_id,
196+
const char *summary,
197+
const char *body,
198+
const git_signature *author,
179199
const git_email_create_options *given_opts)
180200
{
181-
git_diff *diff = NULL;
182201
git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
183-
git_repository *repo;
184-
int error = 0;
202+
int error;
185203

186204
GIT_ASSERT_ARG(out);
187-
GIT_ASSERT_ARG(commit);
205+
GIT_ASSERT_ARG(diff);
206+
GIT_ASSERT_ARG(!patch_idx || patch_idx <= patch_count);
207+
GIT_ASSERT_ARG(commit_id);
208+
GIT_ASSERT_ARG(author);
188209

189210
GIT_ERROR_CHECK_VERSION(given_opts,
190211
GIT_EMAIL_CREATE_OPTIONS_VERSION,
@@ -196,16 +217,49 @@ int git_email_create_from_commit(
196217
git_buf_sanitize(out);
197218
git_buf_clear(out);
198219

199-
repo = git_commit_owner(commit);
200-
201-
if ((error = git_diff__commit(&diff, repo, commit, &opts.diff_opts)) == 0 &&
202-
(error = append_header(out, commit, 1, 1, &opts)) == 0 &&
203-
(error = append_body(out, commit)) == 0 &&
220+
if ((error = append_header(out, patch_idx, patch_count, commit_id, summary, author, &opts)) == 0 &&
221+
(error = append_body(out, body)) == 0 &&
204222
(error = git_buf_puts(out, "---\n")) == 0 &&
205223
(error = append_diffstat(out, diff)) == 0 &&
206224
(error = append_patches(out, diff)) == 0)
207225
error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
208226

227+
return error;
228+
}
229+
230+
int git_email_create_from_commit(
231+
git_buf *out,
232+
git_commit *commit,
233+
const git_email_create_options *opts)
234+
{
235+
const git_diff_options *diff_opts;
236+
git_diff *diff = NULL;
237+
git_repository *repo;
238+
const git_signature *author;
239+
const char *summary, *body;
240+
const git_oid *commit_id;
241+
int error = -1;
242+
243+
GIT_ASSERT_ARG(out);
244+
GIT_ASSERT_ARG(commit);
245+
246+
GIT_ERROR_CHECK_VERSION(opts,
247+
GIT_EMAIL_CREATE_OPTIONS_VERSION,
248+
"git_email_create_options");
249+
250+
repo = git_commit_owner(commit);
251+
author = git_commit_author(commit);
252+
summary = git_commit_summary(commit);
253+
body = git_commit_body(commit);
254+
commit_id = git_commit_id(commit);
255+
diff_opts = opts ? &opts->diff_opts : NULL;
256+
257+
if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
258+
goto done;
259+
260+
error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, opts);
261+
262+
done:
209263
git_diff_free(diff);
210264
return error;
211265
}

tests/email/create.c

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "clar_libgit2.h"
33

44
#include "buffer.h"
5+
#include "diff_generate.h"
56

67
static git_repository *repo;
78

@@ -111,27 +112,60 @@ void test_email_create__commit(void)
111112
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL);
112113
}
113114

114-
void test_email_create__mode_change(void)
115+
void test_email_create__custom_summary_and_body(void)
115116
{
116-
const char *expected =
117-
"From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \
118-
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
119-
"Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \
120-
"Subject: [PATCH] Update permissions\n" \
117+
const char *expected = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \
118+
"From: Patrick Steinhardt <ps@pks.im>\n" \
119+
"Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \
120+
"Subject: [PPPPPATCH 2/4] This is a subject\n" \
121+
"\n" \
122+
"Modify content of file3.txt by appending a new line. Make this\n" \
123+
"commit message somewhat longer to test behavior with newlines\n" \
124+
"embedded in the message body.\n" \
121125
"\n" \
126+
"Also test if new paragraphs are included correctly.\n" \
122127
"---\n" \
123-
" file1.txt.renamed | 0\n" \
124-
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
125-
" mode change 100644 => 100755 file1.txt.renamed\n" \
128+
" file3.txt | 1 +\n" \
129+
" 1 file changed, 1 insertion(+)\n" \
126130
"\n" \
127-
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
128-
"old mode 100644\n" \
129-
"new mode 100755\n" \
131+
"diff --git a/file3.txt b/file3.txt\n" \
132+
"index 9a2d780..7309653 100644\n" \
133+
"--- a/file3.txt\n" \
134+
"+++ b/file3.txt\n" \
135+
"@@ -3,3 +3,4 @@ file3!\n" \
136+
" file3\n" \
137+
" file3\n" \
138+
" file3\n" \
139+
"+file3\n" \
130140
"--\n" \
131141
"libgit2 " LIBGIT2_VERSION "\n" \
132142
"\n";
133143

134-
assert_email_match(expected, "7ade76dd34bba4733cf9878079f9fd4a456a9189", NULL);
144+
const char *summary = "This is a subject\nwith\nnewlines";
145+
const char *body = "Modify content of file3.txt by appending a new line. Make this\n" \
146+
"commit message somewhat longer to test behavior with newlines\n" \
147+
"embedded in the message body.\n" \
148+
"\n" \
149+
"Also test if new paragraphs are included correctly.";
150+
151+
git_oid oid;
152+
git_commit *commit = NULL;
153+
git_diff *diff = NULL;
154+
git_buf buf = GIT_BUF_INIT;
155+
git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
156+
157+
opts.subject_prefix = "PPPPPATCH";
158+
159+
git_oid_fromstr(&oid, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270");
160+
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
161+
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
162+
cl_git_pass(git_email_create_from_diff(&buf, diff, 2, 4, &oid, summary, body, git_commit_author(commit), &opts));
163+
164+
cl_assert_equal_s(expected, git_buf_cstr(&buf));
165+
166+
git_diff_free(diff);
167+
git_commit_free(commit);
168+
git_buf_dispose(&buf);
135169
}
136170

137171
void test_email_create__commit_subjects(void)

0 commit comments

Comments
 (0)