Checking status and diff before every commit is the habit that prevents most accidental commits.
git status is the command you will run most. It tells you the current branch, how it compares to its upstream (ahead/behind), and lists files in three groups: staged, not staged, and untracked. It also prints hints for the obvious next commands. git status -s gives a compact two-column view: the left column is the index, the right column is the working tree (M modified, A added, D deleted, ?? untracked).
git diff shows line-level changes in unified diff format. Lines starting with - were removed, + were added, and @@ -12,7 +12,8 @@ headers say where each hunk sits.
The variants differ only in which two snapshots they compare:
git diff: working tree vs index (unstaged changes).git diff --staged(alias--cached): index vsHEAD(staged changes).git diff HEAD: working tree vs last commit (everything).git diff main feature: tip of one branch vs another.git diff main...feature: whatfeaturechanged since it branched offmain.
Handy flags: --stat for a per-file summary, --name-only for just paths, --word-diff for prose, and -- path to limit output to some files.
Get into the habit of git status then git diff --staged before each commit. It catches debug prints, stray files and secrets before they enter history, where they are much harder to remove.
Example
git status
# On branch main
# Your branch is ahead of 'origin/main' by 1 commit.
# Changes to be committed: modified: app.py
# Changes not staged: modified: README.md
# Untracked files: notes.txt
git status -s
# M app.py <- staged
# M README.md <- modified, not staged
# ?? notes.txt
git diff --staged --stat # app.py | 4 ++--
git diff main...feature -- src/Remember
- git status: branch, ahead/behind, staged / unstaged / untracked
- diff variants differ in which two snapshots they compare
- A...B shows what B changed since it diverged from A
Watch out for
- Running plain git diff after staging everything, seeing no output, and assuming there are no changes.