DEV Community

Chakrit Wichian
Chakrit Wichian

Posted on • Updated on

Multiple Identity Gitconfig (with GPG signing)

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:

[user]
  name       = Chakrit Wichian
  email      = service@chakrit.example.net
  signingkey = E888C1BC6A4DB2F4  
Enter fullscreen mode Exit fullscreen mode

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

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

Previously I have used GNU Stow with multiple git folder in my dotfiles repository and only stow the one I needed into place.

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

# on work machine
cd dotfiles && stow git-work
Enter fullscreen mode Exit fullscreen mode

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.

# 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
Enter fullscreen mode Exit fullscreen mode

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 has an EVEN BETTER solution 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):

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

.
├── company-x
│   ├── hello-world
│   └── awesome-project
└── personal
    ├── hello-world
    └── awesome-learning
Enter fullscreen mode Exit fullscreen mode

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

# 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
Enter fullscreen mode Exit fullscreen mode

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

# default case
[include]
  path = config.personal

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

Lastly, save all files and test them out!

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!
Enter fullscreen mode Exit fullscreen mode

Some other uses that I think is cool follows:

Automatically switching hub context.

If the companies you work for uses GHE. You could point the hub CLI to different domains base on where the repo is placed on your system.

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

# config.company-y
[hub]
  host = git.company-y.example.com
Enter fullscreen mode Exit fullscreen mode

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.

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

# config.master
[commit]
  gpgsign = true
Enter fullscreen mode Exit fullscreen mode

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

Top comments (4)

Collapse
 
miquelbrazil profile image
Miquel Brazil

I've been looking for something just like this. Def plan on implementing this later today.

Collapse
 
blairnangle profile image
Blair Nangle

Super useful—thank you!

Collapse
 
wardy3 profile image
Wardy

thank you! never noticed this

Collapse
 
zhilyaev profile image
Dmitriy Zhilyaev

Thanks!