Add ~ expansion to global attributes and excludes This adds ~/ prefix expansion for the value of core.attributesfile and core.excludesfile, plus it fixes the fact that the attributes cache was holding on to the string data from the config for a long time (instead of making its own strdup) which could have caused a problem if the config was refreshed. Adds a test for the new expansion capability.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
diff --git a/src/attr.c b/src/attr.c
index 9fe4471..6cdff29 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -596,26 +596,33 @@ static int collect_attr_files(
}
static int attr_cache__lookup_path(
- const char **out, git_config *cfg, const char *key, const char *fallback)
+ char **out, git_config *cfg, const char *key, const char *fallback)
{
git_buf buf = GIT_BUF_INIT;
int error;
+ const char *cfgval = NULL;
- if (!(error = git_config_get_string(out, cfg, key)))
- return 0;
+ *out = NULL;
+
+ if (!(error = git_config_get_string(&cfgval, cfg, key))) {
+
+ /* expand leading ~/ as needed */
+ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
+ !git_futils_find_global_file(&buf, &cfgval[2]))
+ *out = git_buf_detach(&buf);
+ else if (cfgval)
+ *out = git__strdup(cfgval);
- if (error == GIT_ENOTFOUND) {
+ } else if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
if (!git_futils_find_xdg_file(&buf, fallback))
*out = git_buf_detach(&buf);
- else
- *out = NULL;
-
- git_buf_free(&buf);
}
+ git_buf_free(&buf);
+
return error;
}
@@ -696,6 +703,12 @@ void git_attr_cache_flush(
git_pool_clear(&cache->pool);
+ git__free(cache->cfg_attr_file);
+ cache->cfg_attr_file = NULL;
+
+ git__free(cache->cfg_excl_file);
+ cache->cfg_excl_file = NULL;
+
cache->initialized = 0;
}
diff --git a/src/attrcache.h b/src/attrcache.h
index 12cec4b..077633b 100644
--- a/src/attrcache.h
+++ b/src/attrcache.h
@@ -13,10 +13,10 @@
typedef struct {
int initialized;
git_pool pool;
- git_strmap *files; /* hash path to git_attr_file of rules */
- git_strmap *macros; /* hash name to vector<git_attr_assignment> */
- const char *cfg_attr_file; /* cached value of core.attributesfile */
- const char *cfg_excl_file; /* cached value of core.excludesfile */
+ git_strmap *files; /* hash path to git_attr_file of rules */
+ git_strmap *macros; /* hash name to vector<git_attr_assignment> */
+ char *cfg_attr_file; /* cached value of core.attributesfile */
+ char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
extern int git_attr_cache__init(git_repository *repo);
diff --git a/tests-clar/attr/ignore.c b/tests-clar/attr/ignore.c
index aa81e92..8df0eb9 100644
--- a/tests-clar/attr/ignore.c
+++ b/tests-clar/attr/ignore.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
+#include "fileops.h"
static git_repository *g_repo = NULL;
@@ -20,7 +21,7 @@ void assert_is_ignored(bool expected, const char *filepath)
int is_ignored;
cl_git_pass(git_ignore_path_is_ignored(&is_ignored, g_repo, filepath));
- cl_assert_equal_i(expected, is_ignored == 1);
+ cl_assert_equal_b(expected, is_ignored);
}
void test_attr_ignore__honor_temporary_rules(void)
@@ -46,3 +47,35 @@ void test_attr_ignore__skip_gitignore_directory(void)
assert_is_ignored(true, "NewFolder/NewFolder");
assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
}
+
+void test_attr_ignore__expand_tilde_to_homedir(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_config *cfg;
+
+ assert_is_ignored(false, "example.global_with_tilde");
+
+ /* construct fake home with fake global excludes */
+
+ cl_must_pass(p_mkdir("home", 0777));
+ cl_git_pass(git_path_prettify(&path, "home", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_git_mkfile("home/globalexcludes", "# found me\n*.global_with_tilde\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexcludes"));
+ git_config_free(cfg);
+
+ git_attr_cache_flush(g_repo); /* must reset to pick up change */
+
+ assert_is_ignored(true, "example.global_with_tilde");
+
+ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
+
+ git_buf_free(&path);
+}