diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2017-07-19 13:55:55 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-19 13:55:55 -0700 |
commit | e0568621535869451c91e915d28a69ac7c84b8b7 (patch) | |
tree | fe5b2d43a4586c2840f127e0b13814309ba4215a | |
parent | a94a54021c118df5d0cf526c0ace4bd96a442f39 (diff) | |
parent | 1b32908973d687e4526079eeecdb82d9cb13c6e6 (diff) | |
download | libgit2-e0568621535869451c91e915d28a69ac7c84b8b7.tar.gz |
Merge pull request #4250 from pks-t/pks/config-file-iteration
Configuration file fixes with includes
-rw-r--r-- | src/config_file.c | 285 | ||||
-rw-r--r-- | tests/config/include.c | 97 |
2 files changed, 209 insertions, 173 deletions
diff --git a/src/config_file.c b/src/config_file.c index e15d57bbb..8ac89b3a6 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -74,9 +74,14 @@ typedef struct git_config_file_iter { (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\ (iter) = (tmp)) -struct reader { +struct config_file { git_oid checksum; - char *file_path; + char *path; + git_array_t(struct config_file) includes; +}; + +struct reader { + struct config_file *file; git_buf buffer; char *read_ptr; int line_number; @@ -100,13 +105,11 @@ typedef struct { git_config_level_t level; - git_array_t(struct reader) readers; - bool locked; git_filebuf locked_buf; git_buf locked_content; - char *file_path; + struct config_file file; } diskfile_backend; typedef struct { @@ -115,7 +118,7 @@ typedef struct { diskfile_backend *snapshot_from; } diskfile_readonly_backend; -static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth); +static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth); static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value); static char *escape_value(const char *ptr); @@ -125,7 +128,7 @@ static int config_snapshot(git_config_backend **out, git_config_backend *in); static void set_parse_error(struct reader *reader, int col, const char *error_str) { giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)", - error_str, reader->file_path, reader->line_number, col); + error_str, reader->file->path, reader->line_number, col); } static int config_error_readonly(void) @@ -261,10 +264,25 @@ static int refcounted_strmap_alloc(refcounted_strmap **out) return error; } +static void config_file_clear(struct config_file *file) +{ + struct config_file *include; + uint32_t i; + + if (file == NULL) + return; + + git_array_foreach(file->includes, i, include) { + config_file_clear(include); + } + git_array_clear(file->includes); + + git__free(file->path); +} + static int config_open(git_config_backend *cfg, git_config_level_t level) { int res; - struct reader *reader; diskfile_backend *b = (diskfile_backend *)cfg; b->level = level; @@ -272,112 +290,102 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) if ((res = refcounted_strmap_alloc(&b->header.values)) < 0) return res; - git_array_init(b->readers); - reader = git_array_alloc(b->readers); - if (!reader) { - refcounted_strmap_free(b->header.values); - return -1; - } - memset(reader, 0, sizeof(struct reader)); - - reader->file_path = git__strdup(b->file_path); - GITERR_CHECK_ALLOC(reader->file_path); - - git_buf_init(&reader->buffer, 0); - res = git_futils_readbuffer_updated( - &reader->buffer, b->file_path, &reader->checksum, NULL); - /* It's fine if the file doesn't exist */ - if (res == GIT_ENOTFOUND) + if (!git_path_exists(b->file.path)) return 0; - if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) { + if (res < 0 || (res = config_read(b->header.values->values, &b->file, level, 0)) < 0) { refcounted_strmap_free(b->header.values); b->header.values = NULL; } - reader = git_array_get(b->readers, 0); - git_buf_free(&reader->buffer); - return res; } -/* The meat of the refresh, as we want to use it in different places */ -static int config__refresh(git_config_backend *cfg) +static int config_is_modified(int *modified, struct config_file *file) { - refcounted_strmap *values = NULL, *tmp; - diskfile_backend *b = (diskfile_backend *)cfg; - struct reader *reader = NULL; - int error = 0; + struct config_file *include; + git_buf buf = GIT_BUF_INIT; + git_oid hash; + uint32_t i; + int error; - if ((error = refcounted_strmap_alloc(&values)) < 0) - goto out; + *modified = 0; - reader = git_array_get(b->readers, git_array_size(b->readers) - 1); - GITERR_CHECK_ALLOC(reader); + if ((error = git_futils_readbuffer(&buf, file->path)) < 0) + goto out; - if ((error = config_read(values->values, b, reader, b->level, 0)) < 0) + if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0) goto out; - if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) { - giterr_set(GITERR_OS, "failed to lock config backend"); + if (!git_oid_equal(&hash, &file->checksum)) { + *modified = 1; goto out; } - tmp = b->header.values; - b->header.values = values; - values = tmp; - - git_mutex_unlock(&b->header.values_mutex); + git_array_foreach(file->includes, i, include) { + if ((error = config_is_modified(modified, include)) < 0 || *modified) + goto out; + } out: - refcounted_strmap_free(values); - if (reader) - git_buf_free(&reader->buffer); + git_buf_free(&buf); + return error; } static int config_refresh(git_config_backend *cfg) { - int error = 0, updated = 0, any_updated = 0; diskfile_backend *b = (diskfile_backend *)cfg; - struct reader *reader = NULL; + refcounted_strmap *values = NULL, *tmp; + struct config_file *include; + int error, modified; uint32_t i; - for (i = 0; i < git_array_size(b->readers); i++) { - reader = git_array_get(b->readers, i); - error = git_futils_readbuffer_updated( - &reader->buffer, reader->file_path, - &reader->checksum, &updated); + error = config_is_modified(&modified, &b->file); + if (error < 0 && error != GIT_ENOTFOUND) + goto out; - if (error < 0 && error != GIT_ENOTFOUND) - return error; + if (!modified) + return 0; - if (updated) - any_updated = 1; + if ((error = refcounted_strmap_alloc(&values)) < 0) + goto out; + + /* Reparse the current configuration */ + git_array_foreach(b->file.includes, i, include) { + config_file_clear(include); + } + git_array_clear(b->file.includes); + + if ((error = config_read(values->values, &b->file, b->level, 0)) < 0) + goto out; + + if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) { + giterr_set(GITERR_OS, "failed to lock config backend"); + goto out; } - if (!any_updated) - return (error == GIT_ENOTFOUND) ? 0 : error; + tmp = b->header.values; + b->header.values = values; + values = tmp; - return config__refresh(cfg); + git_mutex_unlock(&b->header.values_mutex); + +out: + refcounted_strmap_free(values); + + return (error == GIT_ENOTFOUND) ? 0 : error; } static void backend_free(git_config_backend *_backend) { diskfile_backend *backend = (diskfile_backend *)_backend; - uint32_t i; if (backend == NULL) return; - for (i = 0; i < git_array_size(backend->readers); i++) { - struct reader *r = git_array_get(backend->readers, i); - git__free(r->file_path); - } - git_array_clear(backend->readers); - - git__free(backend->file_path); + config_file_clear(&backend->file); refcounted_strmap_free(backend->header.values); git_mutex_free(&backend->header.values_mutex); git__free(backend); @@ -482,6 +490,12 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val goto out; } + if (existing->included) { + giterr_set(GITERR_CONFIG, "modifying included variable is not supported"); + ret = -1; + goto out; + } + /* don't update if old and new values already match */ if ((!existing->entry->value && !value) || (existing->entry->value && value && @@ -616,6 +630,11 @@ static int config_delete(git_config_backend *cfg, const char *name) var = git_strmap_value_at(values, pos); refcounted_strmap_free(map); + if (var->included) { + giterr_set(GITERR_CONFIG, "cannot delete included variable"); + return -1; + } + if (var->next != NULL) { giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete"); return -1; @@ -685,10 +704,10 @@ static int config_lock(git_config_backend *_cfg) diskfile_backend *cfg = (diskfile_backend *) _cfg; int error; - if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) + if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0) return error; - error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path); + error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path); if (error < 0 && error != GIT_ENOTFOUND) { git_filebuf_cleanup(&cfg->locked_buf); return error; @@ -726,8 +745,9 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; git_mutex_init(&backend->header.values_mutex); - backend->file_path = git__strdup(path); - GITERR_CHECK_ALLOC(backend->file_path); + backend->file.path = git__strdup(path); + GITERR_CHECK_ALLOC(backend->file.path); + git_array_init(backend->file.includes); backend->header.parent.open = config_open; backend->header.parent.get = config_get; @@ -1486,10 +1506,10 @@ on_error: static int config_parse( struct reader *reader, - int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data), - int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data), - int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data), - int (*on_eof)(struct reader **reader, const char *current_section, void *data), + int (*on_section)(struct reader *reader, const char *current_section, const char *line, size_t line_len, void *data), + int (*on_variable)(struct reader *reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data), + int (*on_comment)(struct reader *reader, const char *line, size_t line_len, void *data), + int (*on_eof)(struct reader *reader, const char *current_section, void *data), void *data) { char *current_section = NULL, *var_name, *var_value, *line_start; @@ -1515,7 +1535,7 @@ static int config_parse( if ((result = parse_section_header(reader, ¤t_section)) == 0 && on_section) { line_len = reader->read_ptr - line_start; - result = on_section(&reader, current_section, line_start, line_len, data); + result = on_section(reader, current_section, line_start, line_len, data); } break; @@ -1526,21 +1546,21 @@ static int config_parse( if (on_comment) { line_len = reader->read_ptr - line_start; - result = on_comment(&reader, line_start, line_len, data); + result = on_comment(reader, line_start, line_len, data); } break; default: /* assume variable declaration */ if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) { line_len = reader->read_ptr - line_start; - result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data); + result = on_variable(reader, current_section, var_name, var_value, line_start, line_len, data); } break; } } if (on_eof) - result = on_eof(&reader, current_section, data); + result = on_eof(reader, current_section, data); git__free(current_section); return result; @@ -1548,14 +1568,12 @@ static int config_parse( struct parse_data { git_strmap *values; - diskfile_backend *cfg_file; - uint32_t reader_idx; git_config_level_t level; int depth; }; static int read_on_variable( - struct reader **reader, + struct reader *reader, const char *current_section, char *var_name, char *var_value, @@ -1597,21 +1615,13 @@ static int read_on_variable( /* Add or append the new config option */ if (!git__strcmp(var->entry->name, "include.path")) { - struct reader *r; + struct config_file *include; git_buf path = GIT_BUF_INIT; char *dir; - uint32_t index; - - r = git_array_alloc(parse_data->cfg_file->readers); - /* The reader may have been reallocated */ - *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx); - memset(r, 0, sizeof(struct reader)); - if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0) + if ((result = git_path_dirname_r(&path, reader->file->path)) < 0) return result; - /* We need to know our index in the array, as the next config_parse call may realloc */ - index = git_array_size(parse_data->cfg_file->readers) - 1; dir = git_buf_detach(&path); result = included_path(&path, dir, var->entry->value); git__free(dir); @@ -1619,51 +1629,60 @@ static int read_on_variable( if (result < 0) return result; - r->file_path = git_buf_detach(&path); - git_buf_init(&r->buffer, 0); + include = git_array_alloc(reader->file->includes); + memset(include, 0, sizeof(*include)); + git_array_init(include->includes); + include->path = git_buf_detach(&path); - result = git_futils_readbuffer_updated( - &r->buffer, r->file_path, &r->checksum, NULL); + result = config_read(parse_data->values, include, parse_data->level, parse_data->depth+1); - if (result == 0) { - result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1); - r = git_array_get(parse_data->cfg_file->readers, index); - *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx); - } else if (result == GIT_ENOTFOUND) { + if (result == GIT_ENOTFOUND) { giterr_clear(); result = 0; } - - git_buf_free(&r->buffer); } return result; } -static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth) +static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth) { struct parse_data parse_data; + struct reader reader; + int error; if (depth >= MAX_INCLUDE_DEPTH) { giterr_set(GITERR_CONFIG, "maximum config include depth reached"); return -1; } + git_buf_init(&reader.buffer, 0); + + if ((error = git_futils_readbuffer(&reader.buffer, file->path)) < 0) + goto out; + + if ((error = git_hash_buf(&file->checksum, reader.buffer.ptr, reader.buffer.size)) < 0) + goto out; + /* Initialize the reading position */ - reader->read_ptr = reader->buffer.ptr; - reader->eof = 0; + reader.file = file; + reader.line_number = 0; + reader.read_ptr = reader.buffer.ptr; + reader.eof = 0; /* If the file is empty, there's nothing for us to do */ - if (*reader->read_ptr == '\0') - return 0; + if (*reader.read_ptr == '\0') + goto out; parse_data.values = values; - parse_data.cfg_file = cfg_file; - parse_data.reader_idx = git_array_size(cfg_file->readers) - 1; parse_data.level = level; parse_data.depth = depth; - return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data); + error = config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data); + +out: + git_buf_free(&reader.buffer); + return error; } static int write_section(git_buf *fbuf, const char *key) @@ -1760,7 +1779,7 @@ static int write_value(struct write_data *write_data) } static int write_on_section( - struct reader **reader, + struct reader *reader, const char *current_section, const char *line, size_t line_len, @@ -1796,7 +1815,7 @@ static int write_on_section( } static int write_on_variable( - struct reader **reader, + struct reader *reader, const char *current_section, char *var_name, char *var_value, @@ -1846,7 +1865,7 @@ static int write_on_variable( return write_value(write_data); } -static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data) +static int write_on_comment(struct reader *reader, const char *line, size_t line_len, void *data) { struct write_data *write_data; @@ -1857,7 +1876,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin } static int write_on_eof( - struct reader **reader, const char *current_section, void *data) + struct reader *reader, const char *current_section, void *data) { struct write_data *write_data = (struct write_data *)data; int result = 0; @@ -1896,31 +1915,35 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p char *section, *name, *ldot; git_filebuf file = GIT_FILEBUF_INIT; git_buf buf = GIT_BUF_INIT; - struct reader *reader = git_array_get(cfg->readers, 0); + struct reader reader; struct write_data write_data; + memset(&reader, 0, sizeof(reader)); + git_buf_init(&reader.buffer, 0); + reader.file = &cfg->file; + if (cfg->locked) { - result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content)); + result = git_buf_puts(&reader.buffer, git_buf_cstr(&cfg->locked_content)); } else { /* Lock the file */ if ((result = git_filebuf_open( - &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) { - git_buf_free(&reader->buffer); + &file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) { + git_buf_free(&reader.buffer); return result; } /* We need to read in our own config file */ - result = git_futils_readbuffer(&reader->buffer, cfg->file_path); + result = git_futils_readbuffer(&reader.buffer, cfg->file.path); } /* Initialise the reading position */ if (result == GIT_ENOTFOUND) { - reader->read_ptr = NULL; - reader->eof = 1; - git_buf_clear(&reader->buffer); + reader.read_ptr = NULL; + reader.eof = 1; + git_buf_clear(&reader.buffer); } else if (result == 0) { - reader->read_ptr = reader->buffer.ptr; - reader->eof = 0; + reader.read_ptr = reader.buffer.ptr; + reader.eof = 0; } else { git_filebuf_cleanup(&file); return -1; /* OS error when reading the file */ @@ -1939,7 +1962,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p write_data.preg = preg; write_data.value = value; - result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data); + result = config_parse(&reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data); git__free(section); git_buf_free(&write_data.buffered_comment); @@ -1960,6 +1983,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p done: git_buf_free(&buf); - git_buf_free(&reader->buffer); + git_buf_free(&reader.buffer); return result; } diff --git a/tests/config/include.c b/tests/config/include.c index 0a07c9b85..e440b9a78 100644 --- a/tests/config/include.c +++ b/tests/config/include.c @@ -2,25 +2,31 @@ #include "buffer.h" #include "fileops.h" -void test_config_include__relative(void) +static git_config *cfg; +static git_buf buf; + +void test_config_include__initialize(void) +{ + cfg = NULL; + git_buf_init(&buf, 0); +} + +void test_config_include__cleanup(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; + git_config_free(cfg); + git_buf_free(&buf); +} +void test_config_include__relative(void) +{ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include"))); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); - - git_buf_free(&buf); - git_config_free(cfg); } void test_config_include__absolute(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; - cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config"))); cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf)); @@ -29,16 +35,10 @@ void test_config_include__absolute(void) cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); - - git_buf_free(&buf); - git_config_free(cfg); } void test_config_include__homedir(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; - cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config"))); cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included"); @@ -47,18 +47,12 @@ void test_config_include__homedir(void) cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); - git_buf_free(&buf); - git_config_free(cfg); - cl_sandbox_set_search_path_defaults(); } /* We need to pretend that the variables were defined where the file was included */ void test_config_include__ordering(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; - cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya"); cl_git_mkfile("including", "[foo \"bar\"]\nfrotz = hello\n" @@ -72,16 +66,11 @@ void test_config_include__ordering(void) git_buf_clear(&buf); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); - - git_buf_free(&buf); - git_config_free(cfg); } /* We need to pretend that the variables were defined where the file was included */ void test_config_include__depth(void) { - git_config *cfg; - cl_git_mkfile("a", "[include]\npath = b"); cl_git_mkfile("b", "[include]\npath = a"); @@ -93,9 +82,6 @@ void test_config_include__depth(void) void test_config_include__missing(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; - cl_git_mkfile("including", "[include]\npath = nonexistentfile\n[foo]\nbar = baz"); giterr_clear(); @@ -103,16 +89,10 @@ void test_config_include__missing(void) cl_assert(giterr_last() == NULL); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_assert_equal_s("baz", git_buf_cstr(&buf)); - - git_buf_free(&buf); - git_config_free(cfg); } void test_config_include__missing_homedir(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; - cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config"))); cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz"); @@ -122,17 +102,12 @@ void test_config_include__missing_homedir(void) cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_assert_equal_s("baz", git_buf_cstr(&buf)); - git_buf_free(&buf); - git_config_free(cfg); - cl_sandbox_set_search_path_defaults(); } #define replicate10(s) s s s s s s s s s s void test_config_include__depth2(void) { - git_config *cfg; - git_buf buf = GIT_BUF_INIT; const char *content = "[include]\n" replicate10(replicate10("path=bottom\n")); cl_git_mkfile("top-level", "[include]\npath = middle\n[foo]\nbar = baz"); @@ -147,7 +122,45 @@ void test_config_include__depth2(void) git_buf_clear(&buf); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2")); cl_assert_equal_s("baz2", git_buf_cstr(&buf)); +} - git_buf_free(&buf); - git_config_free(cfg); +void test_config_include__removing_include_removes_values(void) +{ + cl_git_mkfile("top-level", "[include]\npath = included"); + cl_git_mkfile("included", "[foo]\nbar = value"); + + cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); + cl_git_mkfile("top-level", ""); + cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar")); +} + +void test_config_include__rewriting_include_refreshes_values(void) +{ + cl_git_mkfile("top-level", "[include]\npath = first\n[include]\npath = second"); + cl_git_mkfile("first", "[first]\nfoo = bar"); + cl_git_mkfile("second", "[second]\nfoo = bar"); + + cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); + cl_git_mkfile("first", "[first]\nother = value"); + cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar")); + cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other")); + cl_assert_equal_s(buf.ptr, "value"); +} + +void test_config_include__included_variables_cannot_be_deleted(void) +{ + cl_git_mkfile("top-level", "[include]\npath = included\n"); + cl_git_mkfile("included", "[foo]\nbar = value"); + + cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); + cl_git_fail(git_config_delete_entry(cfg, "foo.bar")); +} + +void test_config_include__included_variables_cannot_be_modified(void) +{ + cl_git_mkfile("top-level", "[include]\npath = included\n"); + cl_git_mkfile("included", "[foo]\nbar = value"); + + cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); + cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value")); } |