← Back to dashboard
githubtoolingfresh

GitHub Developer Course

What is GitHub?

The real model

Commit DAG + PR-gated trunk; automate with Actions, secure with the built-in suite, drive from Claude Code.

Master is protected; work happens on branches merged via PRs that must pass review + required status checks. Choose your integration deliberately: merge (preserve shape), rebase (linear, local-only before pushing), squash (one tidy commit — codeAmani's default into master). Actions runs CI on `on: [push, pull_request]` with jobs/needs/matrix and least-privilege `permissions`. Turn on secret scanning + push protection so a leaked Daraja key never lands. Claude Code drives PRs/issues/Actions via the GitHub MCP, and a push to master fires the Vercel deploy — so green CI + branch protection are what keep production safe.

Six things to master

From commits to CI to Claude Code. Tap a card for the commands + gotchas.

Branch & PR simulator — merge vs squash vs rebase

The concept new developers most often get wrong. Switch the integration strategy and watch the commit graph redraw — then walk a pull request from open to deployed, and toggle a failing check to see branch protection block the merge.

Integrate the feature branch with:
masterfeatureABCSDE
merge --squashD + E collapse into one commit S on master. The branch detail is discarded — master reads as one tidy commit per feature. codeAmani's default.
PR lifecycle
  1. 1Open
  2. 2Review
  3. 3CI checks
  4. 4Merged

Push a branch and `gh pr create`. The PR is a proposal — nothing has touched master yet.

Text
 ██████╗ ██╗████████╗██╗  ██╗██╗   ██╗██████╗
██╔════╝ ██║╚══██╔══╝██║  ██║██║   ██║██╔══██╗
██║  ███╗██║   ██║   ███████║██║   ██║██████╔╝
██║   ██║██║   ██║   ██╔══██║██║   ██║██╔══██╗
╚██████╔╝██║   ██║   ██║  ██║╚██████╔╝██████╔╝
 ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═════╝

GitHub Developer Course

Focus: A hands-on path from git init to shipping with confidence — Git fundamentals, collaboration on GitHub, Actions/CI-CD, security, the wider ecosystem, and driving it all from Claude Code. Built to enhance your capabilities, not just list commands. Grounded in docs.github.com; reviewed 2026-06-07.

How this course works

Three levels, each ending with a ✅ capability checkpoint ("you can now…") and a 🛠 exercise. Work top-to-bottom the first time; use the command cheat-sheet and Table of Contents as reference after. The interactive learn module above this page — a branch & PR lifecycle simulator — is your illustration for Level 2; play with it before reading the merge/rebase section.

Table of Contents

Official Documentation


Level 1 — Git fundamentals

Git vs GitHub

Git is the version-control tool that runs on your machine — it records snapshots (commits) of your files. GitHub is the hosted home for Git repositories that adds collaboration: pull requests, reviews, issues, CI/CD, and security.

Text
   your machine (Git)                         GitHub (remote)
 ┌────────────────────┐    git push     ┌────────────────────┐
 │ working dir        │ ─────────────▶  │  origin/master     │
 │   └ staging (index)│                 │  PRs · Actions ·    │
 │       └ .git/ (commits, branches)    │  Issues · Security  │
 └────────────────────┘ ◀───────────── └────────────────────┘
                          git pull

A file moves through three states: working directorystaging area (git add) → committed history (git commit).

Setup & first-time config

Bash
# Install Git + the GitHub CLI
winget install Git.Git GitHub.cli     # Windows
brew install git gh                    # macOS
sudo apt install git gh               # Debian/Ubuntu (incl. WSL)

# One-time identity
git config --global user.name  "Your Name"
git config --global user.email "you@codeamani.com"
git config --global init.defaultBranch main
git config --global pull.rebase false   # merge on pull (or 'true' to rebase)

# Authenticate the CLI (browser flow; stores a token securely)
gh auth login

The core loop

Bash
git init                      # start a repo here   (or: gh repo clone owner/name)
git status                    # what changed?
git add file.ts               # stage a change      (git add -A for everything)
git commit -m "feat: add login form"
git log --oneline --graph     # see history as a graph
git diff                      # unstaged changes    (git diff --staged for staged)

Write good commit messages — a short imperative summary (fix: handle null token), optionally a body explaining why. The codeAmani convention follows Conventional Commits (feat:, fix:, chore:, docs:).

Branches: the cheap pointer

A branch is just a movable pointer to a commit — creating one is instant and free. This is the mental model that makes everything else click.

Bash
git switch -c feature/mpesa-stk      # create + switch (old: git checkout -b)
git switch master                    # switch back
git branch                           # list local branches
git branch -d feature/mpesa-stk      # delete a merged branch

Remotes & pushing to GitHub

Bash
gh repo create codeAmani/my-app --private --source=. --push   # create + link + push
# …or link an existing remote
git remote add origin https://github.com/codeAmani/my-app.git
git push -u origin master            # -u sets the upstream once
git pull                             # fetch + merge remote changes
git fetch origin                     # download without merging
gitignore
# .gitignore — never track secrets or build junk
.env*
node_modules/
.next/
*.log

Checkpoint: You can initialise a repo, stage and commit changes, branch, and push to GitHub. 🛠 Exercise: Create a repo with gh repo create, add a README.md, commit on a feature/readme branch, and push it.


Level 2 — Collaboration

Merge vs rebase vs squash

The three ways to integrate a branch — the concept developers most often get wrong. Play with the simulator above to see the commit graph redraw for each. Here's a feature branch being merged back, drawn as a real commit graph:

StrategyWhat it doesHistoryUse when
MergeCreates a merge commit joining both linesPreserves true branch shapeShared/long-lived branches; you want the full record
RebaseReplays your commits on top of the targetLinear, no merge commitsCleaning up your local branch before a PR
SquashCombines all branch commits into oneOne tidy commit per featureMerging a PR into master (the codeAmani default)
Bash
git merge feature/x                  # merge feature/x into current branch
git rebase master                    # replay current branch on top of master
git rebase -i HEAD~3                  # interactively squash/reorder last 3 commits
# Golden rule: never rebase commits you've already pushed to a shared branch.
Text
merge:   A───B───C (master)          rebase:  A───B───C───D'──E' (master)
              \                                (D,E replayed cleanly on top)
               D───E (feature) ──▶ merge commit M

Pull requests: the unit of collaboration

A PR proposes merging one branch into another, gated by review + CI before it lands. Lifecycle: open → review → CI checks → approve → merge.

Bash
gh pr create --title "feat: M-Pesa STK push" --body "Implements Daraja STK flow"
gh pr create --fill                  # use branch name + last commit as title/body
gh pr list                           # open PRs
gh pr view 42 --web                  # open in browser
gh pr checks 42                      # CI status for the PR
gh pr merge 42 --squash --delete-branch

Code review

Bash
gh pr diff 42                        # read the changes
gh pr review 42 --approve
gh pr review 42 --request-changes --body "Validate the phone format (2547…)"
gh pr review 42 --comment --body "Nice — one nit inline"

A good review checks: correctness, security (no secrets, input validated), tests, and clarity. Keep PRs small — they get reviewed faster and merge cleaner.

Resolving conflicts

Bash
git switch feature/x
git merge master                     # conflict markers appear in files
# Edit the <<<<<<< / ======= / >>>>>>> sections, choosing the right code, then:
git add resolved-file.ts
git commit                           # completes the merge

Undoing things safely

GoalCommandSafe on shared history?
Discard unstaged file changegit restore file
Unstage a filegit restore --staged file
Amend the last commitgit commit --amend❌ (rewrites)
Undo a commit, keep changesgit reset --soft HEAD~1
Revert a pushed commitgit revert <sha>✅ (new inverse commit)
Stash work-in-progressgit stash / git stash pop
Recover "lost" commitsgit reflog✅ (your safety net)

git revert is the safe public undo; git reset/--amend rewrite history (force-push territory). When in doubt, git reflog remembers where everything was.

Issues, labels & Projects

Bash
gh issue create --title "Bug: STK callback times out" --label bug,priority:high
gh issue list --assignee @me
gh issue close 17 --comment "Fixed in #42"
# Projects (v2) — track work on a board; manage via the GraphQL API or the UI
gh project list --owner codeAmani

Branch protection

Protect master so nothing merges unreviewed or red. Set via Settings → Branches or the API:

Bash
gh api -X PUT repos/codeAmani/my-app/branches/master/protection \
  -F required_pull_request_reviews.required_approving_review_count=1 \
  -F required_status_checks.strict=true \
  -F enforce_admins=true

Checkpoint: You can open a PR, review it, resolve conflicts, undo mistakes safely, and protect a branch. 🛠 Exercise: Open a PR from your feature/readme branch, request a change on it, push a fix, then squash-merge it.


Level 3 — Automation & ecosystem

GitHub Actions (CI/CD)

Workflows are YAML in .github/workflows/. Structure: events (on) → jobs → steps. Jobs run in parallel unless chained with needs.

YAML
# .github/workflows/ci.yml
name: CI
on:
  push: { branches: [master] }
  pull_request: { branches: [master] }
  workflow_dispatch:           # manual "Run workflow" button

permissions:
  contents: read               # least privilege by default

concurrency:                   # cancel superseded runs on the same ref
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [20, 22]         # run across versions in parallel
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with: { node-version: ${{ matrix.node }}, cache: npm }
      - run: npm ci
      - run: npm test

  deploy:
    needs: test                # only after test passes
    if: github.ref == 'refs/heads/master'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: echo "Deploy step (Vercel auto-deploys on push for codeAmani)"

Key building blocks:

  • Triggerspush, pull_request, workflow_dispatch (manual), schedule (cron).
  • needs — declare job dependencies and pass outputs between jobs.
  • matrix — fan a job across versions/OSes ([20,22] × [ubuntu,windows] = N jobs).
  • permissions — scope the built-in GITHUB_TOKEN; start at read.
  • Cachingactions/cache@v5, or cache: npm on setup-node.
  • Secrets${{ secrets.NAME }}; prefer OIDC over long-lived cloud keys.
  • Reusable workflowsuses: org/repo/.github/workflows/deploy.yml@main with with: inputs.
Bash
gh workflow list
gh workflow run ci.yml --field environment=production
gh run watch                   # live-stream the active run
gh run view 12345678 --log

Packages & Releases

Bash
# Releases (auto-generate notes from merged PRs)
gh release create v1.2.0 --generate-notes
gh release list
# Packages — publish to GitHub Packages (npm/Container/etc.)
# npm: set "publishConfig": { "registry": "https://npm.pkg.github.com" } then `npm publish`

Security (shift left)

GitHub's built-in developer security suite (enable under Settings → Code security):

FeatureWhat it does
Dependabot alertsFlags dependencies with known vulnerabilities
Dependabot security updatesAuto-opens PRs to patch vulnerable deps
Dependabot version updatesAuto-opens PRs to keep deps current (dependabot.yml)
Secret scanningDetects hardcoded credentials committed to the repo
Push protectionBlocks a push if it contains a detected secret
Code scanning (CodeQL)Static analysis for vulns/bugs in new or changed code
Dependency graphMaps what your repo depends on (and what depends on it)
Security advisoriesPrivately discuss + fix, then publish a vulnerability alert
YAML
# .github/dependabot.yml — keep npm deps current weekly
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule: { interval: "weekly" }
YAML
# .github/workflows/codeql.yml — CodeQL code scanning
name: CodeQL
on: { push: { branches: [master] }, pull_request: { branches: [master] } }
jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions: { security-events: write, contents: read }
    steps:
      - uses: actions/checkout@v5
      - uses: github/codeql-action/init@v3
        with: { languages: javascript-typescript }
      - uses: github/codeql-action/analyze@v3

Codespaces

A cloud dev environment (a container in the browser or VS Code) defined by a devcontainer.json — instant, reproducible onboarding.

JSON
// .devcontainer/devcontainer.json
{
  "image": "mcr.microsoft.com/devcontainers/javascript-node:22",
  "features": { "ghcr.io/devcontainers/features/github-cli:1": {} },
  "postCreateCommand": "npm install",
  "customizations": { "vscode": { "extensions": ["dbaeumer.vscode-eslint"] } }
}
Bash
gh codespace create -r codeAmani/my-app
gh codespace code            # open in VS Code

GitHub Pages

Free static hosting from a repo (great for docs/landing pages). Enable under Settings → Pages, or deploy via Actions:

YAML
# publishes ./dist to Pages
permissions: { pages: write, id-token: write }
# … build, then:
- uses: actions/upload-pages-artifact@v3
  with: { path: dist }
- uses: actions/deploy-pages@v4

gh CLI & API mastery

Bash
# REST — anything the API exposes
gh api repos/codeAmani/my-app/pulls --jq '.[].title'
gh api -X POST repos/codeAmani/my-app/issues -f title="From the CLI" -f body="…"
# GraphQL — precise, fewer round-trips
gh api graphql -f query='query { viewer { login } }'
# Aliases + JSON output power scripting
gh pr list --json number,title,author --jq '.[] | "\(.number) \(.title)"'

Tokens & scopes

Token typeUse it for
Fine-grained PATPreferred — per-repo, least-privilege, expiring
Classic PATLegacy; broad scopes (repo, workflow, read:org)
GITHUB_TOKEN (Actions)Auto-injected, scoped per-workflow via permissions:
OIDCKeyless cloud auth from Actions — no stored secrets
Bash
export GITHUB_TOKEN=github_pat_...   # gh + MCP read this (GH_TOKEN also works)
# Generate at: https://github.com/settings/tokens

Checkpoint: You can write a CI workflow with matrix + job dependencies, enable Dependabot + CodeQL + push protection, spin up a Codespace, and script the API. 🛠 Exercise: Add a ci.yml that runs npm test on every PR, then turn on secret scanning + push protection for the repo.


Level 4 — GitHub + Claude Code

The GitHub MCP server

The official github/github-mcp-server lets Claude open PRs, comment on issues, trigger workflows, and read security alerts in-session.

Bash
claude mcp add -s user --transport http github \
  https://api.githubcopilot.com/mcp \
  -H "Authorization: Bearer ${GITHUB_TOKEN}"
JSON
// .mcp.json
{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp",
      "headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
    }
  }
}

Docker transport (full toolset control)

Bash
claude mcp add github -- docker run -i --rm \
  -e GITHUB_PERSONAL_ACCESS_TOKEN \
  -e GITHUB_TOOLSETS="repos,issues,pull_requests,actions,code_security" \
  ghcr.io/github/github-mcp-server
ToolsetTools
reposcreate, read files, commit, push
issuescreate, comment, label, close
pull_requestscreate, review, merge, comment
actionslist + trigger workflows
code_securityread Dependabot + code-scanning alerts

The npm package @modelcontextprotocol/server-github is deprecated (April 2025). Use HTTP or the Docker image above.

Claude Code automations

Markdown
<!-- .claude/commands/review-pr.md -->
Review pull request #$ARGUMENTS.
1. Use the GitHub MCP to fetch the PR diff + description and existing comments.
2. Analyse for bugs, security issues, missing tests, and convention drift.
3. Post a review: approve if safe, else request changes with specific line notes.

Usage: /project:review-pr 42

JavaScript
// scripts/auto-pr.js — open a PR after a feature branch is pushed (Stop hook)
import { execFileSync } from "child_process";       // execFileSync = no shell injection
const branch = execFileSync("git", ["branch", "--show-current"]).toString().trim();
if (branch === "main" || branch === "master") process.exit(0);
if (execFileSync("git", ["status", "--porcelain"]).toString()) process.exit(0);
try { execFileSync("gh", ["pr", "create", "--fill"], { stdio: "inherit" }); } catch {}

Claude Code in CI

YAML
# .github/workflows/ai-fix.yml — headless Claude fixes lint on new PRs
name: AI Auto-fix
on: { pull_request: { types: [opened] } }
jobs:
  fix:
    runs-on: ubuntu-latest
    permissions: { contents: write, pull-requests: write }
    steps:
      - uses: actions/checkout@v5
        with: { ref: ${{ github.head_ref }} }
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - run: npm install -g @anthropic-ai/claude-code
      - env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: claude -p "Fix all ESLint errors" --allowedTools "Bash,Edit,Read,Glob,Grep" --output-format text
      - run: |
          git config user.name "Claude Code Bot"; git config user.email "noreply@github.com"
          git add -A && git diff --staged --quiet || git commit -m "fix: auto-fix ESLint errors"
          git push

Command cheat-sheet

Bash
# Daily loop
git status / git add -A / git commit -m "…" / git push
git switch -c feature/x      # branch        git switch master
git pull --rebase            # update cleanly  git stash / git stash pop
# History
git log --oneline --graph --all
git revert <sha>             # safe undo of a pushed commit
git reflog                   # find lost commits
# GitHub via gh
gh pr create --fill / gh pr checks 42 / gh pr merge 42 --squash --delete-branch
gh issue create / gh run watch / gh release create vX --generate-notes
gh api repos/OWNER/REPO/... # raw REST/GraphQL

