TAI BUI
Back to curriculum
Learn—

Version Control

Git is not a nice-to-have you bolt on later. It's the only reason a bad edit costs you thirty seconds instead of your whole afternoon.

TIME: ~30 minutesPREREQUISITES: Phase 0, Lesson 01

Learning Objectives

  • Configure git identity and run the daily loop of add, commit, push
  • Branch off for experiments so you can wreck things without wrecking main
  • Write a `.gitignore` that keeps model checkpoints and other giant binaries out of the repo
  • Read `git log` to see how a project actually got built, one commit at a time

The Problem

You're about to write code across 20 phases of this course. Some of it will work. Some of it will silently break something that worked yesterday. Without version control, "undo" stops existing the moment you close your editor, and there's no way to hand your code to anyone else, including future-you, six lessons from now, wondering what changed.

Git solves the first problem: it's the tool that remembers every version of every file, and it does that entirely on your own machine, no internet connection required. GitHub solves a different problem, and it is not the same thing as git even though people talk about them like they are. GitHub is a company that hosts a copy of your git history online, so other people (and other computers) can get to it. You'll hear "push it to GitHub" and "back it up to the cloud" used interchangeably, but git already did the hard part the moment you committed; GitHub is just the parking lot everyone agreed to park in. This lesson covers exactly what you need for this course, nothing more. I'm not going to make you learn rebase before you've written your first perceptron.

Warm-up0/3
1. What does version control primarily help you do?Warm-up
A project folder holding six copies of a thesis with names like thesis_final_FINAL, beside an email from a coworker asking to combine her edited copy with the sender's.
A group thesis kept as plain files.
2. You overwrite a working script with a broken edit and close your editor. Which habit would have let you get the working version back?Warm-up
Two versions of analyze.py: Monday's runs and prints 0.91, Tuesday's has a missing closing parenthesis saved over it and raises a SyntaxError. The folder lists only analyze.py.
The Monday version of the file no longer exists anywhere.
3. You want to try a risky rewrite of a script but keep the working version safe, with no special tools. What is a sensible low-tech move?Warm-up
A project folder with train.py (working, 120 lines), data.csv and notes.txt, next to a note saying the plan is a risky rewrite of the training loop while the working file must stay safe, using only a file explorer and text editor.
Check: The Problem0/2
1. What is a remote in git?Define
Terminal showing git remote -v with the name origin and an https GitHub URL for fetch and push, followed by the [remote "origin"] section of .git/config.
2. What is the actual relationship between git and GitHub?Explain
Two labelled boxes side by side: git, with the output of git --version, and GitHub, with github.com and the repository name, joined by a dashed line marked how are related.

The Concept

Mermaid diagram

Start with what a repository actually is, because everything else in this lesson sits on top of it. A repo is just a regular folder on your computer, except git is watching it. The moment you run git init (or clone one that already exists), git starts keeping a private history of every file in that folder, stored in a hidden .git subdirectory. Delete that .git folder and your files are still there, but the entire history is gone. That folder, plus everything git has recorded about it, is the repo.

A commit is what that history is made of. Think of a commit as a photograph of your entire project at one instant: every file, exactly as it looked, tagged with a message describing what changed and why. git log is just flipping back through that stack of photographs in order, newest first. A commit is never partial. It captures the whole project state at that moment, not just the one file you happened to be editing.

Three things to remember, and honestly, three things is the whole mental model:

  1. Save often (git commit)
  2. Push to remote (git push)
  3. Branch for experiments (git checkout -b experiment)

Everything else git can do is an elaboration on those three moves.

Check: The Concept0/3
1. What is a repository, in practice?Define
Terminal listing a project folder with ls -a: ., .., .git, README.md, perceptron.py, then the contents of the hidden .git directory.
A project folder after git init and one commit.
2. What does a single git commit actually capture?Define
A project folder with four files (perceptron.py edited last, data.csv, README.md, utils.py) and the two commands git add perceptron.py and git commit -m with the message Add perceptron implementation.
3. A project has commits A, B and C, made in that order. In what order does `git log` list them?Apply
Three commits drawn left to right in the order they were made: A Add data loader, B Add perceptron, C Add README, followed by the command git log --oneline with no output yet.

Build It

Step 1: Configure git

Every commit needs an author, so before anything else, tell git who you are. It only takes once per machine:

bash
git config --global user.name "Your Name"
git config --global user.email "[email protected]"

That stamps your name on every commit you make from here on.

Step 2: The daily workflow

bash
git status

Run that after editing a file and you'll see something like this:

On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
	modified:   file.py

no changes added to commit (use "git add" and/or "git commit -a")

