File Coverage

deps/libgit2/src/libgit2/diff_generate.c
Criterion Covered Total %
statement 599 870 68.8
branch 344 688 50.0
condition n/a
subroutine n/a
pod n/a
total 943 1558 60.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 "diff_generate.h"
9              
10             #include "diff.h"
11             #include "patch_generate.h"
12             #include "futils.h"
13             #include "config.h"
14             #include "attr_file.h"
15             #include "filter.h"
16             #include "pathspec.h"
17             #include "index.h"
18             #include "odb.h"
19             #include "submodule.h"
20              
21             #define DIFF_FLAG_IS_SET(DIFF,FLAG) \
22             (((DIFF)->base.opts.flags & (FLAG)) != 0)
23             #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
24             (((DIFF)->base.opts.flags & (FLAG)) == 0)
25             #define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->base.opts.flags = \
26             (VAL) ? ((DIFF)->base.opts.flags | (FLAG)) : \
27             ((DIFF)->base.opts.flags & ~(FLAG))
28              
29             typedef struct {
30             struct git_diff base;
31              
32             git_vector pathspec;
33              
34             uint32_t diffcaps;
35             bool index_updated;
36             } git_diff_generated;
37              
38 293           static git_diff_delta *diff_delta__alloc(
39             git_diff_generated *diff,
40             git_delta_t status,
41             const char *path)
42             {
43 293           git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
44 293 50         if (!delta)
45 0           return NULL;
46              
47 293           delta->old_file.path = git_pool_strdup(&diff->base.pool, path);
48 293 50         if (delta->old_file.path == NULL) {
49 0           git__free(delta);
50 0           return NULL;
51             }
52              
53 293           delta->new_file.path = delta->old_file.path;
54              
55 293 100         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
56 2           switch (status) {
57 1           case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
58 0           case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
59 1           default: break; /* leave other status values alone */
60             }
61             }
62 293           delta->status = status;
63              
64 293           return delta;
65             }
66              
67 293           static int diff_insert_delta(
68             git_diff_generated *diff,
69             git_diff_delta *delta,
70             const char *matched_pathspec)
71             {
72 293           int error = 0;
73              
74 293 50         if (diff->base.opts.notify_cb) {
75 0           error = diff->base.opts.notify_cb(
76 0           &diff->base, delta, matched_pathspec, diff->base.opts.payload);
77              
78 0 0         if (error) {
79 0           git__free(delta);
80              
81 0 0         if (error > 0) /* positive value means to skip this delta */
82 0           return 0;
83             else /* negative value means to cancel diff */
84 0           return git_error_set_after_callback_function(error, "git_diff");
85             }
86             }
87              
88 293 50         if ((error = git_vector_insert(&diff->base.deltas, delta)) < 0)
89 0           git__free(delta);
90              
91 293           return error;
92             }
93              
94 677           static bool diff_pathspec_match(
95             const char **matched_pathspec,
96             git_diff_generated *diff,
97             const git_index_entry *entry)
98             {
99 677           bool disable_pathspec_match =
100 677           DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH);
101              
102             /* If we're disabling fnmatch, then the iterator has already applied
103             * the filters to the files for us and we don't have to do anything.
104             * However, this only applies to *files* - the iterator will include
105             * directories that we need to recurse into when not autoexpanding,
106             * so we still need to apply the pathspec match to directories.
107             */
108 677 50         if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) &&
    100          
    100          
