Modular Git Configuration
Introduction
Most of my Git configuration is platform agnostic. I’ve been toting some of these command aliases around with me for over a decade - between Windows, Mac, Linux, and FreeBSD - with no issues. But there are a few things that don’t work when moved to a different operating system:
- External tool configuration
- Identity information
- Editor configuration
Stop using .gitconfig
The first step to having a composable Git configuration is to stop using ~/.gitconfig. Instead, put your Git configuration in ~/.config/git/config. This allows us to dedicate a directory to our Git configuration, rather than just a single file. With a collection of configuration files, we can compose the configuration we need for any environment.
External tool configuration
You may be resolving merge conflicts by manually editing conflict markers like this:
If you have questions, please
<<<<<<< HEAD
open an issue
=======
ask your question in IRC.
>>>>>>> branch-a
If so, configuring a visual difftool and mergetool in Git will change your life.

But not every tool works on every system where Git is installed. I like to use P4Merge. It’s free and runs on Windows, Linux, and MacOS, but it doesn’t help me on systems where I only have a text interface. For those servers, I use vimdiff.
To address these scenarios, I added a few additional configuration files to my ~/.config/git/config folder. These files can have any names you like, but I’ve found that using the .gitconfig extension helps some file editors with syntax highlighting, etc.
p4merge.gitconfig
[diff]
tool = p4merge
[merge]
tool = p4merge
[difftool]
prompt = false
keepBackup = false
[mergetool]
prompt = false
keepBackup = false
[difftool "p4merge"]
cmd = p4merge "$LOCAL" "$REMOTE"
trustExitCode = false
[mergetool "p4merge"]
cmd = p4merge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
trustExitCode = false
p4merge-macos.gitconfig
[diff]
tool = p4merge
[merge]
tool = p4merge
[difftool]
prompt = false
keepBackup = false
[mergetool]
prompt = false
keepBackup = false
[difftool "p4merge"]
cmd = /Applications/p4merge.app/Contents/Resources/launchp4merge "$LOCAL" "$REMOTE"
trustExitCode = false
[mergetool "p4merge"]
cmd = /Applications/p4merge.app/Contents/Resources/launchp4merge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
trustExitCode = false
vimdiff.gitconfig
[difftool]
prompt = false
[mergetool]
prompt = false
[difftool]
prompt = false
keepBackup = false
[mergetool]
prompt = false
keepBackup = false
Identity information
Git keeps track of your name and email so they can be added to commits that you contribute. Most people need to, at least, distinguish between a personal identity and a work identity. If you are a contractor, consultant, or freelancer, you may need more. You probably already know how to configure user.email per-repository, but there is a better way.
Let’s create a few more configuration files - one for each identity.
personal.gitconfig
[user]
name = Neo
email = neo@example.org
work.gitconfig
[user]
name = Thomas A. Anderson
email = tanderson@metacortex.com
signingkey = <signing-key>
[commit]
gpgsign = true
hero.gitconfig
[user]
name = The One
email = theone@example.org
signingkey = <signing-key>
[commit]
gpgsign = true
Notice I’ve included settings for signing commits in these files. They can include any Git configuration that is bound to that identity. In the example above, the personal identity does not sign his commits, but maybe your company has a policy that requires signed commits, so the work identity enables signing by default.
Configuration includes
Git configuration files can contain include and includeIf sections to include directives from other files. The difference between them is that include is unconditional (always included), and includeIf is conditional (dependent on some condition).
The includeIf directive is pretty limited in the conditions it can evaluate, but we will only use one in this example: gitdir.
The gitdir condition will save you a lot of trouble if you organize your local repositories. For example, everything under my ~/work folder should get my work identity, and everything under my ~/dev folder should get my personal identity. This way, I never have to remember to set my user.email. I just place the repository in the right folder, and it inherits the correct configuration.
NOTE: Configuration files are processed top to bottom, and included files are processed at the location where they are defined. The last value encountered for a setting wins.
Composing configuration
We now have a collection of configurations for every scenario:
- Common and default settings (
config) - External tool configurations (
p4merge.gitconfig,vimdiff.gitconfig, etc.) - Identity configurations (
pernonal.gitconfig,work.gitconfig, etc.)
At the bottom of your config file, add this section:
[include]
path = local.gitconfig
Again, the actual file name does not matter. I use local.gitconfig as a reminder that this file does not get synchronized between my systems. Instead, this file should be customized per system.
On my personal Windows PC
# Configure external tools for Windows
[include]
path = p4merge.gitconfig
# Default to personal identity
[include]
path = personal.gitconfig
On a customer Linux server
# Configure external tools for Linux
[include]
path = vimdiff.gitconfig
# Include customer identity
[include]
path = customer.gitconfig
On my work Mac:
# Configure external tools for MacOS
[include]
path = p4merge-macos.gitconfig
# Default to work identity
[include]
path = work.gitconfig
# Include identities
[includeIf "gitdir:~/nebuchadnezzar"]
path = hero.gitconfig
[includeIf "gitdir:~/zion"]
path = personal.gitconfig
[includeIf "gitdir:~/metacortex"]
path = work.gitconfig
Synchronizing configuration
As you can see in the examples above, the only file that needs to be customized per-environment is local.gitconfig. This means that everything else can be synchronized between work, personal, and customer environments, and between desktops and servers. You can synchronize them however you like, but I prefer Git.
Just create a .gitignore file to exclude the local configuration:
# Exclude local configuration
local.gitconfig
Once these are in place, you just push/pull to any centralized repository to keep your Git configuration in sync everywhere.
Editor configuration
Your editor configuration will have the same problems as the difftool and mergetool we looked at above. It is possible to configure the editor using the core.editor setting in your Git configuration, and solve it using the same composable configuration solution.
I prefer to set it using the $VISUAL environment variable, rather than using core.editor, precisely because this is not a Git-specific setting. It sets the editor for all command line utilities. If you like to use VS Code to edit commit messages, you probably want to use it for visudo, sudoedit, and others.
If you really want Git to use a different text editor than the rest of your system, you can set $GIT_EDITOR or set core.editor in your composable configuration. The order of preference is:
$GIT_EDITORcore.editor$VISUAL$EDITOR- Compile-time default (usually
vi)
Here are the appropriate values for many popular text editors:
| Editor | Command |
|---|---|
| Vim | vim |
| NeoVim | nvim |
| Emacs | emacs |
| Nano | nano |
| Micro | micro |
| VS Code | code --wait --new-window |
| Atom | atom --wait -n |
| Sublime Text | subl -n -w |
| Windows Notepad | notepad |
| TextMate | mate -w |
| BBEdit | bbedit -w --new-window |
| TextWrangler | edit -w |
| MacVim | mvim -f -c 'au VimLeave * !open -a Terminal' |
| Xcode | xed -w -n |
| Gedit (GNOME) | gedit -w -s --new-window |
| Kate (KDE) | kate -b -n |
| Mousepad (XFCE) | mousepad -w |
| Leafpad | leafpad -w |
Conclusion
By splitting up our Git configuration into a few different files, and configuring the editor outside of Git, we now have a modular configuration that can be used anywhere. It can be synchronized between operating systems, works on desktops and servers, and even shared between work and personal computers!