:bulb: Running personal and work accounts (or two or more GitHub accounts) on the same machine is common. Since git stores credentials per host (github.com) by default, switching accounts without configuration causes contributions to land on the wrong account or triggers random Permission denied errors. This guide covers 4 methods to build a stable multi-account environment by separating commit identity (author) from authentication (auth).

:memo: This post targets macOS/Linux/WSL. The prerequisite step (includeIf) is compatible with all methods. For authentication, pick just one of methods A through D depending on your situation.


[01] The Core Problem — Author ≠ Auth

Multi-account operations require thinking of two independent axes.

Axis Meaning Where it’s decided
commit author Name/email stamped onto commits git config user.name, user.email
authentication Which credentials are sent at push credential helper, SSH key, URL

These are completely independent. The author can be personal while auth is company — or vice versa. Most multi-account confusion comes from conflating them.


[02] Prerequisite — Folder-Based Auto Author Separation

Regardless of which auth method you pick, the cleanest baseline is auto-switching commit author by folder. Use git 2.13+’s includeIf feature.

2-1. Define Folder Structure

1
2
3
~/work/
├── personal/   # personal account repos
└── company/    # company account repos

2-2. Configure Global ~/.gitconfig

1
2
3
4
5
[includeIf "gitdir:~/work/personal/"]
  path = ~/.gitconfig-personal

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

:warning: The gitdir: path must end with a trailing slash (/). Without it, the match silently fails.

2-3. Per-Account gitconfig Files

1
2
3
4
# ~/.gitconfig-personal
[user]
  name = personal-username
  email = personal@users.noreply.github.com
1
2
3
4
# ~/.gitconfig-company
[user]
  name = company-username
  email = company@users.noreply.github.com

2-4. Verify

1
2
3
4
5
6
7
cd ~/work/personal/some-repo
git config user.email
# → personal@users.noreply.github.com

cd ~/work/company/some-repo
git config user.email
# → company@users.noreply.github.com

With this alone, commit author auto-switches based on folder location. Now we just need to handle auth.


[03] Four Auth Separation Methods — Decision Flow

