Git Advanced Guide 2026 — Branching Strategies, Rebase, and Team Workflows

Sanjeev SharmaSanjeev Sharma
6 min read

Advertisement

Introduction

Why This Matters

Most developers use 10% of Git's capabilities. The other 90% — interactive rebase, bisect, hooks, cherry-pick, and branching strategies — are what separate engineers who ship clean code from those who ship messy histories. Mastering Git makes you faster at debugging, better at code review, and a more effective collaborator in any team.

Branching Strategies

Trunk-Based Development (recommended for teams using CI/CD):

main  ← always deployable, deployed on every merge
├── feature/add-oauth   → merge within 1-2 days
├── fix/payment-bug     → merge within hours
└── release/v2.0        → cut from main, hotfixes only

Git Flow (for software with scheduled release cycles):

main       ← production releases only
develop    ← integration branch
├── feature/*   → branch from develop, merge to develop
├── release/*   → from develop → merge to main + develop
└── hotfix/*    → from main → merge to main + develop

For most teams in 2026: trunk-based development with feature flags is the faster approach.

Interactive Rebase: Clean Up History

# Squash last 5 messy commits into clean commits before pushing
git rebase -i HEAD~5
 
# Editor opens with:
# pick a1b2c3 Add user auth
# pick e4f5g6 Fix typo
# pick h7i8j9 Add tests
# pick l0m1n2 WIP
# pick p3q4r5 Fix WIP
 
# Change to:
# pick a1b2c3 Add user auth
# squash e4f5g6 Fix typo         (merge into previous)
# pick h7i8j9 Add tests
# fixup l0m1n2 WIP               (merge + discard message)
# fixup p3q4r5 Fix WIP
 
# Commands:
# pick   = use as-is
# squash = merge into previous, combine messages
# fixup  = merge into previous, discard message
# reword = change commit message only
# drop   = delete commit entirely

Cherry-Pick: Apply Specific Commits

# Apply a single commit from another branch
git cherry-pick a1b2c3
 
# Cherry-pick a range
git cherry-pick a1b2c3..p3q4r5
 
# Cherry-pick without committing (inspect first)
git cherry-pick --no-commit a1b2c3
 
# Common use case: backport a hotfix to a release branch
git checkout release/v2.0
git cherry-pick a1b2c3   # Only the bugfix commit

Git Bisect: Binary Search for Bugs

# "My app broke somewhere in the last 300 commits. Which one?"
git bisect start
git bisect bad              # Current HEAD is broken
git bisect good v1.5.0      # This tag was working fine
 
# Git checks out the midpoint commit
# Test your app, then tell Git:
git bisect good   # This commit is OK → search later half
git bisect bad    # This commit broke it → search earlier half
 
# After ~8 iterations (log base 2 of 300), Git shows the culprit
 
git bisect reset   # Return to HEAD when done
 
# Fully automated: provide a test script
git bisect run npm test   # Passes = good, fails = bad

Stash: Save Work Without Committing

# Save current changes
git stash
git stash push -m "WIP: payment integration"
git stash push --include-untracked     # Include new files
 
# List stashes
git stash list
# stash@{0}: WIP: payment integration
# stash@{1}: On main: UI experiment
 
# Apply stash
git stash pop              # Apply and remove stash@{0}
git stash apply stash@{1}  # Apply without removing
git stash drop stash@{0}   # Delete specific stash
git stash branch feat/from-stash stash@{0}  # Create branch from stash

Git Hooks with Husky

# Install Husky and lint-staged
npm install -D husky lint-staged
npx husky init
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md,yml}": ["prettier --write"]
  }
}
#!/bin/sh
# .husky/pre-commit
npx lint-staged
#!/bin/sh
# .husky/commit-msg
# Enforce Conventional Commits format
commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
 
if ! echo "$commit_msg" | grep -qE "$pattern"; then
  echo "Commit message must follow Conventional Commits:"
  echo "  type(scope): description"
  echo "  Example: feat(auth): add JWT refresh rotation"
  exit 1
fi

Conventional Commits

# Format: type(scope): description
feat(auth): add Google OAuth login
fix(api): handle null user in getProfile
docs(readme): update local dev setup
refactor(db): extract repository pattern
test(auth): add unit tests for JWT refresh
perf(search): add vector index for AI search
ci(actions): add staging deploy workflow
chore(deps): bump Next.js to 15.3.0

Monorepo with Turborepo

{
  "name": "my-monorepo",
  "workspaces": ["apps/*", "packages/*"]
}
{
  "$schema": "https://turborepo.com/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": { "outputs": ["coverage/**"] },
    "lint": {}
  }
}
# Run commands across all packages with caching
turbo build                     # Build all (uses cache for unchanged)
turbo test                      # Test all in parallel
turbo lint --filter=web         # Only the web app
turbo build --filter=...api     # api + all its dependencies

Common Mistakes

  • Rebasing shared branches — never rebase commits that have been pushed and pulled by teammates; it rewrites history they depend on
  • Giant commits — each commit should represent one logical change; giant commits make git bisect and git blame useless
  • No .gitignore — always ignore node_modules, .env, build artifacts, and OS files from the start
  • Force-pushing to main — this is irreversible and overwrites teammates' work; protect main with branch protection rules
  • Skipping pre-commit hooksgit commit --no-verify defeats the entire purpose of automated quality checks

Best Practices

  • Write commit messages in the imperative mood: "Add auth" not "Added auth" or "Adding auth"
  • Use git log --oneline --graph --all to visualize branch relationships when debugging merge conflicts
  • Set up branch protection rules: require PRs, status checks, and at least one reviewer before merging to main
  • Use git reflog to recover from mistakes — it tracks every HEAD movement for 90 days, even after rebase and reset
  • Run git diff --staged before committing to review exactly what you are about to commit

Key Takeaways

  • Trunk-based development with short-lived feature branches is faster than Git Flow for teams deploying multiple times per day
  • Interactive rebase (git rebase -i HEAD~N) cleans up commit history before merging — squash WIP commits into meaningful ones
  • git bisect uses binary search to find the exact commit that introduced a bug across hundreds of commits in under 10 steps
  • Cherry-pick applies individual commits across branches — the right tool for backporting hotfixes to release branches
  • Husky pre-commit hooks enforce linting and formatting automatically, keeping code quality consistent across the team
  • Conventional Commits (feat:, fix:, chore:) enable automated changelogs and semantic version bumping
  • Turborepo builds only what changed in a monorepo and caches results remotely — speeds up CI by 60-80% on large repos
  • git reflog is the safety net: it records every action including rebase and reset, letting you recover lost commits

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading