Skip to content
Published on

Git Mastery Guide — From Basics to rebase, cherry-pick, bisect, and worktree

Authors

Introduction

Git is the tool developers use every single day, yet most never move beyond the add-commit-push loop. Merge conflicts cause panic, rebase feels scary, and many have never even heard of bisect or worktree.

This guide starts from Git internals and covers everything you truly need in practice: advanced commands, branching strategies, PR review culture, monorepo management, and crisis recovery -- all in one comprehensive reference.


1. Understanding Git Internals

To use Git properly, you need to understand its internal structure. At its core, Git is a content-addressable filesystem -- essentially a key-value store.

1.1 Four Core Objects

All data in Git is stored as four object types.

ObjectRoleDescription
blobFile contentA snapshot of file contents (no filename)
treeDirectoryA list referencing blobs and other trees
commitSnapshottree + parent commit + metadata
tagLabelAn annotated tag pointing to a specific commit

Each object is identified by its SHA-1 hash. Identical content always produces the same hash, so Git naturally deduplicates data.

# Check object type
git cat-file -t HEAD

# View commit object contents
git cat-file -p HEAD

# Explore the tree object
git ls-tree HEAD

1.2 Three Areas

Git operates across three core areas.

  • Working Directory -- Where your actual files live. This is where you edit code.
  • Staging Area (Index) -- A preparation area for changes to include in the next commit.
  • Repository (.git) -- Where commit history and all objects are permanently stored.
# File movement flow between the three areas
# Working -> Staging
git add file.txt

# Staging -> Repository
git commit -m "feat: add file"

# Repository -> Working (restore a specific file)
git checkout HEAD -- file.txt

1.3 HEAD and refs

  • HEAD -- A pointer to the currently checked-out commit. Usually points to a branch.
  • branch -- A movable pointer to a specific commit.
  • tag -- A fixed pointer to a specific commit.
# Check where HEAD points
cat .git/HEAD
# ref: refs/heads/main

# Check which commit a branch points to
cat .git/refs/heads/main

# Enter detached HEAD state
git checkout HEAD~2

2. Essential Commands Cheat Sheet

Here are everyday commands organized by scenario.

2.1 Basic Workflow

# Initialize / clone a repository
git init
git clone https://github.com/user/repo.git

# Check changes
git status
git diff                    # unstaged changes
git diff --staged           # staged changes
git diff HEAD               # all changes

# Stage & commit
git add -p                  # interactive partial staging
git commit -m "feat: login" # commit
git commit --amend          # amend last commit

# Remote sync
git fetch origin            # fetch remote changes only
git pull --rebase origin main  # fetch + rebase
git push origin feature/login  # push

2.2 Branch Management

# Create & switch branches
git branch feature/auth
git checkout -b feature/auth  # create + switch
git switch -c feature/auth    # recommended (Git 2.23+)

# List branches
git branch -a               # local + remote
git branch --merged          # fully merged branches
git branch -d feature/old    # safe delete
git branch -D feature/old    # force delete

# Clean up remote branches
git remote prune origin
git fetch -p

2.3 stash -- Temporarily Save Work

Use stash when you need to urgently switch branches mid-work.

# Save current changes
git stash
git stash push -m "WIP: login form"

# List stashes
git stash list

# Restore
git stash pop               # pop and remove
git stash apply stash@{0}  # apply but keep

# Stash specific files only
git stash push -m "partial" -- src/auth.ts

# Convert a stash to a branch
git stash branch new-branch stash@{0}

3. Branching Strategy Comparison

3.1 Git Flow

The most traditional branching model, proposed by Vincent Driessen in 2010.

