Cheat SheetsGitAdvanced

Advanced — Cheat Sheet

Git · 6 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Advanced
Git6 topicsQuick revision reference
1

Interactive Rebase — Rewrite History Like a Pro

git rebase -i lets you reorder, squash, edit, split, or drop commits before merging. It is the tool for turning a messy development history into clean, reviewable commits.

  • git rebase -i HEAD~N opens the last N commits for editing — reorder, squash, fixup, drop, reword
  • fixup silently discards the commit message and merges changes into the previous commit
  • squash merges changes AND combines both commit messages (prompts you to edit the result)
  • Use edit to split one commit into multiple: reset HEAD~1, then stage/commit in pieces, rebase --continue
  • Never interactive rebase commits already pushed to a shared remote branch
Interactive rebase commands
# Open interactive rebase for last 5 commits
git rebase -i HEAD~5

# The editor opens with:
# pick a3f8c12 Add user registration
# pick 9d2e441 WIP login
# pick 7b2e441 fix typo
# pick 3d9f2e0 fix login properly
# pick 2c1a8b7 add tests for login
#
# Commands:
# pick   = use commit as-is
# reword = use commit but edit message
# edit   = stop to amend the commit
# squash = meld into previous commit (keep both messages)
# fixup  = meld into previous commit (discard this message) ← most common
# drop   = remove the commit entirely
# reorder= just rearrange the lines

# Modified:
# pick a3f8c12 Add user registration
# reword 9d2e441 WIP login         ← fix the message
# fixup 7b2e441 fix typo           ← squash into previous
# squash 3d9f2e0 fix login properly ← squash, keep message
# pick 2c1a8b7 add tests for login
2

Cherry-pick — Apply Specific Commits

git cherry-pick applies the changes from one or more specific commits onto your current branch, creating new commits with the same diff but different hashes.

  • cherry-pick applies the diff of a specific commit onto your current branch as a new commit
  • Most common use: backporting a hotfix from main to a release/maintenance branch
  • Cherry-pick creates a new commit with a new hash — the original commit is unchanged
  • git cherry-pick --no-commit stages the changes without committing — useful for combining multiple cherry-picks
  • Overusing cherry-pick leads to duplicate commits and confusing history — prefer merge/rebase when possible
Cherry-picking for backports
//  Scenario: hotfix on main, need to backport to release branch
//
//  main:         A ── B ── C ── fix ── D
//  release/v2:   A ── B ── C ──────────────
//
//  We want "fix" on release/v2 WITHOUT D

# Find the commit hash of the fix
git log --oneline main
# f2a1c9b hotfix: prevent null pointer in checkout
# 3d9f2e0 Add new feature X
# a3f8c12 Add feature Y

# Switch to release branch and cherry-pick
git switch release/v2
git cherry-pick f2a1c9b

//  After:
//  main:         A ── B ── C ── fix  ── D
//  release/v2:   A ── B ── C ── fix'
//  (fix' has the same changes as fix but a new hash)

# Cherry-pick a range of commits (oldest..newest)
git cherry-pick a3f8c12..f2a1c9b

# Cherry-pick without committing (stage only)
git cherry-pick --no-commit f2a1c9b
3

Reflog — Recovering Lost Commits

The reflog is Git's safety net — it records every change to HEAD and branch pointers for 90 days. You can recover commits lost to reset --hard, dropped rebases, or accidental branch deletions.

  • git reflog records every HEAD movement for 90 days — it is Git's safety net
  • HEAD@{n} syntax references how many HEAD positions ago (HEAD@{0} = current, HEAD@{1} = previous)
  • Use git reflog to find the hash of a "lost" commit, then git reset --hard <hash> to recover
  • git fsck --lost-found finds dangling objects not reachable from any ref
  • Reflog is per-repository and local — it is NOT shared or pushed to remotes
Reading the reflog
git reflog
# a3f8c12 HEAD@{0}: commit: Add payment gateway
# 9d2e441 HEAD@{1}: rebase -i (finish): returning to refs/heads/main
# 7b2e441 HEAD@{2}: rebase -i (squash): Implement payment
# 3d9f2e0 HEAD@{3}: rebase -i (pick): Add auth
# 2c1a8b7 HEAD@{4}: reset: moving to HEAD~3
# f1a2b3c HEAD@{5}: commit: Messy WIP commit  ← "lost" commit
# e9d8c7b HEAD@{6}: checkout: moving from feature to main

