git status answers "what state am I in?" and git log answers "how did we get here?" — the two reading commands you will run more than all others combined. Learning their useful forms turns history from a wall of hashes into a working index.

status: the present tense #

The three areas status reports
$ git status
On branch main
Changes to be committed:        # staged (git add ran)
  modified:   src/app.js
Changes not staged for commit:  # tracked, changed after staging
  modified:   src/blog.js
Untracked files:                # git has never seen these
  scripts/new-build.py

Everything in status is one of those three areas. The short form -s compresses it — M staged, M unstaged, ?? untracked:

The compact form, and one thoughtful flag
$ git status -s
M  src/app.js
 M src/blog.js
?? scripts/new-build.py
$ git status -sb        # adds the branch and its tracking state

log: the past tense #

Plain git log is verbose. The two forms most people settle on:

One line per commit, and the graph
$ git log --oneline -5
f3e2d1c fix: correct cache headers on media
b2c3d4e feat: paginate journal index
$ git log --oneline --graph --all -10
* f3e2d1c (HEAD -> main) fix: ...
| * 9a8b7c (feature/toc) wip: ...
|/

The graph form makes branches visible — which commit is on which line of development — and --all includes branches beyond the current one.

Filtering history #

GoalCommand
One file's historygit log --oneline -- src/blog.js
Commits touching a functiongit log -L :renderIndex:src/blog.js
By authorgit log --author="ana"
Since a dategit log --since="2 weeks ago"
Content changesgit log -p -3 (patch per commit)
Commits containing a stringgit log -S "cache-control"

-S — the pickaxe — finds commits that added or removed a string anywhere in the codebase: the answer to "when did this stop working" before bisect even starts.

status discipline #

The habit that prevents most accidents: run git status before every staging decision and after every operation that moves history. It is free, instant, and the antidote to committing the wrong files or reviewing changes you forgot to stage.

The changes behind the hashes come from add and commit; what the remote has is a fetch away. The Git series continues.