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 "pathspec.h"
9 -
10 - #include "git2/pathspec.h"
11 - #include "git2/diff.h"
12 - #include "buf_text.h"
13 - #include "attr_file.h"
14 - #include "iterator.h"
15 - #include "repository.h"
16 - #include "index.h"
17 - #include "bitvec.h"
18 - #include "diff.h"
19 - #include "wildmatch.h"
20 -
21 - /* what is the common non-wildcard prefix for all items in the pathspec */
22 6240 2 char *git_pathspec_prefix(const git_strarray *pathspec)
23 - {
24 6240 2 git_buf prefix = GIT_BUF_INIT;
25 - const char *scan;
26 -
27 6240 2,3,5 if (!pathspec || !pathspec->count ||
28 3498 4 git_buf_text_common_prefix(&prefix, pathspec) < 0)
29 2742 6 return NULL;
30 -
31 - /* diff prefix will only be leading non-wildcards */
32 44891 7,12,13 for (scan = prefix.ptr; *scan; ++scan) {
33 41412 8-10 if (git__iswildcard(*scan) &&
34 19 10,11 (scan == prefix.ptr || (*(scan - 1) != '\\')))
35 - break;
36 - }
37 3498 14 git_buf_truncate(&prefix, scan - prefix.ptr);
38 -
39 3498 15 if (prefix.size <= 0) {
40 421 16 git_buf_dispose(&prefix);
41 421 17 return NULL;
42 - }
43 -
44 3077 18 git_buf_text_unescape(&prefix);
45 -
46 3077 19 return git_buf_detach(&prefix);
47 - }
48 -
49 - /* is there anything in the spec that needs to be filtered on */
50 7572 2 bool git_pathspec_is_empty(const git_strarray *pathspec)
51 - {
52 - size_t i;
53 -
54 7572 2 if (pathspec == NULL)
55 44 3 return true;
56 -
57 7531 4,8,9 for (i = 0; i < pathspec->count; ++i) {
58 4609 5 const char *str = pathspec->strings[i];
59 -
60 4609 5,6 if (str && str[0])
61 4606 7 return false;
62 - }
63 -
64 2922 10 return true;
65 - }
66 -
67 - /* build a vector of fnmatch patterns to evaluate efficiently */
68 7572 2 int git_pathspec__vinit(
69 - git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
70 - {
71 - size_t i;
72 -
73 7572 2 memset(vspec, 0, sizeof(*vspec));
74 -
75 7572 2,3 if (git_pathspec_is_empty(strspec))
76 2966 4 return 0;
77 -
78 4606 5,6 if (git_vector_init(vspec, strspec->count, NULL) < 0)
79 ##### 7 return -1;
80 -
81 10307 8,24,25 for (i = 0; i < strspec->count; ++i) {
82 - int ret;
83 5701 9 const char *pattern = strspec->strings[i];
84 5701 9 git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
85 5701 10 if (!match)
86 ##### 11,23 return -1;
87 -
88 5701 12 match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
89 -
90 5701 12 ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
91 5701 13 if (ret == GIT_ENOTFOUND) {
92 2 14 git__free(match);
93 2 15 continue;
94 5699 16 } else if (ret < 0) {
95 ##### 17 git__free(match);
96 ##### 18 return ret;
97 - }
98 -
99 5699 19,20 if (git_vector_insert(vspec, match) < 0)
100 5699 21,22 return -1;
101 - }
102 -
103 4606 26 return 0;
104 - }
105 -
106 - /* free data from the pathspec vector */
107 8490 2 void git_pathspec__vfree(git_vector *vspec)
108 - {
109 8490 2 git_vector_free_deep(vspec);
110 8490 3 }
111 -
112 - struct pathspec_match_context {
113 - int wildmatch_flags;
114 - int (*strcomp)(const char *, const char *);
115 - int (*strncomp)(const char *, const char *, size_t);
116 - };
117 -
118 113330 2 static void pathspec_match_context_init(
119 - struct pathspec_match_context *ctxt,
120 - bool disable_fnmatch,
121 - bool casefold)
122 - {
123 113330 2 if (disable_fnmatch)
124 28 3 ctxt->wildmatch_flags = -1;
125 113302 4 else if (casefold)
126 5 5 ctxt->wildmatch_flags = WM_CASEFOLD;
127 - else
128 113297 6 ctxt->wildmatch_flags = 0;
129 -
130 113330 7 if (casefold) {
131 5 8 ctxt->strcomp = git__strcasecmp;
132 5 8 ctxt->strncomp = git__strncasecmp;
133 - } else {
134 113325 9 ctxt->strcomp = git__strcmp;
135 113325 9 ctxt->strncomp = git__strncmp;
136 - }
137 113330 10 }
138 -
139 286080 2 static int pathspec_match_one(
140 - const git_attr_fnmatch *match,
141 - struct pathspec_match_context *ctxt,
142 - const char *path)
143 - {
144 286080 2 int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : WM_NOMATCH;
145 -
146 286080 2 if (result == WM_NOMATCH)
147 286045 3,4 result = ctxt->strcomp(match->pattern, path) ? WM_NOMATCH : 0;
148 -
149 286080 5,6 if (ctxt->wildmatch_flags >= 0 && result == WM_NOMATCH)
150 282557 7 result = wildmatch(match->pattern, path, ctxt->wildmatch_flags);
151 -
152 - /* if we didn't match, look for exact dirname prefix match */
153 286080 8,9 if (result == WM_NOMATCH &&
154 282407 9,11 (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
155 282205 10,12 ctxt->strncomp(path, match->pattern, match->length) == 0 &&
156 72 12 path[match->length] == '/')
157 55 13 result = 0;
158 -
159 - /* if we didn't match and this is a negative match, check for exact
160 - * match of filename with leading '!'
161 - */
162 286080 14,15 if (result == WM_NOMATCH &&
163 282352 15,16 (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
164 28 16,18 *path == '!' &&
165 ##### 17,19 ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
166 ##### 19,20 (!path[match->length + 1] || path[match->length + 1] == '/'))
167 ##### 21 return 1;
168 -
169 286080 22 if (result == 0)
170 3725 23 return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
171 282355 24 return -1;
172 - }
173 -
174 113574 2 static int git_pathspec__match_at(
175 - size_t *matched_at,
176 - const git_vector *vspec,
177 - struct pathspec_match_context *ctxt,
178 - const char *path0,
179 - const char *path1)
180 - {
181 113574 2 int result = GIT_ENOTFOUND;
182 113574 2 size_t i = 0;
183 - const git_attr_fnmatch *match;
184 -
185 395841 2,11-13 git_vector_foreach(vspec, i, match) {
186 285988 3-5 if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0)
187 3721 6 break;
188 282267 7-9 if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0)
189 ##### 10 break;
190 - }
191 -
192 113574 14 *matched_at = i;
193 113574 14 return result;
194 - }
195 -
196 - /* match a path against the vectorized pathspec */
197 - 2 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files)bool git_pathspec__match(
198 - const git_vector *vspec,
199 - const char *path,
200 - bool disable_fnmatch,
201 - bool casefold,
202 - const char **matched_pathspec,
203 - size_t *matched_at)
204 - {
205 - int result;
206 - size_t pos;
207 - struct pathspec_match_context ctxt;
208 -
209 - 2 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) if (matched_pathspec)
210 - 3 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) *matched_pathspec = NULL;
211 - 4 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) if (matched_at)
212 - 5 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) *matched_at = GIT_PATHSPEC_NOMATCH;
213 -
214 - 6,7 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) if (!vspec || !vspec->length)
215 - 8 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) return true;
216 -
217 - 9 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) pathspec_match_context_init(&ctxt, disable_fnmatch, casefold);
218 -
219 - 10 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL);
220 - 11 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) if (result >= 0) {
221 - 12 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) if (matched_pathspec) {
222 - 13 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) const git_attr_fnmatch *match = git_vector_get(vspec, pos);
223 - 14 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) *matched_pathspec = match->pattern;
224 - }
225 -
226 - 15 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) if (matched_at)
227 - 16 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) *matched_at = pos;
228 - }
229 -
230 - 17 suppressed: function cannot be solved git_pathspec__match (automatic due to inconsistent arc counts in .gcda files) return (result > 0);
231 - }
232 -
233 -
234 68 2 int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
235 - {
236 68 2 int error = 0;
237 -
238 68 2 memset(ps, 0, sizeof(*ps));
239 -
240 68 2 ps->prefix = git_pathspec_prefix(paths);
241 -
242 68 3-6 if ((error = git_pool_init(&ps->pool, 1)) < 0 ||
243 68 5 (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
244 ##### 7 git_pathspec__clear(ps);
245 -
246 68 8 return error;
247 - }
248 -
249 68 2 void git_pathspec__clear(git_pathspec *ps)
250 - {
251 68 2 git__free(ps->prefix);
252 68 3 git_pathspec__vfree(&ps->pathspec);
253 68 4 git_pool_clear(&ps->pool);
254 68 5 memset(ps, 0, sizeof(*ps));
255 68 5 }
256 -
257 10 2 int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec)
258 - {
259 10 2 int error = 0;
260 10 2 git_pathspec *ps = git__malloc(sizeof(git_pathspec));
261 10 3,4 GIT_ERROR_CHECK_ALLOC(ps);
262 -
263 10 5,6 if ((error = git_pathspec__init(ps, pathspec)) < 0) {
264 ##### 7 git__free(ps);
265 ##### 8 return error;
266 - }
267 -
268 10 9 GIT_REFCOUNT_INC(ps);
269 10 10 *out = ps;
270 10 10 return 0;
271 - }
272 -
273 10 2 static void pathspec_free(git_pathspec *ps)
274 - {
275 10 2 git_pathspec__clear(ps);
276 10 3 git__free(ps);
277 10 4 }
278 -
279 36 2 void git_pathspec_free(git_pathspec *ps)
280 - {
281 36 2 if (!ps)
282 36 3,8 return;
283 36 4-7 GIT_REFCOUNT_DEC(ps, pathspec_free);
284 - }
285 -
286 13 2 int git_pathspec_matches_path(
287 - const git_pathspec *ps, uint32_t flags, const char *path)
288 - {
289 13 2 bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
290 13 2 bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
291 -
292 13 2-4 assert(ps && path);
293 -
294 13 5 return (0 != git_pathspec__match(
295 - &ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
296 - }
297 -
298 26 2 static void pathspec_match_free(git_pathspec_match_list *m)
299 - {
300 26 2 if (!m)
301 26 3,9 return;
302 -
303 26 4 git_pathspec_free(m->pathspec);
304 26 5 m->pathspec = NULL;
305 -
306 26 5 git_array_clear(m->matches);
307 26 6 git_array_clear(m->failures);
308 26 7 git_pool_clear(&m->pool);
309 26 8 git__free(m);
310 - }
311 -
312 26 2 static git_pathspec_match_list *pathspec_match_alloc(
313 - git_pathspec *ps, int datatype)
314 - {
315 26 2 git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
316 26 3 if (!m)
317 ##### 4 return NULL;
318 -
319 26 5,6 if (git_pool_init(&m->pool, 1) < 0)
320 ##### 7 return NULL;
321 -
322 - /* need to keep reference to pathspec and increment refcount because
323 - * failures array stores pointers to the pattern strings of the
324 - * pathspec that had no matches
325 - */
326 26 8 GIT_REFCOUNT_INC(ps);
327 26 9 m->pathspec = ps;
328 26 9 m->datatype = datatype;
329 -
330 26 9 return m;
331 - }
332 -
333 161 2 GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos)
334 - {
335 161 2,3 if (!git_bitvec_get(used, pos)) {
336 26 4 git_bitvec_set(used, pos, true);
337 26 5 return 1;
338 - }
339 -
340 135 6 return 0;
341 - }
342 -
343 57 2 static size_t pathspec_mark_remaining(
344 - git_bitvec *used,
345 - git_vector *patterns,
346 - struct pathspec_match_context *ctxt,
347 - size_t start,
348 - const char *path0,
349 - const char *path1)
350 - {
351 57 2 size_t count = 0;
352 -
353 57 2 if (path1 == path0)
354 ##### 3 path1 = NULL;
355 -
356 164 4,19,20 for (; start < patterns->length; ++start) {
357 107 5 const git_attr_fnmatch *pat = git_vector_get(patterns, start);
358 -
359 107 6,7 if (git_bitvec_get(used, start))
360 25 8 continue;
361 -
362 82 9-11 if (path0 && pathspec_match_one(pat, ctxt, path0) > 0)
363 4 12,13 count += pathspec_mark_pattern(used, start);
364 78 14-16 else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0)
365 ##### 17,18 count += pathspec_mark_pattern(used, start);
366 - }
367 -
368 57 21 return count;
369 - }
370 -
371 9 2 static int pathspec_build_failure_array(
372 - git_pathspec_string_array_t *failures,
373 - git_vector *patterns,
374 - git_bitvec *used,
375 - git_pool *pool)
376 - {
377 - size_t pos;
378 - char **failed;
379 - const git_attr_fnmatch *pat;
380 -
381 32 2,18,19 for (pos = 0; pos < patterns->length; ++pos) {
382 23 3,4 if (git_bitvec_get(used, pos))
383 9 5 continue;
384 -
385 14 6-12 if ((failed = git_array_alloc(*failures)) == NULL)
386 ##### 13 return -1;
387 -
388 14 14 pat = git_vector_get(patterns, pos);
389 -
390 14 15,16 if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL)
391 ##### 17 return -1;
392 - }
393 -
394 9 20 return 0;
395 - }
396 -
397 23 2 static int pathspec_match_from_iterator(
398 - git_pathspec_match_list **out,
399 - git_iterator *iter,
400 - uint32_t flags,
401 - git_pathspec *ps)
402 - {
403 23 2 int error = 0;
404 23 2 git_pathspec_match_list *m = NULL;
405 23 2 const git_index_entry *entry = NULL;
406 - struct pathspec_match_context ctxt;
407 23 2 git_vector *patterns = &ps->pathspec;
408 23 2-5 bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
409 23 6-9 bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
410 23 10 size_t pos, used_ct = 0, found_files = 0;
411 23 10 git_index *index = NULL;
412 - git_bitvec used_patterns;
413 - char **file;
414 -
415 23 10,11 if (git_bitvec_init(&used_patterns, patterns->length) < 0)
416 ##### 12 return -1;
417 -
418 23 13 if (out) {
419 23 14 *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS);
420 23 15,16 GIT_ERROR_CHECK_ALLOC(m);
421 - }
422 -
423 23 17,18 if ((error = git_iterator_reset_range(iter, ps->prefix, ps->prefix)) < 0)
424 ##### 19 goto done;
425 -
426 23 20,21,23,24 if (git_iterator_type(iter) == GIT_ITERATOR_WORKDIR &&
427 15 22 (error = git_repository_index__weakptr(
428 - &index, git_iterator_owner(iter))) < 0)
429 ##### 25 goto done;
430 -
431 23 27,27 pathspec_match_context_init(
432 23 27 &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
433 23 26 git_iterator_ignore_case(iter));
434 -
435 269 60-62 while (!(error = git_iterator_advance(&entry, iter))) {
436 - /* search for match with entry->path */
437 246 28 int result = git_pathspec__match_at(
438 246 28 &pos, patterns, &ctxt, entry->path, NULL);
439 -
440 - /* no matches for this path */
441 246 29 if (result < 0)
442 95 30 continue;
443 -
444 - /* if result was a negative pattern match, then don't list file */
445 151 31 if (!result) {
446 6 32 used_ct += pathspec_mark_pattern(&used_patterns, pos);
447 6 33 continue;
448 - }
449 -
450 - /* check if path is ignored and untracked */
451 145 34,36 if (index != NULL &&
452 95 35,38 git_iterator_current_is_ignored(iter) &&
453 8 37 git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0)
454 8 39 continue;
455 -
456 - /* mark the matched pattern as used */
457 137 40 used_ct += pathspec_mark_pattern(&used_patterns, pos);
458 137 41 ++found_files;
459 -
460 - /* if find_failures is on, check if any later patterns also match */
461 137 41,42 if (find_failures && used_ct < patterns->length)
462 57 43,44 used_ct += pathspec_mark_remaining(
463 57 43 &used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL);
464 -
465 - /* if only looking at failures, exit early or just continue */
466 137 45,46 if (failures_only || !out) {
467 10 47 if (used_ct == patterns->length)
468 ##### 48 break;
469 10 49 continue;
470 - }
471 -
472 - /* insert matched path into matches array */
473 127 50-56,58 if ((file = (char **)git_array_alloc(m->matches)) == NULL ||
474 127 57 (*file = git_pool_strdup(&m->pool, entry->path)) == NULL) {
475 ##### 59 error = -1;
476 ##### 59 goto done;
477 - }
478 - }
479 -
480 23 63,64 if (error < 0 && error != GIT_ITEROVER)
481 ##### 65 goto done;
482 23 66 error = 0;
483 -
484 - /* insert patterns that had no matches into failures array */
485 23 66-69 if (find_failures && used_ct < patterns->length &&
486 9 68 (error = pathspec_build_failure_array(
487 - &m->failures, patterns, &used_patterns, &m->pool)) < 0)
488 ##### 70 goto done;
489 -
490 - /* if every pattern failed to match, then we have failed */
491 23 71,72 if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
492 2 73 git_error_set(GIT_ERROR_INVALID, "no matching files were found");
493 2 74 error = GIT_ENOTFOUND;
494 - }
495 -
496 - done:
497 23 75 git_bitvec_free(&used_patterns);
498 -
499 23 76 if (error < 0) {
500 2 77 pathspec_match_free(m);
501 2 78,79 if (out) *out = NULL;
502 - }
503 -
504 23 80 return error;
505 - }
506 -
507 23 2 static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags)
508 - {
509 23 2 git_iterator_flag_t f = 0;
510 -
511 23 2 if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0)
512 4 3 f |= GIT_ITERATOR_IGNORE_CASE;
513 19 4 else if ((flags & GIT_PATHSPEC_USE_CASE) != 0)
514 4 5 f |= GIT_ITERATOR_DONT_IGNORE_CASE;
515 -
516 23 6 return f;
517 - }
518 -
519 15 2 int git_pathspec_match_workdir(
520 - git_pathspec_match_list **out,
521 - git_repository *repo,
522 - uint32_t flags,
523 - git_pathspec *ps)
524 - {
525 - git_iterator *iter;
526 15 2 git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
527 15 2 int error = 0;
528 -
529 15 2,3 assert(repo);
530 -
531 15 4 iter_opts.flags = pathspec_match_iter_flags(flags);
532 -
533 15 5,6 if (!(error = git_iterator_for_workdir(&iter, repo, NULL, NULL, &iter_opts))) {
534 15 7 error = pathspec_match_from_iterator(out, iter, flags, ps);
535 15 8 git_iterator_free(iter);
536 - }
537 -
538 15 9 return error;
539 - }
540 -
541 2 2 int git_pathspec_match_index(
542 - git_pathspec_match_list **out,
543 - git_index *index,
544 - uint32_t flags,
545 - git_pathspec *ps)
546 - {
547 - git_iterator *iter;
548 2 2 git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
549 2 2 int error = 0;
550 -
551 2 2,3 assert(index);
552 -
553 2 4 iter_opts.flags = pathspec_match_iter_flags(flags);
554 -
555 2 5-7 if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) {
556 2 8 error = pathspec_match_from_iterator(out, iter, flags, ps);
557 2 9 git_iterator_free(iter);
558 - }
559 -
560 2 10 return error;
561 - }
562 -
563 6 2 int git_pathspec_match_tree(
564 - git_pathspec_match_list **out,
565 - git_tree *tree,
566 - uint32_t flags,
567 - git_pathspec *ps)
568 - {
569 - git_iterator *iter;
570 6 2 git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
571 6 2 int error = 0;
572 -
573 6 2,3 assert(tree);
574 -
575 6 4 iter_opts.flags = pathspec_match_iter_flags(flags);
576 -
577 6 5,6 if (!(error = git_iterator_for_tree(&iter, tree, &iter_opts))) {
578 6 7 error = pathspec_match_from_iterator(out, iter, flags, ps);
579 6 8 git_iterator_free(iter);
580 - }
581 -
582 6 9 return error;
583 - }
584 -
585 3 2 int git_pathspec_match_diff(
586 - git_pathspec_match_list **out,
587 - git_diff *diff,
588 - uint32_t flags,
589 - git_pathspec *ps)
590 - {
591 3 2 int error = 0;
592 3 2 git_pathspec_match_list *m = NULL;
593 - struct pathspec_match_context ctxt;
594 3 2 git_vector *patterns = &ps->pathspec;
595 3 2-5 bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
596 3 6-9 bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
597 3 10 size_t i, pos, used_ct = 0, found_deltas = 0;
598 - const git_diff_delta *delta, **match;
599 - git_bitvec used_patterns;
600 -
601 3 10,11 assert(diff);
602 -
603 3 12,13 if (git_bitvec_init(&used_patterns, patterns->length) < 0)
604 ##### 14 return -1;
605 -
606 3 15 if (out) {
607 3 16 *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF);
608 3 17,18 GIT_ERROR_CHECK_ALLOC(m);
609 - }
610 -
611 3 20,20 pathspec_match_context_init(
612 3 20 &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
613 3 19 git_diff_is_sorted_icase(diff));
614 -
615 27 21,46-48 git_vector_foreach(&diff->deltas, i, delta) {
616 - /* search for match with delta */
617 24 22 int result = git_pathspec__match_at(
618 - &pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path);
619 -
620 - /* no matches for this path */
621 24 23 if (result < 0)
622 10 24 continue;
623 -
624 - /* mark the matched pattern as used */
625 14 25 used_ct += pathspec_mark_pattern(&used_patterns, pos);
626 -
627 - /* if result was a negative pattern match, then don't list file */
628 14 26 if (!result)
629 ##### 27 continue;
630 -
631 14 28 ++found_deltas;
632 -
633 - /* if find_failures is on, check if any later patterns also match */
634 14 28,29 if (find_failures && used_ct < patterns->length)
635 ##### 30,31 used_ct += pathspec_mark_remaining(
636 - &used_patterns, patterns, &ctxt, pos + 1,
637 - delta->old_file.path, delta->new_file.path);
638 -
639 - /* if only looking at failures, exit early or just continue */
640 14 32,33 if (failures_only || !out) {
641 ##### 34 if (used_ct == patterns->length)
642 ##### 35 break;
643 ##### 36 continue;
644 - }
645 -
646 - /* insert matched delta into matches array */
647 14 37-43 if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) {
648 ##### 44 error = -1;
649 ##### 44 goto done;
650 - } else {
651 14 45 *match = delta;
652 - }
653 - }
654 -
655 - /* insert patterns that had no matches into failures array */
656 3 49-52 if (find_failures && used_ct < patterns->length &&
657 ##### 51 (error = pathspec_build_failure_array(
658 - &m->failures, patterns, &used_patterns, &m->pool)) < 0)
659 ##### 53 goto done;
660 -
661 - /* if every pattern failed to match, then we have failed */
662 3 54,55 if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
663 ##### 56 git_error_set(GIT_ERROR_INVALID, "no matching deltas were found");
664 ##### 57 error = GIT_ENOTFOUND;
665 - }
666 -
667 - done:
668 3 58 git_bitvec_free(&used_patterns);
669 -
670 3 59 if (error < 0) {
671 ##### 60 pathspec_match_free(m);
672 ##### 61,62 if (out) *out = NULL;
673 - }
674 -
675 3 63 return error;
676 - }
677 -
678 24 2 void git_pathspec_match_list_free(git_pathspec_match_list *m)
679 - {
680 24 2 if (m)
681 24 3 pathspec_match_free(m);
682 24 4 }
683 -
684 24 2 size_t git_pathspec_match_list_entrycount(
685 - const git_pathspec_match_list *m)
686 - {
687 24 2 return m ? git_array_size(m->matches) : 0;
688 - }
689 -
690 41 2 const char *git_pathspec_match_list_entry(
691 - const git_pathspec_match_list *m, size_t pos)
692 - {
693 41 2-4 if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS ||
694 38 4 !git_array_valid_index(m->matches, pos))
695 7 5 return NULL;
696 -
697 34 6 return *((const char **)git_array_get(m->matches, pos));
698 - }
699 -
700 10 2 const git_diff_delta *git_pathspec_match_list_diff_entry(
701 - const git_pathspec_match_list *m, size_t pos)
702 - {
703 10 2-4 if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF ||
704 9 4 !git_array_valid_index(m->matches, pos))
705 1 5 return NULL;
706 -
707 9 6 return *((const git_diff_delta **)git_array_get(m->matches, pos));
708 - }
709 -
710 15 2 size_t git_pathspec_match_list_failed_entrycount(
711 - const git_pathspec_match_list *m)
712 - {
713 15 2 return m ? git_array_size(m->failures) : 0;
714 - }
715 -
716 10 2 const char * git_pathspec_match_list_failed_entry(
717 - const git_pathspec_match_list *m, size_t pos)
718 - {
719 10 2-7 char **entry = m ? git_array_get(m->failures, pos) : NULL;
720 -
721 10 8 return entry ? *entry : NULL;
722 - }