109             disable_pathspec_match) {
110 39           *matched_pathspec = entry->path;
111 39           return true;
112             }
113              
114 638           return git_pathspec__match(
115 638           &diff->pathspec, entry->path, disable_pathspec_match,
116 638           DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
117             matched_pathspec, NULL);
118             }
119              
120 586           static void diff_delta__flag_known_size(git_diff_file *file)
121             {
122             /*
123             * If we don't know the ID, that can only come from the workdir
124             * iterator, which means we *do* know the file size. This is a
125             * leaky abstraction, but alas. Otherwise, we test against the
126             * empty blob id.
127             */
128 586 100         if (file->size ||
    50          
129 434 100         !(file->flags & GIT_DIFF_FLAG_VALID_ID) ||
130 434           git_oid_equal(&file->id, &git_oid__empty_blob_sha1))
131 164           file->flags |= GIT_DIFF_FLAG_VALID_SIZE;
132 586           }
133              
134 293           static void diff_delta__flag_known_sizes(git_diff_delta *delta)
135             {
136 293           diff_delta__flag_known_size(&delta->old_file);
137 293           diff_delta__flag_known_size(&delta->new_file);
138 293           }
139              
140 281           static int diff_delta__from_one(
141             git_diff_generated *diff,
142             git_delta_t status,
143             const git_index_entry *oitem,
144             const git_index_entry *nitem)
145             {
146 281           const git_index_entry *entry = nitem;
147 281           bool has_old = false;
148             git_diff_delta *delta;
149             const char *matched_pathspec;
150              
151 281 50         GIT_ASSERT_ARG((oitem != NULL) ^ (nitem != NULL));
152              
153 281 100         if (oitem) {
154 21           entry = oitem;
155 21           has_old = true;
156             }
157              
158 281 100         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
159 1           has_old = !has_old;
160              
161 281 50         if ((entry->flags & GIT_INDEX_ENTRY_VALID) != 0)
162 0           return 0;
163              
164 281 100         if (status == GIT_DELTA_IGNORED &&
    100          
165 9           DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
166 3           return 0;
167              
168 278 100         if (status == GIT_DELTA_UNTRACKED &&
    100          
169 202           DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
170 136           return 0;
171              
172 142 50         if (status == GIT_DELTA_UNREADABLE &&
    0          
173 0           DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
174 0           return 0;
175              
176 142 50         if (!diff_pathspec_match(&matched_pathspec, diff, entry))
177 0           return 0;
178              
179 142           delta = diff_delta__alloc(diff, status, entry->path);
180 142 50         GIT_ERROR_CHECK_ALLOC(delta);
181              
182             /* This fn is just for single-sided diffs */
183 142 50         GIT_ASSERT(status != GIT_DELTA_MODIFIED);
184 142           delta->nfiles = 1;
185              
186 142 100         if (has_old) {
187 22           delta->old_file.mode = entry->mode;
188 22           delta->old_file.size = entry->file_size;
189 22           delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
190 22           git_oid_cpy(&delta->old_file.id, &entry->id);
191 22           delta->old_file.id_abbrev = GIT_OID_HEXSZ;
192             } else /* ADDED, IGNORED, UNTRACKED */ {
193 120           delta->new_file.mode = entry->mode;
194 120           delta->new_file.size = entry->file_size;
195 120           delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
196 120           git_oid_cpy(&delta->new_file.id, &entry->id);
197 120           delta->new_file.id_abbrev = GIT_OID_HEXSZ;
198             }
199              
200 142           delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
201              
202 142 100         if (has_old || !git_oid_is_zero(&delta->new_file.id))
    100          
203 70           delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
204              
205 142           diff_delta__flag_known_sizes(delta);
206              
207 281           return diff_insert_delta(diff, delta, matched_pathspec);
208             }
209              
210 533           static int diff_delta__from_two(
211             git_diff_generated *diff,
212             git_delta_t status,
213             const git_index_entry *old_entry,
214             uint32_t old_mode,
215             const git_index_entry *new_entry,
216             uint32_t new_mode,
217             const git_oid *new_id,
218             const char *matched_pathspec)
219             {
220 533           const git_oid *old_id = &old_entry->id;
221             git_diff_delta *delta;
222 533           const char *canonical_path = old_entry->path;
223              
224 533 100         if (status == GIT_DELTA_UNMODIFIED &&
    100          
225 470           DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
226 382           return 0;
227              
228 151 100         if (!new_id)
229 139           new_id = &new_entry->id;
230              
231 151 100         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
232 1           uint32_t temp_mode = old_mode;
233 1           const git_index_entry *temp_entry = old_entry;
234 1           const git_oid *temp_id = old_id;
235              
236 1           old_entry = new_entry;
237 1           new_entry = temp_entry;
238 1           old_mode = new_mode;
239 1           new_mode = temp_mode;
240 1           old_id = new_id;
241 1           new_id = temp_id;
242             }
243              
244 151           delta = diff_delta__alloc(diff, status, canonical_path);
245 151 50         GIT_ERROR_CHECK_ALLOC(delta);
246 151           delta->nfiles = 2;
247              
248 151 50         if (!git_index_entry_is_conflict(old_entry)) {
249 151           delta->old_file.size = old_entry->file_size;
250 151           delta->old_file.mode = old_mode;
251 151           git_oid_cpy(&delta->old_file.id, old_id);
252 151           delta->old_file.id_abbrev = GIT_OID_HEXSZ;
253 151           delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID |
254             GIT_DIFF_FLAG_EXISTS;
255             }
256              
257 151 50         if (!git_index_entry_is_conflict(new_entry)) {
258 151           git_oid_cpy(&delta->new_file.id, new_id);
259 151           delta->new_file.id_abbrev = GIT_OID_HEXSZ;
260 151           delta->new_file.size = new_entry->file_size;
261 151           delta->new_file.mode = new_mode;
262 151           delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
263 151           delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
264              
265 151 100         if (!git_oid_is_zero(&new_entry->id))
266 132           delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
267             }
268              
269 151           diff_delta__flag_known_sizes(delta);
270              
271 151           return diff_insert_delta(diff, delta, matched_pathspec);
272             }
273              
274 41           static git_diff_delta *diff_delta__last_for_item(
275             git_diff_generated *diff,
276             const git_index_entry *item)
277             {
278 41           git_diff_delta *delta = git_vector_last(&diff->base.deltas);
279 41 100         if (!delta)
280 29           return NULL;
281              
282 12           switch (delta->status) {
283             case GIT_DELTA_UNMODIFIED:
284             case GIT_DELTA_DELETED:
285 0 0         if (git_oid__cmp(&delta->old_file.id, &item->id) == 0)
286 0           return delta;
287 0           break;
288             case GIT_DELTA_ADDED:
289 0 0         if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
290 0           return delta;
291 0           break;
292             case GIT_DELTA_UNREADABLE:
293             case GIT_DELTA_UNTRACKED:
294 22           if (diff->base.strcomp(delta->new_file.path, item->path) == 0 &&
295 11           git_oid__cmp(&delta->new_file.id, &item->id) == 0)
296 11           return delta;
297 0           break;
298             case GIT_DELTA_MODIFIED:
299 0 0         if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 ||
    0          
300 0 0         (delta->new_file.mode == item->mode &&
301 0           git_oid__cmp(&delta->new_file.id, &item->id) == 0))
302 0           return delta;
303 0           break;
304             default:
305 1           break;
306             }
307              
308 1           return NULL;
309             }
310              
311 596           static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
312             {
313 596           size_t len = strlen(prefix);
314              
315             /* append '/' at end if needed */
316 596 50         if (len > 0 && prefix[len - 1] != '/')
    100          
317 6           return git_pool_strcat(pool, prefix, "/");
318             else
319 590           return git_pool_strndup(pool, prefix, len + 1);
320             }
321              
322 40           GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
323             {
324 40 50         return delta->old_file.path ?
325             delta->old_file.path : delta->new_file.path;
326             }
327              
328 20           static int diff_delta_i2w_cmp(const void *a, const void *b)
329             {
330 20           const git_diff_delta *da = a, *db = b;
331 20           int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
332 20 50         return val ? val : ((int)da->status - (int)db->status);
333             }
334              
335 0           static int diff_delta_i2w_casecmp(const void *a, const void *b)
336             {
337 0           const git_diff_delta *da = a, *db = b;
338 0           int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
339 0 0         return val ? val : ((int)da->status - (int)db->status);
340             }
341              
342 108           bool git_diff_delta__should_skip(
343             const git_diff_options *opts, const git_diff_delta *delta)
344             {
345 108 50         uint32_t flags = opts ? opts->flags : 0;
346              
347 108 50         if (delta->status == GIT_DELTA_UNMODIFIED &&
    0          
348 0           (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
349 0           return true;
350              
351 108 100         if (delta->status == GIT_DELTA_IGNORED &&
    50          
352 2           (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
353 0           return true;
354              
355 108 100         if (delta->status == GIT_DELTA_UNTRACKED &&
    50          
356 38           (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
357 0           return true;
358              
359 108 50         if (delta->status == GIT_DELTA_UNREADABLE &&
    0          
360 0           (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
361 0           return true;
362              
363 108           return false;
364             }
365              
366              
367 0           static const char *diff_mnemonic_prefix(
368             git_iterator_t type, bool left_side)
369             {
370 0           const char *pfx = "";
371              
372 0           switch (type) {
373 0           case GIT_ITERATOR_EMPTY: pfx = "c"; break;
374 0           case GIT_ITERATOR_TREE: pfx = "c"; break;
375 0           case GIT_ITERATOR_INDEX: pfx = "i"; break;
376 0           case GIT_ITERATOR_WORKDIR: pfx = "w"; break;
377 0 0         case GIT_ITERATOR_FS: pfx = left_side ? "1" : "2"; break;
378 0           default: break;
379             }
380              
381             /* note: without a deeper look at pathspecs, there is no easy way
382             * to get the (o)bject / (w)ork tree mnemonics working...
383             */
384              
385 0           return pfx;
386             }
387              
388 298           static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
389             {
390 298 50         if (!ignore_case) {
391 298           diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
392              
393 298           diff->strcomp = git__strcmp;
394 298           diff->strncomp = git__strncmp;
395 298           diff->pfxcomp = git__prefixcmp;
396 298           diff->entrycomp = git_diff__entry_cmp;
397              
398 298           git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
399             } else {
400 0           diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
401              
402 0           diff->strcomp = git__strcasecmp;
403 0           diff->strncomp = git__strncasecmp;
404 0           diff->pfxcomp = git__prefixcmp_icase;
405 0           diff->entrycomp = git_diff__entry_icmp;
406              
407 0           git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
408             }
409              
410 298           git_vector_sort(&diff->deltas);
411 298           }
412              
413 298           static void diff_generated_free(git_diff *d)
414             {
415 298           git_diff_generated *diff = (git_diff_generated *)d;
416              
417 298           git_attr_session__free(&diff->base.attrsession);
418 298           git_vector_free_deep(&diff->base.deltas);
419              
420 298           git_pathspec__vfree(&diff->pathspec);
421 298           git_pool_clear(&diff->base.pool);
422              
423 298           git__memzero(diff, sizeof(*diff));
424 298           git__free(diff);
425 298           }
426              
427 298           static git_diff_generated *diff_generated_alloc(
428             git_repository *repo,
429             git_iterator *old_iter,
430             git_iterator *new_iter)
431             {
432             git_diff_generated *diff;
433 298           git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
434              
435 298 50         GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
436 298 50         GIT_ASSERT_ARG_WITH_RETVAL(old_iter, NULL);
437 298 50         GIT_ASSERT_ARG_WITH_RETVAL(new_iter, NULL);
438              
439 298 50         if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL)
440 0           return NULL;
441              
442 298           GIT_REFCOUNT_INC(&diff->base);
443 298           diff->base.type = GIT_DIFF_TYPE_GENERATED;
444 298           diff->base.repo = repo;
445 298           diff->base.old_src = old_iter->type;
446 298           diff->base.new_src = new_iter->type;
447 298           diff->base.patch_fn = git_patch_generated_from_diff;
448 298           diff->base.free_fn = diff_generated_free;
449 298           git_attr_session__init(&diff->base.attrsession, repo);
450 298           memcpy(&diff->base.opts, &dflt, sizeof(git_diff_options));
451              
452 596           if (git_pool_init(&diff->base.pool, 1) < 0 ||
453 298           git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
454 0           git_diff_free(&diff->base);
455 0           return NULL;
456             }
457              
458             /* Use case-insensitive compare if either iterator has
459             * the ignore_case bit set */
460 298           diff_set_ignore_case(
461 298           &diff->base,
462 596           git_iterator_ignore_case(old_iter) ||
463 298           git_iterator_ignore_case(new_iter));
464              
465 298           return diff;
466             }
467              
468 298           static int diff_generated_apply_options(
469             git_diff_generated *diff,
470             const git_diff_options *opts)
471             {
472 298           git_config *cfg = NULL;
473 298           git_repository *repo = diff->base.repo;
474 298           git_pool *pool = &diff->base.pool;
475             int val;
476              
477 298 100         if (opts) {
478             /* copy user options (except case sensitivity info from iterators) */
479 294           bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
480 294           memcpy(&diff->base.opts, opts, sizeof(diff->base.opts));
481 294 50         DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
482              
483             /* initialize pathspec from options */
484 294 50         if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
485 0           return -1;
486             }
487              
488             /* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
489 298 100         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
490 53           diff->base.opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
491              
492             /* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
493 298 50         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
494 0           diff->base.opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
495              
496             /* load config values that affect diff behavior */
497 298 50         if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
498 0           return val;
499              
500 298 50         if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_SYMLINKS) && val)
    50          
501 298           diff->diffcaps |= GIT_DIFFCAPS_HAS_SYMLINKS;
502              
503 298 50         if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_IGNORESTAT) && val)
    50          
504 0           diff->diffcaps |= GIT_DIFFCAPS_IGNORE_STAT;
505              
506 596           if ((diff->base.opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
507 596 50         !git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_FILEMODE) && val)
508 298           diff->diffcaps |= GIT_DIFFCAPS_TRUST_MODE_BITS;
509              
510 298 50         if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_TRUSTCTIME) && val)
    50          
511 298           diff->diffcaps |= GIT_DIFFCAPS_TRUST_CTIME;
512              
513             /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
514              
515             /* If not given explicit `opts`, check `diff.xyz` configs */
516 298 100         if (!opts) {
517 4           int context = git_config__get_int_force(cfg, "diff.context", 3);
518 4 50         diff->base.opts.context_lines = context >= 0 ? (uint32_t)context : 3;
519              
520             /* add other defaults here */
521             }
522              
523             /* Reverse src info if diff is reversed */
524 298 100         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
525 2           git_iterator_t tmp_src = diff->base.old_src;
526 2           diff->base.old_src = diff->base.new_src;
527 2           diff->base.new_src = tmp_src;
528             }
529              
530             /* Unset UPDATE_INDEX unless diffing workdir and index */
531 298 50         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) &&
    0          
532 0 0         (!(diff->base.old_src == GIT_ITERATOR_WORKDIR ||
533 0 0         diff->base.new_src == GIT_ITERATOR_WORKDIR) ||
534 0 0         !(diff->base.old_src == GIT_ITERATOR_INDEX ||
535 0           diff->base.new_src == GIT_ITERATOR_INDEX)))
536 0           diff->base.opts.flags &= ~GIT_DIFF_UPDATE_INDEX;
537              
538             /* if ignore_submodules not explicitly set, check diff config */
539 298 100         if (diff->base.opts.ignore_submodules <= 0) {
540             git_config_entry *entry;
541 280           git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true);
542              
543 280 50         if (entry && git_submodule_parse_ignore(
    0          
544 0           &diff->base.opts.ignore_submodules, entry->value) < 0)
545 0           git_error_clear();
546 280           git_config_entry_free(entry);
547             }
548              
549             /* if either prefix is not set, figure out appropriate value */
550 298 100         if (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) {
    50          
551 295           const char *use_old = DIFF_OLD_PREFIX_DEFAULT;
552 295           const char *use_new = DIFF_NEW_PREFIX_DEFAULT;
553              
554 295 50         if (git_config__get_bool_force(cfg, "diff.noprefix", 0))
555 0           use_old = use_new = "";
556 295 50         else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) {
557 0           use_old = diff_mnemonic_prefix(diff->base.old_src, true);
558 0           use_new = diff_mnemonic_prefix(diff->base.new_src, false);
559             }
560              
561 295 50         if (!diff->base.opts.old_prefix)
562 295           diff->base.opts.old_prefix = use_old;
563 295 50         if (!diff->base.opts.new_prefix)
564 295           diff->base.opts.new_prefix = use_new;
565             }
566              
567             /* strdup prefix from pool so we're not dependent on external data */
568 298           diff->base.opts.old_prefix = diff_strdup_prefix(pool, diff->base.opts.old_prefix);
569 298           diff->base.opts.new_prefix = diff_strdup_prefix(pool, diff->base.opts.new_prefix);
570              
571 298 100         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
572 2           const char *tmp_prefix = diff->base.opts.old_prefix;
573 2           diff->base.opts.old_prefix = diff->base.opts.new_prefix;
574 2           diff->base.opts.new_prefix = tmp_prefix;
575             }
576              
577 298           git_config_free(cfg);
578              
579             /* check strdup results for error */
580 298 50         return (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) ? -1 : 0;
    50          
