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