diff --git a/Documentation/config.txt b/Documentation/config.txt index df3e34cfcf3dd0..b1d1136d85b8b1 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -6,9 +6,8 @@ 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. On Windows, -configuration can also be stored in `C:\ProgramData\Git\config`; This -file will be used also by libgit2-based software. +can be used to store a system-wide default configuration +(`%PROGRAMDATA%\Git\gitconfig` on Windows). 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 02ec096faac77a..92eca8758f0e17 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -117,12 +117,14 @@ See also <>. --system:: For writing options: write to system-wide - `$(prefix)/etc/gitconfig` rather than the repository + `/etc/gitconfig` rather than the repository `.git/config`. + -For reading options: read only from system-wide `$(prefix)/etc/gitconfig` +For reading options: read only from system-wide `/etc/gitconfig` rather than from all available files. + +On Windows, the system-wide config file is `%PROGRAMDATA%\Git\gitconfig`. ++ See also <>. --local:: @@ -221,11 +223,21 @@ See also <>. FILES ----- -If not set explicitly with '--file', there are four files where +If not set explicitly with '--file', there are five files where 'git config' will search for configuration options: $(prefix)/etc/gitconfig:: - System-wide configuration file. + Installation-specific configuration file, where '$(prefix)' is the + installation root directory specified via 'make prefix=...'. This allows + software distributions to provide installation-specific default values + (e.g. 'help.format=html' if the installation only includes html pages). + +/etc/gitconfig:: + System-wide configuration file (`%PROGRAMDATA%\Git\gitconfig` on Windows). ++ +If git was built with relative `$(sysconfdir)`, this file will not be +used, and the '--system' option refers to the installation-specific +`$(prefix)/etc/gitconfig` instead. $XDG_CONFIG_HOME/git/config:: Second user-specific configuration file. If $XDG_CONFIG_HOME is not set @@ -268,11 +280,11 @@ ENVIRONMENT GIT_CONFIG:: Take the configuration from the given file instead of .git/config. Using the "--global" option forces this to ~/.gitconfig. Using the - "--system" option forces this to $(prefix)/etc/gitconfig. + "--system" option forces this to /etc/gitconfig. GIT_CONFIG_NOSYSTEM:: - Whether to skip reading settings from the system-wide - $(prefix)/etc/gitconfig file. See linkgit:git[1] for details. + Whether to skip reading settings from the installation-specific and + system-wide /etc/gitconfig files. See linkgit:git[1] for details. See also <>. diff --git a/Documentation/git.txt b/Documentation/git.txt index 5808df6a83777e..1e87aeef6ec907 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -939,8 +939,8 @@ for further details. on the terminal (e.g., when asking for HTTP authentication). 'GIT_CONFIG_NOSYSTEM':: - Whether to skip reading settings from the system-wide - `$(prefix)/etc/gitconfig` file. This environment variable can + Whether to skip reading settings from the installation-specific and + system-wide `/etc/gitconfig` files. 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/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 70899b302365f1..69074850c8a74f 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -84,7 +84,7 @@ for a single user should be placed in a file specified by the Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/attributes is used instead. Attributes for all users on a system should be placed in the -`$(prefix)/etc/gitattributes` file. +`/etc/gitattributes` file (`%PROGRAMDATA%\Git\gitattributes` on Windows). Sometimes you would need to override an setting of an attribute for a path to `Unspecified` state. This can be done by listing diff --git a/config.c b/config.c index 348bd7353b5f24..4fa3ea331bd6cb 100644 --- a/config.c +++ b/config.c @@ -1166,14 +1166,32 @@ static int git_config_from_blob_ref(config_fn_t fn, return git_config_from_blob_sha1(fn, name, sha1, data); } +static const char *etc_gitconfig = ETC_GITCONFIG; + const char *git_etc_gitconfig(void) { static const char *system_wide; if (!system_wide) - system_wide = system_path(ETC_GITCONFIG); + system_wide = system_path(etc_gitconfig); return system_wide; } +static const char *git_inst_gitconfig(void) +{ + static const char *installation_defaults; + if (!installation_defaults) { + /* + * if ETC_GITCONFIG as configured in the Makefile is an absolute path, + * also load installation-specific defaults (relative to $(prefix)) + */ + if (is_dir_sep(*etc_gitconfig)) + installation_defaults = system_path(etc_gitconfig + 1); + else + installation_defaults = ""; + } + return *installation_defaults ? installation_defaults : NULL; +} + /* * Parse environment variable 'k' as a boolean (in various * possible spellings); if missing, use the default value 'def'. @@ -1201,41 +1219,38 @@ int git_config_system(void) return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); } +static inline int config_early_helper(config_fn_t fn, const char *filename, + void *data, unsigned access_flags, int count) { + if (!filename || access_or_die(filename, R_OK, access_flags)) + /* no file: return unchanged */ + return count; + + if (git_config_from_file(fn, filename, data)) + /* error: decrement or start counting errors at -1 */ + return count < 0 ? count - 1 : -1; + else + /* ok: increment unless we had errors before */ + return count < 0 ? count : count + 1; +} + int git_config_early(config_fn_t fn, void *data, const char *repo_config) { - int ret = 0, found = 0; - const char *super_config = git_super_config(); + /* count loaded files (> 0) or errors (< 0) */ + int cnt = 0; char *xdg_config = NULL; char *user_config = NULL; home_config_paths(&user_config, &xdg_config, "config"); - if (super_config && git_config_system() && - !access(super_config, R_OK)) { - ret += git_config_from_file(fn, super_config, data); - found += 1; + if (git_config_system()) { + cnt = config_early_helper(fn, git_inst_gitconfig(), data, 0, cnt); + cnt = config_early_helper(fn, git_etc_gitconfig(), data, 0, cnt); } - 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; - } + cnt = config_early_helper(fn, xdg_config, data, ACCESS_EACCES_OK, cnt); + cnt = config_early_helper(fn, user_config, data, ACCESS_EACCES_OK, cnt); - if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) { - ret += git_config_from_file(fn, xdg_config, data); - found += 1; - } - - 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; - } + cnt = config_early_helper(fn, repo_config, data, 0, cnt); switch (git_config_from_parameters(fn, data)) { case -1: /* error */ @@ -1244,13 +1259,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) case 0: /* found nothing */ break; default: /* found at least one item */ - found++; + if (cnt >= 0) + cnt++; break; } free(xdg_config); free(user_config); - return ret == 0 ? found : ret; + return cnt; } int git_config_with_options(config_fn_t fn, void *data, @@ -1932,6 +1948,24 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_) return -CONFIG_INVALID_KEY; } + +static int lock_config_file(const char *config_filename, + struct lock_file **result) +{ + int fd; + /* make sure the parent directory exists */ + if (safe_create_leading_directories_const(config_filename)) { + error("could not create parent directory of %s", config_filename); + return -1; + } + *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. @@ -1980,10 +2014,8 @@ int git_config_set_multivar_in_file(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("could not lock config file %s: %s", config_filename, strerror(errno)); free(store.key); ret = CONFIG_NO_LOCK; goto out_free; @@ -2251,12 +2283,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/config.mak.uname b/config.mak.uname index 148d8cd8f7c47b..7def04cdd3238c 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -330,6 +330,7 @@ endif ifeq ($(uname_S),Windows) GIT_VERSION := $(GIT_VERSION).MSVC pathsep = ; + sysconfdir = /etc HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease @@ -484,6 +485,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL) endif ifneq (,$(findstring MINGW,$(uname_S))) pathsep = ; + sysconfdir = /etc HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease @@ -532,6 +534,10 @@ ifneq (,$(findstring MINGW,$(uname_S))) ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) htmldir = share/doc/git/$(firstword $(subst -, ,$(GIT_VERSION)))/html prefix = + # prevent conversion to Windows path on MSys1 (see + # http://www.mingw.org/wiki/Posix_path_conversion) + ETC_GITCONFIG = //etc\gitconfig + ETC_GITATTRIBUTES = //etc\gitattributes INSTALL = /bin/install EXTLIBS += /mingw/lib/libz.a NO_R_TO_GCC_LINKER = YesPlease diff --git a/exec_cmd.c b/exec_cmd.c index 8ab37b5f74f360..79823cbc38186e 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -15,8 +15,24 @@ char *system_path(const char *path) #endif struct strbuf d = STRBUF_INIT; - if (is_absolute_path(path)) + if (is_absolute_path(path)) { +#ifdef _WIN32 + /* + * On Windows (all variants), replace '/etc/' with '%PROGRAMDATA%/Git/' + * (or '%ALLUSERSPROFILE%/Git' in case of Windows XP) + */ + if (!strncmp(path, "/etc/", 5)) { + const char *sysconfdir = getenv("PROGRAMDATA"); + if (!sysconfdir) + sysconfdir = getenv("ALLUSERSPROFILE"); + if (sysconfdir) { + strbuf_addf(&d, "%s/Git/%s", sysconfdir, path + 5); + return strbuf_detach(&d, NULL); + } + } +#endif return xstrdup(path); + } #ifdef RUNTIME_PREFIX assert(argv0_path);