File Coverage

deps/libgit2/src/libgit2/attr.c
Criterion Covered Total %
statement 195 308 63.3
branch 106 248 42.7
condition n/a
subroutine n/a
pod n/a
total 301 556 54.1


line stmt bran cond sub pod time code
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 "attr.h"
9              
10             #include "repository.h"
11             #include "sysdir.h"
12             #include "config.h"
13             #include "attr_file.h"
14             #include "ignore.h"
15             #include "git2/oid.h"
16             #include
17              
18             const char *git_attr__true = "[internal]__TRUE__";
19             const char *git_attr__false = "[internal]__FALSE__";
20             const char *git_attr__unset = "[internal]__UNSET__";
21              
22 2456           git_attr_value_t git_attr_value(const char *attr)
23             {
24 2456 100         if (attr == NULL || attr == git_attr__unset)
    50          
25 2196           return GIT_ATTR_VALUE_UNSPECIFIED;
26              
27 260 50         if (attr == git_attr__true)
28 260           return GIT_ATTR_VALUE_TRUE;
29              
30 0 0         if (attr == git_attr__false)
31 0           return GIT_ATTR_VALUE_FALSE;
32              
33 0           return GIT_ATTR_VALUE_STRING;
34             }
35              
36             static int collect_attr_files(
37             git_repository *repo,
38             git_attr_session *attr_session,
39             git_attr_options *opts,
40             const char *path,
41             git_vector *files);
42              
43             static void release_attr_files(git_vector *files);
44              
45 4           int git_attr_get_ext(
46             const char **value,
47             git_repository *repo,
48             git_attr_options *opts,
49             const char *pathname,
50             const char *name)
51             {
52             int error;
53             git_attr_path path;
54 4           git_vector files = GIT_VECTOR_INIT;
55             size_t i, j;
56             git_attr_file *file;
57             git_attr_name attr;
58             git_attr_rule *rule;
59 4           git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
60              
61 4 50         GIT_ASSERT_ARG(value);
62 4 50         GIT_ASSERT_ARG(repo);
63 4 50         GIT_ASSERT_ARG(name);
64 4 50         GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
65              
66 4           *value = NULL;
67              
68 4 50         if (git_repository_is_bare(repo))
69 0           dir_flag = GIT_DIR_FLAG_FALSE;
70              
71 4 50         if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
72 0           return -1;
73              
74 4 50         if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
75 0           goto cleanup;
76              
77 4           memset(&attr, 0, sizeof(attr));
78 4           attr.name = name;
79 4           attr.name_hash = git_attr_file__name_hash(name);
80              
81 12 100         git_vector_foreach(&files, i, file) {
82              
83 8 0         git_attr_file__foreach_matching_rule(file, &path, j, rule) {
    50          
84             size_t pos;
85              
86 0 0         if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) {
87 0           *value = ((git_attr_assignment *)git_vector_get(
88 0           &rule->assigns, pos))->value;
89 0           goto cleanup;
90             }
91             }
92             }
93              
94             cleanup:
95 4           release_attr_files(&files);
96 4           git_attr_path__free(&path);
97              
98 4           return error;
99             }
100              
101 4           int git_attr_get(
102             const char **value,
103             git_repository *repo,
104             uint32_t flags,
105             const char *pathname,
106             const char *name)
107             {
108 4           git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
109              
110 4           opts.flags = flags;
111              
112 4           return git_attr_get_ext(value, repo, &opts, pathname, name);
113             }
114              
115              
116             typedef struct {
117             git_attr_name name;
118             git_attr_assignment *found;
119             } attr_get_many_info;
120              
121 627           int git_attr_get_many_with_session(
122             const char **values,
123             git_repository *repo,
124             git_attr_session *attr_session,
125             git_attr_options *opts,
126             const char *pathname,
127             size_t num_attr,
128             const char **names)
129             {
130             int error;
131             git_attr_path path;
132 627           git_vector files = GIT_VECTOR_INIT;
133             size_t i, j, k;
134             git_attr_file *file;
135             git_attr_rule *rule;
136 627           attr_get_many_info *info = NULL;
137 627           size_t num_found = 0;
138 627           git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
139              
140 627 50         if (!num_attr)
141 0           return 0;
142              
143 627 50         GIT_ASSERT_ARG(values);
144 627 50         GIT_ASSERT_ARG(repo);
145 627 50         GIT_ASSERT_ARG(pathname);
146 627 50         GIT_ASSERT_ARG(names);
147 627 50         GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
148              
149 627 50         if (git_repository_is_bare(repo))
150 0           dir_flag = GIT_DIR_FLAG_FALSE;
151              
152 627 50         if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
153 0           return -1;
154              
155 627 50         if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
156 0           goto cleanup;
157              
158 627           info = git__calloc(num_attr, sizeof(attr_get_many_info));
159 627 50         GIT_ERROR_CHECK_ALLOC(info);
160              
161 2005 100         git_vector_foreach(&files, i, file) {
162              
163 1378 0         git_attr_file__foreach_matching_rule(file, &path, j, rule) {
    50          
164              
165 0 0         for (k = 0; k < num_attr; k++) {
166             size_t pos;
167              
168 0 0         if (info[k].found != NULL) /* already found assignment */
169 0           continue;
170              
171 0 0         if (!info[k].name.name) {
172 0           info[k].name.name = names[k];
173 0           info[k].name.name_hash = git_attr_file__name_hash(names[k]);
174             }
175              
176 0 0         if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) {
177 0           info[k].found = (git_attr_assignment *)
178 0           git_vector_get(&rule->assigns, pos);
179 0           values[k] = info[k].found->value;
180              
181 0 0         if (++num_found == num_attr)
182 0           goto cleanup;
183             }
184             }
185             }
186             }
187              
188 1774 100         for (k = 0; k < num_attr; k++) {
189 1147 50         if (!info[k].found)
190 1147           values[k] = NULL;
191             }
192              
193             cleanup:
194 627           release_attr_files(&files);
195 627           git_attr_path__free(&path);
196 627           git__free(info);
197              
198 627           return error;
199             }
200              
201 0           int git_attr_get_many(
202             const char **values,
203             git_repository *repo,
204             uint32_t flags,
205             const char *pathname,
206             size_t num_attr,
207             const char **names)
208             {
209 0           git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
210              
211 0           opts.flags = flags;
212              
213 0           return git_attr_get_many_with_session(
214             values, repo, NULL, &opts, pathname, num_attr, names);
215             }
216              
217 0           int git_attr_get_many_ext(
218             const char **values,
219             git_repository *repo,
220             git_attr_options *opts,
221             const char *pathname,
222             size_t num_attr,
223             const char **names)
224             {
225 0           return git_attr_get_many_with_session(
226             values, repo, NULL, opts, pathname, num_attr, names);
227             }
228              
229 0           int git_attr_foreach(
230             git_repository *repo,
231             uint32_t flags,
232             const char *pathname,
233             int (*callback)(const char *name, const char *value, void *payload),
234             void *payload)
235             {
236 0           git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
237              
238 0           opts.flags = flags;
239              
240 0           return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
241             }
242              
243 0           int git_attr_foreach_ext(
244             git_repository *repo,
245             git_attr_options *opts,
246             const char *pathname,
247             int (*callback)(const char *name, const char *value, void *payload),
248             void *payload)
249             {
250             int error;
251             git_attr_path path;
252 0           git_vector files = GIT_VECTOR_INIT;
253             size_t i, j, k;
254             git_attr_file *file;
255             git_attr_rule *rule;
256             git_attr_assignment *assign;
257 0           git_strmap *seen = NULL;
258 0           git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
259              
260 0 0         GIT_ASSERT_ARG(repo);
261 0 0         GIT_ASSERT_ARG(callback);
262 0 0         GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
263              
264 0 0         if (git_repository_is_bare(repo))
265 0           dir_flag = GIT_DIR_FLAG_FALSE;
266              
267 0 0         if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
268 0           return -1;
269              
270 0 0         if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
    0          
271             (error = git_strmap_new(&seen)) < 0)
272             goto cleanup;
273              
274 0 0         git_vector_foreach(&files, i, file) {
275              
276 0 0         git_attr_file__foreach_matching_rule(file, &path, j, rule) {
    0          
277              
278 0 0         git_vector_foreach(&rule->assigns, k, assign) {
279             /* skip if higher priority assignment was already seen */
280 0 0         if (git_strmap_exists(seen, assign->name))
281 0           continue;
282              
283 0 0         if ((error = git_strmap_set(seen, assign->name, assign)) < 0)
284 0           goto cleanup;
285              
286 0           error = callback(assign->name, assign->value, payload);
287 0 0         if (error) {
288 0           git_error_set_after_callback(error);
289 0           goto cleanup;
290             }
291             }
292             }
293             }
294              
295             cleanup:
296 0           git_strmap_free(seen);
297 0           release_attr_files(&files);
298 0           git_attr_path__free(&path);
299              
300 0           return error;
301             }
302              
303 1551           static int preload_attr_source(
304             git_repository *repo,
305             git_attr_session *attr_session,
306             git_attr_file_source *source)
307             {
308             int error;
309 1551           git_attr_file *preload = NULL;
310              
311 1551 50         if (!source)
312 0           return 0;
313              
314 1551           error = git_attr_cache__get(&preload, repo, attr_session, source,
315             git_attr_file__parse_buffer, true);
316              
317 1551 50         if (!error)
318 1551           git_attr_file__free(preload);
319              
320 1551           return error;
321             }
322              
323 1551           GIT_INLINE(int) preload_attr_file(
324             git_repository *repo,
325             git_attr_session *attr_session,
326             const char *base,
327             const char *filename)
328             {
329 1551           git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
330              
331 1551 100         if (!filename)
332 517           return 0;
333              
334 1034           source.base = base;
335 1034           source.filename = filename;
336              
337 1551           return preload_attr_source(repo, attr_session, &source);
338             }
339              
340 1148           static int system_attr_file(
341             git_str *out,
342             git_attr_session *attr_session)
343             {
344             int error;
345              
346 1148 100         if (!attr_session) {
347 926           error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM);
348              
349 926 50         if (error == GIT_ENOTFOUND)
350 926           git_error_clear();
351              
352 926           return error;
353             }
354              
355 222 100         if (!attr_session->init_sysdir) {
356 54           error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM);
357              
358 54 50         if (error == GIT_ENOTFOUND)
359 54           git_error_clear();
360 0 0         else if (error)
361 0           return error;
362              
363 54           attr_session->init_sysdir = 1;
364             }
365              
366 222 50         if (attr_session->sysdir.size == 0)
367 222           return GIT_ENOTFOUND;
368              
369             /* We can safely provide a git_str with no allocation (asize == 0) to
370             * a consumer. This allows them to treat this as a regular `git_str`,
371             * but their call to `git_str_dispose` will not attempt to free it.
372             */
373 0           git_str_attach_notowned(
374 0           out, attr_session->sysdir.ptr, attr_session->sysdir.size);
375 0           return 0;
376             }
377              
378 631           static int attr_setup(
379             git_repository *repo,
380             git_attr_session *attr_session,
381             git_attr_options *opts)
382             {
383 631           git_str system = GIT_STR_INIT, info = GIT_STR_INIT;
384 631           git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL };
385 631           git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_HEAD, NULL, GIT_ATTR_FILE, NULL };
386 631           git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
387 631           git_index *idx = NULL;
388             const char *workdir;
389 631           int error = 0;
390              
391 631 100         if (attr_session && attr_session->init_setup)
    100          
