Git & GitHub Advanced: Branching Strategies, Rebasing & GitHub Actions

Most developers know git commit, git push, and git merge. But Git's real power — the tools that distinguish average developers from great ones — lives in interactive rebase, bisect, hooks, cherry-pick, and reflog. On top of that, GitHub Actions has become the industry-standard CI/CD platform. This guide covers all of it: advanced Git workflows, branching strategies used by large teams, and complete GitHub Actions patterns from lint automation to multi-environment deployments.

1. Branching Strategies: Gitflow vs Trunk-Based

StrategyHow It WorksProsConsBest For
Gitflow Long-lived main + develop branches; feature branches from develop; release branches; hotfix branches from main Clear release lifecycle; supports multiple versions in production Complex; merge complexity grows with team; long-lived feature branches accumulate debt Products with explicit versioned releases (libraries, mobile apps with app-store gates)
Trunk-Based Development Everyone commits to main (trunk) daily; short-lived feature branches max 1–2 days; feature flags control release Simple; continuous integration is easy; less merge conflict; fast CI feedback Requires strong discipline; feature flags add complexity; harder for junior teams High-performing engineering teams, SaaS products, teams practicing continuous delivery
GitHub Flow Branch from main, open PR, CI runs, merge to main = deploy Simple; deploys on every merge; good for web apps No staging concept; rollback is a new PR not a branch Small teams, web apps with simple CI/CD

In 2026, high-performing teams use trunk-based development according to the DORA State of DevOps report. The key enabler is feature flags (LaunchDarkly, Unleash, or open-source Flagsmith) — code is merged but not yet visible to users until the flag is enabled.

2. Merge vs Rebase: When to Use Which

Merge creates a merge commit that preserves the exact history of when branches diverged and rejoined. History is truthful but can be cluttered with thousands of merge commits on active repos.

Rebase replays commits from your branch on top of the target branch, producing a linear history. Cleaner log, but rewrites commit SHAs — never rebase commits that have already been pushed to a shared branch.

# --- Merge approach ---
git checkout feature/login
git merge main   # creates a merge commit, preserves history

# --- Rebase approach ---
git checkout feature/login
git rebase main  # replays your commits on top of latest main
# Resolve conflicts per commit if any arise
# git add . && git rebase --continue

# After rebase, force-push (only for your private feature branch)
git push --force-with-lease origin feature/login
# --force-with-lease is safer than --force: fails if remote changed

3. Interactive Rebase

Interactive rebase (git rebase -i) lets you rewrite local commit history before sharing with others. Use it to squash WIP commits, reorder commits, edit commit messages, and remove debugging commits:

# Interactively rebase the last 4 commits
git rebase -i HEAD~4

# This opens your editor with something like:
# pick a1b2c3d Add login form HTML
# pick e4f5a6b Start login validation (WIP)
# pick 7c8d9e0 Fix typo
# pick b1c2d3e Complete login validation with tests
#
# Change 'pick' to one of:
#   squash (s) — meld into previous commit
#   fixup (f)  — like squash, but discard commit message
#   reword (r) — keep commit but edit message
#   drop (d)   — remove commit entirely
#   edit (e)   — pause and amend commit
#
# Result: squash/fixup WIP and typo into final commit:
# pick a1b2c3d Add login form HTML
# fixup e4f5a6b Start login validation (WIP)
# fixup 7c8d9e0 Fix typo
# reword b1c2d3e Complete login validation with tests

4. Cherry-Pick

Cherry-pick copies one or more specific commits from any branch and applies them to your current branch. Useful for backporting a bug fix to an older release branch without merging all changes:

# Cherry-pick a single commit by SHA
git cherry-pick a1b2c3d

# Cherry-pick a range of commits
git cherry-pick a1b2c3d^..e4f5a6b

# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit a1b2c3d

# If conflicts occur:
git add .
git cherry-pick --continue

5. Git Hooks

Git hooks are shell scripts that run automatically at key points in the Git workflow. Common uses: enforce commit message format, run linting before commit, run tests before push.

# .git/hooks/commit-msg — enforce conventional commit format
#!/bin/sh
MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|ci|build|perf|revert)(\(.+\))?!?: .{1,72}"
if ! echo "$MSG" | grep -qE "$PATTERN"; then
  echo "ERROR: Commit message must follow Conventional Commits format"
  echo "Examples: feat(auth): add JWT refresh   fix: resolve memory leak"
  exit 1
fi

For team-wide hooks, use Husky (Node.js projects) or pre-commit (Python/general). These manage hooks in version control so all team members get them automatically:

# Husky setup
npm install --save-dev husky
npx husky init
# Creates .husky/pre-commit — runs before every commit
echo "npx lint-staged" > .husky/pre-commit

