/ Tags: DEVELOPER TIPS / Categories: TIPS

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 api is the escape hatch that makes everything else reachable — anything the REST or GraphQL API can do, whether or not gh has 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/:repo placeholders 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 something gh doesn’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.

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! 💻 🏡 Grad. Student, MCS. 🎓 Class of '23. GitKraken Ambassador 🇳🇵 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More