Commit e3a2a04c by Russell Belfer

Preload attribute files that may contain macros

There was a latent bug where files that use macro definitions
could be parsed before the macro definitions were loaded.  Because
of attribute file caching, preloading files that are going to be
used doesn't add a significant amount of overhead, so let's always
preload any files that could contain macros before we assemble the
actual vector of files to scan for attributes.
parent 50e46d60
......@@ -217,6 +217,74 @@ cleanup:
return error;
}
static int preload_attr_file(
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *file)
{
int error;
git_attr_file *preload = NULL;
if (!file)
return 0;
if (!(error = git_attr_cache__get(
&preload, repo, source, base, file, git_attr_file__parse_buffer)))
git_attr_file__free(preload);
return error;
}
static int attr_setup(git_repository *repo)
{
int error = 0;
const char *workdir = git_repository_workdir(repo);
git_index *idx = NULL;
git_buf sys = GIT_BUF_INIT;
if ((error = git_attr_cache__init(repo)) < 0)
return error;
/* preload attribute files that could contain macros so the
* definitions will be available for later file parsing
*/
if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) {
error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
git_buf_free(&sys);
}
if (error < 0) {
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
} else
return error;
}
if ((error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE,
NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
return error;
if ((error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE,
git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0)
return error;
if (workdir != NULL &&
(error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0)
return error;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
(error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0)
return error;
return error;
}
int git_attr_add_macro(
git_repository *repo,
const char *name,
......@@ -226,8 +294,8 @@ int git_attr_add_macro(
git_attr_rule *macro = NULL;
git_pool *pool;
if (git_attr_cache__init(repo) < 0)
return -1;
if ((error = attr_setup(repo)) < 0)
return error;
macro = git__calloc(1, sizeof(git_attr_rule));
GITERR_CHECK_ALLOC(macro);
......@@ -348,7 +416,7 @@ static int collect_attr_files(
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
if ((error = git_attr_cache__init(repo)) < 0)
if ((error = attr_setup(repo)) < 0)
return error;
/* Resolve path in a non-bare repo */
......
......@@ -248,9 +248,7 @@ int git_attr_file__parse_buffer(
repo, &attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any
* file other than .gitattributes at repo root.
*/
/* TODO: warning if macro found in file below repo root */
error = git_attr_cache__insert_macro(repo, rule);
else
error = git_vector_insert(&attrs->rules, rule);
......
......@@ -23,9 +23,7 @@ void test_attr_repo__cleanup(void)
g_repo = NULL;
}
void test_attr_repo__get_one(void)
{
struct attr_expected test_cases[] = {
static struct attr_expected get_one_test_cases[] = {
{ "root_test1", "repoattr", EXPECT_TRUE, NULL },
{ "root_test1", "rootattr", EXPECT_TRUE, NULL },
{ "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
......@@ -59,13 +57,40 @@ void test_attr_repo__get_one(void)
{ "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
{ "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
{ "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
{ NULL, NULL, 0, NULL }
}, *scan;
};
void test_attr_repo__get_one(void)
{
int i;
for (scan = test_cases; scan->path != NULL; scan++) {
for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) {
struct attr_expected *scan = &get_one_test_cases[i];
const char *value;
cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
attr_check_expected(
scan->expected, scan->expected_str, scan->attr, value);
}
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_one_start_deep(void)
{
int i;
for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) {
struct attr_expected *scan = &get_one_test_cases[i];
const char *value;
cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
attr_check_expected(scan->expected, scan->expected_str, scan->attr, value);
attr_check_expected(
scan->expected, scan->expected_str, scan->attr, value);
}
cl_assert(git_attr_cache__is_cached(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment