Using git with multiple identities

17 Nov 2023 10:23 git

I want to keep my work and personal Github accounts separate. Here’s how I set that up:

Background

I’ve had a personal Github account – @rlipscombe – since 2010. In the past when I’ve needed to use Github for work, I’ve just used that account and added my work email to it.

This time, my work account is going to be managed by Corporate IT, and they’ve got more stringent security and sign-on requirements, and I don’t feel comfortable blurring that boundary, so I’m going to set up a separate Github account.

Because I still want to be able to access both my personal and work accounts from my work laptop, I need to set up git to manage multiple identities.

Create another SSH Key

Github doesn’t let you attach the same SSH public key to multiple accounts, so you’ll need to have one keypair per identity.

ssh-keygen -t ed25519 -f ~/.ssh/id_work -C "$USER@$(hostname)-Work"

Github has documentation for this process here, and you can add it to your new account by following the docs here.

My company requires that the SSH key be authorized for SSO, which is something I’ve never seen before, but it’s just a couple of clicks, so that’s easy enough.

Using the alternative SSH key

To use the new (non-default) SSH key, you need to use the GIT_SSH_COMMAND environment variable:

GIT_SSH_COMMAND='ssh -i ~/.ssh/id_work -o IdentitiesOnly=yes' git clone git@github.com:work-org/some-repo.git

I can see this getting very annoying very quickly.

I did some poking around on the Internet, and I can identify a few options, with varying effectiveness.

Use a separate SSH configuration, with a made up host name

I don’t like this option.

Put something like this in your ~/.ssh/config file:

Host work.github.com
    Hostname github.com
    IdentityFile ~/.ssh/id_work
    IdentitiesOnly yes

Then this will work:

git clone git@work.github.com:big-company/boring-crud.git

…and you can continue using your default identity otherwise. If you have multiple non-default identities, you can add extra prefixes:

# ...

Host personal.github.com
    Hostname github.com
    IdentityFile ~/.ssh/id_personal
    IdentitiesOnly yes
git clone git@personal.github.com:you/exciting-side-project.git

That’s still a bit annoying, though, because you need to remember to add work. or personal. prefixes to select between your two identities.

If you’re using the Code dropdown button in the Github UI, you have to remember to edit it every time. If someone sends you a repo URL in Slack, you have to remember to edit it, etc., etc.

Using includeIf

This is my preferred option.

The idea here is that your ~/.gitconfig looks like this (the trailing slashes on the paths are required):

[includeIf "gitdir:~/Source/Work/"]
	path = ~/Source/Work/.gitconfig
[includeIf "gitdir:~/Source/Personal/"]
	path = ~/Source/Personal/.gitconfig

And then you have ~/Source/Work/.gitconfig that looks like this:

[core]
    sshCommand = ssh -i ~/.ssh/id_work -o IdentitiesOnly=yes

…and so on, for each of your identities. It means that you need to keep your work-related and personal projects in separate directories (until now, I just stuck everything in ~/Source).

Other rejected options

  • Using something like direnv to switch out the GIT_SSH_COMMAND environment variable when you change directories.
    • This would also work for the GIT_AUTHOR_EMAIL, GIT_COMMITTER_EMAIL, etc. variables.
    • I rejected this because it would require all of my .envrc files to remember to have source_up directives in them, and forgetting it just once would break things in weird ways.
  • Replacing git clone with something that set the environment variable appropriately, and then set core.sshCommand in the newly-checked-out .git/config file.
    • git won’t let you replace built-in commands with aliases, so I’d have to use a shell alias or function.
    • I rejected this because I thought it might be fragile.
  • I was previously using https://github.com/DrVanScott/git-clone-init, but that only works to patch the configuration after you’ve cloned the repo.