summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <carlosmn@github.com>2017-11-04 18:30:29 +0100
committerGitHub <noreply@github.com>2017-11-04 18:30:29 +0100
commit0d723f39decff4ff77def8a4acf3b7a656af59b2 (patch)
tree4e7363374dff5ed9fd55023129c0c1c1c71909e2
parent990d2b854ab7eef28dd09209f77ff95d8e6a4ca2 (diff)
parent42627933327bb8f91ae847bc8c72ab15ec1e2592 (diff)
downloadlibgit2-0d723f39decff4ff77def8a4acf3b7a656af59b2.tar.gz
Merge pull request #4332 from pks-t/pks/conditional-includes
Conditional includes
-rw-r--r--include/git2/config.h3
-rw-r--r--include/git2/repository.h12
-rw-r--r--include/git2/sys/config.h5
-rw-r--r--src/config.c18
-rw-r--r--src/config_file.c195
-rw-r--r--src/config_file.h4
-rw-r--r--src/path.h13
-rw-r--r--src/repository.c24
-rw-r--r--src/submodule.c2
-rw-r--r--src/win32/path_w32.c15
-rw-r--r--tests/config/backend.c4
-rw-r--r--tests/config/conditionals.c103
-rw-r--r--tests/config/configlevel.c14
-rw-r--r--tests/config/multivar.c12
-rw-r--r--tests/config/read.c30
-rw-r--r--tests/config/readonly.c10
-rw-r--r--tests/config/write.c8
17 files changed, 359 insertions, 113 deletions
diff --git a/include/git2/config.h b/include/git2/config.h
index d0f1ba1b3..d812e16bd 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -199,6 +199,8 @@ GIT_EXTERN(int) git_config_new(git_config **out);
* @param path path to the configuration file to add
* @param level the priority level of the backend
* @param force replace config file at the given priority level
+ * @param repo optional repository to allow parsing of
+ * conditional includes
* @return 0 on success, GIT_EEXISTS when adding more than one file
* for a given priority level (and force_replace set to 0),
* GIT_ENOTFOUND when the file doesn't exist or error code
@@ -207,6 +209,7 @@ GIT_EXTERN(int) git_config_add_file_ondisk(
git_config *cfg,
const char *path,
git_config_level_t level,
+ const git_repository *repo,
int force);
/**
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 8aac0b3f7..6e0c1f71e 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -440,7 +440,7 @@ typedef enum {
* @param item The repository item for which to retrieve the path
* @return 0, GIT_ENOTFOUND if the path cannot exist or an error code
*/
-GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item);
+GIT_EXTERN(int) git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item);
/**
* Get the path of this repository
@@ -451,7 +451,7 @@ GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git
* @param repo A repository object
* @return the path to the repository
*/
-GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_path(const git_repository *repo);
/**
* Get the path of the working directory for this repository
@@ -462,7 +462,7 @@ GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
* @param repo A repository object
* @return the path to the working dir, if it exists
*/
-GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_workdir(const git_repository *repo);
/**
* Get the path of the shared common directory for this repository
@@ -473,7 +473,7 @@ GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
* @param repo A repository object
* @return the path to the common dir
*/
-GIT_EXTERN(const char *) git_repository_commondir(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_commondir(const git_repository *repo);
/**
* Set the path to the working directory for this repository
@@ -501,7 +501,7 @@ GIT_EXTERN(int) git_repository_set_workdir(
* @param repo Repo to test
* @return 1 if the repository is bare, 0 otherwise.
*/
-GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_bare(const git_repository *repo);
/**
* Check if a repository is a linked work tree
@@ -509,7 +509,7 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
* @param repo Repo to test
* @return 1 if the repository is a linked work tree, 0 otherwise.
*/
-GIT_EXTERN(int) git_repository_is_worktree(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_worktree(const git_repository *repo);
/**
* Get the configuration file for this repository.
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 4dad6da42..ed203226f 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -58,7 +58,7 @@ struct git_config_backend {
struct git_config *cfg;
/* Open means open the file/database and parse if necessary */
- int (*open)(struct git_config_backend *, git_config_level_t level);
+ int (*open)(struct git_config_backend *, git_config_level_t level, const git_repository *repo);
int (*get)(struct git_config_backend *, const char *key, git_config_entry **entry);
int (*set)(struct git_config_backend *, const char *key, const char *value);
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
@@ -111,6 +111,8 @@ GIT_EXTERN(int) git_config_init_backend(
* @param cfg the configuration to add the file to
* @param file the configuration file (backend) to add
* @param level the priority level of the backend
+ * @param repo optional repository to allow parsing of
+ * conditional includes
* @param force if a config file already exists for the given
* priority level, replace it
* @return 0 on success, GIT_EEXISTS when adding more than one file
@@ -120,6 +122,7 @@ GIT_EXTERN(int) git_config_add_backend(
git_config *cfg,
git_config_backend *file,
git_config_level_t level,
+ const git_repository *repo,
int force);
/** @} */
diff --git a/src/config.c b/src/config.c
index 602e0e827..1bc11b99f 100644
--- a/src/config.c
+++ b/src/config.c
@@ -99,6 +99,7 @@ int git_config_add_file_ondisk(
git_config *cfg,
const char *path,
git_config_level_t level,
+ const git_repository *repo,
int force)
{
git_config_backend *file = NULL;
@@ -116,7 +117,7 @@ int git_config_add_file_ondisk(
if (git_config_file__ondisk(&file, path) < 0)
return -1;
- if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
+ if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
@@ -138,7 +139,7 @@ int git_config_open_ondisk(git_config **out, const char *path)
if (git_config_new(&config) < 0)
return -1;
- if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
+ if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)) < 0)
git_config_free(config);
else
*out = config;
@@ -164,7 +165,7 @@ int git_config_snapshot(git_config **out, git_config *in)
if ((error = internal->file->snapshot(&b, internal->file)) < 0)
break;
- if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
+ if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
b->free(b);
break;
}
@@ -307,6 +308,7 @@ int git_config_add_backend(
git_config *cfg,
git_config_backend *file,
git_config_level_t level,
+ const git_repository *repo,
int force)
{
file_internal *internal;
@@ -316,7 +318,7 @@ int git_config_add_backend(
GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
- if ((result = file->open(file, level)) < 0)
+ if ((result = file->open(file, level, repo)) < 0)
return result;
internal = git__malloc(sizeof(file_internal));
@@ -1147,20 +1149,20 @@ int git_config_open_default(git_config **out)
if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) {
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_GLOBAL, 0);
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
}
if (!error && !git_config_find_xdg(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_XDG, 0);
+ GIT_CONFIG_LEVEL_XDG, NULL, 0);
if (!error && !git_config_find_system(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_SYSTEM, 0);
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0);
if (!error && !git_config_find_programdata(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_PROGRAMDATA, 0);
+ GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
git_buf_free(&buf);
diff --git a/src/config_file.c b/src/config_file.c
index 9a6d81516..cc4e7b3b7 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -105,6 +105,7 @@ typedef struct {
diskfile_header header;
git_config_level_t level;
+ const git_repository *repo;
bool locked;
git_filebuf locked_buf;
@@ -119,7 +120,7 @@ typedef struct {
diskfile_backend *snapshot_from;
} diskfile_readonly_backend;
-static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth);
+static int config_read(git_strmap *values, const git_repository *repo, struct config_file *file, git_config_level_t level, int depth);
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
@@ -281,12 +282,13 @@ static void config_file_clear(struct config_file *file)
git__free(file->path);
}
-static int config_open(git_config_backend *cfg, git_config_level_t level)
+static int config_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
{
int res;
diskfile_backend *b = (diskfile_backend *)cfg;
b->level = level;
+ b->repo = repo;
if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
return res;
@@ -295,7 +297,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
if (!git_path_exists(b->file.path))
return 0;
- if (res < 0 || (res = config_read(b->header.values->values, &b->file, level, 0)) < 0) {
+ if (res < 0 || (res = config_read(b->header.values->values, repo, &b->file, level, 0)) < 0) {
refcounted_strmap_free(b->header.values);
b->header.values = NULL;
}
@@ -359,7 +361,7 @@ static int config_refresh(git_config_backend *cfg)
}
git_array_clear(b->file.includes);
- if ((error = config_read(values->values, &b->file, b->level, 0)) < 0)
+ if ((error = config_read(values->values, b->repo, &b->file, b->level, 0)) < 0)
goto out;
if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
@@ -439,7 +441,7 @@ static int config_iterator_new(
if ((error = config_snapshot(&snapshot, backend)) < 0)
return error;
- if ((error = snapshot->open(snapshot, b->level)) < 0)
+ if ((error = snapshot->open(snapshot, b->level, b->repo)) < 0)
return error;
it = git__calloc(1, sizeof(git_config_file_iter));
@@ -831,7 +833,7 @@ static void backend_readonly_free(git_config_backend *_backend)
git__free(backend);
}
-static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
+static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
{
diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
diskfile_backend *src = b->snapshot_from;
@@ -842,8 +844,9 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
return error;
- /* We're just copying data, don't care about the level */
+ /* We're just copying data, don't care about the level or repo*/
GIT_UNUSED(level);
+ GIT_UNUSED(repo);
if ((src_map = refcounted_strmap_take(src_header)) == NULL)
return -1;
@@ -1568,11 +1571,148 @@ static int config_parse(
}
struct parse_data {
+ const git_repository *repo;
+ const char *file_path;
git_strmap *values;
git_config_level_t level;
int depth;
};
+static int parse_include(struct reader *reader,
+ struct parse_data *parse_data, const char *file)
+{
+ struct config_file *include;
+ git_buf path = GIT_BUF_INIT;
+ char *dir;
+ int result;
+
+ if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
+ return result;
+
+ dir = git_buf_detach(&path);
+ result = included_path(&path, dir, file);
+ git__free(dir);
+
+ if (result < 0)
+ return result;
+
+ include = git_array_alloc(reader->file->includes);
+ memset(include, 0, sizeof(*include));
+ git_array_init(include->includes);
+ include->path = git_buf_detach(&path);
+
+ result = config_read(parse_data->values, parse_data->repo,
+ include, parse_data->level, parse_data->depth+1);
+
+ if (result == GIT_ENOTFOUND) {
+ giterr_clear();
+ result = 0;
+ }
+
+ return result;
+}
+
+static int do_match_gitdir(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value,
+ bool case_insensitive)
+{
+ git_buf path = GIT_BUF_INIT;
+ int error, fnmatch_flags;
+
+ if (value[0] == '.' && git_path_is_dirsep(value[1])) {
+ git_path_dirname_r(&path, cfg_file);
+ git_buf_joinpath(&path, path.ptr, value + 2);
+ } else if (value[0] == '~' && git_path_is_dirsep(value[1]))
+ git_sysdir_expand_global_file(&path, value + 1);
+ else if (!git_path_is_absolute(value))
+ git_buf_joinpath(&path, "**", value);
+ else
+ git_buf_sets(&path, value);
+
+ if (git_buf_oom(&path)) {
+ error = -1;
+ goto out;
+ }
+
+ if (git_path_is_dirsep(value[strlen(value) - 1]))
+ git_buf_puts(&path, "**");
+
+ fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
+ if (case_insensitive)
+ fnmatch_flags |= FNM_IGNORECASE;
+
+ if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
+ goto out;
+
+ *matches = (error == 0);
+
+out:
+ git_buf_free(&path);
+ return error;
+}
+
+static int conditional_match_gitdir(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value)
+{
+ return do_match_gitdir(matches, repo, cfg_file, value, false);
+}
+
+static int conditional_match_gitdir_i(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value)
+{
+ return do_match_gitdir(matches, repo, cfg_file, value, true);
+}
+
+static const struct {
+ const char *prefix;
+ int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
+} conditions[] = {
+ { "gitdir:", conditional_match_gitdir },
+ { "gitdir/i:", conditional_match_gitdir_i }
+};
+
+static int parse_conditional_include(struct reader *reader,
+ struct parse_data *parse_data, const char *section, const char *file)
+{
+ char *condition;
+ size_t i;
+ int error = 0, matches;
+
+ if (!parse_data->repo)
+ return 0;
+
+ condition = git__substrdup(section + strlen("includeIf."),
+ strlen(section) - strlen("includeIf.") - strlen(".path"));
+
+ for (i = 0; i < ARRAY_SIZE(conditions); i++) {
+ if (git__prefixcmp(condition, conditions[i].prefix))
+ continue;
+
+ if ((error = conditions[i].matches(&matches,
+ parse_data->repo,
+ parse_data->file_path,
+ condition + strlen(conditions[i].prefix))) < 0)
+ break;
+
+ if (matches)
+ error = parse_include(reader, parse_data, file);
+
+ break;
+ }
+
+ git__free(condition);
+ return error;
+}
+
static int read_on_variable(
struct reader *reader,
const char *current_section,
@@ -1615,38 +1755,23 @@ static int read_on_variable(
result = 0;
/* Add or append the new config option */
- if (!git__strcmp(var->entry->name, "include.path")) {
- struct config_file *include;
- git_buf path = GIT_BUF_INIT;
- char *dir;
-
- if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
- return result;
-
- dir = git_buf_detach(&path);
- result = included_path(&path, dir, var->entry->value);
- git__free(dir);
-
- if (result < 0)
- return result;
+ if (!git__strcmp(var->entry->name, "include.path"))
+ result = parse_include(reader, parse_data, var->entry->value);
+ else if (!git__prefixcmp(var->entry->name, "includeif.") &&
+ !git__suffixcmp(var->entry->name, ".path"))
+ result = parse_conditional_include(reader, parse_data,
+ var->entry->name, var->entry->value);
- include = git_array_alloc(reader->file->includes);
- memset(include, 0, sizeof(*include));
- git_array_init(include->includes);
- include->path = git_buf_detach(&path);
-
- result = config_read(parse_data->values, include, parse_data->level, parse_data->depth+1);
-
- if (result == GIT_ENOTFOUND) {
- giterr_clear();
- result = 0;
- }
- }
return result;
}
-static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth)
+static int config_read(
+ git_strmap *values,
+ const git_repository *repo,
+ struct config_file *file,
+ git_config_level_t level,
+ int depth)
{
struct parse_data parse_data;
struct reader reader;
@@ -1675,6 +1800,8 @@ static int config_read(git_strmap *values, struct config_file *file, git_config_
if (*reader.read_ptr == '\0')
goto out;
+ parse_data.repo = repo;
+ parse_data.file_path = file->path;
parse_data.values = values;
parse_data.level = level;
parse_data.depth = depth;
diff --git a/src/config_file.h b/src/config_file.h
index 11b8118f5..25ef45e5b 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -12,9 +12,9 @@
#include "git2/sys/config.h"
#include "git2/config.h"
-GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
+GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
{
- return cfg->open(cfg, level);
+ return cfg->open(cfg, level, repo);
}
GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
diff --git a/src/path.h b/src/path.h
index 360372cfb..aa24bcd2f 100644
--- a/src/path.h
+++ b/src/path.h
@@ -105,6 +105,12 @@ GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name)
(name[1] == L'.' && name[2] == L'\0')));
}
+#define git_path_is_absolute(p) \
+ (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
+
+#define git_path_is_dirsep(p) \
+ ((p) == '/' || (p) == '\\')
+
/**
* Convert backslashes in path to forward slashes.
*/
@@ -119,6 +125,13 @@ GIT_INLINE(void) git_path_mkposix(char *path)
}
#else
# define git_path_mkposix(p) /* blank */
+
+#define git_path_is_absolute(p) \
+ ((p)[0] == '/')
+
+#define git_path_is_dirsep(p) \
+ ((p) == '/')
+
#endif
/**
diff --git a/src/repository.c b/src/repository.c
index fe549e6e8..fe0696355 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -946,7 +946,7 @@ static int load_config(
return error;
if ((error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0)
- error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0);
+ error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, repo, 0);
if (error && error != GIT_ENOTFOUND)
goto on_error;
@@ -955,25 +955,25 @@ static int load_config(
if (global_config_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 &&
+ cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
if (xdg_config_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0)) < 0 &&
+ cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
if (system_config_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0)) < 0 &&
+ cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
if (programdata_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, 0)) < 0 &&
+ cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
@@ -1475,7 +1475,7 @@ static int repo_local_config(
giterr_clear();
if (!(error = git_config_add_file_ondisk(
- parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
+ parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, repo, false)))
error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
}
@@ -2256,7 +2256,7 @@ int git_repository_is_empty(git_repository *repo)
return is_empty;
}
-int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item)
+int git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item)
{
const char *parent;
@@ -2296,13 +2296,13 @@ int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_
return 0;
}
-const char *git_repository_path(git_repository *repo)
+const char *git_repository_path(const git_repository *repo)
{
assert(repo);
return repo->gitdir;
}
-const char *git_repository_workdir(git_repository *repo)
+const char *git_repository_workdir(const git_repository *repo)
{
assert(repo);
@@ -2312,7 +2312,7 @@ const char *git_repository_workdir(git_repository *repo)
return repo->workdir;
}
-const char *git_repository_commondir(git_repository *repo)
+const char *git_repository_commondir(const git_repository *repo)
{
assert(repo);
return repo->commondir;
@@ -2362,13 +2362,13 @@ int git_repository_set_workdir(
return error;
}
-int git_repository_is_bare(git_repository *repo)
+int git_repository_is_bare(const git_repository *repo)
{
assert(repo);
return repo->is_bare;
}
-int git_repository_is_worktree(git_repository *repo)
+int git_repository_is_worktree(const git_repository *repo)
{
assert(repo);
return repo->is_worktree;
diff --git a/src/submodule.c b/src/submodule.c
index 6c3e5f6bd..3ec0307b3 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -1960,7 +1960,7 @@ static git_config_backend *open_gitmodules(
if (git_config_file__ondisk(&mods, path.ptr) < 0)
mods = NULL;
/* open should only fail here if the file is malformed */
- else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+ else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
git_config_file_free(mods);
mods = NULL;
}
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index f8416b848..5e24260f7 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -18,11 +18,6 @@
#define PATH__ABSOLUTE_LEN 3
-#define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
-
-#define path__is_absolute(p) \
- (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
-
#define path__is_nt_namespace(p) \
(((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
@@ -59,7 +54,7 @@ static wchar_t *path__skip_server(wchar_t *path)
wchar_t *c;
for (c = path; *c; c++) {
- if (path__is_dirsep(*c))
+ if (git_path_is_dirsep(*c))
return c + 1;
}
@@ -73,9 +68,9 @@ static wchar_t *path__skip_prefix(wchar_t *path)
if (wcsncmp(path, L"UNC\\", 4) == 0)
path = path__skip_server(path + 4);
- else if (path__is_absolute(path))
+ else if (git_path_is_absolute(path))
path += PATH__ABSOLUTE_LEN;
- } else if (path__is_absolute(path)) {
+ } else if (git_path_is_absolute(path)) {
path += PATH__ABSOLUTE_LEN;
} else if (path__is_unc(path)) {
path = path__skip_server(path + 2);
@@ -196,7 +191,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
dest += PATH__NT_NAMESPACE_LEN;
/* See if this is an absolute path (beginning with a drive letter) */
- if (path__is_absolute(src)) {
+ if (git_path_is_absolute(src)) {
if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
goto on_error;
}
@@ -220,7 +215,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
if (path__cwd(dest, MAX_PATH) < 0)
goto on_error;
- if (!path__is_absolute(dest)) {
+ if (!git_path_is_absolute(dest)) {
errno = ENOENT;
goto on_error;
}
diff --git a/tests/config/backend.c b/tests/config/backend.c
index 3fd6eb114..18c4ca59e 100644
--- a/tests/config/backend.c
+++ b/tests/config/backend.c
@@ -10,13 +10,13 @@ void test_config_backend__checks_version(void)
backend.version = 1024;
cl_git_pass(git_config_new(&cfg));
- cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
err = giterr_last();
cl_assert_equal_i(GITERR_INVALID, err->klass);
giterr_clear();
backend.version = 1024;
- cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
err = giterr_last();
cl_assert_equal_i(GITERR_INVALID, err->klass);
diff --git a/tests/config/conditionals.c b/tests/config/conditionals.c
new file mode 100644
index 000000000..3a87de21f
--- /dev/null
+++ b/tests/config/conditionals.c
@@ -0,0 +1,103 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.h"
+
+#ifdef GIT_WIN32
+# define ROOT_PREFIX "C:"
+#else
+# define ROOT_PREFIX
+#endif
+
+static git_repository *_repo;
+
+void test_config_conditionals__initialize(void)
+{
+ _repo = cl_git_sandbox_init("empty_standard_repo");
+}
+
+void test_config_conditionals__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_condition_includes(const char *keyword, const char *path, bool expected)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path);
+ git_buf_puts(&buf, "path = other\n");
+
+ cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
+ cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
+ _repo = cl_git_sandbox_reopen();
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ if (expected) {
+ git_buf_clear(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", git_buf_cstr(&buf));
+ } else {
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ }
+
+ git_buf_free(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_conditionals__gitdir(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ char *sandbox_path;
+
+ assert_condition_includes("gitdir", ROOT_PREFIX "/", true);
+ assert_condition_includes("gitdir", "empty_standard_repo", true);
+ assert_condition_includes("gitdir", "empty_standard_repo/", true);
+ assert_condition_includes("gitdir", "./", true);
+
+ assert_condition_includes("gitdir", ROOT_PREFIX "/nonexistent", false);
+ assert_condition_includes("gitdir", ROOT_PREFIX "/empty_standard_repo", false);
+ assert_condition_includes("gitdir", "empty_stand", false);
+ assert_condition_includes("gitdir", "~/empty_standard_repo", false);
+
+ sandbox_path = p_realpath(clar_sandbox_path(), NULL);
+
+ git_buf_joinpath(&path, sandbox_path, "/");
+ assert_condition_includes("gitdir", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "/*");
+ assert_condition_includes("gitdir", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "empty_standard_repo");
+ assert_condition_includes("gitdir", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "Empty_Standard_Repo");
+ assert_condition_includes("gitdir", path.ptr, false);
+
+ git__free(sandbox_path);
+ git_buf_free(&path);
+}
+
+void test_config_conditionals__gitdir_i(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ char *sandbox_path;
+
+ sandbox_path = p_realpath(clar_sandbox_path(), NULL);
+
+ git_buf_joinpath(&path, sandbox_path, "empty_standard_repo");
+ assert_condition_includes("gitdir/i", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "EMPTY_STANDARD_REPO");
+ assert_condition_includes("gitdir/i", path.ptr, true);
+
+ git__free(sandbox_path);
+ git_buf_free(&path);
+}
+
+void test_config_conditionals__invalid_conditional_fails(void)
+{
+ assert_condition_includes("foobar", ".git", false);
+}
diff --git a/tests/config/configlevel.c b/tests/config/configlevel.c
index ca478b1a5..b73656cb9 100644
--- a/tests/config/configlevel.c
+++ b/tests/config/configlevel.c
@@ -7,11 +7,11 @@ void test_config_configlevel__adding_the_same_level_twice_returns_EEXISTS(void)
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_GLOBAL, 0);
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
cl_git_fail(error);
cl_assert_equal_i(GIT_EEXISTS, error);
@@ -26,9 +26,9 @@ void test_config_configlevel__can_replace_a_config_file_at_an_existing_level(voi
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
- GIT_CONFIG_LEVEL_LOCAL, 1));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_LOCAL, 1));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
cl_assert_equal_s("don't find me!", buf.ptr);
@@ -45,9 +45,9 @@ void test_config_configlevel__can_read_from_a_single_level_focused_file_after_pa
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
diff --git a/tests/config/multivar.c b/tests/config/multivar.c
index d1b8c4cda..4f08a4817 100644
--- a/tests/config/multivar.c
+++ b/tests/config/multivar.c
@@ -94,27 +94,27 @@ void test_config_multivar__get(void)
check_get_multivar_foreach(cfg, 2, 1);
/* add another that has the _name entry */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry at the end */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* drop original file */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
check_get_multivar_foreach(cfg, 1, 1);
/* drop other file with match */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 0, 0);
/* reload original file (add different place in order) */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 2, 1);
check_get_multivar(cfg, 2);
diff --git a/tests/config/read.c b/tests/config/read.c
index f86b2d79e..25a4fcaf4 100644
--- a/tests/config/read.c
+++ b/tests/config/read.c
@@ -289,9 +289,9 @@ void test_config_read__foreach(void)
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
count = 0;
cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
@@ -313,9 +313,9 @@ void test_config_read__iterator(void)
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
count = 0;
cl_git_pass(git_config_iterator_new(&iter, cfg));
@@ -445,7 +445,7 @@ void test_config_read__read_git_config_entry(void)
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
cl_assert_equal_s("core.dummy2", entry->name);
@@ -469,11 +469,11 @@ void test_config_read__local_config_overrides_global_config_overrides_system_con
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert_equal_i(28, i);
@@ -482,9 +482,9 @@ void test_config_read__local_config_overrides_global_config_overrides_system_con
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert_equal_i(7, i);
@@ -510,11 +510,11 @@ void test_config_read__fallback_from_local_to_global_and_from_global_to_system(v
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
cl_assert_equal_i(17, i);
@@ -546,9 +546,9 @@ void test_config_read__simple_read_from_specific_level(void)
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
diff --git a/tests/config/readonly.c b/tests/config/readonly.c
index 6d6819eef..a424922c1 100644
--- a/tests/config/readonly.c
+++ b/tests/config/readonly.c
@@ -22,7 +22,7 @@ void test_config_readonly__writing_to_readonly_fails(void)
cl_git_pass(git_config_file__ondisk(&backend, "global"));
backend->readonly = 1;
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(!git_path_exists("global"));
@@ -34,10 +34,10 @@ void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
cl_git_pass(git_config_file__ondisk(&backend, "global"));
backend->readonly = 1;
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_file__ondisk(&backend, "local"));
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
@@ -52,10 +52,10 @@ void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
cl_git_pass(git_config_file__ondisk(&backend, "local"));
backend->readonly = 1;
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_file__ondisk(&backend, "global"));
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
diff --git a/tests/config/write.c b/tests/config/write.c
index 01b018b12..6687ba1f7 100644
--- a/tests/config/write.c
+++ b/tests/config/write.c
@@ -93,9 +93,9 @@ void test_config_write__delete_value_at_specific_level(void)
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
@@ -368,9 +368,9 @@ void test_config_write__add_value_at_specific_level(void)
// open config15 as global level config file
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));