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