Skip to content

GitHub CLI (gh) Guide: Repos, PRs, Issues and Actions from the Terminal

7 min readGitHub Engineering · GitHub CLI

gh exposes GitHub platform operations in a command-line interface.

Git already has a command line, and gh is not a replacement for it. The two cover different halves of the boundary this pillar keeps returning to: Git manages the repository; gh manages everything GitHub adds around it.

You commit with git. You open the pull request with gh. You push with git. You check whether CI passed with gh. Neither replaces the other, and the split is exactly the Git/GitHub division.

Start with Lesson 1

The obvious benefit is avoiding a context switch — no reaching for a browser to check a pull request. That is real and it is the smaller half.

The larger half is that gh makes GitHub scriptable without writing an HTTP client. It handles authentication, pagination, error handling and response parsing, and it emits structured JSON on request. That turns operations which would be a hundred lines of API code into one line of shell.

This is the third rung of the automation ladder from the pillar overview: the point where GitHub stops being something you operate and becomes something you program.

Setup — installing gh on the platforms you actually use, and authenticating properly, including multiple accounts and non-interactive use in CI.

The command familiesrepo, pr, issue, workflow and run. These cover the great majority of daily use, and each lesson works through the operations worth knowing rather than enumerating every flag.

The escape hatchgh api, which gives you the entire REST and GraphQL surface with gh’s authentication and pagination already handled. This is the bridge to the API cluster.

Automation — writing shell around gh that is safe to run unattended, and then the harder material: structured output, error handling, exit codes, and scripts that behave correctly when they run a thousand times instead of once.

You should be able toCovered in
Work comfortably at a shell prompt
Commit, branch and pushGit Fundamentals
Explain what a pull request isPull Requests Explained
Have a GitHub account with 2FAGitHub Account Setup

Familiarity with jq is useful for the later lessons but is taught where it is needed.

By the end of this cluster you should be able to:

  • Install and upgrade gh from the correct source on Linux, macOS and Windows.
  • Authenticate for interactive use, for multiple accounts, and for CI — and explain why those need different approaches.
  • Perform the common repository, pull request and Issue operations without leaving the terminal.
  • Inspect and trigger workflow runs, and diagnose a failing one from the command line.
  • Use gh api for anything the porcelain commands do not cover, including GraphQL.
  • Write shell automation that uses structured output, checks exit codes, and fails safely.
  • Explain when to reach for gh and when to use the API directly.
  1. Lesson 1: 01. Install GitHub CLIInstall gh from the correct source on each platform, verify the version, enable shell completion, and avoid the packaging traps that install an outdated build.Beginner → Intermediate9 min read
  2. Lesson 2: 02. gh authLog in interactively or with a token, manage multiple accounts and hosts, choose the Git protocol, and authenticate gh safely in CI without leaking credentials.Intermediate11 min read
  3. Lesson 3: 03. gh repoCreate, clone, fork, view, rename, sync, archive and delete GitHub repositories from the terminal with gh repo — including JSON output for scripts.Beginner → Intermediate10 min read
  4. Lesson 4: 04. gh prThe pull request lifecycle from the terminal with gh pr: create, list, check out, review, watch checks and merge — with JSON output for scripts and the flags that matter.Intermediate6 min read
  5. Lesson 5: 05. gh issueCreate, list, view, edit, close and comment on Issues with gh — plus linked branches with gh issue develop, search qualifiers and structured output for triage.Beginner → Intermediate8 min read
  6. Lesson 6: 06. gh workflowList, view, enable, disable and trigger GitHub Actions workflows from the terminal with gh workflow — workflow_dispatch inputs, choosing a ref, and reading run results.Intermediate10 min read
  7. Lesson 7: 07. gh runList, view, watch, rerun and cancel workflow runs, read failed job logs, download artifacts, and build CI troubleshooting into scripts with JSON output.Intermediate9 min read
  8. Lesson 8: 08. gh apiCall any GitHub REST or GraphQL endpoint with gh api — auth handled, --paginate for every page, --slurp and --jq for filtering, and the combinations that silently return the wrong answer.Intermediate → Advanced12 min read
  9. Lesson 9: 09. Automating with BashBuild real automation around gh — exit codes, quoting, JSON with jq, loops, error handling, rate-limit awareness and idempotency — without unsafe bulk operations.Intermediate → Advanced11 min read
  10. Lesson 10: 10. GitHub CLI ScriptingStructured output, templates, environment configuration, multi-repository operations, error handling, exit codes, CI authentication and reusable functions built on gh.Intermediate → Advanced12 min read

gh groups its commands, and knowing the groups makes the surface far less intimidating than a flat list of forty verbs.