581             }
582              
583 1           int git_diff__oid_for_file(
584             git_oid *out,
585             git_diff *diff,
586             const char *path,
587             uint16_t mode,
588             git_object_size_t size)
589             {
590             git_index_entry entry;
591              
592 1 50         if (size > UINT32_MAX) {
593 0           git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'", path);
594 0           return -1;
595             }
596              
597 1           memset(&entry, 0, sizeof(entry));
598 1           entry.mode = mode;
599 1           entry.file_size = (uint32_t)size;
600 1           entry.path = (char *)path;
601              
602 1           return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
603             }
604              
605 156           int git_diff__oid_for_entry(
606             git_oid *out,
607             git_diff *d,
608             const git_index_entry *src,
609             uint16_t mode,
610             const git_oid *update_match)
611             {
612             git_diff_generated *diff;
613 156           git_str full_path = GIT_STR_INIT;
614 156           git_index_entry entry = *src;
615 156           git_filter_list *fl = NULL;
616 156           int error = 0;
617              
618 156 50         GIT_ASSERT(d->type == GIT_DIFF_TYPE_GENERATED);
619 156           diff = (git_diff_generated *)d;
620              
621 156           memset(out, 0, sizeof(*out));
622              
623 156 50         if (git_repository_workdir_path(&full_path, diff->base.repo, entry.path) < 0)
624 0           return -1;
625              
626 156 50         if (!mode) {
627             struct stat st;
628              
629 0           diff->base.perf.stat_calls++;
630              
631 0 0         if (p_stat(full_path.ptr, &st) < 0) {
632 0           error = git_fs_path_set_error(errno, entry.path, "stat");
633 0           git_str_dispose(&full_path);
634 0           return error;
635             }
636              
637 0           git_index_entry__init_from_stat(&entry,
638 0           &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0);
639             }
640              
641             /* calculate OID for file if possible */
642 156 50         if (S_ISGITLINK(mode)) {
643             git_submodule *sm;
644              
645 0 0         if (!git_submodule_lookup(&sm, diff->base.repo, entry.path)) {
646 0           const git_oid *sm_oid = git_submodule_wd_id(sm);
647 0 0         if (sm_oid)
648 0           git_oid_cpy(out, sm_oid);
649 0           git_submodule_free(sm);
650             } else {
651             /* if submodule lookup failed probably just in an intermediate
652             * state where some init hasn't happened, so ignore the error
653             */
654 0           git_error_clear();
655             }
656 156 50         } else if (S_ISLNK(mode)) {
657 0           error = git_odb__hashlink(out, full_path.ptr);
658 0           diff->base.perf.oid_calculations++;
659 156 50         } else if (!git__is_sizet(entry.file_size)) {
660 0           git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'",
661             entry.path);
662 0           error = -1;
663 156 50         } else if (!(error = git_filter_list_load(&fl,
664             diff->base.repo, NULL, entry.path,
665             GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)))
666             {
667 156           int fd = git_futils_open_ro(full_path.ptr);
668 156 50         if (fd < 0)
669 0           error = fd;
670             else {
671 156           error = git_odb__hashfd_filtered(
672 156           out, fd, (size_t)entry.file_size, GIT_OBJECT_BLOB, fl);
673 156           p_close(fd);
674 156           diff->base.perf.oid_calculations++;
675             }
676              
677 156           git_filter_list_free(fl);
678             }
679              
680             /* update index for entry if requested */
681 156 50         if (!error && update_match && git_oid_equal(out, update_match)) {
    50          
    0          
682             git_index *idx;
683             git_index_entry updated_entry;
684              
685 0           memcpy(&updated_entry, &entry, sizeof(git_index_entry));
686 0           updated_entry.mode = mode;
687 0           git_oid_cpy(&updated_entry.id, out);
688              
689 0 0         if (!(error = git_repository_index__weakptr(&idx,
690             diff->base.repo))) {
691 0           error = git_index_add(idx, &updated_entry);
692 0           diff->index_updated = true;
693             }
694             }
695              
696 156           git_str_dispose(&full_path);
697 156           return error;
698             }
699              
700             typedef struct {
701             git_repository *repo;
702             git_iterator *old_iter;
703             git_iterator *new_iter;
704             const git_index_entry *oitem;
705             const git_index_entry *nitem;
706             git_strmap *submodule_cache;
707             bool submodule_cache_initialized;
708             } diff_in_progress;
709              
710             #define MODE_BITS_MASK 0000777
711              
712 0           static int maybe_modified_submodule(
713             git_delta_t *status,
714             git_oid *found_oid,
715             git_diff_generated *diff,
716             diff_in_progress *info)
717             {
718 0           int error = 0;
719             git_submodule *sub;
720 0           unsigned int sm_status = 0;
721 0           git_submodule_ignore_t ign = diff->base.opts.ignore_submodules;
722 0           git_strmap *submodule_cache = NULL;
723              
724 0           *status = GIT_DELTA_UNMODIFIED;
725              
726 0 0         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
    0          
727             ign == GIT_SUBMODULE_IGNORE_ALL)
728 0           return 0;
729              
730 0 0         if (diff->base.repo->submodule_cache != NULL) {
731 0           submodule_cache = diff->base.repo->submodule_cache;
732             } else {
733 0 0         if (!info->submodule_cache_initialized) {
734 0           info->submodule_cache_initialized = true;
735             /*
736             * Try to cache the submodule information to avoid having to parse it for
737             * every submodule. It is okay if it fails, the cache will still be NULL
738             * and the submodules will be attempted to be looked up individually.
739             */
740 0           git_submodule_cache_init(&info->submodule_cache, diff->base.repo);
741             }
742 0           submodule_cache = info->submodule_cache;
743             }
744              
745 0 0         if ((error = git_submodule__lookup_with_cache(
746 0           &sub, diff->base.repo, info->nitem->path, submodule_cache)) < 0) {
747              
748             /* GIT_EEXISTS means dir with .git in it was found - ignore it */
749 0 0         if (error == GIT_EEXISTS) {
750 0           git_error_clear();
751 0           error = 0;
752             }
753 0           return error;
754             }
755              
756 0 0         if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
    0          