Structure:

  • main -- Production code
  • develop -- Next release development
  • feature/* -- Feature development
  • release/* -- Release preparation
  • hotfix/* -- Emergency bug fixes

Best for: Projects with long release cycles (1-2 per month), or when maintaining multiple versions simultaneously.

Drawbacks: Too many branches make it complex, and it does not fit CI/CD workflows well.

3.2 GitHub Flow

The simple model that GitHub actually uses.

Structure:

  • main -- Always deployable
  • feature/* -- Branch from main, merge via PR

Best for: Continuous deployment environments, small teams, web services.

Rule: main must always be deployable. Work on feature branches and merge through PRs.

3.3 Trunk-Based Development

The strategy used by large tech companies like Google and Meta.

Structure:

  • main (trunk) -- All developers commit directly or use short-lived branches
  • Branch lifetime: 1-2 days maximum

Best for: Teams with feature flag infrastructure, large organizations, fast feedback loops.

3.4 Comparison Summary

AspectGit FlowGitHub FlowTrunk-Based
ComplexityHighLowMedium
Deploy frequency1-2x/monthDailyMultiple/day
Team sizeLargeSmall-MediumAny size
Feature flags neededNoNoYes
CI/CD compatibilityFairGoodExcellent

4. Merge vs Rebase

One of the most debated topics in Git.

4.1 Merge -- Preserve History

# Merge feature branch into main
git checkout main
git merge feature/auth

Merge performs a 3-way merge by finding the common ancestor of both branches and creates a merge commit. History is preserved exactly as it happened, so you can trace "when and on which branch work was done."

4.2 Rebase -- Clean History

# Rebase feature branch onto latest main
git checkout feature/auth
git rebase main

Rebase replays the feature branch commits one by one on top of main. The result is a linear history.

4.3 Interactive Rebase -- The Key to Commit Cleanup

# Clean up the last 5 commits
git rebase -i HEAD~5

Available commands in the interactive rebase editor:

  • pick -- Keep the commit
  • reword -- Edit the commit message
  • edit -- Modify the commit content
  • squash -- Combine with previous commit (merge messages)
  • fixup -- Combine with previous commit (discard message)
  • drop -- Delete the commit
# Practical example: clean up before PR
# In the editor, modify like this:
# pick abc1234 feat: add login page
# squash def5678 fix: typo in login
# squash ghi9012 style: adjust padding
# -> 3 commits become 1

4.4 When to Use Which

ScenarioRecommendation
PR mergeSquash Merge or Rebase Merge
Local commit cleanupInteractive Rebase
Shared branchesMerge (never rebase)
Personal feature branchRebase to sync with main

The Golden Rule: Never rebase commits that have already been pushed. Others may be referencing them.


5. Mastering Advanced Commands

5.1 cherry-pick -- Copy Specific Commits

Copy a single commit from another branch to your current branch.

# Pick a specific commit
git cherry-pick abc1234

# Pick multiple commits at once
git cherry-pick abc1234 def5678

# Pick a range (abc excluded, def included)
git cherry-pick abc1234..def5678

# Apply changes without committing
git cherry-pick --no-commit abc1234

Use cases:

  • Apply a hotfix bug fix to develop as well
  • Move a commit from the wrong branch to the correct one
  • Selectively include specific features in a release branch

5.2 bisect -- Binary Search for Bugs

Find the exact commit that introduced a bug using binary search across hundreds of commits.

# Start bisect
git bisect start

# Mark current (buggy) as bad
git bisect bad

# Mark a known good commit
git bisect good v2.0.0

# Git checks out a midpoint commit -> test -> mark
git bisect good  # or git bisect bad

# Once found, reset
git bisect reset

You can also automate the process.

# Automated bisect with a test script
git bisect start HEAD v2.0.0
git bisect run npm test

Git will automatically run tests on each commit and pinpoint the first failing one.

5.3 worktree -- Work on Multiple Branches Simultaneously

Create multiple working directories from a single repository to work on different branches at the same time.

# Create a new worktree
git worktree add ../project-hotfix hotfix/critical-bug

# List worktrees
git worktree list

# Remove a worktree
git worktree remove ../project-hotfix

# Create a new branch while adding a worktree
git worktree add -b feature/new-ui ../project-ui

Use cases:

  • Review code on main while continuing development on feature
  • Work on an urgent hotfix and current feature simultaneously
  • Start new work while waiting for a CI build

5.4 reflog -- The Last Resort for Mistake Recovery

reflog records everywhere HEAD has pointed. Even if you lose commits by accident, reflog can bring them back.

# View reflog
git reflog

# Recover after accidental reset --hard
git reflog
# Find: HEAD@{2}: commit: important feature
git reset --hard HEAD@{2}

# Recover a deleted branch
git reflog
git checkout -b recovered-branch HEAD@{5}

# View records within a time range
git reflog --since="2 days ago"

Note: reflog exists only locally and expires after 90 days by default.


6. Optimizing .gitconfig

Here are Git settings that boost productivity.

6.1 Useful Aliases

[alias]
    # Status & log
    st = status -sb
    lg = log --oneline --graph --decorate --all
    last = log -1 HEAD --stat

    # Branch
    co = checkout
    sw = switch
    br = branch -vv
    brd = branch -d

    # Commit
    cm = commit -m
    ca = commit --amend --no-edit
    undo = reset HEAD~1 --mixed

    # Diff
    df = diff --stat
    dfc = diff --cached

    # Stash
    sl = stash list
    sp = stash pop
    ss = stash push -m

    # Cleanup
    cleanup = "!git branch --merged | grep -v '\\*\\|main\\|develop' | xargs -n 1 git branch -d"

6.2 Core Settings

[core]
    editor = code --wait
    autocrlf = input          # macOS/Linux
    ignorecase = false
    pager = delta              # use delta pager

[pull]
    rebase = true              # always rebase on pull

[push]
    default = current          # push only current branch
    autoSetupRemote = true     # auto set upstream on push

[init]
    defaultBranch = main

[diff]
    tool = vscode
    colorMoved = default

[merge]
    conflictstyle = diff3      # 3-way conflict display
    tool = vscode

[rerere]
    enabled = true             # remember conflict resolutions

6.3 delta -- A Better Diff Tool

delta transforms Git diff output into a readable, beautifully formatted display.

# Install
brew install git-delta

# Add to .gitconfig
[core]
    pager = delta

[interactive]
    diffFilter = delta --color-only

[delta]
    navigate = true
    line-numbers = true
    side-by-side = true

7. GitHub PR Review Best Practices

7.1 What Makes a Good PR

  1. Size: Keep it under 200-400 lines. Split if larger.
  2. Single purpose: One PR should address one change.
  3. Description: Explain why the change is needed and how it was tested.
  4. Self-review: Review your own PR before submitting.

7.2 PR Template

## Changes
- What changed and why

## Testing
- [ ] Added/updated unit tests
- [ ] Verified locally
- [ ] Edge cases validated

## Screenshots (for UI changes)

## Related Issues
- closes #123

7.3 Review Etiquette

As a reviewer:

  • Understand the "intent" of the code first. Focus on logic over style.
  • Frame feedback as questions: "Why was this approach chosen?" works better than "This is wrong."
  • Distinguish comment types: nit, suggestion, question, blocker.
  • Use Approve, Request Changes, and Comment deliberately.

As an author:

  • Respect the reviewer's time. Keep PRs small.
  • Respond to every comment (at minimum with a reaction).
  • Notify reviewers after a force push.

7.4 CODEOWNERS

# .github/CODEOWNERS

# Entire codebase
* @team-lead

# Frontend
/src/components/ @frontend-team
/src/pages/ @frontend-team

# Backend API
/src/api/ @backend-team

# Infrastructure
/terraform/ @devops-team
/k8s/ @devops-team

# Security-sensitive files
/src/auth/ @security-team @team-lead

8. Commit Message Conventions

8.1 Conventional Commits

type(scope): description

body (optional)

footer (optional)

type reference:

typeDescription
featNew feature
fixBug fix
docsDocumentation changes
styleCode formatting (no functional change)
refactorCode refactoring
perfPerformance improvement
testAdd/update tests
choreBuild/config changes
ciCI configuration changes
# Examples
git commit -m "feat(auth): add Google OAuth login"
git commit -m "fix(api): handle null response from payment gateway"
git commit -m "docs(readme): update installation instructions"

# Breaking Change
git commit -m "feat(api)!: change response format to JSON:API"

8.2 gitmoji

Visually distinguish commit types using emojis.

git commit -m ":sparkles: add user profile page"
git commit -m ":bug: fix login redirect loop"
git commit -m ":recycle: refactor database connection pool"
git commit -m ":white_check_mark: add unit tests for auth module"

8.3 Enforcing with commitlint

# Install
npm install -D @commitlint/cli @commitlint/config-conventional

# commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style',
      'refactor', 'perf', 'test', 'chore', 'ci'
    ]],
    'subject-max-length': [2, 'always', 72],
  },
};

9. Monorepo Management

9.1 git sparse-checkout

Check out only the directories you need from a large monorepo.

# Enable sparse-checkout
git sparse-checkout init --cone

# Specify only needed directories
git sparse-checkout set packages/frontend packages/shared

# Add a directory
git sparse-checkout add packages/backend

# Check current configuration
git sparse-checkout list

# Restore full checkout
git sparse-checkout disable

9.2 git subtree

Integrate and manage an external repository as a subdirectory.

# Add an external repository
git subtree add --prefix=libs/shared-utils \
  https://github.com/org/shared-utils.git main --squash

# Pull changes
git subtree pull --prefix=libs/shared-utils \
  https://github.com/org/shared-utils.git main --squash

# Push changes back
git subtree push --prefix=libs/shared-utils \
  https://github.com/org/shared-utils.git main

9.3 Integration with Nx / Turborepo

Combining monorepo build tools with Git lets you build and test only the changed packages.

# Nx: test only affected projects
npx nx affected --target=test --base=main --head=HEAD

# Turborepo: build only changed packages
npx turbo run build --filter=...[HEAD~1]

# In GitHub Actions: detect changes
# Compare against main in CI to process only changed packages

10. Crisis Recovery Playbook

10.1 Recovering from a Force Push

When someone accidentally force-pushes over shared history.

# 1. Find the original commit in reflog
git reflog show origin/main

# 2. Restore to the original commit
git push origin HEAD@{1}:main --force

# 3. Notify the team
# "main branch has been restored. Please run git pull --rebase."

Prevention:

# Block force push on main (GitHub Settings)
# Settings -> Branches -> Branch protection rules
# Enable "Restrict force pushes"

10.2 Removing Sensitive Data (BFG Repo-Cleaner)

When passwords, API keys, or other secrets are accidentally committed.

# Install BFG
brew install bfg

# Completely remove a file from all history
bfg --delete-files secrets.env

# Replace specific text patterns
bfg --replace-text passwords.txt

# Clean up
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# Force push
git push --force

Important notes:

  • Always back up your repository before running BFG.
  • Team members who have cloned the repo will need a fresh clone.
  • You may need to request cache clearing from GitHub.

10.3 Managing Large Files (Git LFS)

Use Git LFS for binary files, media assets, and other large files.

# Install & initialize Git LFS
brew install git-lfs
git lfs install

# Specify file patterns to track
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/videos/*"

# .gitattributes is auto-generated -- commit it
git add .gitattributes
git commit -m "chore: configure Git LFS tracking"

# List LFS-tracked files
git lfs ls-files

# Migrate already-committed large files to LFS
git lfs migrate import --include="*.psd" --everything

10.4 Reverting a Bad Merge

# Revert the merge commit itself
git revert -m 1 MERGE_COMMIT_SHA

# -m 1: revert relative to the first parent (main)
# -m 2: revert relative to the second parent (feature)

10.5 The Absolute Last Resort

# 1. Check reflog (available for 90 days)
git reflog

# 2. Recover from dangling objects
git fsck --lost-found

# 3. Worst case: recover from a teammate's local repo
# Ask them to push, or receive a bundle
git bundle create backup.bundle --all

Conclusion

Git is not merely a version control tool. It defines how teams collaborate, guarantees code quality, and forms the backbone of deployment pipelines -- it is core infrastructure.

Here is a summary of what this guide covered.

  1. Understanding internals makes commands intuitive.
  2. Choose a branching strategy that fits your team -- there is no one right answer.
  3. Use rebase locally, merge on shared branches.
  4. cherry-pick, bisect, worktree, and reflog shine in real-world scenarios.
  5. PR review culture determines code quality.
  6. Conventional Commits keep commit history clean and navigable.
  7. In a crisis, reflog and BFG are your lifelines.

Mastering Git does not happen overnight. But if you try one new command each day and gradually build your understanding of the internals, you will eventually become the kind of developer who stays calm and effective in any Git situation.