How to Use GitHub CLI for PRs and Issues Without Touching the Browser
Push a branch, switch to the browser, wait for it to load, click “Compare & pull request”, retype the description you already wrote in your commit message, pick reviewers from a dropdown, submit. That’s ninety seconds and a context switch, repeated several times a day. gh collapses it to one command that runs from the directory you’re already in.
Setup
brew install gh # or apt, winget, etc.
gh auth login
Pick HTTPS and let it configure git credentials — gh then handles authentication for git push too, which removes the personal access token dance.
gh auth status # confirm
gh repo view --web # sanity check it found the right repo
Pull Requests
Example:
# Interactive — prompts for title, body, reviewers
gh pr create
# Non-interactive
gh pr create \
--title "Speed up bulk invoice generation" \
--body-file .github/pr_draft.md \
--reviewer alice,bob \
--label performance \
--assignee @me
# Draft, and push the branch in the same step
gh pr create --draft --fill --head "$(git branch --show-current)"
--fill populates the title and body from your commits — the title from the first commit subject, the body from the commit bodies. If you write good commit messages, this is often all you need.
--body-file is the one worth building a habit around. Writing a PR description in your editor produces noticeably better prose than a browser textarea, and it means the description can be drafted before the code is finished.
Working with an existing PR
gh pr status # yours, review-requested, and current branch
gh pr list --author "@me"
gh pr list --search "review-requested:@me draft:false"
gh pr view # current branch's PR in the terminal
gh pr view 412 --comments # including the discussion
gh pr diff # the diff, paged and coloured
gh pr checks # CI status, one line per check
gh pr ready # draft → ready for review
gh pr merge --squash --delete-branch
gh pr checks --watch blocks until CI finishes and exits non-zero on failure, which makes it scriptable:
gh pr checks --watch && gh pr merge --squash --delete-branch
Reviewing Without the Browser
The genuinely underused half.
Example:
# Check out someone else's PR locally, by number
gh pr checkout 412
# Read the diff
gh pr diff 412
# Approve
gh pr review 412 --approve --body "Looks good. One nit inline, not blocking."
# Request changes
gh pr review 412 --request-changes --body "The retry path double-charges — see comment."
# Comment without a verdict
gh pr review 412 --comment --body "Left some questions."
gh pr checkout is the important one. It creates a local branch tracking the PR — including from a fork — so you can run the tests, poke at it in a console, and actually verify the change rather than reading it. That’s the difference between a review and a skim, and it takes four seconds.
Issues
Example:
gh issue create --title "Bulk export times out over 2k line items" \
--body-file bug.md \
--label bug,performance
gh issue list --assignee "@me" --state open
gh issue list --label bug --limit 20
gh issue view 388
gh issue comment 388 --body "Reproduced on staging with 3,200 items."
gh issue close 388 --comment "Fixed in #412"
# Search across repos
gh search issues --owner myorg --state open "connection pool"
Linking an issue to a PR so it closes on merge is just the body text:
gh pr create --title "Fix bulk export timeout" --body "Closes #388"
Scripting With –json
Where gh stops being a convenience and becomes a tool. Every list command speaks JSON.
Example:
# What's waiting on me?
gh pr list --search "review-requested:@me" \
--json number,title,author,createdAt \
--jq '.[] | "#\(.number) \(.title) — @\(.author.login)"'
# PRs older than 3 days with no reviews
gh pr list --json number,title,createdAt,reviews \
--jq '[.[] | select(.reviews | length == 0)] | .[] | "#\(.number) \(.title)"'
# Which of my PRs are failing CI?
gh pr list --author "@me" --json number,title,statusCheckRollup \
--jq '.[] | select(any(.statusCheckRollup[]; .conclusion == "FAILURE"))
| "#\(.number) \(.title)"'
--jq runs the filter inside gh, so you don’t need jq installed. Discover available fields with:
gh pr list --json 2>&1 | head -40
That prints the full field list for the command, which is faster than searching the docs.
A morning standup script
#!/usr/bin/env bash
echo "── Awaiting my review ──"
gh pr list --search "review-requested:@me draft:false" \
--json number,title,author --jq '.[] | " #\(.number) \(.title) (@\(.author.login))"'
echo
echo "── My open PRs ──"
gh pr list --author "@me" --json number,title,isDraft,statusCheckRollup \
--jq '.[] | " #\(.number) \(.title)\(if .isDraft then " [draft]" else "" end)"'
echo
echo "── Assigned issues ──"
gh issue list --assignee "@me" --json number,title \
--jq '.[] | " #\(.number) \(.title)"'
Workflows and Releases
gh run list --limit 10
gh run view 1234567 --log-failed # just the failing step's output
gh run watch # live, for the current run
gh run rerun 1234567 --failed # rerun only failed jobs
gh release create v2.4.0 --generate-notes
gh release create v2.4.0 --notes-file CHANGELOG_EXCERPT.md dist/*.gem
gh run view --log-failed is a real time-saver — it skips straight to the output of the step that broke instead of making you scroll a 4,000-line log in a browser tab.
Aliases
gh alias set prc 'pr create --fill --draft'
gh alias set prv 'pr view --web'
gh alias set mine 'pr list --author "@me"'
gh alias set todo 'pr list --search "review-requested:@me draft:false"'
# Shell-out aliases for anything with pipes
gh alias set stale --shell 'gh pr list --json number,title,createdAt \
--jq "[.[] | select(now - (.createdAt | fromdate) > 259200)] | .[] | \"#\(.number) \(.title)\""'
gh alias list
--shell is required whenever the alias contains a pipe or shell syntax. Without it, gh treats the whole thing as arguments to a subcommand.
Pro-Tip:
gh apiis the escape hatch that makes everything else reachable — anything the REST or GraphQL API can do, whether or notghhas a subcommand for it. The one worth knowing immediately:gh api repos/:owner/:repo/pulls/412/files --jq '.[] | "\(.additions)+ \(.deletions)- \(.filename)"'gives you a per-file change summary before deciding whether you have time to review something. The:owner/:repoplaceholders resolve from the current repository automatically. It also paginates properly with--paginate, which the web UI won’t do —gh api --paginate repos/:owner/:repo/issues --jq '.[].title'pulls every issue rather than the first thirty. When you find yourself opening the browser to do somethingghdoesn’t cover, check the API docs first; it’s usually one line.
Extensions
gh extension install dlvhdr/gh-dash # TUI dashboard for PRs and issues
gh extension install github/gh-copilot
gh extension list
gh-dash is worth trying if you review a lot — a configurable terminal dashboard with sections for your PRs, review requests, and issues, refreshing in the background.
Conclusion
gh pr create --fill and gh pr checkout are the two commands that change your day: one removes the browser round trip when opening a PR, the other makes reviewing locally as cheap as reading the diff. Add --json with --jq and the CLI becomes scriptable — a five-line morning script tells you what’s waiting on you across every repo without opening a tab. Set aliases for the queries you run daily, use gh run view --log-failed instead of scrolling CI logs, and reach for gh api for anything the subcommands don’t cover.
FAQs
Q1: Does gh work with GitHub Enterprise?
Yes — gh auth login --hostname github.mycompany.com. It handles multiple hosts, and picks the right one based on the repo’s remote.
Q2: How do I use gh in CI?
Set GH_TOKEN in the environment. In GitHub Actions, GH_TOKEN: $ is enough for most operations against the current repo.
Q3: What’s the difference between gh pr checkout and git fetch?
gh pr checkout resolves the PR number to the right ref — including from forks, which plain git fetch origin won’t reach — and sets up tracking so git push updates the PR.
Q4: Can I create a PR against a different base branch?
gh pr create --base develop. Set a repo default with gh repo set-default if you’re often working across forks.
Q5: Is there a way to see the PR template before writing the body?
gh pr create without --body opens your editor pre-filled with the repository’s template, same as the web UI does.
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs - boost your Personal Brand & career! 🚀