File Coverage

deps/libgit2/src/libgit2/diff.c
Criterion Covered Total %
statement 106 171 61.9
branch 53 114 46.4
condition n/a
subroutine n/a
pod n/a
total 159 285 55.7


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 "diff.h"
9              
10             #include "common.h"
11             #include "buf.h"
12             #include "patch.h"
13             #include "email.h"
14             #include "commit.h"
15             #include "index.h"
16             #include "diff_generate.h"
17              
18             #include "git2/version.h"
19             #include "git2/email.h"
20              
21             struct patch_id_args {
22             git_hash_ctx ctx;
23             git_oid result;
24             int first_file;
25             };
26              
27 46           GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
28             {
29 46           const char *str = delta->old_file.path;
30              
31 46 50         if (!str ||
    50          
32 46 100         delta->status == GIT_DELTA_ADDED ||
33 42 50         delta->status == GIT_DELTA_RENAMED ||
34 42           delta->status == GIT_DELTA_COPIED)
35 4           str = delta->new_file.path;
36              
37 46           return str;
38             }
39              
40 23           int git_diff_delta__cmp(const void *a, const void *b)
41             {
42 23           const git_diff_delta *da = a, *db = b;
43 23           int val = strcmp(diff_delta__path(da), diff_delta__path(db));
44 23 50         return val ? val : ((int)da->status - (int)db->status);
45             }
46              
47 0           int git_diff_delta__casecmp(const void *a, const void *b)
48             {
49 0           const git_diff_delta *da = a, *db = b;
50 0           int val = strcasecmp(diff_delta__path(da), diff_delta__path(db));
51 0 0         return val ? val : ((int)da->status - (int)db->status);
52             }
53              
54 822           int git_diff__entry_cmp(const void *a, const void *b)
55             {
56 822           const git_index_entry *entry_a = a;
57 822           const git_index_entry *entry_b = b;
58              
59 822           return strcmp(entry_a->path, entry_b->path);
60             }
61              
62 0           int git_diff__entry_icmp(const void *a, const void *b)
63             {
64 0           const git_index_entry *entry_a = a;
65 0           const git_index_entry *entry_b = b;
66              
67 0           return strcasecmp(entry_a->path, entry_b->path);
68             }
69              
70 660           void git_diff_free(git_diff *diff)
71             {
72 660 100         if (!diff)
73 307           return;
74              
75 353 100         GIT_REFCOUNT_DEC(diff, diff->free_fn);
    50          
76             }
77              
78 49           void git_diff_addref(git_diff *diff)
79             {
80 49           GIT_REFCOUNT_INC(diff);
81 49           }
82              
83 59           size_t git_diff_num_deltas(const git_diff *diff)
84             {
85 59 50         GIT_ASSERT_ARG(diff);
86 59           return diff->deltas.length;
87             }
88              
89 0           size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
90             {
91 0           size_t i, count = 0;
92             const git_diff_delta *delta;
93              
94 0 0         GIT_ASSERT_ARG(diff);
95              
96 0 0         git_vector_foreach(&diff->deltas, i, delta) {
97 0           count += (delta->status == type);
98             }
99              
100 0           return count;
101             }
102              
103 61           const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
104             {
105 61 50         GIT_ASSERT_ARG_WITH_RETVAL(diff, NULL);
106 61           return git_vector_get(&diff->deltas, idx);
107             }
108              
109 83           int git_diff_is_sorted_icase(const git_diff *diff)
110             {
111 83           return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
112             }
113              
114 0           int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
115             {
116 0 0         GIT_ASSERT_ARG(out);
117 0 0         GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
118 0           out->stat_calls = diff->perf.stat_calls;
119 0           out->oid_calculations = diff->perf.oid_calculations;
120 0           return 0;
121             }
122              
123 22           int git_diff_foreach(
124             git_diff *diff,
125             git_diff_file_cb file_cb,
126             git_diff_binary_cb binary_cb,
127             git_diff_hunk_cb hunk_cb,
128             git_diff_line_cb data_cb,
129             void *payload)
130             {
131 22           int error = 0;
132             git_diff_delta *delta;
133             size_t idx;
134              
135 22 50         GIT_ASSERT_ARG(diff);
136              
137 53 100         git_vector_foreach(&diff->deltas, idx, delta) {
138             git_patch *patch;
139              
140             /* check flags against patch status */
141 32 50         if (git_diff_delta__should_skip(&diff->opts, delta))
142 0           continue;
143              
144 32 50         if ((error = git_patch_from_diff(&patch, diff, idx)) != 0)
145 1           break;
146              
147 32           error = git_patch__invoke_callbacks(patch, file_cb, binary_cb,
148             hunk_cb, data_cb, payload);
149 32           git_patch_free(patch);
150              
151 32 100         if (error)
152 32           break;
153             }
154              
155 22           return error;
156             }
157              
158             #ifndef GIT_DEPRECATE_HARD
159              
160 0           int git_diff_format_email(
161             git_buf *out,
162             git_diff *diff,
163             const git_diff_format_email_options *opts)
164             {
165 0           git_email_create_options email_create_opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
166 0           git_str email = GIT_STR_INIT;
167             int error;
168              
169 0 0         GIT_ASSERT_ARG(out);
170 0 0         GIT_ASSERT_ARG(diff);
171 0 0         GIT_ASSERT_ARG(opts && opts->summary && opts->id && opts->author);
    0          
    0          
    0          
172              
173 0 0         GIT_ERROR_CHECK_VERSION(opts,
174             GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION,
175             "git_format_email_options");
176              
177             /* This is a `git_buf` special case; subsequent calls append. */
178 0           email.ptr = out->ptr;
179 0           email.asize = out->reserved;
180 0           email.size = out->size;
181              
182 0           out->ptr = git_str__initstr;
183 0           out->reserved = 0;
184 0           out->size = 0;
185              
186 0 0         if ((opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0)
187 0           email_create_opts.subject_prefix = "";
188              
189 0           error = git_email__append_from_diff(&email, diff, opts->patch_no,
190             opts->total_patches, opts->id, opts->summary, opts->body,
191             opts->author, &email_create_opts);
192              
193 0 0         if (error < 0)
194 0           goto done;
195              
196 0           error = git_buf_fromstr(out, &email);
197              
198             done:
199 0           git_str_dispose(&email);
200 0           return error;
201             }
202              
203 3           int git_diff_commit_as_email(
204             git_buf *out,
205             git_repository *repo,
206             git_commit *commit,
207             size_t patch_no,
208             size_t total_patches,
209             uint32_t flags,
210             const git_diff_options *diff_opts)
211             {
212 3           git_diff *diff = NULL;
213 3           git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
214             const git_oid *commit_id;
215             const char *summary, *body;
216             const git_signature *author;
217             int error;
218              
219 3 50         GIT_ASSERT_ARG(out);
220 3 50         GIT_ASSERT_ARG(repo);
221 3 50         GIT_ASSERT_ARG(commit);
222              
223 3           commit_id = git_commit_id(commit);
224 3           summary = git_commit_summary(commit);
225 3           body = git_commit_body(commit);
226 3           author = git_commit_author(commit);
227              
228 3 100         if ((flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0)
229 1           opts.subject_prefix = "";
230              
231 3 50         if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
232 0           return error;
233              
234 3           error = git_email_create_from_diff(out, diff, patch_no, total_patches, commit_id, summary, body, author, &opts);
235              
236 3           git_diff_free(diff);
237 3           return error;
238             }
239              
240 0           int git_diff_init_options(git_diff_options *opts, unsigned int version)
241             {
242 0           return git_diff_options_init(opts, version);
243             }
244              
245 0           int git_diff_find_init_options(
246             git_diff_find_options *opts, unsigned int version)
247             {
248 0           return git_diff_find_options_init(opts, version);
249             }
250              
251 0           int git_diff_format_email_options_init(
252             git_diff_format_email_options *opts, unsigned int version)
253             {
254 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
255             opts, version, git_diff_format_email_options,
256             GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
257 0           return 0;
258             }
259              
260 0           int git_diff_format_email_init_options(
261             git_diff_format_email_options *opts, unsigned int version)
262             {
263 0           return git_diff_format_email_options_init(opts, version);
264             }
265              
266             #endif
267              
268 1           int git_diff_options_init(git_diff_options *opts, unsigned int version)
269             {
270 1 50         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
271             opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
272 1           return 0;
273             }
274              
275 0           int git_diff_find_options_init(
276             git_diff_find_options *opts, unsigned int version)
277             {
278 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
279             opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
280 0           return 0;
281             }
282              
283 2           static int flush_hunk(git_oid *result, git_hash_ctx *ctx)
284             {
285             git_oid hash;
286 2           unsigned short carry = 0;
287             int error, i;
288              
289 2 50         if ((error = git_hash_final(hash.id, ctx)) < 0 ||
    50          
290             (error = git_hash_init(ctx)) < 0)
291 0           return error;
292              
293 42 100         for (i = 0; i < GIT_OID_RAWSZ; i++) {
294 40           carry += result->id[i] + hash.id[i];
295 40           result->id[i] = (unsigned char)carry;
296 40           carry >>= 8;
297             }
298              
299 2           return 0;
300             }
301              
302 5           static void strip_spaces(git_str *buf)
303             {
304 5           char *src = buf->ptr, *dst = buf->ptr;
305             char c;
306 5           size_t len = 0;
307              
308 139 100         while ((c = *src++) != '\0') {
309 134 100         if (!git__isspace(c)) {
310 110           *dst++ = c;
311 110           len++;
312             }
313             }
314              
315 5           git_str_truncate(buf, len);
316 5           }
317              
318 5           static int diff_patchid_print_callback_to_buf(
319             const git_diff_delta *delta,
320             const git_diff_hunk *hunk,
321             const git_diff_line *line,
322             void *payload)
323             {
324 5           struct patch_id_args *args = (struct patch_id_args *) payload;
325 5           git_str buf = GIT_STR_INIT;
326 5           int error = 0;
327              
328 5 50         if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL ||
    50          
329 5 50         line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
330 5           line->origin == GIT_DIFF_LINE_DEL_EOFNL)
331             goto out;
332              
333 5 50         if ((error = git_diff_print_callback__to_buf(delta, hunk,
334             line, &buf)) < 0)
335 0           goto out;
336              
337 5           strip_spaces(&buf);
338              
339 5 100         if (line->origin == GIT_DIFF_LINE_FILE_HDR &&
    100          
340 1 50         !args->first_file &&
341 1           (error = flush_hunk(&args->result, &args->ctx) < 0))
342 0           goto out;
343              
344 5 50         if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
345 0           goto out;
346              
347 5 100         if (line->origin == GIT_DIFF_LINE_FILE_HDR && args->first_file)
    100          
348 1           args->first_file = 0;
349              
350             out:
351 5           git_str_dispose(&buf);
352 5           return error;
353             }
354              
355 0           int git_diff_patchid_options_init(git_diff_patchid_options *opts, unsigned int version)
356             {
357 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
358             opts, version, git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_INIT);
359 0           return 0;
360             }
361              
362 1           int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts)
363             {
364             struct patch_id_args args;
365             int error;
366              
367 1 50         GIT_ERROR_CHECK_VERSION(
368             opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options");
369              
370 1           memset(&args, 0, sizeof(args));
371 1           args.first_file = 1;
372 1 50         if ((error = git_hash_ctx_init(&args.ctx, GIT_HASH_ALGORITHM_SHA1)) < 0)
373 0           goto out;
374              
375 1 50         if ((error = git_diff_print(diff,
376             GIT_DIFF_FORMAT_PATCH_ID,
377             diff_patchid_print_callback_to_buf,
378             &args)) < 0)
379 0           goto out;
380              
381 1 50         if ((error = (flush_hunk(&args.result, &args.ctx))) < 0)
382 0           goto out;
383              
384 1           git_oid_cpy(out, &args.result);
385              
386             out:
387 1           git_hash_ctx_cleanup(&args.ctx);
388 1           return error;
389             }