Git from First PrinciplesPost 5 of 16 · 16 of 16 written
- How to use this guide
- 01Why version control exists
- 02Git's data model: a content-addressed object database
- 03References: branches, HEAD and tags are pointers
- 04The three areas: working tree, index, repository
- 05Daily commands, mapped onto the model
- 06Branching and integration: merge, rebase, cherry-pick
- 07Undoing things safely
- 08Remotes and collaboration
- 09Workflows and conventions
- 10Git for AI and ML work
- 11Git with AI coding agents
- 12Power tools
- 13Under the hood: build a commit by hand
- 14Debugging mindset and recovery playbook
- 15Practice path, fluency checklist and glossary
- 16Sources
5. Daily commands, mapped onto the model
About fifteen commands cover nearly all daily work, and each one is just a read or a write on the objects, refs and areas from Parts 2–4.
Setup, once per machine
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global core.editor "code --wait" # or nano, vim
git config --list --show-origin # every setting and the file it came fromYour name and email are written into every commit object, permanently. Use the email linked to your GitHub account, or its private noreply address, so your work is attributed to you.
Config has three levels, and the narrower one wins: system (the machine), global (~/.gitconfig, you), and local (.git/config, one repository). A work email in one repository is a local setting.
The commands, seen through the model
| Command | Reads | Writes | Remember |
|---|---|---|---|
git init |
- | A new .git database |
Safe to run; it never touches your files |
git status |
Working tree, index, HEAD | Nothing | Run it before and after everything |
git add <path> |
Working tree | Blobs and the index | -p stages hunk by hunk |
git commit |
Index, HEAD | Trees, a commit, moves the branch | -v shows the diff while you write |
git diff |
Two areas or commits | Nothing | See the three comparisons in Part 4 |
git log |
Commits | Nothing | Flags below |
git show <rev> |
One object | Nothing | A commit and its diff against its parent |
git restore <path> |
Index | Working tree | Discards unstaged edits |
git restore --staged <path> |
HEAD | Index | Unstages; your edits stay on disk |
git switch <branch> |
That branch’s commit | HEAD, index, working tree | Refuses if it would destroy edits |
git switch -c <new> |
HEAD | A new ref, HEAD | Create and move in one step |
git branch |
Refs | Refs | List, create, -d delete merged, -D force |
git rm <path> |
- | Index and working tree | --cached stops tracking but keeps the file |
git mv <a> <b> |
- | Index and working tree | Same as delete plus add; renames are detected later |
git tag |
- | A ref, and a tag object if annotated | -a for annotated release tags |
About git checkout. It used to do everything. In 2019 Git split it into switch (move between branches) and restore (restore files). All three remain supported, so tutorials still use checkout: git checkout <branch> is switch, git checkout -- <file> is restore, and git checkout -b <new> is switch -c.
Reading history with log
git log --oneline --graph --all # the shape of the graph (your git lg alias)
git log --stat # which files each commit touched
git log -p -- src/train.py # every change to one file, with diffs
git log --follow -- src/model.py # the same, following renames
git log --author="Ulvi" --since="2 weeks ago"
git log -S "learning_rate" # commits that added or removed this text
git log main..feature # what feature has that main lacks.gitignore
.gitignore lists untracked files Git should never offer to add. It has no effect on files already tracked. To stop tracking one, run git rm --cached <path> and commit.
*.ckptmatches anywhere./data/matches only at the repository root. A trailing/matches folders only.!keep.txtre-includes something an earlier rule excluded.**/matches any depth.- Shared rules go in the committed
.gitignore. Personal rules for one repository go in.git/info/exclude. Rules for all your repositories (editor and OS junk) go in a global file set bycore.excludesFile. git check-ignore -v <path>tells you which rule, in which file, matched.
A starting point for Python and ML projects:
# Python
__pycache__/
*.py[cod]
.venv/
*.egg-info/
# Notebooks
.ipynb_checkpoints/
# Secrets: never commit these
.env
*.pem
# Data and model artifacts: version them with DVC or LFS instead (Part 10)
data/
models/
checkpoints/
*.ckpt
*.pt
*.safetensors
# Experiment tracker output
wandb/
mlruns/
outputs/
# OS and editor
.DS_Store
.idea/What makes a good commit
One commit should hold one logical change that you could describe in one sentence. That makes history readable, reverts surgical and bisect precise (Part 12).
Write the subject line in the imperative, about 50 characters: “Add stratified split to data loader”. After a blank line, the body explains why, because the diff already shows what. Part 9 covers conventions.
Check yourself
- Which single command shows exactly what your next commit will contain?
- You added
data/to.gitignore, yet Git still reports changes todata/raw.csv. Why, and what is the fix? - In model terms, what is the difference between
git restore fandgit restore --staged f?