working tree ──add──▶ index ──commit──▶ repository ──push──▶ remote
▲ │ │ │
└────restore─────────┘ │ │
└───────────────reset──────────────────── ┘ │
└──────────────────────────fetch / pull ────────────────────────┘
Most git confusion is not about syntax. It is about not knowing which
of these four places a command reads from and which one it writes to. Everything
below is grouped by that, so you can find a command by describing where your work
is now and where you want it to end up.
Looking around
Read-only. Nothing here changes any of the four states.
- git status --short --branch
- The compact form. Left column is the index, right column is the working tree, so
MM means you staged a change and then edited the file again.
- git diff
- Working tree against the index — what you have not staged yet.
- git diff --staged
- Index against the last commit — what you are about to commit.
- git log --oneline --graph --decorate
- History with the branch topology drawn in. Add
--all to see branches
you are not currently on.
- git log -p -- path/to/file
- Every commit that touched one file, with the diffs. The bare
--
separates paths from revisions when a file and a branch share a name.
- git show HEAD~3
- One commit in full.
~3 counts back three first parents;
^2 picks the second parent of a merge.
- git blame -L 40,60 file.py
- Who last touched each line in a range.
-w ignores whitespace-only
changes, which usually cuts the noise in half.
Working tree to index
Choosing what goes into the next commit.
- git add -p
- Stage hunk by hunk. Answer
s to split a hunk further and
e to hand-edit one. This is the single habit that most improves commit
quality.
- git add -u
- Stage modifications and deletions of files git already tracks, but no new files.
- git restore --staged file.txt
- Take a file back out of the index and leave your edits alone.
- git restore file.txt
- Throw away working-tree edits and take the file from the index.
This one loses work with no recovery.
- git stash push -m "wip" -- src/
- Park changes, optionally only under some paths.
git stash pop brings
them back and drops the entry; apply keeps it.
- git clean -nd
- List untracked files and directories that
-fd would delete. Run the
dry form first, always.
Index to repository
Committing, and editing commits you have not shared.
- git commit -v
- Puts the staged diff in the editor below the message, so you write the message
while looking at what you actually staged.
- git commit --amend --no-edit
- Fold the staged changes into the previous commit and keep its message. Rewrites
that commit, so only for work you have not pushed.
- git reset --soft HEAD~1
- Undo the last commit and leave everything staged. Useful for splitting one commit
into two.
- git reset HEAD~1
- The default,
--mixed: undo the commit and unstage, but keep the files
as they are.
- git reset --hard HEAD~1
- Undo the commit and wipe the changes. The one command here that genuinely
destroys work.
- git rebase -i HEAD~5
- Reorder, squash, reword or drop the last five commits. Only for a branch nobody
else has pulled.
- git revert <sha>
- Make a new commit that undoes an old one. This is the safe undo for anything
already published — history stays intact.
Branches
Moving HEAD around inside the local repository.
- git switch -c topic
- Create a branch and move to it.
switch and restore split
the old overloaded checkout into two clearer commands.
- git switch -
- Back to the branch you were on before. Same idea as
cd -.
- git branch --merged main
- Branches whose work is already contained in
main — the safe list to
delete with git branch -d.
- git merge --no-ff topic
- Always record a merge commit, even when a fast-forward was possible, so the branch
stays visible in the graph.
- git rebase main
- Replay your commits on top of the current
main. Produces a linear
history and new commit hashes.
- git cherry-pick <sha>
- Copy one commit onto the current branch.
-x records where it came
from in the message.
Repository and remotes
The only commands here that touch the network.
- git fetch --prune
- Update your view of the remote and delete tracking refs for branches that no
longer exist there. Changes nothing in your working tree.
- git pull --rebase
- Fetch, then replay your local commits on top instead of merging. Keeps the history
free of merge commits that say nothing.
- git push -u origin topic
- Push and set the upstream, so later a bare
git push works on this
branch.
- git push --force-with-lease
- Force-push, but refuse if the remote moved since your last fetch. Use this instead
of
--force every time.
- git remote -v
- Fetch and push URLs for each remote. They can differ, which is how read-only
mirrors are set up.
Getting out of trouble
Where to look when a state has gone somewhere unexpected.
- git reflog
- Every position HEAD has held, including commits no branch points at any more. A
bad reset or rebase is nearly always recoverable from here with
git reset --hard HEAD@{2}.
- git bisect start / bad / good <sha>
- Binary search through history for the commit that introduced a problem. With
git bisect run ./test.sh it finishes on its own.
- git rebase --abort
- Put everything back the way it was before the rebase started. The same flag exists
for
merge and cherry-pick.
- git fsck --lost-found
- Find dangling objects when even the reflog has been expired.
The reflog is local and expires — ninety days for reachable entries,
thirty for unreachable ones by default. It will not save you from a bad
git clean, because untracked files were never objects in the first place.
Three commands here can lose work that no other command can bring
back: git restore without --staged, git reset
--hard, and git clean -fd. Everything else writes a new object
before it moves a pointer.