That's git telling you exactly what it sees: which branch you're on, and which files changed since your last commit.

Before you run the next command, it helps to know why staging is its own step instead of git just saving everything that changed: git add lets you choose exactly which edits go into the next snapshot. Touch three files but only one is actually ready? Add that one and commit it, leaving the other two for later. Skip staging and every commit would be forced to include everything that's currently different, whether it belongs together or not.

Now stage, commit, and push:

bash
git add file.py
git commit -m "Add perceptron implementation"
git push origin main

That four-command sequence, status, add, commit, push, is 90% of what you'll type all course: status tells you what changed, add stages it, commit snapshots it with a message, push backs it up to GitHub. (origin is just git's name for the remote you cloned from or first pushed to; main is the branch you're pushing.)

Step 3: Branching for experiments

Branches are how you try an idea without risking main: switch to a new one, make your changes, and only bring them back once you know they work.

bash
git checkout -b experiment/new-optimizer
 
# ... make changes, commit ...
 
git checkout main
git merge experiment/new-optimizer

A branch costs nothing to create. If the experiment doesn't pan out, delete the branch and main never knew it happened.

Sometimes git merge can't combine the two branches automatically, usually because the same lines of the same file changed on both sides. That's a merge conflict, and it looks worse than it is. Git stops, tells you which file has the problem, and marks both versions directly inside that file:

<<<<<<< HEAD
your version of the line
=======
the other branch's version of the line
>>>>>>> experiment/new-optimizer

To fix it: open the file, decide which lines you actually want (one side, the other, or a mix), delete the <<<<<<<, =======, and >>>>>>> markers, then run git add on the file and git commit to finish the merge. Nothing is lost. Git is just asking you to make a call it can't make on its own.

Step 4: Keeping large files out

Git tracks every version of a file forever, which is exactly the wrong behavior for a 2GB model checkpoint: commit it once, re-save it ten times, and your repo now carries ten 2GB copies whether you still need them or not. The fix isn't to be careful about what you commit, it's to tell git never to look at those files at all.

A .gitignore file at the root of your repo does that: one pattern per line, and anything matching it disappears from git status and never gets staged by accident.

*.pt
*.pth
*.safetensors
__pycache__/
.env

Create that file once at the start of a project and forget about it. One catch: if a file was already tracked before you added it to .gitignore, the ignore rule won't touch it retroactively. Run git rm --cached <file> once to untrack it, then commit that removal.

Step 5: Working with this course repo

bash
git clone https://github.com/Tai-Chi-Bui/taibui.dev.git
cd taibui.dev
 
git checkout -b my-progress
# work through lessons, commit your code
git push origin my-progress

The first time you push, GitHub won't just accept your username and password anymore. It will ask you to authenticate, either by pasting a personal access token where it prompts for a password, or by setting up an SSH key so it recognizes your machine automatically. Lesson 5 walks through generating an SSH key, so treat this as a heads-up rather than something to solve right now.

