}

GitHub CLI (gh) Tutorial 2026: Replace Your Browser Workflow from the Terminal

GitHub CLI (gh) Tutorial 2026: Replace Your Browser Workflow from the Terminal

Every developer knows the pattern: you finish a commit, push your branch, then switch to the browser to open a pull request, copy a URL to share with a reviewer, click through a dozen pages to check CI status, and finally go back to merge. The GitHub CLI (gh) eliminates all of that switching. Every action you take on GitHub.com has a terminal equivalent — and in many cases the terminal version is faster, scriptable, and more powerful.

This tutorial covers the full gh workflow in 2026: installation, authentication, repository operations, the complete issue and pull request lifecycle, GitHub Actions control, release management, aliases, CI/CD integration, and advanced search and API access. By the end you will be able to run an entire feature-branch-to-merged-PR workflow without touching a browser.


gh vs git: Two Complementary Tools

Before diving in, it is important to understand what gh is and is not.

git manages your local repository: commits, branches, merges, the object store. It talks to remote servers only for push/pull/fetch operations and does not understand GitHub-specific concepts like pull requests, issues, or Actions.

gh manages GitHub resources via the GitHub API. It knows nothing about your commit graph but understands everything GitHub offers: issues, PRs, CI runs, releases, gists, and even raw REST and GraphQL calls. The two tools are complementary — you still use git commit, git push, and git rebase. You use gh for everything that would otherwise require a browser tab.


Installation

macOS (Homebrew)

brew install gh

Upgrade later with brew upgrade gh.

Ubuntu and Debian (official apt repository)

GitHub maintains a signed apt repository. Add it and install:

