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