757             /* ignore it */;
758 0 0         else if ((error = git_submodule__status(
759             &sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
760             /* return error below */;
761              
762             /* check IS_WD_UNMODIFIED because this case is only used
763             * when the new side of the diff is the working directory
764             */
765 0 0         else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
766 0           *status = GIT_DELTA_MODIFIED;
767              
768             /* now that we have a HEAD OID, check if HEAD moved */
769 0           else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
770 0           !git_oid_equal(&info->oitem->id, found_oid))
771 0           *status = GIT_DELTA_MODIFIED;
772              
773 0           git_submodule_free(sub);
774 0           return error;
775             }
776              
777 535           static int maybe_modified(
778             git_diff_generated *diff,
779             diff_in_progress *info)
780             {
781             git_oid noid;
782 535           git_delta_t status = GIT_DELTA_MODIFIED;
783 535           const git_index_entry *oitem = info->oitem;
784 535           const git_index_entry *nitem = info->nitem;
785 535           unsigned int omode = oitem->mode;
786 535           unsigned int nmode = nitem->mode;
787 535           bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_WORKDIR);
788 535           bool modified_uncertain = false;
789             const char *matched_pathspec;
790 535           int error = 0;
791              
792 535 100         if (!diff_pathspec_match(&matched_pathspec, diff, oitem))
793 2           return 0;
794              
795 533           memset(&noid, 0, sizeof(noid));
796              
797             /* on platforms with no symlinks, preserve mode of existing symlinks */
798 533 50         if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
    0          
    0          
    0          
799 0           !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
800 0           nmode = omode;
801              
802             /* on platforms with no execmode, just preserve old mode */
803 533 50         if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
    0          
804 0 0         (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
805             new_is_workdir)
806 0           nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
807              
808             /* if one side is a conflict, mark the whole delta as conflicted */
809 1066           if (git_index_entry_is_conflict(oitem) ||
810 533           git_index_entry_is_conflict(nitem)) {
811 0           status = GIT_DELTA_CONFLICTED;
812              
813             /* support "assume unchanged" (poorly, b/c we still stat everything) */
814 533 50         } else if ((oitem->flags & GIT_INDEX_ENTRY_VALID) != 0) {
815 0           status = GIT_DELTA_UNMODIFIED;
816              
817             /* support "skip worktree" index bit */
818 533 50         } else if ((oitem->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE) != 0) {
819 0           status = GIT_DELTA_UNMODIFIED;
820              
821             /* if basic type of file changed, then split into delete and add */
822 533 50         } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
823 0 0         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) {
824 0           status = GIT_DELTA_TYPECHANGE;
825             }
826              
827 0 0         else if (nmode == GIT_FILEMODE_UNREADABLE) {
828 0 0         if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
829 0           error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem);
830 0           return error;
831             }
832              
833             else {
834 0 0         if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
835 0           error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
836 0           return error;
837             }
838              
839             /* if oids and modes match (and are valid), then file is unmodified */
840 533 100         } else if (git_oid_equal(&oitem->id, &nitem->id) &&
    50          
841 289 50         omode == nmode &&
842 289           !git_oid_is_zero(&oitem->id)) {
843 289           status = GIT_DELTA_UNMODIFIED;
844              
845             /* if we have an unknown OID and a workdir iterator, then check some
846             * circumstances that can accelerate things or need special handling
847             */
848 444 100         } else if (git_oid_is_zero(&nitem->id) && new_is_workdir) {
    50          
849 200           bool use_ctime =
850 200           ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
851 200           git_index *index = git_iterator_index(info->new_iter);
852              
853 200           status = GIT_DELTA_UNMODIFIED;
854              
855 200 50         if (S_ISGITLINK(nmode)) {
856 0 0         if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0)
857 0           return error;
858             }
859              
860             /* if the stat data looks different, then mark modified - this just
861             * means that the OID will be recalculated below to confirm change
862             */
863 200 50         else if (omode != nmode || oitem->file_size != nitem->file_size) {
    100          
864 26           status = GIT_DELTA_MODIFIED;
865 26           modified_uncertain =
866 26 100         (oitem->file_size <= 0 && nitem->file_size > 0);
    50          
867             }
868 174 50         else if (!git_index_time_eq(&oitem->mtime, &nitem->mtime) ||
    50          
869 174 50         (use_ctime && !git_index_time_eq(&oitem->ctime, &nitem->ctime)) ||
    50          
870 174 50         oitem->ino != nitem->ino ||
871 174 50         oitem->uid != nitem->uid ||
872 174 100         oitem->gid != nitem->gid ||
873 174           git_index_entry_newer_than_index(nitem, index))
874             {
875 91           status = GIT_DELTA_MODIFIED;
876 91           modified_uncertain = true;
877             }
878              
879             /* if mode is GITLINK and submodules are ignored, then skip */
880 44 50         } else if (S_ISGITLINK(nmode) &&
    0          
881 0           DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) {
882 0           status = GIT_DELTA_UNMODIFIED;
883             }
884              
885             /* if we got here and decided that the files are modified, but we
886             * haven't calculated the OID of the new item, then calculate it now
887             */
888 533 100         if (modified_uncertain && git_oid_is_zero(&nitem->id)) {
    50          
889 110           const git_oid *update_check =
890 0 0         DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ?
891 110 50         &oitem->id : NULL;
892              
893 110 50         if ((error = git_diff__oid_for_entry(
894 110           &noid, &diff->base, nitem, nmode, update_check)) < 0)
895 0           return error;
896              
897             /* if oid matches, then mark unmodified (except submodules, where
898             * the filesystem content may be modified even if the oid still
899             * matches between the index and the workdir HEAD)
900             */
901 220 50         if (omode == nmode && !S_ISGITLINK(omode) &&
902 110           git_oid_equal(&oitem->id, &noid))
903 98           status = GIT_DELTA_UNMODIFIED;
904             }
905              
906             /* If we want case changes, then break this into a delete of the old
907             * and an add of the new so that consumers can act accordingly (eg,
908             * checkout will update the case on disk.)
909             */
910 533 50         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE) &&
    0          
