config: read (but not write) from $XDG_CONFIG_HOME/git/config file
Teach git to read the "gitconfig" information from a new location, $XDG_CONFIG_HOME/git/config; this allows the user to avoid cluttering $HOME with many per-application configuration files. In the order of reading, this file comes between the global configuration file (typically $HOME/.gitconfig) and the system wide configuration file (typically /etc/gitconfig). We do not write to this new location (yet). If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config will be used. This is in line with XDG specification. If the new file does not exist, the behavior is unchanged. Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr> Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr> Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr> Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr> Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									0e18bef7e6
								
							
						
					
					
						commit
						21cf322791
					
				|  | @ -99,8 +99,8 @@ OPTIONS | |||
| 	For writing options: write to global ~/.gitconfig file rather than | ||||
| 	the repository .git/config. | ||||
| + | ||||
| For reading options: read only from global ~/.gitconfig rather than | ||||
| from all available files. | ||||
| For reading options: read only from global ~/.gitconfig and from | ||||
| $XDG_CONFIG_HOME/git/config rather than from all available files. | ||||
| + | ||||
| See also <<FILES>>. | ||||
|  | ||||
|  | @ -194,7 +194,7 @@ See also <<FILES>>. | |||
| FILES | ||||
| ----- | ||||
|  | ||||
| If not set explicitly with '--file', there are three files where | ||||
| If not set explicitly with '--file', there are four files where | ||||
| 'git config' will search for configuration options: | ||||
|  | ||||
| $GIT_DIR/config:: | ||||
|  | @ -204,6 +204,14 @@ $GIT_DIR/config:: | |||
| 	User-specific configuration file. Also called "global" | ||||
| 	configuration file. | ||||
|  | ||||
| $XDG_CONFIG_HOME/git/config:: | ||||
| 	Second user-specific configuration file. If $XDG_CONFIG_HOME is not set | ||||
| 	or empty, $HOME/.config/git/config will be used. Any single-valued | ||||
| 	variable set in this file will be overwritten by whatever is in | ||||
| 	~/.gitconfig.  It is a good idea not to create this file if | ||||
| 	you sometimes use older versions of Git, as support for this | ||||
| 	file was added fairly recently. | ||||
|  | ||||
| $(prefix)/etc/gitconfig:: | ||||
| 	System-wide configuration file. | ||||
|  | ||||
|  |  | |||
|  | @ -161,7 +161,7 @@ static int show_config(const char *key_, const char *value_, void *cb) | |||
| static int get_value(const char *key_, const char *regex_) | ||||
| { | ||||
| 	int ret = -1; | ||||
| 	char *global = NULL, *repo_config = NULL; | ||||
| 	char *global = NULL, *xdg = NULL, *repo_config = NULL; | ||||
| 	const char *system_wide = NULL, *local; | ||||
| 	struct config_include_data inc = CONFIG_INCLUDE_INIT; | ||||
| 	config_fn_t fn; | ||||
|  | @ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_) | |||
|  | ||||
| 	local = given_config_file; | ||||
| 	if (!local) { | ||||
| 		const char *home = getenv("HOME"); | ||||
| 		local = repo_config = git_pathdup("config"); | ||||
| 		if (home) | ||||
| 			global = xstrdup(mkpath("%s/.gitconfig", home)); | ||||
| 		if (git_config_system()) | ||||
| 			system_wide = git_etc_gitconfig(); | ||||
| 		home_config_paths(&global, &xdg, "config"); | ||||
| 	} | ||||
|  | ||||
| 	if (use_key_regexp) { | ||||
|  | @ -229,6 +227,8 @@ static int get_value(const char *key_, const char *regex_) | |||
|  | ||||
| 	if (do_all && system_wide) | ||||
| 		git_config_from_file(fn, system_wide, data); | ||||
| 	if (do_all && xdg) | ||||
| 		git_config_from_file(fn, xdg, data); | ||||
| 	if (do_all && global) | ||||
| 		git_config_from_file(fn, global, data); | ||||
| 	if (do_all) | ||||
|  | @ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_) | |||
| 		git_config_from_file(fn, local, data); | ||||
| 	if (!do_all && !seen && global) | ||||
| 		git_config_from_file(fn, global, data); | ||||
| 	if (!do_all && !seen && xdg) | ||||
| 		git_config_from_file(fn, xdg, data); | ||||
| 	if (!do_all && !seen && system_wide) | ||||
| 		git_config_from_file(fn, system_wide, data); | ||||
|  | ||||
|  | @ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_) | |||
| free_strings: | ||||
| 	free(repo_config); | ||||
| 	free(global); | ||||
| 	free(xdg); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
|  | @ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix) | |||
| 	} | ||||
|  | ||||
| 	if (use_global_config) { | ||||
| 		char *home = getenv("HOME"); | ||||
| 		if (home) { | ||||
| 			char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); | ||||
| 		char *user_config = NULL; | ||||
| 		char *xdg_config = NULL; | ||||
|  | ||||
| 		home_config_paths(&user_config, &xdg_config, "config"); | ||||
|  | ||||
| 		if (access(user_config, R_OK) && !access(xdg_config, R_OK) && | ||||
| 		    (actions == ACTION_LIST || | ||||
| 		     actions == ACTION_GET_COLOR || | ||||
| 		     actions == ACTION_GET_COLORBOOL)) | ||||
| 			given_config_file = xdg_config; | ||||
| 		else if (user_config) | ||||
| 			given_config_file = user_config; | ||||
| 		} else { | ||||
| 		else | ||||
| 			die("$HOME not set"); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (use_system_config) | ||||
| 		given_config_file = git_etc_gitconfig(); | ||||
|  |  | |||
							
								
								
									
										3
									
								
								cache.h
								
								
								
								
							
							
						
						
									
										3
									
								
								cache.h
								
								
								
								
							|  | @ -619,6 +619,8 @@ extern char *git_snpath(char *buf, size_t n, const char *fmt, ...) | |||
| 	__attribute__((format (printf, 3, 4))); | ||||
| extern char *git_pathdup(const char *fmt, ...) | ||||
| 	__attribute__((format (printf, 1, 2))); | ||||
| extern char *mkpathdup(const char *fmt, ...) | ||||
| 	__attribute__((format (printf, 1, 2))); | ||||
|  | ||||
| /* Return a statically allocated filename matching the sha1 signature */ | ||||
| extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | ||||
|  | @ -708,6 +710,7 @@ int set_shared_perm(const char *path, int mode); | |||
| int safe_create_leading_directories(char *path); | ||||
| int safe_create_leading_directories_const(const char *path); | ||||
| int mkdir_in_gitdir(const char *path); | ||||
| extern void home_config_paths(char **global, char **xdg, char *file); | ||||
| extern char *expand_user_path(const char *path); | ||||
| const char *enter_repo(const char *path, int strict); | ||||
| static inline int is_absolute_path(const char *path) | ||||
|  |  | |||
							
								
								
									
										23
									
								
								config.c
								
								
								
								
							
							
						
						
									
										23
									
								
								config.c
								
								
								
								
							|  | @ -929,7 +929,10 @@ int git_config_system(void) | |||
| int git_config_early(config_fn_t fn, void *data, const char *repo_config) | ||||
| { | ||||
| 	int ret = 0, found = 0; | ||||
| 	const char *home = NULL; | ||||
| 	char *xdg_config = NULL; | ||||
| 	char *user_config = NULL; | ||||
|  | ||||
| 	home_config_paths(&user_config, &xdg_config, "config"); | ||||
|  | ||||
| 	if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { | ||||
| 		ret += git_config_from_file(fn, git_etc_gitconfig(), | ||||
|  | @ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) | |||
| 		found += 1; | ||||
| 	} | ||||
|  | ||||
| 	home = getenv("HOME"); | ||||
| 	if (home) { | ||||
| 		char buf[PATH_MAX]; | ||||
| 		char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home); | ||||
| 		if (!access(user_config, R_OK)) { | ||||
| 			ret += git_config_from_file(fn, user_config, data); | ||||
| 			found += 1; | ||||
| 		} | ||||
| 	if (!access(xdg_config, R_OK)) { | ||||
| 		ret += git_config_from_file(fn, xdg_config, data); | ||||
| 		found += 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!access(user_config, R_OK)) { | ||||
| 		ret += git_config_from_file(fn, user_config, data); | ||||
| 		found += 1; | ||||
| 	} | ||||
|  | ||||
| 	if (repo_config && !access(repo_config, R_OK)) { | ||||
|  | @ -963,6 +966,8 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) | |||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	free(xdg_config); | ||||
| 	free(user_config); | ||||
| 	return ret == 0 ? found : ret; | ||||
| } | ||||
|  | ||||
|  |  | |||
							
								
								
									
										41
									
								
								path.c
								
								
								
								
							
							
						
						
									
										41
									
								
								path.c
								
								
								
								
							|  | @ -87,6 +87,21 @@ char *git_pathdup(const char *fmt, ...) | |||
| 	return xstrdup(path); | ||||
| } | ||||
|  | ||||
| char *mkpathdup(const char *fmt, ...) | ||||
| { | ||||
| 	char *path; | ||||
| 	struct strbuf sb = STRBUF_INIT; | ||||
| 	va_list args; | ||||
|  | ||||
| 	va_start(args, fmt); | ||||
| 	strbuf_vaddf(&sb, fmt, args); | ||||
| 	va_end(args); | ||||
| 	path = xstrdup(cleanup_path(sb.buf)); | ||||
|  | ||||
| 	strbuf_release(&sb); | ||||
| 	return path; | ||||
| } | ||||
|  | ||||
| char *mkpath(const char *fmt, ...) | ||||
| { | ||||
| 	va_list args; | ||||
|  | @ -122,6 +137,32 @@ char *git_path(const char *fmt, ...) | |||
| 	return cleanup_path(pathname); | ||||
| } | ||||
|  | ||||
| void home_config_paths(char **global, char **xdg, char *file) | ||||
| { | ||||
| 	char *xdg_home = getenv("XDG_CONFIG_HOME"); | ||||
| 	char *home = getenv("HOME"); | ||||
| 	char *to_free = NULL; | ||||
|  | ||||
| 	if (!home) { | ||||
| 		if (global) | ||||
| 			*global = NULL; | ||||
| 	} else { | ||||
| 		if (!xdg_home) { | ||||
| 			to_free = mkpathdup("%s/.config", home); | ||||
| 			xdg_home = to_free; | ||||
| 		} | ||||
| 		if (global) | ||||
| 			*global = mkpathdup("%s/.gitconfig", home); | ||||
| 	} | ||||
|  | ||||
| 	if (!xdg_home) | ||||
| 		*xdg = NULL; | ||||
| 	else | ||||
| 		*xdg = mkpathdup("%s/git/%s", xdg_home, file); | ||||
|  | ||||
| 	free(to_free); | ||||
| } | ||||
|  | ||||
| char *git_path_submodule(const char *path, const char *fmt, ...) | ||||
| { | ||||
| 	char *pathname = get_pathname(); | ||||
|  |  | |||
|  | @ -0,0 +1,70 @@ | |||
| #!/bin/sh | ||||
| # | ||||
| # Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas, | ||||
| #		     Thomas Nguy, Khoi Nguyen | ||||
| #		     Grenoble INP Ensimag | ||||
| # | ||||
|  | ||||
| test_description='Compatibility with $XDG_CONFIG_HOME/git/ files' | ||||
|  | ||||
| . ./test-lib.sh | ||||
|  | ||||
| test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' ' | ||||
| 	mkdir -p .config/git && | ||||
| 	echo "[alias]" >.config/git/config && | ||||
| 	echo "	myalias = !echo in_config" >>.config/git/config && | ||||
| 	echo in_config >expected && | ||||
| 	git myalias >actual && | ||||
| 	test_cmp expected actual | ||||
| ' | ||||
|  | ||||
|  | ||||
| test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' ' | ||||
| 	>.gitconfig && | ||||
| 	echo "[alias]" >.gitconfig && | ||||
| 	echo "	myalias = !echo in_gitconfig" >>.gitconfig && | ||||
| 	echo in_gitconfig >expected && | ||||
| 	git myalias >actual && | ||||
| 	test_cmp expected actual | ||||
| ' | ||||
|  | ||||
|  | ||||
| test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\''t' ' | ||||
| 	rm .gitconfig && | ||||
| 	echo "[user]" >.config/git/config && | ||||
| 	echo "	name = read_config" >>.config/git/config && | ||||
| 	echo read_config >expected && | ||||
| 	git config --get user.name >actual && | ||||
| 	test_cmp expected actual | ||||
| ' | ||||
|  | ||||
|  | ||||
| test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' ' | ||||
| 	>.gitconfig && | ||||
| 	echo "[user]" >.gitconfig && | ||||
| 	echo "	name = read_gitconfig" >>.gitconfig && | ||||
| 	echo read_gitconfig >expected && | ||||
| 	git config --get user.name >actual && | ||||
| 	test_cmp expected actual | ||||
| ' | ||||
|  | ||||
|  | ||||
| test_expect_success 'read with --list: xdg file exists and ~/.gitconfig doesn'\''t' ' | ||||
| 	rm .gitconfig && | ||||
| 	echo user.name=read_config >expected && | ||||
| 	git config --global --list >actual && | ||||
| 	test_cmp expected actual | ||||
| ' | ||||
|  | ||||
|  | ||||
| test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists' ' | ||||
| 	>.gitconfig && | ||||
| 	echo "[user]" >.gitconfig && | ||||
| 	echo "	name = read_gitconfig" >>.gitconfig && | ||||
| 	echo user.name=read_gitconfig >expected && | ||||
| 	git config --global --list >actual && | ||||
| 	test_cmp expected actual | ||||
| ' | ||||
|  | ||||
|  | ||||
| test_done | ||||
		Loading…
	
		Reference in New Issue
	
	 Huynh Khoi Nguyen Nguyen
						Huynh Khoi Nguyen Nguyen