]> git.proxmox.com Git - libgit2.git/blame - src/email.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / email.c
CommitLineData
c25aa7cd
PP
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
8#include "email.h"
9
c25aa7cd 10#include "common.h"
e579e0f7 11#include "buf.h"
c25aa7cd 12#include "diff_generate.h"
e579e0f7
MB
13#include "diff_stats.h"
14#include "patch.h"
15#include "date.h"
c25aa7cd
PP
16
17#include "git2/email.h"
18#include "git2/patch.h"
19#include "git2/version.h"
20
21/*
22 * Git uses a "magic" timestamp to indicate that an email message
23 * is from `git format-patch` (or our equivalent).
24 */
25#define EMAIL_TIMESTAMP "Mon Sep 17 00:00:00 2001"
26
27GIT_INLINE(int) include_prefix(
28 size_t patch_count,
29 git_email_create_options *opts)
30{
31 return ((!opts->subject_prefix || *opts->subject_prefix) ||
32 (opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 ||
33 opts->reroll_number ||
34 (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS)));
35}
36
37static int append_prefix(
e579e0f7 38 git_str *out,
c25aa7cd
PP
39 size_t patch_idx,
40 size_t patch_count,
41 git_email_create_options *opts)
42{
43 const char *subject_prefix = opts->subject_prefix ?
44 opts->subject_prefix : "PATCH";
45
e579e0f7 46 git_str_putc(out, '[');
c25aa7cd
PP
47
48 if (*subject_prefix)
e579e0f7 49 git_str_puts(out, subject_prefix);
c25aa7cd
PP
50
51 if (opts->reroll_number) {
52 if (*subject_prefix)
e579e0f7 53 git_str_putc(out, ' ');
c25aa7cd 54
e579e0f7 55 git_str_printf(out, "v%" PRIuZ, opts->reroll_number);
c25aa7cd
PP
56 }
57
58 if ((opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 ||
59 (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))) {
60 size_t start_number = opts->start_number ?
61 opts->start_number : 1;
62
63 if (*subject_prefix || opts->reroll_number)
e579e0f7 64 git_str_putc(out, ' ');
c25aa7cd 65
e579e0f7 66 git_str_printf(out, "%" PRIuZ "/%" PRIuZ,
c25aa7cd
PP
67 patch_idx + (start_number - 1),
68 patch_count + (start_number - 1));
69 }
70
e579e0f7 71 git_str_puts(out, "]");
c25aa7cd 72
e579e0f7
MB
73 return git_str_oom(out) ? -1 : 0;
74}
75
76static int append_date(
77 git_str *out,
78 const git_time *date)
79{
80 int error;
81
82 if ((error = git_str_printf(out, "Date: ")) == 0 &&
83 (error = git_date_rfc2822_fmt(out, date->time, date->offset)) == 0)
84 error = git_str_putc(out, '\n');
85
86 return error;
c25aa7cd
PP
87}
88
89static int append_subject(
e579e0f7 90 git_str *out,
c25aa7cd
PP
91 size_t patch_idx,
92 size_t patch_count,
93 const char *summary,
94 git_email_create_options *opts)
95{
96 bool prefix = include_prefix(patch_count, opts);
97 size_t summary_len = summary ? strlen(summary) : 0;
98 int error;
99
100 if (summary_len) {
101 const char *nl = strchr(summary, '\n');
102
103 if (nl)
104 summary_len = (nl - summary);
105 }
106
e579e0f7 107 if ((error = git_str_puts(out, "Subject: ")) < 0)
c25aa7cd
PP
108 return error;
109
110 if (prefix &&
111 (error = append_prefix(out, patch_idx, patch_count, opts)) < 0)
112 return error;
113
e579e0f7 114 if (prefix && summary_len && (error = git_str_putc(out, ' ')) < 0)
c25aa7cd
PP
115 return error;
116
117 if (summary_len &&
e579e0f7 118 (error = git_str_put(out, summary, summary_len)) < 0)
c25aa7cd
PP
119 return error;
120
e579e0f7 121 return git_str_putc(out, '\n');
c25aa7cd
PP
122}
123
124static int append_header(
e579e0f7 125 git_str *out,
c25aa7cd
PP
126 size_t patch_idx,
127 size_t patch_count,
128 const git_oid *commit_id,
129 const char *summary,
130 const git_signature *author,
131 git_email_create_options *opts)
132{
133 char id[GIT_OID_HEXSZ];
c25aa7cd
PP
134 int error;
135
136 if ((error = git_oid_fmt(id, commit_id)) < 0 ||
e579e0f7
MB
137 (error = git_str_printf(out, "From %.*s %s\n", GIT_OID_HEXSZ, id, EMAIL_TIMESTAMP)) < 0 ||
138 (error = git_str_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 ||
139 (error = append_date(out, &author->when)) < 0 ||
c25aa7cd
PP
140 (error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0)
141 return error;
142
e579e0f7 143 if ((error = git_str_putc(out, '\n')) < 0)
c25aa7cd
PP
144 return error;
145
146 return 0;
147}
148
e579e0f7 149static int append_body(git_str *out, const char *body)
c25aa7cd
PP
150{
151 size_t body_len;
152 int error;
153
154 if (!body)
155 return 0;
156
157 body_len = strlen(body);
158
e579e0f7 159 if ((error = git_str_puts(out, body)) < 0)
c25aa7cd
PP
160 return error;
161
162 if (body_len && body[body_len - 1] != '\n')
e579e0f7 163 error = git_str_putc(out, '\n');
c25aa7cd
PP
164
165 return error;
166}
167
e579e0f7 168static int append_diffstat(git_str *out, git_diff *diff)
c25aa7cd
PP
169{
170 git_diff_stats *stats = NULL;
171 unsigned int format_flags;
172 int error;
173
174 format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
175
176 if ((error = git_diff_get_stats(&stats, diff)) == 0 &&
e579e0f7
MB
177 (error = git_diff__stats_to_buf(out, stats, format_flags, 0)) == 0)
178 error = git_str_putc(out, '\n');
c25aa7cd
PP
179
180 git_diff_stats_free(stats);
181 return error;
182}
183
e579e0f7 184static int append_patches(git_str *out, git_diff *diff)
c25aa7cd
PP
185{
186 size_t i, deltas;
187 int error = 0;
188
189 deltas = git_diff_num_deltas(diff);
190
191 for (i = 0; i < deltas; ++i) {
192 git_patch *patch = NULL;
193
194 if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
e579e0f7 195 error = git_patch__to_buf(out, patch);
c25aa7cd
PP
196
197 git_patch_free(patch);
198
199 if (error < 0)
200 break;
201 }
202
203 return error;
204}
205
206int git_email__append_from_diff(
e579e0f7 207 git_str *out,
c25aa7cd
PP
208 git_diff *diff,
209 size_t patch_idx,
210 size_t patch_count,
211 const git_oid *commit_id,
212 const char *summary,
213 const char *body,
214 const git_signature *author,
215 const git_email_create_options *given_opts)
216{
217 git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
218 int error;
219
220 GIT_ASSERT_ARG(out);
221 GIT_ASSERT_ARG(diff);
222 GIT_ASSERT_ARG(!patch_idx || patch_idx <= patch_count);
223 GIT_ASSERT_ARG(commit_id);
224 GIT_ASSERT_ARG(author);
225
226 GIT_ERROR_CHECK_VERSION(given_opts,
227 GIT_EMAIL_CREATE_OPTIONS_VERSION,
228 "git_email_create_options");
229
230 if (given_opts)
231 memcpy(&opts, given_opts, sizeof(git_email_create_options));
232
c25aa7cd
PP
233 if ((error = append_header(out, patch_idx, patch_count, commit_id, summary, author, &opts)) == 0 &&
234 (error = append_body(out, body)) == 0 &&
e579e0f7 235 (error = git_str_puts(out, "---\n")) == 0 &&
c25aa7cd
PP
236 (error = append_diffstat(out, diff)) == 0 &&
237 (error = append_patches(out, diff)) == 0)
e579e0f7 238 error = git_str_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
c25aa7cd
PP
239
240 return error;
241}
242
243int git_email_create_from_diff(
244 git_buf *out,
245 git_diff *diff,
246 size_t patch_idx,
247 size_t patch_count,
248 const git_oid *commit_id,
249 const char *summary,
250 const char *body,
251 const git_signature *author,
252 const git_email_create_options *given_opts)
253{
e579e0f7 254 git_str email = GIT_STR_INIT;
c25aa7cd
PP
255 int error;
256
e579e0f7 257 git_buf_tostr(&email, out);
c25aa7cd 258
e579e0f7 259 error = git_email__append_from_diff(&email, diff, patch_idx,
c25aa7cd
PP
260 patch_count, commit_id, summary, body, author,
261 given_opts);
262
e579e0f7
MB
263 if (error == 0)
264 error = git_buf_fromstr(out, &email);
265
266 git_str_dispose(&email);
c25aa7cd
PP
267 return error;
268}
269
270int git_email_create_from_commit(
271 git_buf *out,
272 git_commit *commit,
273 const git_email_create_options *given_opts)
274{
275 git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
276 git_diff *diff = NULL;
277 git_repository *repo;
278 git_diff_options *diff_opts;
279 git_diff_find_options *find_opts;
280 const git_signature *author;
281 const char *summary, *body;
282 const git_oid *commit_id;
283 int error = -1;
284
285 GIT_ASSERT_ARG(out);
286 GIT_ASSERT_ARG(commit);
287
288 GIT_ERROR_CHECK_VERSION(given_opts,
289 GIT_EMAIL_CREATE_OPTIONS_VERSION,
290 "git_email_create_options");
291
292 if (given_opts)
293 memcpy(&opts, given_opts, sizeof(git_email_create_options));
294
295 repo = git_commit_owner(commit);
296 author = git_commit_author(commit);
297 summary = git_commit_summary(commit);
298 body = git_commit_body(commit);
299 commit_id = git_commit_id(commit);
300 diff_opts = &opts.diff_opts;
301 find_opts = &opts.diff_find_opts;
302
303 if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
304 goto done;
305
306 if ((opts.flags & GIT_EMAIL_CREATE_NO_RENAMES) == 0 &&
307 (error = git_diff_find_similar(diff, find_opts)) < 0)
308 goto done;
309
310 error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, &opts);
311
312done:
313 git_diff_free(diff);
314 return error;
315}