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 "apply.h"
9 -
10 - #include "git2/apply.h"
11 - #include "git2/patch.h"
12 - #include "git2/filter.h"
13 - #include "git2/blob.h"
14 - #include "git2/index.h"
15 - #include "git2/checkout.h"
16 - #include "git2/repository.h"
17 - #include "array.h"
18 - #include "patch.h"
19 - #include "futils.h"
20 - #include "delta.h"
21 - #include "zstream.h"
22 - #include "reader.h"
23 - #include "index.h"
24 -
25 - typedef struct {
26 - /* The lines that we allocate ourself are allocated out of the pool.
27 - * (Lines may have been allocated out of the diff.)
28 - */
29 - git_pool pool;
30 - git_vector lines;
31 - } patch_image;
32 -
33 - static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
34 10 2 static int apply_err(const char *fmt, ...)
35 - {
36 - va_list ap;
37 -
38 10 2 va_start(ap, fmt);
39 10 2 git_error_vset(GIT_ERROR_PATCH, fmt, ap);
40 10 3 va_end(ap);
41 -
42 10 3 return GIT_EAPPLYFAIL;
43 - }
44 -
45 1303 2 static void patch_line_init(
46 - git_diff_line *out,
47 - const char *in,
48 - size_t in_len,
49 - size_t in_offset)
50 - {
51 1303 2 out->content = in;
52 1303 2 out->content_len = in_len;
53 1303 2 out->content_offset = in_offset;
54 1303 2 }
55 -
56 - #define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
57 -
58 123 2 static int patch_image_init_fromstr(
59 - patch_image *out, const char *in, size_t in_len)
60 - {
61 - git_diff_line *line;
62 - const char *start, *end;
63 -
64 123 2 memset(out, 0x0, sizeof(patch_image));
65 -
66 123 2,3 if (git_pool_init(&out->pool, sizeof(git_diff_line)) < 0)
67 ##### 4 return -1;
68 -
69 1426 5,17,18 for (start = in; start < in + in_len; start = end) {
70 1303 6 end = memchr(start, '\n', in_len - (start - in));
71 -
72 1303 6 if (end == NULL)
73 2 7 end = in + in_len;
74 -
75 1301 8 else if (end < in + in_len)
76 1301 9 end++;
77 -
78 1303 10 line = git_pool_mallocz(&out->pool, 1);
79 1303 11,12 GIT_ERROR_CHECK_ALLOC(line);
80 -
81 1303 13,14 if (git_vector_insert(&out->lines, line) < 0)
82 ##### 15 return -1;
83 -
84 1303 16 patch_line_init(line, start, (end - start), (start - in));
85 - }
86 -
87 123 19 return 0;
88 - }
89 -
90 443 2 static void patch_image_free(patch_image *image)
91 - {
92 443 2 if (image == NULL)
93 443 3,6 return;
94 -
95 443 4 git_pool_clear(&image->pool);
96 443 5 git_vector_free(&image->lines);
97 - }
98 -
99 148 2 static bool match_hunk(
100 - patch_image *image,
101 - patch_image *preimage,
102 - size_t linenum)
103 - {
104 148 2 bool match = 0;
105 - size_t i;
106 -
107 - /* Ensure this hunk is within the image boundaries. */
108 148 2,4 if (git_vector_length(&preimage->lines) + linenum >
109 148 3 git_vector_length(&image->lines))
110 ##### 5 return 0;
111 -
112 148 6 match = 1;
113 -
114 - /* Check exact match. */
115 565 6,12-14 for (i = 0; i < git_vector_length(&preimage->lines); i++) {
116 421 7 git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
117 421 8 git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
118 -
119 421 9,10 if (preimage_line->content_len != image_line->content_len ||
120 418 10 memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
121 4 11 match = 0;
122 4 11 break;
123 - }
124 - }
125 -
126 148 15 return match;
127 - }
128 -
129 148 2 static bool find_hunk_linenum(
130 - size_t *out,
131 - patch_image *image,
132 - patch_image *preimage,
133 - size_t linenum)
134 - {
135 148 2 size_t max = git_vector_length(&image->lines);
136 - bool match;
137 -
138 148 3 if (linenum > max)
139 ##### 4 linenum = max;
140 -
141 148 5 match = match_hunk(image, preimage, linenum);
142 -
143 148 6 *out = linenum;
144 148 6 return match;
145 - }
146 -
147 144 2 static int update_hunk(
148 - patch_image *image,
149 - size_t linenum,
150 - patch_image *preimage,
151 - patch_image *postimage)
152 - {
153 144 2 size_t postlen = git_vector_length(&postimage->lines);
154 144 3 size_t prelen = git_vector_length(&preimage->lines);
155 - size_t i;
156 144 4 int error = 0;
157 -
158 144 4 if (postlen > prelen)
159 31 5 error = git_vector_insert_null(
160 - &image->lines, linenum, (postlen - prelen));
161 113 6 else if (prelen > postlen)
162 15 7 error = git_vector_remove_range(
163 - &image->lines, linenum, (prelen - postlen));
164 -
165 144 8 if (error) {
166 ##### 9 git_error_set_oom();
167 ##### 10 return -1;
168 - }
169 -
170 587 11,13-15 for (i = 0; i < git_vector_length(&postimage->lines); i++) {
171 443 12,13 image->lines.contents[linenum + i] =
172 443 12 git_vector_get(&postimage->lines, i);
173 - }
174 -
175 144 16 return 0;
176 - }
177 -
178 - typedef struct {
179 - git_apply_options opts;
180 - size_t skipped_new_lines;
181 - size_t skipped_old_lines;
182 - } apply_hunks_ctx;
183 -
184 160 2 static int apply_hunk(
185 - patch_image *image,
186 - git_patch *patch,
187 - git_patch_hunk *hunk,
188 - apply_hunks_ctx *ctx)
189 - {
190 160 2 patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
191 - size_t line_num, i;
192 160 2 int error = 0;
193 -
194 160 2 if (ctx->opts.hunk_cb) {
195 23 3 error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload);
196 -
197 23 4 if (error) {
198 12 5 if (error > 0) {
199 8 6 ctx->skipped_new_lines += hunk->hunk.new_lines;
200 8 6 ctx->skipped_old_lines += hunk->hunk.old_lines;
201 8 6 error = 0;
202 - }
203 -
204 12 7 goto done;
205 - }
206 - }
207 -
208 800 8,39,40 for (i = 0; i < hunk->line_count; i++) {
209 652 9 size_t linenum = hunk->line_start + i;
210 652 9-11 git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
211 -
212 652 12 if (!line) {
213 ##### 13 error = apply_err("preimage does not contain line %"PRIuZ, linenum);
214 ##### 14 goto done;
215 - }
216 -
217 652 15 switch (line->origin) {
218 - case GIT_DIFF_LINE_CONTEXT_EOFNL:
219 - case GIT_DIFF_LINE_DEL_EOFNL:
220 - case GIT_DIFF_LINE_ADD_EOFNL:
221 6 16-21 prev = i ? git_array_get(patch->lines, linenum - 1) : NULL;
222 6 22,23 if (prev && prev->content[prev->content_len - 1] == '\n')
223 1 24 prev->content_len -= 1;
224 6 25 break;
225 - case GIT_DIFF_LINE_CONTEXT:
226 229 26-29 if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
227 - (error = git_vector_insert(&postimage.lines, line)) < 0)
228 - goto done;
229 229 30 break;
230 - case GIT_DIFF_LINE_DELETION:
231 195 31,32 if ((error = git_vector_insert(&preimage.lines, line)) < 0)
232 ##### 33 goto done;
233 195 34 break;
234 - case GIT_DIFF_LINE_ADDITION:
235 222 35,36 if ((error = git_vector_insert(&postimage.lines, line)) < 0)
236 ##### 37 goto done;
237 222 38 break;
238 - }
239 - }
240 -
241 148 41 if (hunk->hunk.new_start) {
242 141 42,42,42 line_num = hunk->hunk.new_start -
243 141 42,42 ctx->skipped_new_lines +
244 141 42,42 ctx->skipped_old_lines -
245 - 1;
246 - } else {
247 7 43 line_num = 0;
248 - }
249 -
250 148 44,45 if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
251 4 46 error = apply_err("hunk at line %d did not apply",
252 - hunk->hunk.new_start);
253 4 47 goto done;
254 - }
255 -
256 144 48 error = update_hunk(image, line_num, &preimage, &postimage);
257 -
258 - done:
259 160 49 patch_image_free(&preimage);
260 160 50 patch_image_free(&postimage);
261 -
262 160 51 return error;
263 - }
264 -
265 123 2 static int apply_hunks(
266 - git_buf *out,
267 - const char *source,
268 - size_t source_len,
269 - git_patch *patch,
270 - apply_hunks_ctx *ctx)
271 - {
272 - git_patch_hunk *hunk;
273 - git_diff_line *line;
274 - patch_image image;
275 - size_t i;
276 123 2 int error = 0;
277 -
278 123 2,3 if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
279 ##### 4 goto done;
280 -
281 275 5,9-11 git_array_foreach(patch->hunks, i, hunk) {
282 160 6,7 if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
283 8 8 goto done;
284 - }
285 -
286 1383 12,14-16 git_vector_foreach(&image.lines, i, line)
287 1268 13 git_buf_put(out, line->content, line->content_len);
288 -
289 - done:
290 123 17 patch_image_free(&image);
291 -
292 123 18 return error;
293 - }
294 -
295 24 2 static int apply_binary_delta(
296 - git_buf *out,
297 - const char *source,
298 - size_t source_len,
299 - git_diff_binary_file *binary_file)
300 - {
301 24 2 git_buf inflated = GIT_BUF_INIT;
302 24 2 int error = 0;
303 -
304 - /* no diff means identical contents */
305 24 2 if (binary_file->datalen == 0)
306 ##### 3 return git_buf_put(out, source, source_len);
307 -
308 24 4,4 error = git_zstream_inflatebuf(&inflated,
309 24 4 binary_file->data, binary_file->datalen);
310 -
311 24 5,6 if (!error && inflated.size != binary_file->inflatedlen) {
312 ##### 7 error = apply_err("inflated delta does not match expected length");
313 ##### 8 git_buf_dispose(out);
314 - }
315 -
316 24 9 if (error < 0)
317 ##### 10 goto done;
318 -
319 24 11 if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
320 - void *data;
321 - size_t data_len;
322 -
323 11 12,12 error = git_delta_apply(&data, &data_len, (void *)source, source_len,
324 11 12 (void *)inflated.ptr, inflated.size);
325 -
326 11 13 out->ptr = data;
327 11 13 out->size = data_len;
328 11 13 out->asize = data_len;
329 - }
330 13 14 else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
331 13 15 git_buf_swap(out, &inflated);
332 - }
333 - else {
334 ##### 16 error = apply_err("unknown binary delta type");
335 ##### 17 goto done;
336 - }
337 -
338 - done:
339 24 18 git_buf_dispose(&inflated);
340 24 19 return error;
341 - }
342 -
343 13 2 static int apply_binary(
344 - git_buf *out,
345 - const char *source,
346 - size_t source_len,
347 - git_patch *patch)
348 - {
349 13 2 git_buf reverse = GIT_BUF_INIT;
350 13 2 int error = 0;
351 -
352 13 2 if (!patch->binary.contains_data) {
353 ##### 3 error = apply_err("patch does not contain binary data");
354 ##### 4 goto done;
355 - }
356 -
357 13 5,6 if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
358 1 7 goto done;
359 -
360 - /* first, apply the new_file delta to the given source */
361 12 8,9 if ((error = apply_binary_delta(out, source, source_len,
362 - &patch->binary.new_file)) < 0)
363 ##### 10 goto done;
364 -
365 - /* second, apply the old_file delta to sanity check the result */
366 12 11,12 if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
367 - &patch->binary.old_file)) < 0)
368 1 13 goto done;
369 -
370 - /* Verify that the resulting file with the reverse patch applied matches the source file */
371 11 14,15 if (source_len != reverse.size ||
372 9 16 (source_len && memcmp(source, reverse.ptr, source_len) != 0)) {
373 1 17 error = apply_err("binary patch did not apply cleanly");
374 1 18 goto done;
375 - }
376 -
377 - done:
378 13 19 if (error < 0)
379 2 20 git_buf_dispose(out);
380 -
381 13 21 git_buf_dispose(&reverse);
382 13 22 return error;
383 - }
384 -
385 150 2 int git_apply__patch(
386 - git_buf *contents_out,
387 - char **filename_out,
388 - unsigned int *mode_out,
389 - const char *source,
390 - size_t source_len,
391 - git_patch *patch,
392 - const git_apply_options *given_opts)
393 - {
394 150 2 apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
395 150 2 char *filename = NULL;
396 150 2 unsigned int mode = 0;
397 150 2 int error = 0;
398 -
399 150 2-8 assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
400 -
401 150 9 if (given_opts)
402 91 10 memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
403 -
404 150 11 *filename_out = NULL;
405 150 11 *mode_out = 0;
406 -
407 150 11 if (patch->delta->status != GIT_DELTA_DELETED) {
408 146 12 const git_diff_file *newfile = &patch->delta->new_file;
409 -
410 146 12 filename = git__strdup(newfile->path);
411 146 13-16 mode = newfile->mode ?
412 136 14 newfile->mode : GIT_FILEMODE_BLOB;
413 - }
414 -
415 150 17 if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
416 13 18 error = apply_binary(contents_out, source, source_len, patch);
417 137 19 else if (patch->hunks.size)
418 123 20 error = apply_hunks(contents_out, source, source_len, patch, &ctx);
419 - else
420 14 21 error = git_buf_put(contents_out, source, source_len);
421 -
422 150 22 if (error)
423 10 23 goto done;
424 -
425 140 24,26 if (patch->delta->status == GIT_DELTA_DELETED &&
426 4 25 git_buf_len(contents_out) > 0) {
427 ##### 27 error = apply_err("removal patch leaves file contents");
428 ##### 28 goto done;
429 - }
430 -
431 140 29 *filename_out = filename;
432 140 29 *mode_out = mode;
433 -
434 - done:
435 150 30 if (error < 0)
436 10 31 git__free(filename);
437 -
438 150 32 return error;
439 - }
440 -
441 94 2 static int apply_one(
442 - git_repository *repo,
443 - git_reader *preimage_reader,
444 - git_index *preimage,
445 - git_reader *postimage_reader,
446 - git_index *postimage,
447 - git_diff *diff,
448 - git_strmap *removed_paths,
449 - size_t i,
450 - const git_apply_options *opts)
451 - {
452 94 2 git_patch *patch = NULL;
453 94 2 git_buf pre_contents = GIT_BUF_INIT, post_contents = GIT_BUF_INIT;
454 - const git_diff_delta *delta;
455 94 2 char *filename = NULL;
456 - unsigned int mode;
457 - git_oid pre_id, post_id;
458 - git_filemode_t pre_filemode;
459 - git_index_entry pre_entry, post_entry;
460 94 2 bool skip_preimage = false;
461 - int error;
462 -
463 94 2,3 if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
464 ##### 4 goto done;
465 -
466 94 5 delta = git_patch_get_delta(patch);
467 -
468 94 6 if (opts->delta_cb) {
469 4 7 error = opts->delta_cb(delta, opts->payload);
470 -
471 4 8 if (error) {
472 2 9 if (error > 0)
473 1 10 error = 0;
474 -
475 2 11 goto done;
476 - }
477 - }
478 -
479 - /*
480 - * Ensure that the file has not been deleted or renamed if we're
481 - * applying a modification delta.
482 - */
483 92 12,13 if (delta->status != GIT_DELTA_RENAMED &&
484 79 13 delta->status != GIT_DELTA_ADDED) {
485 72 14,15 if (git_strmap_exists(removed_paths, delta->old_file.path)) {
486 2 16 error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
487 2 17 goto done;
488 - }
489 - }
490 -
491 - /*
492 - * We may be applying a second delta to an already seen file. If so,
493 - * use the already modified data in the postimage instead of the
494 - * content from the index or working directory. (Don't do this in
495 - * the case of a rename, which must be specified before additional
496 - * deltas since we apply deltas to the target filename.)
497 - */
498 90 18 if (delta->status != GIT_DELTA_RENAMED) {
499 77 19,20 if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
500 - postimage_reader, delta->old_file.path)) == 0) {
501 5 21 skip_preimage = true;
502 72 22 } else if (error == GIT_ENOTFOUND) {
503 72 23 git_error_clear();
504 72 24 error = 0;
505 - } else {
506 ##### 25 goto done;
507 - }
508 - }
509 -
510 90 26,27 if (!skip_preimage && delta->status != GIT_DELTA_ADDED) {
511 78 28 error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
512 - preimage_reader, delta->old_file.path);
513 -
514 - /* ENOTFOUND means the preimage was not found; apply failed. */
515 78 29 if (error == GIT_ENOTFOUND)
516 2 30 error = GIT_EAPPLYFAIL;
517 -
518 - /* When applying to BOTH, the index did not match the workdir. */
519 78 31 if (error == GIT_READER_MISMATCH)
520 3 32 error = apply_err("%s: does not match index", delta->old_file.path);
521 -
522 78 33 if (error < 0)
523 5 34 goto done;
524 -
525 - /*
526 - * We need to populate the preimage data structure with the
527 - * contents that we are using as the preimage for this file.
528 - * This allows us to apply patches to files that have been
529 - * modified in the working directory. During checkout,
530 - * we will use this expected preimage as the baseline, and
531 - * limit checkout to only the paths affected by patch
532 - * application. (Without this, we would fail to write the
533 - * postimage contents to any file that had been modified
534 - * from HEAD on-disk, even if the patch application succeeded.)
535 - * Use the contents from the delta where available - some
536 - * fields may not be available, like the old file mode (eg in
537 - * an exact rename situation) so trust the patch parsing to
538 - * validate and use the preimage data in that case.
539 - */
540 73 35 if (preimage) {
541 73 36 memset(&pre_entry, 0, sizeof(git_index_entry));
542 73 36 pre_entry.path = delta->old_file.path;
543 73 36-38 pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode;
544 73 39 git_oid_cpy(&pre_entry.id, &pre_id);
545 -
546 73 40,41 if ((error = git_index_add(preimage, &pre_entry)) < 0)
547 ##### 42 goto done;
548 - }
549 - }
550 -
551 85 43 if (delta->status != GIT_DELTA_DELETED) {
552 79 44,44,45 if ((error = git_apply__patch(&post_contents, &filename, &mode,
553 79 44,46,47 pre_contents.ptr, pre_contents.size, patch, opts)) < 0 ||
554 76 46 (error = git_blob_create_from_buffer(&post_id, repo,
555 76 46 post_contents.ptr, post_contents.size)) < 0)
556 - goto done;
557 -
558 76 48 memset(&post_entry, 0, sizeof(git_index_entry));
559 76 48 post_entry.path = filename;
560 76 48 post_entry.mode = mode;
561 76 48 git_oid_cpy(&post_entry.id, &post_id);
562 -
563 76 49,50 if ((error = git_index_add(postimage, &post_entry)) < 0)
564 ##### 51 goto done;
565 - }
566 -
567 82 52,53 if (delta->status == GIT_DELTA_RENAMED ||
568 69 53 delta->status == GIT_DELTA_DELETED)
569 19 54 error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
570 -
571 82 55,56 if (delta->status == GIT_DELTA_RENAMED ||
572 69 56 delta->status == GIT_DELTA_ADDED)
573 20 57 git_strmap_delete(removed_paths, delta->new_file.path);
574 -
575 - done:
576 94 58 git_buf_dispose(&pre_contents);
577 94 59 git_buf_dispose(&post_contents);
578 94 60 git__free(filename);
579 94 61 git_patch_free(patch);
580 -
581 94 62 return error;
582 - }
583 -
584 55 2 static int apply_deltas(
585 - git_repository *repo,
586 - git_reader *pre_reader,
587 - git_index *preimage,
588 - git_reader *post_reader,
589 - git_index *postimage,
590 - git_diff *diff,
591 - const git_apply_options *opts)
592 - {
593 - git_strmap *removed_paths;
594 - size_t i;
595 55 2 int error = 0;
596 -
597 55 2,3 if (git_strmap_new(&removed_paths) < 0)
598 ##### 4 return -1;
599 -
600 138 5,9-11 for (i = 0; i < git_diff_num_deltas(diff); i++) {
601 94 6,7 if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0)
602 11 8 goto done;
603 - }
604 -
605 - done:
606 55 12 git_strmap_free(removed_paths);
607 55 13 return error;
608 - }
609 -
610 2 2 int git_apply_to_tree(
611 - git_index **out,
612 - git_repository *repo,
613 - git_tree *preimage,
614 - git_diff *diff,
615 - const git_apply_options *given_opts)
616 - {
617 2 2 git_index *postimage = NULL;
618 2 2 git_reader *pre_reader = NULL, *post_reader = NULL;
619 2 2 git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
620 - const git_diff_delta *delta;
621 - size_t i;
622 2 2 int error = 0;
623 -
624 2 2-6 assert(out && repo && preimage && diff);
625 -
626 2 7 *out = NULL;
627 -
628 2 7 if (given_opts)
629 ##### 8 memcpy(&opts, given_opts, sizeof(git_apply_options));
630 -
631 2 9,10 if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0)
632 ##### 11 goto done;
633 -
634 - /*
635 - * put the current tree into the postimage as-is - the diff will
636 - * replace any entries contained therein
637 - */
638 2 12-15 if ((error = git_index_new(&postimage)) < 0 ||
639 2 14,16,17 (error = git_index_read_tree(postimage, preimage)) < 0 ||
640 2 16 (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
641 - goto done;
642 -
643 - /*
644 - * Remove the old paths from the index before applying diffs -
645 - * we need to do a full pass to remove them before adding deltas,
646 - * in order to handle rename situations.
647 - */
648 5 18,25-27 for (i = 0; i < git_diff_num_deltas(diff); i++) {
649 3 19 delta = git_diff_get_delta(diff, i);
650 -
651 3 20,21 if (delta->status == GIT_DELTA_DELETED ||
652 3 21 delta->status == GIT_DELTA_RENAMED) {
653 ##### 22,23 if ((error = git_index_remove(postimage,
654 - delta->old_file.path, 0)) < 0)
655 ##### 24 goto done;
656 - }
657 - }
658 -
659 2 28,29 if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0)
660 ##### 30 goto done;
661 -
662 2 31 *out = postimage;
663 -
664 - done:
665 2 32 if (error < 0)
666 ##### 33 git_index_free(postimage);
667 -
668 2 34 git_reader_free(pre_reader);
669 2 35 git_reader_free(post_reader);
670 -
671 2 36 return error;
672 - }
673 -
674 31 2 static int git_apply__to_workdir(
675 - git_repository *repo,
676 - git_diff *diff,
677 - git_index *preimage,
678 - git_index *postimage,
679 - git_apply_location_t location,
680 - git_apply_options *opts)
681 - {
682 31 2 git_vector paths = GIT_VECTOR_INIT;
683 31 2 git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
684 - const git_diff_delta *delta;
685 - size_t i;
686 - int error;
687 -
688 - GIT_UNUSED(opts);
689 -
690 - /*
691 - * Limit checkout to the paths affected by the diff; this ensures
692 - * that other modifications in the working directory are unaffected.
693 - */
694 31 2-4 if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0)
695 ##### 5 goto done;
696 -
697 84 6,15-17 for (i = 0; i < git_diff_num_deltas(diff); i++) {
698 53 7 delta = git_diff_get_delta(diff, i);
699 -
700 53 8,9 if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0)
701 ##### 10 goto done;
702 -
703 53 11-13 if (strcmp(delta->old_file.path, delta->new_file.path) &&
704 12 12 (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0)
705 ##### 14 goto done;
706 - }
707 -
708 31 18 checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
709 31 18 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
710 31 18 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
711 -
712 31 18 if (location == GIT_APPLY_LOCATION_WORKDIR)
713 12 19 checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
714 -
715 31 20 checkout_opts.paths.strings = (char **)paths.contents;
716 31 20 checkout_opts.paths.count = paths.length;
717 -
718 31 20 checkout_opts.baseline_index = preimage;
719 -
720 31 20 error = git_checkout_index(repo, postimage, &checkout_opts);
721 -
722 - done:
723 31 21 git_vector_free(&paths);
724 31 22 return error;
725 - }
726 -
727 8 2 static int git_apply__to_index(
728 - git_repository *repo,
729 - git_diff *diff,
730 - git_index *preimage,
731 - git_index *postimage,
732 - git_apply_options *opts)
733 - {
734 8 2 git_index *index = NULL;
735 - const git_diff_delta *delta;
736 - const git_index_entry *entry;
737 - size_t i;
738 - int error;
739 -
740 - GIT_UNUSED(preimage);
741 - GIT_UNUSED(opts);
742 -
743 8 2,3 if ((error = git_repository_index(&index, repo)) < 0)
744 ##### 4 goto done;
745 -
746 - /* Remove deleted (or renamed) paths from the index. */
747 21 5,12-14 for (i = 0; i < git_diff_num_deltas(diff); i++) {
748 13 6 delta = git_diff_get_delta(diff, i);
749 -
750 13 7,8 if (delta->status == GIT_DELTA_DELETED ||
751 12 8 delta->status == GIT_DELTA_RENAMED) {
752 1 9,10 if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
753 ##### 11 goto done;
754 - }
755 - }
756 -
757 - /* Then add the changes back to the index. */
758 20 15,20-22 for (i = 0; i < git_index_entrycount(postimage); i++) {
759 12 16 entry = git_index_get_byindex(postimage, i);
760 -
761 12 17,18 if ((error = git_index_add(index, entry)) < 0)
762 ##### 19 goto done;
763 - }
764 -
765 - done:
766 8 23 git_index_free(index);
767 8 24 return error;
768 - }
769 -
770 1 2 int git_apply_options_init(git_apply_options *opts, unsigned int version)
771 - {
772 1 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
773 - opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
774 1 5 return 0;
775 - }
776 -
777 - /*
778 - * Handle the three application options ("locations"):
779 - *
780 - * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
781 - * Applies the diff only to the workdir items and ignores the index
782 - * entirely.
783 - *
784 - * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
785 - * Applies the diff only to the index items and ignores the workdir
786 - * completely.
787 - *
788 - * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
789 - * Applies the diff to both the index items and the working directory
790 - * items.
791 - */
792 -
793 53 2 int git_apply(
794 - git_repository *repo,
795 - git_diff *diff,
796 - git_apply_location_t location,
797 - const git_apply_options *given_opts)
798 - {
799 53 2 git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
800 53 2 git_index *index = NULL, *preimage = NULL, *postimage = NULL;
801 53 2 git_reader *pre_reader = NULL, *post_reader = NULL;
802 53 2 git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
803 53 2 int error = GIT_EINVALID;
804 -
805 53 2-4 assert(repo && diff);
806 -
807 53 5-7 GIT_ERROR_CHECK_VERSION(
808 - given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");
809 -
810 53 8 if (given_opts)
811 9 9 memcpy(&opts, given_opts, sizeof(git_apply_options));
812 -
813 - /*
814 - * by default, we apply a patch directly to the working directory;
815 - * in `--cached` or `--index` mode, we apply to the contents already
816 - * in the index.
817 - */
818 53 10 switch (location) {
819 - case GIT_APPLY_LOCATION_BOTH:
820 27 11 error = git_reader_for_workdir(&pre_reader, repo, true);
821 27 17 break;
822 - case GIT_APPLY_LOCATION_INDEX:
823 13 12 error = git_reader_for_index(&pre_reader, repo, NULL);
824 13 13 break;
825 - case GIT_APPLY_LOCATION_WORKDIR:
826 13 14 error = git_reader_for_workdir(&pre_reader, repo, false);
827 13 15 break;
828 - default:
829 - 16 assert(false);
830 - }
831 -
832 53 18 if (error < 0)
833 ##### 19 goto done;
834 -
835 - /*
836 - * Build the preimage and postimage (differences). Note that
837 - * this is not the complete preimage or postimage, it only
838 - * contains the files affected by the patch. We want to avoid
839 - * having the full repo index, so we will limit our checkout
840 - * to only write these files that were affected by the diff.
841 - */
842 53 20-23 if ((error = git_index_new(&preimage)) < 0 ||
843 53 24,25 (error = git_index_new(&postimage)) < 0 ||
844 53 24 (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
845 - goto done;
846 -
847 53 26 if (!(opts.flags & GIT_APPLY_CHECK))
848 49 27-30 if ((error = git_repository_index(&index, repo)) < 0 ||
849 49 29 (error = git_indexwriter_init(&indexwriter, index)) < 0)
850 - goto done;
851 -
852 53 31,32 if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
853 11 33 goto done;
854 -
855 42 34 if ((opts.flags & GIT_APPLY_CHECK))
856 3 35 goto done;
857 -
858 39 36 switch (location) {
859 - case GIT_APPLY_LOCATION_BOTH:
860 19 37 error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
861 19 43 break;
862 - case GIT_APPLY_LOCATION_INDEX:
863 8 38 error = git_apply__to_index(repo, diff, preimage, postimage, &opts);
864 8 39 break;
865 - case GIT_APPLY_LOCATION_WORKDIR:
866 12 40 error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
867 12 41 break;
868 - default:
869 - 42 assert(false);
870 - }
871 -
872 39 44 if (error < 0)
873 ##### 45 goto done;
874 -
875 39 46 error = git_indexwriter_commit(&indexwriter);
876 -
877 - done:
878 53 47 git_indexwriter_cleanup(&indexwriter);
879 53 48 git_index_free(postimage);
880 53 49 git_index_free(preimage);
881 53 50 git_index_free(index);
882 53 51 git_reader_free(pre_reader);
883 53 52 git_reader_free(post_reader);
884 -
885 53 53 return error;
886 - }