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>
This commit is contained in:
parent
0e18bef7e6
commit
21cf322791
@ -99,8 +99,8 @@ OPTIONS
|
|||||||
For writing options: write to global ~/.gitconfig file rather than
|
For writing options: write to global ~/.gitconfig file rather than
|
||||||
the repository .git/config.
|
the repository .git/config.
|
||||||
+
|
+
|
||||||
For reading options: read only from global ~/.gitconfig rather than
|
For reading options: read only from global ~/.gitconfig and from
|
||||||
from all available files.
|
$XDG_CONFIG_HOME/git/config rather than from all available files.
|
||||||
+
|
+
|
||||||
See also <<FILES>>.
|
See also <<FILES>>.
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ See also <<FILES>>.
|
|||||||
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 config' will search for configuration options:
|
||||||
|
|
||||||
$GIT_DIR/config::
|
$GIT_DIR/config::
|
||||||
@ -204,6 +204,14 @@ $GIT_DIR/config::
|
|||||||
User-specific configuration file. Also called "global"
|
User-specific configuration file. Also called "global"
|
||||||
configuration file.
|
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::
|
$(prefix)/etc/gitconfig::
|
||||||
System-wide configuration file.
|
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_)
|
static int get_value(const char *key_, const char *regex_)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
char *global = NULL, *repo_config = NULL;
|
char *global = NULL, *xdg = NULL, *repo_config = NULL;
|
||||||
const char *system_wide = NULL, *local;
|
const char *system_wide = NULL, *local;
|
||||||
struct config_include_data inc = CONFIG_INCLUDE_INIT;
|
struct config_include_data inc = CONFIG_INCLUDE_INIT;
|
||||||
config_fn_t fn;
|
config_fn_t fn;
|
||||||
@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_)
|
|||||||
|
|
||||||
local = given_config_file;
|
local = given_config_file;
|
||||||
if (!local) {
|
if (!local) {
|
||||||
const char *home = getenv("HOME");
|
|
||||||
local = repo_config = git_pathdup("config");
|
local = repo_config = git_pathdup("config");
|
||||||
if (home)
|
|
||||||
global = xstrdup(mkpath("%s/.gitconfig", home));
|
|
||||||
if (git_config_system())
|
if (git_config_system())
|
||||||
system_wide = git_etc_gitconfig();
|
system_wide = git_etc_gitconfig();
|
||||||
|
home_config_paths(&global, &xdg, "config");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_key_regexp) {
|
if (use_key_regexp) {
|
||||||
@ -229,6 +227,8 @@ static int get_value(const char *key_, const char *regex_)
|
|||||||
|
|
||||||
if (do_all && system_wide)
|
if (do_all && system_wide)
|
||||||
git_config_from_file(fn, system_wide, data);
|
git_config_from_file(fn, system_wide, data);
|
||||||
|
if (do_all && xdg)
|
||||||
|
git_config_from_file(fn, xdg, data);
|
||||||
if (do_all && global)
|
if (do_all && global)
|
||||||
git_config_from_file(fn, global, data);
|
git_config_from_file(fn, global, data);
|
||||||
if (do_all)
|
if (do_all)
|
||||||
@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_)
|
|||||||
git_config_from_file(fn, local, data);
|
git_config_from_file(fn, local, data);
|
||||||
if (!do_all && !seen && global)
|
if (!do_all && !seen && global)
|
||||||
git_config_from_file(fn, global, data);
|
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)
|
if (!do_all && !seen && system_wide)
|
||||||
git_config_from_file(fn, system_wide, data);
|
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_strings:
|
||||||
free(repo_config);
|
free(repo_config);
|
||||||
free(global);
|
free(global);
|
||||||
|
free(xdg);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (use_global_config) {
|
if (use_global_config) {
|
||||||
char *home = getenv("HOME");
|
char *user_config = NULL;
|
||||||
if (home) {
|
char *xdg_config = NULL;
|
||||||
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
|
|
||||||
|
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;
|
given_config_file = user_config;
|
||||||
} else {
|
else
|
||||||
die("$HOME not set");
|
die("$HOME not set");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (use_system_config)
|
else if (use_system_config)
|
||||||
given_config_file = git_etc_gitconfig();
|
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)));
|
__attribute__((format (printf, 3, 4)));
|
||||||
extern char *git_pathdup(const char *fmt, ...)
|
extern char *git_pathdup(const char *fmt, ...)
|
||||||
__attribute__((format (printf, 1, 2)));
|
__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 */
|
/* Return a statically allocated filename matching the sha1 signature */
|
||||||
extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
|
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(char *path);
|
||||||
int safe_create_leading_directories_const(const char *path);
|
int safe_create_leading_directories_const(const char *path);
|
||||||
int mkdir_in_gitdir(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);
|
extern char *expand_user_path(const char *path);
|
||||||
const char *enter_repo(const char *path, int strict);
|
const char *enter_repo(const char *path, int strict);
|
||||||
static inline int is_absolute_path(const char *path)
|
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 git_config_early(config_fn_t fn, void *data, const char *repo_config)
|
||||||
{
|
{
|
||||||
int ret = 0, found = 0;
|
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)) {
|
if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
|
||||||
ret += git_config_from_file(fn, git_etc_gitconfig(),
|
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;
|
found += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
home = getenv("HOME");
|
if (!access(xdg_config, R_OK)) {
|
||||||
if (home) {
|
ret += git_config_from_file(fn, xdg_config, data);
|
||||||
char buf[PATH_MAX];
|
found += 1;
|
||||||
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);
|
if (!access(user_config, R_OK)) {
|
||||||
found += 1;
|
ret += git_config_from_file(fn, user_config, data);
|
||||||
}
|
found += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repo_config && !access(repo_config, R_OK)) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(xdg_config);
|
||||||
|
free(user_config);
|
||||||
return ret == 0 ? found : ret;
|
return ret == 0 ? found : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
path.c
41
path.c
@ -87,6 +87,21 @@ char *git_pathdup(const char *fmt, ...)
|
|||||||
return xstrdup(path);
|
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, ...)
|
char *mkpath(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
@ -122,6 +137,32 @@ char *git_path(const char *fmt, ...)
|
|||||||
return cleanup_path(pathname);
|
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 *git_path_submodule(const char *path, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
char *pathname = get_pathname();
|
char *pathname = get_pathname();
|
||||||
|
70
t/t1306-xdg-files.sh
Executable file
70
t/t1306-xdg-files.sh
Executable file
@ -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
Block a user