Skip to content

Support pinning an account per repository via the github.account git config key - #14269

Closed
bwt615 wants to merge 1 commit into
cli:trunkfrom
bwt615:feat/git-config-account-pin
Closed

bwt615 wants to merge 1 commit into
cli:trunkfrom
bwt615:feat/git-config-account-pin

Conversation

@bwt615

@bwt615 bwt615 commented Aug 27, 2026

Copy link
Copy Markdown

Relates to #12459.

Description

When multiple accounts are logged in for a host, gh resolves a single global active account, changed with gh auth switch. That state is shared by every terminal and directory on the machine. Working across repositories belonging to different accounts means switching back and forth, and concurrent shells race each other: a switch in one terminal silently changes the identity every other terminal acts as, so a gh pr create aimed at a work account can land under a personal one (or vice versa) depending on timing.

This PR adds the opt-in, per-repository pin proposed in #12459: setting the github.account git configuration key to the username of a logged-in account makes token resolution for commands run inside that repository use that account's stored credentials. Because the pin lives in git config it follows the repository rather than the process, and git's conditional includes extend it to whole directory trees (e.g. everything under ~/work/).

Resolution order in ActiveToken:

  1. GH_TOKEN / GITHUB_TOKEN (and the enterprise variants) still win — an environment token is an explicit per-process choice.
  2. A github.account pin, when set and that account has stored credentials (keyring or insecure config) for the hostname.
  3. Otherwise the stored active account, exactly as today.

An unauthenticated pin (for example after gh auth logout) falls back silently to the active account rather than erroring, so a stale pin never breaks a repository.

One correctness detail: SwitchUser captured its rollback token via ActiveToken, which would have let a pin substitute another account's token into the rollback path. The stored-active resolution is extracted into a pin-immune storedActiveToken helper and SwitchUser uses it, so auth switch / auth logout / auth status semantics are unchanged by a pin.

How did you test this change?

Built gh from this branch on macOS with two accounts logged in (bwt615 active, a work account secondary, both keyring-stored), in a fresh git init directory, without ever running auth switch:

Given no github.account key is set
When I run gh api user
Then I see bwt615 (the active account — behavior unchanged)

Given git config github.account <work-account>
When I run gh api user
Then I see the work account (the pin overrides the active account)

Given git config github.account no-such-account-zzz
When I run gh api user
Then I see bwt615 (an unauthenticated pin falls back to the active account)

Given git config github.account <work-account> and GH_TOKEN set to the bwt615 token
When I run gh api user
Then I see bwt615 (the environment token wins over the pin)

Also verified that gh auth token inside the pinned repository returns the pinned account's token, and that gh auth status still reports the stored active account throughout.

Key points

  • The pin is resolved with git config --get github.account (via safeexec), cached once per process; gh never changes its working directory, so the value cannot change between calls. When git is unavailable or the key is unset this costs one failed lookup and behaves exactly as today.
  • The pin affects token resolution only. ActiveUser, auth switch, auth logout, and auth status intentionally keep operating on the stored active account in this PR — surfacing the pin in auth status output is a natural follow-up, left out to keep this reviewable.
  • Also deliberately out of scope, as possible follow-ups: honoring the pin in the git credential helper path, and a GH_ACCOUNT environment variable as a process-scoped equivalent.
  • I'm aware PRs are normally expected to be linked to a help wanted issue, and Feature: Auto-switch accounts based on git config #12459 does not carry that label yet (feat: auto-switch accounts based on git config github.account #12628 was closed on that basis). I've asked on the issue for triage, and I'm opening this anyway in case a concrete, tested implementation is useful for evaluating the proposal — happy to adjust or split it however the team prefers, and no hard feelings if it's closed pending triage.

Notes for reviewers

Start with ActiveToken in internal/config/config.go (the resolution-order change), then pinnedActiveToken, then the SwitchUser rollback change together with TestStoredActiveTokenIgnoresPinnedAccount, which documents why storedActiveToken exists.

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @username will read and reply directly. Name the account.
  • An agent will draft replies and @bwt615 will read them before they are posted.
  • Nobody has explicitly committed to replying.

When multiple accounts are logged in for a host, gh resolves one global
active account, switched with gh auth switch. That state is shared by
every terminal and directory on the machine, so working across
repositories belonging to different accounts means switching back and
forth, and concurrent sessions race each other: a switch in one terminal
silently changes the identity every other terminal acts as.

This adds an opt-in per-repository pin: setting the github.account git
configuration key to the username of a logged-in account makes token
resolution for commands run in that repository use that account's stored
credentials. Environment tokens (GH_TOKEN and friends) still take
precedence, and an unauthenticated pin falls back to the stored active
account unchanged.

SwitchUser's rollback now captures the stored active token through a
pin-immune helper so a failed switch inside a pinned repository cannot
write the pinned account's token into the active slot.

Requested in cli#12459.

Co-Authored-By: Claude Fable 5 <[email protected]>
@bwt615
bwt615 requested a review from a team as a code owner August 27, 2026 05:58
@bwt615
bwt615 requested a review from BagToad August 27, 2026 05:58
@github-actions github-actions Bot added external pull request originating outside of the CLI core team needs-triage needs to be reviewed unmet-requirements and removed needs-triage needs to be reviewed labels Aug 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your pull request! Unfortunately, it doesn't meet the requirements for review:

  • None of the referenced issues have the help wanted label

Please update your PR to address the above. This PR will be automatically closed in 4 days if these requirements are not met.

Full contribution requirements
  1. Include a detailed description of what this PR does
  2. Link to an issue with the help wanted label (use Fixes #123 or Closes #123)

@BagToad BagToad closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team unmet-requirements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants