Overview
gh is the official GitHub CLI. It replaces most browser workflows for PRs, issues, releases, and CI with terminal commands that compose cleanly with jq-syntax and bash-one-liners. All commands require authentication via gh auth login.
Auth
Set up once per machine; tokens are stored in the system keychain.
| Command | What it does |
|---|---|
gh auth login | Interactive login via browser or token. |
gh auth login --with-token <<< $GITHUB_TOKEN | Login with a token from stdin; useful in CI. |
gh auth status | Show current auth status and scopes. |
gh auth logout | Remove stored credentials. |
gh auth refresh -s repo,workflow | Request additional OAuth scopes. |
Use a fine-grained personal access token with the minimum required scopes. Avoid classic tokens with broad repo scope.
Pull requests
Create and manage PRs without leaving the terminal.
| Command | What it does |
|---|---|
gh pr create --title "feat: ..." --body "..." | Open a PR on the current branch. |
gh pr create --draft | Open a draft PR. |
gh pr create --base main --head feature-x | Specify base and head branches explicitly. |
gh pr list | List open PRs in the current repo. |
gh pr list --state all | All PRs (open, closed, merged). |
gh pr view 42 | View PR #42. |
gh pr view --web | Open current branch’s PR in the browser. |
gh pr checkout 42 | Check out a PR branch locally. |
gh pr merge 42 --squash --delete-branch | Squash-merge and delete the branch. |
gh pr review --approve | Approve the current PR. |
gh pr review --request-changes -b "Needs tests." | Request changes. |
gh pr close 42 | Close without merging. |
gh pr diff | Show the diff for the current PR. |
gh pr status | Summary of open, requested, and authored PRs. |
Issues
| Command | What it does |
|---|---|
gh issue create --title "Bug: ..." | Create an issue interactively. |
gh issue create --title "Bug" --body "Steps..." --label bug | Create with a label. |
gh issue list | List open issues. |
gh issue list --label bug --assignee @me | Filter by label and assignee. |
gh issue view 10 | View issue #10. |
gh issue close 10 --comment "Fixed in #42" | Close with a comment. |
gh issue edit 10 --add-label "priority" | Add a label. |
gh issue comment 10 --body "More context." | Add a comment. |
Repos and workflow
| Command | What it does |
|---|---|
gh repo clone org/repo | Clone a repo. |
gh repo create my-app --public --clone | Create and clone a new repo. |
gh repo fork | Fork the current repo. |
gh repo view --web | Open repo in browser. |
gh workflow list | List workflows. |
gh workflow run ci.yml | Trigger a workflow manually. |
gh run list | List recent workflow runs. |
gh run view 123456789 | View a specific run. |
gh run watch | Stream the current run’s output. |
gh release list | List releases. |
gh release create v1.2.0 --notes "Changelog..." | Create a release. |
gh release upload v1.2.0 dist/app.tar.gz | Attach an asset to a release. |
API and scripting with —jq
gh api exposes the GitHub REST and GraphQL APIs; --jq pipes the response through jq before output.
| Command | What it does |
|---|---|
gh api repos/{owner}/{repo} | Get repo metadata. |
gh api repos/{owner}/{repo}/pulls --jq '.[].number' | List PR numbers. |
gh api repos/{owner}/{repo}/actions/runs --jq '.workflow_runs[0].conclusion' | Last run conclusion. |
gh api graphql -f query='{ viewer { login } }' | GraphQL query. |
gh pr list --json number,title --jq '.[] | "\(.number)\t\(.title)"' | TSV of open PRs. |
gh issue list --json title,labels --jq '.[] | select(.labels[].name == "bug") | .title' | Issue titles with “bug” label. |
gh run list --json databaseId,status --jq '[.[] | select(.status == "in_progress")] | length' | Count in-progress runs. |
Use gh api --paginate to fetch all pages and --jq to filter each page:
gh api --paginate repos/org/repo/issues --jq '.[].title'Common gotchas
gh pr createwith no flags drops you into an interactive editor. Pass--titleand--bodyto skip interactive mode in scripts.gh pr checkoutmodifies your local git state. Make sure your working tree is clean first or stash changes.gh apiuses{owner}and{repo}as path templates that expand from the current directory’s remote. Outside a git repo, pass the full path:repos/org/name.--jqruns after the full response is received, not per page. For large datasets with--paginate, use--jqto filter early and reduce memory usage.gh workflow runrequireson: workflow_dispatchin the workflow YAML. Workflows without this trigger cannot be run manually.- Labels passed with
--labelmust already exist in the repo. Create them withgh label createfirst.