Go beyond git add and commit — master rebase, cherry-pick, bisect, reflog, worktrees, and the commands that save you when things go wrong.
Every developer knows git add, git commit, git push. The developers who never lose work and can untangle any merge mess know the commands in this guide.
# Rebase your feature branch onto main
# Replays your commits on top of main instead of creating a merge commit
git checkout feature/my-feature
git rebase main
# Interactive rebase — rewrite last 5 commits
git rebase -i HEAD~5
# In the editor:
# pick a1b2c3d Add login form
# squash e4f5g6h Fix typo in login form ← squash into previous
# reword i7j8k9l Add logout button ← keep but edit message
# drop m1n2o3p WIP: half-baked feature ← remove entirely
# Continue after resolving conflicts
git rebase --continue
# Abort and go back to before rebase started
git rebase --abort
Golden rule: Never rebase commits that have been pushed to a shared branch. Rebase rewrites history — it breaks everyone else's clone.
# Apply one specific commit from another branch
git cherry-pick a1b2c3d
# Cherry-pick a range of commits
git cherry-pick a1b2c3d..e4f5g6h
# Cherry-pick without committing (stage the changes only)
git cherry-pick --no-commit a1b2c3d
# Useful scenario: hotfix on main, backport to release branch
git checkout release/v2.1
git cherry-pick a1b2c3d # the hotfix commit from main
# Stash uncommitted changes
git stash
git stash push -m "WIP: half-done auth refactor"
# Stash including untracked files
git stash push --include-untracked
# List stashes
git stash list
# stash@{0}: On main: WIP: half-done auth refactor
# stash@{1}: On feature/x: quick experiment
# Apply a specific stash (keeps it in the list)
git stash apply stash@{1}
# Pop the latest stash (apply and remove from list)
git stash pop
# Create a new branch from a stash
git stash branch feature/auth-refactor stash@{0}
# Delete a stash
git stash drop stash@{1}
git stash clear # remove all stashes
Reflog records every place HEAD has pointed. It's how you recover "lost" commits after a bad reset or rebase:
# Show the reflog
git reflog
# a1b2c3d HEAD@{0}: commit: Add payment integration
# e4f5g6h HEAD@{1}: reset: moving to HEAD~1
# f7g8h9i HEAD@{2}: commit: Add cart functionality
# Recover a commit you just reset away
git checkout HEAD@{2} # detached HEAD at that commit
git checkout -b recovery-branch # save it as a branch
# Or just reset back to it
git reset --hard HEAD@{2}
# Reflog expires after 90 days by default
# git reset — rewinds history (local only)
git reset --soft HEAD~1 # undo commit, keep changes staged
git reset --mixed HEAD~1 # undo commit, keep changes unstaged (default)
git reset --hard HEAD~1 # undo commit, DELETE changes (dangerous!)
# git revert — creates a new commit that undoes a previous one
# Safe for shared/public branches
git revert a1b2c3d # creates "Revert: ..." commit
git revert HEAD~3..HEAD # revert last 3 commits
git revert --no-commit HEAD~3..HEAD # stage reverts without committing
# Binary search through commits to find when a bug was introduced
git bisect start
git bisect bad # current commit is broken
git bisect good v2.0 # this tag/commit was fine
# Git checks out a middle commit. Test it, then mark it:
git bisect good # this commit is fine
git bisect bad # this commit is broken
# Repeat until Git identifies the first bad commit.
# Then clean up:
git bisect reset
# Automate with a test script
git bisect run npm test # git marks good/bad automatically
# Check out a branch in a separate directory — without switching branches
git worktree add ../hotfix-123 hotfix/critical-bug
# Now you have:
# /projects/myapp/ ← main branch
# /projects/hotfix-123/ ← hotfix branch (separate folder)
# List worktrees
git worktree list
# Remove when done
git worktree remove ../hotfix-123
# Beautiful log graph
git log --oneline --graph --all --decorate
# Find which commit introduced a string
git log -S "function calculateTax" --source --all
# Show all files changed in a commit
git show --stat a1b2c3d
# Find which branch contains a commit
git branch --contains a1b2c3d
# Undo last commit but keep all file changes staged
git reset --soft HEAD~1
# Remove a file from the last commit
git reset HEAD~ path/to/file
git commit --amend --no-edit
# Delete all local branches that have been merged into main
git branch --merged main | grep -v "main" | xargs git branch -d
# Show who changed each line of a file
git blame -L 10,25 src/api/users.ts
# Clean untracked files (dry run first!)
git clean -n # preview
git clean -fd # delete untracked files and directories
[alias]
st = status -sb
lg = log --oneline --graph --all --decorate
undo = reset --soft HEAD~1
unstage = reset HEAD --
last = log -1 HEAD --stat
aliases = config --get-regexp alias
pushf = push --force-with-lease # safer than --force