911 0 0         DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) &&
912 0           strcmp(oitem->path, nitem->path) != 0) {
913              
914 0 0         if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
915 0           error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
916              
917 0           return error;
918             }
919              
920 535 100         return diff_delta__from_two(
921             diff, status, oitem, omode, nitem, nmode,
922 533           git_oid_is_zero(&noid) ? NULL : &noid, matched_pathspec);
923             }
924              
925 382           static bool entry_is_prefixed(
926             git_diff_generated *diff,
927             const git_index_entry *item,
928             const git_index_entry *prefix_item)
929             {
930             size_t pathlen;
931              
932 382 100         if (!item || diff->base.pfxcomp(item->path, prefix_item->path) != 0)
    100          
933 272           return false;
934              
935 110           pathlen = strlen(prefix_item->path);
936              
937 112 50         return (prefix_item->path[pathlen - 1] == '/' ||
938 112 100         item->path[pathlen] == '\0' ||
    50          
939 2           item->path[pathlen] == '/');
940             }
941              
942 596           static int iterator_current(
943             const git_index_entry **entry,
944             git_iterator *iterator)
945             {
946             int error;
947              
948 596 100         if ((error = git_iterator_current(entry, iterator)) == GIT_ITEROVER) {
949 50           *entry = NULL;
950 50           error = 0;
951             }
952              
953 596           return error;
954             }
955              
956 1340           static int iterator_advance(
957             const git_index_entry **entry,
958             git_iterator *iterator)
959             {
960 1340           const git_index_entry *prev_entry = *entry;
961             int cmp, error;
962              
963             /* if we're looking for conflicts, we only want to report
964             * one conflict for each file, instead of all three sides.
965             * so if this entry is a conflict for this file, and the
966             * previous one was a conflict for the same file, skip it.
967             */
968 1340 100         while ((error = git_iterator_advance(entry, iterator)) == 0) {
969 1107           if (!(iterator->flags & GIT_ITERATOR_INCLUDE_CONFLICTS) ||
970 312 0         !git_index_entry_is_conflict(prev_entry) ||
971 0           !git_index_entry_is_conflict(*entry))
972             break;
973              
974 0           cmp = (iterator->flags & GIT_ITERATOR_IGNORE_CASE) ?
975 0 0         strcasecmp(prev_entry->path, (*entry)->path) :
976 0           strcmp(prev_entry->path, (*entry)->path);
977              
978 0 0         if (cmp)
979 0           break;
980             }
981              
982 1340 100         if (error == GIT_ITEROVER) {
983 545           *entry = NULL;
984 545           error = 0;
985             }
986              
987 1340           return error;
988             }
989              
990 114           static int iterator_advance_into(
991             const git_index_entry **entry,
992             git_iterator *iterator)
993             {
994             int error;
995              
996 114 50         if ((error = git_iterator_advance_into(entry, iterator)) == GIT_ITEROVER) {
997 0           *entry = NULL;
998 0           error = 0;
999             }
1000              
1001 114           return error;
1002             }
1003              
1004 11           static int iterator_advance_over(
1005             const git_index_entry **entry,
1006             git_iterator_status_t *status,
1007             git_iterator *iterator)
1008             {
1009 11           int error = git_iterator_advance_over(entry, status, iterator);
1010              
1011 11 100         if (error == GIT_ITEROVER) {
1012 1           *entry = NULL;
1013 1           error = 0;
1014             }
1015              
1016 11           return error;
1017             }
1018              
1019 374           static int handle_unmatched_new_item(
1020             git_diff_generated *diff, diff_in_progress *info)
1021             {
1022 374           int error = 0;
1023 374           const git_index_entry *nitem = info->nitem;
1024 374           git_delta_t delta_type = GIT_DELTA_UNTRACKED;
1025             bool contains_oitem;
1026              
1027             /* check if this is a prefix of the other side */
1028 374           contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
1029              
1030             /* update delta_type if this item is conflicted */
1031 374 50         if (git_index_entry_is_conflict(nitem))
1032 0           delta_type = GIT_DELTA_CONFLICTED;
1033              
1034             /* update delta_type if this item is ignored */
1035 374 100         else if (git_iterator_current_is_ignored(info->new_iter))
1036 9           delta_type = GIT_DELTA_IGNORED;
1037              
1038 374 100         if (nitem->mode == GIT_FILEMODE_TREE) {
1039 155           bool recurse_into_dir = contains_oitem;
1040              
1041             /* check if user requests recursion into this type of dir */
1042 47 50         recurse_into_dir = contains_oitem ||
1043 47 100         (delta_type == GIT_DELTA_UNTRACKED &&
1044 202 100         DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
    50          
1045 0 0         (delta_type == GIT_DELTA_IGNORED &&
1046 0           DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
1047              
1048             /* do not advance into directories that contain a .git file */
1049 155 100         if (recurse_into_dir && !contains_oitem) {
    100          
1050 6           git_str *full = NULL;
1051 6 50         if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
1052 0           return -1;
1053 6 50         if (full && git_fs_path_contains(full, DOT_GIT)) {
    50          
1054             /* TODO: warning if not a valid git repository */
1055 6           recurse_into_dir = false;
1056             }
1057             }
1058              
1059             /* still have to look into untracked directories to match core git -
1060             * with no untracked files, directory is treated as ignored
1061             */
1062 155 100         if (!recurse_into_dir &&
    50          
1063 41 50         delta_type == GIT_DELTA_UNTRACKED &&
1064 41           DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
1065             {
1066             git_diff_delta *last;
1067             git_iterator_status_t untracked_state;
1068              
1069             /* attempt to insert record for this directory */
1070 41 50         if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
1071 0           return error;
1072              
1073             /* if delta wasn't created (because of rules), just skip ahead */
1074 41           last = diff_delta__last_for_item(diff, nitem);
1075 41 100         if (!last)
1076 30           return iterator_advance(&info->nitem, info->new_iter);
1077              
1078             /* iterate into dir looking for an actual untracked file */
1079 11 50         if ((error = iterator_advance_over(
1080             &info->nitem, &untracked_state, info->new_iter)) < 0)
1081 0           return error;
1082              
1083             /* if we found nothing that matched our pathlist filter, exclude */
1084 11 50         if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) {
1085 0           git_vector_pop(&diff->base.deltas);
1086 0           git__free(last);
1087             }
1088              
1089             /* if we found nothing or just ignored items, update the record */
1090 11 50         if (untracked_state == GIT_ITERATOR_STATUS_IGNORED ||
    100          
1091 11           untracked_state == GIT_ITERATOR_STATUS_EMPTY) {
1092 1           last->status = GIT_DELTA_IGNORED;
1093              
1094             /* remove the record if we don't want ignored records */
1095 1 50         if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) {
1096 0           git_vector_pop(&diff->base.deltas);
1097 0           git__free(last);
1098             }
1099             }
1100              
1101 41           return 0;
1102             }
1103              
1104             /* try to advance into directory if necessary */
1105 114 50         if (recurse_into_dir) {
1106 114           error = iterator_advance_into(&info->nitem, info->new_iter);
1107              
1108             /* if directory is empty, can't advance into it, so skip it */
1109 114 50         if (error == GIT_ENOTFOUND) {
1110 0           git_error_clear();
1111 0           error = iterator_advance(&info->nitem, info->new_iter);
1112             }
1113              
1114 114           return error;
1115             }
1116             }
1117              
1118 219 100         else if (delta_type == GIT_DELTA_IGNORED &&
    100          
1119 7 50         DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
1120 7           git_iterator_current_tree_is_ignored(info->new_iter))
1121             /* item contained in ignored directory, so skip over it */
1122 0           return iterator_advance(&info->nitem, info->new_iter);
1123              
1124 219 100         else if (info->new_iter->type != GIT_ITERATOR_WORKDIR) {
1125 49 50         if (delta_type != GIT_DELTA_CONFLICTED)
1126 49           delta_type = GIT_DELTA_ADDED;
1127             }
1128              
1129 170 50         else if (nitem->mode == GIT_FILEMODE_COMMIT) {
1130             /* ignore things that are not actual submodules */
1131 0 0         if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
1132 0           git_error_clear();
1133 0           delta_type = GIT_DELTA_IGNORED;
1134              
1135             /* if this contains a tracked item, treat as normal TREE */
1136 0 0         if (contains_oitem) {
1137 0           error = iterator_advance_into(&info->nitem, info->new_iter);
1138 0 0         if (error != GIT_ENOTFOUND)
1139 0           return error;
1140              
1141 0           git_error_clear();
1142 0           return iterator_advance(&info->nitem, info->new_iter);
1143             }
1144             }
1145             }
1146              
1147 170 50         else if (nitem->mode == GIT_FILEMODE_UNREADABLE) {
1148 0 0         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED))
1149 0           delta_type = GIT_DELTA_UNTRACKED;
1150             else
1151 0           delta_type = GIT_DELTA_UNREADABLE;
1152             }
1153              
1154             /* Actually create the record for this item if necessary */
1155 219 50         if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
1156 0           return error;
1157              
1158             /* If user requested TYPECHANGE records, then check for that instead of
1159             * just generating an ADDED/UNTRACKED record
1160             */
1161 219 100         if (delta_type != GIT_DELTA_IGNORED &&
    100          
1162 7 50         DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
1163             contains_oitem)
1164             {
1165             /* this entry was prefixed with a tree - make TYPECHANGE */
1166 0           git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
1167 0 0         if (last) {
1168 0           last->status = GIT_DELTA_TYPECHANGE;
1169 0           last->old_file.mode = GIT_FILEMODE_TREE;
1170             }
1171             }
1172              
1173 219           return iterator_advance(&info->nitem, info->new_iter);
1174             }
1175              
1176 21           static int handle_unmatched_old_item(
1177             git_diff_generated *diff, diff_in_progress *info)
1178             {
1179 21           git_delta_t delta_type = GIT_DELTA_DELETED;
1180             int error;
1181              
1182             /* update delta_type if this item is conflicted */
1183 21 50         if (git_index_entry_is_conflict(info->oitem))
1184 0           delta_type = GIT_DELTA_CONFLICTED;
1185              
1186 21 50         if ((error = diff_delta__from_one(diff, delta_type, info->oitem, NULL)) < 0)
1187 0           return error;
1188              
1189             /* if we are generating TYPECHANGE records then check for that
1190             * instead of just generating a DELETE record
1191             */
1192 29           if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
1193 8           entry_is_prefixed(diff, info->nitem, info->oitem))
1194             {
1195             /* this entry has become a tree! convert to TYPECHANGE */
1196 0           git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem);
1197 0 0         if (last) {
1198 0           last->status = GIT_DELTA_TYPECHANGE;
1199 0           last->new_file.mode = GIT_FILEMODE_TREE;
1200             }
1201              
1202             /* If new_iter is a workdir iterator, then this situation
1203             * will certainly be followed by a series of untracked items.
1204             * Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
1205             */
1206 0 0         if (S_ISDIR(info->nitem->mode) &&
    0          
1207 0           DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
1208 0           return iterator_advance(&info->nitem, info->new_iter);
1209             }
1210              
1211 21           return iterator_advance(&info->oitem, info->old_iter);
1212             }
1213              
1214 535           static int handle_matched_item(
1215             git_diff_generated *diff, diff_in_progress *info)
1216             {
1217 535           int error = 0;
1218              
1219 535 50         if ((error = maybe_modified(diff, info)) < 0)
1220 0           return error;
1221              
1222 535 50         if (!(error = iterator_advance(&info->oitem, info->old_iter)))
1223 535           error = iterator_advance(&info->nitem, info->new_iter);
1224              
1225 535           return error;
1226             }
1227              
1228 298           int git_diff__from_iterators(
1229             git_diff **out,
1230             git_repository *repo,
1231             git_iterator *old_iter,
1232             git_iterator *new_iter,
1233             const git_diff_options *opts)
1234             {
1235             git_diff_generated *diff;
1236 298           diff_in_progress info = {0};
1237 298           int error = 0;
1238              
1239 298           *out = NULL;
1240              
1241 298           diff = diff_generated_alloc(repo, old_iter, new_iter);
1242 298 50         GIT_ERROR_CHECK_ALLOC(diff);
1243              
1244 298           info.repo = repo;
1245 298           info.old_iter = old_iter;
1246 298           info.new_iter = new_iter;
1247              
1248             /* make iterators have matching icase behavior */
1249 298 50         if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
1250 0 0         if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
    0          
1251             (error = git_iterator_set_ignore_case(new_iter, true)) < 0)
1252             goto cleanup;
1253             }
1254              
1255             /* finish initialization */
1256 298 50         if ((error = diff_generated_apply_options(diff, opts)) < 0)
1257 0           goto cleanup;
1258              
1259 298 50         if ((error = iterator_current(&info.oitem, old_iter)) < 0 ||
    50          
1260             (error = iterator_current(&info.nitem, new_iter)) < 0)
1261             goto cleanup;
1262              
1263             /* run iterators building diffs */
1264 1228 50         while (!error && (info.oitem || info.nitem)) {
    100          
    100          
1265             int cmp;
1266              
1267             /* report progress */
1268 930 100         if (opts && opts->progress_cb) {
    50          
1269 0 0         if ((error = opts->progress_cb(&diff->base,
    0          
    0          
1270 0           info.oitem ? info.oitem->path : NULL,
1271 0           info.nitem ? info.nitem->path : NULL,
1272             opts->payload)))
1273 0           break;
1274             }
1275              
1276 1860           cmp = info.oitem ?
1277 930 100         (info.nitem ? diff->base.entrycomp(info.oitem, info.nitem) : -1) : 1;
    100          
1278              
1279             /* create DELETED records for old items not matched in new */
1280 930 100         if (cmp < 0)
1281 21           error = handle_unmatched_old_item(diff, &info);
1282              
1283             /* create ADDED, TRACKED, or IGNORED records for new items not
1284             * matched in old (and/or descend into directories as needed)
1285             */
1286 909 100         else if (cmp > 0)
1287 374           error = handle_unmatched_new_item(diff, &info);
1288              
1289             /* otherwise item paths match, so create MODIFIED record
1290             * (or ADDED and DELETED pair if type changed)
1291             */
1292             else
1293 535           error = handle_matched_item(diff, &info);
1294             }
1295              
1296 298           diff->base.perf.stat_calls +=
1297 298           old_iter->stat_calls + new_iter->stat_calls;
1298              
1299             cleanup:
1300 298 50         if (!error)
1301 298           *out = &diff->base;
1302             else
1303 0           git_diff_free(&diff->base);
1304 298 50         if (info.submodule_cache)
1305 0           git_submodule_cache_free(info.submodule_cache);
1306              
1307 298           return error;
1308             }
1309              
1310 227           static int diff_prepare_iterator_opts(char **prefix, git_iterator_options *a, int aflags,
1311             git_iterator_options *b, int bflags,
1312             const git_diff_options *opts)
1313             {
1314 227 50         GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
1315              
1316 227           *prefix = NULL;
1317              
1318 227 100         if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) {
    100          
1319 36           a->pathlist.strings = opts->pathspec.strings;
1320 36           a->pathlist.count = opts->pathspec.count;
1321 36           b->pathlist.strings = opts->pathspec.strings;
1322 36           b->pathlist.count = opts->pathspec.count;
1323 191 100         } else if (opts) {
1324 187           *prefix = git_pathspec_prefix(&opts->pathspec);
1325 187 50         GIT_ERROR_CHECK_ALLOC(prefix);
1326             }
1327              
1328 227           a->flags = aflags;
1329 227           b->flags = bflags;
1330 227           a->start = b->start = *prefix;
1331 227           a->end = b->end = *prefix;
1332              
1333 227           return 0;
1334             }
1335              
1336 18           int git_diff_tree_to_tree(
1337             git_diff **out,
1338             git_repository *repo,
1339             git_tree *old_tree,
1340             git_tree *new_tree,
1341             const git_diff_options *opts)
1342             {
1343 18           git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE;
1344 18           git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
1345 18           b_opts = GIT_ITERATOR_OPTIONS_INIT;
1346 18           git_iterator *a = NULL, *b = NULL;
1347 18           git_diff *diff = NULL;
1348 18           char *prefix = NULL;
1349 18           int error = 0;
1350              
1351 18 50         GIT_ASSERT_ARG(out);
1352 18 50         GIT_ASSERT_ARG(repo);
1353              
1354 18           *out = NULL;
1355              
1356             /* for tree to tree diff, be case sensitive even if the index is
1357             * currently case insensitive, unless the user explicitly asked
1358             * for case insensitivity
1359             */
1360 18 50         if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
    50          
1361 0           iflag = GIT_ITERATOR_IGNORE_CASE;
1362              
1363 18 50         if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, iflag, &b_opts, iflag, opts)) < 0 ||
    50          
