Seamlessly Switch Between Personal and Work GitHub Accounts on macOS

Git
SSH
macOS
Productivity
Bash

07 Nov, 2025

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:

  1. Updating git config user.name and git config user.email
  2. Managing multiple SSH keys
  3. Editing SSH config files
  4. 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/config file
  • 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
  • $HOME works reliably in variable assignments and file paths
  • It's more explicit about what you're referencing

SSH Config Management

The script:

  1. Backs up your existing SSH config
  2. Removes any existing GitHub.com entries
  3. 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

  1. Verify the SSH key exists: ls -la ~/.ssh/id_*
  2. Check if key is in agent: ssh-add -l
  3. Test connection: ssh -T git@github.com

Git Commit Still Shows Wrong Author

  1. Check global config: git config --global --list
  2. Check local config: git config --local --list (in repo directory)
  3. 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.

That's it for now, thanks for reading! You can find me at @samuellawrentz on X.
00:00

This helps me increase the session time of my site. Thank you!

Can you stay a bit longer?