Managing Multiple GitHub Accounts on One Machine

#Tools #Git #GitHub 1,013 words 5 min read

Running both personal and work projects on one dev machine, with commits going to different GitHub accounts. Don’t want to manually change user.email after every clone, and don’t want a global account-switching script. SSH key isolation + Git conditional config solves it once and for all.

1. Scenario

Typical needs:

  • Personal projects (~/personal/*) commit with personal GitHub account
  • Work projects (~/work/*) commit with company GitHub account
  • Each account has its own SSH key, no interference
  • Identity switches automatically when entering different directories, no manual setup

Additional pain points:

  • Corporate network might block SSH default port 22, need to use 443
  • Some projects need aliases (like work.github.com) to differentiate remote URLs

2. Overall Strategy

Three-layer configuration working together:

  1. SSH layer (~/.ssh/config): Configure separate keys and Host aliases for different accounts
  2. Git global layer (~/.gitconfig): Set default account and SSH command
  3. Git conditional layer (~/.gitconfig-work): Auto-override account info based on directory path

Key mechanism: includeIf lets Git load additional config files in specific directories.

3. Steps

3.1 Generate SSH Key Pairs

Generate independent keys for each account:

# Personal account key
ssh-keygen -t rsa -b 4096 -C "personal@example.com" -f ~/.ssh/id_rsa_personal

# Work account key
ssh-keygen -t rsa -b 4096 -C "work@company.com" -f ~/.ssh/id_rsa_work

After generation you’ll have:

  • ~/.ssh/id_rsa_personal + ~/.ssh/id_rsa_personal.pub
  • ~/.ssh/id_rsa_work + ~/.ssh/id_rsa_work.pub

Add the .pub public keys to the SSH keys settings page of the corresponding GitHub accounts.

3.2 Configure SSH Host Aliases

Edit ~/.ssh/config (create if it doesn’t exist):

# Personal account (default)
Host github.com
  HostName ssh.github.com
  User git
  IdentityFile ~/.ssh/id_rsa_personal
  Port 443

# Work account (using alias work.github.com)
Host work.github.com
  HostName ssh.github.com
  User git
  IdentityFile ~/.ssh/id_rsa_work
  Port 443

Key points:

  • Port 443: Bypass firewall blocking of port 22, GitHub officially supports ssh.github.com:443
  • Host alias: work.github.com is a local alias, actual connection is still to ssh.github.com
  • IdentityFile: Specify which key to use

Test connections:

# Test personal account
ssh -T git@github.com
# Expected output: Hi personal-username! You've successfully authenticated...

# Test work account
ssh -T git@work.github.com
# Expected output: Hi work-username! You've successfully authenticated...

3.3 Configure Git Global Defaults

Edit ~/.gitconfig:

[user]
  name = Your Name
  email = personal@example.com

[core]
  # Globally force use of personal account key
  sshCommand = ssh -i ~/.ssh/id_rsa_personal -F /dev/null

# Conditional config: work directory auto-switches account
[includeIf "gitdir:~/work/"]
  path = ~/.gitconfig-work

Key points:

  • sshCommand forces use of specified key, -F /dev/null prevents ~/.ssh/config interference (optional, depends on your priority strategy)
  • includeIf path matching: the path after gitdir: must end with / to match subdirectories
  • Path supports ~ expansion and wildcards *

3.4 Configure Work Account Conditional File

Create ~/.gitconfig-work:

[user]
  name = Your Name
  email = work@company.com

[core]
  sshCommand = ssh -i ~/.ssh/id_rsa_work -F /dev/null

Now the logic is:

  • Default uses personal account (~/.gitconfig)
  • Entering any subdirectory of ~/work/, auto-switch to work account (~/.gitconfig-work overrides)

3.5 Clone Work Projects Using Host Alias

When cloning work projects, modify the URL:

# Original URL (would use personal account)
git clone git@github.com:company/project.git

# Use Host alias (will use work account key)
git clone git@work.github.com:company/project.git

Or modify remote after cloning:

git remote set-url origin git@work.github.com:company/project.git

Why need alias?

Even in ~/work/ directory, if remote URL is git@github.com, SSH will prioritize matching the Host github.com config in ~/.ssh/config. Host aliases let you differentiate accounts at the URL level.

4. Verify Configuration

4.1 Check Active Config in Current Directory

# In personal project directory
cd ~/personal/my-project
git config user.email
# Output: personal@example.com

# In work project directory
cd ~/work/company-project
git config user.email
# Output: work@company.com

4.2 Check SSH Key Being Used

# View actual SSH command in use
git config core.sshCommand

# Manual test (with verbose logging)
GIT_SSH_COMMAND="ssh -v" git ls-remote origin
# Output will show "Offering public key: /path/to/id_rsa_xxx"

4.3 Test Commit Identity

# Create test commit
echo "test" > test.txt
git add test.txt
git commit -m "test commit"

# View commit author
git log -1 --format="%an <%ae>"

5. Common Issues

5.1 Port 22 Blocked

Symptom: ssh -T git@github.com times out

Solution: Add to ~/.ssh/config:

Host github.com
  HostName ssh.github.com
  Port 443

GitHub officially provides SSH service on port 443 at ssh.github.com, specifically for bypassing firewalls.

5.2 Key Permission Issues

Symptom: Permissions 0644 for 'id_rsa_work' are too open

Solution:

chmod 600 ~/.ssh/id_rsa_*
chmod 644 ~/.ssh/id_rsa_*.pub

Private keys must be readable/writable only by owner (600), public keys can be more permissive (644).

5.3 includeIf Not Taking Effect

Checklist:

  1. Path must end with /: gitdir:~/work/ (correct) vs gitdir:~/work (wrong)
  2. Git version: includeIf requires Git 2.13+, run git --version to confirm
  3. Path expansion: ~ expands, but relative paths don’t, recommend absolute paths or ~/
  4. Debug: Run git config --show-origin user.email to see config source

5.4 Switching Account for Existing Projects

If project has already committed with wrong account:

# 1. Modify remote URL (if needed)
git remote set-url origin git@work.github.com:company/project.git

# 2. Modify most recent commit author (if not yet pushed)
git commit --amend --reset-author

# 3. Batch modify historical commits (use with caution, rewrites history)
git filter-branch --env-filter '
if [ "$GIT_COMMITTER_EMAIL" = "wrong@example.com" ]; then
  export GIT_COMMITTER_EMAIL="correct@example.com"
  export GIT_AUTHOR_EMAIL="correct@example.com"
fi
' --tag-name-filter cat -- --branches --tags

6. Advanced: Finer-Grained Control

6.1 Project-Specific Configuration

If a project needs special configuration, run within the project:

git config user.email "special@example.com"
git config core.sshCommand "ssh -i ~/.ssh/id_rsa_special"

Project-level config (.git/config) has highest priority, overrides global and conditional configs.

6.2 Multiple Work Directories

If you have multiple work directories:

[includeIf "gitdir:~/work/"]
  path = ~/.gitconfig-work

[includeIf "gitdir:~/company-a/"]
  path = ~/.gitconfig-company-a

[includeIf "gitdir:~/company-b/"]
  path = ~/.gitconfig-company-b

6.3 HTTPS Protocol Multi-Account

This article focuses on SSH, but HTTPS also supports multi-account:

  • macOS Keychain stores credentials, managed by git-credential-osxkeychain
  • Or use GitHub CLI: gh auth login supports multi-account switching
  • Conditional config can set different credential.helper

7. Summary

Workflow after configuration:

  1. Personal projects in ~/personal/, auto-use personal account
  2. Work projects in ~/work/, auto-use work account
  3. Remember to use git@work.github.com when cloning work projects
  4. Forgot to configure? Check with git config user.email before committing

Core benefits:

  • Zero manual switching: Automatic upon entering directory
  • Thorough isolation: Keys, identity, directories three-layer isolation
  • Verifiable: Clear testing methods for each step

This setup has been in use for two years, never had a commit with the wrong account since.