Merge branch 'mm/config-xdg'
Teach git to read various information from $XDG_CONFIG_HOME/git/ to allow the user to avoid cluttering $HOME. * mm/config-xdg: config: write to $XDG_CONFIG_HOME/git/config file when appropriate Let core.attributesfile default to $XDG_CONFIG_HOME/git/attributes Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore config: read (but not write) from $XDG_CONFIG_HOME/git/config file
This commit is contained in:
commit
d02d7ac303
@ -484,7 +484,9 @@ core.excludesfile::
|
||||
'.git/info/exclude', git looks into this file for patterns
|
||||
of files which are not meant to be tracked. "`~/`" is expanded
|
||||
to the value of `$HOME` and "`~user/`" to the specified user's
|
||||
home directory. See linkgit:gitignore[5].
|
||||
home directory. Its default value is $XDG_CONFIG_HOME/git/ignore.
|
||||
If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore
|
||||
is used instead. See linkgit:gitignore[5].
|
||||
|
||||
core.askpass::
|
||||
Some commands (e.g. svn and http interfaces) that interactively
|
||||
@ -499,7 +501,9 @@ core.attributesfile::
|
||||
In addition to '.gitattributes' (per-directory) and
|
||||
'.git/info/attributes', git looks into this file for attributes
|
||||
(see linkgit:gitattributes[5]). Path expansions are made the same
|
||||
way as for `core.excludesfile`.
|
||||
way as for `core.excludesfile`. 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.
|
||||
|
||||
core.editor::
|
||||
Commands such as `commit` and `tag` that lets you edit
|
||||
|
@ -97,10 +97,11 @@ OPTIONS
|
||||
|
||||
--global::
|
||||
For writing options: write to global ~/.gitconfig file rather than
|
||||
the repository .git/config.
|
||||
the repository .git/config, write to $XDG_CONFIG_HOME/git/config file
|
||||
if this file exists and the ~/.gitconfig file doesn't.
|
||||
+
|
||||
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 +195,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 +205,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.
|
||||
|
||||
|
@ -75,6 +75,8 @@ repositories (i.e., attributes of interest to all users) should go into
|
||||
`.gitattributes` files. Attributes that should affect all repositories
|
||||
for a single user should be placed in a file specified by the
|
||||
`core.attributesfile` configuration option (see linkgit:git-config[1]).
|
||||
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.
|
||||
|
||||
|
@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into
|
||||
the `$GIT_DIR/info/exclude` file. Patterns which a user wants git to
|
||||
ignore in all situations (e.g., backup or temporary files generated by
|
||||
the user's editor of choice) generally go into a file specified by
|
||||
`core.excludesfile` in the user's `~/.gitconfig`.
|
||||
`core.excludesfile` in the user's `~/.gitconfig`. Its default value is
|
||||
$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,
|
||||
$HOME/.config/git/ignore is used instead.
|
||||
|
||||
The underlying git plumbing tools, such as
|
||||
'git ls-files' and 'git read-tree', read
|
||||
|
17
attr.c
17
attr.c
@ -497,6 +497,7 @@ static int git_attr_system(void)
|
||||
static void bootstrap_attr_stack(void)
|
||||
{
|
||||
struct attr_stack *elem;
|
||||
char *xdg_attributes_file;
|
||||
|
||||
if (attr_stack)
|
||||
return;
|
||||
@ -515,13 +516,15 @@ static void bootstrap_attr_stack(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (git_attributes_file) {
|
||||
elem = read_attr_from_file(git_attributes_file, 1);
|
||||
if (elem) {
|
||||
elem->origin = NULL;
|
||||
elem->prev = attr_stack;
|
||||
attr_stack = elem;
|
||||
}
|
||||
if (!git_attributes_file) {
|
||||
home_config_paths(NULL, &xdg_attributes_file, "attributes");
|
||||
git_attributes_file = xdg_attributes_file;
|
||||
}
|
||||
elem = read_attr_from_file(git_attributes_file, 1);
|
||||
if (elem) {
|
||||
elem->origin = NULL;
|
||||
elem->prev = attr_stack;
|
||||
attr_stack = elem;
|
||||
}
|
||||
|
||||
if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
|
||||
|
@ -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,17 @@ 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))
|
||||
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
@ -622,6 +622,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)));
|
||||
@ -711,6 +713,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;
|
||||
}
|
||||
|
||||
|
7
dir.c
7
dir.c
@ -1303,12 +1303,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)
|
||||
void setup_standard_excludes(struct dir_struct *dir)
|
||||
{
|
||||
const char *path;
|
||||
char *xdg_path;
|
||||
|
||||
dir->exclude_per_dir = ".gitignore";
|
||||
path = git_path("info/exclude");
|
||||
if (!excludes_file) {
|
||||
home_config_paths(NULL, &xdg_path, "ignore");
|
||||
excludes_file = xdg_path;
|
||||
}
|
||||
if (!access(path, R_OK))
|
||||
add_excludes_from_file(dir, path);
|
||||
if (excludes_file && !access(excludes_file, R_OK))
|
||||
if (!access(excludes_file, R_OK))
|
||||
add_excludes_from_file(dir, excludes_file);
|
||||
}
|
||||
|
||||
|
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();
|
||||
|
158
t/t1306-xdg-files.sh
Executable file
158
t/t1306-xdg-files.sh
Executable file
@ -0,0 +1,158 @@
|
||||
#!/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_expect_success 'Setup' '
|
||||
git init git &&
|
||||
cd git &&
|
||||
echo foo >to_be_excluded
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'Exclusion of a file in the XDG ignore file' '
|
||||
mkdir -p "$HOME"/.config/git/ &&
|
||||
echo to_be_excluded >"$HOME"/.config/git/ignore &&
|
||||
test_must_fail git add to_be_excluded
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'Exclusion in both XDG and local ignore files' '
|
||||
echo to_be_excluded >.gitignore &&
|
||||
test_must_fail git add to_be_excluded
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'Exclusion in a non-XDG global ignore file' '
|
||||
rm .gitignore &&
|
||||
echo >"$HOME"/.config/git/ignore &&
|
||||
echo to_be_excluded >"$HOME"/my_gitignore &&
|
||||
git config core.excludesfile "$HOME"/my_gitignore &&
|
||||
test_must_fail git add to_be_excluded
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'Checking attributes in the XDG attributes file' '
|
||||
echo foo >f &&
|
||||
git check-attr -a f >actual &&
|
||||
test_line_count -eq 0 actual &&
|
||||
echo "f attr_f" >"$HOME"/.config/git/attributes &&
|
||||
echo "f: attr_f: set" >expected &&
|
||||
git check-attr -a f >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'Checking attributes in both XDG and local attributes files' '
|
||||
echo "f -attr_f" >.gitattributes &&
|
||||
echo "f: attr_f: unset" >expected &&
|
||||
git check-attr -a f >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'Checking attributes in a non-XDG global attributes file' '
|
||||
test_might_fail rm .gitattributes &&
|
||||
echo "f attr_f=test" >"$HOME"/my_gitattributes &&
|
||||
git config core.attributesfile "$HOME"/my_gitattributes &&
|
||||
echo "f: attr_f: test" >expected &&
|
||||
git check-attr -a f >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' '
|
||||
mkdir -p "$HOME"/.config/git &&
|
||||
>"$HOME"/.config/git/config &&
|
||||
test_might_fail rm "$HOME"/.gitconfig &&
|
||||
git config --global user.name "write_config" &&
|
||||
echo "[user]" >expected &&
|
||||
echo " name = write_config" >>expected &&
|
||||
test_cmp expected "$HOME"/.config/git/config
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '
|
||||
>"$HOME"/.gitconfig &&
|
||||
git config --global user.name "write_gitconfig" &&
|
||||
echo "[user]" >expected &&
|
||||
echo " name = write_gitconfig" >>expected &&
|
||||
test_cmp expected "$HOME"/.gitconfig
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' '
|
||||
test_might_fail rm "$HOME"/.gitconfig &&
|
||||
test_might_fail rm "$HOME"/.config/git/config &&
|
||||
git config --global user.name "write_gitconfig" &&
|
||||
echo "[user]" >expected &&
|
||||
echo " name = write_gitconfig" >>expected &&
|
||||
test_cmp expected "$HOME"/.gitconfig
|
||||
'
|
||||
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user