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 "patch_generate.h"
9 -
10 - #include "git2/blob.h"
11 - #include "diff.h"
12 - #include "diff_generate.h"
13 - #include "diff_file.h"
14 - #include "diff_driver.h"
15 - #include "diff_xdiff.h"
16 - #include "delta.h"
17 - #include "zstream.h"
18 - #include "futils.h"
19 -
20 - static void diff_output_init(
21 - git_patch_generated_output *, const git_diff_options *, git_diff_file_cb,
22 - git_diff_binary_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
23 -
24 - static void diff_output_to_patch(
25 - git_patch_generated_output *, git_patch_generated *);
26 -
27 1264 2 static void patch_generated_free(git_patch *p)
28 - {
29 1264 2 git_patch_generated *patch = (git_patch_generated *)p;
30 -
31 1264 2 git_array_clear(patch->base.lines);
32 1264 3 git_array_clear(patch->base.hunks);
33 -
34 1264 4 git__free((char *)patch->base.binary.old_file.data);
35 1264 5 git__free((char *)patch->base.binary.new_file.data);
36 -
37 1264 6 git_diff_file_content__clear(&patch->ofile);
38 1264 7 git_diff_file_content__clear(&patch->nfile);
39 -
40 1264 8 git_diff_free(patch->diff); /* decrements refcount */
41 1264 9 patch->diff = NULL;
42 -
43 1264 9 git_pool_clear(&patch->flattened);
44 -
45 1264 10 git__free((char *)patch->base.diff_opts.old_prefix);
46 1264 11 git__free((char *)patch->base.diff_opts.new_prefix);
47 -
48 1264 12 if (patch->flags & GIT_PATCH_GENERATED_ALLOCATED)
49 1215 13 git__free(patch);
50 1264 14 }
51 -
52 2523 2 static void patch_generated_update_binary(git_patch_generated *patch)
53 - {
54 2523 2 if ((patch->base.delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
55 2523 3,13 return;
56 -
57 1739 4,5 if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 ||
58 1695 5 (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
59 57 6 patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
60 -
61 1682 7,8 else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE ||
62 1682 8 patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
63 ##### 9 patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
64 -
65 1682 10,11 else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
66 1105 11 (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
67 1017 12 patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
68 - }
69 -
70 1264 2 static void patch_generated_init_common(git_patch_generated *patch)
71 - {
72 1264 2 patch->base.free_fn = patch_generated_free;
73 -
74 1264 2 patch_generated_update_binary(patch);
75 -
76 1264 3 patch->flags |= GIT_PATCH_GENERATED_INITIALIZED;
77 -
78 1264 3 if (patch->diff)
79 1132 4 git_diff_addref(patch->diff);
80 1264 5 }
81 -
82 1266 2 static int patch_generated_normalize_options(
83 - git_diff_options *out,
84 - const git_diff_options *opts)
85 - {
86 1266 2 if (opts) {
87 1232 3-5 GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
88 1230 6 memcpy(out, opts, sizeof(git_diff_options));
89 - } else {
90 34 7 git_diff_options default_opts = GIT_DIFF_OPTIONS_INIT;
91 34 7 memcpy(out, &default_opts, sizeof(git_diff_options));
92 - }
93 -
94 1264 8-11 out->old_prefix = opts && opts->old_prefix ?
95 1132 10 git__strdup(opts->old_prefix) :
96 132 11 git__strdup(DIFF_OLD_PREFIX_DEFAULT);
97 -
98 1264 12-15 out->new_prefix = opts && opts->new_prefix ?
99 1132 14 git__strdup(opts->new_prefix) :
100 132 15 git__strdup(DIFF_NEW_PREFIX_DEFAULT);
101 -
102 1264 16,17 GIT_ERROR_CHECK_ALLOC(out->old_prefix);
103 1264 18,19 GIT_ERROR_CHECK_ALLOC(out->new_prefix);
104 -
105 1264 20 return 0;
106 - }
107 -
108 1132 2 static int patch_generated_init(
109 - git_patch_generated *patch, git_diff *diff, size_t delta_index)
110 - {
111 1132 2 int error = 0;
112 -
113 1132 2 memset(patch, 0, sizeof(*patch));
114 -
115 1132 2 patch->diff = diff;
116 1132 2 patch->base.repo = diff->repo;
117 1132 2 patch->base.delta = git_vector_get(&diff->deltas, delta_index);
118 1132 3 patch->delta_index = delta_index;
119 -
120 1132 3,4 if ((error = patch_generated_normalize_options(
121 1132 3,5,6 &patch->base.diff_opts, &diff->opts)) < 0 ||
122 1132 5 (error = git_diff_file_content__init_from_diff(
123 1132 7,8 &patch->ofile, diff, patch->base.delta, true)) < 0 ||
124 1132 7 (error = git_diff_file_content__init_from_diff(
125 - &patch->nfile, diff, patch->base.delta, false)) < 0)
126 ##### 9 return error;
127 -
128 1132 10 patch_generated_init_common(patch);
129 -
130 1132 11 return 0;
131 - }
132 -
133 1132 2 static int patch_generated_alloc_from_diff(
134 - git_patch_generated **out, git_diff *diff, size_t delta_index)
135 - {
136 - int error;
137 1132 2 git_patch_generated *patch = git__calloc(1, sizeof(git_patch_generated));
138 1132 3,4 GIT_ERROR_CHECK_ALLOC(patch);
139 -
140 1132 5,6 if (!(error = patch_generated_init(patch, diff, delta_index))) {
141 1132 7 patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
142 1132 7 GIT_REFCOUNT_INC(&patch->base);
143 - } else {
144 ##### 8 git__free(patch);
145 ##### 9 patch = NULL;
146 - }
147 -
148 1132 10 *out = patch;
149 1132 10 return error;
150 - }
151 -
152 2484 2 GIT_INLINE(bool) should_skip_binary(git_patch_generated *patch, git_diff_file *file)
153 - {
154 2484 2 if ((patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0)
155 102 3 return false;
156 -
157 2382 4 return (file->flags & GIT_DIFF_FLAG_BINARY) != 0;
158 - }
159 -
160 1259 2 static bool patch_generated_diffable(git_patch_generated *patch)
161 - {
162 - size_t olen, nlen;
163 -
164 1259 2 if (patch->base.delta->status == GIT_DELTA_UNMODIFIED)
165 64 3 return false;
166 -
167 - /* if we've determined this to be binary (and we are not showing binary
168 - * data) then we have skipped loading the map data. instead, query the
169 - * file data itself.
170 - */
171 1195 4,5 if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
172 63 5 (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0) {
173 42 6 olen = (size_t)patch->ofile.file->size;
174 42 6 nlen = (size_t)patch->nfile.file->size;
175 - } else {
176 1153 7 olen = patch->ofile.map.len;
177 1153 7 nlen = patch->nfile.map.len;
178 - }
179 -
180 - /* if both sides are empty, files are identical */
181 1195 8,9 if (!olen && !nlen)
182 263 10 return false;
183 -
184 - /* otherwise, check the file sizes and the oid */
185 932 11,13 return (olen != nlen ||
186 83 12 !git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id));
187 - }
188 -
189 1259 2 static int patch_generated_load(git_patch_generated *patch, git_patch_generated_output *output)
190 - {
191 1259 2 int error = 0;
192 - bool incomplete_data;
193 -
194 1259 2 if ((patch->flags & GIT_PATCH_GENERATED_LOADED) != 0)
195 ##### 3 return 0;
196 -
197 - /* if no hunk and data callbacks and user doesn't care if data looks
198 - * binary, then there is no need to actually load the data
199 - */
200 1259 4,5 if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 &&
201 ##### 6-8 output && !output->binary_cb && !output->hunk_cb && !output->data_cb)
202 ##### 9 return 0;
203 -
204 1259 16 incomplete_data =
205 1259 10,11 (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
206 1259 10,12,14,15 (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0) &&
207 1259 12,13 ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
208 714 13 (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0));
209 -
210 1259 16,17 if ((error = git_diff_file_content__load(
211 1259 19 &patch->ofile, &patch->base.diff_opts)) < 0 ||
212 1259 18 should_skip_binary(patch, patch->ofile.file))
213 - goto cleanup;
214 1225 20,21 if ((error = git_diff_file_content__load(
215 1225 23 &patch->nfile, &patch->base.diff_opts)) < 0 ||
216 1225 22 should_skip_binary(patch, patch->nfile.file))
217 - goto cleanup;
218 -
219 - /* if previously missing an oid, and now that we have it the two sides
220 - * are the same (and not submodules), update MODIFIED -> UNMODIFIED
221 - */
222 1214 24,25 if (incomplete_data &&
223 982 25,26 patch->ofile.file->mode == patch->nfile.file->mode &&
224 383 26,28 patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
225 381 27,29 git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id) &&
226 89 29 patch->base.delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
227 ##### 30 patch->base.delta->status = GIT_DELTA_UNMODIFIED;
228 -
229 - cleanup:
230 1259 31 patch_generated_update_binary(patch);
231 -
232 1259 32 if (!error) {
233 1259 33,34 if (patch_generated_diffable(patch))
234 889 35 patch->flags |= GIT_PATCH_GENERATED_DIFFABLE;
235 -
236 1259 36 patch->flags |= GIT_PATCH_GENERATED_LOADED;
237 - }
238 -
239 1259 37 return error;
240 - }
241 -
242 1259 2 static int patch_generated_invoke_file_callback(
243 - git_patch_generated *patch, git_patch_generated_output *output)
244 - {
245 1259 2,5 float progress = patch->diff ?
246 1259 2-4 ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f;
247 -
248 1259 5 if (!output->file_cb)
249 7 6 return 0;
250 -
251 1252 7,7 return git_error_set_after_callback_function(
252 1252 7,7 output->file_cb(patch->base.delta, progress, output->payload),
253 - "git_patch");
254 - }
255 -
256 42 2 static int create_binary(
257 - git_diff_binary_t *out_type,
258 - char **out_data,
259 - size_t *out_datalen,
260 - size_t *out_inflatedlen,
261 - const char *a_data,
262 - size_t a_datalen,
263 - const char *b_data,
264 - size_t b_datalen)
265 - {
266 42 2 git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT;
267 42 2 size_t delta_data_len = 0;
268 - int error;
269 -
270 - /* The git_delta function accepts unsigned long only */
271 42 2-5 if (!git__is_ulong(a_datalen) || !git__is_ulong(b_datalen))
272 ##### 6 return GIT_EBUFS;
273 -
274 42 7,8 if ((error = git_zstream_deflatebuf(&deflate, b_data, b_datalen)) < 0)
275 ##### 9 goto done;
276 -
277 - /* The git_delta function accepts unsigned long only */
278 42 10,11 if (!git__is_ulong(deflate.size)) {
279 ##### 12 error = GIT_EBUFS;
280 ##### 12 goto done;
281 - }
282 -
283 42 13,14 if (a_datalen && b_datalen) {
284 - void *delta_data;
285 -
286 26 15 error = git_delta(&delta_data, &delta_data_len,
287 - a_data, a_datalen,
288 - b_data, b_datalen,
289 - deflate.size);
290 -
291 26 16 if (error == 0) {
292 26 17 error = git_zstream_deflatebuf(
293 - &delta, delta_data, delta_data_len);
294 -
295 26 18 git__free(delta_data);
296 ##### 19 } else if (error == GIT_EBUFS) {
297 ##### 20 error = 0;
298 - }
299 -
300 26 21 if (error < 0)
301 26 22,23 goto done;
302 - }
303 -
304 42 24,25 if (delta.size && delta.size < deflate.size) {
305 20 26 *out_type = GIT_DIFF_BINARY_DELTA;
306 20 26 *out_datalen = delta.size;
307 20 26 *out_data = git_buf_detach(&delta);
308 20 27 *out_inflatedlen = delta_data_len;
309 - } else {
310 22 28 *out_type = GIT_DIFF_BINARY_LITERAL;
311 22 28 *out_datalen = deflate.size;
312 22 28 *out_data = git_buf_detach(&deflate);
313 22 29 *out_inflatedlen = b_datalen;
314 - }
315 -
316 - done:
317 42 30 git_buf_dispose(&deflate);
318 42 31 git_buf_dispose(&delta);
319 -
320 42 32 return error;
321 - }
322 -
323 60 2 static int diff_binary(git_patch_generated_output *output, git_patch_generated *patch)
324 - {
325 60 2 git_diff_binary binary = {0};
326 60 2 const char *old_data = patch->ofile.map.data;
327 60 2 const char *new_data = patch->nfile.map.data;
328 60 2 size_t old_len = patch->ofile.map.len,
329 60 2 new_len = patch->nfile.map.len;
330 - int error;
331 -
332 - /* Only load contents if the user actually wants to diff
333 - * binary files. */
334 60 2 if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) {
335 21 3 binary.contains_data = 1;
336 -
337 - /* Create the old->new delta (as the "new" side of the patch),
338 - * and the new->old delta (as the "old" side)
339 - */
340 21 3,4 if ((error = create_binary(&binary.old_file.type,
341 - (char **)&binary.old_file.data,
342 - &binary.old_file.datalen,
343 - &binary.old_file.inflatedlen,
344 21 5,6 new_data, new_len, old_data, old_len)) < 0 ||
345 - (error = create_binary(&binary.new_file.type,
346 - (char **)&binary.new_file.data,
347 - &binary.new_file.datalen,
348 - &binary.new_file.inflatedlen,
349 - old_data, old_len, new_data, new_len)) < 0)
350 ##### 7 return error;
351 - }
352 -
353 60 8,8,9 error = git_error_set_after_callback_function(
354 60 8,8 output->binary_cb(patch->base.delta, &binary, output->payload),
355 - "git_patch");
356 -
357 60 10 git__free((char *) binary.old_file.data);
358 60 11 git__free((char *) binary.new_file.data);
359 -
360 60 12 return error;
361 - }
362 -
363 1259 2 static int patch_generated_create(
364 - git_patch_generated *patch,
365 - git_patch_generated_output *output)
366 - {
367 1259 2 int error = 0;
368 -
369 1259 2 if ((patch->flags & GIT_PATCH_GENERATED_DIFFED) != 0)
370 ##### 3 return 0;
371 -
372 - /* if we are not looking at the binary or text data, don't do the diff */
373 1259 4-6 if (!output->binary_cb && !output->hunk_cb && !output->data_cb)
374 ##### 7 return 0;
375 -
376 1259 8-10 if ((patch->flags & GIT_PATCH_GENERATED_LOADED) == 0 &&
377 - (error = patch_generated_load(patch, output)) < 0)
378 ##### 11 return error;
379 -
380 1259 12 if ((patch->flags & GIT_PATCH_GENERATED_DIFFABLE) == 0)
381 370 13 return 0;
382 -
383 889 14 if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
384 60 15 if (output->binary_cb)
385 60 16,17 error = diff_binary(output, patch);
386 - }
387 - else {
388 829 18 if (output->diff_cb)
389 829 19 error = output->diff_cb(output, patch);
390 - }
391 -
392 889 20 patch->flags |= GIT_PATCH_GENERATED_DIFFED;
393 889 20 return error;
394 - }
395 -
396 1133 2 static int diff_required(git_diff *diff, const char *action)
397 - {
398 1133 2 if (diff)
399 1133 3 return 0;
400 ##### 4 git_error_set(GIT_ERROR_INVALID, "must provide valid diff to %s", action);
401 ##### 5 return -1;
402 - }
403 -
404 - typedef struct {
405 - git_patch_generated patch;
406 - git_diff_delta delta;
407 - char paths[GIT_FLEX_ARRAY];
408 - } patch_generated_with_delta;
409 -
410 132 2 static int diff_single_generate(patch_generated_with_delta *pd, git_xdiff_output *xo)
411 - {
412 132 2 int error = 0;
413 132 2 git_patch_generated *patch = &pd->patch;
414 132 2 bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
415 132 2 bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
416 -
417 132 2-10 pd->delta.status = has_new ?
418 - (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
419 - (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
420 -
421 132 11,12 if (git_oid_equal(&patch->nfile.file->id, &patch->ofile.file->id))
422 15 13 pd->delta.status = GIT_DELTA_UNMODIFIED;
423 -
424 132 14 patch->base.delta = &pd->delta;
425 -
426 132 14 patch_generated_init_common(patch);
427 -
428 132 15,16 if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
429 15 16 !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) {
430 -
431 - /* Even empty patches are flagged as binary, and even though
432 - * there's no difference, we flag this as "containing data"
433 - * (the data is known to be empty, as opposed to wholly unknown).
434 - */
435 5 17 if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY)
436 1 18 patch->base.binary.contains_data = 1;
437 -
438 5 19 return error;
439 - }
440 -
441 127 20 error = patch_generated_invoke_file_callback(patch, (git_patch_generated_output *)xo);
442 -
443 127 21 if (!error)
444 127 22 error = patch_generated_create(patch, (git_patch_generated_output *)xo);
445 -
446 127 23 return error;
447 - }
448 -
449 134 2 static int patch_generated_from_sources(
450 - patch_generated_with_delta *pd,
451 - git_xdiff_output *xo,
452 - git_diff_file_content_src *oldsrc,
453 - git_diff_file_content_src *newsrc,
454 - const git_diff_options *opts)
455 - {
456 134 2 int error = 0;
457 134 8 git_repository *repo =
458 134 2,3,7 oldsrc->blob ? git_blob_owner(oldsrc->blob) :
459 67 4-6 newsrc->blob ? git_blob_owner(newsrc->blob) : NULL;
460 134 8 git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
461 134 8 git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
462 -
463 134 8,9 if ((error = patch_generated_normalize_options(&pd->patch.base.diff_opts, opts)) < 0)
464 2 10 return error;
465 -
466 132 11,12 if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
467 5 13 void *tmp = lfile; lfile = rfile; rfile = tmp;
468 5 13 tmp = ldata; ldata = rdata; rdata = tmp;
469 - }
470 -
471 132 14 pd->patch.base.delta = &pd->delta;
472 -
473 132 14 if (!oldsrc->as_path) {
474 56 15 if (newsrc->as_path)
475 ##### 16 oldsrc->as_path = newsrc->as_path;
476 - else
477 56 17,18 oldsrc->as_path = newsrc->as_path = "file";
478 - }
479 76 19 else if (!newsrc->as_path)
480 3 20 newsrc->as_path = oldsrc->as_path;
481 -
482 132 21 lfile->path = oldsrc->as_path;
483 132 21 rfile->path = newsrc->as_path;
484 -
485 132 21,22 if ((error = git_diff_file_content__init_from_src(
486 132 23,24 ldata, repo, opts, oldsrc, lfile)) < 0 ||
487 - (error = git_diff_file_content__init_from_src(
488 - rdata, repo, opts, newsrc, rfile)) < 0)
489 ##### 25 return error;
490 -
491 132 26 return diff_single_generate(pd, xo);
492 - }
493 -
494 83 2 static int patch_generated_with_delta_alloc(
495 - patch_generated_with_delta **out,
496 - const char **old_path,
497 - const char **new_path)
498 - {
499 - patch_generated_with_delta *pd;
500 83 2-4 size_t old_len = *old_path ? strlen(*old_path) : 0;
501 83 5-7 size_t new_len = *new_path ? strlen(*new_path) : 0;
502 - size_t alloc_len;
503 -
504 83 8-14 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len);
505 83 15-21 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len);
506 83 22-28 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
507 -
508 83 29 *out = pd = git__calloc(1, alloc_len);
509 83 30,31 GIT_ERROR_CHECK_ALLOC(pd);
510 -
511 83 32 pd->patch.flags = GIT_PATCH_GENERATED_ALLOCATED;
512 -
513 83 32 if (*old_path) {
514 62 33 memcpy(&pd->paths[0], *old_path, old_len);
515 62 33 *old_path = &pd->paths[0];
516 21 34 } else if (*new_path)
517 3 35 *old_path = &pd->paths[old_len + 1];
518 -
519 83 36 if (*new_path) {
520 54 37 memcpy(&pd->paths[old_len + 1], *new_path, new_len);
521 54 37 *new_path = &pd->paths[old_len + 1];
522 29 38 } else if (*old_path)
523 11 39 *new_path = &pd->paths[0];
524 -
525 83 40 return 0;
526 - }
527 -
528 51 2 static int diff_from_sources(
529 - git_diff_file_content_src *oldsrc,
530 - git_diff_file_content_src *newsrc,
531 - const git_diff_options *opts,
532 - git_diff_file_cb file_cb,
533 - git_diff_binary_cb binary_cb,
534 - git_diff_hunk_cb hunk_cb,
535 - git_diff_line_cb data_cb,
536 - void *payload)
537 - {
538 51 2 int error = 0;
539 - patch_generated_with_delta pd;
540 - git_xdiff_output xo;
541 -
542 51 2 memset(&xo, 0, sizeof(xo));
543 51 2 diff_output_init(
544 - &xo.output, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
545 51 3 git_xdiff_init(&xo, opts);
546 -
547 51 4 memset(&pd, 0, sizeof(pd));
548 -
549 51 4 error = patch_generated_from_sources(&pd, &xo, oldsrc, newsrc, opts);
550 -
551 51 5 git_patch_free(&pd.patch.base);
552 -
553 51 6 return error;
554 - }
555 -
556 83 2 static int patch_from_sources(
557 - git_patch **out,
558 - git_diff_file_content_src *oldsrc,
559 - git_diff_file_content_src *newsrc,
560 - const git_diff_options *opts)
561 - {
562 83 2 int error = 0;
563 - patch_generated_with_delta *pd;
564 - git_xdiff_output xo;
565 -
566 83 2,3 assert(out);
567 83 4 *out = NULL;
568 -
569 83 4,5 if ((error = patch_generated_with_delta_alloc(
570 - &pd, &oldsrc->as_path, &newsrc->as_path)) < 0)
571 ##### 6 return error;
572 -
573 83 7 memset(&xo, 0, sizeof(xo));
574 83 7 diff_output_to_patch(&xo.output, &pd->patch);
575 83 8 git_xdiff_init(&xo, opts);
576 -
577 83 9,10 if (!(error = patch_generated_from_sources(pd, &xo, oldsrc, newsrc, opts)))
578 83 11 *out = (git_patch *)pd;
579 - else
580 ##### 12 git_patch_free((git_patch *)pd);
581 -
582 83 13 return error;
583 - }
584 -
585 23 2 int git_diff_blobs(
586 - const git_blob *old_blob,
587 - const char *old_path,
588 - const git_blob *new_blob,
589 - const char *new_path,
590 - const git_diff_options *opts,
591 - git_diff_file_cb file_cb,
592 - git_diff_binary_cb binary_cb,
593 - git_diff_hunk_cb hunk_cb,
594 - git_diff_line_cb data_cb,
595 - void *payload)
596 - {
597 23 2 git_diff_file_content_src osrc =
598 - GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
599 23 2 git_diff_file_content_src nsrc =
600 - GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
601 23 2 return diff_from_sources(
602 - &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
603 - }
604 -
605 12 2 int git_patch_from_blobs(
606 - git_patch **out,
607 - const git_blob *old_blob,
608 - const char *old_path,
609 - const git_blob *new_blob,
610 - const char *new_path,
611 - const git_diff_options *opts)
612 - {
613 12 2 git_diff_file_content_src osrc =
614 - GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
615 12 2 git_diff_file_content_src nsrc =
616 - GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
617 12 2 return patch_from_sources(out, &osrc, &nsrc, opts);
618 - }
619 -
620 25 2 int git_diff_blob_to_buffer(
621 - const git_blob *old_blob,
622 - const char *old_path,
623 - const char *buf,
624 - size_t buflen,
625 - const char *buf_path,
626 - const git_diff_options *opts,
627 - git_diff_file_cb file_cb,
628 - git_diff_binary_cb binary_cb,
629 - git_diff_hunk_cb hunk_cb,
630 - git_diff_line_cb data_cb,
631 - void *payload)
632 - {
633 25 2 git_diff_file_content_src osrc =
634 - GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
635 25 2 git_diff_file_content_src nsrc =
636 - GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
637 25 2 return diff_from_sources(
638 - &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
639 - }
640 -
641 13 2 int git_patch_from_blob_and_buffer(
642 - git_patch **out,
643 - const git_blob *old_blob,
644 - const char *old_path,
645 - const void *buf,
646 - size_t buflen,
647 - const char *buf_path,
648 - const git_diff_options *opts)
649 - {
650 13 2 git_diff_file_content_src osrc =
651 - GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
652 13 2 git_diff_file_content_src nsrc =
653 - GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
654 13 2 return patch_from_sources(out, &osrc, &nsrc, opts);
655 - }
656 -
657 3 2 int git_diff_buffers(
658 - const void *old_buf,
659 - size_t old_len,
660 - const char *old_path,
661 - const void *new_buf,
662 - size_t new_len,
663 - const char *new_path,
664 - const git_diff_options *opts,
665 - git_diff_file_cb file_cb,
666 - git_diff_binary_cb binary_cb,
667 - git_diff_hunk_cb hunk_cb,
668 - git_diff_line_cb data_cb,
669 - void *payload)
670 - {
671 3 2 git_diff_file_content_src osrc =
672 - GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
673 3 2 git_diff_file_content_src nsrc =
674 - GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
675 3 2 return diff_from_sources(
676 - &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
677 - }
678 -
679 58 2 int git_patch_from_buffers(
680 - git_patch **out,
681 - const void *old_buf,
682 - size_t old_len,
683 - const char *old_path,
684 - const void *new_buf,
685 - size_t new_len,
686 - const char *new_path,
687 - const git_diff_options *opts)
688 - {
689 58 2 git_diff_file_content_src osrc =
690 - GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
691 58 2 git_diff_file_content_src nsrc =
692 - GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
693 58 2 return patch_from_sources(out, &osrc, &nsrc, opts);
694 - }
695 -
696 1133 2 int git_patch_generated_from_diff(
697 - git_patch **patch_ptr, git_diff *diff, size_t idx)
698 - {
699 1133 2 int error = 0;
700 - git_xdiff_output xo;
701 1133 2 git_diff_delta *delta = NULL;
702 1133 2 git_patch_generated *patch = NULL;
703 -
704 1133 2,3 if (patch_ptr) *patch_ptr = NULL;
705 -
706 1133 4,5 if (diff_required(diff, "git_patch_from_diff") < 0)
707 ##### 6 return -1;
708 -
709 1133 7 delta = git_vector_get(&diff->deltas, idx);
710 1133 8 if (!delta) {
711 1 9 git_error_set(GIT_ERROR_INVALID, "index out of range for delta in diff");
712 1 10 return GIT_ENOTFOUND;
713 - }
714 -
715 1132 11,12 if (git_diff_delta__should_skip(&diff->opts, delta))
716 ##### 13 return 0;
717 -
718 - /* don't load the patch data unless we need it for binary check */
719 1132 14,15 if (!patch_ptr &&
720 ##### 15,16 ((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 ||
721 ##### 16 (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
722 ##### 17 return 0;
723 -
724 1132 18,19 if ((error = patch_generated_alloc_from_diff(&patch, diff, idx)) < 0)
725 ##### 20 return error;
726 -
727 1132 21 memset(&xo, 0, sizeof(xo));
728 1132 21 diff_output_to_patch(&xo.output, patch);
729 1132 22 git_xdiff_init(&xo, &diff->opts);
730 -
731 1132 23 error = patch_generated_invoke_file_callback(patch, &xo.output);
732 -
733 1132 24 if (!error)
734 1132 25,26 error = patch_generated_create(patch, &xo.output);
735 -
736 - if (!error) {
737 - /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */
738 - /* TODO: and unload the file content */
739 - }
740 -
741 1132 27,28 if (error || !patch_ptr)
742 ##### 29 git_patch_free(&patch->base);
743 - else
744 1132 30 *patch_ptr = &patch->base;
745 -
746 1132 31 return error;
747 - }
748 -
749 829 2 git_diff_driver *git_patch_generated_driver(git_patch_generated *patch)
750 - {
751 - /* ofile driver is representative for whole patch */
752 829 2 return patch->ofile.driver;
753 - }
754 -
755 829 2 void git_patch_generated_old_data(
756 - char **ptr, size_t *len, git_patch_generated *patch)
757 - {
758 829 2 *ptr = patch->ofile.map.data;
759 829 2 *len = patch->ofile.map.len;
760 829 2 }
761 -
762 829 2 void git_patch_generated_new_data(
763 - char **ptr, size_t *len, git_patch_generated *patch)
764 - {
765 829 2 *ptr = patch->nfile.map.data;
766 829 2 *len = patch->nfile.map.len;
767 829 2 }
768 -
769 1210 2 static int patch_generated_file_cb(
770 - const git_diff_delta *delta,
771 - float progress,
772 - void *payload)
773 - {
774 - GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload);
775 1210 2 return 0;
776 - }
777 -
778 49 2 static int patch_generated_binary_cb(
779 - const git_diff_delta *delta,
780 - const git_diff_binary *binary,
781 - void *payload)
782 - {
783 49 2 git_patch *patch = payload;
784 -
785 - GIT_UNUSED(delta);
786 -
787 49 2 memcpy(&patch->binary, binary, sizeof(git_diff_binary));
788 -
789 49 2 if (binary->old_file.data) {
790 20 3 patch->binary.old_file.data = git__malloc(binary->old_file.datalen);
791 20 4,5 GIT_ERROR_CHECK_ALLOC(patch->binary.old_file.data);
792 -
793 20 6,6 memcpy((char *)patch->binary.old_file.data,
794 20 6 binary->old_file.data, binary->old_file.datalen);
795 - }
796 -
797 49 7 if (binary->new_file.data) {
798 20 8 patch->binary.new_file.data = git__malloc(binary->new_file.datalen);
799 20 9,10 GIT_ERROR_CHECK_ALLOC(patch->binary.new_file.data);
800 -
801 20 11,11 memcpy((char *)patch->binary.new_file.data,
802 20 11 binary->new_file.data, binary->new_file.datalen);
803 - }
804 -
805 49 12 return 0;
806 - }
807 -
808 907 2 static int git_patch_hunk_cb(
809 - const git_diff_delta *delta,
810 - const git_diff_hunk *hunk_,
811 - void *payload)
812 - {
813 907 2 git_patch_generated *patch = payload;
814 - git_patch_hunk *hunk;
815 -
816 - GIT_UNUSED(delta);
817 -
818 907 2-7 hunk = git_array_alloc(patch->base.hunks);
819 907 8,9 GIT_ERROR_CHECK_ALLOC(hunk);
820 -
821 907 10 memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
822 -
823 907 10 patch->base.header_size += hunk_->header_len;
824 -
825 907 10 hunk->line_start = git_array_size(patch->base.lines);
826 907 10 hunk->line_count = 0;
827 -
828 907 10 return 0;
829 - }
830 -
831 8568 2 static int patch_generated_line_cb(
832 - const git_diff_delta *delta,
833 - const git_diff_hunk *hunk_,
834 - const git_diff_line *line_,
835 - void *payload)
836 - {
837 8568 2 git_patch_generated *patch = payload;
838 - git_patch_hunk *hunk;
839 - git_diff_line *line;
840 -
841 - GIT_UNUSED(delta);
842 - GIT_UNUSED(hunk_);
843 -
844 8568 2-4 hunk = git_array_last(patch->base.hunks);
845 8568 5,6 assert(hunk); /* programmer error if no hunk is available */
846 -
847 8568 7-12 line = git_array_alloc(patch->base.lines);
848 8568 13,14 GIT_ERROR_CHECK_ALLOC(line);
849 -
850 8568 15 memcpy(line, line_, sizeof(*line));
851 -
852 - /* do some bookkeeping so we can provide old/new line numbers */
853 -
854 8568 15 patch->base.content_size += line->content_len;
855 -
856 8568 15,16 if (line->origin == GIT_DIFF_LINE_ADDITION ||
857 5018 16 line->origin == GIT_DIFF_LINE_DELETION)
858 7005 17 patch->base.content_size += 1;
859 1563 18 else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
860 1499 19 patch->base.content_size += 1;
861 1499 19 patch->base.context_size += line->content_len + 1;
862 64 20 } else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
863 6 21 patch->base.context_size += line->content_len;
864 -
865 8568 22 hunk->line_count++;
866 -
867 8568 22 return 0;
868 - }
869 -
870 1266 2 static void diff_output_init(
871 - git_patch_generated_output *out,
872 - const git_diff_options *opts,
873 - git_diff_file_cb file_cb,
874 - git_diff_binary_cb binary_cb,
875 - git_diff_hunk_cb hunk_cb,
876 - git_diff_line_cb data_cb,
877 - void *payload)
878 - {
879 - GIT_UNUSED(opts);
880 -
881 1266 2 memset(out, 0, sizeof(*out));
882 -
883 1266 2 out->file_cb = file_cb;
884 1266 2 out->binary_cb = binary_cb;
885 1266 2 out->hunk_cb = hunk_cb;
886 1266 2 out->data_cb = data_cb;
887 1266 2 out->payload = payload;
888 1266 2 }
889 -
890 1215 2 static void diff_output_to_patch(
891 - git_patch_generated_output *out, git_patch_generated *patch)
892 - {
893 1215 2 diff_output_init(
894 - out,
895 - NULL,
896 - patch_generated_file_cb,
897 - patch_generated_binary_cb,
898 - git_patch_hunk_cb,
899 - patch_generated_line_cb,
900 - patch);
901 1215 3 }