1364 18 50         (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 ||
1365 18 50         (error = git_iterator_for_tree(&b, new_tree, &b_opts)) < 0 ||
1366 18           (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
1367             goto out;
1368              
1369 18           *out = diff;
1370 18           diff = NULL;
1371             out:
1372 18           git_iterator_free(a);
1373 18           git_iterator_free(b);
1374 18           git_diff_free(diff);
1375 18           git__free(prefix);
1376              
1377 18           return error;
1378             }
1379              
1380 16           static int diff_load_index(git_index **index, git_repository *repo)
1381             {
1382 16           int error = git_repository_index__weakptr(index, repo);
1383              
1384             /* reload the repository index when user did not pass one in */
1385 16 50         if (!error && git_index_read(*index, false) < 0)
    50          
1386 0           git_error_clear();
1387              
1388 16           return error;
1389             }
1390              
1391 95           int git_diff_tree_to_index(
1392             git_diff **out,
1393             git_repository *repo,
1394             git_tree *old_tree,
1395             git_index *index,
1396             const git_diff_options *opts)
1397             {
1398 95           git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE |
1399             GIT_ITERATOR_INCLUDE_CONFLICTS;
1400 95           git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
1401 95           b_opts = GIT_ITERATOR_OPTIONS_INIT;
1402 95           git_iterator *a = NULL, *b = NULL;
1403 95           git_diff *diff = NULL;
1404 95           char *prefix = NULL;
1405 95           bool index_ignore_case = false;
1406 95           int error = 0;
1407              
1408 95 50         GIT_ASSERT_ARG(out);
1409 95 50         GIT_ASSERT_ARG(repo);
1410              
1411 95           *out = NULL;
1412              
1413 95 50         if (!index && (error = diff_load_index(&index, repo)) < 0)
    0          
1414 0           return error;
1415              
1416 95           index_ignore_case = index->ignore_case;
1417              
1418 95 50         if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, iflag, &b_opts, iflag, opts)) < 0 ||
    50          
