GitHub CLI in the terminal: what it saves over the website

A branch is pushed, a pull request needs opening, and the work moves to a browser tab. Then the checks need watching, so the tab gets refreshed. Then a colleague's review arrives, so the tab gets refreshed again. The code never left the terminal, but attention did, several times per change. The GitHub CLI exists to collapse that loop, and the honest question is not whether it works but which parts of the loop it actually removes.

What the round trip to the browser really costs

Git and GitHub are two different things, and the split is where the cost comes from. Git is local. It stages, commits, branches, merges, and rebases without a network. GitHub holds everything layered on top: pull requests, reviews, issues, releases, Actions runs, labels, and project boards. None of that is in the Git data model, so none of it has a Git command.

That gap is why the browser appears in the middle of terminal work. The commit is a keystroke away, but the pull request that carries it is behind an address bar, a page load, a repository picker, and a scroll to the right button. Each visit is small. The number of visits is not.

The GitHub CLI, invoked as gh, closes the gap by talking to the GitHub API from the same prompt that runs git. It infers the repository from the current directory's remote, so a command like listing open pull requests needs no repository name, no login step, and no page load. The official manual lists the full surface at cli.github.com/manual, and the shape of it is worth a look before deciding which parts matter.

The saving is not typing speed, it is context. A page load pulls a person out of the current shell, the current directory, and the current train of thought. A command returns an answer in place and leaves everything else where it was.

Installing and authenticating once

On a Mac the common route is Homebrew. The formula is named gh, and Homebrew's formula page currently lists version 2.101.0 as stable. Precompiled binaries are published on the project's releases page for anyone who prefers not to use a package manager.

brew install gh
gh --version

Authentication happens once per host. gh auth login runs a browser-based flow by default and stores the resulting token in the system credential store. The manual is explicit about the fallback: if a credential store is not found, or there is a problem using it, gh writes the token to a plain text file instead. The flags worth knowing at this step are narrow and useful.

  • --with-token reads a token from standard input, which is how continuous integration and scripts authenticate.
  • --git-protocol ssh or --git-protocol https sets what protocol git operations use on that host.
  • --hostname points the login at a GitHub Enterprise Server instance rather than github.com.
  • --scopes requests extra permissions up front, so a later command does not stop to ask.

There is a second habit worth forming during setup. gh auth setup-git configures gh as a Git credential helper, which means pushing and pulling stop prompting for a password separately from the CLI's own token. Without it, two different credential paths end up in play, and only one of them was ever configured.

For scripted use, a token in the environment is read directly, so a container or a CI job needs no interactive login at all. The manual names GH_TOKEN and GITHUB_TOKEN in that order of precedence for github.com, with GH_ENTERPRISE_TOKEN and GITHUB_ENTERPRISE_TOKEN playing the same role for an Enterprise Server host.

The commands that actually change the day

The manual lists more than thirty top-level commands. A working set is much smaller, and it clusters around three moments.

Opening and landing work

gh pr create builds the pull request from the current branch. Titles and bodies can be passed as flags or filled interactively, and the base branch is inferred unless stated. gh pr view --web opens the finished thing in a browser when a visual check is wanted, which is the rare case rather than the default. gh repo create handles the other end, turning a local directory into a published repository without visiting a form.

Reviewing someone else's work

This is where the CLI earns the most. gh pr list prints open pull requests with numbers, titles, and branches. gh pr checkout <number> fetches the contributor's branch and switches to it, including for forks, which is otherwise a three command dance with a remote that has to be added and later removed. gh pr diff prints the change in the pager. Reading a diff in the same terminal that can run the test suite against it removes most of the reason to open a review in a browser at all.

Watching what happens next

gh pr checks reports the status of every check on a pull request. gh run list and gh run view cover Actions workflow runs, and a failed run's logs can be printed rather than clicked through. gh pr status gives a single screen for what is waiting: pull requests assigned for review, and the state of the current branch's own request.

Around those sit the smaller conveniences. gh issue create and gh issue list mirror the pull request commands. gh release create attaches built artifacts to a tag. gh browse opens the current repository, or a specific file and line, in a browser without anyone typing a URL. gh search queries code, issues, and repositories across GitHub from the prompt.

Where gh stops and something else has to start

The CLI is a client for GitHub's API. It is not a Git front end, and it is not a file manager. Knowing the boundary prevents the wrong expectation.

Task Handled by
Stage, commit, branch, rebase, resolve conflicts git
Open a pull request, review, merge, check CI gh
Read a long diff with syntax colour and a scrollback The terminal and its pager
Find which of forty local clones holds the branch Neither, this is a file and folder problem
Drag a build artifact somewhere, rename a batch of files Finder or a Finder replacement