flowchart TD
    A["Multi-account auth"] --> B{"Use gh CLI
daily?"} B -->|Yes| C["Method A
gh helper"] B -->|No| D{"Long-term machine?"} D -->|Yes| E["Method D
SSH alias"] D -->|No| F{"OK with plaintext PAT?"} F -->|No| G["Method B
HTTPS+username"] F -->|Yes / one-shot CI| H["Method C
URL+token"] style C fill:#fff3e0,stroke:#ef6c00 style E fill:#e8f5e9,stroke:#2e7d32 style G fill:#e3f2fd,stroke:#1565c0 style H fill:#ffcccc,stroke:#c62828

[04] Method A — Use gh CLI as Credential Helper

The simplest approach if you already have GitHub CLI installed.

4-1. Log In to Both Accounts

1
2
gh auth login    # personal account
gh auth login    # company account (run again)
1
2
3
4
gh auth status
# github.com
#   ✓ Logged in to github.com account personal-username
#   ✓ Logged in to github.com account company-username

4-2. Register gh as Git Credential Helper

1
gh auth setup-git

This adds the following to ~/.gitconfig:

1
2
[credential "https://github.com"]
  helper = !/usr/bin/gh auth git-credential

4-3. Switch Accounts

1
2
3
4
5
gh auth switch --user personal-username
# All subsequent git push goes as personal

gh auth switch --user company-username
# All subsequent git push goes as company

:warning: gh auth switch applies machine-wide, not per-repo or per-directory.


[05] Method B — Embed Username in HTTPS URL + Credential Helper

Pure git, no gh CLI.

5-1. Enable a Credential Helper

1
2
3
4
5
6
7
8
# Linux/WSL — plaintext store
git config --global credential.helper store

# macOS — Keychain
git config --global credential.helper osxkeychain

# Linux — libsecret (gnome-keyring backed, more secure)
git config --global credential.helper libsecret

5-2. Issue a PAT per Account

GitHub web → Settings → Developer settings → Personal access tokens → Tokens (classic) → issue a token with repo, workflow scope for each account.

5-3. Bake Username into the Remote URL

1
2
3
4
5
# personal account repo
git remote set-url origin https://personal-username@github.com/personal-username/repo.git

# company account repo
git remote set-url origin https://company-username@github.com/company-username/repo.git

5-4. Enter PAT on First Push

1
2
3
git push
# Username: (auto-filled)
# Password: <paste PAT>

After this, the credential helper stores credentials per-username, so the two accounts’ tokens don’t collide.

:warning: In WSL with wincred (Windows credential manager) active, per-username separation may not work. Run git config --global --unset credential.helper first, then explicitly pick one of the helpers above.


[06] Method C — Embed PAT Directly in URL

A last resort when you refuse to use any helper.

6-1. Issue PAT

Same as 5-2.

6-2. Bake the Token into the Remote URL

1
git remote set-url origin https://personal-username:ghp_xxxxxxxxxxxxxxxxxxxx@github.com/personal-username/repo.git

6-3. Use

git push / pull works without any further authentication.

:warning: The token is stored in plaintext in .git/config. Never use this on machines without disk encryption, in paths included in backups, or on shared machines. Not recommended unless the PAT is injected via CI env vars.


[07] Method D — SSH Keys + Host Aliases (Recommended for Long-Term)

The most stable for long-term operations.

7-1. Generate One SSH Key per Account

1
2
ssh-keygen -t ed25519 -C "personal@github" -f ~/.ssh/id_ed25519_personal
ssh-keygen -t ed25519 -C "company@github"  -f ~/.ssh/id_ed25519_company

7-2. Register Public Keys with Each GitHub Account

1
2
3
4
5
gh auth switch --user personal-username
gh ssh-key add ~/.ssh/id_ed25519_personal.pub --title "$(hostname)-personal"

gh auth switch --user company-username
gh ssh-key add ~/.ssh/id_ed25519_company.pub --title "$(hostname)-company"

Manual registration: Settings → SSH and GPG keys → New SSH key.

7-3. Define Host Aliases in ~/.ssh/config

Host github.com-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal
  IdentitiesOnly yes

Host github.com-company
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_company
  IdentitiesOnly yes

:warning: IdentitiesOnly yes is required. Without it, SSH tries the first key from ssh-agent and authenticates as the wrong account.

7-4. Set Permissions

1
2
chmod 600 ~/.ssh/config ~/.ssh/id_ed25519_personal ~/.ssh/id_ed25519_company
chmod 644 ~/.ssh/id_ed25519_personal.pub ~/.ssh/id_ed25519_company.pub

7-5. Test Connections

1
2
3
4
5
ssh -T git@github.com-personal
# → Hi personal-username! You've successfully authenticated...

ssh -T git@github.com-company
# → Hi company-username! ...

7-6. Use the Alias in Remote URLs

1
2
3
4
5
git clone git@github.com-personal:personal-username/repo.git
git clone git@github.com-company:company-username/repo.git

# For an existing repo
git remote set-url origin git@github.com-personal:personal-username/repo.git

[08] Comparison Table

Item A (gh helper) B (URL+username) C (URL+token) D (SSH alias)
Initial setup difficulty ★☆☆☆☆ (1 command) ★★☆☆☆ ★☆☆☆☆ ★★★☆☆
Long-term maintenance Medium (token renewal) Medium (PAT re-entry at expiry) High (URL edit on expiry) Low (permanent)
Account switching gh auth switch Auto via remote URL Auto via remote URL Auto via remote URL
Per-repo auto-separation ✗ (follows active)
Credential exposure risk Low (OAuth token) Medium (helper store) High (plaintext) Very low (private key never leaves)
gh CLI dependency Required None None Only at setup
WSL helper conflict None Possible None None
CI/server portability Needs gh Helper reconfig URL copy only Copy key files
Public key registration ✓ (once)
Token/key expiry ~1 year (OAuth) Up to 1 year (PAT) Up to 1 year (PAT) Indefinite
2FA/SSO compatibility ✓ (PAT must be authorized) ✓ (PAT must be authorized)
Recommended use gh-centric users Light multi-account One-shot CI Long-term primary machine

[09] Recommendations by Scenario

🥇 Primary Development Machine — Method D (SSH) + Prerequisite (includeIf)

The least friction long-term. Pay the setup cost once and you’re free from token renewals, auth re-configuration, and credential helper conflicts. Combine with folder-based includeIf and both commit author and auth separate automatically.

🥈 Daily gh CLI Users — Method A

If gh pr create, gh issue list are already in your muscle memory, gh auth setup-git unifies git push with that flow. The cost: mental overhead of remembering which account is active. Once you start confusing which account a repo needs, migrate to D.

🥉 Occasional Light Use — Method B

If multi-account usage is infrequent and SSH setup feels heavy, B is reasonable. On WSL, explicitly pin the credential helper first to block wincred conflicts.

🚫 Combinations to Avoid

  • Method C on a daily machine — tokens sit in plaintext on disk. Risk of leaks via backups or accidental pushes is too high.
  • Using .netrc — only recognizes one credential per host, unsuitable for multi-account.
  • A + D simultaneously — running credential helper (A) and SSH (D) together on the same machine makes it hard to trace which auth path was used. Standardize on one.

[10] One-Page Summary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Multi-account = author separation + auth separation

Author separation
  └─ ~/.gitconfig + includeIf
       └─ Auto-switch per folder (compatible with all methods)

Auth separation — 4 options
  ├─ A. gh helper            → gh-centric users, short-term
  ├─ B. HTTPS + username     → light multi-account
  ├─ C. URL + plaintext PAT  → one-shot CI; avoid daily
  └─ D. SSH + Host alias     → long-term primary machine ⭐

Final formula
  includeIf (author) + Method D (auth)
     = cd alone switches everything

:star: Multi-account configuration is a one-time investment that lasts years. The cost of “I don’t feel like registering this once” is always smaller than the accumulated cost of “token renewal and auth debugging every quarter.” For your primary machine, spend 30 minutes and go with Method D.