Line Flow Count Block(s) Source
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 "git2/version.h"
11 - #include "diff_generate.h"
12 - #include "patch.h"
13 - #include "commit.h"
14 - #include "index.h"
15 -
16 - struct patch_id_args {
17 - git_hash_ctx ctx;
18 - git_oid result;
19 - int first_file;
20 - };
21 -
22 4094 2 GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
23 - {
24 4094 2 const char *str = delta->old_file.path;
25 -
26 4094 2,3 if (!str ||
27 4094 3,4 delta->status == GIT_DELTA_ADDED ||
28 3459 4,5 delta->status == GIT_DELTA_RENAMED ||
29 2945 5 delta->status == GIT_DELTA_COPIED)
30 1169 6 str = delta->new_file.path;
31 -
32 4094 7 return str;
33 - }
34 -
35 2041 2 int git_diff_delta__cmp(const void *a, const void *b)
36 - {
37 2041 2 const git_diff_delta *da = a, *db = b;
38 2041 2,3 int val = strcmp(diff_delta__path(da), diff_delta__path(db));
39 2041 4 return val ? val : ((int)da->status - (int)db->status);
40 - }
41 -
42 6 2 int git_diff_delta__casecmp(const void *a, const void *b)
43 - {
44 6 2 const git_diff_delta *da = a, *db = b;
45 6 2,3 int val = strcasecmp(diff_delta__path(da), diff_delta__path(db));
46 6 4 return val ? val : ((int)da->status - (int)db->status);
47 - }
48 -
49 231047 2 int git_diff__entry_cmp(const void *a, const void *b)
50 - {
51 231047 2 const git_index_entry *entry_a = a;
52 231047 2 const git_index_entry *entry_b = b;
53 -
54 231047 2 return strcmp(entry_a->path, entry_b->path);
55 - }
56 -
57 113 2 int git_diff__entry_icmp(const void *a, const void *b)
58 - {
59 113 2 const git_index_entry *entry_a = a;
60 113 2 const git_index_entry *entry_b = b;
61 -
62 113 2 return strcasecmp(entry_a->path, entry_b->path);
63 - }
64 -
65 16804 2 void git_diff_free(git_diff *diff)
66 - {
67 16804 2 if (!diff)
68 16804 3,8 return;
69 -
70 8864 4-7 GIT_REFCOUNT_DEC(diff, diff->free_fn);
71 - }
72 -
73 1132 2 void git_diff_addref(git_diff *diff)
74 - {
75 1132 2 GIT_REFCOUNT_INC(diff);
76 1132 3 }
77 -
78 11621 2 size_t git_diff_num_deltas(const git_diff *diff)
79 - {
80 11621 2,3 assert(diff);
81 11621 4 return diff->deltas.length;
82 - }
83 -
84 172 2 size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
85 - {
86 172 2 size_t i, count = 0;
87 - const git_diff_delta *delta;
88 -
89 172 2,3 assert(diff);
90 -
91 236 4-7 git_vector_foreach(&diff->deltas, i, delta) {
92 64 5 count += (delta->status == type);
93 - }
94 -
95 172 8 return count;
96 - }
97 -
98 8707 2 const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
99 - {
100 8707 2,3 assert(diff);
101 8707 4 return git_vector_get(&diff->deltas, idx);
102 - }
103 -
104 1113 2 int git_diff_is_sorted_icase(const git_diff *diff)
105 - {
106 1113 2 return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
107 - }
108 -
109 4 2 int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
110 - {
111 4 2,3 assert(out);
112 4 4-6 GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
113 4 7 out->stat_calls = diff->perf.stat_calls;
114 4 7 out->oid_calculations = diff->perf.oid_calculations;
115 4 7 return 0;
116 - }
117 -
118 218 2 int git_diff_foreach(
119 - git_diff *diff,
120 - git_diff_file_cb file_cb,
121 - git_diff_binary_cb binary_cb,
122 - git_diff_hunk_cb hunk_cb,
123 - git_diff_line_cb data_cb,
124 - void *payload)
125 - {
126 218 2 int error = 0;
127 - git_diff_delta *delta;
128 - size_t idx;
129 -
130 218 2,3 assert(diff);
131 -
132 939 4,17-19 git_vector_foreach(&diff->deltas, idx, delta) {
133 - git_patch *patch;
134 -
135 - /* check flags against patch status */
136 728 5,6 if (git_diff_delta__should_skip(&diff->opts, delta))
137 ##### 7 continue;
138 -
139 728 8,9 if ((error = git_patch_from_diff(&patch, diff, idx)) != 0)
140 7 10,16 break;
141 -
142 728 11 error = git_patch__invoke_callbacks(patch, file_cb, binary_cb,
143 - hunk_cb, data_cb, payload);
144 728 12 git_patch_free(patch);
145 -
146 728 13 if (error)
147 721 14,15 break;
148 - }
149 -
150 218 20 return error;
151 - }
152 -
153 16 2 static int diff_format_email_append_header_tobuf(
154 - git_buf *out,
155 - const git_oid *id,
156 - const git_signature *author,
157 - const char *summary,
158 - const char *body,
159 - size_t patch_no,
160 - size_t total_patches,
161 - bool exclude_patchno_marker)
162 - {
163 - char idstr[GIT_OID_HEXSZ + 1];
164 - char date_str[GIT_DATE_RFC2822_SZ];
165 16 2 int error = 0;
166 -
167 16 2 git_oid_fmt(idstr, id);
168 16 3 idstr[GIT_OID_HEXSZ] = '\0';
169 -
170 16 3,4 if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str),
171 - &author->when)) < 0)
172 ##### 5 return error;
173 -
174 16 6 error = git_buf_printf(out,
175 - "From %s Mon Sep 17 00:00:00 2001\n" \
176 - "From: %s <%s>\n" \
177 - "Date: %s\n" \
178 - "Subject: ",
179 - idstr,
180 - author->name, author->email,
181 - date_str);
182 -
183 16 7 if (error < 0)
184 ##### 8 return error;
185 -
186 16 9 if (!exclude_patchno_marker) {
187 14 10 if (total_patches == 1) {
188 12 11 error = git_buf_puts(out, "[PATCH] ");
189 - } else {
190 2 12 error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ",
191 - patch_no, total_patches);
192 - }
193 -
194 14 13 if (error < 0)
195 ##### 14 return error;
196 - }
197 -
198 16 15 error = git_buf_printf(out, "%s\n\n", summary);
199 -
200 16 16 if (body) {
201 2 17 git_buf_puts(out, body);
202 -
203 2 18 if (out->ptr[out->size - 1] != '\n')
204 2 19 git_buf_putc(out, '\n');
205 - }
206 -
207 16 20 return error;
208 - }
209 -
210 16 2 static int diff_format_email_append_patches_tobuf(
211 - git_buf *out,
212 - git_diff *diff)
213 - {
214 - size_t i, deltas;
215 16 2 int error = 0;
216 -
217 16 2 deltas = git_diff_num_deltas(diff);
218 -
219 36 3,10,11 for (i = 0; i < deltas; ++i) {
220 20 4 git_patch *patch = NULL;
221 -
222 20 4,5 if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
223 20 6 error = git_patch_to_buf(out, patch);
224 -
225 20 7 git_patch_free(patch);
226 -
227 20 8 if (error < 0)
228 ##### 9 break;
229 - }
230 -
231 16 12 return error;
232 - }
233 -
234 19 2 int git_diff_format_email(
235 - git_buf *out,
236 - git_diff *diff,
237 - const git_diff_format_email_options *opts)
238 - {
239 19 2 git_diff_stats *stats = NULL;
240 19 2 char *summary = NULL, *loc = NULL;
241 - bool ignore_marker;
242 19 2 unsigned int format_flags = 0;
243 - size_t allocsize;
244 - int error;
245 -
246 19 2-5 assert(out && diff && opts);
247 19 6-9 assert(opts->summary && opts->id && opts->author);
248 -
249 19 10-12 GIT_ERROR_CHECK_VERSION(opts,
250 - GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION,
251 - "git_format_email_options");
252 -
253 19 13 ignore_marker = (opts->flags &
254 - GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0;
255 -
256 19 13 if (!ignore_marker) {
257 17 14 if (opts->patch_no > opts->total_patches) {
258 2 15 git_error_set(GIT_ERROR_INVALID,
259 - "patch %"PRIuZ" out of range. max %"PRIuZ,
260 - opts->patch_no, opts->total_patches);
261 2 16 return -1;
262 - }
263 -
264 15 17 if (opts->patch_no == 0) {
265 1 18 git_error_set(GIT_ERROR_INVALID,
266 - "invalid patch no %"PRIuZ". should be >0", opts->patch_no);
267 1 19 return -1;
268 - }
269 - }
270 -
271 - /* the summary we receive may not be clean.
272 - * it could potentially contain new line characters
273 - * or not be set, sanitize, */
274 16 20 if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) {
275 1 21 size_t offset = 0;
276 -
277 1 21 if ((offset = (loc - opts->summary)) == 0) {
278 ##### 22 git_error_set(GIT_ERROR_INVALID, "summary is empty");
279 ##### 23 error = -1;
280 ##### 23 goto on_error;
281 - }
282 -
283 1 24-30 GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, offset, 1);
284 1 31 summary = git__calloc(allocsize, sizeof(char));
285 1 32,33 GIT_ERROR_CHECK_ALLOC(summary);
286 -
287 1 34 strncpy(summary, opts->summary, offset);
288 - }
289 -
290 16 35-38 error = diff_format_email_append_header_tobuf(out,
291 - opts->id, opts->author, summary == NULL ? opts->summary : summary,
292 - opts->body, opts->patch_no, opts->total_patches, ignore_marker);
293 -
294 16 39 if (error < 0)
295 ##### 40 goto on_error;
296 -
297 16 41 format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
298 -
299 16 41-44 if ((error = git_buf_puts(out, "---\n")) < 0 ||
300 16 45,46 (error = git_diff_get_stats(&stats, diff)) < 0 ||
301 16 45,47,48 (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) < 0 ||
302 16 49,50 (error = git_buf_putc(out, '\n')) < 0 ||
303 - (error = diff_format_email_append_patches_tobuf(out, diff)) < 0)
304 - goto on_error;
305 -
306 16 51 error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
307 -
308 - on_error:
309 16 52 git__free(summary);
310 16 53 git_diff_stats_free(stats);
311 -
312 16 54 return error;
313 - }
314 -
315 9 2 int git_diff_commit_as_email(
316 - git_buf *out,
317 - git_repository *repo,
318 - git_commit *commit,
319 - size_t patch_no,
320 - size_t total_patches,
321 - uint32_t flags,
322 - const git_diff_options *diff_opts)
323 - {
324 9 2 git_diff *diff = NULL;
325 9 2 git_diff_format_email_options opts =
326 - GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
327 - int error;
328 -
329 9 2-5 assert (out && repo && commit);
330 -
331 9 6 opts.flags = flags;
332 9 6 opts.patch_no = patch_no;
333 9 6 opts.total_patches = total_patches;
334 9 6 opts.id = git_commit_id(commit);
335 9 7 opts.summary = git_commit_summary(commit);
336 9 8 opts.body = git_commit_body(commit);
337 9 9 opts.author = git_commit_author(commit);
338 -
339 9 10,11 if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
340 ##### 12 return error;
341 -
342 9 13 error = git_diff_format_email(out, diff, &opts);
343 -
344 9 14 git_diff_free(diff);
345 9 15 return error;
346 - }
347 -
348 130 2 int git_diff_options_init(git_diff_options *opts, unsigned int version)
349 - {
350 130 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
351 - opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
352 130 5 return 0;
353 - }
354 -
355 - #ifndef GIT_DEPRECATE_HARD
356 ##### 2 int git_diff_init_options(git_diff_options *opts, unsigned int version)
357 - {
358 ##### 2 return git_diff_options_init(opts, version);
359 - }
360 - #endif
361 -
362 1 2 int git_diff_find_options_init(
363 - git_diff_find_options *opts, unsigned int version)
364 - {
365 1 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
366 - opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
367 1 5 return 0;
368 - }
369 -
370 - #ifndef GIT_DEPRECATE_HARD
371 ##### 2 int git_diff_find_init_options(
372 - git_diff_find_options *opts, unsigned int version)
373 - {
374 ##### 2 return git_diff_find_options_init(opts, version);
375 - }
376 - #endif
377 -
378 ##### 2 int git_diff_format_email_options_init(
379 - git_diff_format_email_options *opts, unsigned int version)
380 - {
381 ##### 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
382 - opts, version, git_diff_format_email_options,
383 - GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
384 ##### 5 return 0;
385 - }
386 -
387 - #ifndef GIT_DEPRECATE_HARD
388 ##### 2 int git_diff_format_email_init_options(
389 - git_diff_format_email_options *opts, unsigned int version)
390 - {
391 ##### 2 return git_diff_format_email_options_init(opts, version);
392 - }
393 - #endif
394 -
395 18 2 static int flush_hunk(git_oid *result, git_hash_ctx *ctx)
396 - {
397 - git_oid hash;
398 18 2 unsigned short carry = 0;
399 - int error, i;
400 -
401 18 2-5 if ((error = git_hash_final(&hash, ctx)) < 0 ||
402 - (error = git_hash_init(ctx)) < 0)
403 ##### 6 return error;
404 -
405 378 7-9 for (i = 0; i < GIT_OID_RAWSZ; i++) {
406 360 8 carry += result->id[i] + hash.id[i];
407 360 8 result->id[i] = (unsigned char)carry;
408 360 8 carry >>= 8;
409 - }
410 -
411 18 10 return 0;
412 - }
413 -
414 95 2 static void strip_spaces(git_buf *buf)
415 - {
416 95 2 char *src = buf->ptr, *dst = buf->ptr;
417 - char c;
418 95 2 size_t len = 0;
419 -
420 2484 2,6 while ((c = *src++) != '\0') {
421 2389 3,4 if (!git__isspace(c)) {
422 1960 5 *dst++ = c;
423 1960 5 len++;
424 - }
425 - }
426 -
427 95 7 git_buf_truncate(buf, len);
428 95 8 }
429 -
430 96 2 static int diff_patchid_print_callback_to_buf(
431 - const git_diff_delta *delta,
432 - const git_diff_hunk *hunk,
433 - const git_diff_line *line,
434 - void *payload)
435 - {
436 96 2 struct patch_id_args *args = (struct patch_id_args *) payload;
437 96 2 git_buf buf = GIT_BUF_INIT;
438 96 2 int error = 0;
439 -
440 96 2,3 if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL ||
441 96 3,4 line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
442 95 4 line->origin == GIT_DIFF_LINE_DEL_EOFNL)
443 - goto out;
444 -
445 95 5,6 if ((error = git_diff_print_callback__to_buf(delta, hunk,
446 - line, &buf)) < 0)
447 ##### 7 goto out;
448 -
449 95 8 strip_spaces(&buf);
450 -
451 95 9,10 if (line->origin == GIT_DIFF_LINE_FILE_HDR &&
452 18 10,12 !args->first_file &&
453 3 11 (error = flush_hunk(&args->result, &args->ctx) < 0))
454 ##### 13 goto out;
455 -
456 95 14,15 if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
457 ##### 16 goto out;
458 -
459 95 17,18 if (line->origin == GIT_DIFF_LINE_FILE_HDR && args->first_file)
460 15 19 args->first_file = 0;
461 -
462 - out:
463 96 20 git_buf_dispose(&buf);
464 96 21 return error;
465 - }
466 -
467 1 2 int git_diff_patchid_options_init(git_diff_patchid_options *opts, unsigned int version)
468 - {
469 1 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
470 - opts, version, git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_INIT);
471 1 5 return 0;
472 - }
473 -
474 15 2 int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts)
475 - {
476 - struct patch_id_args args;
477 - int error;
478 -
479 15 2-4 GIT_ERROR_CHECK_VERSION(
480 - opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options");
481 -
482 15 5 memset(&args, 0, sizeof(args));
483 15 5 args.first_file = 1;
484 15 5,6 if ((error = git_hash_ctx_init(&args.ctx)) < 0)
485 ##### 7 goto out;
486 -
487 15 8,9 if ((error = git_diff_print(diff,
488 - GIT_DIFF_FORMAT_PATCH_ID,
489 - diff_patchid_print_callback_to_buf,
490 - &args)) < 0)
491 ##### 10 goto out;
492 -
493 15 11,12 if ((error = (flush_hunk(&args.result, &args.ctx))) < 0)
494 ##### 13 goto out;
495 -
496 15 14 git_oid_cpy(out, &args.result);
497 -
498 - out:
499 15 15 git_hash_ctx_cleanup(&args.ctx);
500 15 16 return error;
501 - }