A small Windows app for people who use more than one GitHub account on the same machine. Every repository automatically commits and pushes as the account that owns it — no per-repo setup, nothing to remember when you clone.
With two accounts, HTTPS doesn't work. Git's credential store keys tokens by
host, and both accounts are github.com, so whichever you authenticated last
wins. You end up pushing to a work repo as your personal account, or hitting
403 for no obvious reason — and the wrong author email is baked into commits
before you notice.
The usual fix is SSH host aliases plus includeIf in .gitconfig. It works,
but it's fiddly to set up, easy to get subtly wrong, and invisible once done.
This app sets it up, keeps it correct, and shows you the current state.
git clone https://github.com/web-dev-nav/multi-git-holder
cd multi-git-holder
install.cmdThere is nothing to install first — Windows already ships the compiler this
uses. A prebuilt GhAccounts.exe may also be attached to
Releases.
install.cmd compiles the app, copies it to %LOCALAPPDATA%\Programs\GhAccounts
and adds Desktop and Start Menu shortcuts.
Requirements: Windows with .NET Framework 4.x (in-box since Windows 8) and Git for Windows. No .NET SDK, no runtime installer, no admin rights. The exe is about 50 KB and starts instantly.
If you have never set up SSH for GitHub, Accounts → Add account... walks you through it in four steps. Do it once per account.
![]() |
![]() |
![]() |
![]() |
1. Which account. The username exactly as it appears in your profile URL. Leave name and email blank to use the GitHub noreply address, which keeps working even with Keep my email private turned on.
2. Choose a key. Generate a new key if you have never set up SSH — it
creates an ed25519 key in your .ssh folder. Otherwise point at one you have.
3. Give the key to GitHub. The public half is already on your clipboard and shown on screen; the private half never leaves your machine. Sign in to GitHub as this account before pasting — for a second account use a private window or sign out first. This is the step people get wrong.
4. Check it works. The app asks GitHub who the key belongs to. You want
Hi <username>! If GitHub reports a different username than you typed, the
account is saved under GitHub's answer — URL matching needs it exact, and a
typo would otherwise stop every repo for that account from being recognised.
Already have working keys? Skip all of it and press Detect keys... — it
tests every key in ~/.ssh against GitHub and adds the ones that work.
Accounts → Remove takes it out of the routing and deletes the identity file
it created. Repositories keep working; they just stop being assigned to that
account automatically. If the key lives in your .ssh folder, it offers to
delete that too — it never touches a key you pointed at from elsewhere, and it
reminds you that GitHub keeps its own copy until you remove it there as well.
1. Add your accounts. Detect keys... tries every key in ~/.ssh against
GitHub and adds the ones that authenticate, naming each from GitHub's own
Hi <user>! reply — so a key can't end up filed under the wrong account.
Add account... handles a new one, including generating the SSH key, copying
the public half to your clipboard and opening GitHub's "add key" page. Add any
organizations that should use that identity (for example TNHBSupport) under
Organizations / owners. A GitHub username and an organization are separate:
one SSH identity may route repositories for several owners.
2. Point it at your code. Add the folders holding your repositories and set how deep to search. Common locations are detected on first run.
3. That's it. Clone anything — paste a plain https://github.com/... URL —
and it is routed to the right key and the right commit identity automatically.
Use Test repository... on the Accounts tab to verify an exact private URL
before cloning it. The test follows the same Git-to-SSH routing as git clone
and does not download or modify the repository.
Use regular
git clonefor automatic account routing.gh repo clonefirst checks repository access through the GitHub CLI's independently selected account, before Git can apply these SSH routing rules.
The Repositories tab flags anything inconsistent: a repo committing as the wrong account for its remote, an identity belonging to neither account, a locally pinned override. Fix all mismatches repairs them in one click.
Three standard Git and SSH features, wired together:
| Piece | What it does |
|---|---|
| SSH host aliases | github-<account> in ~/.ssh/config, each bound to one key with IdentitiesOnly yes |
url.<alias>.insteadOf |
Rewrites any pasted github.com URL for every assigned repository owner or organization onto that account's alias |
includeIf "hasconfig:remote.*.url:…" |
Selects user.name / user.email from the remote URL (Git 2.36+) |
Everything the app writes lives between markers:
# >>> gh-account-switcher >>>
...
# <<< gh-account-switcher <<<
Only that block is rewritten. Anything else in your .gitconfig or
.ssh/config — other hosts, other settings — is left untouched.
Configuration is a single JSON file at %USERPROFILE%\.gh-accounts.json.
Existing configuration files are upgraded automatically: the authenticated
username becomes the first repository owner, and organizations can then be
added with Edit routing.
Windows and WSL keep separate ~/.ssh and ~/.gitconfig, and WSL's ssh rejects
keys stored on /mnt/c because they always appear world-readable. To mirror the
setup into WSL:
bash wsl-sync.shIt copies the keys in with mode 600, writes the same managed blocks on the
Linux side, and tests each alias.
- Email privacy. If an account has Keep my email private enabled, pushes
using a real address are rejected. Use the
ID+username@users.noreply.github.comform from that account's email settings. New accounts default to the noreply form for this reason. core.sshCommandis cleared. If it points at some other ssh config, the aliases won't resolve and every push fails withCould not resolve hostname github-<account>. The app removes it and verifies withssh -Gthat each alias resolves before reporting success.**inhasconfigpatterns degrades to*mid-token and cannot cross//, so thehttps://andssh://forms are written out in full rather than prefixed with**. A pattern like**github.com/org/**silently matches nothing.- Past commits keep their original author. This only affects new commits.
git commit --amend --reset-authorfixes the most recent one, if it isn't pushed yet. - Unknown orgs fall back to your global
user.name/user.emailrather than guessing.
uninstall.cmdRemoves the app and its shortcuts. Your keys, repositories and config are left
alone; delete the marked blocks from .gitconfig and .ssh\config by hand for
a full revert.
build.cmdCompiles with csc.exe from the in-box .NET Framework, so there is nothing to
install. The source targets C# 5 for that compiler — no string interpolation,
no null-conditional operator.
MIT