GroupCommandsCovers
Coreauth, repo, pr, issue, release, org, gist, browseEveryday collaboration
Actionsrun, workflow, cacheCI: inspecting, triggering, debugging
Additionalapi, label, ruleset, search, secret, variable, ssh-key, gpg-key, alias, config, extensionConfiguration, policy and the raw API

Most commands follow the same shape:

gh <noun> <verb> [target] [flags]

gh pr list, gh issue create, gh repo view, gh run watch. Once you know the noun, the verbs are guessable — and gh <noun> --help confirms them for your installed version.

Several commands infer the repository from the current directory’s Git remotes, so gh pr list inside a checkout needs no arguments. Outside one, or when targeting somewhere else, --repo OWNER/REPO makes it explicit. In scripts, always be explicit: a command that infers its target from the working directory behaves differently depending on where it runs, which is exactly the kind of surprise automation should not have.

It is not a Git client. There is no gh commit, no gh push, no gh merge that operates on your local repository. gh pr merge asks GitHub to merge; it does not run git merge. When you need to manipulate history, branches or the working tree, that is Git’s job.

It does not replace the API for applications. gh is a tool for humans and shell scripts. It shells out, it depends on a binary being installed at a particular version, and its interface is tuned for interactive use. A service that needs to call GitHub should use the API directly with a proper HTTP client.

The dividing line is roughly: if a person or a shell script is the consumer, use gh; if a program is, use the API.

gh supports extensions — third-party commands installed with gh extension install and invoked as gh <name>. They are ordinary executables that gh discovers, and they can be written in any language.

They are genuinely useful for team-specific workflows, and they carry the caveat that applies to any third-party code you run with your credentials: an extension runs with your authenticated gh session. Install them the way you would install anything else that can act as you.

If this cluster teaches you a single thing, it should be this: never parse human-readable output.

Terminal window
# Fragile — breaks when a column width, colour or icon changes
gh pr list | awk '{print $1}'
# Robust — a documented field, extracted structurally
gh pr list --json number --jq '.[].number'

gh’s default output is designed for humans: aligned columns, colour, truncation, symbols. All of that is presentation and none of it is a contract. It changes between releases, and it changes based on whether output is a terminal.

--json selects fields, --jq filters them, and --template formats them. Together they are the difference between automation that survives a CLI upgrade and automation that silently produces wrong results after one.

gh is an HTTP client. Every command becomes one or more requests to GitHub’s API, authenticated with the credential from gh auth.

Knowing that explains several behaviours that otherwise seem arbitrary:

  • Commands need network access. There is no offline mode, because there is no local state to read.
  • Rate limits apply. A loop over five hundred repositories is five hundred API requests.
  • Some commands are slower than they look. gh pr list --json with many fields may make several requests.
  • gh api is not a special case. It is the same client with the curation removed.

You can watch this directly:

Terminal window
GH_DEBUG=api gh pr list --limit 3

That prints the actual HTTP requests and responses. It is the fastest way to learn which endpoint a command uses — and therefore how to reproduce it in a language that is not shell, which is what the API cluster covers.

gh keeps configuration in ~/.config/gh/ — or %APPDATA%\GitHub CLI on Windows — and exposes it through gh config:

Terminal window
gh config list
gh config set editor vim
gh config set git_protocol ssh
gh config set prompt disabled

prompt disabled is worth knowing for automation: it stops gh asking interactive questions and makes it fail with a clear error instead of hanging waiting for input that will never arrive. A CI job that mysteriously times out is often a gh command waiting at a prompt.

Aliases cover the commands you run constantly:

Terminal window
gh alias set prs 'pr list --author "@me"'
gh alias set review 'pr list --search "review-requested:@me"'
gh alias set --shell failing 'gh pr checks "$1" --json name,state --jq ".[] | select(.state==\"FAILURE\") | .name"'

--shell aliases run through the shell, so they can take arguments and pipe. They are the same idea as Git aliases, applied to the hosted half.

gh releases frequently and gains commands regularly. Some ship marked (preview), which means their flags and output can change between releases.

Two consequences worth internalising now. First, gh <command> --help on your installed version is more authoritative than any tutorial, including this one — where a lesson and your help output disagree, believe the help output. Second, preview commands are a poor foundation for durable automation; for anything you intend to leave running, prefer stable commands or the API directly.

The API cluster follows, and the boundary between them is worth stating.

gh is excellent for humans, shell scripts, developer workstations and CI glue. The REST and GraphQL APIs are better when you are building an application, need control over requests, or are writing a service rather than a script.

gh api sits deliberately between the two, and is where this cluster hands over.

Begin: Install GitHub CLI