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