GitHub CLI (gh) auth¶
gh is the official GitHub CLI. This repo installs it via the devtools ansible role and uses it for issue/PR workflows, the gh-dash extension, and (most importantly for this page) as the credential helper for git push/git fetch over HTTPS.
Two things on this page:
- How to authenticate
ghso HTTPS git operations work without typing a token. - How this repo's chezmoi-managed
~/.gitconfigcoexists withgh auth setup-git— important if you're new to the dotfiles, because there's a non-obvious modify_ script involved.
Quick auth¶
# Interactive: pick https + "Login with a web browser"
gh auth login
# When asked "Authenticate Git with your GitHub credentials?", answer Yes.
# That runs `gh auth setup-git` for you, which writes the credential helper
# into ~/.gitconfig (see next section).
To verify:
gh auth status # token scopes + which account
git config --get-all credential."https://github.com".helper
# Expected:
# (empty line)
# !/opt/homebrew/bin/gh auth git-credential ← path varies per machine
To re-run just the git-credential setup without re-logging in:
To remove:
How gh auth setup-git interacts with chezmoi¶
gh auth setup-git writes two sections into ~/.gitconfig:
[credential "https://github.com"]
helper =
helper = !/opt/homebrew/bin/gh auth git-credential
[credential "https://gist.github.com"]
helper =
helper = !/opt/homebrew/bin/gh auth git-credential
Two non-obvious things about that block:
- The empty
helper =line is intentional, not a bug. It resets any inherited helper (e.g. macOS Keychain'sosxkeychain) forgithub.comsoghis the only authority. Without it, the OS keychain helper would race withghand you'd see "Bad credentials" errors after rotating tokens. - The path to
ghis absolute and machine-specific: - Apple Silicon homebrew:
/opt/homebrew/bin/gh - Intel mac homebrew:
/usr/local/bin/gh - Linuxbrew:
/home/linuxbrew/.linuxbrew/bin/gh - apt / dnf / official
.deb:/usr/bin/gh
The path is hard-coded by gh at the moment you run gh auth setup-git on that machine. There is no portable form (it intentionally bypasses PATH so git doesn't pick up a different gh binary later).
Why a plain ~/.gitconfig template would break this¶
If ~/.gitconfig were a normal chezmoi template, every chezmoi apply would overwrite the entire file — including the [credential "..."] blocks that gh just wrote. The next git push would either:
- Fail with no helper → re-prompt for credentials interactively, or
- Ping-pong: you'd run
gh auth setup-gitagain, and the nextchezmoi applywould clobber it again.
This is exactly the "out-of-band tool rewrites a managed file" problem that chezmoi's modify_ prefix exists to solve.
What this repo actually does¶
~/.gitconfig is managed by modify_dot_gitconfig.tmpl, a modify_-prefixed bash script that runs on every chezmoi apply. The script:
- Reads the current
~/.gitconfigfrom stdin (chezmoi'smodify_contract). - Emits the chezmoi-managed baseline —
[user],[http],[filter "lfs"],[init],[core], optional[delta]blocks, and[include] path = ~/.gitconfig.local. - Re-emits any
[credential "<URL>"]sections found in the live file verbatim, including the gh-injected absolute path.
Net effect: you can run gh auth setup-git once per machine, and chezmoi apply will preserve the helper forever after — no ping-pong, no per-machine template branching, no need to commit machine-specific paths.
The modify_ prefix is documented at chezmoi prefixes. The case study for this specific file is mentioned in the same place.
Three places per-machine git config can live¶
| Place | Managed by | When to use |
|---|---|---|
chezmoi baseline (modify_dot_gitconfig.tmpl) |
This repo | Settings shared across all your machines (user.email, init.defaultBranch, core.pager = delta, …). Edit the template and chezmoi apply. |
[credential "<URL>"] in ~/.gitconfig |
gh auth setup-git |
HTTPS auth via gh. Run gh auth login once per machine; chezmoi preserves it. Don't hand-edit these blocks — let gh own them. |
~/.gitconfig.local |
You, by hand | Per-machine overrides chezmoi can't predict: http.proxy, safe.directory, work-account email overrides, [includeIf "gitdir:..."] for separate identities, [url "..."] rewrites for a corporate mirror, etc. Loaded via [include] from the baseline. Git-ignored by chezmoi. |
The three layers compose left-to-right (later wins for the same key), which mirrors the ~/.zshrc → ~/.zshrc.local → ~/.zshrc.adhoc pattern used elsewhere in this repo.
What is NOT preserved by the modify_ script¶
The awk filter inside modify_dot_gitconfig.tmpl matches only ^[credential "<URL>"] (with a quoted URL scope). Anything else in the live ~/.gitconfig is replaced by the baseline on the next apply. In particular:
- A bare
[credential](no URL scope) is not preserved. [user],[core],[alias], etc. that you hand-edited into~/.gitconfigare silently overwritten. Use~/.gitconfig.localfor those.[credential.helper]style flat keys (no section header) are not preserved. Stick to the URL-scoped section form, which is whatgh auth setup-gitwrites.
If you need to preserve another section type (e.g. [includeIf "gitdir:~/work/"] written by some other tool), the awk rule is the single place to extend — see the script for the pattern.
Troubleshooting¶
fatal: could not read Username for 'https://github.com': terminal prompts disabled in CI / non-interactive shell after chezmoi apply:
- Check the credential block survived:
git config --get-all credential."https://github.com".helpershould show two lines (empty +!.../gh auth git-credential). - If the second line is missing,
gh auth setup-gitwas never run on this machine. Rungh auth login(orgh auth setup-gitif a token is already stored). - If both lines were there but git still prompts,
ghitself may be unauthenticated:gh auth statusshould print a green check, not "not logged in".
gh auth status prints token but git push still prompts:
- The absolute path in the helper line points to a
ghbinary that no longer exists (you migrated mac → mac via Migration Assistant, or rebuilt Linuxbrew). Re-rungh auth setup-gitto refresh the path; chezmoi will preserve the new one.
chezmoi diff ~/.gitconfig shows churn after every gh operation:
- Expected to be empty. If it isn't, the awk filter probably doesn't recognise the section header gh wrote (older or newer
ghversions might use a different scope URL). Open the live file and the rendered output side by side:chezmoi cat ~/.gitconfig | diff - ~/.gitconfig. If the diff is in a[credential "<URL>"]block, the script has a bug — file an issue.
Finding & cloning your repos¶
gh repo list is non-interactive and stops at 30 rows. To fuzzy-browse all your repos by name + description — preview the README, then clone / open / copy — this repo ships a picker in two twinned surfaces:
ghrepo(shell function,dot_config/shell/41_github.sh) — fzf overgh repo list --limit 4000with agh repo viewREADME preview:Enter→ clone into${GHREPO_ROOT:-$PWD}/<repo>andcdinto itAlt-O→ open on github.com ·Ctrl-Y→ copy the repo URLtv github-repos(Television channel) — same browse / open / copy. A tv action can'tcdyour shell (subprocess), soAlt+Cclones into the launch cwd without cd; useghrepofor the clone-and-work flow.
GitLab has the exact twins — glrepo + tv gitlab-repos (via glab repo list --mine). Self-hosted GitLab: export GITLAB_HOST=git.example.com.
Prefer a full TUI? gh select (remcostoeten/gh-select, installed by the devtools role) is a Raycast-like repo browser — fuzzy search, tree view, sparse clone. It's GitHub-only and (like the tv channel) can't cd your shell after cloning, so it complements ghrepo rather than replacing it.
The list is cached in ~/.cache/tv/ (stale-while-revalidate; GHREPO_CACHE_TTL default 1 h): the first fetch of 300+ repos takes ~15 s, but every launch after that opens instantly and revalidates in the background. Ctrl-R inside ghrepo/glrepo (or Alt+R in the tv channels) forces a live refresh. The cache is produced by the shared helper ~/.config/television/g{h,l}-repos-source.sh, used by both the functions and the channels.
Set GHREPO_ROOT / GLREPO_ROOT to clone into a fixed repo root instead of the current dir — handy for pairing with try-cli's graduate flow (TRY_PROJECTS). See also tv.md → github-repos / gitlab-repos channels.
Related¶
- chezmoi prefixes —
modify_/create_/_removesemantics in this repo - Agent overlays — the same modify_ pattern applied to Claude / OpenCode / Codex / Cursor configs
- git diff workflow — how this repo uses
gh pr diff, delta,gh-dash, etc.