diff --git a/Documentation/config.txt b/Documentation/config.txt index 9fce3c4782..c3e10a420a 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..96cd5612d1 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -253,6 +253,14 @@ $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 (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 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 1023909aab..08843e8c9d 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2714,3 +2714,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 25ccfdf1bb..bdaf40cd56 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -428,6 +428,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 c47ecc79e4..fff0766076 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,19 @@ 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_program_data_config(), data, + 0, &ret, &found); + 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 */ @@ -1972,6 +1974,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. @@ -2023,8 +2039,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); @@ -2325,12 +2340,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 */ diff --git a/git-compat-util.h b/git-compat-util.h index c0cea3c170..b9edf46ac6 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