Skip to main content

Manage multiple GitHub identities for git operations.

Project description

ghswitch

A small CLI for juggling multiple GitHub identities (personal, work, client, …) across git clone, git push, and per-repo user.name / user.email.

  • Stores profile metadata in $XDG_CONFIG_HOME/ghswitch/profiles.json (0600)
  • Stores Personal Access Tokens in the OS keychain via keyring (macOS Keychain, Windows Credential Manager, GNOME Secret Service). Falls back to a 0600 JSON file if keyring isn't installed.
  • Picks the right profile from folder patterns or the SSH host alias in the repo's origin URL.
  • Cross-platform: Linux, macOS, Windows (WSL or native Python).

Install

pip install ghswitch-cli                  # core
pip install 'ghswitch-cli[keyring]'       # recommended — uses OS keychain for tokens

That installs a ghswitch console script. Verify with ghswitch --help.

If you'd rather not install: python3 ghswitch.py … works the same way.

Setup walkthrough

1. Generate distinct SSH keys (recommended)

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_personal -C "you@personal"
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work     -C "you@work"

Add each public key to the respective GitHub account.

2. Define SSH host aliases

Append to ~/.ssh/config:

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

Host github.com-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_work
    IdentitiesOnly yes

3. Register profiles

ghswitch add personal \
  --username alice \
  --email alice@personal.dev \
  --ssh-key ~/.ssh/id_ed25519_personal \
  --host-alias github.com-personal \
  --folder '~/code/personal/*' \
  --set-default

ghswitch add work \
  --username alice-corp \
  --email alice@workco.com \
  --ssh-key ~/.ssh/id_ed25519_work \
  --host-alias github.com-work \
  --folder '~/code/work/*'

For HTTPS + PAT instead of SSH, add --prompt-token (hidden input) or --token-stdin (read from stdin), and skip --ssh-key/--host-alias.

Commands

Command Purpose
ghswitch list List all profiles. The default is marked with *.
ghswitch add <name> [...] Add or update a profile. With no flags, prompts interactively.
ghswitch remove <name> Delete a profile and its stored token.
ghswitch use <name> Apply a profile to the current repo (user.name, user.email, core.sshCommand). Add --rewrite-remote to also flip the origin URL to the profile's host alias.
ghswitch clone <url> [profile] [dir] Clone with the chosen profile; auto-detects from URL host alias / folder patterns / default.
ghswitch status Show the current repo's identity and which profile (if any) it matches.
ghswitch whoami Print the default profile name.
ghswitch completion bash|zsh Emit a completion script.

Example workflows

Cloning into a work folder, hands-off

work has --folder '~/code/work/*' configured.

cd ~/code/work
ghswitch clone git@github.com:workco/api.git
# → auto-detects "work", rewrites URL to git@github.com-work:workco/api.git,
#   sets core.sshCommand to use the work key, and pins user.name/email.

Pushing from a personal repo

cd ~/code/personal/dotfiles
ghswitch use personal --rewrite-remote
git push

use --rewrite-remote flips origin to git@github.com-personal:owner/repo.git so subsequent git pull/push ride the right SSH key without env vars.

Verifying

ghswitch status
# repo:       /Users/alice/code/work/api
# remote:     git@github.com-work:workco/api.git
# user.name:  alice-corp
# user.email: alice@workco.com
# ssh:        ssh -i /Users/alice/.ssh/id_ed25519_work -o IdentitiesOnly=yes
# profile:    work  <- matches stored profile

Shell completion

# bash
ghswitch completion bash >> ~/.bash_completion
# zsh
ghswitch completion zsh > ~/.local/share/zsh-completions/_ghswitch

Auto-switching rules

When a profile match is needed (e.g. during clone without an explicit profile), ghswitch checks, in order:

  1. Folder patterns — longest-matching pattern wins. Patterns support ~ expansion and fnmatch-style globs.
  2. SSH host alias — if origin (or the URL passed to clone) uses a host matching a profile's host_alias.
  3. Default profile — set via add --set-default or implicitly the first profile added.

If none of these resolve and you didn't pass a name, clone exits with an error.

GitHub CLI integration

gh is not required, but if it's installed ghswitch plays well with it: SSH-based clones go through your ~/.ssh/config host aliases regardless of which tool you use, and HTTPS clones can use the PAT stored under the matching profile in your OS keychain.

Security notes

  • profiles.json and the fallback secrets.json are written 0600.
  • PATs are never written into git remote URLs on disk; for HTTPS clones the token is injected into the URL only for the git clone invocation, and the remote is reset to the canonical URL afterward.
  • core.sshCommand is set with IdentitiesOnly=yes so SSH won't fall back to another agent-loaded key.
  • Removing a profile also deletes its token from the keychain.

Development

pip install -e '.[dev]'
pytest

CI is in .github/workflows/:

  • test.yml — runs pytest on Linux, macOS, and Windows across Python 3.9 / 3.11 / 3.13 on every push and PR.
  • release.yml — on vX.Y.Z tag pushes, builds sdist + wheel, verifies the tag matches pyproject.toml's version, and creates a GitHub Release with auto-generated notes. PyPI trusted publishing is wired up but commented out — uncomment the pypi-publish job after configuring trusted publishing for the project.

To cut a release:

  1. Bump version in pyproject.toml and commit + push
  2. Go to Actions → release → Run workflow and enter the version number

The workflow creates the tag, builds sdist + wheel, creates a GitHub Release, and publishes to PyPI automatically.

Uninstall

pip uninstall ghswitch
rm -rf ~/.config/ghswitch        # Linux/macOS
# or %APPDATA%\ghswitch on Windows

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ghswitch_cli-1.0.0.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ghswitch_cli-1.0.0-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file ghswitch_cli-1.0.0.tar.gz.

File metadata

  • Download URL: ghswitch_cli-1.0.0.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ghswitch_cli-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1589cfa9f9483b74d7f8105057b61fc3a4b25b22198c0f34330aa78ed78ed9b8
MD5 4049138e83cd193064e21e171cc78e2f
BLAKE2b-256 d0ce1cbfe851773a1b72dec06aa13bff63507b271fd64f74d3be2cc1631a47b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghswitch_cli-1.0.0.tar.gz:

Publisher: release.yml on saugat86/ghswitch-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ghswitch_cli-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: ghswitch_cli-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ghswitch_cli-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a6eefbbc2577e743835639f01a8712092652a1bccae74b4416521c832b552911
MD5 099921357bb42fe28892eccfa5c70296
BLAKE2b-256 5279da5cdbd2f1033cc244720108381d96ecc6e95718b2a669a1ba47f663f740

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghswitch_cli-1.0.0-py3-none-any.whl:

Publisher: release.yml on saugat86/ghswitch-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page