1419 95 50         (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 ||
1420 95 50         (error = git_iterator_for_index(&b, repo, index, &b_opts)) < 0 ||
1421 95           (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
1422             goto out;
1423              
1424             /* if index is in case-insensitive order, re-sort deltas to match */
1425 95 50         if (index_ignore_case)
1426 0           diff_set_ignore_case(diff, true);
1427              
1428 95           *out = diff;
1429 95           diff = NULL;
1430             out:
1431 95           git_iterator_free(a);
1432 95           git_iterator_free(b);
1433 95           git_diff_free(diff);
1434 95           git__free(prefix);
1435              
1436 95           return error;
1437             }
1438              
1439 112           int git_diff_index_to_workdir(
1440             git_diff **out,
1441             git_repository *repo,
1442             git_index *index,
1443             const git_diff_options *opts)
1444             {
1445 112           git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
1446 112           b_opts = GIT_ITERATOR_OPTIONS_INIT;
1447 112           git_iterator *a = NULL, *b = NULL;
1448 112           git_diff *diff = NULL;
1449 112           char *prefix = NULL;
1450 112           int error = 0;
1451              
1452 112 50         GIT_ASSERT_ARG(out);
1453 112 50         GIT_ASSERT_ARG(repo);
1454              
1455 112           *out = NULL;
1456              
1457 112 100         if (!index && (error = diff_load_index(&index, repo)) < 0)
    50          
1458 0           return error;
1459              
1460 112 50         if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, GIT_ITERATOR_INCLUDE_CONFLICTS,
1461 112 50         &b_opts, GIT_ITERATOR_DONT_AUTOEXPAND, opts)) < 0 ||
1462 112 50         (error = git_iterator_for_index(&a, repo, index, &a_opts)) < 0 ||
1463 112 50         (error = git_iterator_for_workdir(&b, repo, index, NULL, &b_opts)) < 0 ||
1464 112           (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
1465             goto out;
1466              
1467 112 50         if ((diff->opts.flags & GIT_DIFF_UPDATE_INDEX) && ((git_diff_generated *)diff)->index_updated)
    0          
1468 0 0         if ((error = git_index_write(index)) < 0)
1469 0           goto out;
1470              
1471 112           *out = diff;
1472 112           diff = NULL;
1473             out:
1474 112           git_iterator_free(a);
1475 112           git_iterator_free(b);
1476 112           git_diff_free(diff);
1477 112           git__free(prefix);
1478              
1479 112           return error;
1480             }
1481              
1482 2           int git_diff_tree_to_workdir(
1483             git_diff **out,
1484             git_repository *repo,
1485             git_tree *old_tree,
1486             const git_diff_options *opts)
1487             {
1488 2           git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
1489 2           b_opts = GIT_ITERATOR_OPTIONS_INIT;
1490 2           git_iterator *a = NULL, *b = NULL;
1491 2           git_diff *diff = NULL;
1492 2           char *prefix = NULL;
1493             git_index *index;
1494             int error;
1495              
1496 2 50         GIT_ASSERT_ARG(out);
1497 2 50         GIT_ASSERT_ARG(repo);
1498              
1499 2           *out = NULL;
1500              
1501 2 50         if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, 0,
1502 2 50         &b_opts, GIT_ITERATOR_DONT_AUTOEXPAND, opts) < 0) ||
1503 2 50         (error = git_repository_index__weakptr(&index, repo)) < 0 ||
1504 2 50         (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 ||
1505 2 50         (error = git_iterator_for_workdir(&b, repo, index, old_tree, &b_opts)) < 0 ||
1506 2           (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
1507             goto out;
1508              
1509 2           *out = diff;
1510 2           diff = NULL;
1511             out:
1512 2           git_iterator_free(a);
1513 2           git_iterator_free(b);
1514 2           git_diff_free(diff);
1515 2           git__free(prefix);
1516              
1517 2           return error;
1518             }
1519              
1520 0           int git_diff_tree_to_workdir_with_index(
1521             git_diff **out,
1522             git_repository *repo,
1523             git_tree *tree,
1524             const git_diff_options *opts)
1525             {
1526 0           git_diff *d1 = NULL, *d2 = NULL;
1527 0           git_index *index = NULL;
1528 0           int error = 0;
1529              
1530 0 0         GIT_ASSERT_ARG(out);
1531 0 0         GIT_ASSERT_ARG(repo);
1532              
1533 0           *out = NULL;
1534              
1535 0 0         if ((error = diff_load_index(&index, repo)) < 0)
1536 0           return error;
1537              
1538 0 0         if (!(error = git_diff_tree_to_index(&d1, repo, tree, index, opts)) &&
    0          
1539 0           !(error = git_diff_index_to_workdir(&d2, repo, index, opts)))
1540 0           error = git_diff_merge(d1, d2);
1541              
1542 0           git_diff_free(d2);
1543              
1544 0 0         if (error) {
1545 0           git_diff_free(d1);
1546 0           d1 = NULL;
1547             }
1548              
1549 0           *out = d1;
1550 0           return error;
1551             }
1552              
1553 0           int git_diff_index_to_index(
1554             git_diff **out,
1555             git_repository *repo,
1556             git_index *old_index,
1557             git_index *new_index,
1558             const git_diff_options *opts)
1559             {
1560 0           git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
1561 0           b_opts = GIT_ITERATOR_OPTIONS_INIT;
1562 0           git_iterator *a = NULL, *b = NULL;
1563 0           git_diff *diff = NULL;
1564 0           char *prefix = NULL;
1565             int error;
1566              
1567 0 0         GIT_ASSERT_ARG(out);
1568 0 0         GIT_ASSERT_ARG(old_index);
1569 0 0         GIT_ASSERT_ARG(new_index);
1570              
1571 0           *out = NULL;
1572              
1573 0 0         if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, GIT_ITERATOR_DONT_IGNORE_CASE,
1574 0 0         &b_opts, GIT_ITERATOR_DONT_IGNORE_CASE, opts) < 0) ||
1575 0 0         (error = git_iterator_for_index(&a, repo, old_index, &a_opts)) < 0 ||
1576 0 0         (error = git_iterator_for_index(&b, repo, new_index, &b_opts)) < 0 ||
1577 0           (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
1578             goto out;
1579              
1580             /* if index is in case-insensitive order, re-sort deltas to match */
1581 0 0         if (old_index->ignore_case || new_index->ignore_case)
    0          
1582 0           diff_set_ignore_case(diff, true);
1583              
1584 0           *out = diff;
1585 0           diff = NULL;
1586             out:
1587 0           git_iterator_free(a);
1588 0           git_iterator_free(b);
1589 0           git_diff_free(diff);
1590 0           git__free(prefix);
1591              
1592 0           return error;
1593             }
1594              
1595 63           int git_diff__paired_foreach(
1596             git_diff *head2idx,
1597             git_diff *idx2wd,
1598             int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
1599             void *payload)
1600             {
1601 63           int cmp, error = 0;
1602             git_diff_delta *h2i, *i2w;
1603             size_t i, j, i_max, j_max;
1604 63           int (*strcomp)(const char *, const char *) = git__strcmp;
1605             bool h2i_icase, i2w_icase, icase_mismatch;
1606              
1607 63 100         i_max = head2idx ? head2idx->deltas.length : 0;
1608 63 100         j_max = idx2wd ? idx2wd->deltas.length : 0;
1609 63 100         if (!i_max && !j_max)
    100          
1610 21           return 0;
1611              
1612             /* At some point, tree-to-index diffs will probably never ignore case,
1613             * even if that isn't true now. Index-to-workdir diffs may or may not
1614             * ignore case, but the index filename for the idx2wd diff should
1615             * still be using the canonical case-preserving name.
1616             *
1617             * Therefore the main thing we need to do here is make sure the diffs
1618             * are traversed in a compatible order. To do this, we temporarily
1619             * resort a mismatched diff to get the order correct.
1620             *
1621             * In order to traverse renames in the index->workdir, we need to
1622             * ensure that we compare the index name on both sides, so we
1623             * always sort by the old name in the i2w list.
1624             */
1625 42 100         h2i_icase = head2idx != NULL && git_diff_is_sorted_icase(head2idx);
    50          
1626 42 50         i2w_icase = idx2wd != NULL && git_diff_is_sorted_icase(idx2wd);
    50          
1627              
1628 42           icase_mismatch =
1629 42 100         (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
    50          
    50          
1630              
1631 42 50         if (icase_mismatch && h2i_icase) {
    0          
1632 0           git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
1633 0           git_vector_sort(&head2idx->deltas);
1634             }
1635              
1636 42 50         if (i2w_icase && !icase_mismatch) {
    0          
1637 0           strcomp = git__strcasecmp;
1638              
1639 0           git_vector_set_cmp(&idx2wd->deltas, diff_delta_i2w_casecmp);
1640 0           git_vector_sort(&idx2wd->deltas);
1641 42 50         } else if (idx2wd != NULL) {
1642 42           git_vector_set_cmp(&idx2wd->deltas, diff_delta_i2w_cmp);
1643 42           git_vector_sort(&idx2wd->deltas);
1644             }
1645              
1646 108 100         for (i = 0, j = 0; i < i_max || j < j_max; ) {
    100          
1647 66 100         h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
    100          
1648 66 50         i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
    100          
1649              
1650 66 100         cmp = !i2w ? -1 : !h2i ? 1 :
    100          
1651 21           strcomp(h2i->new_file.path, i2w->old_file.path);
1652              
1653 66 100         if (cmp < 0) {
1654 20           i++; i2w = NULL;
1655 46 100         } else if (cmp > 0) {
1656 36           j++; h2i = NULL;
1657             } else {
1658 10           i++; j++;
1659             }
1660              
1661 66 50         if ((error = cb(h2i, i2w, payload)) != 0) {
1662 0           git_error_set_after_callback(error);
1663 0           break;
1664             }
1665             }
1666              
1667             /* restore case-insensitive delta sort */
1668 42 50         if (icase_mismatch && h2i_icase) {
    0          
1669 0           git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
1670 0           git_vector_sort(&head2idx->deltas);
1671             }
1672              
1673             /* restore idx2wd sort by new path */
1674 42 50         if (idx2wd != NULL) {
1675 42 50         git_vector_set_cmp(&idx2wd->deltas,
1676             i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
1677 42           git_vector_sort(&idx2wd->deltas);
1678             }
1679              
1680 42           return error;
1681             }
1682              
1683 3           int git_diff__commit(
1684             git_diff **out,
1685             git_repository *repo,
1686             const git_commit *commit,
1687             const git_diff_options *opts)
1688             {
1689 3           git_commit *parent = NULL;
1690 3           git_diff *commit_diff = NULL;
1691 3           git_tree *old_tree = NULL, *new_tree = NULL;
1692             size_t parents;
1693 3           int error = 0;
1694              
1695 3           *out = NULL;
1696              
1697 3 50         if ((parents = git_commit_parentcount(commit)) > 1) {
1698             char commit_oidstr[GIT_OID_HEXSZ + 1];
1699              
1700 0           error = -1;
1701 0           git_error_set(GIT_ERROR_INVALID, "commit %s is a merge commit",
1702             git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
1703 0           goto on_error;
1704             }
1705              
1706 3 50         if (parents > 0)
1707 3 50         if ((error = git_commit_parent(&parent, commit, 0)) < 0 ||
    50          
1708 3           (error = git_commit_tree(&old_tree, parent)) < 0)
1709             goto on_error;
1710              
1711 3 50         if ((error = git_commit_tree(&new_tree, commit)) < 0 ||
    50          
1712 3           (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0)
1713             goto on_error;
1714              
1715 3           *out = commit_diff;
1716              
1717             on_error:
1718 3           git_tree_free(new_tree);
1719 3           git_tree_free(old_tree);
1720 3           git_commit_free(parent);
1721              
1722 3           return error;
1723             }
1724