Git: conditional configurations
More than five years ago Jeff King has added include directive to git config subprogram. This directive allows splitting configuration file across multiple files. A nice feature by itself was boosted in Git 2.13.0 by adding conditional includes - one of my favourite features of this release. And something that makes me sad.
Consider a simple example of ~/.gitconfig
file.
# content of ~/.gitconfig
[core]
editor = emacsclient
[user]
useconfigonly = true
[include]
path = ~/.gitconfig_machine_specific
path = ~/.gitconfig_sensitive
# include ~/.gitconfig_personal only when active repository is under
# ~/Projects/personal/
[includeIf "gitdir:~/Projects/personal/"]
path = ~/.gitconfig_personal
# include ~/.gitconfig_work only when active repository is under
# ~/Projects/work/
[includeIf "gitdir:~/Projects/work/"]
path = ~/.gitconfig_work
As you can see, using include
and includeIf
directives allows to achieve following things.
- File
~/.gitconfig
can be shared among different computers as machine specific configurations are included as a separate file (~/.gitconfig_machine_specific
). - File
~/.gitconfig
can be made public as all sensitive configurations are included as a separate file (~/.gitconfig_sensitive
). - You can set some configurations based on git repository location (e. g. personal or work).
Please note that these directives can be used in any git configuration level (system level, user level, repository level or individual command invocation). Which makes it even more powerful.
Also, note that the only condition implemented now is gitdir
that matches against repository path.
The same but different
Almost a year ago I have created a tool named git-config-manager. The idea is simple1, you have several configuration schemes which you may apply on a repository level. For example, you may use one name, email address and commit sign key when working on open source projects and totally different identity when committing to your day job repository. You just ask the tool to set one or several schemes in current repository and it loops through key-values and sets them using git config
. Nothing fancy.
With git-config-manager
you define schemes in a single json
file.
{
"personal": {
"user": {
"name": "Drunk Monkey",
"email": "drunk.monkey@protonmail.ch",
"signingkey": "A1B2C3D4"
},
"pull": {
"rebase": true
}
},
"work": {
"user": {
"name": "Mr. Tie",
"email": "tie@corp.me",
"signingkey": "E5F6G7H8"
},
"pull": {
"rebase": null
}
}
}
When you ask git-config-manager
to set configurations from the personal
scheme ($ git-config-manager set personal
), it takes the definition and calls git config
for every configuration key-value pair. This is really straightforward.
But the simple approach has several drawbacks. First of all, you have to manually call git-config-manager set scheme
(from the terminal or using the magit
extension). While with includeIf
directory everything is handled automatically. Secondly, if you wish to change configuration piece in all affected repositories (e. g. update sign key in all work projects), git configuration system allows you to make the change in one place (in the ~/.gitconfig_work
file) and it will be automatically propagated to all affected repositories. With git-config-manager
it’s not that simple because it just sets values when it’s asked to set.
What’s the point?
When I learned about includeIf
directory I thought that the time has come to deprecate git-config-manager
. But then I realised that while they overlap in use cases, I might implement several new features I’ve been thinking about lately.
So again, stay tuned. Meanwhile, please read about other changes in Git 2.13.0.
The idea is simple, but solution is slightly over-engineered. Mostly because I didn’t know about include directive in the first place. And partly because I wanted to write it in Haskell.↩︎