Seamlessly Switch Between Personal and Work GitHub Accounts on macOS
Ever found yourself in this scenario? You're working on a personal project, push to GitHub, and suddenly you're committing as your work account. Or worse, you get that dreaded "Permission denied (publickey)" error even though your SSH config looks perfect.
If you're juggling multiple GitHub accounts, you know the pain. Let's build a solution that actually works.
The Problem: Manual Git Profile Management
As developers, we often need to maintain separate GitHub identities:
- Personal account for side projects and open source contributions
- Work account for company projects
- Client accounts for freelance work
The manual process involves:
- Updating
git config user.nameandgit config user.email - Managing multiple SSH keys
- Editing SSH config files
- Dealing with SSH agent caching issues
But here's the hidden problem that trips everyone up...
The Hidden Issue: SSH Agent Caching
You've done everything right:
- Generated separate SSH keys for each account
- Updated your
~/.ssh/configfile - Set the correct git user config
Yet you still get authentication errors. Why?
The SSH agent caches your keys and ignores your SSH config file!
When you run ssh-add, the agent remembers which key to use for GitHub.com. Even if you change your SSH config, the agent keeps using the cached key. This is why switching accounts feels like black magic.
The Solution: A Complete Profile Switcher
Let's build a bash script called gsw (Git Switch) that handles everything:
#!/bin/bash
# Git Profile Switcher (gsw)
# Switch between personal and work GitHub accounts seamlessly
set -e
# Configuration
PERSONAL_NAME="Your Personal Name"
PERSONAL_EMAIL="personal@example.com"
PERSONAL_SSH_KEY="$HOME/.ssh/id_personal"
WORK_NAME="Your Work Name"
WORK_EMAIL="work@company.com"
WORK_SSH_KEY="$HOME/.ssh/id_work"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to show current configuration
show_current_config() {
echo "Current Git Configuration:"
echo "------------------------"
echo "Name: $(git config --global user.name)"
echo "Email: $(git config --global user.email)"
echo "SSH Key: $(ssh-add -l | grep github || echo 'No GitHub key in agent')"
echo ""
}
# Function to update SSH config
update_ssh_config() {
local profile=$1
local ssh_key=$2
# Backup existing config
cp "$HOME/.ssh/config" "$HOME/.ssh/config.backup" 2>/dev/null || true
# Remove existing GitHub entries
sed -i '' '/^Host github\.com$/,/^$/d' "$HOME/.ssh/config" 2>/dev/null || true
# Add new GitHub configuration
cat >> "$HOME/.ssh/config" << EOF
Host github.com
HostName github.com
User git
IdentityFile $ssh_key
IdentitiesOnly yes
AddKeysToAgent yes
EOF
print_status "SSH config updated for $profile profile"
}
# Function to switch SSH keys
switch_ssh_key() {
local ssh_key=$1
# Remove all SSH keys from agent
ssh-add -D 2>/dev/null || true
# Add the new key
ssh-add "$ssh_key"
print_status "SSH key switched to $ssh_key"
}
# Function to update git configuration
update_git_config() {
local name=$1
local email=$2
git config --global user.name "$name"
git config --global user.email "$email"
print_status "Git config updated: $name <$email>"
}
# Main switching logic
switch_profile() {
local profile=$1
case $profile in
"personal"|"p")
print_status "Switching to PERSONAL profile"
update_ssh_config "personal" "$PERSONAL_SSH_KEY"
switch_ssh_key "$PERSONAL_SSH_KEY"
update_git_config "$PERSONAL_NAME" "$PERSONAL_EMAIL"
;;
"work"|"w")
print_status "Switching to WORK profile"
update_ssh_config "work" "$WORK_SSH_KEY"
switch_ssh_key "$WORK_SSH_KEY"
update_git_config "$WORK_NAME" "$WORK_EMAIL"
;;
*)
print_error "Unknown profile: $profile"
echo "Usage: gsw [personal|work|p|w|status]"
exit 1
;;
esac
show_current_config
}
# Function to show help
show_help() {
echo "Git Profile Switcher (gsw)"
echo "Usage: gsw [personal|work|p|w|status|help]"
echo ""
echo "Commands:"
echo " personal, p Switch to personal GitHub account"
echo " work, w Switch to work GitHub account"
echo " status Show current configuration"
echo " help Show this help message"
}
# Main script logic
case "${1:-}" in
"personal"|"p"|"work"|"w")
switch_profile "$1"
;;
"status"|"s")
show_current_config
;;
"help"|"h"|"-h"|"--help")
show_help
;;
"")
print_error "No profile specified"
show_help
exit 1
;;
*)
print_error "Unknown command: $1"
show_help
exit 1
;;
esac
Implementation Details Explained
Why ssh-add -D is Critical
The ssh-add -D command removes all keys from the SSH agent. This is the magic that makes profile switching work. Without it, the agent would keep using the cached key regardless of your SSH config changes.
Using $HOME Instead of ~
In bash scripts, $HOME is more reliable than ~ because:
~expansion doesn't work consistently in all contexts$HOMEworks reliably in variable assignments and file paths- It's more explicit about what you're referencing
SSH Config Management
The script:
- Backs up your existing SSH config
- Removes any existing GitHub.com entries
- Adds a fresh configuration for the selected profile
This prevents conflicts and ensures clean switching.
Setting Up Your Environment
1. Generate SSH Keys
# Personal key
ssh-keygen -t ed25519 -C "personal@example.com" -f ~/.ssh/id_personal
# Work key
ssh-keygen -t ed25519 -C "work@company.com" -f ~/.ssh/id_work
2. Add Keys to GitHub
Add the public keys (id_personal.pub and id_work.pub) to their respective GitHub accounts.
3. Install the Script
# Make it executable
chmod +x gsw
# Move to a directory in your PATH
sudo mv gsw /usr/local/bin/
4. Add Aliases (Optional)
Add these to your ~/.zshrc or ~/.bashrc:
alias gsw-p='gsw personal'
alias gsw-w='gsw work'
alias gsw-s='gsw status'
Usage Examples
# Switch to personal account
gsw personal
# Switch to work account
gsw work
# Check current configuration
gsw status
# Quick aliases
gsw-p # Switch to personal
gsw-w # Switch to work
gsw-s # Show status
Troubleshooting Common Issues
"Permission denied (publickey)" Error
- Verify the SSH key exists:
ls -la ~/.ssh/id_* - Check if key is in agent:
ssh-add -l - Test connection:
ssh -T git@github.com
Git Commit Still Shows Wrong Author
- Check global config:
git config --global --list - Check local config:
git config --local --list(in repo directory) - Remove local config if needed:
git config --local --unset user.name
SSH Agent Not Running
# Start SSH agent
eval "$(ssh-agent -s)"
# Add it to your shell profile
echo 'eval "$(ssh-agent -s)"' >> ~/.zshrc
Bonus Tips
Same SSH Key on Multiple Accounts
You can use the same SSH key for multiple GitHub accounts. Just add the same public key to both accounts. The script will still work for switching git user configs.
Shell Integration
Add this function to your shell for a nice prompt indicator:
# Add to ~/.zshrc or ~/.bashrc
git_profile_prompt() {
local email=$(git config --global user.email)
if [[ $email == *"personal"* ]]; then
echo "🏠"
elif [[ $email == *"work"* ]]; then
echo "💼"
else
echo "❓"
fi
}
# Add to your prompt
export PS1='$(git_profile_prompt) '$PS1
Git Hooks for Safety
Create a pre-commit hook to verify you're using the right identity:
#!/bin/sh
# .git/hooks/pre-commit
REPO_EMAIL=$(git config user.email)
GLOBAL_EMAIL=$(git config --global user.email)
if [ "$REPO_EMAIL" != "$GLOBAL_EMAIL" ]; then
echo "⚠️ Warning: Local git email differs from global profile"
echo "Local: $REPO_EMAIL"
echo "Global: $GLOBAL_EMAIL"
fi
The Bottom Line
Managing multiple GitHub accounts doesn't have to be a headache. The key is understanding that SSH agent caching is the real culprit behind most authentication issues.
With this gsw script, you can:
- Switch profiles instantly
- Avoid SSH agent conflicts
- Maintain clean git history
- Focus on coding instead of configuration
No more "why isn't this working?" moments. Just clean, reliable profile switching.
Happy coding with the right identity! 🚀
P.S. If you're interested in more productivity hacks, check out my post on useful CSS generators or learn about vim productivity tips.