# lint-staged in package.json: only lint files staged for commit
{
  "lint-staged": {
    "*.ts": ["eslint --fix", "prettier --write"],
    "*.css": ["prettier --write"]
  }
}

6. Stash, Worktrees & Reflog

Stash

git stash                     # stash all tracked changes
git stash push -m "WIP login fix"  # stash with name
git stash list                # list all stashes
git stash pop                 # apply most recent stash and drop it
git stash apply stash@{2}     # apply specific stash without dropping

Worktrees

Git worktrees let you check out multiple branches simultaneously in separate directories — no stashing needed when you need to quickly fix a bug while in the middle of a feature:

git worktree add ../hotfix-branch hotfix/critical-bug
# Now you have two working directories:
# /project                — your feature branch
# /hotfix-branch          — the hotfix branch
# Work on hotfix, commit, push, then remove the worktree:
git worktree remove ../hotfix-branch

Reflog

Reflog records every change to HEAD — the ultimate undo. Recovers from accidental hard resets, dropped stashes, and deleted branches:

git reflog               # show all recent HEAD movements
git reset --hard HEAD@{2}  # restore HEAD to 2 moves ago

7. Git Bisect: Binary Search for Bugs

Bisect performs a binary search through commit history to find the exact commit that introduced a bug. Invaluable for regression hunting:

git bisect start
git bisect bad           # current commit is broken
git bisect good v2.1.0   # last known good version

# Git checks out a commit halfway between — you test it
npm test
git bisect good   # or: git bisect bad

# Repeat until Git identifies the offending commit
# Automate with a test script:
git bisect run npm test  # Git runs tests automatically on each candidate

git bisect reset  # return to original HEAD when done

8. GitHub Actions Anatomy

A GitHub Actions workflow is a YAML file in .github/workflows/. Key concepts:

name: CI Pipeline

on:
  push:
    branches: [main, 'release/**']
  pull_request:
    branches: [main]

jobs:
  lint-and-test:
    runs-on: ubuntu-24.04    # runner OS

    steps:
      - uses: actions/checkout@v4          # action from marketplace

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'                     # cache node_modules between runs

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Test
        run: npm test

      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

9. Matrix Builds

Matrix builds test across multiple OS/Node/Python versions in parallel without duplicating YAML:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-24.04, windows-latest, macos-latest]
        node: ['18', '20', '22']
        exclude:
          - os: windows-latest
            node: '18'  # skip this combination

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test

10. Secrets, Environments & OIDC

Store secrets in repository Settings → Secrets, or in GitHub Environments (environment-scoped secrets with required reviewers and deployment rules).

OIDC (keyless authentication): Instead of storing long-lived cloud credentials, use OpenID Connect to assume cloud roles from GitHub Actions with short-lived tokens:

# Authenticate to AWS without storing AWS credentials as secrets
- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789:role/github-actions-role
    aws-region: us-east-1
    # GitHub issues a short-lived token — no AWS keys in GitHub secrets

11. Complete Deploy Workflow Example

12. Frequently Asked Questions

When should I squash commits on a PR?

Squash when the PR's commit history is noisy (WIP, typos, review fixes) and the PR as a whole represents one logical change. Use merge commits (no squash) when the PR's individual commits are meaningful and you want the granular history. Never squash on long-lived branches like develop — only on short-lived feature branches before merging to main.

What is the difference between git pull --rebase and git pull?

git pull is equivalent to git fetch + git merge — it creates a merge commit when your local branch and remote have diverged. git pull --rebase replays your local unpushed commits on top of the fetched remote state, keeping history linear. Set as default with git config --global pull.rebase true.

How do I undo the last commit without losing changes?

git reset --soft HEAD~1 — undoes the commit, keeps changes staged. git reset --mixed HEAD~1 — undoes the commit, keeps changes unstaged. git reset --hard HEAD~1 — undoes the commit AND discards all changes (destructive; use with caution).

13. Glossary

Trunk-Based Development
A branching model where all developers integrate to a single shared branch (trunk/main) at least once per day.
Interactive Rebase
A Git operation that lets you reorder, squash, edit, or drop commits before integrating a branch.
Cherry-Pick
Applying one or more specific commits from any branch to the current branch.
Git Hooks
Scripts that Git executes automatically before or after events like commit, push, and merge.
GitHub Actions
GitHub's built-in CI/CD platform that automates workflows via YAML files stored in the repository.
OIDC (OpenID Connect)
A standard for federated authentication; used by GitHub Actions to assume cloud roles without storing credentials.
Matrix Build
A GitHub Actions feature that runs the same job across multiple combinations of OS/language version in parallel.

14. References & Further Reading

Try git bisect on your repo today to find an old regression. It's one of Git's most powerful features and most developers have never used it. The binary search algorithm means you find the culprit commit in about 10 steps even across 1000 commits.