1 #include "repository.h"
9 const char *git_attr__true
= "[internal]__TRUE__";
10 const char *git_attr__false
= "[internal]__FALSE__";
11 const char *git_attr__unset
= "[internal]__UNSET__";
13 git_attr_t
git_attr_value(const char *attr
)
15 if (attr
== NULL
|| attr
== git_attr__unset
)
16 return GIT_ATTR_UNSPECIFIED_T
;
18 if (attr
== git_attr__true
)
19 return GIT_ATTR_TRUE_T
;
21 if (attr
== git_attr__false
)
22 return GIT_ATTR_FALSE_T
;
24 return GIT_ATTR_VALUE_T
;
28 static int collect_attr_files(
44 git_vector files
= GIT_VECTOR_INIT
;
52 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
55 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
59 attr
.name_hash
= git_attr_file__name_hash(name
);
61 git_vector_foreach(&files
, i
, file
) {
63 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
64 int pos
= git_vector_bsearch(&rule
->assigns
, &attr
);
66 *value
= ((git_attr_assignment
*)git_vector_get(
67 &rule
->assigns
, pos
))->value
;
74 git_vector_free(&files
);
75 git_attr_path__free(&path
);
83 git_attr_assignment
*found
;
86 int git_attr_get_many(
96 git_vector files
= GIT_VECTOR_INIT
;
100 attr_get_many_info
*info
= NULL
;
101 size_t num_found
= 0;
103 memset((void *)values
, 0, sizeof(const char *) * num_attr
);
105 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
108 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
111 info
= git__calloc(num_attr
, sizeof(attr_get_many_info
));
112 GITERR_CHECK_ALLOC(info
);
114 git_vector_foreach(&files
, i
, file
) {
116 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
118 for (k
= 0; k
< num_attr
; k
++) {
121 if (info
[k
].found
!= NULL
) /* already found assignment */
124 if (!info
[k
].name
.name
) {
125 info
[k
].name
.name
= names
[k
];
126 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
129 pos
= git_vector_bsearch(&rule
->assigns
, &info
[k
].name
);
131 info
[k
].found
= (git_attr_assignment
*)
132 git_vector_get(&rule
->assigns
, pos
);
133 values
[k
] = info
[k
].found
->value
;
135 if (++num_found
== num_attr
)
143 git_vector_free(&files
);
144 git_attr_path__free(&path
);
151 int git_attr_foreach(
152 git_repository
*repo
,
154 const char *pathname
,
155 int (*callback
)(const char *name
, const char *value
, void *payload
),
160 git_vector files
= GIT_VECTOR_INIT
;
164 git_attr_assignment
*assign
;
165 git_strmap
*seen
= NULL
;
167 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
170 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
173 seen
= git_strmap_alloc();
174 GITERR_CHECK_ALLOC(seen
);
176 git_vector_foreach(&files
, i
, file
) {
178 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
180 git_vector_foreach(&rule
->assigns
, k
, assign
) {
181 /* skip if higher priority assignment was already seen */
182 if (git_strmap_exists(seen
, assign
->name
))
185 git_strmap_insert(seen
, assign
->name
, assign
, error
);
189 error
= callback(assign
->name
, assign
->value
, payload
);
199 git_strmap_free(seen
);
200 git_vector_free(&files
);
201 git_attr_path__free(&path
);
207 int git_attr_add_macro(
208 git_repository
*repo
,
213 git_attr_rule
*macro
= NULL
;
216 if (git_attr_cache__init(repo
) < 0)
219 macro
= git__calloc(1, sizeof(git_attr_rule
));
220 GITERR_CHECK_ALLOC(macro
);
222 pool
= &git_repository_attr_cache(repo
)->pool
;
224 macro
->match
.pattern
= git_pool_strdup(pool
, name
);
225 GITERR_CHECK_ALLOC(macro
->match
.pattern
);
227 macro
->match
.length
= strlen(macro
->match
.pattern
);
228 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
230 error
= git_attr_assignment__parse(repo
, pool
, ¯o
->assigns
, &values
);
233 error
= git_attr_cache__insert_macro(repo
, macro
);
236 git_attr_rule__free(macro
);
241 bool git_attr_cache__is_cached(
242 git_repository
*repo
, git_attr_file_source source
, const char *path
)
244 git_buf cache_key
= GIT_BUF_INIT
;
245 git_strmap
*files
= git_repository_attr_cache(repo
)->files
;
246 const char *workdir
= git_repository_workdir(repo
);
249 if (workdir
&& git__prefixcmp(path
, workdir
) == 0)
250 path
+= strlen(workdir
);
251 if (git_buf_printf(&cache_key
, "%d#%s", (int)source
, path
) < 0)
254 rval
= git_strmap_exists(files
, git_buf_cstr(&cache_key
));
256 git_buf_free(&cache_key
);
261 static int load_attr_file(
263 git_attr_file_stat_sig
*sig
,
264 const char *filename
)
267 git_buf content
= GIT_BUF_INIT
;
270 if (p_stat(filename
, &st
) < 0)
271 return GIT_ENOTFOUND
;
274 (git_time_t
)st
.st_mtime
== sig
->seconds
&&
275 (git_off_t
)st
.st_size
== sig
->size
&&
276 (unsigned int)st
.st_ino
== sig
->ino
)
277 return GIT_ENOTFOUND
;
279 error
= git_futils_readbuffer_updated(&content
, filename
, NULL
, NULL
);
284 sig
->seconds
= (git_time_t
)st
.st_mtime
;
285 sig
->size
= (git_off_t
)st
.st_size
;
286 sig
->ino
= (unsigned int)st
.st_ino
;
289 *data
= git_buf_detach(&content
);
294 static int load_attr_blob_from_index(
295 const char **content
,
297 git_repository
*repo
,
298 const git_oid
*old_oid
,
303 git_index_entry
*entry
;
305 if ((error
= git_repository_index__weakptr(&index
, repo
)) < 0 ||
306 (error
= git_index_find(index
, relfile
)) < 0)
309 entry
= git_index_get(index
, error
);
311 if (old_oid
&& git_oid_cmp(old_oid
, &entry
->oid
) == 0)
312 return GIT_ENOTFOUND
;
314 if ((error
= git_blob_lookup(blob
, repo
, &entry
->oid
)) < 0)
317 *content
= git_blob_rawcontent(*blob
);
321 static int load_attr_from_cache(
322 git_attr_file
**file
,
323 git_attr_cache
*cache
,
324 git_attr_file_source source
,
325 const char *relative_path
)
327 git_buf cache_key
= GIT_BUF_INIT
;
332 if (!cache
|| !cache
->files
)
335 if (git_buf_printf(&cache_key
, "%d#%s", (int)source
, relative_path
) < 0)
338 cache_pos
= git_strmap_lookup_index(cache
->files
, cache_key
.ptr
);
340 git_buf_free(&cache_key
);
342 if (git_strmap_valid_index(cache
->files
, cache_pos
))
343 *file
= git_strmap_value_at(cache
->files
, cache_pos
);
348 int git_attr_cache__internal_file(
349 git_repository
*repo
,
350 const char *filename
,
351 git_attr_file
**file
)
354 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
355 khiter_t cache_pos
= git_strmap_lookup_index(cache
->files
, filename
);
357 if (git_strmap_valid_index(cache
->files
, cache_pos
)) {
358 *file
= git_strmap_value_at(cache
->files
, cache_pos
);
362 if (git_attr_file__new(file
, 0, filename
, &cache
->pool
) < 0)
365 git_strmap_insert(cache
->files
, (*file
)->key
+ 2, *file
, error
);
372 int git_attr_cache__push_file(
373 git_repository
*repo
,
375 const char *filename
,
376 git_attr_file_source source
,
377 git_attr_file_parser parse
,
381 git_buf path
= GIT_BUF_INIT
;
382 const char *workdir
= git_repository_workdir(repo
);
383 const char *relfile
, *content
= NULL
;
384 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
385 git_attr_file
*file
= NULL
;
386 git_blob
*blob
= NULL
;
387 git_attr_file_stat_sig st
;
389 assert(filename
&& stack
);
391 /* join base and path as needed */
392 if (base
!= NULL
&& git_path_root(filename
) < 0) {
393 if (git_buf_joinpath(&path
, base
, filename
) < 0)
399 if (workdir
&& git__prefixcmp(relfile
, workdir
) == 0)
400 relfile
+= strlen(workdir
);
403 if (load_attr_from_cache(&file
, cache
, source
, relfile
) < 0)
406 /* if not in cache, load data, parse, and cache */
408 if (source
== GIT_ATTR_FILE_FROM_FILE
) {
410 memcpy(&st
, &file
->cache_data
.st
, sizeof(st
));
412 memset(&st
, 0, sizeof(st
));
414 error
= load_attr_file(&content
, &st
, filename
);
416 error
= load_attr_blob_from_index(&content
, &blob
,
417 repo
, file
? &file
->cache_data
.oid
: NULL
, relfile
);
421 /* not finding a file is not an error for this function */
422 if (error
== GIT_ENOTFOUND
) {
429 /* if we got here, we have to parse and/or reparse the file */
431 git_attr_file__clear_rules(file
);
433 error
= git_attr_file__new(&file
, source
, relfile
, &cache
->pool
);
438 if (parse
&& (error
= parse(repo
, content
, file
)) < 0)
441 git_strmap_insert(cache
->files
, file
->key
, file
, error
); //-V595
445 /* remember "cache buster" file signature */
447 git_oid_cpy(&file
->cache_data
.oid
, git_object_id((git_object
*)blob
));
449 memcpy(&file
->cache_data
.st
, &st
, sizeof(st
));
452 /* push file onto vector if we found one*/
453 if (!error
&& file
!= NULL
)
454 error
= git_vector_insert(stack
, file
);
457 git_attr_file__free(file
);
462 git__free((void *)content
);
469 #define push_attr_file(R,S,B,F) \
470 git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))
473 git_repository
*repo
;
480 int git_attr_cache__decide_sources(
481 uint32_t flags
, bool has_wd
, bool has_index
, git_attr_file_source
*srcs
)
485 switch (flags
& 0x03) {
486 case GIT_ATTR_CHECK_FILE_THEN_INDEX
:
488 srcs
[count
++] = GIT_ATTR_FILE_FROM_FILE
;
490 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
492 case GIT_ATTR_CHECK_INDEX_THEN_FILE
:
494 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
496 srcs
[count
++] = GIT_ATTR_FILE_FROM_FILE
;
498 case GIT_ATTR_CHECK_INDEX_ONLY
:
500 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
507 static int push_one_attr(void *ref
, git_buf
*path
)
509 int error
= 0, n_src
, i
;
510 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
511 git_attr_file_source src
[2];
513 n_src
= git_attr_cache__decide_sources(
514 info
->flags
, info
->workdir
!= NULL
, info
->index
!= NULL
, src
);
516 for (i
= 0; !error
&& i
< n_src
; ++i
)
517 error
= git_attr_cache__push_file(
518 info
->repo
, path
->ptr
, GIT_ATTR_FILE
, src
[i
],
519 git_attr_file__parse_buffer
, info
->files
);
524 static int collect_attr_files(
525 git_repository
*repo
,
531 git_buf dir
= GIT_BUF_INIT
;
532 const char *workdir
= git_repository_workdir(repo
);
533 attr_walk_up_info info
;
535 if (git_attr_cache__init(repo
) < 0 ||
536 git_vector_init(files
, 4, NULL
) < 0)
539 /* Resolve path in a non-bare repo */
541 error
= git_path_find_dir(&dir
, path
, workdir
);
543 error
= git_path_dirname_r(&dir
, path
);
547 /* in precendence order highest to lowest:
548 * - $GIT_DIR/info/attributes
549 * - path components with .gitattributes
550 * - config core.attributesfile
551 * - $GIT_PREFIX/etc/gitattributes
554 error
= push_attr_file(
555 repo
, files
, git_repository_path(repo
), GIT_ATTR_FILE_INREPO
);
561 info
.workdir
= workdir
;
562 if (git_repository_index__weakptr(&info
.index
, repo
) < 0)
563 giterr_clear(); /* no error even if there is no index */
566 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
570 if (git_repository_attr_cache(repo
)->cfg_attr_file
!= NULL
) {
571 error
= push_attr_file(
572 repo
, files
, NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
);
577 if ((flags
& GIT_ATTR_CHECK_NO_SYSTEM
) == 0) {
578 error
= git_futils_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
580 error
= push_attr_file(repo
, files
, NULL
, dir
.ptr
);
581 else if (error
== GIT_ENOTFOUND
)
587 git_vector_free(files
);
593 static char *try_global_default(const char *relpath
)
595 git_buf dflt
= GIT_BUF_INIT
;
598 if (!git_futils_find_global_file(&dflt
, relpath
))
599 rval
= git_buf_detach(&dflt
);
606 int git_attr_cache__init(git_repository
*repo
)
609 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
612 if (cache
->initialized
)
615 /* cache config settings for attributes and ignores */
616 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
619 ret
= git_config_get_string(&cache
->cfg_attr_file
, cfg
, GIT_ATTR_CONFIG
);
620 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
622 if (ret
== GIT_ENOTFOUND
)
623 cache
->cfg_attr_file
= try_global_default(GIT_ATTR_CONFIG_DEFAULT
);
625 ret
= git_config_get_string(&cache
->cfg_excl_file
, cfg
, GIT_IGNORE_CONFIG
);
626 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
628 if (ret
== GIT_ENOTFOUND
)
629 cache
->cfg_excl_file
= try_global_default(GIT_IGNORE_CONFIG_DEFAULT
);
633 /* allocate hashtable for attribute and ignore file contents */
634 if (cache
->files
== NULL
) {
635 cache
->files
= git_strmap_alloc();
636 GITERR_CHECK_ALLOC(cache
->files
);
639 /* allocate hashtable for attribute macros */
640 if (cache
->macros
== NULL
) {
641 cache
->macros
= git_strmap_alloc();
642 GITERR_CHECK_ALLOC(cache
->macros
);
645 /* allocate string pool */
646 if (git_pool_init(&cache
->pool
, 1, 0) < 0)
649 cache
->initialized
= 1;
651 /* insert default macros */
652 return git_attr_add_macro(repo
, "binary", "-diff -crlf -text");
655 void git_attr_cache_flush(
656 git_repository
*repo
)
658 git_attr_cache
*cache
;
663 cache
= git_repository_attr_cache(repo
);
665 if (cache
->files
!= NULL
) {
668 git_strmap_foreach_value(cache
->files
, file
, {
669 git_attr_file__free(file
);
672 git_strmap_free(cache
->files
);
675 if (cache
->macros
!= NULL
) {
678 git_strmap_foreach_value(cache
->macros
, rule
, {
679 git_attr_rule__free(rule
);
682 git_strmap_free(cache
->macros
);
685 git_pool_clear(&cache
->pool
);
687 cache
->initialized
= 0;
690 int git_attr_cache__insert_macro(git_repository
*repo
, git_attr_rule
*macro
)
692 git_strmap
*macros
= git_repository_attr_cache(repo
)->macros
;
695 /* TODO: generate warning log if (macro->assigns.length == 0) */
696 if (macro
->assigns
.length
== 0)
699 git_strmap_insert(macros
, macro
->match
.pattern
, macro
, error
);
700 return (error
< 0) ? -1 : 0;
703 git_attr_rule
*git_attr_cache__lookup_macro(
704 git_repository
*repo
, const char *name
)
706 git_strmap
*macros
= git_repository_attr_cache(repo
)->macros
;
709 pos
= git_strmap_lookup_index(macros
, name
);
711 if (!git_strmap_valid_index(macros
, pos
))
714 return (git_attr_rule
*)git_strmap_value_at(macros
, pos
);