Git and SSH
When using Git with SSH to connect a Git client to a Git server, these are the tricks to handle multiple Github/Gitlab accounts and servers.
I wrote this because I got tired of managing a chaotic environment with
- numerous Git accounts (Github, Gitlab, Bitbucket),
- many SSH keys tied to different identities,
- and many different Git servers to connect to.
This guide assumes you're in a Unix/Linux environment or something like cygwin.
Overview
- Separate folders when cloning repos so you don't mix up user handles across accounts.
- Separate .gitconfig files to set different Git identities (name, email) per folder.
- Separate SSH keys for authenticating as different Git users.
- Separate SSH configuration to route connections to different Git servers with different keys.
- (Optional) Automate cloning and pulls to keep a record of what's in each folder.
Organize folder structure
I keep a single code folder for all code. Inside it, I create subfolders matching the Git servers and profiles I use. For example, say user1 is the username.
You can have multiple folders for different organizations and projects. Each organization maps to a Git server (Github, Gitlab, etc.) or an organization within one (like https://github.com/google). Each project underneath is a Git repository.
Once the folders are set up, you can configure Git identity to tie your username and email to each organization folder.
/home/user1/code
├── org1
│ ├── project1
│ ├── project2
├── org2
│ ├── project1
│ ├── project2
├── org3
│ ├── projectz
├── org4
│ ├── project9
│ ├── projectb
Git config identity
Most people set their Git identity once, globally, because Git commits require it. Git servers like GitHub also verify commits via the config email to show a Verified badge.
The usual way is via the command line:
Note
GitHub recommends using a noreply email they provide so spammers can't harvest your commit email.
To automate git config across the folder structure from earlier, create a gitconfig file at the root of your home directory. It has no file extension -- just a plain text file.
For example, my .gitconfig file is at ~/.gitconfig or /home/user1/.gitconfig.
Then add includeIf clauses pointing at each folder. The path after gitdir: must end in / to match a directory. This tells Git: when working inside ~/code/org1/, use the config file .gitconfig-org1.
[includeIf "gitdir:~/code/org1/"]
path = .gitconfig-org1
[includeIf "gitdir:~/code/org2/"]
path = .gitconfig-org2
[includeIf "gitdir:~/code/github-org1/"]
path = .gitconfig-github-org1
[includeIf "gitdir:~/code/github-org2/"]
path = .gitconfig-github-org2
[includeIf "gitdir:~/code/gitlab-org1/"]
path = .gitconfig-org1
[core]
attributesfile = .gitattributes
The .gitconfig-org1 file just has the user's name and email:
Note
If you want to keep your email private on Github, go to Github email settings and find the noreply address they give you.
SSH keys
SSH keys are an alternative to passwords for authenticating to an SSH server. Typing passwords every time is annoying, and the security tradeoffs between passwords and keys are debated.
To generate an SSH keypair, I prefer ED25519 keys -- they're faster and smaller than RSA keys.
Otherwise, to generate an RSA 4096-bit keypair:
Note
RSA 2048-bit keys are still widely used until standards phase them out in 2030. They're about 4x faster than RSA 4096-bit keys, while 4096-bit keys offer slightly better encryption strength.
(Optional) You can add a passphrase for extra security. To avoid retyping it, cache it with an SSH agent.
To start the SSH agent:
(Optional) Add your SSH key to the agent:
By convention, the private key file has no extension and the public key file ends in .pub.
SSH configurations
The SSH config file determines which keypair and identity to use when connecting to a Git server. It binds a specific key to a specific host.
The config file lives at ~/.ssh/config.
I set each Host entry to target a specific HostName (github.com, gitlab.com), use git as the User, prefer publickey authentication, and point IdentityFile at the right private key. You can have as many Host entries as you need.
Host github.com-org1
HostName github.com
User git
PreferredAuthentications publickey
IdentityFile ~/.ssh/git/github_org1_ed25519
IdentitiesOnly yes
Host github.com-org2
HostName github.com
User git
PreferredAuthentications publickey
IdentityFile ~/.ssh/git/github_org2_ed25519
IdentitiesOnly yes
Host gitlab.com-org1
HostName gitlab.com
User git
PreferredAuthentications publickey
IdentityFile ~/.ssh/git/gitlab_org1_ed25519
IdentitiesOnly yes
Automate cloning and pulls
clone.sh clones a list of repositories into a folder.
Note
Notice that git@github.com-org1 matches the Host field in the SSH config. This is how the Git client knows which SSH key to use for which server. The SSH config ties the Git user, the SSH key, and the Git server together.
#!/usr/bin/env bash
git clone git@github.com-org1:fartbagxp/aas-cidr-ranges.git
git clone git@github.com-org1:fartbagxp/git-and-ssh.git
git clone git@github.com-org1:fartbagxp/asdf-oauth2c.git
Following the folder structure, you might have another clone.sh for a different organization:
#!/usr/bin/env bash
git clone git@gitlab.com-org1:dwt1/dotfiles.git
git clone git@gitlab.com-org1:wireshark/wireshark.git
(Optional) To pull all repos in a folder, pull.sh updates everything. This will cause conflicts if there are unmerged changes!
Miscellaneous tricks
Cloning or pushing to a Git server can get tricky depending on how your client connects (sometimes through firewalls) for the first time.
Here are some debugging tips for both SSH and HTTPS connections.
Debugging SSH on Git
The default git clone <repository> doesn't show useful error messages when SSH connections fail.
To get debug output, prepend GIT_SSH_COMMAND:
For even more detail:
Debugging HTTPS on Git
If the Git server or a firewall blocks SSH, you'll need to use HTTPS instead.
Use nc and curl first to check connectivity.
Test whether you can reach the Git server on SSH port 22:
If this fails and your internet works otherwise, a firewall is probably blocking SSH, or the server won't accept SSH connections from you.
Test HTTPS connectivity:
You should see a wall of HTML fetched from the site. If it fails or hangs, something is blocking HTTPS too, and there's not much else to try.
If HTTPS works, clone over it:
You can also inline the password (less secure -- it gets stored in shell history):
A third option is a Personal Access Token, which most Git servers support:
SOCKS proxy with Git
A SOCKS proxy can get around firewalls blocking HTTPS to the Git server.
Once you have a SOCKS proxy running, clone through it like this. This example uses 127.0.0.1 (localhost) on port 8055: