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
) {
406 /* if we got here, we have to parse and/or reparse the file */
408 git_attr_file__clear_rules(file
);
410 error
= git_attr_file__new(&file
, source
, relfile
, &cache
->pool
);
415 if (parse
&& (error
= parse(repo
, content
, file
)) < 0)
418 git_strmap_insert(cache
->files
, file
->key
, file
, error
);
422 /* remember "cache buster" file signature */
424 git_oid_cpy(&file
->cache_data
.oid
, git_object_id((git_object
*)blob
));
426 memcpy(&file
->cache_data
.st
, &st
, sizeof(st
));
429 /* push file onto vector if we found one*/
430 if (!error
&& file
!= NULL
)
431 error
= git_vector_insert(stack
, file
);
434 git_attr_file__free(file
);
439 git__free((void *)content
);
446 #define push_attr_file(R,S,B,F) \
447 git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))
450 git_repository
*repo
;
457 int git_attr_cache__decide_sources(
458 uint32_t flags
, bool has_wd
, bool has_index
, git_attr_file_source
*srcs
)
462 switch (flags
& 0x03) {
463 case GIT_ATTR_CHECK_FILE_THEN_INDEX
:
465 srcs
[count
++] = GIT_ATTR_FILE_FROM_FILE
;
467 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
469 case GIT_ATTR_CHECK_INDEX_THEN_FILE
:
471 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
473 srcs
[count
++] = GIT_ATTR_FILE_FROM_FILE
;
475 case GIT_ATTR_CHECK_INDEX_ONLY
:
477 srcs
[count
++] = GIT_ATTR_FILE_FROM_INDEX
;
484 static int push_one_attr(void *ref
, git_buf
*path
)
486 int error
= 0, n_src
, i
;
487 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
488 git_attr_file_source src
[2];
490 n_src
= git_attr_cache__decide_sources(
491 info
->flags
, info
->workdir
!= NULL
, info
->index
!= NULL
, src
);
493 for (i
= 0; !error
&& i
< n_src
; ++i
)
494 error
= git_attr_cache__push_file(
495 info
->repo
, path
->ptr
, GIT_ATTR_FILE
, src
[i
],
496 git_attr_file__parse_buffer
, info
->files
);
501 static int collect_attr_files(
502 git_repository
*repo
,
508 git_buf dir
= GIT_BUF_INIT
;
509 const char *workdir
= git_repository_workdir(repo
);
510 attr_walk_up_info info
;
512 if (git_attr_cache__init(repo
) < 0 ||
513 git_vector_init(files
, 4, NULL
) < 0)
516 /* Resolve path in a non-bare repo */
518 error
= git_path_find_dir(&dir
, path
, workdir
);
520 error
= git_path_dirname_r(&dir
, path
);
524 /* in precendence order highest to lowest:
525 * - $GIT_DIR/info/attributes
526 * - path components with .gitattributes
527 * - config core.attributesfile
528 * - $GIT_PREFIX/etc/gitattributes
531 error
= push_attr_file(
532 repo
, files
, git_repository_path(repo
), GIT_ATTR_FILE_INREPO
);
538 info
.workdir
= workdir
;
539 if (git_repository_index__weakptr(&info
.index
, repo
) < 0)
540 giterr_clear(); /* no error even if there is no index */
543 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
547 if (git_repository_attr_cache(repo
)->cfg_attr_file
!= NULL
) {
548 error
= push_attr_file(
549 repo
, files
, NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
);
554 if ((flags
& GIT_ATTR_CHECK_NO_SYSTEM
) == 0) {
555 error
= git_futils_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
557 error
= push_attr_file(repo
, files
, NULL
, dir
.ptr
);
558 else if (error
== GIT_ENOTFOUND
)
564 git_vector_free(files
);
571 int git_attr_cache__init(git_repository
*repo
)
574 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
577 if (cache
->initialized
)
580 /* cache config settings for attributes and ignores */
581 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
584 ret
= git_config_get_string(&cache
->cfg_attr_file
, cfg
, GIT_ATTR_CONFIG
);
585 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
588 ret
= git_config_get_string(&cache
->cfg_excl_file
, cfg
, GIT_IGNORE_CONFIG
);
589 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
594 /* allocate hashtable for attribute and ignore file contents */
595 if (cache
->files
== NULL
) {
596 cache
->files
= git_strmap_alloc();
597 GITERR_CHECK_ALLOC(cache
->files
);
600 /* allocate hashtable for attribute macros */
601 if (cache
->macros
== NULL
) {
602 cache
->macros
= git_strmap_alloc();
603 GITERR_CHECK_ALLOC(cache
->macros
);
606 /* allocate string pool */
607 if (git_pool_init(&cache
->pool
, 1, 0) < 0)
610 cache
->initialized
= 1;
612 /* insert default macros */
613 return git_attr_add_macro(repo
, "binary", "-diff -crlf -text");
616 void git_attr_cache_flush(
617 git_repository
*repo
)
619 git_attr_cache
*cache
;
624 cache
= git_repository_attr_cache(repo
);
626 if (cache
->files
!= NULL
) {
629 git_strmap_foreach_value(cache
->files
, file
, {
630 git_attr_file__free(file
);
633 git_strmap_free(cache
->files
);
636 if (cache
->macros
!= NULL
) {
639 git_strmap_foreach_value(cache
->macros
, rule
, {
640 git_attr_rule__free(rule
);
643 git_strmap_free(cache
->macros
);
646 git_pool_clear(&cache
->pool
);
648 cache
->initialized
= 0;
651 int git_attr_cache__insert_macro(git_repository
*repo
, git_attr_rule
*macro
)
653 git_strmap
*macros
= git_repository_attr_cache(repo
)->macros
;
656 /* TODO: generate warning log if (macro->assigns.length == 0) */
657 if (macro
->assigns
.length
== 0)
660 git_strmap_insert(macros
, macro
->match
.pattern
, macro
, error
);
661 return (error
< 0) ? -1 : 0;
664 git_attr_rule
*git_attr_cache__lookup_macro(
665 git_repository
*repo
, const char *name
)
667 git_strmap
*macros
= git_repository_attr_cache(repo
)->macros
;
670 pos
= git_strmap_lookup_index(macros
, name
);
672 if (!git_strmap_valid_index(macros
, pos
))
675 return (git_attr_rule
*)git_strmap_value_at(macros
, pos
);