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
| Flag | Effect |
|---|---|
--generate-notes | Auto-generate title and notes from merged PRs |
-n, --notes | Notes inline |
-F, --notes-file | Notes from a file, - for stdin |
--notes-from-tag | Use the annotated tag's message as the notes |
--notes-start-tag | Where auto-generated notes should start from |
--target <branch|SHA> | What the tag points at when it does not exist yet |
-p, --prerelease | Mark as a prerelease |
-d, --draft | Unpublished draft |
--latest, --latest=false | Force or forbid the "Latest" badge |
--verify-tag | Abort unless the tag already exists on the remote |
--discussion-category | Open a discussion alongside the release |
--fail-on-no-commits | Fail if nothing landed since the last release |
-t, --title | Release 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
| Flag | Effect |
|---|---|
-p, --public | Public gist, default is secret |
-d, --desc | Description |
-f, --filename | Filename to use when reading stdin |
-w, --web | Open 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.