Working with GitHub
Version control is essential for any non-trivial project. GitHub is a cloud-based Git (distributed version control system) repository manager that allows developers to store, share, and collaborate on a codebase. This chapter covers two basic concepts — Initial setup for SSH access, and Managing multiple git accounts from same machine.
Initial Setup: Creating and Configuring SSH
The approach supports multiple GitHub accounts. In this chapter, we would use two emails username@company.com and username@gmail.com for accessing the organisation's and personal repositories respectively. Accordingly, we would setup two GitHub accounts for these two identities — each with its own SSH key-pair, all on the same machine.
Step 1: Create or Log in to GitHub Account
First step is to just navigate to our GitHub instance (e.g. github.com or the organisation's self-hosted instance) and either sign-up or sign-in.
We can just use our emails username@company.com and username@gmail.com to create two accounts on GitHub. For Enterprise Managed Users (EMU), the system might have automatically created the GitHub accounts for the employees, and they just need to sign-in through internal identity system (like Okta or Azure) for accessing company account.
When we signup, we need to provide unique username that would be associated with our GitHub account. This username must be completely unique across the entire GitHub platform - which includes all personal and organisational accounts together. For our example, let's assume that two accounts associated with emails username@company.com and username@gmail.com are work-user and personal-user respectively.
Step 2: Generate an SSH Key-Pair
Let's check whether a .ssh directory already exists:
% ssh -V
OpenSSH_10.2p1, LibreSSL 3.3.6
% cd .ssh/
cd: no such file or directory: .ssh/
Since no SSH key exist yet, we need to generate our ed25519 (recommended over RSA and others) key-pairs from scratch on the terminal. We must have separate keys for different GitHub accounts to avoid the unintentional access/updates of the files. We start with the company account associated with email id username@company.com, and have accordingly named our key as id_ed25519_github_work_user. These values should be substituted as desired:
% ssh-keygen -t ed25519 -C "username@company.com"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/username/.ssh/id_ed25519): /Users/username/.ssh/id_ed25519_github_work_user
Created directory '/Users/username/.ssh'.
Enter passphrase for "/Users/username/.ssh/id_ed25519_github_work_user" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/username/.ssh/id_ed25519_github_work_user
Your public key has been saved in /Users/username/.ssh/id_ed25519_github_work_user.pub
The key fingerprint is:
...
Saving to a custom filename — id_ed25519_github_work_user instead of the default name id_ed25519 — allows us to maintain multiple key-pairs for different accounts or projects. Also, adding the keyword github in the name helps in identifying that this key is specifically for GitHub account. We can follow the same approach as mentioned above for creating another SSH key-pair, named as id_ed25519_github_personal, for our personal account associated with username@gmail.com.
Another important part is to add a passphrase while creating the SSH key. The primary advantage of using a passphrase is defense in depth — it prevents an unauthorized person from using the SSH key when the local computer or storage drive is compromised, stolen, or hacked. The only hurdle about passphrases is having to type them every single time we push the code to Git (or connect to a server for other usage of SSH key). We can entirely bypass this friction by using an SSH Agent (ssh-agent) or storing it in Keychain Access app in the macbook:
% ssh-add --apple-use-keychain ~/.ssh/id_ed25519_github_work_user
Enter passphrase for /Users/username/.ssh/id_ed25519_github_work_user:
Identity added: /Users/username/.ssh/id_ed25519_github_work_user (username@company.com)
% ssh-add --apple-use-keychain ~/.ssh/id_ed25519_github_personal
Enter passphrase for /Users/username/.ssh/id_ed25519_github_personal:
Identity added: /Users/username/.ssh/id_ed25519_github_personal (username@gmail.com)
To make these settings permanent, so that macOS doesn't forget the association when we restart the computer, we need to configure the SSH config file ~/.ssh/config:
% nano ~/.ssh/config
The ~/.ssh/config file needs to be edited as follows (again the suffixes company & personal and filenames id_ed25519_github_work_user & id_ed25519_github_personal needs to be substituted with actual names):
# Company Account Configuration
Host github.com-company
HostName github.com
User git
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519_github_work_user
# Personal Account Configuration
Host github.com-personal
HostName github.com
User git
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519_github_personal
Step 3: Add the Public Key to GitHub
Once the keys are generated and configured in local system, we need to provide the public keys to respective GitHub accounts. We can copy the public key to the clipboard using following command:
% tr -d '\n' < ~/.ssh/id_ed25519_github_work_user.pub | pbcopy
In GitHub, we need to navigate to Keys (Avatar → Settings → SSH and GPG Keys), and click on New SSH key button. We can paste the copied key here, add a title — clicking on Add SSH key saves the public key at GitHub's end.
We can follow the same approach for personal account as well.
Step 4: Verify the Connection
We can check if SSH key is working fine and git is successfully connected using following command:
% ssh -T git@github.com-company
Hi work-user! You've successfully authenticated, but GitHub does not provide shell access.
% ssh -T git@github.com-personal
Hi personal-user! You've successfully authenticated, but GitHub does not provide shell access.
% ssh -T git@github.com
Hi work-user! You've successfully authenticated, but GitHub does not provide shell access.
We see the successful authentication messages which indicates that our primary setting of SSH key pairs are done. We can also see that when full Host alias (example, git@github.com-company) is provided as per ~/.ssh/config file, the account is resolved correctly with corresponding GitHub username — however when we provide default Host git@github.com, it utilises the first SSH key in ~/.ssh/config file, reflecting the work-user GitHub account. Rest of this chapter covers how these multiple accounts can be managed seamlessly by setting up correct defaults in different working folders.
Managing Multiple Accounts
If we look at the edits done in ~/.ssh/config file in step 2, we see the line User git appearing twice — once for each account — indicating both the users are named as git. This is further confirmed in step 4, where the part before the '@' symbol is the username ('git' for both the users). This means we always log into GitHub's servers as a user named git, regardless of whether it is a personal account, a company account, or someone else's account. GitHub does not create separate operating system accounts for millions of developers; everyone logs into the exact same gateway.
GitHub relies entirely on our SSH public-private key pair to figure out who we actually are. Our system sends a public key during the connection handshake. GitHub looks through its massive database to see which specific account (e.g., our work account) has that exact public key registered. Once it finds the match, GitHub maps the account to company/repo.git based on employee's specific permissions. This is why we must never reuse the exact same SSH key across two different GitHub accounts. If we upload the same key to both a personal and a company account, GitHub's database wouldn't know which account is trying to log in when we connect as git.
Once, we have different SSH key associated with two accounts and ~/.ssh/config file is updated, we can access both accounts as per the need. However, just relying on the sequencing of the keys in ~/.ssh/config file is not reliable. In fact, in the Step 4 of previous section, priority was given to first SSH key in ~/.ssh/config file only because it was an "SSH Server Authentication" and not a "Git Authoring Commit". When we use the git commands for authoring commits, the identity fallback mechanism (auto-detection) is completely different.
Once we have different SSH keys associated with two accounts and the ~/.ssh/config file is updated, we can access both accounts as needed. However, as seen in Step 4 of the previous section, using the default host git@github.com causes the system to read the ~/.ssh/config file from top to bottom and default to the first key listed (the work key). This routing handles "SSH Server Authentication" (pushing/pulling code). On the other hand, when we actually write and create a commit, Git uses a completely separate "Git Commit Authoring" identity fallback mechanism (auto-detection).
Git generally looks at the environment variables $USER or $LOGNAME on Linux/macOS or system's password database (/etc/passwd) to retrieve the account's full name. It also auto-generates the email using OS username and the system's hostname. Depending on the specific Git installation configuration, Git will do one of the following:
- Successfully make the commit using the auto-detected credentials and print a warning in the terminal stating that it had to guess the identity.
- Completely block the commit and throw the error message: "Please tell me who you are".
In summary, it can be dangerous to use Git with multiple accounts without updating the git configurations properly. Follwoing sub-sections describes the cleanest way to set this up on our system.
Step 1: Organise the Project Folders
We start with making sure that our local repositories are separated into distinct folders on our system. Here we create two folders — one for all work related repositiries and one for all personal repositories.
% mkdir ~/Projects/work ~/Projects/personal
Step 2: Set the Global Defaults
It is always better to set the global config to the personal identity. This acts as the safety net for any random folder outside of ~/Projects/work/, and keeps the work related confidential repositories safe.
% git config --global user.name "User Name"
% git config --global user.email "username@gmail.com"
In the above commands, user.name is just the display name and can be as per user's choice that would be shown against any git action. However, user.email needs to be exactly as per email-id associated with the personal GitHub account. GitHub does not look at the user.name string to link a commit to a profile — instead, it relies entirely on the user.email.
Step 3: Configure Git to Auto-Switch to Work Profile
Once the global default is set to personal identity, it becomes the fallback profile across all folders in the system. However, we want to explicitly use only work profile for all repositories in ~/Projects/work/ directory.
Git always first tries to resolve the identity based on the content in the ~/.gitconfig file. Currently this file contains following lines:
[user]
name = User Name
email = username@gmail.com
To use work profile identity when we are inside ~/Projects/work/ directory, we need to create and edit another parallel config file, let's say ~/.gitconfig-work:
% nano ~/.gitconfig-work
Inside the ~/.gitconfig-work file, we need add following content:
[user]
name = User Name
email = username@company.com
Once ~/.gitconfig-work is saved with work profile identity, we need to edit ~/.gitconfig file with the instruction to override global default identity with the content of ~/.gitconfig-work whenever we use git from ~/Projects/work/ directory.
% nano ~/.gitconfig
We need to append includeIf block in the ~/.gitconfig file to make final content as below:
[user]
name = User Name
email = username@gmail.com
[includeIf "gitdir:~/Projects/work/"]
path = ~/.gitconfig-work
This is all that needs to be done. Let's use it and see how it behaves.
Step 4: Check Multiple Profile Configurations
We navigate through different directory and check the Git profile associated with those directories as below:
% git config user.email
username@gmail.com
% cd ~/Projects/work
% git config user.email
username@company.com
% cd ~/Projects/personal
% git config user.email
username@gmail.com
% cd ..
% git config user.email
username@gmail.com
We see that work profile is activated only when we are in the ~/Projects/work/ directory - everywhere else, Git always resolves it to the personal identity. This is the desired behaviour that we wanted to get for managing multiple GitHub profiles from same machine.
Step 5: Common Git Commands
Below are some commonly used commands for using Git:
# Clone a repository using your custom personal alias
% git clone git@github.com-personal:personal-user/project_name.git
# Stage changes
% git add .
# Commit
% git commit -m "Comments for commit"
# Rebase before pushing to keep history linear
% git pull -r
# Push
% git push
Note: Always run git pull -r (pull with rebase) before pushing. This keeps commit history linear and avoids unnecessary merge commits. Add a .gitignore file to exclude system artefacts such as .DS_Store, __pycache__/, *.pyc, and .ipynb_checkpoints/. A Python template is available at GitHub.