392 114           return 0;
393              
394 517 50         if ((error = git_attr_cache__init(repo)) < 0)
395 0           return error;
396              
397             /*
398             * Preload attribute files that could contain macros so the
399             * definitions will be available for later file parsing.
400             */
401              
402 517 50         if ((error = system_attr_file(&system, attr_session)) < 0 ||
    0          
403 0           (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) {
404 517 50         if (error != GIT_ENOTFOUND)
405 0           goto out;
406              
407 517           error = 0;
408             }
409              
410 517 50         if ((error = preload_attr_file(repo, attr_session, NULL,
411 517           git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
412 0           goto out;
413              
414 517 50         if ((error = git_repository__item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
    50          
415 517           (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
416 0 0         if (error != GIT_ENOTFOUND)
417 0           goto out;
418              
419 0           error = 0;
420             }
421              
422 517 50         if ((workdir = git_repository_workdir(repo)) != NULL &&
    50          
423             (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0)
424 0           goto out;
425              
426 517 50         if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
    50          
427             (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
428             goto out;
429              
430 517 100         if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
    50          
    0          
431             (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
432 0           goto out;
433              
434 517 100         if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
    50          
435             #ifndef GIT_DEPRECATE_HARD
436 0 0         if (opts->commit_id)
437 0           commit_source.commit_id = opts->commit_id;
438             else
439             #endif
440 0           commit_source.commit_id = &opts->attr_commit_id;
441              
442 0 0         if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
443 0           goto out;
444             }
445              
446 517 100         if (attr_session)
447 54           attr_session->init_setup = 1;
448              
449             out:
450 517           git_str_dispose(&system);
451 517           git_str_dispose(&info);
452              
453 631           return error;
454             }
455              
456 16           int git_attr_add_macro(
457             git_repository *repo,
458             const char *name,
459             const char *values)
460             {
461             int error;
462 16           git_attr_rule *macro = NULL;
463             git_pool *pool;
464              
465 16 50         GIT_ASSERT_ARG(repo);
466 16 50         GIT_ASSERT_ARG(name);
467              
468 16 50         if ((error = git_attr_cache__init(repo)) < 0)
469 0           return error;
470              
471 16           macro = git__calloc(1, sizeof(git_attr_rule));
472 16 50         GIT_ERROR_CHECK_ALLOC(macro);
473              
474 16           pool = &git_repository_attr_cache(repo)->pool;
475              
476 16           macro->match.pattern = git_pool_strdup(pool, name);
477 16 50         GIT_ERROR_CHECK_ALLOC(macro->match.pattern);
478              
479 16           macro->match.length = strlen(macro->match.pattern);
480 16           macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
481              
482 16           error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values);
483              
484 16 50         if (!error)
485 16           error = git_attr_cache__insert_macro(repo, macro);
486              
487 16 50         if (error < 0)
488 0           git_attr_rule__free(macro);
489              
490 16           return error;
491             }
492              
493             typedef struct {
494             git_repository *repo;
495             git_attr_session *attr_session;
496             git_attr_options *opts;
497             const char *workdir;
498             git_index *index;
499             git_vector *files;
500             } attr_walk_up_info;
501              
502 755           static int attr_decide_sources(
503             uint32_t flags,
504             bool has_wd,
505             bool has_index,
506             git_attr_file_source_t *srcs)
507             {
508 755           int count = 0;
509              
510 755           switch (flags & 0x03) {
511             case GIT_ATTR_CHECK_FILE_THEN_INDEX:
512 755 50         if (has_wd)
513 755           srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
514 755 50         if (has_index)
515 755           srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
516 755           break;
517             case GIT_ATTR_CHECK_INDEX_THEN_FILE:
518 0 0         if (has_index)
519 0           srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
520 0 0         if (has_wd)
521 0           srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
522 0           break;
523             case GIT_ATTR_CHECK_INDEX_ONLY:
524 0 0         if (has_index)
525 0           srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
526 0           break;
527             }
528              
529 755 50         if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
530 0           srcs[count++] = GIT_ATTR_FILE_SOURCE_HEAD;
531              
532 755 50         if ((flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
533 0           srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
534              
535 755           return count;
536             }
537              
538 2141           static int push_attr_source(
539             git_repository *repo,
540             git_attr_session *attr_session,
541             git_vector *list,
542             git_attr_file_source *source,
543             bool allow_macros)
544             {
545 2141           int error = 0;
546 2141           git_attr_file *file = NULL;
547              
548 2141           error = git_attr_cache__get(&file, repo, attr_session,
549             source,
550             git_attr_file__parse_buffer,
551             allow_macros);
552              
553 2141 50         if (error < 0)
554 0           return error;
555              
556 2141 100         if (file != NULL) {
557 1386 50         if ((error = git_vector_insert(list, file)) < 0)
558 0           git_attr_file__free(file);
559             }
560              
561 2141           return error;
562             }
563              
564 631           GIT_INLINE(int) push_attr_file(
565             git_repository *repo,
566             git_attr_session *attr_session,
567             git_vector *list,
568             const char *base,
569             const char *filename)
570             {
571 631           git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
572 631           return push_attr_source(repo, attr_session, list, &source, true);
573             }
574              
575 755           static int push_one_attr(void *ref, const char *path)
576             {
577 755           attr_walk_up_info *info = (attr_walk_up_info *)ref;
578             git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES];
579 755           int error = 0, n_src, i;
580             bool allow_macros;
581              
582 755 100         n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
583 755           info->workdir != NULL,
584 755           info->index != NULL,
585             src);
586              
587 755 50         allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
    100          
588              
589 2265 50         for (i = 0; !error && i < n_src; ++i) {
    100          
590 1510           git_attr_file_source source = { src[i], path, GIT_ATTR_FILE };
591              
592 1510 50         if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) {
    0          
593             #ifndef GIT_DEPRECATE_HARD
594 0 0         if (info->opts->commit_id)
595 0           source.commit_id = info->opts->commit_id;
596             else
597             #endif
598 0           source.commit_id = &info->opts->attr_commit_id;
599             }
600              
601 1510           error = push_attr_source(info->repo, info->attr_session, info->files,
602             &source, allow_macros);
603             }
604              
605 755           return error;
606             }
607              
608 631           static void release_attr_files(git_vector *files)
609             {
610             size_t i;
611             git_attr_file *file;
612              
613 2017 100         git_vector_foreach(files, i, file) {
614 1386           git_attr_file__free(file);
615 1386           files->contents[i] = NULL;
616             }
617 631           git_vector_free(files);
618 631           }
619              
620 631           static int collect_attr_files(
621             git_repository *repo,
622             git_attr_session *attr_session,
623             git_attr_options *opts,
624             const char *path,
625             git_vector *files)
626             {
627 631           int error = 0;
628 631           git_str dir = GIT_STR_INIT, attrfile = GIT_STR_INIT;
629 631           const char *workdir = git_repository_workdir(repo);
630 631           attr_walk_up_info info = { NULL };
631              
632 631 50         GIT_ASSERT(!git_fs_path_is_absolute(path));
633              
634 631 50         if ((error = attr_setup(repo, attr_session, opts)) < 0)
635 0           return error;
636              
637             /* Resolve path in a non-bare repo */
638 631 50         if (workdir != NULL) {
639 631 50         if (!(error = git_repository_workdir_path(&dir, repo, path)))
640 631           error = git_fs_path_find_dir(&dir);
641             }
642             else {
643 0           error = git_fs_path_dirname_r(&dir, path);
644             }
645              
646 631 50         if (error < 0)
647 0           goto cleanup;
648              
649             /* in precedence order highest to lowest:
650             * - $GIT_DIR/info/attributes
651             * - path components with .gitattributes
652             * - config core.attributesfile
653             * - $GIT_PREFIX/etc/gitattributes
654             */
655              
656 631 50         if ((error = git_repository__item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
    50          
657 631           (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
658 0 0         if (error != GIT_ENOTFOUND)
659 0           goto cleanup;
660             }
661              
662 631           info.repo = repo;
663 631           info.attr_session = attr_session;
664 631           info.opts = opts;
665 631           info.workdir = workdir;
666 631 50         if (git_repository_index__weakptr(&info.index, repo) < 0)
667 0           git_error_clear(); /* no error even if there is no index */
668 631           info.files = files;
669              
670 631 50         if (!strcmp(dir.ptr, "."))
671 0           error = push_one_attr(&info, "");
672             else
673 631           error = git_fs_path_walk_up(&dir, workdir, push_one_attr, &info);
674              
675 631 50         if (error < 0)
676 0           goto cleanup;
677              
678 631 50         if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
679 0           error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
680 0 0         if (error < 0)
681 0           goto cleanup;
682             }
683              
684 631 100         if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
    50          
685 631           error = system_attr_file(&dir, attr_session);
686              
687 631 50         if (!error)
688 0           error = push_attr_file(repo, attr_session, files, NULL, dir.ptr);
689 631 50         else if (error == GIT_ENOTFOUND)
690 631           error = 0;
691             }
692              
693             cleanup:
694 631 50         if (error < 0)
695 0           release_attr_files(files);
696 631           git_str_dispose(&attrfile);
697 631           git_str_dispose(&dir);
698              
699 631           return error;
700             }