A native Windows and macOS 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.
Download the macOS .dmg or Windows .exe from Releases.
On Mac, open the disk image and drag the app to Applications. On Windows, run
the portable executable directly.
The source build instructions below are an alternative to downloading a release.
Requires macOS 13 Ventura or later and Git 2.36 or later.
Current macOS releases are ad-hoc signed and have not been notarized by Apple. On first launch, macOS may show “Apple could not verify GitHub Accounts is free of malware that may harm your Mac or compromise your privacy.”
If you downloaded the app from this repository's Releases and choose to trust it:
- Open the
.dmgand drag GitHub Accounts to Applications. If updating, quit the old app first and choose Replace when prompted. - In Finder, open Applications and double-click GitHub Accounts.
- When the warning appears, click Done or Cancel to dismiss it and keep the app.
- Immediately open System Settings → Privacy & Security. Scroll down past the privacy permissions to the Security section.
- Click Open Anyway beside the message about GitHub Accounts, authenticate when prompted, then confirm Open.
If Open Anyway is missing: close System Settings, try opening the installed
app again to trigger a fresh warning, dismiss it, and immediately reopen
Privacy & Security. Scroll all the way down to Security; the option
appears after a blocked launch attempt. Make sure you are opening the copy you
just installed in /Applications, rather than an older copy or the app inside
the mounted disk image. Source builds installed by install-macos.sh live in
~/Applications, which is a different folder.
On a Mac managed by an employer or school, these options may be restricted; contact the administrator if they remain unavailable. See Apple's instructions for opening apps safely.
Install Apple's Command Line Tools with xcode-select --install if they are not
already present.
No .NET, Homebrew, Python, or Swift package dependencies are needed.
git clone https://github.com/web-dev-nav/multi-git-holder
cd multi-git-holder
sh install-macos.sh
open "$HOME/Applications/GitHub Accounts.app"The installer builds a universal app for Apple Silicon and Intel, installs it
in ~/Applications, and keeps a backup of a previous app installation. To build
only for your current Mac, use ARCH="$(uname -m)" sh install-macos.sh.
In the macOS app:
The Mac interface follows the Windows layout: Repositories, Accounts, and Folders tabs, compact tables and toolbars, and a status bar.
- On Accounts, use Detect keys… to verify existing
~/.ssh/*.pubkey pairs with GitHub and save/apply their routing. Select an account to use Edit routing, Remove, Test SSH, or Copy public key. - Add account… opens the same four-step flow as Windows: account details, choose or generate a key, give its public half to GitHub, then verify and finish. The saved username comes from GitHub's authentication response.
- Add organizations using Edit routing. Use Test repository… to check an exact repository URL with the selected account without cloning it.
- Add parent folders in Folders, set Search depth, and choose Rescan now to return to Repositories.
- On Repositories, select one or more rows, choose an account, and use Apply to switch them. Fix all mismatches restores owner-based routing across the scanned repositories. Both actions ask for confirmation before changing origin URLs and local commit settings. Open folder opens the selection in Finder. Existing commits are unchanged.
For an encrypted key, first unlock it with
ssh-add --apple-use-keychain ~/.ssh/your_key. Key generation creates a new
ed25519 key without a passphrase and never overwrites an existing key.
The macOS app reads and writes the same PascalCase JSON format at
~/.gh-accounts.json as Windows. It backs up affected configuration files before
applying routing and preserves text outside the managed markers. It rejects
conflicting owner assignments, malformed marker blocks, and managed-file symlinks
rather than overwriting them. Private keys are never read into the app or uploaded;
OpenSSH uses them to authenticate. Key paths must point to files on this Mac;
Windows key paths are not automatically migrated.
The included CLI uses the same routing engine:
"bin/GitHub Accounts.app/Contents/MacOS/gh-accounts" detect
"bin/GitHub Accounts.app/Contents/MacOS/gh-accounts" setup-existing
"bin/GitHub Accounts.app/Contents/MacOS/gh-accounts" test-repository https://github.com/owner/repo.gitdetect verifies keys without changing routing; setup-existing verifies them
again, saves the accounts, and applies the configuration. GHACCT_HOME can point
at an isolated home directory for testing. The CLI and app do not switch gh's
independently authenticated account.
Unlike Windows, the macOS app reports a global core.sshCommand override instead
of deleting it. Review its source with
git config --show-origin --get core.sshCommand if routing fails. Removing an
account on macOS keeps its SSH keys and identity file; only the routing is removed.
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.
On Windows, 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. Both interfaces provide Apply for selected repositories and Fix all mismatches for the scanned list.
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 on
Windows or ~/.gh-accounts.json on macOS.
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. - Windows clears
core.sshCommand; macOS reports it. If it points at some other ssh config, the aliases won't resolve and every push fails withCould not resolve hostname github-<account>. Both apps verify 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.
On macOS, quit the app and move ~/Applications/GitHub Accounts.app to Trash.
Keys and account routing are retained. Remove the marked blocks from
~/.gitconfig and ~/.ssh/config to stop routing, and remove
~/.gh-accounts.json and the generated ~/.gitconfig-<account> files if desired.
On Windows:
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.
sh build-macos.sh
sh test-macos.sh
sh package-macos.shThe build produces bin/GitHub Accounts.app, containing a SwiftUI interface and
a command-line helper, with arm64 and x86_64 slices. Set ARCH=arm64 or
ARCH=x86_64 for a single architecture. Tests use a temporary home directory and
real Git/SSH parsers, without changing your account configuration or making
network requests. The packaging script creates bin/GitHub-Accounts-macOS.dmg
with the app and an Applications shortcut, then verifies the disk image.
The CI workflow uploads the DMG and the standalone Windows executable.
The app is ad-hoc signed for local use. Public distribution outside a local build requires a Developer ID signature and notarization to avoid Gatekeeper prompts.
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.
Both platforms use the same blue/purple two-account icon. To regenerate the
Windows ICO, macOS ICNS, and source PNG sizes, run
swift macos/GenerateIcons.swift on a Mac and commit the updated assets/ and
src/app.ico files. The Mac build bundles the ICNS; Windows embeds the ICO in
the executable and uses it for its windows and taskbar.
Push a version tag to build and publish both apps automatically:
git pull --ff-only
git tag -a v1.0.1 -m "GitHub Accounts v1.0.1"
git push origin v1.0.1Use a new vMAJOR.MINOR.PATCH version for each release. In Actions → Build and
release, GitHub tests and builds the universal macOS app, builds the Windows
executable, then publishes a release containing GitHub-Accounts-macOS.dmg and
GitHub-Accounts-Windows.exe. Publication only runs after both builds succeed.
The Mac app's bundle version is taken from the tag.
For a local Mac build without publishing, run sh build-macos.sh; optionally set
APP_VERSION=1.0.1. The bundle is written to bin/GitHub Accounts.app.
MIT






