source src/pathspec.c
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 | - | } |