(type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \
  && sudo mkdir -p -m 755 /etc/apt/keyrings \
  && wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg \
     | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
  && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
  && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
     | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
  && sudo apt update \
  && sudo apt install gh -y

Windows (winget)

winget install --id GitHub.cli

Verify the installation on any platform:

gh --version
# gh version 2.50.0 (2026-04-15)

Authentication

Interactive browser OAuth

Run gh auth login and follow the prompts. Choose GitHub.com (or your GitHub Enterprise hostname), select HTTPS or SSH for git operations, then authenticate via browser:

gh auth login
# ? What account do you want to log into? GitHub.com
# ? What is your preferred protocol for Git operations? HTTPS
# ? Authenticate Git with your GitHub credentials? Yes
# ? How would you like to authenticate GitHub CLI? Login with a web browser
# ! First copy your one-time code: ABCD-1234
# Press Enter to open github.com in your browser...

Token-based authentication (headless / CI)

Set the environment variable GITHUB_TOKEN or pass a token directly:

echo "ghp_YourPersonalAccessTokenHere" | gh auth login --with-token

Check authentication status

gh auth status
# github.com
#   ✓ Logged in to github.com as yourname (keyring)
#   ✓ Git operations for github.com configured to use https protocol.
#   ✓ Token: gho_****
#   ✓ Token scopes: gist, read:org, repo, workflow

Repository Operations

Clone a repository

gh repo clone owner/repo
# equivalent to git clone but also sets up gh context

Create a new repository

# Public repo, initialized with a README
gh repo create my-new-project --public --add-readme

# Private repo with description, clone it locally after creation
gh repo create my-private-project --private \
  --description "Work in progress" --clone

Fork a repository

gh repo fork cli/cli
# Forks to your account and optionally clones locally
gh repo fork cli/cli --clone

View repository information

gh repo view
# Shows repo description, topics, stars, latest activity

gh repo view torvalds/linux
# View any public repo

List your repositories

gh repo list
gh repo list --limit 50 --source   # only repos you own, not forks
gh repo list --fork                 # only forks

Issues: Full Lifecycle

List issues

gh issue list
gh issue list --state open
gh issue list --label bug --assignee @me
gh issue list --milestone "v2.0"
gh issue list --author octocat

View an issue

gh issue view 42
gh issue view 42 --web   # open in browser

Create an issue

gh issue create \
  --title "Login page throws 500 on empty password" \
  --body "Steps to reproduce: ..." \
  --label "bug,priority:high" \
  --assignee yourname \
  --milestone "v1.3"

Use --body-file body.md to read the body from a file — useful for templated issues.

Edit an issue

gh issue edit 42 --title "Updated title"
gh issue edit 42 --add-label "needs-review" --remove-label "triage"
gh issue edit 42 --assignee newteammember

Comment on an issue

gh issue comment 42 --body "Confirmed on production. Assigning to @backend-team."

Close and reopen issues

gh issue close 42
gh issue close 42 --comment "Fixed in PR #87."
gh issue reopen 42

Pull Requests: The Core Workflow

Pull requests are where gh pays off most. You can open, review, check, and merge PRs without leaving the terminal.

Create a pull request

gh pr create \
  --title "feat: add OAuth2 login support" \
  --body "Resolves #42. Adds Google and GitHub OAuth2 providers." \
  --reviewer alice,bob \
  --label "feature,ready-for-review" \
  --assignee @me \
  --draft

Remove --draft when the PR is ready. Use --fill to auto-populate title and body from your commit messages.

List pull requests

gh pr list
gh pr list --state open
gh pr list --author @me
gh pr list --reviewer @me            # PRs waiting for your review
gh pr list --label "ready-for-review"
gh pr list --base main               # PRs targeting main
gh pr list --search "is:pr is:open draft:false"

View a pull request

gh pr view 87
gh pr view 87 --web

Check out a PR locally — the killer feature

This is one of the most useful things gh does. Given any PR number — including PRs submitted from a fork — gh fetches the branch and checks it out automatically:

gh pr checkout 87

Without gh, checking out a PR from a fork requires manually adding the contributor's remote, fetching their branch, and checking it out. gh pr checkout does all of that in one command. You can immediately run the code, reproduce a bug, or test a fix.

To return to your previous branch:

git checkout -

Review a pull request

Approve, request changes, or leave a comment review:

gh pr review 87 --approve
gh pr review 87 --request-changes --body "Please add unit tests for the OAuth callback."
gh pr review 87 --comment --body "Left a few inline notes. LGTM overall."

Check CI status

gh pr checks 87
# Shows each check: name, status (pass/fail/pending), duration, and a link

Wait for all checks to pass before merging:

gh pr checks 87 --watch   # streams updates until all checks finish

Merge a pull request

Three merge strategies are available:

gh pr merge 87 --squash --delete-branch
gh pr merge 87 --rebase --delete-branch
gh pr merge 87 --merge  --delete-branch

--delete-branch removes the remote branch after the merge — good hygiene for feature branches.

You can also merge interactively by running gh pr merge with no flags and answering the prompts.


GitHub Actions: Control from the Terminal

List workflows

gh workflow list
# ID     Name              State
# 12345  CI                active
# 12346  Release           active
# 12347  Nightly build     disabled

Trigger a workflow manually

gh workflow run ci.yml
gh workflow run release.yml --field version=2.1.0 --field environment=production

The --field flag maps to workflow_dispatch inputs defined in your YAML.

List recent runs

gh run list
gh run list --workflow ci.yml
gh run list --branch feature/oauth --limit 5

Watch a run in real time

gh run watch 9876543
# Streams live log output until the run completes

View logs for a completed run

gh run view 9876543
gh run view 9876543 --log               # full logs
gh run view 9876543 --log-failed        # only failing steps

Rerun failed jobs

gh run rerun 9876543 --failed           # rerun only the failed jobs
gh run rerun 9876543                    # rerun the entire workflow

Releases

Create a release

gh release create v2.1.0 \
  --title "Version 2.1.0" \
  --generate-notes \
  dist/app-linux-amd64 dist/app-darwin-arm64 dist/app-windows-amd64.exe

--generate-notes automatically drafts release notes from merged PRs since the previous tag. Upload any number of asset files as positional arguments.

Create a pre-release or draft

gh release create v2.2.0-beta.1 --prerelease --title "Beta 1"
gh release create v2.2.0 --draft --title "Version 2.2.0 (draft)"

List releases

gh release list

Download release assets

gh release download v2.1.0
gh release download v2.1.0 --pattern "*.tar.gz"

Aliases: Build Your Own Shortcuts

gh alias set lets you create short commands for long or frequently used operations.

Create aliases

# List your open PRs
gh alias set prs 'pr list --author @me'

# Open a PR for review and assign yourself
gh alias set review 'pr review --approve'

# Check out a PR by number
gh alias set co 'pr checkout'

# List open bugs assigned to you
gh alias set bugs 'issue list --label bug --assignee @me'

# Close an issue with a standard message
gh alias set done 'issue close --comment "Done. Closing."'

Use shell aliases for multi-step commands

gh alias set open-pr --shell \
  'gh pr create --fill --assignee @me && gh pr view --web'

List all aliases

gh alias list

Remove an alias

gh alias delete bugs

Using gh in Scripts and CI/CD

Authentication in CI

In GitHub Actions, the GITHUB_TOKEN secret is available automatically. Pass it to gh:

- name: Create release
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    gh release create "${{ github.ref_name }}" \
      --generate-notes \
      dist/*

For scripts outside GitHub Actions, export the token:

export GITHUB_TOKEN="ghp_YourTokenHere"
gh auth status   # verify it works

Automated release script

This script tags, creates a release with generated notes, and uploads build artifacts — all without the browser:

#!/usr/bin/env bash
set -euo pipefail

VERSION="${1:?Usage: release.sh <version>}"
ARTIFACTS_DIR="${2:-dist}"

echo "==> Tagging $VERSION"
git tag -a "$VERSION" -m "Release $VERSION"
git push origin "$VERSION"

echo "==> Creating GitHub release"
gh release create "$VERSION" \
  --title "Release $VERSION" \
  --generate-notes \
  "$ARTIFACTS_DIR"/*

echo "==> Done: $(gh release view "$VERSION" --json url -q .url)"

Save as scripts/release.sh, make it executable (chmod +x), and call it from your CI pipeline or locally.

Comment on a PR from CI

Useful for posting test coverage reports or benchmark results directly on the PR:

gh pr comment "$PR_NUMBER" \
  --body "Coverage report: $(cat coverage-summary.txt)"

Auto-close stale issues from a script

gh issue list --label stale --json number -q '.[].number' | \
  xargs -I{} gh issue close {} --comment "Closing as stale. Reopen if still relevant."

Advanced Features

Search across GitHub

gh search repos "language:python stars:>1000 topic:machine-learning"
gh search issues "is:open label:bug repo:cli/cli"
gh search prs "is:open review-requested:@me"

Gist management

gh gist create script.sh --public --desc "Useful deploy script"
gh gist list
gh gist view <gist-id>
gh gist clone <gist-id>
gh gist edit <gist-id>

Raw API access with gh api

For anything not covered by a dedicated subcommand, use gh api to call the REST or GraphQL API directly:

# REST: get rate limit info
gh api /rate_limit

# REST: list org members
gh api /orgs/my-org/members --jq '.[].login'

# GraphQL: get PR review decisions
gh api graphql -f query='
{
  repository(owner: "cli", name: "cli") {
    pullRequest(number: 1234) {
      title
      reviewDecision
      reviews(last: 5) {
        nodes { author { login } state }
      }
    }
  }
}'

The --jq flag applies a jq expression to the JSON response, making it easy to extract specific fields in scripts.

Configuration

# Set your preferred editor
gh config set editor nvim

# Use SSH instead of HTTPS for git operations
gh config set git_protocol ssh

# Set default browser
gh config set browser firefox

# View all config
gh config list

End-to-End Workflow: Feature Branch to Merged PR

Here is a complete example of developing a feature and merging it using only the terminal.

Step 1: Start from a fresh branch

git checkout main && git pull
git checkout -b feat/add-oauth-login

Step 2: Write code, commit, push

# ... edit files ...
git add src/auth/oauth.py tests/test_oauth.py
git commit -m "feat: add OAuth2 login with Google and GitHub providers"
git push -u origin feat/add-oauth-login

Step 3: Open a PR

gh pr create \
  --title "feat: add OAuth2 login" \
  --body "Resolves #42. Adds Google and GitHub OAuth2 providers via authlib." \
  --reviewer alice,bob \
  --label "feature"

Step 4: Check CI

gh pr checks
# Wait and watch:
gh pr checks --watch

Step 5: Address review feedback

A reviewer requests changes. Check out the PR (useful if you switched branches):

gh pr checkout 87   # or just stay on the branch

Make the requested changes, commit, and push:

git add src/auth/oauth.py
git commit -m "fix: handle token refresh edge case per review"
git push

Step 6: Approve (as the reviewer)

gh pr review 87 --approve --body "LGTM, great work."

Step 7: Merge

gh pr merge 87 --squash --delete-branch

Step 8: Confirm and clean up locally

git checkout main
git pull
git branch -d feat/add-oauth-login

The issue linked in the PR body (Resolves #42) is automatically closed by GitHub when the PR merges.


Quick Reference

TaskCommand
Authenticategh auth login
Clone a repogh repo clone owner/repo
Create a PRgh pr create --fill
Check out any PRgh pr checkout NUMBER
Watch CI checksgh pr checks --watch
Approve a PRgh pr review NUMBER --approve
Merge with squashgh pr merge NUMBER --squash --delete-branch
Trigger a workflowgh workflow run name.yml
Watch a rungh run watch RUN_ID
Create a releasegh release create vX.Y.Z --generate-notes
Add an aliasgh alias set NAME 'command'
Call the APIgh api /endpoint --jq '.field'

Summary

gh is not a replacement for git — it is a complement that covers everything git cannot: the GitHub layer of your workflow. With gh you can manage issues, open and review pull requests, trigger and observe CI runs, publish releases, and call the raw GitHub API, all from one tool already in your PATH.

The features worth internalizing first are gh pr checkout (instant PR checkout from any fork), gh pr checks --watch (real-time CI feedback), and aliases (turning multi-flag commands into two-letter shortcuts). Together they eliminate the majority of context switches to the browser that slow down a day of active development.

All commands shown here are documented at cli.github.com/manual and the official GitHub CLI docs.

Leonardo Lazzaro

Software engineer and technical writer. 10+ years experience in DevOps, Python, and Linux systems.

More articles by Leonardo Lazzaro