From 4977620ac1f515a2122c512e84803311763237bf Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Sat, 23 May 2015 02:19:50 +0200 Subject: [PATCH 1/4] config: factor out repeated code Factor out near identical per-file logic. Signed-off-by: Karsten Blees Signed-off-by: Johannes Schindelin --- config.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/config.c b/config.c index c47ecc79e4..70b1e00489 100644 --- a/config.c +++ b/config.c @@ -1205,6 +1205,15 @@ int git_config_system(void) return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); } +static inline void config_from_file_gently(config_fn_t fn, const char *filename, + void *data, unsigned access_flags, int *ret, int *found) { + if (!filename || access_or_die(filename, R_OK, access_flags)) + return; + + *ret += git_config_from_file(fn, filename, data); + (*found)++; +} + static int do_git_config_sequence(config_fn_t fn, void *data) { int ret = 0, found = 0; @@ -1212,26 +1221,16 @@ static int do_git_config_sequence(config_fn_t fn, void *data) char *user_config = expand_user_path("~/.gitconfig"); char *repo_config = git_pathdup("config"); - if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) { - ret += git_config_from_file(fn, git_etc_gitconfig(), - data); - found += 1; - } + if (git_config_system()) + config_from_file_gently(fn, git_etc_gitconfig(), data, 0, + &ret, &found); - if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) { - ret += git_config_from_file(fn, xdg_config, data); - found += 1; - } + config_from_file_gently(fn, xdg_config, data, ACCESS_EACCES_OK, + &ret, &found); + config_from_file_gently(fn, user_config, data, ACCESS_EACCES_OK, + &ret, &found); - if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) { - ret += git_config_from_file(fn, user_config, data); - found += 1; - } - - if (repo_config && !access_or_die(repo_config, R_OK, 0)) { - ret += git_config_from_file(fn, repo_config, data); - found += 1; - } + config_from_file_gently(fn, repo_config, data, 0, &ret, &found); switch (git_config_from_parameters(fn, data)) { case -1: /* error */ From bba2ba479bf810185ac81949f372d6a550c74876 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 22 May 2015 15:20:21 +0200 Subject: [PATCH 2/4] config.c: create missing parent directories when modifying config files 'git config' (--add / --unset etc.) automatically creates missing config files. However, it fails with a misleading error message "could not lock config file" if the parent directory doesn't exist. Also create missing parent directories. This is particularly important when calling git config -f /non/existing/directory/config ... We *must not* create the leading directories when locking a config file. It is the caller's responsibility to ensure that the directory exists, just like it is the caller's responsibility to call `git init` before running repository operations. Point in case: if we simply create all leading directories, calling `git config user.name me` *outside* of a Git worktree will *create* .git/! This fixes https://github.com/git-for-windows/git/issues/643 and https://groups.google.com/forum/#!topic/git-for-windows/fVRdnDIKVuw [jes: prevented bogus .git/ directories from being created] Signed-off-by: Karsten Blees Signed-off-by: Johannes Schindelin --- config.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index 70b1e00489..165b6cfcbd 100644 --- a/config.c +++ b/config.c @@ -1971,6 +1971,20 @@ int git_config_key_is_valid(const char *key) return !git_config_parse_key_1(key, NULL, NULL, 1); } +static int lock_config_file(const char *config_filename, + struct lock_file **result) +{ + int fd; + + *result = xcalloc(1, sizeof(struct lock_file)); + fd = hold_lock_file_for_update(*result, config_filename, 0); + if (fd < 0) + error("could not lock config file %s: %s", config_filename, + strerror(errno)); + + return fd; +} + /* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. @@ -2022,8 +2036,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ - lock = xcalloc(1, sizeof(struct lock_file)); - fd = hold_lock_file_for_update(lock, config_filename, 0); + fd = lock_config_file(config_filename, &lock); if (fd < 0) { error_errno("could not lock config file %s", config_filename); free(store.key); @@ -2324,12 +2337,9 @@ int git_config_rename_section_in_file(const char *config_filename, if (!config_filename) config_filename = filename_buf = git_pathdup("config"); - lock = xcalloc(1, sizeof(struct lock_file)); - out_fd = hold_lock_file_for_update(lock, config_filename, 0); - if (out_fd < 0) { - ret = error("could not lock config file %s", config_filename); + out_fd = lock_config_file(config_filename, &lock); + if (out_fd < 0) goto out; - } if (!(config_file = fopen(config_filename, "rb"))) { /* no config file means nothing to rename, no error */ From de35446bbe621e075fed1e520ef34301ab51a07f Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 22 Apr 2015 14:47:27 +0100 Subject: [PATCH 3/4] Windows: add support for a Windows-wide configuration Between the libgit2 and the Git for Windows project, there has been a discussion how we could share Git configuration to avoid duplication (or worse: skew). Earlier, libgit2 was nice enough to just re-use Git for Windows' C:\Program Files (x86)\Git\etc\gitconfig but with the upcoming Git for Windows 2.x, there would be more paths to search, as we will have 64-bit and 32-bit versions, and the corresponding config files will be in %PROGRAMFILES%\Git\mingw64\etc and ...\mingw32\etc, respectively. Worse: there are portable Git for Windows versions out there which live in totally unrelated directories, still. Therefore we came to a consensus to use `%PROGRAMDATA%\Git\config` as the location for shared Git settings that are of wider interest than just Git for Windows. On XP, there is no %PROGRAMDATA%, therefore we need to use "%ALLUSERSPROFILE%\Application Data\Git\config" in those setups. Of course, the configuration in `%PROGRAMDATA%\Git\config` has the widest reach, therefore it must take the lowest precedence, i.e. Git for Windows can still override settings in its `etc/gitconfig` file. Signed-off-by: Johannes Schindelin --- Documentation/config.txt | 4 +++- Documentation/git-config.txt | 5 +++++ Documentation/git.txt | 3 ++- compat/mingw.c | 19 +++++++++++++++++++ compat/mingw.h | 2 ++ config.c | 5 ++++- git-compat-util.h | 4 ++++ 7 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 3863140e05..9fad0c44ff 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -6,7 +6,9 @@ the Git commands' behavior. The `.git/config` file in each repository is used to store the configuration for that repository, and `$HOME/.gitconfig` is used to store a per-user configuration as fallback values for the `.git/config` file. The file `/etc/gitconfig` -can be used to store a system-wide default configuration. +can be used to store a system-wide default configuration. On Windows, +configuration can also be stored in `C:\ProgramData\Git\config`; This +file will be used also by libgit2-based software. The configuration variables are used by both the Git plumbing and the porcelains. The variables are divided into sections, wherein diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index f163113a6f..e07ace4c11 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -253,6 +253,11 @@ $XDG_CONFIG_HOME/git/config:: $GIT_DIR/config:: Repository specific configuration file. +On Windows, as there is no central `/etc/` directory, there is yet another +config file, intended to contain settings for *all* Git-related software +running on the machine. Consequently, this config file takes an even lower +precedence than the `$(prefix)/etc/gitconfig` file. + If no further options are given, all reading options will read all of these files that are available. If the global or the system-wide configuration file are not available they will be ignored. If the repository configuration diff --git a/Documentation/git.txt b/Documentation/git.txt index ff25701d4e..5501b71fdd 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -1007,7 +1007,8 @@ for further details. `GIT_CONFIG_NOSYSTEM`:: Whether to skip reading settings from the system-wide - `$(prefix)/etc/gitconfig` file. This environment variable can + `$(prefix)/etc/gitconfig` file (and on Windows, also from the + `%PROGRAMDATA%\Git\config` file). This environment variable can be used along with `$HOME` and `$XDG_CONFIG_HOME` to create a predictable environment for a picky script, or you can set it temporarily to avoid using a buggy `/etc/gitconfig` file while diff --git a/compat/mingw.c b/compat/mingw.c index 45ca23f244..8337f6fd56 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2455,3 +2455,22 @@ int uname(struct utsname *buf) "%u", (v >> 16) & 0x7fff); return 0; } + +const char *program_data_config(void) +{ + static struct strbuf path = STRBUF_INIT; + static unsigned initialized; + + if (!initialized) { + const char *env = mingw_getenv("PROGRAMDATA"); + const char *extra = ""; + if (!env) { + env = mingw_getenv("ALLUSERSPROFILE"); + extra = "/Application Data"; + } + if (env) + strbuf_addf(&path, "%s%s/Git/config", env, extra); + initialized = 1; + } + return *path.buf ? path.buf : NULL; +} diff --git a/compat/mingw.h b/compat/mingw.h index e7b16a8c3c..6924238d8c 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -430,6 +430,8 @@ static inline void convert_slashes(char *path) int mingw_offset_1st_component(const char *path); #define offset_1st_component mingw_offset_1st_component #define PATH_SEP ';' +extern const char *program_data_config(void); +#define git_program_data_config program_data_config #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800) #define PRIuMAX "I64u" #define PRId64 "I64d" diff --git a/config.c b/config.c index 165b6cfcbd..fff0766076 100644 --- a/config.c +++ b/config.c @@ -1221,9 +1221,12 @@ static int do_git_config_sequence(config_fn_t fn, void *data) char *user_config = expand_user_path("~/.gitconfig"); char *repo_config = git_pathdup("config"); - if (git_config_system()) + if (git_config_system()) { + config_from_file_gently(fn, git_program_data_config(), data, + 0, &ret, &found); config_from_file_gently(fn, git_etc_gitconfig(), data, 0, &ret, &found); + } config_from_file_gently(fn, xdg_config, data, ACCESS_EACCES_OK, &ret, &found); diff --git a/git-compat-util.h b/git-compat-util.h index 62841f62ec..01800b7576 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -372,6 +372,10 @@ static inline char *git_find_last_dir_sep(const char *path) #define find_last_dir_sep git_find_last_dir_sep #endif +#ifndef git_program_data_config +#define git_program_data_config() NULL +#endif + #if defined(__HP_cc) && (__HP_cc >= 61000) #define NORETURN __attribute__((noreturn)) #define NORETURN_PTR From cd1c23bf5ba9c375435aee6e545c55ac71ae0ca0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 16 Feb 2016 15:21:45 +0100 Subject: [PATCH 4/4] Clarify the location of the Windows-specific ProgramData config On Windows, there is no (single) `/etc/` directory. To address that, in conjunction with the libgit2 project, Git for Windows introduced yet another level of system-wide config files, located in C:\ProgramData (and the equivalent on Windows XP). Let's spell this out in the documentation. This closes https://github.com/git-for-windows/git/pull/470 (because there was no reaction in three months in that Pull Request). Signed-off-by: Johannes Schindelin --- Documentation/git-config.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index e07ace4c11..96cd5612d1 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -254,9 +254,12 @@ $GIT_DIR/config:: Repository specific configuration file. On Windows, as there is no central `/etc/` directory, there is yet another -config file, intended to contain settings for *all* Git-related software -running on the machine. Consequently, this config file takes an even lower -precedence than the `$(prefix)/etc/gitconfig` file. +config file (located at `$PROGRAMDATA/Git/config`), intended to contain +settings for *all* Git-related software running on the machine. Consequently, +this config file takes an even lower precedence than the +`$(prefix)/etc/gitconfig` file. Typically `$PROGRAMDATA` points to +`C:\ProgramData` (on Windows XP the equivalent in `$ALLUSERSPROFILE` is used, +i.e. `C:\Documents and Settings\All Users\Application Data\Git\config`). If no further options are given, all reading options will read all of these files that are available. If the global or the system-wide configuration