Multiple Identity Gitconfig (with GPG signing)
Tags: git, gpg, gitconfig, identity
Published: 2019-10-29 16:12:40

Have you ever had these problems like I did?

* You work with multiple groups or companies, or you want to separate your work commits from your personal commits.
* You want to GPG sign all your commits.
* You do not put your personal GPG keys on your work machine, and you do not put your work GPG keys on your personal machine.
* You use a different GPG identity (and perhaps, email) for different repository.
* You synchronize your dotfiles regularly. It contains your GPG configuration as well as your global .gitconfig and use the same dotfiles repository on both your work machine and your personal machine.

If you did you'll find out that you now have a conflict in your `.config/git/config` file, namely in the `[user]` section.

I have mine, looking something like this:

```ini
[user]
  name       = Chakrit Wichian
  email      = service@chakrit.example.net
  signingkey = E888C1BC6A4DB2F4  
```

So this tells git, for all my **personal projects**, to use my **personal email** and **personal GPG key** to sign it.

![](https://chakrit-public.us-east-1.linodeobjects.com/2019-10-29-verified-commits.png)

This all works nice and well but then, how do we setup our work accounts?

Previously I have used [GNU Stow](https://www.gnu.org/software/stow/) with multiple `git` folder in my dotfiles repository and only `stow` the one I needed into place.

```sh
# on personal machine
cd dotfiles && stow git-personal

# on work machine
cd dotfiles && stow git-work
```

Obviously, though, this approach has an annoying flaw: I cannot synchronize changes easily across all git installations I use.

That got me thinking. What is the *BEST* approach to this problem? Usually other configuration system have a way to `include` files so that you can split them into bits and keep most of the shared configuration items the same in the main file and include the parts that are different from other files.

I could modify my workflow so that I have a main configuration that is shared for all installations and have another `supplemental` configs just for the specific machine I happen to be on.

```sh
# shared configuration
cd dotfiles && stow git-base && cd ~
tree

.
└── .config
    ├── git
    │   ├── config # ← contains an include directive.
    │   └── ignore
    └── hub

2 directories, 3 files

# selecting the right "extra" to install
cd dotfiles && stow git-company-x && cd ~
tree

.
└── .config
    ├── git
    │   ├── config
    │   ├── extras # ← contains extras, for company X
    │   └── ignore
    └── hub

2 directories, 4 files
```

So after realizing this, I began to search for git configuration documentation, seeing if this functionality is available.

To my surprise, the [git manual on includes](https://git-scm.com/docs/git-config#_includes) has an [*EVEN BETTER* solution](https://git-scm.com/docs/git-config#_conditional_includes) lying in wait for me with the `includeIf` directive.

# Conditional Includes

Git supports a neat way of specifying an `includeIf` condition by selecting based on the **location of the .git dir**. And better yet, it also supports `**` directory globbing.

This means that you can structure your workspace such that all of Company X's work are under a dir named `company-x` and then using a condition matching `**/company-x/**/.git` to match all git checkouts under that dir.

**First**, if you havn't already, you need to reorganize your repos based on where you work (or how you want to apply gitconfigs):

```sh
# example directory tree under your `git/` or something.
tree

.
├── company-x
│   ├── hello-world
│   └── awesome-project
└── personal
    ├── hello-world
    └── awesome-learning
```

**Second**, create multiple git configuration files for each different situation.

```ini
# config.personal
[user]
  name = Chakrit Wichian
  email = service@chakrit.example.net
  signingkey = E888C1BC6A4DB2F4

# config.company-x
[user]
  name = Chakrit Wichian
  email = chakrit@company-x.example.com
  signingkey = 3CDC219617574C11
```

**Third**, in your main git configuration file, adds as many `includeIf` directive as needed:

```ini
# default case
[include]
  path = config.personal

# when working with company-x
[includeIf "gitdir:**/company-x/**/.git"]
  path = config.company-x
```

**Lastly**, save all files and test them out!

```sh
cd chakrit/secret-project-x
git config user.email
service@chakrit.example.net # ← outputs personal email!

cd ../../company-x/awesome-product-y
git config user.email
chakrit@company-x.example.com # ← outputs work email!
```

Some other uses that I think is cool follows:

### Automatically switching `hub` context.

If the companies you work for uses [GHE](https://github.com/enterprise). You could point the [`hub` CLI](https://github.com/github/hub) to different domains base on where the repo is placed on your system.

```ini
# config.company-x
[hub]
  host = git.company-x.example.com

# config.company-y
[hub]
  host = git.company-y.example.com
```

### Different signing settings base on branch.

Aside from the `gitdir:` matching condition, you can also use `onbranch:` to setup branch-specific configs!

If you have specific branch system setup, for example `master` for production commits, you could set it up so that GPG signing is only done there and only require commits on that branch to have proper verification.

```ini
# config
[commit]
  gpgsign = false
[includeIf "onbranch:master"]
  path = config.master

# config.master
[commit]
  gpgsign = true
```

Thanks for reading! Ping me on Twitter and say hi :)