From 8c7c5fa585c6a63dc8186febd6e032880655e85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 20 Oct 2015 17:42:42 +0200 Subject: [PATCH] config: add a ProgramData level This is where portable git stores the global configuration which we can use to adhere to it even though git isn't quite installed on the system. --- include/git2/config.h | 24 +++++++++++++++++++----- src/config.c | 10 ++++++++++ src/config.h | 1 + src/repository.c | 14 ++++++++++++-- src/sysdir.c | 19 ++++++++++++++++++- src/sysdir.h | 14 ++++++++++++-- src/win32/findfile.c | 10 ++++++++++ src/win32/findfile.h | 1 + tests/config/global.c | 40 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 123 insertions(+), 10 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 56b5431ac..d0f1ba1b3 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -29,25 +29,28 @@ GIT_BEGIN_DECL * priority levels as well. */ typedef enum { + /** System-wide on Windows, for compatibility with portable git */ + GIT_CONFIG_LEVEL_PROGRAMDATA = 1, + /** System-wide configuration file; /etc/gitconfig on Linux systems */ - GIT_CONFIG_LEVEL_SYSTEM = 1, + GIT_CONFIG_LEVEL_SYSTEM = 2, /** XDG compatible configuration file; typically ~/.config/git/config */ - GIT_CONFIG_LEVEL_XDG = 2, + GIT_CONFIG_LEVEL_XDG = 3, /** User-specific configuration file (also called Global configuration * file); typically ~/.gitconfig */ - GIT_CONFIG_LEVEL_GLOBAL = 3, + GIT_CONFIG_LEVEL_GLOBAL = 4, /** Repository specific configuration file; $WORK_DIR/.git/config on * non-bare repos */ - GIT_CONFIG_LEVEL_LOCAL = 4, + GIT_CONFIG_LEVEL_LOCAL = 5, /** Application specific configuration file; freely defined by applications */ - GIT_CONFIG_LEVEL_APP = 5, + GIT_CONFIG_LEVEL_APP = 6, /** Represents the highest level available config file (i.e. the most * specific config file available that actually is loaded) @@ -141,6 +144,17 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out); */ GIT_EXTERN(int) git_config_find_system(git_buf *out); +/** + * Locate the path to the configuration file in ProgramData + * + * Look for the file in %PROGRAMDATA%\Git\config used by portable git. + * + * @param out Pointer to a user-allocated git_buf in which to store the path + * @return 0 if a ProgramData configuration file has been + * found. Its path will be stored in `out`. + */ +GIT_EXTERN(int) git_config_find_programdata(git_buf *out); + /** * Open the global, XDG and system configuration files * diff --git a/src/config.c b/src/config.c index f0b2c3a61..f4d4cb2b9 100644 --- a/src/config.c +++ b/src/config.c @@ -1086,6 +1086,12 @@ int git_config_find_system(git_buf *path) return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); } +int git_config_find_programdata(git_buf *path) +{ + git_buf_sanitize(path); + return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); +} + int git_config__global_location(git_buf *buf) { const git_buf *paths; @@ -1133,6 +1139,10 @@ int git_config_open_default(git_config **out) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_SYSTEM, 0); + if (!error && !git_config_find_programdata(&buf)) + error = git_config_add_file_ondisk(cfg, buf.ptr, + GIT_CONFIG_LEVEL_PROGRAMDATA, 0); + git_buf_free(&buf); if (error) { diff --git a/src/config.h b/src/config.h index ba745331a..00c12b50d 100644 --- a/src/config.h +++ b/src/config.h @@ -12,6 +12,7 @@ #include "vector.h" #include "repository.h" +#define GIT_CONFIG_FILENAME_PROGRAMDATA "config" #define GIT_CONFIG_FILENAME_SYSTEM "gitconfig" #define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig" #define GIT_CONFIG_FILENAME_XDG "config" diff --git a/src/repository.c b/src/repository.c index 77145cfc8..38d18693a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -585,7 +585,8 @@ static int load_config( git_repository *repo, const char *global_config_path, const char *xdg_config_path, - const char *system_config_path) + const char *system_config_path, + const char *programdata_path) { int error; git_buf config_path = GIT_BUF_INIT; @@ -626,6 +627,12 @@ static int load_config( 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 && + error != GIT_ENOTFOUND) + goto on_error; + giterr_clear(); /* clear any lingering ENOTFOUND errors */ *out = cfg; @@ -651,11 +658,13 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_buf global_buf = GIT_BUF_INIT; git_buf xdg_buf = GIT_BUF_INIT; git_buf system_buf = GIT_BUF_INIT; + git_buf programdata_buf = GIT_BUF_INIT; git_config *config; git_config_find_global(&global_buf); git_config_find_xdg(&xdg_buf); git_config_find_system(&system_buf); + git_config_find_programdata(&programdata_buf); /* If there is no global file, open a backend for it anyway */ if (git_buf_len(&global_buf) == 0) @@ -665,7 +674,8 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) &config, repo, path_unless_empty(&global_buf), path_unless_empty(&xdg_buf), - path_unless_empty(&system_buf)); + path_unless_empty(&system_buf), + path_unless_empty(&programdata_buf)); if (!error) { GIT_REFCOUNT_OWN(config, repo); diff --git a/src/sysdir.c b/src/sysdir.c index 2795de491..bf53d830f 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -15,6 +15,16 @@ #include "win32/findfile.h" #endif +static int git_sysdir_guess_programdata_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_programdata_dirs(out); +#else + git_buf_clear(out); + return 0; +#endif +} + static int git_sysdir_guess_system_dirs(git_buf *out) { #ifdef GIT_WIN32 @@ -76,12 +86,13 @@ static int git_sysdir_guess_template_dirs(git_buf *out) typedef int (*git_sysdir_guess_cb)(git_buf *out); static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] = - { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; + { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = { git_sysdir_guess_system_dirs, git_sysdir_guess_global_dirs, git_sysdir_guess_xdg_dirs, + git_sysdir_guess_programdata_dirs, git_sysdir_guess_template_dirs, }; @@ -258,6 +269,12 @@ int git_sysdir_find_xdg_file(git_buf *path, const char *filename) path, filename, GIT_SYSDIR_XDG, "global/xdg"); } +int git_sysdir_find_programdata_file(git_buf *path, const char *filename) +{ + return git_sysdir_find_in_dirlist( + path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData"); +} + int git_sysdir_find_template_dir(git_buf *path) { return git_sysdir_find_in_dirlist( diff --git a/src/sysdir.h b/src/sysdir.h index f1bbf0bae..12874fc85 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -38,6 +38,15 @@ extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename); */ extern int git_sysdir_find_system_file(git_buf *path, const char *filename); +/** + * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%) + * + * @param path buffer to write the full path into + * @param filename name of file to find in the ProgramData directory + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename); + /** * Find template directory. * @@ -50,8 +59,9 @@ typedef enum { GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_GLOBAL = 1, GIT_SYSDIR_XDG = 2, - GIT_SYSDIR_TEMPLATE = 3, - GIT_SYSDIR__MAX = 4, + GIT_SYSDIR_PROGRAMDATA = 3, + GIT_SYSDIR_TEMPLATE = 4, + GIT_SYSDIR__MAX = 5, } git_sysdir_t; /** diff --git a/src/win32/findfile.c b/src/win32/findfile.c index de27dd060..58c22279e 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -215,3 +215,13 @@ int git_win32__find_xdg_dirs(git_buf *out) return win32_find_existing_dirs(out, global_tmpls); } + +int git_win32__find_programdata_dirs(git_buf *out) +{ + static const wchar_t *programdata_tmpls[2] = { + L"%PROGRAMDATA%\\Git", + NULL, + }; + + return win32_find_existing_dirs(out, programdata_tmpls); +} diff --git a/src/win32/findfile.h b/src/win32/findfile.h index a50319b9a..3d5fff439 100644 --- a/src/win32/findfile.h +++ b/src/win32/findfile.h @@ -11,6 +11,7 @@ extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath); extern int git_win32__find_global_dirs(git_buf *out); extern int git_win32__find_xdg_dirs(git_buf *out); +extern int git_win32__find_programdata_dirs(git_buf *out); #endif diff --git a/tests/config/global.c b/tests/config/global.c index b5e83fec0..1336ef6e7 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -65,3 +65,43 @@ void test_config_global__open_xdg(void) git_config_free(xdg); git_config_free(cfg); } + +void test_config_global__open_programdata(void) +{ + char *programdata; + git_config *cfg; + git_repository *repo; + git_buf config_path = GIT_BUF_INIT; + git_buf var_contents = GIT_BUF_INIT; + + if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE")) + cl_skip(); + + programdata = cl_getenv("PROGRAMDATA"); + cl_git_pass(git_buf_printf(&config_path, "%s/Git", programdata)); + cl_git_pass(p_mkdir(config_path.ptr, 0777)); + cl_git_pass(git_buf_puts(&config_path, "/config")); + + cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr)); + cl_git_pass(git_config_set_string(cfg, "programdata.var", "even higher level")); + + git_buf_free(&config_path); + git_config_free(cfg); + + git_config_open_default(&cfg); + cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var")); + cl_assert_equal_s("even higher level", var_contents.ptr); + + git_config_free(cfg); + git_buf_free(&var_contents); + + cl_git_pass(git_repository_init(&repo, "./foo.git", true)); + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var")); + cl_assert_equal_s("even higher level", var_contents.ptr); + + git_config_free(cfg); + git_buf_free(&var_contents); + git_repository_free(repo); + cl_fixture_cleanup("./foo.git"); +}