Git: Everyday Commands and Advanced Tools
A practical reference for the git commands you reach for most — diff, reset, fetch, log, stash — plus advanced tools like cherry-pick and worktree that solve specific problems cleanly.
git diff
git diff shows what has changed but not yet been staged. git diff --cached (or --staged) shows what is staged and ready to commit. git diff HEAD shows everything changed since the last commit, staged or not.
git diff # unstaged changes
git diff --cached # staged changes
git diff HEAD # all changes since last commit
git diff HEAD~2 HEAD # changes between two commits
git diff main feature/foo # diff between two branches
Adding --stat gives a summary of which files changed and how many lines, without showing the full diff content. Useful for a quick overview before reviewing.
git diff --stat
git diff --stat HEAD~1 HEAD
git log
git log is the history viewer. The default output is verbose. Most of the time you want a condensed view.
git log --oneline # one line per commit
git log --oneline -10 # last 10 commits
git log --oneline --graph # ASCII branch graph
git log --oneline --graph --all # all branches in the graph
Filtering by author, date, or file:
git log --author="Brian"
git log --since="2024-01-01" --until="2024-06-01"
git log -- src/components/PostCard.astro # commits that touched a specific file
Searching commit messages:
git log --grep="fix" # commits whose message contains "fix"
git reset
git reset moves the current branch pointer and optionally changes the index and working tree. The three modes differ in how much they touch:
| Mode | Branch pointer | Index (staging) | Working tree |
|---|---|---|---|
--soft | Moves | Unchanged | Unchanged |
--mixed (default) | Moves | Cleared | Unchanged |
--hard | Moves | Cleared | Cleared |
git reset HEAD~1 # undo last commit, keep changes staged (--mixed)
git reset --soft HEAD~1 # undo last commit, keep changes staged
git reset --hard HEAD~1 # undo last commit, discard all changes
git reset HEAD src/foo.ts # unstage a specific file
--hard is destructive. The working tree changes are gone. Use with care.
git stash
Stash saves your current working tree and index state so you can switch branches or pull without committing half-finished work.
git stash # stash everything (tracked files)
git stash -u # include untracked files
git stash push -m "wip: refactor header" # named stash
git stash list # see all stashes
git stash pop # apply most recent stash and drop it
git stash apply stash@{2} # apply a specific stash without dropping it
git stash drop stash@{0} # delete a specific stash
git stash clear # delete all stashes
pop = apply + drop. Prefer apply when you want to keep the stash around in case the apply goes wrong.
git fetch
git fetch downloads changes from the remote without merging them into your working branch. It updates origin/* tracking refs so you can inspect what changed before integrating.
git fetch # fetch all remotes
git fetch origin # fetch a specific remote
git fetch origin main # fetch a specific branch
fetch —prune
--prune removes remote-tracking refs that no longer exist on the remote. If a branch was deleted on GitHub, origin/that-branch will linger locally until you prune.
git fetch --prune
git fetch --prune --all # prune from all remotes
You can set this to happen automatically on every fetch:
git config --global fetch.prune true
git cherry-pick
Cherry-pick applies the changes introduced by one or more commits onto the current branch, creating new commits with the same diff but different SHAs.
git cherry-pick <sha> # apply a single commit
git cherry-pick <sha1> <sha2> # apply multiple commits
git cherry-pick <sha1>..<sha2> # apply a range (exclusive of sha1)
git cherry-pick <sha1>^..<sha2> # apply a range (inclusive of sha1)
Common use cases:
- A hotfix was committed to
mainand needs to go toreleasetoo - A useful commit from a feature branch that got abandoned
- Moving a commit that was made on the wrong branch
Conflicts during cherry-pick
If there’s a conflict, git pauses and lets you resolve it:
# resolve conflicts in your editor, then:
git add <resolved-files>
git cherry-pick --continue
# or abort entirely:
git cherry-pick --abort
-n / --no-commit
Apply the changes to your working tree and index without immediately committing. Useful when you want to squash multiple cherry-picks into one commit.
git cherry-pick -n <sha1> <sha2>
# make any additional edits
git commit -m "apply fixes from main"
git worktree
A worktree lets you check out a different branch in a separate directory, while your main working directory stays on its current branch. Both are backed by the same .git directory, no cloning needed.
Added in Git 2.5 (2015), but became reliable and widely used around 2024–2025.
git worktree add ../project-hotfix hotfix/login-bug
# now ../project-hotfix has the hotfix branch checked out
# your main directory is still on main/master
You can work in both directories simultaneously. Build tools, editors, and servers can run in each independently.
Listing and removing worktrees
git worktree list # see all active worktrees
git worktree remove ../project-hotfix # remove when done
git worktree prune # clean up stale worktree metadata
When worktree is useful
- Reviewing a PR while keeping your in-progress work untouched
- Running a long build or test suite on one branch while working on another
- Applying a hotfix to
productionwithout stashing or committing WIP onmain
Each worktree is its own directory. You can open it in a separate editor window, run a dev server on a different port, and close it when done.
git rebase
Rebase replays commits from one branch on top of another, rewriting history. The result is a linear history without merge commits.
git rebase main # rebase current branch onto main
git rebase --interactive HEAD~3 # interactively edit the last 3 commits
Interactive rebase (-i) lets you squash, reorder, reword, or drop commits before they’re shared. Only use rebase on commits that haven’t been pushed to a shared branch.
git reflog
The reflog is a local log of every position HEAD has been at. It’s your safety net for recovering commits that were lost to a reset, rebase, or accidental branch deletion.
git reflog # show HEAD history
git reflog show main # show history for a specific branch
If you ran git reset --hard and want the commit back:
git reflog # find the SHA before the reset
git checkout -b recovery <sha> # recover it into a new branch
Reflog entries expire after 90 days by default.
git bisect
Bisect performs a binary search through commit history to find which commit introduced a bug. You tell it a good commit and a bad commit; it checks out the midpoint and asks you to test.
git bisect start
git bisect bad # current commit is broken
git bisect good v1.4.0 # this tag was working
# git checks out a commit halfway between
# test your code, then:
git bisect good # or: git bisect bad
# repeat until git identifies the first bad commit
git bisect reset # return to original HEAD when done
Quick Reference
| Command | What it does |
|---|---|
git diff --cached | Show staged changes |
git log --oneline --graph --all | Visual branch history |
git reset --soft HEAD~1 | Undo last commit, keep changes staged |
git reset --hard HEAD~1 | Undo last commit, discard changes |
git stash push -m "name" | Save WIP with a label |
git fetch --prune | Fetch and remove stale remote refs |
git cherry-pick <sha> | Apply a specific commit to current branch |
git worktree add <path> <branch> | Check out a branch in a new directory |
git reflog | Recover lost commits |
git bisect start | Binary search for a buggy commit |