Troubleshooting

IssueFix
gh: command not foundInstall via winget/brew/apt
401 on MCP HTTPRegenerate PAT; ensure repo scope (or correct fine-grained perms)
Push rejected (non-fast-forward)git pull --rebase then push
Push blocked by push protectionA secret was detected — remove it, rotate it, recommit
Merge conflictEdit markers, git add, git commit (or git merge --abort)
Rebase went wronggit rebase --abort, or recover via git reflog
Workflow didn't runCheck the on: triggers + branch/path filters
Accidentally committed a secretRotate it immediately; history rewrite (git filter-repo) + force-push

codeAmani notes

  • master is the deploy trigger. A push to master fires the Vercel build, so master must always be green and reviewed. Enforce branch protection (1 review + required CI) and merge via squash for a clean, revertable history.
  • Secrets never touch the repo. Keep them in .env.local (git-ignored) and a secret manager; turn on secret scanning + push protection, and pair with Infisical's scan in CI to catch leaks before they reach the remote. If a secret does land, rotate it first — scrubbing history is secondary.
  • AI routing. Use the GitHub MCP for PR/issue/Actions work in-session; reserve heavier review with claude -p in CI for auto-fixing lint/format. See anthropic for model/key setup.
  • African-market workflow. CI runs on GitHub's hosted runners regardless of local bandwidth — push small, let Actions do the heavy build/test. Pair with the vercel deploy and the chrome-devtools CI Lighthouse budget to guard 2G/3G performance on every PR.
  • Run it from WSL. Keep repos on the Linux filesystem for fast git, and let Git Credential Manager bridge your Windows auth.