Lesson 02 of 8 · Level 1 — Foundations
The everyday workflow
The loop you'll run hundreds of times a week: see what changed, stage exactly what belongs together, write a commit message someone can use later, keep junk out with .gitignore, and search history when you need to know why something changed.
The loop
$ git status -sb # what changed?
$ git diff # the actual lines
$ git add -p # stage what belongs together
$ git diff --staged # review exactly what you're about to commit
$ git commit # write a good message
That fourth line is the habit that separates tidy history from messy history: always look at the staged diff before committing.
Packing a suitcase: you lay everything out (working tree), put into the suitcase only what this trip needs (staging), check the suitcase before closing it (staged diff), then zip it and label it (commit with a message).
Commits that help later
A commit should be one logical change: a bug fix, a feature step, a refactor. Small, focused commits are easy to review, easy to revert and easy to understand in git blame a year later.
A good message says what and why:
fix(api): return 404 for unknown order IDs
The handler raised a KeyError and returned 500, which paged on-call
for what is a client error. Look the order up with .get() and return
404 with an error body.
Refs: TICKET-123
- Subject line: imperative ("fix", "add", "remove"), under ~70 characters.
- Blank line, then the body: the reasoning, the alternative you rejected, anything surprising.
- Many teams use Conventional Commits (
feat:,fix:,chore:,docs:), which tools can turn into changelogs and version bumps.
Keep junk out: .gitignore
# Terraform
.terraform/
*.tfstate
*.tfstate.backup
*.tfplan
crash.log
# Secrets and local config
.env
*.pem
*.key
kubeconfig*
# Builds and editors
dist/
node_modules/
__pycache__/
.idea/
.vscode/
.DS_Store
.gitignore only affects untracked files. If a file is already committed, ignoring it later does nothing: remove it from the index with git rm --cached <file> (and if it was a secret, treat it as leaked: see lesson 06 and lesson 07).
Reading history
History is only useful if you can search it:
$ git log --oneline --graph -15
$ git log --author=asha --since="2 weeks ago"
$ git log -S "replicas: 3" -- k8s/ # when did this value appear or disappear?
$ git blame -L 10,30 values-prod.yaml # who changed these lines, in which commit
$ git show 3f1c9a0 # read that commit's message and diff
git log -S ("pickaxe") is one of the most useful and least known commands: it finds the commit that introduced or removed a string anywhere in the repository.
Try it: two clean commits
- In any repository, change one function (a "fix") and reformat another part of the same file.
git add -p, answeryonly for the fix hunks, thengit diff --stagedand commit with a proper message.- Stage and commit the formatting separately (
style: ...). - Add a
.envfile with a fake secret; confirmgit statusdoesn't show it after adding it to.gitignore. - Use
git log -Sto find the commit that introduced one of your strings.
Going deeper: tooling for commit quality
git commit -vshows the diff in the editor while you write the message.- Commit message linting (commitlint) in CI enforces Conventional Commits where teams rely on them for releases.
git config --global commit.template ~/.gitmessagegives everyone a message skeleton (what / why / refs).git log --format='%h %an %s'andgit shortlog -snanswer "who works on this?" quickly.
Recap
- Loop: status → diff → add -p → diff --staged → commit.
- One logical change per commit; messages say what and why.
- .gitignore for generated, local and secret files;
git rm --cachedfor files already tracked. - Search history with
log -p,log -S,blameandshow.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.