Table of contents
To work effectively with services such as GitHub and GitLab, it is useful to set up a workflow that doesn’t get in the way, especially when multiple accounts are involved. SSH will be set up to avoid entering the username/password combination every time we interact with remote repositories and Git will be set up to work differently for each account while signing the commits potentially with different GPG keys.
In short, we need to do the following:
- Set up SSH locally (in
- Set up SSH keys remotely (GitLab, GitHub, Bitbucket, etc.);
- Set up Git locally (in
- Set up GPG keys remotely (add our keys(s) to GitLab, GitHub, etc.);
- Start interacting with remote repositories.
Setting SSH locally
First, let’s make sure we have some SSH keys to work with. The default location is
~/.ssh/id_rsa (you can just press
Enter when asked to save to a path):
$ ssh-keygen -t rsa -C "email@example.com"
The next key should have a different path:
$ ssh-keygen -t rsa -C "firstname.lastname@example.org" $ Enter file in which to save the key (/home/sglavoie/.ssh/id_rsa): /home/sglavoie/.ssh/id_rsa_work
Add the keys to the authentication agent like so:
$ ssh-add /.ssh/id_rsa $ ssh-add /.ssh/id_rsa_work
See which keys were added:
$ ssh-add -l # list the keys
If you need to delete any keys that were cached prior to that:
$ ssh-add -D
Now, we need a configuration file:
$ touch ~/.ssh/config $ chmod 600 ~/.ssh/config
Let’s add some content in there, assuming we deal with GitHub and GitLab, both with a personal account and a work account:
AddKeysToAgent yes Host github HostName github.com User git IdentityFile ~/.ssh/id_rsa Host github-work HostName github.com User git IdentityFile ~/.ssh/id_rsa_work Host gitlab HostName gitlab.com User git IdentityFile ~/.ssh/id_rsa Host gitlab-work HostName gitlab.com User git IdentityFile ~/.ssh/id_rsa_work
- The directive
AddKeysToAgent yesis useful to avoid typing
ssh-add path_to_keyevery time SSH is needed.
Hostcan have any name we want, it doesn’t need to match the
HostNameis the address we need to access. This should be the same thing for all accounts using a particular service (here, GitHub or GitLab).
- We can set the user to be
- For each
Host, we indicate which
IdentityFileto use when trying to work with a remote repository.
Automatically load the keys from the Shell
For Zsh, the following can be added near the top of
~/.zshrc when using the
# Load multiple SSH keys zstyle :omz:plugins:ssh-agent identities id_rsa id_rsa_work
With Oh-My-Zsh, the
ssh-agent plugin should be contained in
plugins like so:
plugins=(git ssh-agent fzf gitignore zsh-autosuggestions history-substring-search) # Somewhere below source $ZSH/oh-my-zsh.sh
With this setup, the SSH keys will be loaded when opening a terminal after booting up and those will be available for any subsequent terminal sessions until the user session is exited.
Setting SSH keys remotely
The process will be slightly different on each platform on which we want to authenticate, but the gist of it is to paste the content of the public SSH key in the field when asked to do so.
Setting Git locally
The idea is to have a
~/.gitconfig file from which we load the main Git configuration by default (let’s say, our personal account) and then we load another account – overwriting the Git settings of the personal account with the settings defined for that other account – whenever we navigate to a directory that relates to that other account. Let’s see this in action.
# default configuration settings to load [include] path = ~/.gitconfig-personal # when working with company-x # those settings are loaded only when the # directory matches the pattern defined [includeIf "gitdir:**/company-x/**/.git"] path = ~/.gitconfig-company-x [gpg] program = gpg [credential] helper = store
[credential]section with the setting
helper = storewill store your username/password combination when using HTTPS so you don’t have to type it over and over again. You could also set this to
helper = cacheif you don’t want to permanently store credentials.
includeIfdirective will be triggered whenever you are in a directory containing
company-xin this case so that your correct GPG key and Git settings will be used instead of the default settings for your personal account.
- In the block
[gpg], your system may be using the program
Example for one of those:
[user] email = email@example.com name = Sébastien Lavoie signingkey = A343702EBE11E0C2 [commit] gpgsign = true
If you don’t have a GPG key already, you can generate one with this command:
To list existing GPG keys to determine the
signinkey value to use in those files, you can type:
gpg --list-secret-keys --keyid-format LONG
You might get an output similar to the following (this one is showing only one key):
sec rsa3072/A343702EBE11E0C2 2020-10-03 [SC] [expires: 2022-10-03] EF731EFC008D47D176C05910A343702EBE11E0C2 uid [ultimate] Sébastien Lavoie <firstname.lastname@example.org> ssb rsa3072/718726CCFED43B47 2020-10-03 [E] [expires: 2022-10-03]
The bit you need to retrieve for the
signingkey value comes after the type of encryption, here it’s
rsa3072 and the bit we want is
If you need to edit a key, there are plenty of options described with
man gpg or
man gpg2. For instance, to remove the expiration date for the above key:
gpg2 --edit-key EF731EFC008D47D176C05910A343702EBE11E0C2
gpg> prompt, type
expire and follow the instructions.
To delete a key, you can do so by referring to the email address like this:
gpg --delete-secret-and-public-key email@example.com
Just follow the instructions from there. You may need to repeat the process multiple times if your email address is associated with more than one key.
Setting GPG keys remotely
Just like with the SSH keys, the process differs from one platform to the other.
Interacting with remote repositories
Now that the Git configuration is set up and we have SSH and GPG keys to authenticate ourselves and verify our identity when committing, respectively, we can start interacting with remote repositories. From the example we have been following, the file
~/.gitconfig-personal will be used by default (and by consequence, our personal account).
The SSH part
When first cloning, change the host so that it reflects what you have in
~/.ssh/id_rsa_correct_key_file. For your personal account, no change would be required:
git clone firstname.lastname@example.org:organization/repo.git
For a repository at work requiring the SSH key set up for the work account, you would need to change to the appropriate host like so (we still use the
git user for convenience):
git clone git@github-work:organization/repo.git
For a refresher, the following are the hosts we have set in
The difference will be noticed when pushing/pulling as seen with
git remote -v:
$ git remote -v origin git@github-work:organization/repo.git (fetch) origin git@github-work:organization/repo.git (push)
Whereas the personal account will have the same host as usual, for instance
The GPG part
If we want to keep our personal and work Git configurations separate (and we probably want that! ;)), it’s only a matter of ensuring that the
includeIf pattern contains, in this example,
company-x somewhere in the path. When this is the case, we will see with
git config --list in the cloned repository that our personal account details are loaded first, but if the
includeIf directive matches, the settings for that other account will be applied on top and used when committing.
If you type
git config --list and search for the word “email” in the output, it will appear only once when the default
~/.ssh/id_rsa key is used (or whatever is read for the
IdentityFile from the SSH configuration file) while you will see that same personal email showing up first in a work repository, but then later in the output you will find the work email, work GPG key and so on.
This is one possible kind of setup we can use to work with SSH and GPG comfortably. This is pretty much a “set it and forget it” approach as long as you remember the following:
- SSH: Change the SSH host when cloning. If the repository is not publicly available, it would fail anyways (or you may realize you can clone it if it’s a public repo but have no
- Git: Make sure you are in a directory where the
includeIfdirective will kick in to set up the email, GPG key and so on.
One nice tip to help with the latter bullet point could be to define an alias, say in
~/.bash_aliases, something like what follows:
Then, you simply
cdwork and store all your Git repositories for work under that root repository, which would always match the desired pattern by default. That’s it. Anything else related to the personal account can be cloned anywhere on the file system since this is the default configuration file used. For more Git-related content, you might enjoy reading Git the gist of it: common commands for a working workflow.