Check: Build It0/19
1. What is a branch in git?Define
Terminal output of git log --oneline --decorate --graph --all: five commits, with HEAD, experiment/new-optimizer and main shown as labels next to commit hashes.
2. What does merging do in git?Define
Commit graph: main has three commits and is checked out, while branch experiment/new-optimizer has two more commits built on the third. Below, git branch lists the branches with main current, then the command git merge experiment/new-optimizer.
Real repo state right before the merge command runs.
3. In `git push origin main`, what does `origin` refer to?Define
Terminal with git clone of https://github.com/Tai-Chi-Bui/taibui.dev.git, cd taibui.dev, a commit, then git push origin main, with the word origin underlined and marked with a question mark.
4. The first time you `git push` to GitHub over HTTPS, what should you expect?Define
Terminal with git clone of the course repo over HTTPS, cd, git checkout -b my-progress, a commit, and git push origin my-progress, with a banner asking what appears next on this first push from the machine.
5. Why does git require a separate `git add` step before `git commit`, instead of just committing everything that changed?Explain
Terminal output of git status on branch main listing three files under Changes not staged for commit: loss.py, optimizer.py and utils.py.
6. Why does Step 1 have you run `git config --global user.name` and `user.email` before anything else?Explain
Terminal running git config --global user.name and user.email, then cat ~/.gitconfig showing a [user] section with name = Your Name and email = you@example.com.
7. Why is listing checkpoint patterns in `.gitignore` safer than just being careful about what you commit?Explain
Terminal: ls -lh listing best.safetensors (4.0G), epoch_03.pth (2.0G) and model_final.pt (2.0G), and git status --short showing them as untracked. Below, a candidate .gitignore holding *.pt, *.pth and *.safetensors.
8. Why does branching before an experiment limit the cost of a failed idea?Explain
Commit graph with main at three commits and branch experiment/new-optimizer, currently checked out, two commits ahead. A note says the Adam variant was tried, the learning rate tuned, and the loss still diverges.
9. Why does `git merge` stop with a conflict instead of picking a side?Explain
Terminal running git merge experiment/new-optimizer on main: Auto-merging train.py, CONFLICT (content): Merge conflict in train.py, Automatic merge failed; fix conflicts and then commit the result.
10. Why should `.pt`, `.pth`, and `.safetensors` go in your .gitignore?Explain
Terminal listing the ckpt/ folder of a training run with ls -lh: four 2G files named epoch_01.pt, epoch_02.pt, epoch_03.pt and final.pth, and one 4G model.safetensors, with du reporting the folder total.
11. What's the correct order for saving and backing up your work in git?Apply
Three boxes in a row: working folder with perceptron.py edited, local history on this machine, and GitHub as the remote copy, with question marks on the two arrows between them.
12. What does `git checkout -b experiment/new-optimizer` do?Apply
Terminal in a repo with one branch: git branch lists * main, git log shows two commits with HEAD -> main, then the typed command git checkout -b experiment/new-optimizer.
13. `git merge` stops and reports a conflict in `train.py`, with `<<<<<<<`, `=======`, and `>>>>>>>` markers inside the file. What should you do?Apply
Terminal showing train.py with conflict markers: <<<<<<< HEAD, lr = 0.05, =======, lr = 0.001, >>>>>>> experiment/new-optimizer, then epochs = 10, followed by git status --short showing UU train.py.
14. You've made three commits locally but haven't run `git push` yet. Where do those commits exist?Apply
Terminal showing three git commit commands with their short hashes and messages (Add loader, Add perceptron, Add README) and git log --oneline listing the commits. No git push appears.
15. A file was already tracked by git before you added a matching pattern to `.gitignore`. What do you need to do to actually stop tracking it?Apply
Terminal: .gitignore contains *.pt, git ls-files lists model.pt and train.py, and git status --short shows model.pt modified and .gitignore untracked.
16. You tried an idea directly on main, committed it, and now you're untangling it. Which habit would have avoided this?Analyze
Terminal output of git log --oneline --decorate on main: six commits, with Try Adam variant and Tune learning rate sitting between Add perceptron and Fix typo in README, and a further Adam commit on top.
17. You edited three files but only one is ready. After committing, all three changes are in the snapshot. What went wrong?Analyze
Three edited files, loss.py finished and optimizer.py and utils.py half-done, then the terminal output of git commit reporting 3 files changed.
18. You want to try a new optimizer in your clone of this course repo and may abandon it. Which plan fits?Design
Terminal after cloning the course repo: git log --oneline --decorate shows five commits with HEAD -> main. A task card says to try a new optimizer in this clone, which may be abandoned.
19. A new project will produce large `.pt` and `.safetensors` checkpoint files. When should you set up ignoring them?Design
Terminal in a new project: git init reports an empty repository, git status shows On branch main, No commits yet, and train.py untracked. A card lists the checkpoint files training will write: epoch_01.pt to epoch_10.pt at about 2 GB each, and model.safetensors at about 4 GB.

Use It

For this course, you need exactly these commands:

CommandWhen
git cloneGet the course repo
git add + git commitSave your work
git pushBack it up to GitHub
git checkout -bTry something without breaking main
git log --onelineSee what you've done

That's the whole list. You don't need rebase, cherry-pick, or submodules for this course, and I'd argue most people don't need them for most jobs either. Learn those five commands cold before you reach for anything fancier.

Author's Note

The habit that actually paid off for me wasn't any git trick: branching before I was confident, not after. If you wait until you're sure an experiment is a good idea to give it a branch, you've already committed the bad version to main and now you're untangling it. Branch first, decide later. Worst case you delete a branch. That's the whole cost.

Exercises

  1. Clone this repo, create a branch called my-progress, make a file, commit it, push it
  2. Write a .gitignore that excludes model checkpoint files (.pt, .pth, .safetensors)
  3. Read the commit history of this repo with git log --oneline and see how the lessons were actually built up over time

Key Terms

TermWhat people sayWhat it actually means
Commit"Saving"A snapshot of your entire project at a point in time
Branch"A copy"A pointer to a commit that moves forward as you work
Merge"Combining code"Taking changes from one branch and applying them to another
Remote"The cloud"A copy of your repo hosted somewhere else (GitHub, GitLab)