gh is the official GitHub CLI: a single Go binary that lets you create pull requests, triage issues, watch CI runs, and call the GitHub REST and GraphQL APIs without ever opening a browser tab. The killer features are gh pr create (one command from branch to open PR), gh api --paginate --jq (full REST/GraphQL access wired into shell pipelines), and gh run watch (live CI status that exits non-zero when the workflow fails — perfect for scripts). Once you install it and run gh auth login once, every later GitHub interaction stays inside your terminal.
Plain git ends at git push. Everything that happens after — opening the PR, picking reviewers, reading review comments, watching the CI matrix turn green, merging, deleting the branch, posting the release — has historically meant alt-tabbing to a browser, clicking through three menus, and breaking your flow every single time. The cost is invisible but enormous: a 2026 SmartScope analysis puts the average developer at 20 minutes a day on PR-related browser work, and points out that most of it is mechanical clicking that a single gh command replaces.
GitHub CLI is GitHub's own answer to the problem. It is written in Go — the only non-Rust tool in this CLI series — and that's the right call: Go has a first-class HTTP client, cross-compiles to a single binary on every platform, and reuses GitHub's existing internal SDKs. The result is a tool that feels like it was designed to live next to git itself. This article walks through the daily-driver patterns: PR workflows, issue triage, gh api scripting, and CI watching, with the recipes you'll actually use.
Why gh CLI bridges your terminal and GitHub
git was finished in 2005 and it still does its job perfectly. But the platform layer on top — pull requests, code review, issue tracking, GitHub Actions — was bolted on by a company a decade later, and for years the only way to use it was the website. That's the gap gh fills.
The leverage gh gives you comes from removing five specific kinds of context switch:
- Opening a PR. Without
gh, you push your branch, copy the URL the remote prints, paste it in a browser, click "compare & pull request," fill in the form, click submit. Withgh pr create --fill, the title and body are pulled from your commits and the PR opens in roughly half a second. - Reading review comments. The default GitHub UI buries inline comments under tabs and timelines.
gh pr view 123 --commentsdumps every comment, threaded, into your terminal where you can grep, scroll, and copy code. - Checking CI status.
gh pr checksshows the live state of every workflow attached to a PR, andgh run watchsits on a live workflow with a--exit-statusflag that lets you chain it with&& notify-send done. - API scripting. Need to list every open issue across an organization, every release tag, every PR labeled
bug?gh api --paginate --jqiscurl + jq + an auth token + paginationin one command, with the auth handled bygh auth loginonce. - Custom workflows.
gh extension installlets you bolt on community extensions or write your own —gh dashfor a TUI dashboard,gh poifor branch cleanup,gh-pr-awaitfor PR change polling.
Every one of these would be a half-day shell script if you had to do it from scratch with curl and a hand-rolled OAuth flow. gh ships with all of that already wired up.
Install gh and authenticate
Installation is one package and one interactive login. The official installation page lists every platform; the short version is below.
# winget (built into Windows 11)
winget install GitHub.cli
# or Scoop
scoop install gh
# or Chocolatey
choco install gh
After installation, run gh --version and then authenticate once:
gh auth login
This walks you through an interactive prompt: pick GitHub.com (or a GitHub Enterprise host), pick HTTPS or SSH for git operations, and choose Login with a web browser. gh prints an 8-character device code and opens the browser — you paste the code, approve the OAuth scopes, and the token is stored locally in the OS keychain (macOS Keychain, Windows Credential Manager, libsecret on Linux).
Once authenticated, two commands sanity-check the install:
gh auth status
gh api user --jq '.login'
The first prints which host you're logged into and which scopes the token holds. The second hits the GitHub API as you and prints your username — if both work, every other command in this article will work.
PR workflows: from gh pr create to gh pr merge
This is the section that pays for the install. The full daily PR loop fits into about six commands.
Creating a PR
After git push -u origin my-branch, the basic create is one line:
gh pr create --fill
--fill reads the most recent commit message and uses it as the title and body. If you want explicit control:
gh pr create \
--title "Fix delta hover-state regression" \
--body "Closes #142. The hover handler was firing twice." \
--base main \
--reviewer omitsu-dev,@studio-mitsu/reviewers \
--label bug,frontend \
--assignee @me \
--draft
The official examples cover the rarer flags (--project, --milestone, --template). One pattern worth remembering: --reviewer accepts both individuals and @org/team slugs in the same flag, so a single line can request review from a person and a whole team at once.
If your branch isn't pushed yet, gh pr create will offer to push it for you — answer yes and the branch lands on the remote and the PR opens in the same step.
Viewing and reading a PR
gh pr view 142 # Markdown summary in your terminal
gh pr view 142 --comments # Plus every review comment, threaded
gh pr view 142 --web # Open in browser if you need the rich UI
gh pr diff 142 # Plain unified diff
gh pr diff 142 | delta # Pipe to delta for syntax-highlighted side-by-side
That last line is the reason we covered delta in the previous article. gh pr diff | delta is the closest a terminal gets to the GitHub Files Changed tab — and it's faster.
Reviewing and approving
gh pr checkout 142 # Locally check out the PR branch (handles forks)
gh pr review 142 --approve --body "LGTM, ship it"
gh pr review 142 --request-changes --body "See inline comments"
gh pr review 142 --comment --body "Some questions below"
gh pr checkout is the one to remember — it handles cross-repo forks (user:branch) and configures the upstream tracking branch correctly so subsequent git push works without surprises.
Merging and cleanup
gh pr merge 142 --squash --delete-branch
gh pr merge 142 --merge --auto # enable auto-merge: waits for required checks
gh pr merge 142 --rebase --delete-branch
The --auto flag is the one that changes how you work: enable it on a draft PR, mark the PR ready for review, and gh will merge it the moment all required checks pass. No more babysitting CI.
Issue workflows: search, triage, and comment
Issues get less love than PRs, but gh issue is where bulk maintenance gets fast. The issue search syntax is the same as the web UI, which means everything you already know transfers.
# List open issues assigned to you
gh issue list --assignee @me --state open
# Search across labels and authors
gh issue list --search "is:open label:bug author:omitsu-dev"
# Create one quickly
gh issue create --title "Hover state breaks on mobile Safari" \
--body "Reproduced on iOS 18.3. See screenshot." \
--label bug,mobile
# Read it
gh issue view 142 --comments
# Comment, close, reopen
gh issue comment 142 --body "Fixed in #198."
gh issue close 142 --reason completed
gh issue reopen 142
The combination that earns its keep is bulk operations through pipes. Suppose you want to close every issue tagged wontfix that hasn't been touched in six months:
gh issue list \
--label wontfix \
--search "updated:<2025-10-10" \
--json number \
--jq '.[].number' |
xargs -I{} gh issue close {} --reason "not planned"
Two things to notice. First, --json returns structured JSON instead of the human table — that's the magic flag that turns gh from a UI into a scripting tool. Second, the result is piped through xargs exactly the way the xargs guide describes. This is the moment gh stops being a convenience and starts being a force multiplier.
gh api: scripting GitHub with jq and pagination
gh api is the part that turns gh from a productivity tool into the foundation of your automation. It is curl plus authentication plus pagination plus JSON filtering, all in one command, with your existing gh auth token.
The basic shape:
gh api repos/cli/cli/releases/latest --jq '.tag_name'
# v2.89.0
That's the whole pattern. gh api PATH is a GET against https://api.github.com/PATH, with your token already attached. --jq runs the response through jq and prints the result. No headers, no pagination, no auth — gh handles all of it.
Filtering and templating
# List the last 10 releases of a repo, tag + date
gh api repos/cli/cli/releases \
--jq '.[0:10] | .[] | "\(.tag_name)\t\(.published_at)"'
# Same data via Go templates instead of jq
gh api repos/cli/cli/releases \
-t '{{range .}}{{.tag_name}}{{"\t"}}{{.published_at}}{{"\n"}}{{end}}'
The -t flag is a sleeper feature: when you don't have jq installed (a stripped-down container, for instance), Go templates do most of what you need.
Pagination
GitHub paginates almost every list endpoint at 30 items by default. To get all of them:
gh api --paginate repos/cli/cli/issues --jq '.[].number'
--paginate follows the Link: rel="next" header until the API runs out of pages. There is one current limitation worth knowing about: per issue #10459, --paginate --slurp --jq cannot be combined. Use either --paginate --jq (which runs jq once per page) or --paginate --slurp (which collects everything into one JSON array first, then you pipe through jq separately).
POST, PATCH, DELETE
# Comment on an issue
gh api repos/omitsu-dev/32blog/issues/142/comments \
-f body='Fixed in 0a1b2c3.'
# Add a label
gh api repos/omitsu-dev/32blog/issues/142/labels \
-f labels[]=bug -f labels[]=frontend
# DELETE a release
gh api -X DELETE repos/omitsu-dev/32blog/releases/12345
-f sends a static string field, -F sends a typed field (numbers, booleans, file contents via @path). The @- syntax even reads stdin, which makes cat body.md | gh api ... -F body=@- trivial.
GraphQL
The same command speaks the GraphQL endpoint. The official GitHub blog walkthrough is worth bookmarking.
gh api graphql -F owner=cli -F name=cli -f query='
query($owner:String!, $name:String!) {
repository(owner:$owner, name:$name) {
stargazerCount
defaultBranchRef { name }
}
}
' --jq '.data.repository'
GraphQL pagination needs a pageInfo { hasNextPage, endCursor } clause and the magic $endCursor: String variable — see the gh api manual for the full template. With those in place, --paginate --slurp walks every page automatically.
gh run watch, extensions, and aliases
The last layer is everything that turns gh into your personal terminal dashboard.
Watching CI
gh run list # Recent workflow runs
gh run view 1234567890 # One run, with job/step breakdown
gh run view 1234567890 --log # Stream all logs
gh run view --log-failed # Just the failing steps
gh run watch # Live status of the latest in-progress run
gh run watch --exit-status # Exit non-zero if it fails (CI-friendly)
gh run rerun 1234567890 --failed # Rerun only the failed jobs
gh pr checks # Status of every check on the current PR
gh run watch --exit-status is the keystone for scripts. Per the gh run watch manual, it polls every 3 seconds (configurable with -i) and exits non-zero if the workflow fails — so you can chain it with anything:
git push && gh run watch --exit-status && notify-send "Build green"
The notification fires when the build is actually green, not when you remembered to check the browser tab.
Extensions
gh extension install dlvhdr/gh-dash # TUI dashboard for PRs and issues
gh extension install yusukebe/gh-markdown-preview
gh extension list
gh extension upgrade --all
gh extension remove gh-dash
The extension manual covers writing your own — an extension is just an executable named gh-foo on your $PATH, so a one-line bash script counts.
Aliases
gh alias set prs 'pr list --author @me'
gh alias set bugs 'issue list --label bug --state open'
gh alias set ship 'pr merge --auto --squash --delete-branch'
gh alias list
Aliases live in ~/.config/gh/config.yml and are simple string substitutions. For more flexible shorthand (multi-step workflows, conditional logic), pair this with the shell alias guide and write a wrapper function instead.
About the gh-copilot extension
If you're searching for gh copilot suggest in 2026, the answer has changed: the gh-copilot extension was retired on October 25, 2025. The replacement is the standalone GitHub Copilot CLI (binary name copilot, currently at v1.0.22 as of April 9, 2026), which is no longer a gh extension at all — it's a separate agentic assistant you install with brew install copilot-cli or npm install -g @github/copilot. The old ghcs/ghce aliases still work for users who installed the extension before retirement, but new installs should use the standalone tool.
FAQ
Q. Is gh the same as git?
No. git manages local commits and branches; gh manages everything that lives on GitHub itself — PRs, issues, releases, workflow runs, and the API. They're complementary. gh doesn't replace any git command; it adds the GitHub-specific layer on top.
Q. Does gh work with GitHub Enterprise Server?
Yes. Pass --hostname your-ghes.example.com to gh auth login and every subsequent command targets that host. You can be logged into multiple hosts at once and switch between them with gh auth switch.
Q. Can gh create draft PRs from a script?
Yes — gh pr create --draft --fill is the one-liner. To promote a draft to ready: gh pr ready 142.
Q. How do I list every PR I've ever opened across all repos?
gh search prs --author @me --state all --limit 1000. The gh search family hits GitHub's global search and is the right tool for cross-repo queries — much faster than looping through gh pr list per repository.
Q. How do I read the output of gh api in a shell variable?
Use command substitution and --jq to pull out exactly the field you need:
tag=$(gh api repos/cli/cli/releases/latest --jq .tag_name). Always quote the variable when you reuse it.
Q. What's the difference between gh run watch and gh pr checks?
gh pr checks is a one-shot status report on the current PR's workflows. gh run watch is a live tail that blocks until a single workflow run finishes — and exits non-zero on failure, which makes it scriptable.
Q. Is gh written in Go because GitHub is a Go shop?
Mostly. GitHub's internal services are a mix of Ruby and Go, but the CLI was specifically built in Go because Go produces a single static binary per platform with no runtime dependency, and its standard library has a first-class HTTP client. The same trade-offs are why most modern CLI tools pick either Rust or Go over Ruby/Python.
Q. Will gh copilot suggest still work?
Only if you installed the gh-copilot extension before October 25, 2025 and never uninstalled it. New installs are blocked because the extension is retired. Use the standalone GitHub Copilot CLI instead — it's the supported path going forward.
Wrapping Up
gh is the missing half of git. The split is historical (Git predates GitHub by three years and predates pull requests by six), but the practical result is that for most developers in 2026, half of the daily GitHub workflow lives in a browser tab nobody asked for. gh collapses that browser tab back into the terminal where everything else already is.
The four commands that earn the install:
gh pr create --fill— open a PR from your latest commit in one keystroke.gh pr diff | delta— read the diff like a code review tool, in your terminal.gh api --paginate --jq— script GitHub like it's a local database.gh run watch --exit-status— block on CI in scripts and shell loops.
Pair gh with delta for diffs, bat for viewing files inside PR checkouts, ripgrep for searching across cloned repos, and fzf for picking PR numbers interactively, and your daily GitHub work fits inside one terminal window. That's the goal of the modern CLI series — and gh is the piece that ties the whole platform layer to the rest of your workflow.