File Coverage

deps/libgit2/src/libgit2/email.c
Criterion Covered Total %
statement 91 132 68.9
branch 61 132 46.2
condition n/a
subroutine n/a
pod n/a
total 152 264 57.5


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