Git in 10 Minutes

Most Git tutorials teach you commands. Then you type one in the wrong state, Git prints a paragraph of nightmare, and you copy-paste your project into a new folder and start over. This page teaches the model first — three trees, branches as sticky notes — because once you have the model, the commands become obvious and the nightmares become boring.

01The model: three trees, snapshots not diffs

Every Git command moves files between three places: your working directory (the files you edit), the staging area (a loading dock for the next snapshot), and the repository (the permanent photo album). And a commit is a full snapshot of your project, not a diff. Get these two facts and half of Git's weirdness evaporates.

# the three trees, left to right:
#  working dir  --add-->  staging  --commit-->  repository

git init                # start tracking this folder
git status              # where is everything right now?
You will hit this
fatal: not a git repository (or any of the parent directories): .git
You're in the wrong folder — Git looks for a hidden .git directory here and upward. cd into your actual project (the folder where you ran git init or git clone). Beginners hit this most often in a brand-new terminal that opened in the home directory.

02The daily loop: status → add → commit

Ninety percent of Git life is one three-beat loop. Run git status obsessively — before and after every other command. It's free, it's instant, and it tells you which tree your changes are sitting in. Nobody senior is embarrassed about how often they type it.

git status                     # 1. what changed?
git add main.py                # 2. put it on the loading dock
git add .                      #    (or stage everything below here)
git commit -m "Fix login bug"  # 3. take the snapshot

git log --oneline              # the album, one line per snapshot
You will hit this
Changes not staged for commit
You edited a file after adding it, then committed — and the newest edits didn't make it in. add copies the file as it is at that moment. Edit again → add again. This is the #1 "Git ate my change" illusion; the change is still in your working directory, just not in the commit.

03Branches are sticky notes, not folders

A branch is not a copy of your code. It's a sticky note pointing at one commit — 41 bytes. When you commit, the note you're standing on slides forward. That's why creating a branch is instant, and why you should make them freely. HEAD is just "the note you're currently standing on."

git switch -c try-new-parser   # write a note, stand on it
# ...hack away, commit as usual...

git switch main                # jump back — your files change on disk!
git merge try-new-parser       # bring the experiment into main
git branch -d try-new-parser   # throw the note away (commits stay)
Honest take
Old tutorials say git checkout for everything. Ignore them — checkout is three unrelated tools wearing one name, and it's how people end up in detached HEAD (section 07). Modern Git split it: switch for branches, restore for files. Use those two words and you'll type checkout maybe once a year.

04Push & pull: your repo has a twin

GitHub holds a full copy of the repository, nicknamed origin. push sends your new snapshots up; pull brings teammates' snapshots down. That's it. The famous rejection error below scares everyone once — and its fix is one habit: pull before you push.

git clone https://github.com/user/repo.git   # copy down + wire up origin
git pull                       # get the latest before you start
git push                       # publish your commits

git push -u origin my-branch   # first push of a new branch
You will hit this
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs

Not broken — Git is refusing to overwrite a teammate's work. Someone pushed while you were working. Run git pull (merges their snapshots into yours), resolve any conflict, push again. Whatever you do, don't reach for --force on a shared branch; that's how teammates' afternoons disappear.

05Undo anything: the rescue table

"How do I undo ___ in Git" is the most-googled question in software. Here's the whole answer, keyed by what you want to undo. One rule first: if the commit was already pushed, use revert (adds an anti-commit) instead of reset (rewrites history under your teammates' feet).

# unstage a file (keep the edits)
git restore --staged secret.txt

# throw away MY EDITS to one file — careful, gone for real
git restore oops.py

# redo the last commit message
git commit --amend -m "Better message"

# undo last commit, KEEP the work (most common wish)
git reset --soft HEAD~1

# undo a commit that's already pushed — the safe public undo
git revert abc1234
The one that saves careers
Deleted a commit with reset --hard and want it back? git reflog shows every position HEAD has ever been at, even "deleted" ones. Find the line before the disaster, then git reset --hard HEAD@{2}. Git almost never truly deletes a commit — it takes about 30 days of neglect before garbage collection does. The panic is nearly always fixable.

06Merge conflicts: it's just a text file

A conflict is not an error. It's Git saying: "two people edited the same lines — I'm not going to guess." It then writes both versions into the file with markers and waits. Your job: open the file, keep what should survive, delete the markers, commit. That's the entire ceremony.

CONFLICT (content): Merge conflict in config.py

# inside config.py you'll find:
<<<<<<< HEAD
timeout = 30          # your version
=======
timeout = 60          # their version
>>>>>>> feature-x

# edit to the truth (maybe timeout = 60, maybe something new),
# DELETE all three marker lines, then:
git add config.py
git commit
Where people go wrong
Committing the markers. If <<<<<<< ever appears in your running code, you merged without finishing the edit — and yes, the app crashes with a syntax error pointing at a line of angle brackets. Also: mid-conflict panic exit is git merge --abort, which puts everything back the way it was. Knowing the eject handle exists makes the whole thing less scary.

07Detached HEAD: not an error, a field trip

One day you'll check out an old commit to look at something, and Git will print the scariest paragraph in software. Translation: you're standing on a commit directly instead of on a sticky note. Looking around is completely safe. The only risk is committing new work here — it belongs to no branch, so it's easy to lose.

git switch --detach abc1234    # time-travel to inspect an old snapshot

You are in 'detached HEAD' state. You can look around, make
experimental changes and commit them, and you can discard any
commits you make in this state without impacting any branches...

git switch -                   # done looking — back to where you were

# made commits here you want to KEEP?
git switch -c rescued-work     # slap a sticky note on them — saved

08.gitignore, and the day you commit a secret

A file named .gitignore lists what Git should pretend doesn't exist: dependencies, build output, .env files with keys in them. Write it in the first ten minutes of every project, because it has a trap: it only affects files Git isn't already tracking.

# .gitignore
node_modules/
.env
*.log
__pycache__/

# "I added .env to .gitignore but Git still tracks it!"
# — it was committed before. Untrack it (keeps the local file):
git rm --cached .env
git commit -m "Stop tracking .env"
The bad day
Pushed an API key to GitHub? Deleting the file in a new commit does nothing — the key lives on in history, and scrapers scan public repos in minutes. The order of operations: 1) revoke the key at the provider right now, 2) then clean history (git filter-repo) if the repo matters. Rotating the key is the fix; history surgery is just hygiene.

09Cheat sheet

The lines everyone googles. Keyed by the sentence in your head.

# "what state is everything in?"
git status

# "save my work"
git add .  &&  git commit -m "message"

# "undo my last commit but keep the work"
git reset --soft HEAD~1

# "this pushed commit was a mistake"
git revert <hash>

# "throw away my edits to this file"
git restore <file>

# "my push was rejected"
git pull   # then push again

# "I'm in a merge from hell, eject"
git merge --abort

# "I deleted something important"
git reflog   # find it, then: git reset --hard HEAD@{n}

# "stash my mess, I need a clean desk for 5 minutes"
git stash    # later: git stash pop

That's the survival kit. The deeper truth about Git: it almost never loses your work — it just files it somewhere you haven't learned to look yet. Every scary message on this page has a two-line exit. You now know all of them.