# Reflog for a specific branch
git reflog show feature/payment

# Reflog shows both hash AND the operation that moved HEAD
# This is how you find a "lost" commit hash
4

Git Hooks — Automate with Every Git Action

Git hooks are shell scripts that run automatically at key points in the Git workflow — before commits, before pushes, after merges. They enforce code quality, run tests, and prevent bad commits from ever entering the repository.

  • Hooks live in .git/hooks/ — any executable script (bash, node, python) triggered by Git events
  • pre-commit: runs before commit — ideal for linting staged files; non-zero exit rejects the commit
  • commit-msg: validates commit message format — receives the message file as $1
  • pre-push: runs before pushing — use for full test suites (too slow for pre-commit)
  • Use Husky (Node) or pre-commit framework (Python) to version and share hooks with your team
pre-commit hook — run ESLint
# Hooks live in .git/hooks/
ls .git/hooks/
# pre-commit.sample   commit-msg.sample   pre-push.sample

# Make a hook executable by removing .sample and adding a shebang
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
# Run ESLint on staged JS files before every commit
staged=$(git diff --cached --name-only --diff-filter=ACM | grep '.js$')
if [ -n "$staged" ]; then
  echo "$staged" | xargs npx eslint
  if [ $? -ne 0 ]; then
    echo "ESLint failed — commit rejected"
    exit 1   # Non-zero exit rejects the commit
  fi
fi
EOF
chmod +x .git/hooks/pre-commit
5

Submodules & Worktrees — Advanced Repo Management

Submodules embed one Git repository inside another at a pinned commit — for shared libraries or dependencies. Worktrees let you check out multiple branches simultaneously in separate directories from one repository.

  • A submodule is a pointer (commit hash) to another repo — git clone --recurse-submodules fetches both
  • Updating a submodule requires committing the updated pointer in the parent repo
  • Worktrees allow multiple branches checked out simultaneously in separate directories
  • All worktrees share one .git/ directory — no duplication of the object store
  • Prefer worktrees over stash when you need to switch context for more than a few minutes
Git submodules — pinned dependencies
# Add a submodule
git submodule add https://github.com/org/shared-ui.git libs/shared-ui

# .gitmodules is created/updated:
# [submodule "libs/shared-ui"]
#   path = libs/shared-ui
#   url = https://github.com/org/shared-ui.git

# Clone a repo WITH submodules (two ways)
git clone --recurse-submodules https://github.com/org/project.git
# Or in two steps:
git clone https://github.com/org/project.git
git submodule update --init --recursive

# Update submodule to latest remote (then commit the pointer)
cd libs/shared-ui
git fetch && git checkout main && git pull
cd ../..
git add libs/shared-ui
git commit -m "chore: update shared-ui to latest main"

# Check status of all submodules
git submodule status
6

Git Internals Deep Dive — Objects, Packfiles, GC

Under the hood: how Git stores objects in loose and packed format, how the index works, how garbage collection reclaims space, and how remote protocols transfer data efficiently.

  • Loose objects: one zlib-compressed file per object. Packfiles: many objects in one binary, with delta compression
  • git gc packs loose objects into packfiles — reduces disk usage and speeds up network transfer
  • .git/index is a binary cache mapping file paths to blob hashes — git status reads this, not the disk
  • git push only sends objects the remote doesn't have — negotiated before transfer begins
  • git filter-repo (not git filter-branch) is the modern tool to permanently remove large files from history
Loose objects and packfiles
# Loose objects live here (one file per object)
ls .git/objects/
# 4a/ 9c/ f2/ pack/ info/
ls .git/objects/4a/
# 7f3b2c...  (rest of SHA-1 hash)

# Pack loose objects manually
git gc
git gc --aggressive  # deeper compression, slower

# Inspect a packfile
ls .git/objects/pack/
# pack-abc123.idx   ← index for fast lookup
# pack-abc123.pack  ← compressed objects

git verify-pack -v .git/objects/pack/pack-abc123.idx
# Outputs: hash, type, size, compressed-size, offset
# Helps find large objects bloating the repo

# Git auto-gc triggers around 6700 loose objects
# Manual: git count-objects -v
# count: 150
# size: 600    (KB, loose objects)
# in-pack: 45000 (objects in packfiles)
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/git