Shell variable expansion in git config

I have a shell variable which points to the directory where all my configuration files are located. Let's assume the variable is created with export RC=$HOME/rc. I have a global ignore file in the configuration directory: ~/rc/globalgitignore.

My Question is, how can I expand the RC variable in my .gitconfig file?

I already tried the following:

  • excludesfile = $RC/globalgitignore

  • excludesfile = !$RC/globalgitignore

  • excludesfile = !echo $RC/globalgitignore

  • excludesfile = !$(echo $RC/globalgitignore)

None of these solutions work.

The only way this works if I enter the full path: excludesfile = ~/rc/globalgitignore, but then I have to change the path if move my rc directory to a different location.


Solution 1:

You can't. git-config(1) does not support environment variable expansion, but only limited type conversion and path expansion:

The type specifier can be either --int or --bool, to make git config ensure that the variable(s) are of the given type and convert the value to the canonical form (simple decimal number for int, a "true" or "false" string for bool), or --path, which does some path expansion (see --path below). If no type specifier is passed, no checks or transformations are performed on the value.

The documentation for --path states:

--path

git-config will expand leading ~ to the value of $HOME, and ~user to the home directory for the specified user. This option has no effect when setting the value (but you can use git config bla ~/ from the command line to let your shell do the expansion).

The term "expansion" does not appear in any different context in git-config(1). So how did you even get the idea that it should, given that no such feature is documented anywhere?

In order to expand environment variables you have to pre-process the Git config file yourself, i.e. by creating a template file, and expand variables with a script before copying the file to your $HOME directory.

If it's about dotfile management, then do, what all people do: Put them in a directory, and add symlinks to this directory from your $HOME.

Solution 2:

I use bash scripts in my config to enable variable expansion. Just export the variable you need in your .bashrc and use it in the scripts:

In my ~/.bashrc:

export TESTVARIABLE="hello"

In my ~/.gitconfig:

[alias]
    test = !bash -c '"echo \"Value: $TESTVARIABLE\";"'

At my bash prompt:

bash> git test
    Value: hello 

The other way of doing it, is to add a git config command in your shell's rc. I for instance have in my .bashrc:

git config --global user.name "$USER@$HOSTNAME"

I have the same configuration on all my machines, and by adding this I can distinguish between commits from different machines. You could do the same and add to your shell rc:

export RC="$HOME/rc"
git config --global core.excludesfile "$RC/globalgitignore" 

Solution 3:

With Git 2.31 (Q1 2021), you might consider using configuration variable-value pairs via environment variables (and it tweaks the way GIT_CONFIG_PARAMETERS encodes variable/value pairs to make it more robust too).

See commit d8d7715, commit b9d147f, commit 1ff21c0, commit b342ae6, commit ce81b1d (12 Jan 2021), and commit b0812b6 (07 Jan 2021) by Patrick Steinhardt (pks-t).
See commit f9dbb64, commit 13c4495 (12 Jan 2021) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 294e949, 25 Jan 2021)

config: add new way to pass config via --config-env

Co-authored-by: Jeff King
Signed-off-by: Patrick Steinhardt

While it's already possible to pass runtime configuration via git -c <key>=<value>(man), it may be undesirable to use when the value contains sensitive information.
E.g.
if one wants to set http.extraHeader to contain an authentication token, doing so via -c would trivially leak those credentials via e.g. ps(1), which typically also shows command arguments.

To enable this usecase without leaking credentials, this commit introduces a new switch --config-env=<key>=<envvar>.

Instead of directly passing a value for the given key, it instead allows the user to specify the name of an environment variable.
The value of that variable will then be used as value of the key.

git now includes in its man page:

[--super-prefix=<path>] [--config-env <name>=<envvar>]

git now includes in its man page:

--config-env=<name>=<envvar>

Like -c <name>=<value>, give configuration variable '<name>' a value, where <envvar> is the name of an environment variable from which to retrieve the value.

Unlike -c there is no shortcut for directly setting the value to an empty string, instead the environment variable itself must be set to the empty string.

It is an error if the <envvar> does not exist in the environment. <envvar> may not contain an equals sign to avoid ambiguity with <name>s which contain one.

This is useful for cases where you want to pass transitory configuration options to git, but are doing so on OS's where other processes might be able to read your cmdline (e.g. /proc/self/cmdline), but not your environ (e.g. /proc/self/environ).
That behavior is the default on Linux, but may not be on your system.

Note that this might add security for variables such as http.extraHeader where the sensitive information is part of the value, but not e.g. url.<base>.insteadOf where the sensitive information can be part of the key.

In your case, test it out with:

git --config-env=core.excludesfile=RC config core.excludesfile
# or (Git 2.32+ only)
git --config-env core.excludesfile=RC config core.excludesfile

The value for core.excludesfile should be $RC (which should reference the full file path, not just its parent folder though)


Note: Before Git 2.32 (Q2 2021), "git --config-env var=val cmd"(man) were not accepted (only --config-env=var=val was).

See commit c331551, commit 9152904 (29 Apr 2021) by Patrick Steinhardt (pks-t).
(Merged by Junio C Hamano -- gitster -- in commit 5f586f5, 07 May 2021)

git: support separate arg for --config-env's value

Signed-off-by: Patrick Steinhardt
Reviewed-by: Jeff King

While not documented as such, many of the top-level options like --git-dir and --work-tree support two syntaxes: they accept both an equals sign between option and its value, and they do support option and value as two separate arguments.
The recently added --config-env option only supports the syntax with an equals sign.

Mitigate this inconsistency by accepting both syntaxes and add tests to verify both work.


But there is more, still with Git 2.31:

config: allow specifying config entries via envvar pairs

Signed-off-by: Patrick Steinhardt

While we currently have the GIT_CONFIG_PARAMETERS environment variable which can be used to pass runtime configuration data to git processes, it's an internal implementation detail and not supposed to be used by end users.

Next to being for internal use only, this way of passing config entries has a major downside: the config keys need to be parsed as they contain both key and value in a single variable.
As such, it is left to the user to escape any potentially harmful characters in the value, which is quite hard to do if values are controlled by a third party.

This commit thus adds a new way of adding config entries via the environment which gets rid of this shortcoming.
If the user passes the GIT_CONFIG_COUNT=$n environment variable, Git will parse environment variable pairs GIT_CONFIG_KEY_$i and GIT_CONFIG_VALUE_$i for each i in [0,n).

While the same can be achieved with git -c <name>=<value>(man), one may wish to not do so for potentially sensitive information.
E.g.
if one wants to set http.extraHeader to contain an authentication token, doing so via -c would trivially leak those credentials via e.g. ps(1), which typically also shows command arguments.

git config now includes in its man page:

GIT_CONFIG_COUNT

GIT_CONFIG_KEY_<n>

GIT_CONFIG_VALUE_<n>

If GIT_CONFIG_COUNT is set to a positive number, all environment pairs GIT_CONFIG_KEY_<n> and GIT_CONFIG_VALUE_<n> up to that number will be added to the process's runtime configuration.

The config pairs are zero-indexed.
Any missing key or value is treated as an error. An empty GIT_CONFIG_COUNT is treated the same as GIT_CONFIG_COUNT=0, namely no pairs are processed.
These environment variables will override values in configuration files, but will be overridden by any explicit options passed via git -c.

This is useful for cases where you want to spawn multiple git commands with a common configuration but cannot depend on a configuration file, for example when writing scripts.

For example:

    GIT_CONFIG_COUNT=2 \
        GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
        GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
        git config --get-regexp "pair.*" 

will print:

pair.one foo
pair.two bar