GitHub: web features, the gh command line, and Actions
https://github.com | gh <command>
Git is the version control tool on your computer. GitHub is a website and service built around it: it hosts your
repositories and adds collaboration (pull requests, issues, reviews), automation (Actions), security scanning and
publishing (Pages, releases, packages). This sheet covers the parts you use every day, the "gh" command line tool,
and GitHub Actions. See the Git sheet for the git commands themselves. GitHub adds and renames features often,
so treat menu names as a guide and check docs.github.com for the current details.
Git vs GitHub (they are not the same thing)
Git The version control program. Works offline, on your machine, with any host
GitHub A company and website that hosts git repositories and adds pull requests, issues, Actions and more
Others GitLab, Bitbucket, Gitea and Codeberg do similar jobs. The git commands are the same everywhere
Account setup (do this once)
1. Create an account at github.com and turn on two-factor authentication (an authenticator app, a passkey or a security key)
2. Set your name and email in git so commits match your account:
git config --global user.name "Your Name"
git config --global user.email "ID+username@users.noreply.github.com" # your private noreply address (Settings > Emails)
3. Add an SSH key so you can push without typing a password:
ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub # copy it into GitHub: Settings > SSH and GPG keys > New SSH key
ssh -T git@github.com # should greet you by username
4. Or install the GitHub CLI and log in (it can set up credentials for you): gh auth login
Passwords no longer work for git over HTTPS. Use SSH keys, the GitHub CLI, or a personal access token.
Tokens (personal access tokens)
Settings > Developer settings > Personal access tokens
Fine-grained tokens Limited to chosen repositories and specific permissions, with an expiry date. Prefer these
Classic tokens Broad scopes (repo, workflow, ...). Use only if you must
- Give the fewest permissions and the shortest lifetime that works
- Store tokens in a password manager or a secrets store, never in code, chat messages or git
- If one leaks, delete it in Settings right away. GitHub also scans public repos and revokes many leaked tokens
Use in scripts: export GH_TOKEN=ghp_xxx gh api user or curl -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user
Create and connect a repository
On the website: + (top right) > New repository > name, visibility (public or private), README, .gitignore, license
gh repo create my-project --public --clone # create it and clone it
gh repo create my-project --private --source=. --remote=origin --push # publish the folder you are in
git remote add origin git@github.com:USER/my-project.git # connect an existing local repo by hand
git push -u origin main # first push, sets the upstream
gh repo clone owner/repo # clone by name
gh repo view --web # open the repository in your browser
gh repo list gh repo list myorg --limit 100
gh repo delete owner/repo # asks for confirmation. Cannot be undone
Good starting files: README.md (what it is and how to use it), LICENSE, .gitignore, and later CONTRIBUTING.md and SECURITY.md.
Fork and contribute to someone else's project
1. Fork: gh repo fork owner/repo --clone (or the Fork button on the website)
2. Add the original: git remote add upstream https://github.com/owner/repo.git (gh does this when you use --clone)
3. Branch: git switch -c fix-typo
4. Change, commit: git commit -am "Fix typo in README"
5. Push to your fork: git push -u origin fix-typo
6. Open a pull request: gh pr create --fill (or the "Compare & pull request" button)
Keep your fork current:
gh repo sync # sync your fork on GitHub from upstream
git fetch upstream && git switch main && git merge upstream/main && git push # the manual way
Read CONTRIBUTING.md first. Small, focused pull requests with a clear description are merged faster.
Pull requests (PRs)
A PR proposes merging one branch into another and is the place for review and discussion.
gh pr create # interactive: title, body, base branch
gh pr create --title "Add login" --body "Adds the login form. Fixes #12" --base main
gh pr create --fill # use your commit message(s) for the title and body
gh pr create --draft # a draft PR: not ready for review yet
gh pr ready # mark a draft as ready
gh pr list gh pr list --author @me gh pr list --state all --search "login"
gh pr view 42 gh pr view 42 --web gh pr view --comments
gh pr checkout 42 # fetch and switch to someone's PR branch to test it
gh pr diff 42 gh pr checks 42 # show CI status (add --watch to follow)
gh pr review 42 --approve gh pr review 42 --request-changes --body "Please add tests"
gh pr review 42 --comment --body "Looks good overall"
gh pr comment 42 --body "Thanks!"
gh pr merge 42 --squash --delete-branch # squash, merge or rebase (--squash | --merge | --rebase)
gh pr merge 42 --auto --squash # merge automatically when checks pass and approvals are in
gh pr close 42 gh pr reopen 42 gh pr edit 42 --add-label bug --add-reviewer alice
gh pr status # PRs that need your attention
Merge methods:
Merge commit Keeps all commits and adds a merge commit. Full history
Squash and merge Combines the PR into ONE commit on the base branch. Clean history, common default
Rebase and merge Replays each commit on top of the base. Linear history, no merge commit
Closing keywords: write "Fixes #123", "Closes #123" or "Resolves #123" in the PR description or a commit message, and the issue closes when it merges.
Reviewing on the website: Files changed > hover a line and click + to comment > "Review changes" > Comment, Approve or Request changes.
Suggested changes: in a review comment use the "Suggest change" button, and the author can apply it with one click.
Issues
gh issue create # interactive
gh issue create --title "Login crashes" --body "Steps: ..." --label bug --assignee @me
gh issue list gh issue list --label bug --state open --assignee @me
gh issue view 12 gh issue view 12 --web gh issue comment 12 --body "I can reproduce this"
gh issue close 12 gh issue reopen 12 gh issue edit 12 --add-label "help wanted"
gh issue develop 12 --checkout # create a branch linked to the issue and switch to it
gh issue pin 12 gh issue transfer 12 other/repo
Organize with: labels, milestones, assignees, projects (boards and tables) and issue templates.
References in text: #123 (issue or PR), @username (mention and notify), owner/repo#123, a commit SHA (auto-links).
Search (website and gh)
is:issue is:open label:bug author:@me # your open bug issues
is:pr is:open review-requested:@me # PRs waiting for your review
is:pr is:merged merged:>2026-01-01
repo:owner/name in:title crash # search within one repository
language:python stars:>500 topic:cli # find repositories
org:myorg archived:false
path:.github/workflows filename:ci.yml # code search: files by path and name
"exact phrase" -label:wontfix no:assignee comments:>5 created:>2026-01-01
gh search repos "topic:homelab language:go" --limit 10
gh search issues "is:open label:good-first-issue language:python"
gh search prs "review-requested:@me is:open"
gh search code "TODO" --repo owner/repo
Website keyboard shortcuts (press ? on any page for the full list)
? Show shortcuts / Focus the search box
t File finder (fuzzy search files) . Open the repo in the web editor (github.dev)
g i Go to Issues g p Go to Pull requests
c Create an issue (on the Issues tab) y Turn the URL into a permalink to this exact commit
l Change the label (on an issue or PR) b Open the blame view (on a file)
Tip: press . on any repository or PR to edit in a VS Code-style editor in your browser.
Branch protection and rulesets (protect main)
Settings > Branches (or Rules > Rulesets):
[ ] Require a pull request before merging, with at least 1 approval
[ ] Require status checks to pass (your CI jobs)
[ ] Require conversation resolution before merging
[ ] Require signed commits or linear history (optional)
[ ] Restrict who can push, and block force pushes and branch deletion
[ ] Include administrators, so the rules apply to everyone
[ ] Use a merge queue on busy repositories, so PRs are tested together before merging
CODEOWNERS (.github/CODEOWNERS) automatically requests reviews from the right people:
* @myorg/maintainers
/docs/ @alice
*.tf @myorg/platform-team
/.github/ @myorg/security-team
Some rules need a paid plan for private repositories. Public repositories get most features free.
Repository files in the .github folder
.github/workflows/*.yml GitHub Actions workflows
.github/ISSUE_TEMPLATE/bug_report.yml Issue forms (structured templates)
.github/pull_request_template.md PR description template
.github/CODEOWNERS Default reviewers
.github/dependabot.yml Automatic dependency update pull requests
.github/FUNDING.yml Sponsor button
SECURITY.md How to report vulnerabilities (shows in the Security tab)
CONTRIBUTING.md, CODE_OF_CONDUCT.md Rules for contributors
README.md The front page. Also works in a profile repo named after your username (a profile README)
GitHub Actions: automation and CI/CD
Actions run workflows (YAML files in .github/workflows/) when something happens in your repo: a push, a pull request,
a schedule, a manual click. Typical uses: run tests, build, lint, deploy, publish, label issues.
Minimal CI workflow (.github/workflows/ci.yml):
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch: # adds a "Run workflow" button
permissions:
contents: read # least privilege for the built-in token
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test
Structure: workflow > jobs (run in parallel by default) > steps (run in order). "uses:" runs an action, "run:" runs a shell command.
Triggers (on:)
push, pull_request Code events (filter with branches:, tags:, paths:)
workflow_dispatch Manual run, with optional inputs
schedule: - cron: "0 3 * * 1" Times are UTC. Runs may be delayed, and are paused on repos with no activity for 60 days
release, issues, issue_comment, pull_request_target, workflow_run, workflow_call (reusable)
Useful pieces
runs-on: ubuntu-latest | windows-latest | macos-latest | self-hosted Where the job runs
needs: [build] Wait for another job
if: github.ref == 'refs/heads/main' Conditions (also on steps)
strategy:
matrix:
node: [20, 22]
os: [ubuntu-latest, windows-latest] Run the job for every combination
env: and with: Environment variables and action inputs
${{ secrets.MY_SECRET }} A secret (Settings > Secrets and variables > Actions)
${{ vars.MY_SETTING }} A non-secret variable
${{ github.sha }} ${{ github.ref_name }} ${{ github.actor }} Context values
concurrency: group: deploy-${{ github.ref }} cancel-in-progress: true Cancel older runs of the same thing
timeout-minutes: 15 Stop runaway jobs
actions/cache@v4 Cache dependencies
actions/upload-artifact@v4 / download-artifact@v4 Pass files between jobs or keep build output
environment: production Use an environment with secrets and required approvals
Deploy job idea: needs: test, if: github.ref == 'refs/heads/main', environment: production, then a deploy step.
Run and inspect from the command line:
gh workflow list gh workflow run ci.yml gh workflow run deploy.yml -f env=staging
gh run list gh run list --workflow ci.yml --limit 5
gh run view 123456 --log gh run view --log-failed gh run watch
gh run rerun 123456 --failed gh run cancel 123456
gh secret set MY_SECRET # prompts for the value (or: gh secret set NAME < file)
gh secret list gh variable set MY_SETTING --body "value"
Test workflows on your own machine (optional): the "act" tool runs them in Docker. act -l act push
Secure your workflows
- permissions: at the top of every workflow, as narrow as possible (contents: read by default). Grant more only per job
- Pin third-party actions to a full commit SHA, not a moving tag: uses: some/action@a1b2c3d4... (Dependabot can update them)
- Never print secrets. GitHub masks known secrets in logs, but derived values can leak
- Do not put untrusted text (issue titles, PR titles, branch names) straight into a "run:" line. Pass it through env: instead
Bad: run: echo "${{ github.event.pull_request.title }}" Good: env: TITLE: ${{ github.event.pull_request.title }} run: echo "$TITLE"
- Be careful with pull_request_target and workflow_run: they can run with secrets on code from forks
- Use OIDC (permissions: id-token: write) to log in to AWS, Azure or GCP without long-lived cloud keys
- Require approval for workflows from first-time contributors (Settings > Actions > General)
- Prefer GitHub-hosted runners for public repos. Self-hosted runners on public repos are risky
Security features
Dependabot Alerts for vulnerable dependencies, security updates and version updates. Turn on in Settings > Code security
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule: { interval: "weekly" }
- package-ecosystem: "npm"
directory: "/"
schedule: { interval: "weekly" }
Secret scanning Finds committed tokens and keys. Push protection blocks the push before a secret lands in history
Code scanning CodeQL and other analyzers find bugs and vulnerabilities in pull requests (Security tab)
Private vulnerability reporting / Security advisories Let people report problems privately, and publish fixes with CVEs
Dependency graph and SBOM See and export what your project depends on
Signed commits Show a "Verified" badge. Sign with SSH or GPG:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
Deploy keys A read-only (or write) SSH key limited to ONE repository, good for servers that only need to pull
If a secret is committed: revoke and rotate it FIRST. Rewriting history alone does not make it safe again.
Some features are free for public repositories and need a paid plan (GitHub Advanced Security) for private ones.
GitHub Pages (free static website hosting, good for this site)
1. Push your site files (index.html and the rest) to a repository
2. Settings > Pages > Build and deployment > Source: "Deploy from a branch", choose main and / (root) or /docs, then Save
3. After a minute your site is live at https://USERNAME.github.io/REPO/
A repo named USERNAME.github.io is published at https://USERNAME.github.io/
4. Custom domain: Settings > Pages > Custom domain, then add DNS records (a CNAME to USERNAME.github.io, or A records for an apex domain)
and tick "Enforce HTTPS"
Notes: file names are case-sensitive on Pages (About.html is not about.html). Relative links work fine.
Add an empty file named .nojekyll in the root to stop GitHub processing the site with Jekyll.
To build with Actions instead (for a static site generator), choose Source: GitHub Actions and use the actions/deploy-pages workflow.
Pages sites are public even if the repository is private on some plans: check before publishing anything sensitive.
Releases, tags and packages
git tag -a v1.2.0 -m "Release 1.2.0" && git push origin v1.2.0
gh release create v1.2.0 --generate-notes # release with auto-generated notes from merged PRs
gh release create v1.2.0 ./dist/app.tar.gz --title "1.2.0" # attach files
gh release list gh release view v1.2.0 gh release download v1.2.0 --pattern "*.tar.gz"
gh release delete v1.2.0
Container images on GitHub Container Registry (ghcr.io):
echo $GH_TOKEN | docker login ghcr.io -u USERNAME --password-stdin # token needs the write:packages scope
docker tag myapp ghcr.io/USERNAME/myapp:1.0 && docker push ghcr.io/USERNAME/myapp:1.0
Also: npm, Maven, NuGet and RubyGems packages.
Gists (small snippets)
gh gist create notes.txt --public # or without --public for a secret (unlisted) gist
gh gist list gh gist view ID gh gist edit ID
Secret gists are not private: anyone with the link can read them.
Markdown on GitHub (README, issues, PRs, comments)
# Heading **bold** *italic* `inline code` [link text](https://example.com) 
- [ ] a task that can be ticked - [x] a finished task > a quote --- (horizontal rule)
```bash
echo "code block with syntax colors"
```
| Column A | Column B |
|----------|----------|
| one | two |
> [!NOTE] Alerts: NOTE, TIP, IMPORTANT, WARNING, CAUTION
> Useful information.
<details><summary>Click to expand</summary> Hidden content here </details>
@username notifies a person #123 links an issue owner/repo#123 links across repos :tada: emoji shortcodes
```mermaid
graph LR; A[Push] --> B[Test] --> C[Deploy]
```
Drag and drop an image or file into a comment box to upload it.
The gh command line: setup and power tools
Install: brew install gh | sudo apt install gh (recent Ubuntu/Debian, or use GitHub's apt repository) | sudo dnf install gh | winget install GitHub.cli
gh auth login # choose GitHub.com, HTTPS or SSH, and log in through the browser
gh auth status # who am I, and what scopes?
gh auth setup-git # let git use gh's credentials for HTTPS
gh browse # open this repo on github.com gh browse src/app.py:20 a file and line
gh --help gh pr --help # every command has help
gh api user # call the REST API (the token is added for you)
gh api repos/OWNER/REPO/issues --jq '.[].title'
gh api graphql -f query='{ viewer { login } }'
gh pr list --json number,title,author --jq '.[] | "\(.number) \(.title) by \(.author.login)"' # JSON output plus jq
gh alias set co 'pr checkout' # your own shortcuts gh alias list
gh extension install dlvhdr/gh-dash # extensions add features (for example a PR dashboard)
gh label create urgent --color D93F0B gh label list
gh codespace create -R owner/repo gh codespace ssh # a cloud dev environment
Add -R owner/repo to most commands to work on a repository you are not currently inside.
Other useful GitHub features
Projects Boards, tables and roadmaps that track issues and PRs. Workflows can move cards automatically
Discussions Forum-style Q&A and announcements, separate from issues
Wiki A simple documentation area (or keep docs in the repo, which is easier to review)
Codespaces A full development environment in the cloud, configured by .devcontainer/devcontainer.json
Copilot AI code suggestions, chat and pull request summaries. Review everything it writes, as with any AI (see the AI-Assisted Shell Scripting guide)
Organizations Shared ownership: teams, permissions, SSO and audit logs
Notifications Settings > Notifications. Use "Watch" > "Custom" to reduce noise. gh has no inbox, use the web or the mobile app
Insights Contributors, traffic, dependency graph and code frequency graphs
Practical one-liners
# Start a new project and publish it in one go
git init -b main && git add -A && git commit -m "Initial commit" && gh repo create my-project --private --source=. --push
# Open a PR from the current branch, filling the description from commits
git push -u origin HEAD && gh pr create --fill
# Check out and test a colleague's PR, then go back
gh pr checkout 42 && ./run-tests.sh; git switch main
# See whether CI passed on my current PR
gh pr checks --watch
# All open PRs waiting for my review, across GitHub
gh search prs "review-requested:@me is:open"
# Read the failed part of the latest CI run
gh run view --log-failed
# Delete merged branches on the remote for a PR you just merged
gh pr merge 42 --squash --delete-branch
# Find who changed a line and open that commit
git blame -L 10,10 file.py && gh browse COMMIT_SHA
# Clone every repository in an organization
gh repo list myorg --limit 200 --json nameWithOwner --jq '.[].nameWithOwner' | xargs -I{} gh repo clone {}
# Bulk-close stale issues with a label (review first)
gh issue list --label stale --json number --jq '.[].number' | xargs -I{} gh issue close {}
# Download a release asset in a script
gh release download v1.2.0 --repo owner/tool --pattern "*linux-amd64*"
# Export all issues to JSON
gh issue list --state all --limit 1000 --json number,title,state,labels > issues.json
# Rate limit check
gh api rate_limit --jq '.resources.core'
Common problems
"Permission denied (publickey)" Your SSH key is not added to GitHub or to ssh-agent (ssh-add ~/.ssh/id_ed25519). Test with ssh -T git@github.com
"Authentication failed" over HTTPS Passwords are not accepted. Use gh auth login, a personal access token, or SSH
"remote: Repository not found" A typo, or you lack access (private repo). Check gh auth status and the remote URL
"protected branch hook declined" The branch is protected. Push to a branch and open a pull request
"GH006 / required status check is expected" A required check has not run or has failed. Look at the Checks tab
"file exceeds GitHub's file size limit of 100 MB" Use Git LFS, or remove the file from history. Do not commit large binaries
PR says "This branch has conflicts" Merge or rebase the base branch into yours, fix the conflicts, and push
PR "merge" button is greyed out Missing approvals, failing checks, unresolved conversations, or an out-of-date branch
Workflow does not run The file must be in .github/workflows/ on the default branch (for some triggers), the YAML must be valid, and check "on:" filters (branches, paths)
Workflow from a fork has no secrets By design. Secrets are not passed to pull requests from forks
"Resource not accessible by integration" The GITHUB_TOKEN lacks a permission. Add it under permissions: for that job
Pushes made by GITHUB_TOKEN do not start other workflows By design (avoids loops). Use a GitHub App token or a PAT if you really need chaining
Scheduled workflow did not run Cron is UTC, can be delayed, and pauses after 60 days of repo inactivity
Action version not found Check the tag exists (uses: actions/checkout@v4), or pin a full SHA
"API rate limit exceeded" Authenticate (gh or a token). Unauthenticated calls have a small hourly limit
Pages site shows a 404 Check Settings > Pages, the branch and folder, the exact file name and letter case, and wait a minute
Commits show under the wrong user or unlinked The commit email does not match one on your account. Fix user.email, or add the email in Settings
Pushed a secret Revoke and rotate it now, then clean history. See the Git sheet's "Removing a secret" section
Tips
Use SSH keys or the gh CLI for authentication, turn on two-factor authentication, and prefer fine-grained tokens with short lifetimes.
Work in branches and pull requests, and protect main with required reviews and passing checks.
Write clear PR descriptions and link issues with "Fixes #123". Small pull requests get reviewed faster.
Use gh for the daily loop: gh pr create --fill, gh pr checks --watch and gh pr merge. It saves trips to the browser.
Give workflows minimal permissions, pin third-party actions to a commit SHA, and never put untrusted text directly into a run: step.
Turn on Dependabot, secret scanning with push protection, and code scanning. They catch problems automatically.
Add a README, LICENSE, SECURITY.md, CONTRIBUTING.md, issue and PR templates and a CODEOWNERS file to every serious project.
Keep secrets in Actions secrets or a proper secrets manager, never in the repository. Rotate anything that leaks.
GitHub Pages is a free way to publish a static site like this one straight from a repository.
GitHub changes often. Check docs.github.com and gh <command> --help when a menu or option looks different.