Metadata-Version: 2.5
Name: gwitch
Version: 0.3.0
Summary: Switch between git/GitHub accounts per repository.
Project-URL: Homepage, https://github.com/isamrish/gwitch
Project-URL: Repository, https://github.com/isamrish/gwitch
Project-URL: Issues, https://github.com/isamrish/gwitch/issues
Author-email: Amrish Kushwaha <askmaurya48@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: toml>=0.10
Requires-Dist: typer>=0.9
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# gw

[![CI](https://github.com/isamrish/gwitch/actions/workflows/ci.yml/badge.svg)](https://github.com/isamrish/gwitch/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/gwitch.svg)](https://pypi.org/project/gwitch/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Switch between multiple git/GitHub accounts, per repository, and see which
account is active right in your shell prompt.

## Install

    pipx install gwitch

Published on PyPI as `gwitch`; installs the `gw` command. Or, from a
checkout of this repository:

    pipx install .

## Quick start

    gw account add personal
    gw account add work

    cd ~/code/some-repo
    gw use personal
    gw status

## Shell prompt

    gw init zsh    # or bash / fish

prints a small shell function, plus a one-line instruction for wiring it
into your prompt — paste both into your shell config yourself. `gw` never
edits your shell rc files automatically.

    gw init zsh --install    # or bash

`--install` does the wiring for you, for `zsh`/`bash` only (not `fish`
yet): it writes a `gw-prompt` script to `~/.local/bin/gw-prompt` and a
`gw-check` script to `~/.local/bin/gw-check` (the cd-triggered account-check
described below), and appends two marked blocks to your rc file — one per
script. It's idempotent — safe to re-run. Note for macOS bash users:
`~/.bashrc` isn't read by login shells (Terminal and iTerm start bash as a
login shell), so unless you've already wired `~/.bashrc` to be sourced from
`~/.bash_profile`, you'll need to add that yourself for the installed
integration to take effect.

## What `gw use <account>` changes

- `user.name` / `user.email` — local to the current repo only (`git config --local`)
- `core.sshCommand` — forces the SSH key registered for that account
- `credential.https://<host>.helper` — scoped to this repo and this host, so HTTPS pushes/pulls use this account's token instead of whatever's cached in the OS keychain or `gh`'s globally active account (see below)
- `gw.account` — a marker read by `gw status` and the shell prompt function
- `gw-remote-map.<remote URL>.account` — records this repo's remote in **global** git config, for the cd-triggered mismatch check below

`gw use` also records this repo's remote-to-account mapping in **global**
git config (for the cd-triggered mismatch check, see below) — everything
else stays local-only. None of this touches `~/.ssh/config` or ssh-agent.

## HTTPS credential support

If a repo's remote uses HTTPS instead of SSH, `gw use <account>` sets a
repo-scoped git credential helper (`credential.https://<host>.helper`,
cleared and reset each time you switch, so it's never a blanket override —
only this repo, only this host) that points back at `gw credential-helper`.
When git needs to authenticate an HTTPS push/pull, it invokes that
internal command, which looks up the account currently active in this
repo and fetches a token for it via `gh auth token` — `gw` never stores a
credential itself, it just asks `gh` for the one it already has. This
gives an HTTPS remote the same per-repo isolation SSH already had, without
a stale token cached by the OS keychain or `gh`'s globally active account
silently winning instead.

Requires the `gh` CLI to be installed and already logged in to the account
in question; if it isn't, `gw credential-helper` produces no output and
git falls back to its normal (interactive) credential prompt for that
repo, rather than silently reusing a wrong cached credential.

## Remote-account mismatch check

With `gw init <shell> --install`, a `gw-check` script is wired to run every
time your shell `cd`s into a different directory. If the repo you land in
has an `origin` remote that `gw use <account>` has previously recorded, and
the account currently active in that repo doesn't match, it prints a
warning to stderr telling you which account to switch to. This cd-triggered
check never blocks or changes anything on its own — it's purely
informational.

For a blocking version of the same check — useful since the cd-triggered
check above only fires in an interactive shell and is invisible to AI
coding agents, CI, or any other non-interactive git caller — run `gw
install-hooks` once in a repo. It installs `pre-commit`/`pre-push` git
hooks that run the same check and, on a real mismatch, reject the commit
or push outright (exit 1). The block message always names git's own
`--no-verify` flag as the way to bypass it deliberately. If the repo
already has a `pre-commit`/`pre-push` hook, `gw install-hooks` preserves it
(renamed to `<hookname>.gw-original`) and chains to it after its own check
passes, rather than overwriting it.

## Known limitations

**`gh` (GitHub CLI) authentication is global, not per-repo.** GitHub CLI's
authenticated account is global to your machine (`gh` keeps one active user
per host), not per-repo — that's inherent to how `gh` itself works. `gw use
<account>` will call `gh auth switch` for you, which changes `gh`'s active
account machine-wide, even for other repos/terminal tabs, until you `gw use`
a different account somewhere else. Your git identity and SSH key stay
correctly isolated per-repo regardless.

`gw status` shows both this repo's git identity and gh's currently active
account as separate lines, so if they've drifted apart it's visible rather
than silent.

**Linked git worktrees share git identity.** `git config --local` in a
linked worktree (created via `git worktree add`) resolves to the *same*
shared config file as the main checkout — git only isolates local config
per-worktree if the repo opts into `extensions.worktreeConfig`, which `gw`
does not assume. That means `gw use <account>` run inside a linked worktree
changes the identity for the main checkout and every sibling worktree of
that repo too, not just the worktree you ran it in. `gw use` detects this
and prints a warning when you run it inside a linked worktree, but it does
not block the switch or set up per-worktree config automatically.

**The remote-account check is last-write-wins, URL-exact, and cd-triggered
only for the shell-integration version.** The `gw-remote-map` registry has
no per-clone tracking: running `gw use <account>` anywhere for a given
remote overwrites what "the right account" means for every other clone of
that remote — this is intentional, not a bug. It also matches on the exact
remote URL string, so an SSH clone and an HTTPS clone of the same repo are
treated as two unrelated remotes. The cd-triggered `gw-check` script only
runs on `cd`, so a shell already sitting in a mismatched repo when it
starts won't warn until you `cd` away and back — `gw install-hooks`'s
blocking hooks don't have this gap, since they fire on the git action
itself.

## Commands

- `gw account add <name>` — register an account (prompts for git name/email,
  GitHub username; can generate a new SSH key for you)
- `gw account list` — list registered accounts
- `gw account remove <name>` — remove a registered account
- `gw account edit <name>` — interactively update an existing account's
  git name/email, GitHub username/host, or SSH key path (current values
  are the defaults; press Enter to keep any of them)
- `gw use <name>` — activate an account for the current repo
- `gw status` / `gw whoami` — show the active account for the current repo
- `gw init <shell>` — print the shell prompt snippet for `zsh`, `bash`, or
  `fish`; add `--install` (`zsh`/`bash` only) to write and wire it up automatically
- `gw install-hooks` — install blocking `pre-commit`/`pre-push` git hooks in
  the current repo that reject a commit/push when the wrong account is
  active (bypassable with `git`'s own `--no-verify`)
- `gw uninstall-hooks` — cleanly reverse `gw install-hooks`: removes the
  gw-managed hooks and restores any hook they chained to. Leaves the shared
  `gw-check` script (used by `gw init --install`'s `cd`-triggered warning)
  alone — that script is shared machine-wide state, not scoped to this repo
- `gw credential-helper <get|store|erase>` — internal, invoked by git itself
  as this repo's HTTPS credential helper; not meant to be run directly
- `gw doctor` — check that everything is set up correctly: `gh` CLI
  installed, each account's SSH key present, `gh` login status, shell
  prompt/remote-check integration, repo hooks, and whether a newer `gwitch`
  is available on PyPI; prints a remedy for anything that needs fixing

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup and how to run the
tests. Please follow the [Code of Conduct](CODE_OF_CONDUCT.md). For security
issues, see [SECURITY.md](SECURITY.md) instead of opening a public issue.

## License

[MIT](LICENSE)
