Skip to content

Commit 343fb83

Browse files
authored
Merge pull request libgit2#5156 from pks-t/pks/attr-macros-in-subdir
gitattributes: ignore macros defined in subdirectories
2 parents 368b979 + f834690 commit 343fb83

File tree

9 files changed

+365
-171
lines changed

9 files changed

+365
-171
lines changed

src/attr.c

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,16 @@ static int preload_attr_file(
252252
git_attr_session *attr_session,
253253
git_attr_file_source source,
254254
const char *base,
255-
const char *file)
255+
const char *file,
256+
bool allow_macros)
256257
{
257258
int error;
258259
git_attr_file *preload = NULL;
259260

260261
if (!file)
261262
return 0;
262-
if (!(error = git_attr_cache__get(
263-
&preload, repo, attr_session, source, base, file, git_attr_file__parse_buffer)))
263+
if (!(error = git_attr_cache__get(&preload, repo, attr_session, source, base, file,
264+
git_attr_file__parse_buffer, allow_macros)))
264265
git_attr_file__free(preload);
265266

266267
return error;
@@ -324,31 +325,31 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session)
324325

325326
if ((error = system_attr_file(&path, attr_session)) < 0 ||
326327
(error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
327-
NULL, path.ptr)) < 0) {
328+
NULL, path.ptr, true)) < 0) {
328329
if (error != GIT_ENOTFOUND)
329330
goto out;
330331
}
331332

332333
if ((error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
333-
NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
334+
NULL, git_repository_attr_cache(repo)->cfg_attr_file, true)) < 0)
334335
goto out;
335336

336337
git_buf_clear(&path); /* git_repository_item_path expects an empty buffer, because it uses git_buf_set */
337338
if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
338339
(error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
339-
path.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
340+
path.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) {
340341
if (error != GIT_ENOTFOUND)
341342
goto out;
342343
}
343344

344345
if ((workdir = git_repository_workdir(repo)) != NULL &&
345346
(error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
346-
workdir, GIT_ATTR_FILE)) < 0)
347+
workdir, GIT_ATTR_FILE, true)) < 0)
347348
goto out;
348349

349350
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
350351
(error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_INDEX,
351-
NULL, GIT_ATTR_FILE)) < 0)
352+
NULL, GIT_ATTR_FILE, true)) < 0)
352353
goto out;
353354

354355
if (attr_session)
@@ -436,13 +437,14 @@ static int push_attr_file(
436437
git_vector *list,
437438
git_attr_file_source source,
438439
const char *base,
439-
const char *filename)
440+
const char *filename,
441+
bool allow_macros)
440442
{
441443
int error = 0;
442444
git_attr_file *file = NULL;
443445

444446
error = git_attr_cache__get(&file, repo, attr_session,
445-
source, base, filename, git_attr_file__parse_buffer);
447+
source, base, filename, git_attr_file__parse_buffer, allow_macros);
446448

447449
if (error < 0)
448450
return error;
@@ -457,16 +459,18 @@ static int push_attr_file(
457459

458460
static int push_one_attr(void *ref, const char *path)
459461
{
460-
int error = 0, n_src, i;
461462
attr_walk_up_info *info = (attr_walk_up_info *)ref;
462463
git_attr_file_source src[2];
464+
int error = 0, n_src, i;
465+
bool allow_macros;
463466

464467
n_src = attr_decide_sources(
465468
info->flags, info->workdir != NULL, info->index != NULL, src);
469+
allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
466470

467471
for (i = 0; !error && i < n_src; ++i)
468-
error = push_attr_file(info->repo, info->attr_session,
469-
info->files, src[i], path, GIT_ATTR_FILE);
472+
error = push_attr_file(info->repo, info->attr_session, info->files,
473+
src[i], path, GIT_ATTR_FILE, allow_macros);
470474

471475
return error;
472476
}
@@ -515,7 +519,7 @@ static int collect_attr_files(
515519

516520
if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
517521
(error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
518-
attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
522+
attrfile.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) {
519523
if (error != GIT_ENOTFOUND)
520524
goto cleanup;
521525
}
@@ -537,9 +541,8 @@ static int collect_attr_files(
537541
goto cleanup;
538542

539543
if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
540-
error = push_attr_file(
541-
repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
542-
NULL, git_repository_attr_cache(repo)->cfg_attr_file);
544+
error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
545+
NULL, git_repository_attr_cache(repo)->cfg_attr_file, true);
543546
if (error < 0)
544547
goto cleanup;
545548
}
@@ -548,9 +551,8 @@ static int collect_attr_files(
548551
error = system_attr_file(&dir, attr_session);
549552

550553
if (!error)
551-
error = push_attr_file(
552-
repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
553-
NULL, dir.ptr);
554+
error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
555+
NULL, dir.ptr, true);
554556
else if (error == GIT_ENOTFOUND)
555557
error = 0;
556558
}