The last two rows are the ones people underestimate. gh removes the browser from the GitHub half of the work. It does nothing about the other window that keeps getting activated: the folder window where repositories, build outputs, screenshots for the pull request description, and downloaded artifacts actually live. A file manager with a built-in terminal addresses that half, and the comparison of how different file managers handle a built-in shell is a more useful reference for that problem than any CLI documentation.

There is also a limit worth naming plainly. gh is faster than a browser for anything with a definite answer, and slower for anything exploratory. Scanning thirty pull requests for the one with an interesting conversation is a browsing task. Checking whether the tests passed on one specific number is a command. Trying to do the first with commands produces a lot of scrolling.

Aliases, gh api, and extensions

Three features turn the CLI from a lookup tool into something shaped around one person's work.

gh alias set defines shortcuts. A review checkout that always runs the same way, or a pull request list filtered to one author, becomes a single word. Aliases live in the CLI's configuration rather than in a shell profile, so they travel with gh across shells.

gh api reaches anything the REST and GraphQL APIs expose, including endpoints with no dedicated command. Combined with jq, it answers questions the documented commands do not phrase, such as which open pull requests have no reviewer assigned. Output is JSON, so it composes with the rest of the shell rather than having to be parsed out of a table.

gh extension installs third-party commands that appear as if built in. The pattern is the same one that made git subcommands extensible: an executable named a certain way becomes a subcommand. This is also the escape hatch when a team's workflow has a step GitHub never modelled.

One caution applies to all three. Output formats of the human-readable commands can change between releases, so anything that parses them is fragile. Scripts should use --json on the commands that offer it, or gh api directly, and leave the table formats for people to read.

gh config set covers the settings that decide how the tool behaves before any of that. The editor used for commit and pull request bodies, whether the browser opens automatically, and the default protocol for cloning are all set here rather than guessed at each run. Setting the editor first is the highest-value change, because an unset editor is the reason gh pr create sometimes drops a person into an editor they did not expect.

Working across more than one account and host

Most of the friction reported with the CLI comes from having two identities rather than one. A work account and a personal account on github.com, or a company GitHub Enterprise Server instance alongside the public site, both land in the same place.

Authentication is stored per host, so a login against an Enterprise Server hostname does not disturb the github.com login. Within a single host, gh auth login can be run more than once and gh auth switch changes which account is active. gh auth status prints what is currently stored for every host, which is the first command to run when a request comes back with a permission error that makes no sense.

The part that surprises people is that Git and the CLI can disagree. A repository cloned over SSH uses whichever key the SSH agent offers, and that key may belong to the account that is not currently active in gh. The result is a push that succeeds as one identity while pull requests are created as another. Running gh auth setup-git and cloning through gh repo clone keeps both halves pointing at the same account, which is a duller outcome and a much easier one to debug.

Scopes are the other recurring snag. A token created for a narrow purpose will refuse to read organisation projects or write repository secrets, and the error text names the missing scope. gh auth refresh --scopes adds it without starting over.

Deciding whether the switch is worth it

A fair way to judge is to count, for one working day, how many times a browser tab was opened purely to answer a question about a repository. Statuses, review requests, and CI results dominate that list, and all three have a one line command. If the count is under five, the CLI is a nicety. If it is twenty, the browser has quietly become part of the build loop.

A second count matters as much and gets ignored. How many times did work stop to hunt for a folder, drag a file, or open a terminal in a directory that was already visible on screen? That is a different tool's problem, and a window that holds folders, a shell, and an assistant together is what removes it. The feature list for that kind of window is the place to check whether the described behaviour matches the work in question.

What to change first

Install gh, run gh auth login and gh auth setup-git, then use only gh pr status and gh pr checks for a week. Those two cover most of the browser trips without any need to learn the other thirty commands. Once the terminal stops being interrupted by tabs, the remaining interruptions are about files and folders, and Atriens is built for that half of the loop.

Frequently asked questions

Is the GitHub CLI a replacement for git?

No. git handles the local repository: staging, commits, branches, merges, and rebases. gh handles what GitHub adds on top, such as pull requests, reviews, issues, releases, and Actions runs. Both are normally installed and used together.

How do I use gh with a work account and a personal account?

gh auth login can be run for more than one account, and gh auth switch changes which one is active. Authentication is also per host, so a GitHub Enterprise Server instance authenticated with --hostname is tracked separately from github.com.

Does gh store my token securely on a Mac?

By default it uses the system credential store. The manual notes that if a credential store is not found, or cannot be used, the token is written to a plain text file instead, and --insecure-storage forces that behaviour deliberately. Checking which path is in use is worth doing on a shared machine.

Can gh be used inside scripts and CI?

Yes. Set GH_TOKEN in the environment, or pipe a token into gh auth login --with-token, and no interactive step is needed. Use --json or gh api for anything parsed by a script, because the human-readable table output is not a stable interface.

Back to all posts