Have you ever had the problem where you need different git settings depending on the repo you are working with? Or have you ever wanted to customize something about your workflow for only a subset of your local repos? I have these problems frequently!

In this post I am going to show you some tricks for working with multiple configuration files and the structure I use to do this. Along the way I’m also going to show you some other great features about git!

The Problem

If you are like me, you may have side projects, personal projects and everyday work. All of these require different git configurations, credentials, githooks and any other number of differences.

This has led to commits under the wrong author, the wrong hooks being applied, etc. It can get downright confusing! So how does one fix this once and for all? The answer lies in .gitconfig

First, let’s start with the simplest configuration. This sets up my identity for authoring commits.

[user]
    name  = Ross Edman
    email = "my.personal.email@gmail.com"

On my local machine I will be authoring those commits against a directory of code repositories in the structure shown below.

~/code
├── github.com
│   ├── rossedman # requires personal email
│   │   ├── repo1
│   │   ├── repo2
│   │   └── repo3
└── git.jobcorp.com # requires work email
    └── team
        ├── repo1
        ├── repo2
        └── repo3

Each of these repos require different author emails for the commits to be valid. I don’t want to author commits at work under my personal email. The outcome I want is when I’m under ~/code/git.jobcorp.com/team/* I need my ~/.gitconfig to become:

[user]
    name  = Ross Edman
    email = "my.work.email@gmail.com"

Luckily, git has numerous ways to solve this problem! Let’s dig in.

The Big Easy

The first way to solve this problem is the easiest and least scalable, just set the config each time you init a new repo, like this:

git config user.email "my.work.email@gmail.com"

That doesn’t seem to hard but forget a few a times or do it hundreds of times and you will realize this isn’t great and doesn’t cover more complicated needs.

A Little Better

The second way to solve this is by using git-templates. These templates are copied whenever you clone or init a new repo. You could have two template repos that you could init from like this.

~/my-git-templates
├── personal
│   ├── .gitconfig
└── work # requires work email
    └── .gitconfig

And to use these you could run

git clone --template ~/my-git-templates/work git.jobcorp.com/team/repo1.git

This gets you somewhat further but still feels clunky and error prone because you have to remember to use the --template flag.

Conditional Includes

Now to our final answer, conditional includes! Hooray! In git there is a declaration called includeif in the git config syntax that does exactly what we need.

You can include a config file from another conditionally by setting a includeIf..path variable to the name of the file to be included.

Here is an example of how this would be used in a .gitconfig file

[includeIf "gitdir:~/path/to/recognize/"]
    path = ~/path/to/git/config

This would include the config ~/path/to/git/config in any git repo under this directory: ~/path/to/recognize/*. That’s pretty slick.

Referencing our code layout structure from before, we only want to change our email when working under ~/code/git.jobcorp.com/. Taking the example from above, our .gitconfig should then become:

[user]
    name  = Ross Edman
    email = "my.work.email@gmail.com"

+[includeIf "gitdir:~/code/git.jobcorp.com/"]
+    path = ~/.gitconfig.work

And then create a new file, ~/.gitconfig.work that has.

[user]
    email = "my.work.email@gmail.com"

Notice this has very little in the file. That’s because the original file is merged with this and only the user.email reference is overwritten when in the work directories. Now if you commit from a personal and work directory you should see different emails in the git log but the same user.name!

More Patterns

The use case above doesn’t seem complex but what other patterns could we use this for?

Conditional GPG Keys

In some cases you may be required to uses GPG keys just for certain email addresses, maybe your personal one doesn’t require one, but your work email does. We could solve this by add this to our .gitconfig.work file

[user]
    email = "my.work.email@gmail.com"
+   signingkey = "3AA5C34371567BD2"

This would then load our signing key just for work commits! So secure!

Conditional Githooks

I use global hooks (more posts on that later) and they may be different per the environment I’m working on. Maybe at work you are expected to format your PRs a certain way and you want a git hook that applies to all code in ~/code/git.jobcorp.com/. How would we accomplish this?

In your .gitconfig.work you could add

[user]
    email = "my.work.email@gmail.com"

+[core]
+    hooksPath = ~/.githooks.work

Under the path ~/.githooks.work you could then add your commit-msg hooks or whatever you need!

Conclusion

As you can see, this pattern is very powerful for managing your workflow across many different teams, projects and environments. Since I have added this to my workflow I have not had to reauthor a git history and I feel more knowledgable about ways I can leverage git to help fit my workflow. I hope this helps you as well!