2 #include "repository.h"
12 const char *git_attr__true
= "[internal]__TRUE__";
13 const char *git_attr__false
= "[internal]__FALSE__";
14 const char *git_attr__unset
= "[internal]__UNSET__";
16 git_attr_t
git_attr_value(const char *attr
)
18 if (attr
== NULL
|| attr
== git_attr__unset
)
19 return GIT_ATTR_UNSPECIFIED_T
;
21 if (attr
== git_attr__true
)
22 return GIT_ATTR_TRUE_T
;
24 if (attr
== git_attr__false
)
25 return GIT_ATTR_FALSE_T
;
27 return GIT_ATTR_VALUE_T
;
30 static int collect_attr_files(
36 static void release_attr_files(git_vector
*files
);
47 git_vector files
= GIT_VECTOR_INIT
;
53 assert(value
&& repo
&& name
);
57 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
60 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
63 memset(&attr
, 0, sizeof(attr
));
65 attr
.name_hash
= git_attr_file__name_hash(name
);
67 git_vector_foreach(&files
, i
, file
) {
69 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
72 if (!git_vector_bsearch(&pos
, &rule
->assigns
, &attr
)) {
73 *value
= ((git_attr_assignment
*)git_vector_get(
74 &rule
->assigns
, pos
))->value
;
81 release_attr_files(&files
);
82 git_attr_path__free(&path
);
90 git_attr_assignment
*found
;
93 int git_attr_get_many(
103 git_vector files
= GIT_VECTOR_INIT
;
107 attr_get_many_info
*info
= NULL
;
108 size_t num_found
= 0;
113 assert(values
&& repo
&& names
);
115 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
118 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
121 info
= git__calloc(num_attr
, sizeof(attr_get_many_info
));
122 GITERR_CHECK_ALLOC(info
);
124 git_vector_foreach(&files
, i
, file
) {
126 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
128 for (k
= 0; k
< num_attr
; k
++) {
131 if (info
[k
].found
!= NULL
) /* already found assignment */
134 if (!info
[k
].name
.name
) {
135 info
[k
].name
.name
= names
[k
];
136 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
139 if (!git_vector_bsearch(&pos
, &rule
->assigns
, &info
[k
].name
)) {
140 info
[k
].found
= (git_attr_assignment
*)
141 git_vector_get(&rule
->assigns
, pos
);
142 values
[k
] = info
[k
].found
->value
;
144 if (++num_found
== num_attr
)
151 for (k
= 0; k
< num_attr
; k
++) {
157 release_attr_files(&files
);
158 git_attr_path__free(&path
);
165 int git_attr_foreach(
166 git_repository
*repo
,
168 const char *pathname
,
169 int (*callback
)(const char *name
, const char *value
, void *payload
),
174 git_vector files
= GIT_VECTOR_INIT
;
178 git_attr_assignment
*assign
;
179 git_strmap
*seen
= NULL
;
181 assert(repo
&& callback
);
183 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
186 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0 ||
187 (error
= git_strmap_alloc(&seen
)) < 0)
190 git_vector_foreach(&files
, i
, file
) {
192 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
194 git_vector_foreach(&rule
->assigns
, k
, assign
) {
195 /* skip if higher priority assignment was already seen */
196 if (git_strmap_exists(seen
, assign
->name
))
199 git_strmap_insert(seen
, assign
->name
, assign
, error
);
203 error
= callback(assign
->name
, assign
->value
, payload
);
205 giterr_set_after_callback(error
);
213 git_strmap_free(seen
);
214 release_attr_files(&files
);
215 git_attr_path__free(&path
);
220 static int preload_attr_file(
221 git_repository
*repo
,
222 git_attr_file_source source
,
227 git_attr_file
*preload
= NULL
;
231 if (!(error
= git_attr_cache__get(
232 &preload
, repo
, source
, base
, file
, git_attr_file__parse_buffer
)))
233 git_attr_file__free(preload
);
238 static int attr_setup(git_repository
*repo
)
241 const char *workdir
= git_repository_workdir(repo
);
242 git_index
*idx
= NULL
;
243 git_buf sys
= GIT_BUF_INIT
;
245 if ((error
= git_attr_cache__init(repo
)) < 0)
248 /* preload attribute files that could contain macros so the
249 * definitions will be available for later file parsing
252 if (!(error
= git_sysdir_find_system_file(&sys
, GIT_ATTR_FILE_SYSTEM
))) {
253 error
= preload_attr_file(
254 repo
, GIT_ATTR_FILE__FROM_FILE
, NULL
, sys
.ptr
);
258 if (error
== GIT_ENOTFOUND
) {
265 if ((error
= preload_attr_file(
266 repo
, GIT_ATTR_FILE__FROM_FILE
,
267 NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
)) < 0)
270 if ((error
= preload_attr_file(
271 repo
, GIT_ATTR_FILE__FROM_FILE
,
272 git_repository_path(repo
), GIT_ATTR_FILE_INREPO
)) < 0)
275 if (workdir
!= NULL
&&
276 (error
= preload_attr_file(
277 repo
, GIT_ATTR_FILE__FROM_FILE
, workdir
, GIT_ATTR_FILE
)) < 0)
280 if ((error
= git_repository_index__weakptr(&idx
, repo
)) < 0 ||
281 (error
= preload_attr_file(
282 repo
, GIT_ATTR_FILE__FROM_INDEX
, NULL
, GIT_ATTR_FILE
)) < 0)
288 int git_attr_add_macro(
289 git_repository
*repo
,
294 git_attr_rule
*macro
= NULL
;
297 if ((error
= git_attr_cache__init(repo
)) < 0)
300 macro
= git__calloc(1, sizeof(git_attr_rule
));
301 GITERR_CHECK_ALLOC(macro
);
303 pool
= &git_repository_attr_cache(repo
)->pool
;
305 macro
->match
.pattern
= git_pool_strdup(pool
, name
);
306 GITERR_CHECK_ALLOC(macro
->match
.pattern
);
308 macro
->match
.length
= strlen(macro
->match
.pattern
);
309 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
311 error
= git_attr_assignment__parse(repo
, pool
, ¯o
->assigns
, &values
);
314 error
= git_attr_cache__insert_macro(repo
, macro
);
317 git_attr_rule__free(macro
);
323 git_repository
*repo
;
330 static int attr_decide_sources(
331 uint32_t flags
, bool has_wd
, bool has_index
, git_attr_file_source
*srcs
)
335 switch (flags
& 0x03) {
336 case GIT_ATTR_CHECK_FILE_THEN_INDEX
:
338 srcs
[count
++] = GIT_ATTR_FILE__FROM_FILE
;
340 srcs
[count
++] = GIT_ATTR_FILE__FROM_INDEX
;
342 case GIT_ATTR_CHECK_INDEX_THEN_FILE
:
344 srcs
[count
++] = GIT_ATTR_FILE__FROM_INDEX
;
346 srcs
[count
++] = GIT_ATTR_FILE__FROM_FILE
;
348 case GIT_ATTR_CHECK_INDEX_ONLY
:
350 srcs
[count
++] = GIT_ATTR_FILE__FROM_INDEX
;
357 static int push_attr_file(
358 git_repository
*repo
,
360 git_attr_file_source source
,
362 const char *filename
)
365 git_attr_file
*file
= NULL
;
367 error
= git_attr_cache__get(
368 &file
, repo
, source
, base
, filename
, git_attr_file__parse_buffer
);
373 if ((error
= git_vector_insert(list
, file
)) < 0)
374 git_attr_file__free(file
);
380 static int push_one_attr(void *ref
, const char *path
)
382 int error
= 0, n_src
, i
;
383 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
384 git_attr_file_source src
[2];
386 n_src
= attr_decide_sources(
387 info
->flags
, info
->workdir
!= NULL
, info
->index
!= NULL
, src
);
389 for (i
= 0; !error
&& i
< n_src
; ++i
)
390 error
= push_attr_file(
391 info
->repo
, info
->files
, src
[i
], path
, GIT_ATTR_FILE
);
396 static void release_attr_files(git_vector
*files
)
401 git_vector_foreach(files
, i
, file
) {
402 git_attr_file__free(file
);
403 files
->contents
[i
] = NULL
;
405 git_vector_free(files
);
408 static int collect_attr_files(
409 git_repository
*repo
,
415 git_buf dir
= GIT_BUF_INIT
;
416 const char *workdir
= git_repository_workdir(repo
);
417 attr_walk_up_info info
= { NULL
};
419 if ((error
= attr_setup(repo
)) < 0)
422 /* Resolve path in a non-bare repo */
424 error
= git_path_find_dir(&dir
, path
, workdir
);
426 error
= git_path_dirname_r(&dir
, path
);
430 /* in precendence order highest to lowest:
431 * - $GIT_DIR/info/attributes
432 * - path components with .gitattributes
433 * - config core.attributesfile
434 * - $GIT_PREFIX/etc/gitattributes
437 error
= push_attr_file(
438 repo
, files
, GIT_ATTR_FILE__FROM_FILE
,
439 git_repository_path(repo
), GIT_ATTR_FILE_INREPO
);
445 info
.workdir
= workdir
;
446 if (git_repository_index__weakptr(&info
.index
, repo
) < 0)
447 giterr_clear(); /* no error even if there is no index */
450 if (!strcmp(dir
.ptr
, "."))
451 error
= push_one_attr(&info
, "");
453 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
458 if (git_repository_attr_cache(repo
)->cfg_attr_file
!= NULL
) {
459 error
= push_attr_file(
460 repo
, files
, GIT_ATTR_FILE__FROM_FILE
,
461 NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
);
466 if ((flags
& GIT_ATTR_CHECK_NO_SYSTEM
) == 0) {
467 error
= git_sysdir_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
469 error
= push_attr_file(
470 repo
, files
, GIT_ATTR_FILE__FROM_FILE
, NULL
, dir
.ptr
);
471 else if (error
== GIT_ENOTFOUND
) {
479 release_attr_files(files
);