src/attr_file.c

Lines changed: 48 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ int git_attr_file__load(
105105
git_attr_session *attr_session,
106106
git_attr_file_entry *entry,
107107
git_attr_file_source source,
108-
git_attr_file_parser parser)
108+
git_attr_file_parser parser,
109+
bool allow_macros)
109110
{
110111
int error = 0;
111112
git_blob *blob = NULL;
@@ -177,7 +178,7 @@ int git_attr_file__load(
177178
if (attr_session)
178179
file->session_key = attr_session->key;
179180

180-
if (parser && (error = parser(repo, file, content_str)) < 0) {
181+
if (parser && (error = parser(repo, file, content_str, allow_macros)) < 0) {
181182
git_attr_file__free(file);
182183
goto cleanup;
183184
}
@@ -249,16 +250,15 @@ static bool parse_optimized_patterns(
249250
const char *pattern);
250251

251252
int git_attr_file__parse_buffer(
252-
git_repository *repo, git_attr_file *attrs, const char *data)
253+
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros)
253254
{
254-
int error = 0;
255255
const char *scan = data, *context = NULL;
256256
git_attr_rule *rule = NULL;
257+
int error = 0;
257258

258-
/* if subdir file path, convert context for file paths */
259-
if (attrs->entry &&
260-
git_path_root(attrs->entry->path) < 0 &&
261-
!git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
259+
/* If subdir file path, convert context for file paths */
260+
if (attrs->entry && git_path_root(attrs->entry->path) < 0 &&
261+
!git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
262262
context = attrs->entry->path;
263263

264264
if (git_mutex_lock(&attrs->lock) < 0) {
@@ -267,38 +267,38 @@ int git_attr_file__parse_buffer(
267267
}
268268

269269
while (!error && *scan) {
270-
/* allocate rule if needed */
271-
if (!rule && !(rule = git__calloc(1, sizeof(*rule)))) {
272-
error = -1;
273-
break;
274-
}
275-
276-
rule->match.flags =
277-
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO;
278-
279-
/* parse the next "pattern attr attr attr" line */
280-
if (!(error = git_attr_fnmatch__parse(
281-
&rule->match, &attrs->pool, context, &scan)) &&
282-
!(error = git_attr_assignment__parse(
283-
repo, &attrs->pool, &rule->assigns, &scan)))
270+
/* Allocate rule if needed, otherwise re-use previous rule */
271+
if (!rule) {
272+
rule = git__calloc(1, sizeof(*rule));
273+
GIT_ERROR_CHECK_ALLOC(rule);
274+
} else
275+
git_attr_rule__clear(rule);
276+
277+
rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO;
278+
279+
/* Parse the next "pattern attr attr attr" line */
280+
if ((error = git_attr_fnmatch__parse(&rule->match, &attrs->pool, context, &scan)) < 0 ||
281+
(error = git_attr_assignment__parse(repo, &attrs->pool, &rule->assigns, &scan)) < 0)
284282
{
285-
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
286-
/* TODO: warning if macro found in file below repo root */
287-
error = git_attr_cache__insert_macro(repo, rule);
288-
else
289-
error = git_vector_insert(&attrs->rules, rule);
283+
if (error != GIT_ENOTFOUND)
284+
goto out;
285+
error = 0;
286+
continue;
290287
}
291288

292-
/* if the rule wasn't a pattern, on to the next */
293-
if (error < 0) {
294-
git_attr_rule__clear(rule); /* reset rule contents */
295-
if (error == GIT_ENOTFOUND)
296-
error = 0;
297-
} else {
298-
rule = NULL; /* vector now "owns" the rule */
299-
}
289+
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) {
290+
/* TODO: warning if macro found in file below repo root */
291+
if (!allow_macros)
292+
continue;
293+
if ((error = git_attr_cache__insert_macro(repo, rule)) < 0)
294+
goto out;
295+
} else if ((error = git_vector_insert(&attrs->rules, rule)) < 0)
296+
goto out;
297+
298+
rule = NULL;
300299
}
301300

301+
out:
302302
git_mutex_unlock(&attrs->lock);
303303
git_attr_rule__free(rule);
304304

@@ -345,33 +345,28 @@ int git_attr_file__lookup_one(
345345

346346
int git_attr_file__load_standalone(git_attr_file **out, const char *path)
347347
{
348-
int error;
349-
git_attr_file *file;
350348
git_buf content = GIT_BUF_INIT;
349+
git_attr_file *file = NULL;
350+
int error;
351351

352-
error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE);
353-
if (error < 0)
354-
return error;
352+
if ((error = git_futils_readbuffer(&content, path)) < 0)
353+
goto out;
355354

356-
error = git_attr_cache__alloc_file_entry(
357-
&file->entry, NULL, path, &file->pool);
358-
if (error < 0) {
359-
git_attr_file__free(file);
360-
return error;
361-
}
362-
/* because the cache entry is allocated from the file's own pool, we
355+
/*
356+
* Because the cache entry is allocated from the file's own pool, we
363357
* don't have to free it - freeing file+pool will free cache entry, too.
364358
*/
365359

366-
if (!(error = git_futils_readbuffer(&content, path))) {
367-
error = git_attr_file__parse_buffer(NULL, file, content.ptr);
368-
git_buf_dispose(&content);
369-
}
360+
if ((error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE)) < 0 ||
361+
(error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 ||
362+
(error = git_attr_cache__alloc_file_entry(&file->entry, NULL, path, &file->pool)) < 0)
363+
goto out;
370364

365+
*out = file;
366+
out:
371367
if (error < 0)
372368
git_attr_file__free(file);
373-
else
374-
*out = file;
369+
git_buf_dispose(&content);
375370

376371
return error;
377372
}

src/attr_file.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ extern int git_attr_get_many_with_session(
131131
typedef int (*git_attr_file_parser)(
132132
git_repository *repo,
133133
git_attr_file *file,
134-
const char *data);
134+
const char *data,
135+
bool allow_macros);
135136

136137
/*
137138
* git_attr_file API
@@ -150,7 +151,8 @@ int git_attr_file__load(
150151
git_attr_session *attr_session,
151152
git_attr_file_entry *ce,
152153
git_attr_file_source source,
153-
git_attr_file_parser parser);
154+
git_attr_file_parser parser,
155+
bool allow_macros);
154156

155157
int git_attr_file__load_standalone(
156158
git_attr_file **out, const char *path);
@@ -159,7 +161,7 @@ int git_attr_file__out_of_date(
159161
git_repository *repo, git_attr_session *session, git_attr_file *file);
160162

161163
int git_attr_file__parse_buffer(
162-
git_repository *repo, git_attr_file *attrs, const char *data);
164+
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros);
163165

164166
int git_attr_file__clear_rules(
165167
git_attr_file *file, bool need_lock);

src/attrcache.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ int git_attr_cache__get(
208208
git_attr_file_source source,
209209
const char *base,
210210
const char *filename,
211-
git_attr_file_parser parser)
211+
git_attr_file_parser parser,
212+
bool allow_macros)
212213
{
213214
int error = 0;
214215
git_attr_cache *cache = git_repository_attr_cache(repo);
@@ -221,7 +222,7 @@ int git_attr_cache__get(
221222

222223
/* load file if we don't have one or if existing one is out of date */
223224
if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
224-
error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser);
225+
error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser, allow_macros);
225226

226227
/* if we loaded the file, insert into and/or update cache */
227228
if (updated) {
@@ -424,21 +425,36 @@ void git_attr_cache_flush(git_repository *repo)
424425
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
425426
{
426427
git_attr_cache *cache = git_repository_attr_cache(repo);
427-
git_strmap *macros = cache->macros;
428-
int error;
429-
430-
/* TODO: generate warning log if (macro->assigns.length == 0) */
431-
if (macro->assigns.length == 0)
432-
return 0;
428+
git_attr_rule *preexisting;
429+
bool locked = false;
430+
int error = 0;
433431

434-
if (attr_cache_lock(cache) < 0) {
435-
git_error_set(GIT_ERROR_OS, "unable to get attr cache lock");
436-
error = -1;
437-
} else {
438-
error = git_strmap_set(macros, macro->match.pattern, macro);
439-
git_mutex_unlock(&cache->lock);
432+
/*
433+
* Callers assume that if we return success, that the
434+
* macro will have been adopted by the attributes cache.
435+
* Thus, we have to free the macro here if it's not being
436+
* added to the cache.
437+
*
438+
* TODO: generate warning log if (macro->assigns.length == 0)
439+
*/
440+
if (macro->assigns.length == 0) {
441+
git_attr_rule__free(macro);
442+
goto out;
440443
}
441444

445+
if ((error = attr_cache_lock(cache)) < 0)
446+
goto out;
447+
locked = true;
448+
449+
if ((preexisting = git_strmap_get(cache->macros, macro->match.pattern)) != NULL)
450+
git_attr_rule__free(preexisting);
451+
452+
if ((error = git_strmap_set(cache->macros, macro->match.pattern, macro)) < 0)
453+
goto out;
454+
455+
out:
456+
if (locked)
457+
attr_cache_unlock(cache);
442458
return error;
443459
}
444460

src/attrcache.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ extern int git_attr_cache__get(
3434
git_attr_file_source source,
3535
const char *base,
3636
const char *filename,
37-
git_attr_file_parser parser);
37+
git_attr_file_parser parser,
38+
bool allow_macros);
3839

3940
extern bool git_attr_cache__is_cached(
4041
git_repository *repo,

0 commit comments

Comments
 (0)