1 #include "repository.h"
8 static int collect_attr_files(
24 git_vector files
= GIT_VECTOR_INIT
;
32 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
35 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
39 attr
.name_hash
= git_attr_file__name_hash(name
);
41 git_vector_foreach(&files
, i
, file
) {
43 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
44 int pos
= git_vector_bsearch(&rule
->assigns
, &attr
);
46 *value
= ((git_attr_assignment
*)git_vector_get(
47 &rule
->assigns
, pos
))->value
;
54 git_vector_free(&files
);
55 git_attr_path__free(&path
);
63 git_attr_assignment
*found
;
66 int git_attr_get_many(
76 git_vector files
= GIT_VECTOR_INIT
;
80 attr_get_many_info
*info
= NULL
;
83 memset((void *)values
, 0, sizeof(const char *) * num_attr
);
85 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
88 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
91 info
= git__calloc(num_attr
, sizeof(attr_get_many_info
));
92 GITERR_CHECK_ALLOC(info
);
94 git_vector_foreach(&files
, i
, file
) {
96 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
98 for (k
= 0; k
< num_attr
; k
++) {
101 if (info
[k
].found
!= NULL
) /* already found assignment */
104 if (!info
[k
].name
.name
) {
105 info
[k
].name
.name
= names
[k
];
106 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
109 pos
= git_vector_bsearch(&rule
->assigns
, &info
[k
].name
);
111 info
[k
].found
= (git_attr_assignment
*)
112 git_vector_get(&rule
->assigns
, pos
);
113 values
[k
] = info
[k
].found
->value
;
115 if (++num_found
== num_attr
)
123 git_vector_free(&files
);
124 git_attr_path__free(&path
);
131 int git_attr_foreach(
132 git_repository
*repo
,
134 const char *pathname
,
135 int (*callback
)(const char *name
, const char *value
, void *payload
),
140 git_vector files
= GIT_VECTOR_INIT
;
141 unsigned int i
, j
, k
;
144 git_attr_assignment
*assign
;
145 git_strmap
*seen
= NULL
;
147 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
)) < 0)
150 if ((error
= collect_attr_files(repo
, flags
, pathname
, &files
)) < 0)
153 seen
= git_strmap_alloc();
154 GITERR_CHECK_ALLOC(seen
);
156 git_vector_foreach(&files
, i
, file
) {
158 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
160 git_vector_foreach(&rule
->assigns
, k
, assign
) {
161 /* skip if higher priority assignment was already seen */
162 if (git_strmap_exists(seen
, assign
->name
))
165 git_strmap_insert(seen
, assign
->name
, assign
, error
);
167 error
= callback(assign
->name
, assign
->value
, payload
);
176 git_strmap_free(seen
);
177 git_vector_free(&files
);
178 git_attr_path__free(&path
);
184 int git_attr_add_macro(
185 git_repository
*repo
,
190 git_attr_rule
*macro
= NULL
;
193 if (git_attr_cache__init(repo
) < 0)
196 macro
= git__calloc(1, sizeof(git_attr_rule
));
197 GITERR_CHECK_ALLOC(macro
);
199 pool
= &git_repository_attr_cache(repo
)->pool
;
201 macro
->match
.pattern
= git_pool_strdup(pool
, name
);
202 GITERR_CHECK_ALLOC(macro
->match
.pattern
);
204 macro
->match
.length
= strlen(macro
->match
.pattern
);
205 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
207 error
= git_attr_assignment__parse(repo
, pool
, ¯o
->assigns
, &values
);
210 error
= git_attr_cache__insert_macro(repo
, macro
);
213 git_attr_rule__free(macro
);
218 bool git_attr_cache__is_cached(
219 git_repository
*repo
, git_attr_file_source source
, const char *path
)
221 git_buf cache_key
= GIT_BUF_INIT
;
222 git_strmap
*files
= git_repository_attr_cache(repo
)->files
;
223 const char *workdir
= git_repository_workdir(repo
);
226 if (workdir
&& git__prefixcmp(path
, workdir
) == 0)
227 path
+= strlen(workdir
);
228 if (git_buf_printf(&cache_key
, "%d#%s", (int)source
, path
) < 0)
231 rval
= git_strmap_exists(files
, git_buf_cstr(&cache_key
));
233 git_buf_free(&cache_key
);
238 static int load_attr_file(
240 git_attr_file_stat_sig
*sig
,
241 const char *filename
)
244 git_buf content
= GIT_BUF_INIT
;
247 if (p_stat(filename
, &st
) < 0)
248 return GIT_ENOTFOUND
;
251 (git_time_t
)st
.st_mtime
== sig
->seconds
&&
252 (git_off_t
)st
.st_size
== sig
->size
&&
253 (unsigned int)st
.st_ino
== sig
->ino
)
254 return GIT_ENOTFOUND
;
256 error
= git_futils_readbuffer_updated(&content
, filename
, NULL
, NULL
);
261 sig
->seconds
= (git_time_t
)st
.st_mtime
;
262 sig
->size
= (git_off_t
)st
.st_size
;
263 sig
->ino
= (unsigned int)st
.st_ino
;
266 *data
= git_buf_detach(&content
);
271 static int load_attr_blob_from_index(
272 const char **content
,
274 git_repository
*repo
,
275 const git_oid
*old_oid
,
280 git_index_entry
*entry
;
282 if ((error
= git_repository_index__weakptr(&index
, repo
)) < 0 ||
283 (error
= git_index_find(index
, relfile
)) < 0)
286 entry
= git_index_get(index
, error
);
288 if (old_oid
&& git_oid_cmp(old_oid
, &entry
->oid
) == 0)
289 return GIT_ENOTFOUND
;
291 if ((error
= git_blob_lookup(blob
, repo
, &entry
->oid
)) < 0)
294 *content
= git_blob_rawcontent(*blob
);
298 static int load_attr_from_cache(
299 git_attr_file
**file
,
300 git_attr_cache
*cache
,
301 git_attr_file_source source
,
302 const char *relative_path
)
304 git_buf cache_key
= GIT_BUF_INIT
;
309 if (!cache
|| !cache
->files
)
312 if (git_buf_printf(&cache_key
, "%d#%s", (int)source
, relative_path
) < 0)
315 cache_pos
= git_strmap_lookup_index(cache
->files
, cache_key
.ptr
);
317 git_buf_free(&cache_key
);
319 if (git_strmap_valid_index(cache
->files
, cache_pos
))
320 *file
= git_strmap_value_at(cache
->files
, cache_pos
);
325 int git_attr_cache__internal_file(
326 git_repository
*repo
,
327 const char *filename
,
328 git_attr_file
**file
)
331 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
332 khiter_t cache_pos
= git_strmap_lookup_index(cache
->files
, filename
);
334 if (git_strmap_valid_index(cache
->files
, cache_pos
)) {
335 *file
= git_strmap_value_at(cache
->files
, cache_pos
);
339 if (git_attr_file__new(file
, 0, filename
, &cache
->pool
) < 0)
342 git_strmap_insert(cache
->files
, (*file
)->key
+ 2, *file
, error
);
349 int git_attr_cache__push_file(
350 git_repository
*repo
,
352 const char *filename
,
353 git_attr_file_source source
,
354 git_attr_file_parser parse
,
358 git_buf path
= GIT_BUF_INIT
;
359 const char *workdir
= git_repository_workdir(repo
);
360 const char *relfile
, *content
= NULL
;
361 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
362 git_attr_file
*file
= NULL
;
363 git_blob
*blob
= NULL
;
364 git_attr_file_stat_sig st
;
366 assert(filename
&& stack
);
368 /* join base and path as needed */
369 if (base
!= NULL
&& git_path_root(filename
) < 0) {
370 if (git_buf_joinpath(&path
, base
, filename
) < 0)
376 if (workdir
&& git__prefixcmp(relfile
, workdir
) == 0)
377 relfile
+= strlen(workdir
);
380 if (load_attr_from_cache(&file
, cache
, source
, relfile
) < 0)
383 /* if not in cache, load data, parse, and cache */
385 if (source
== GIT_ATTR_FILE_FROM_FILE
) {
387 memcpy(&st
, &file
->cache_data
.st
, sizeof(st
));
389 memset(&st
, 0, sizeof(st
));
391 error
= load_attr_file(&content
, &st
, filename
);
393 error
= load_attr_blob_from_index(&content
, &blob
,
394 repo
, file
? &file
->cache_data
.oid
: NULL
, relfile
);
398 /* not finding a file is not an error for this function */
399 if (error
== GIT_ENOTFOUND
) {
407 (error
= git_attr_file__new(&file
, source
, relfile
, &cache
->pool
)) < 0)
410 if (parse
&& (error
= parse(repo
, content
, file
)) < 0)
413 git_strmap_insert(cache
->files
, file
->key
, file
, error
);
417 /* remember "cache buster" file signature */
419 git_oid_cpy(&file
->cache_data
.oid
, git_object_id((git_object
*)blob
));
421 memcpy(&file
->cache_data
.st
, &st
, sizeof(st
));
424 /* push file onto vector if we found one*/
425 if (!error
&& file
!= NULL
)
426 error
= git_vector_insert(stack
, file
);
429 git_attr_file__free(file
);
434 git__free((void *)content
);
441 #define push_attr_file(R,S,B,F) \
442 git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))
445 git_repository
*repo
;
452 int git_attr_cache__decide_sources(
453 uint32_t flags
, bool has_wd
, bool has_index
, git_attr_file_source
*srcs
)
457 switch (flags
& 0x03) {
458 case GIT_ATTR_CHECK_FILE_THEN_INDEX
:
460 srcs
[count
++] = GIT_ATTR_FILE_FROM_FILE
;
462 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
464 case GIT_ATTR_CHECK_INDEX_THEN_FILE
:
466 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
468 srcs
[count
++] = GIT_ATTR_FILE_FROM_FILE
;
470 case GIT_ATTR_CHECK_INDEX_ONLY
:
472 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
479 static int push_one_attr(void *ref
, git_buf
*path
)
481 int error
= 0, n_src
, i
;
482 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
483 git_attr_file_source src
[2];
485 n_src
= git_attr_cache__decide_sources(
486 info
->flags
, info
->workdir
!= NULL
, info
->index
!= NULL
, src
);
488 for (i
= 0; !error
&& i
< n_src
; ++i
)
489 error
= git_attr_cache__push_file(
490 info
->repo
, path
->ptr
, GIT_ATTR_FILE
, src
[i
],
491 git_attr_file__parse_buffer
, info
->files
);
496 static int collect_attr_files(
497 git_repository
*repo
,
503 git_buf dir
= GIT_BUF_INIT
;
504 const char *workdir
= git_repository_workdir(repo
);
505 attr_walk_up_info info
;
507 if (git_attr_cache__init(repo
) < 0 ||
508 git_vector_init(files
, 4, NULL
) < 0)
511 /* Resolve path in a non-bare repo */
513 error
= git_path_find_dir(&dir
, path
, workdir
);
515 error
= git_path_dirname_r(&dir
, path
);
519 /* in precendence order highest to lowest:
520 * - $GIT_DIR/info/attributes
521 * - path components with .gitattributes
522 * - config core.attributesfile
523 * - $GIT_PREFIX/etc/gitattributes
526 error
= push_attr_file(
527 repo
, files
, git_repository_path(repo
), GIT_ATTR_FILE_INREPO
);
533 info
.workdir
= workdir
;
534 if (git_repository_index__weakptr(&info
.index
, repo
) < 0)
535 giterr_clear(); /* no error even if there is no index */
538 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
542 if (git_repository_attr_cache(repo
)->cfg_attr_file
!= NULL
) {
543 error
= push_attr_file(
544 repo
, files
, NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
);
549 if ((flags
& GIT_ATTR_CHECK_NO_SYSTEM
) == 0) {
550 error
= git_futils_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
552 error
= push_attr_file(repo
, files
, NULL
, dir
.ptr
);
553 else if (error
== GIT_ENOTFOUND
)
559 git_vector_free(files
);
566 int git_attr_cache__init(git_repository
*repo
)
569 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
572 if (cache
->initialized
)
575 /* cache config settings for attributes and ignores */
576 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
579 ret
= git_config_get_string(&cache
->cfg_attr_file
, cfg
, GIT_ATTR_CONFIG
);
580 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
583 ret
= git_config_get_string(&cache
->cfg_excl_file
, cfg
, GIT_IGNORE_CONFIG
);
584 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
589 /* allocate hashtable for attribute and ignore file contents */
590 if (cache
->files
== NULL
) {
591 cache
->files
= git_strmap_alloc();
592 GITERR_CHECK_ALLOC(cache
->files
);
595 /* allocate hashtable for attribute macros */
596 if (cache
->macros
== NULL
) {
597 cache
->macros
= git_strmap_alloc();
598 GITERR_CHECK_ALLOC(cache
->macros
);
601 /* allocate string pool */
602 if (git_pool_init(&cache
->pool
, 1, 0) < 0)
605 cache
->initialized
= 1;
607 /* insert default macros */
608 return git_attr_add_macro(repo
, "binary", "-diff -crlf -text");
611 void git_attr_cache_flush(
612 git_repository
*repo
)
614 git_attr_cache
*cache
;
619 cache
= git_repository_attr_cache(repo
);
621 if (cache
->files
!= NULL
) {
624 git_strmap_foreach_value(cache
->files
, file
, {
625 git_attr_file__free(file
);
628 git_strmap_free(cache
->files
);
631 if (cache
->macros
!= NULL
) {
634 git_strmap_foreach_value(cache
->macros
, rule
, {
635 git_attr_rule__free(rule
);
638 git_strmap_free(cache
->macros
);
641 git_pool_clear(&cache
->pool
);
643 cache
->initialized
= 0;
646 int git_attr_cache__insert_macro(git_repository
*repo
, git_attr_rule
*macro
)
648 git_strmap
*macros
= git_repository_attr_cache(repo
)->macros
;
651 /* TODO: generate warning log if (macro->assigns.length == 0) */
652 if (macro
->assigns
.length
== 0)
655 git_strmap_insert(macros
, macro
->match
.pattern
, macro
, error
);
656 return (error
< 0) ? -1 : 0;
659 git_attr_rule
*git_attr_cache__lookup_macro(
660 git_repository
*repo
, const char *name
)
662 git_strmap
*macros
= git_repository_attr_cache(repo
)->macros
;
665 pos
= git_strmap_lookup_index(macros
, name
);
667 if (!git_strmap_valid_index(macros
, pos
))
670 return (git_attr_rule
*)git_strmap_value_at(macros
, pos
);