From 3a15b172046b6a99be3779ed16303ef361e86ab0 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Sat, 23 May 2015 02:19:50 +0200 Subject: [PATCH 1/4] git_config_early: factor out duplicate code and clarify return value Factor out near identical per-file logic. Its also unclear that 'ret += git_config_from_file()' actually decrements ret on error. It adds the return value of error() (or const_error() for GCC), which is five levels down the call hierarchy. Signed-off-by: Karsten Blees --- config.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/config.c b/config.c index c4424c01388496..34bfc52ae4692d 100644 --- a/config.c +++ b/config.c @@ -1182,34 +1182,36 @@ 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; + /* 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 (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 (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) { - ret += git_config_from_file(fn, xdg_config, data); - found += 1; - } + if (git_config_system()) + cnt = config_early_helper(fn, git_etc_gitconfig(), data, 0, cnt); - if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) { - ret += git_config_from_file(fn, user_config, 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 (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 */ @@ -1218,13 +1220,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, From 3f7ec023c92bf648f701d8f8213337b939ae30be 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. Signed-off-by: Karsten Blees --- config.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/config.c b/config.c index 34bfc52ae4692d..6575a3b1b157c6 100644 --- a/config.c +++ b/config.c @@ -1909,6 +1909,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. @@ -1957,10 +1975,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; @@ -2228,12 +2244,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 90c0a740a2adfd77459e6a952ef5826ab9cd89f5 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 22 May 2015 20:13:42 +0200 Subject: [PATCH 3/4] config.c: add support for installation-specific config files If Git is configured to use the absolute '/etc/gitconfig' for system-wide settings, it may still be useful to have installation-specific settings such as 'help.format' (which depends on the installed set of help pages). This is particularly relevant on Windows, where a variety of Git flavors with different feature sets may be installed in parallel. If ETC_GITCONFIG is an absolute path (typically '/etc/gitconfig'), load installation-specific defaults (from '$(prefix)/$(ETC_GITCONFIG)') before loading the system-wide configuration. As installation-specific settings will typically be defined when creating the software package / installer (i.e. *before* git is installed), we don't need an extra 'git config --installation' option. Update the documentation accordingly. This removes '$(prefix)' from most places (which would be meaningless to normal users anyway). Signed-off-by: Karsten Blees --- Documentation/git-config.txt | 22 ++++++++++++++++------ Documentation/git.txt | 4 ++-- Documentation/gitattributes.txt | 2 +- config.c | 24 ++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 02ec096faac77a..051c78060ee237 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -117,10 +117,10 @@ 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. + See also <>. @@ -221,11 +221,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:: + 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. ++ +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 +278,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..04a052f0d847d8 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. 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 6575a3b1b157c6..e7f117ea58b87a 100644 --- a/config.c +++ b/config.c @@ -1147,14 +1147,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'. @@ -1205,8 +1223,10 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) home_config_paths(&user_config, &xdg_config, "config"); - if (git_config_system()) + 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); + } cnt = config_early_helper(fn, xdg_config, data, ACCESS_EACCES_OK, cnt); cnt = config_early_helper(fn, user_config, data, ACCESS_EACCES_OK, cnt); From 303df68f1d97542172c3a0ef97b51237c2d99993 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Sat, 23 May 2015 01:27:32 +0200 Subject: [PATCH 4/4] Windows: use central directory for system-wide config files Git versions on Windows typically store system-wide config files within their installation directory. Other Git implementations (e.g. libgit2, JGit etc.) may wish to reuse the system-wide config, but finding the installation directory of an already installed Git version is problematic. On Windows, the %PROGRAMDATA% environment variable specifies the root directory for system-wide application data (%ALLUSERSPROFILE% for WinXP). This would be the equivalent of '/etc' (and '/var'), except that naming guidelines recommend organizing data in per-application sub-directories. When looking up a system_path() on Windows, replace the absolute path prefix '/etc/' with '%PROGRAMDATA%/Git/'. For MinGW and MSVC builds, activate system-wide configurations by changing the $(sysconfdir) Makefile variable to an absolute path ('/etc'). Cygwin and MSys2 builds get this automatically when building with 'prefix=/usr'. Update the documentation for /etc/gitconfig and /etc/gitattributes. IOW: All Git versions on Windows (Cygwin, MinGW, MSVC and MSys2) will load configuration settings from '$(prefix)/etc/gitconfig' followed by '%PROGRAMDATA%/Git/gitconfig'. Signed-off-by: Karsten Blees --- Documentation/config.txt | 3 ++- Documentation/git-config.txt | 4 +++- Documentation/gitattributes.txt | 2 +- config.mak.uname | 6 ++++++ exec_cmd.c | 18 +++++++++++++++++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 2e5ceaf71974b1..a1672f557c9631 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -6,7 +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. +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 051c78060ee237..92eca8758f0e17 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -123,6 +123,8 @@ See also <>. 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:: @@ -231,7 +233,7 @@ $(prefix)/etc/gitconfig:: (e.g. 'help.format=html' if the installation only includes html pages). /etc/gitconfig:: - System-wide configuration file. + 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 diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 04a052f0d847d8..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 -`/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.mak.uname b/config.mak.uname index f4e77cb9e5099c..6a35d20a12e685 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 @@ -534,6 +536,10 @@ ifneq (,$(findstring MINGW,$(uname_S))) ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) htmldir = doc/git/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);