tl;dr - Easily manage multiple git personas by naming config stanzas with aliases in your ~/.ssh/config and the names when you git clone (i.e. git clone git@your-alias:user/project.git . Or, you could just use HTTPS to clone. Either way, make sure to use git config --local to change the user.name and user.email values afterwards.

You’d think in 2019, after over a decade of using git I’d be super comfortable with managing identities for different public/private projects in Git, but the other week I realized I wasn’t – in particular, I mistakenly used my personal identity to commit to a private project that I was doing for a client. Looking into it, It took me tens of minutes to find the “right” way to manage this, and a lot of the answers were very non-satisfactory.

This is going to be a quick post going over one way I’ve found very useful, in particular naming your ~/.ssh/config config stanzas, and using the names in there to actually connect. It’s tedious, but it gives me good separation between projects, with a good predictable system. The interplay between ssh and git ( ssh is used by git ) makes this a little tricky and can be confusing, and while ssh is supposed to try all the keys it has, you can actually run into issues where ssh tries too many keys.

Before we start, I want to point out that there’s a very easy solution – you could just use HTTPS. For some projects I did that, but it felt like a cop-out, since I didn’t take the time to understand and solve the problems with the tools at hand ( git and ssh ).

Step 0: Generate one or more SSH keys

Of course, if we’re going to be managing SSH identities, we’re going to need some keys! I personally like to do the following:

$ mkdir ~/.ssh/<company-or-project-name> # make an on-disk folder for the company/project $ ssh-keygen -t rsa -b 2048 -C <email address> -f ~/.ssh/<company-or-project-name>

Here’s a worked example, if I was working with a company called “acme”.

$ mkdir ~/.ssh/acme $ ssh-keygen -t rsa -b 2048 -C vados+acme@vadosware.io -f ~/.ssh/acme/id_rsa

NOTE I prefer to get prompted for the password, you could submit it with the -N option as well. Check out the ssh-keygen MAN page for more options.

It’s hard to remember all the arguments you need for ssh-keygen every time, but you can get by with the minimal ssh-keygen -t rsa -b 2048 and let it ask you for the rest. Be very careful not to overwrite your personal one (usually @ ~/.ssh/id_rsa ) during the prompts, since it’s the default location that shows up. It might even be a good idea to move that to a folder like ~/.ssh/personal or something.

Step 1: Modify ~/.ssh/config for the new persona(s)

Now that you’ve got some new persona(s) to integrate, let’s modify your SSH configuration. Keeping with the “acme” example used above, we’ll need to add a config stanza that looks like this:

Host * ServerAliveInterval 240 AddKeysToAgent yes IdentitiesOnly yes # ... other stuff ... Host acme-gitlab Hostname gitlab.com User git IdentityFile ~/.ssh/acme/id_rsa AddKeysToAgent yes

This will add a alias (basically a fake address) that ssh will recognize called acme-gitlab with the settings above.

Step 2: SSH to the repository, using the SSH host

To test your settings (and add your keys to ssh-agent since AddKeysToAgent was specified), you can ssh to the your repository hosts:

$ ssh git@acme-gitlab PTY allocation request failed on channel 0 Welcome to GitLab, @<user>! Connection to gitlab.com closed.

And here’s what it looks like for Github:

$ ssh git@github.com PTY allocation request failed on channel 0 Hi <user>! You've successfully authenticated, but GitHub does not provide shell access. Connection to github.com closed.

Assuming you see a message like the above, you’re good to go. If not, you’ll need to go back and check your configuration.

Step 3: Pull from your repository

Now we can finally do what we set out to do – you can pull the git repository like so:

$ git clone git@acme-gitlab:<owner>/<project>.git ... git output ...

Under the covers, ssh does the translation for acme-gitlab to github.com and uses the right IdentityFile (no need to guess!).

Step 4: Set up your local persona

In a perfect world we could have ended at Step 3, but there’s the small issue of which identity commits get attributed to. In particular, we need to use git config with the --local option in the cloned repository to set the identity for the repository. Unfortunately I haven’t found a good way to do this in a centralized way, but for now I just remember to do it after I git clone .

Continuing with the “acme” example, the commands would look like this:

$ git config --local user.name vados-acme $ git config --local user.email vados+acme@vadosware.io

There’s also GPG signing of commits which I won’t get into, but you should also set up.

Git Config Alternative

Thanks to u/tpenguinltg on Reddit for offering another solution – editing files like ~/.config/git/config-<project> to manage the SSH command that’s used. I do like to organize my projects by folders (for example $WORK/<company>/<repo> ), so this is also a great way to do it, and possibly even better than the approach above.

Paraphrasing from the comment: > For example, suppose I have a config for this account that looks like this in ~/.config/git/config-tpenguinltg:

[user] name = tPenguinLTG email = tpenguinltg@example.com signingKey = 931XXZ0V214U7W47 [core] sshCommand = ssh -i ~/.ssh/example-tpenguinltg -F /dev/null [credential "https://github.com"] username = tpenguinltg [credential "https://gist.github.com"] username = tpenguinltg

This config sets my name and email to the proper values, sets my username to tpenguinltg for GitHub over HTTP and uses the key located at ~/.ssh/example-tpenguinltg over SSH, and sets my GPG signing key.

And in ~/.gitconfig you must place a line like this:

[IncludeIf "gitdir:~/dev/tpenguinltg"] path = ~/.config/git/config-tpenguinltg

NOTE If you want the configuration to apply to every subdirectory of a given folder, make sure to add a trailing / (so [IncludeIf "gitdir:~/path/to/container/folder/"] – See the Git documentation for more details.

I think this is even better than my solution because it widely applies under a directory, which is fantastic for multiple repositories which are logically connected somehow (usually by the company/organization they’re for). As u/tpenguinltg noted in the comment, this setup does require Git 2.13 (from mid 2017), but I assume most people these days are using recent versions of git .

Wrapup

This was a short one – hopefully it helps at least one frustrated developer out there.

If you know a better way of handling this stuff (preferably without introducing too many new tools), please feel free to contact me and I’ll update the post so we can share this knowledge!