A time machine for your code: every change is saved, so you can go back, work in parallel, and ship together with other people.
← / → to navigate · EN/VI switch below
Without version control, your code lives on one hard drive, file by file. Change something and pray.
index.html, index_v2.html, index_final.html, index_final_REAL.html — at 2 a.m. you have no idea which one is the truth.
An overwrite, a crash, a bad AI edit — and there is no 'undo' for a whole project.
What did the code look like last Tuesday? Why did it break after that change? No way to know.
Two people editing the same file on one laptop? Good luck deciding who wins.
One idea: git keeps a full history of your project on your machine — and can sync that history with a copy on the internet (GitHub).
Every git command moves things between four rooms: working tree → staging area → commits → the branch pointer.
That's the whole vocabulary. Everything else is detail.
The real files on disk, the ones you and AI edit. 'Current', but not yet saved in history.
A checklist of exactly which changes will go into the next commit. You choose; git doesn't guess.
Each commit = one snapshot of the whole project + a message. Snapshots stack up into history.
Not a copy of the code — just a pointer that says 'latest snapshot so far = this one'.
The one diagram to remember: you edit in the working tree, select changes into staging, and commit turns the tray into a new snapshot.
A short ID, a message, and a frozen snapshot of the project. git log shows you the album.
git log --oneline
a1b2c3d fix: handle empty search results
d4e5f6a feat: add user profile page
0f9e8d7 first commit
Newest at the top. Each line = one snapshot you can always go back to.
Both answers are the same word: control. You decide exactly what is saved, and when.
You can stage 1 of 5 changed files. The commit contains exactly what you put in the tray — nothing more, nothing less.
Classic use: stage the code fix, but not the .env file with your secrets.
A commit is a save point you create deliberately. No auto-save, no accidents — history is a sequence of decisions.
Small, frequent commits = a fine-grained undo history.
90% of a day in git is this three-command loop. Learn it until it's muscle memory.
Check, choose, save. Repeat.
Lists changed and new files: 'you edited X, and Y is new'. The first thing to type whenever you're unsure.
git add app.py puts that file's current state into staging. git add . = everything changed.
git commit -m "message" turns the tray into a new snapshot in history. The message is for future you.
It tells you exactly where you are: what's edited, what's selected, what's brand new.
Two commands and a snapshot exists. The message is the only part you write yourself.
Commit small, commit often. A commit is a save point, not a monument.
You'll read these messages months later — or a teammate will. Write for that reader.
Fixed the login bug = its own commit. The new icon = another. Mixing them makes undo impossible.
"fix: handle empty form" — not "I fixed the thing with the form or maybe". A headline, not a diary.
The diff shows WHAT changed. The message is for WHY — 'because the API now returns null'.
Let AI draft the message from your changes — but keep it to one line you'd stand behind.
History is the point. git can show you any past state — and put you back in it.
log = the list, diff = the comparison, show = one snapshot up close.
| Command | It answers | When you use it |
|---|---|---|
| git log | What happened, in what order? | Always — the map of your history |
| git diff | What changed, line by line? | Before committing, or 'did I break it?' |
| git show a1b2c3d | What did THAT one commit change? | When a commit caused a problem |
--oneline = one line per commit. The habit: skim the map before you change anything.
Lines starting with + were added, lines with - were removed. The fastest way to answer 'did I break it?'.
git diff
--- a/app.py
+++ b/app.py
-price = 100
+price = 99 # black friday!
The diff is the truth: exactly what will go into the next commit.
Ask: how far back do I want to go? Working tree → staging → history. Each level has its own undo.
'I messed up this file, give me back the last committed version.' git restore app.py. The commits are untouched.
'I staged the wrong file.' Take it out of the tray. The file on disk is untouched.
'That commit was a mistake.' Move the branch pointer back one step. The work is still recoverable — don't panic.
The most common rescue: one file edited badly (often by an AI), everything else fine.
A branch is a pointer, not a copy. That's why creating one is instant and switching is free.
The branch 'main' points at the newest snapshot. Every new commit slides the pointer forward. That's all.
Creating a branch copies nothing — it just pins a name to a commit. Milliseconds, zero cost.
Commit on 'main' and 'main' slides forward to the new snapshot. History is a chain; the branch is the label on its tail.
Ten branches can point at the same commit — or at different ones. They share history like family trees.
Even if you never make one, 'main' (or 'master') exists. Your commits go wherever the pointer stands.
You fork off from C2, add C3 on the feature branch, then merge back — main absorbs your work.
Four commands: cut a branch, work, go home, merge. That's the whole dance.
Branches are how parallel work stops being a fight.
Mid-refactor? Move to a branch. 'main' stays stable for everyone else.
Try a risky idea on a branch. It fails? Delete the branch — history is untouched.
The team convention: name it after the feature (feature/login, fix/broken-nav). Finish, merge, done.
Ten people, ten branches, zero 'who overwrote my file?'. GitHub turns each branch into a reviewable Pull Request.
The real project lives on a remote repo — usually GitHub. Your laptop is one of many copies.
Every clone gets a default remote named 'origin' — the repo it came from.
Fast, private, offline. You commit here as often as you like — nobody's watching.
The copy on GitHub that teammates, servers and deploys all agree on.
git push origin main = 'send my main branch to the repo I named origin'. You can have many remotes.
You commit locally, teammates push remotely — the copies diverge. Syncing is a daily habit, not a big event.
clone = 'get a copy, wire it to origin'. After that, the loop is just pull in, push out.
Two directions: the pull side (get) and the push side (send). fetch is the quiet one; pull = fetch + apply.
| Command | What it does | In plain words |
|---|---|---|
| git clone <url> | Downloads a whole repo (code + history) and remembers its origin | Start of every project: 'give me a copy' |
| git fetch | Downloads new commits from origin — but changes nothing on your laptop | 'Show me what's new out there' |
| git pull | fetch + merge into your current branch | 'Bring the new stuff into my code' |
| git push | Uploads your commits to origin | 'Publish my work — everyone can see it' |
.gitignore lists patterns to skip. git status won't even mention these files — they stay local.
# secrets — never, ever commit these
.env
*.db
# generated stuff — easy to rebuild
__pycache__/
node_modules/
*.log
dist/
The pattern rule: if it can be regenerated, or if it's a secret — ignore it.
Once a secret is in history, it's public — history doesn't forget, and neither do attackers.
API keys, passwords, tokens. .env files are the #1 leak. Anyone can read your repo history.
Databases with real user data are private by nature. Commit a sample or a schema, not the data.
Python's bytecode cache. It rebuilds itself; in git it's pure noise.
Hundreds of MB of installed libraries. npm install rebuilds it from package.json.
Every real project lives in git. The repo is the single source of truth that everything else is built from.
Memorize these and you can work with any project. The rest, AI and docs cover.
| Command | Job |
|---|---|
| git status | Where am I? What changed? |
| git add / git commit | Stage and save a snapshot |
| git log / git diff / git show | Read the past |
| git restore | Undo one file (or one staging) |
| git switch (-c) / git branch | Move between parallel lines of work |
| git merge | Combine a branch back into main |
| git clone / git pull / git push | Get the copy, keep it fresh, publish work |
Git is a time machine with a share button.
Every project you'll touch in this course lives in git. status → add → commit → push. That's the whole shipper's loop.