Beyond git add and git commit
Most developers use about 10% of Git's capabilities. The remaining 90% — when you need it — makes the difference between spending 2 minutes or 2 hours solving a problem. This guide covers the Git commands that transform how you work.
Interactive Rebase: Rewrite History
# Clean up last 5 commits before pushing
git rebase -i HEAD~5
# Opens editor with:
pick a1b2c3d Fix: handle null user
pick b2c3d4e WIP: add validation
pick c3d4e5f Add more validation
pick d4e5f6g Fix typo in validation
pick e5f6g7h Add tests for validation
# Change 'pick' to:
# r/reword — change commit message
# s/squash — merge into previous commit (keeps both messages)
# f/fixup — merge into previous (discard this commit's message)
# d/drop — remove commit entirely
# e/edit — pause and let you amend the commit
# Result: clean history that tells a story
pick a1b2c3d Fix: handle null user
reword c3d4e5f Add user input validation
squash d4e5f6g fix typo
fixup b2c3d4e WIP cleanup
pick e5f6g7h Add tests for validation
# Rebase feature branch onto updated main
git checkout feature/my-feature
git rebase main
# If conflicts occur:
git rebase --continue # After resolving
git rebase --abort # Start over
# Rebase with autosquash (squash! prefix in commit messages)
git commit --fixup HEAD~2 # Creates "fixup! <original message>"
git rebase -i --autosquash HEAD~5 # Auto-merges fixup commits
Cherry-pick: Apply Specific Commits
# Apply a single commit from another branch to current branch
git cherry-pick a1b2c3d
# Cherry-pick range of commits
git cherry-pick a1b2c3d..e5f6g7h # Exclusive of first
git cherry-pick a1b2c3d^..e5f6g7h # Inclusive of first
# Cherry-pick without committing (review first)
git cherry-pick --no-commit a1b2c3d
# Cherry-pick and edit the commit message
git cherry-pick -e a1b2c3d
# Common use case: hot-fix deployed to main and feature branches
git checkout main
git cherry-pick fix-commit-hash
git checkout feature/new-feature
git cherry-pick fix-commit-hash
git bisect: Binary Search for Bugs
When you know "this worked in version X but broke in version Y":
# Start bisect
git bisect start
# Mark current commit as bad (bug exists here)
git bisect bad
# Mark last known good commit
git bisect good v1.2.0 # tag
# or
git bisect good a1b2c3d # commit hash
# Git checks out the middle commit — test it
# If bug exists:
git bisect bad
# If bug doesn't exist:
git bisect good
# Git narrows down until it finds the culprit
# Bisecting: 25 revisions left to test after this (roughly 5 steps)
# [a1b2c3d] Add new feature X
# ...
# [c3d4e5f] is the first bad commit!
# End bisect session
git bisect reset
# Automated bisect (with a test script)
git bisect start HEAD v1.2.0
git bisect run npm test # Automatically runs test on each commit
git stash: Temporary Shelving
# Stash all changes (staged and unstaged)
git stash
# Stash with a descriptive message
git stash push -m "WIP: adding payment validation"
# Include untracked files
git stash push --include-untracked
# List all stashes
git stash list
# stash@{0}: WIP: adding payment validation
# stash@{1}: On main: quick fix attempt
# Apply most recent stash (keep it in stash list)
git stash apply
# Apply and remove from stash list
git stash pop
# Apply specific stash
git stash apply stash@{2}
# Create a branch from a stash
git stash branch feature/payment-from-stash stash@{0}
# Drop a stash
git stash drop stash@{0}
# Clear all stashes
git stash clear
git reflog: Time Machine
The reflog records every HEAD movement — your safety net:
# See all recent HEAD changes
git reflog
# a1b2c3d HEAD@{0}: commit: Add new feature
# b2c3d4e HEAD@{1}: rebase finished: returning to refs/heads/main
# c3d4e5f HEAD@{2}: reset: moving to HEAD~1
# d4e5f6g HEAD@{3}: commit: WIP: new feature
# Recover "lost" commits after accidental reset
git reset --hard HEAD~3 # "I deleted my last 3 commits!"
git reflog # Find the commit hash
git reset --hard HEAD@{3} # Recover!
# Undo a bad rebase
git reflog # Find commit before rebase
git reset --hard HEAD@{before-rebase}
Advanced Diff and Log
# Visual branch graph
git log --oneline --graph --all --decorate
# Changes between branches
git diff main..feature/my-branch
# Show changes in a commit
git show a1b2c3d
# Who changed this line? (blame with context)
git blame -L 42,56 src/auth.ts
git log -S "functionName" --source --all # Find when a string appeared/disappeared
# Changes to a specific file
git log --follow --all -p -- src/deleted-file.ts
# Commits not in main yet
git log main..HEAD --oneline
Powerful Aliases
# Add to ~/.gitconfig
[alias]
# Compact status
st = status -sb
# Pretty log
lg = log --oneline --graph --all --decorate
# Recent branches
recent = for-each-ref --sort=-committerdate refs/heads/ --format='%(committerdate:short) %(refname:short)'
# Undo last commit (keep changes staged)
undo = reset --soft HEAD~1
# Amend last commit without changing message
amend = commit --amend --no-edit
# Clean up merged branches
cleanup = "!git branch --merged main | grep -v main | xargs -n 1 git branch -d"
# What did I do today?
today = log --since=midnight --author="$(git config user.name)" --oneline
Useful Git Workflows
# Scenario: Need to quickly fix a bug while in the middle of a feature
git stash push -m "WIP: feature X" # Save current work
git checkout main
git checkout -b fix/critical-bug
# ... make fix ...
git commit -am "Fix: critical bug"
git checkout main
git merge fix/critical-bug
git checkout feature/x
git stash pop # Restore work
# Scenario: Committed to wrong branch
git log --oneline -3 # Find the commits
git reset --soft HEAD~1 # Uncommit (keep changes)
git stash
git checkout correct-branch
git stash pop
git commit -m "Same message"
# Scenario: Include only some changes from a file
git add -p src/feature.ts # Interactive staging — choose hunks
→ Use the built-in Git Memo for a quick reference of common Git commands.