Everyday Git
Topic 07

Inspecting State

Core

Three commands answer "where am I?" in a repository: git status shows what has changed and what is staged right now, git diff shows the exact line-level changes, and git log shows the history that led here. They read state and change nothing.

Misreading repo state is the root cause of most accidental data loss — discarding the wrong file, resetting past work you meant to keep, committing half of what you intended. Reading state correctly is the cheap habit that prevents the expensive recovery, so these commands belong before every operation that changes anything.

Status

git status sorts your files into three buckets: staged (going into the next commit), modified-but-unstaged (changed on disk, not yet added), and untracked (Git has never seen them). It also tells you the branch and whether you are ahead of or behind the remote. git status -sb gives the same picture in a compact two-letter-per-file form once you know what the columns mean.

Diffing the Trees

The diff commands compare specific pairs of trees, and which pair you ask for is the whole point. git diff with no arguments compares the working tree to the index — your unstaged changes. git diff --staged compares the index to HEAD — what is staged and headed into the next commit. git diff <a> <b> compares any two commits. Asking for the wrong pair is why a change can look like it vanished.

Reading History

Bare git log dumps full commit entries newest-first, which buries the structure on any real repository. --oneline collapses each commit to one line, --graph draws the branch and merge topology, and --stat shows which files each commit touched. Filters narrow it down: --since, --author, -p to show each commit's diff, and -S<string> — the pickaxe — to find the commits where a given string entered or left the code.

Finding Who and When

git blame <file> annotates each line with the commit, author, and date that last changed it — useful for finding the commit and its reasoning, less useful as a way to assign fault, since a whitespace reformat can put an innocent author on every line. git show <commit> prints one commit in full: its message and its complete diff, which is the fastest way to see what a single commit actually did.

Following a Single File

A file's history can predate its current name. git log --follow <file> traces it across renames, so you see the full lifetime of the content rather than just the commits since the last git mv. Without --follow, history stops at the rename and the earlier work looks like it belongs to a different file.

git diff vs git diff --staged

git diff (no arguments) — compares the working tree to the index, so it shows only the changes you have not staged yet. Once you stage a change, it disappears from this view, which is exactly what makes people think it was lost.

git diff --staged (alias --cached) — compares the index to HEAD, so it shows what is staged and will go into the next commit. When a change "vanishes" from plain git diff after an add, this is the view that shows it.

Common Mistakes
  • Running plain git diff after staging everything, seeing nothing, and concluding the changes were lost — they are staged, so git diff --staged is the correct view.
  • Browsing a large repo's history with bare git log and getting lost, because without --oneline --graph the branch topology is invisible.
  • Using git blame to assign fault rather than to find the commit and its reasoning, missing that a reformat commit can mask the real author.
  • Hunting for when a string was introduced by scrolling through git log by eye instead of using git log -S<string>, the pickaxe built for exactly that.
  • Reading git log on a renamed file without --follow and concluding the file has no history before the rename.
Best Practices
  • Run git status before any state-changing command — reset, checkout, commit — to confirm what is staged.
  • Make git log --oneline --graph --decorate your default history view to see branches and merges at a glance.
  • Use git diff --staged to review precisely what a commit will contain before running git commit.
  • Use git log -S<term> or git log -G<regex> to find the commit that added or removed specific code.
  • Inspect one commit in full — message and diff — with git show <commit> instead of guessing from the log summary.
Comparable toolsMercurial hg status, hg diff, hg log, hg annotate (blame)Subversion svn status, svn diff, svn log, svn blamePerforce p4 status, p4 diff, p4 changes, p4 annotateFossil fossil status, fossil diff, fossil timeline

Knowledge Check

You stage a change, then run plain git diff and see nothing. Where is the change?

  • Staged — plain git diff compares working tree to index, so a staged change only shows under git diff --staged
  • Lost — the act of staging deleted it from both the working tree and the index entirely
  • In the previous commit, already recorded permanently against HEAD by the staging step itself the moment you ran it
  • In the reflog, parked there and waiting for you to run a manual restore

What does git log --oneline --graph reveal that bare git log hides?

  • The branch and merge topology — where branches diverged and rejoined — in a compact, scannable form
  • The full multi-paragraph commit message body for each entry, which the --oneline flag normally truncates away
  • Commits that are otherwise hidden from you by per-commit access control rules
  • The remote's newer commits that you have not yet fetched down locally

When does the pickaxe git log -S<string> beat reading the log by hand?

  • When you need the commit where a specific string was introduced or removed, found directly rather than by scrolling
  • When you want the commits sorted by their author's name rather than by their commit date
  • When the repository was shallow-cloned with a fixed depth limit, so its older history is therefore incomplete on disk
  • When you need to see the staged but uncommitted changes shown alongside the committed ones

What can git blame reliably tell you, and what can it not?

  • It tells you which commit last touched each line; it does not reliably tell you who is responsible, since a reformat can overwrite the real author
  • It reliably tells you who originally wrote every single line, regardless of any later edits made to it
  • It lists every commit that has ever touched the file at all, presented in full graph order with each contributing commit's subject line attached for context
  • It shows the currently staged but still uncommitted edits annotated against each line before you have committed them anywhere

You got correct