GitHub CLI Cheatsheet

Releases and Gists

Use this GitHub CLI reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Creating a Release

The tag is the first argument. Any following arguments are asset files or globs.

gh release create v1.0.0
gh release create v1.0.0 --generate-notes
gh release create v1.0.0 -t "v1.0.0" -n "first stable release"
gh release create v1.0.0 --notes-file CHANGELOG.md
gh release create v1.0.0 --notes-file - < notes.md
gh release create v1.0.0 --notes-from-tag        # use the tag annotation
gh release create v1.0.0 --target main
gh release create v1.0.0 --target 9a3f21c        # a full SHA also works
gh release create v2.0.0-rc.1 --prerelease --generate-notes
gh release create v1.0.0 --draft --generate-notes
gh release create v1.0.0 ./dist/*.tar.gz
gh release create v1.0.0 ./dist/app_linux ./dist/app_mac.zip
gh release create v1.0.0 --verify-tag --generate-notes
gh release create v1.1.0 --generate-notes --notes-start-tag v1.0.0
gh release create v1.0.0 --latest=false
FlagEffect
--generate-notesAuto-generate title and notes from merged PRs
-n, --notesNotes inline
-F, --notes-fileNotes from a file, - for stdin
--notes-from-tagUse the annotated tag's message as the notes
--notes-start-tagWhere auto-generated notes should start from
--target <branch|SHA>What the tag points at when it does not exist yet
-p, --prereleaseMark as a prerelease
-d, --draftUnpublished draft
--latest, --latest=falseForce or forbid the "Latest" badge
--verify-tagAbort unless the tag already exists on the remote
--discussion-categoryOpen a discussion alongside the release
--fail-on-no-commitsFail if nothing landed since the last release
-t, --titleRelease title, defaults to the tag

If the tag does not exist remotely, gh creates it from --target (or the default branch). --verify-tag turns that convenience off, which is what you want in CI where a typo would otherwise tag the wrong commit.

Listing, Viewing, Editing

gh release list
gh release list --limit 100 --order asc
gh release list --exclude-drafts --exclude-pre-releases
gh release list --json tagName,publishedAt,isLatest
gh release view                        # the latest release
gh release view v1.0.0
gh release view v1.0.0 --web
gh release view --json tagName,body --jq .body
gh release view --json assets --jq '.assets[].name'
gh release edit v1.0.0 --title "v1.0.0 (stable)"
gh release edit v1.0.0 --notes-file NOTES.md
gh release edit v1.0.0 --draft=false   # publish a draft
gh release edit v1.0.0 --prerelease=false --latest
gh release edit v1.0.0 --tag v1.0.1    # rename the tag
gh release delete v1.0.0 --yes
gh release delete-asset v1.0.0 app.tar.gz --yes

gh release view with no tag resolves to the latest published release, which makes it a cheap way to script "what is the current version".

Assets

gh release upload v1.0.0 ./dist/app.tar.gz
gh release upload v1.0.0 ./dist/*        # every built artifact
gh release upload v1.0.0 ./dist/app.tar.gz --clobber

--clobber deletes and re-uploads an asset of the same name. Without it, a repeat upload fails, which is the usual cause of a red CI job on a re-run.

gh release download v1.0.0                     # every asset into .
gh release download                            # assets of the latest release
gh release download v1.0.0 --dir ./downloads
gh release download v1.0.0 --pattern "*linux*"
gh release download v1.0.0 --pattern "*.tar.gz" --pattern "*.sha256"
gh release download v1.0.0 --output app.tgz --pattern "*linux*.tar.gz"
gh release download v1.0.0 --output - --pattern "*.tar.gz" | tar xz
gh release download v1.0.0 --archive tar.gz    # the source archive
gh release download v1.0.0 --archive zip
gh release download v1.0.0 --clobber
gh release download v1.0.0 --skip-existing

--archive fetches the auto-generated source tarball instead of the uploaded assets, and cannot be combined with --pattern. -O - streams a single asset to stdout, which is how you pipe a release straight into tar.

Gists

Gists are secret by default. -p makes one public, and there is no way to flip that afterward.

gh gist create script.sh
gh gist create -p script.sh                    # public
gh gist create notes.md -d "deploy runbook"
gh gist create a.py b.py c.py                  # multi-file gist
gh gist create *.sql
gh gist create - < output.txt                  # from stdin
cat log.txt | gh gist create -f log.txt -      # name the stdin file
gh gist create script.sh --web                 # open it after creating
FlagEffect
-p, --publicPublic gist, default is secret
-d, --descDescription
-f, --filenameFilename to use when reading stdin
-w, --webOpen in the browser afterward

"Secret" means unlisted, not private. Anyone with the URL can read it, so never put a credential in one.

Working With Existing Gists

A gist is addressed by its ID or its full URL.

gh gist list                                   # your 10 most recent
gh gist list --limit 50 --public
gh gist list --secret
gh gist list --filter "deploy"                 # regex over names and descriptions
gh gist list --filter "TODO" --include-content # search inside the files too
gh gist view 5b0e0062eb8e9654adad7bb1d81cc75f
gh gist view <id> --files                      # list the filenames
gh gist view <id> --filename script.sh
gh gist view <id> --raw                        # unrendered, pipe-safe
gh gist view <id> --web
gh gist edit <id>                              # open in $EDITOR
gh gist edit <id> script.sh                    # edit that file from disk
gh gist edit <id> --filename script.sh
gh gist edit <id> --add newfile.py
gh gist edit <id> --remove old.py
gh gist edit <id> --desc "updated runbook"
gh gist rename <id> old.py new.py
gh gist clone <id> ~/src/my-gist               # it is a real git repo
gh gist clone <id> -- --depth=1
gh gist delete <id>

Every gist is a git repository, so gh gist clone then normal git commit and git push is the way to make a larger edit or keep history.