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.email values afterwards.
I got some more excellent feedback from u/tpenguinltg on Reddit on an alternative way to do things that's even *more* centralized and easy to manage, via git configs. Huge thanks to u/tpenguinltg, that tip might be better than this entire post -- scroll to the bottom (or click the link in the previous sentence) for the goods!
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 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 (
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 firstname.lastname@example.org -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.
~/.ssh/configfor 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.
To test your settings (and add your keys to
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 email@example.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.
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
github.com and uses the right
IdentityFile (no need to guess!).
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
Continuing with the “acme” example, the commands would look like this:
$ git config --local user.name vados-acme $ git config --local user.email firstname.lastname@example.org
There’s also GPG signing of commits which I won’t get into, but you should also set up.
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 = email@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.
~/.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
[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
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!