Skip to content
  • Home
  • How To
  • Module 0 - Foundations
  • M1
  • Agents
  • M2
  • M3
  • M4
  • M5
  • M6
  • Progress
  • Contact
  • Pricing
  • Sign in
  • Future
  1. Module 0M0
  2. ›
    S3Version Control with Git
  3. ›
    Lesson 1 of 4Why Version Control Exists and How a Repository Works
  4. ›
    S3›Lesson 1 of 4
  1. Module 0
  2. ›
  3. Section 3
  4. ›
  5. Lesson 1

Why Version Control Exists and How a Repository Works

FREE·~1.5 h·2 labs·4 questions·1 assignment


Context

A repository is a project directory that keeps a record of its own past. You choose which changes to record, you record them as a snapshot with a message explaining what happened, and the snapshot stays available afterward no matter what you do to the live files.

The reason this matters in machine learning is that results are tied to code. An experiment that scored well two weeks ago was produced by a specific version of a training script, and without a record you cannot get that version back or say what made it different from the one that scored worse. It is also the end of directories full of files named train_final.py, train_final_v2.py and train_final_actual.py, which is the manual and unreliable version of the same idea.

Without it, undo stops at whatever your editor still has in memory, and a bad afternoon of edits is unrecoverable. The repository is an ordinary directory of the kind Section 1 taught you to navigate, with one hidden subdirectory holding the history, so nothing here replaces what you already know about files.


Goal

  • Initialize a repository in a project folder and confirm it is now tracked.
  • Distinguish your working tree (the live files you edit) from the repository (the recorded history), and prove they are separate.
  • Stage exactly the changes you choose into the staging area, leaving others out on purpose.
  • Create a commit (a saved snapshot) with a meaningful message, and read it back from the recorded history.
  • Trace a single change through the three states (working tree → staging area → commit) and explain what each step did.

Prerequisites

Neither resource below is required, the lesson stands on its own. They are here if you would rather see the picture before you build it.

  1. YouTube - "Git Explained in 100 Seconds" (Fireship). A fast visual mental model of repository, staging and commit before you touch a command.
  2. YouTube - "Git and GitHub for Beginners - Crash Course" (freeCodeCamp.org), the first seven minutes only. The "what is version control, what is a commit" portion. Stop before the GitHub section at 7:05, which is Lesson 0.3.4.

Table of Contents

  • Version Control
  • Repository
  • Working Tree vs. Repository
  • Staging Area
  • Commit

Theory

INSTALL AND CONFIGURE GIT FIRST. Every command in this lesson is bash, run in your WSL/Ubuntu terminal (Windows) or your native terminal (Linux/macOS).

# 'git --version' prints the installed version, or 'command not found' if Git is absent.
git --version                            # git version 2.43.0 on Ubuntu 24.04, every output in this section came from it

# ONLY if that errored. 'sudo' runs as administrator, 'apt' is Ubuntu's package installer.
sudo apt update && sudo apt install git  # once per machine

The user.email should be the same as the verified email that you will use to sign up on GitHub.

# Every commit is stamped with an author, and Git refuses to commit until it knows who.
# '--global' writes the setting for every repository on this machine, not just one.
git config --global user.name "goku"  # the name stamped on your commits
# the email stamped on your commits
git config --global user.email "goku@gmail.com"
# 'init.defaultBranch' names the branch that every FUTURE repository starts on.
git config --global init.defaultBranch main  # new repositories only, renames nothing

A branch is the name Git gives the line of commits a repository records along, one name that always points at the newest commit (the mechanism is Lesson 0.3.3, here you only need the name). Git's own default for that first branch is master, while GitHub and every tool, script and tutorial written in recent years assume main, and the two are plain text that nothing reconciles for you. On a machine without this setting, git init still names the branch master and prints a hint listing main among the names commonly chosen instead, and every output quoted in Section 3 reads main. The setting is read at creation time, so it decides the name in every repository you make from now on and touches none that already exist.

With Git installed and configured, the rest of the lesson is about a single, durable idea: turning an ordinary folder into one whose entire history of changes is recorded, so that no edit you make is ever truly permanent.


Version Control

What. Version control is a system that records the full history of changes to a set of files over time. Instead of keeping only the current state of your files, it keeps every recorded state you saved along the way, each labeled with who changed what, when, and why.

Why. Without it, the only version of a file that exists is the one on disk right now. Make a bad edit, save, and the previous good version is gone. People cope by copying files before editing them, and the copies pile up, lose their order, and record none of the reasons anything changed. For code, and especially for ML work, where a single changed line in a training script quietly changes a model, being able to ask "what did this file look like before I broke it?" and get an exact answer is what everything else in this section rests on.

When. Use version control for any project you will edit more than once: scripts, notebooks, configuration, written work. The moment a folder holds something you would be upset to lose or to silently break, it belongs under version control. In this bootcamp, that is any project you build from now on and want to keep.

# THE MANUAL WAY, so you can see exactly what it fails to record.
# a scratch folder
cd ~ && mkdir messy-experiments && cd messy-experiments
echo "lr = 0.1" > train.py   # a training script with one setting in it
cp train.py train_backup.py  # 'cp' copies it, "just in case"
echo "lr = 0.01" > train.py  # edit the live file
cp train.py train_v2.py      # another copy, another name

# 'ls' lists the folder. Three files, and nothing says which one trained the good model.
ls                           # train_backup.py  train.py  train_v2.py

# 'cat' prints each one. The contents are there, the ORDER and the REASON are not.
cat train_backup.py          # lr = 0.1
cat train_v2.py              # lr = 0.01

The only record that train_v2.py came after train_backup.py is the times the system stamps on each file, and ls -l shows only the modified one, to the minute, so copies made seconds apart look simultaneous, and the next edit to either file replaces it. Nothing records that the change was to the learning rate, or why it was made, and a week from now ls tells you nothing you did not have to remember. Version control records all three, and from the next subtitle on it does so in a hidden folder rather than in filenames.

# Clean the scratch folder away. The next subtitle starts from a fresh directory.
cd ~ && rm -rf messy-experiments  # 'rm -rf' removes the folder and everything in it

DON'T:

  • DON'T equate "I saved the file" with "I have its history". Saving overwrites the previous content, version control is what preserves it.
  • DON'T rely on copy-named backups (_final, _v2, _old) for real work. They lose the order and the reasons, which is the exact problem version control exists to remove.

Repository

What. A repository (or repo) is a project directory whose history is tracked. Concretely, it is an ordinary folder that contains a hidden subfolder named .git, where Git stores every recorded snapshot and all of its bookkeeping. The folder is your project, the .git inside it is the history.

Why. Tracking has to start somewhere, and the repository is that boundary: Git watches the files inside this folder and ignores everything outside it. A plain directory becomes a single tracked unit the moment it becomes a repository, and every commit, every comparison and every undo you will ever do is scoped to one repository. Drawing that boundary is what lets Git answer "what changed?", because it knows precisely which files are its concern.

When. You create a repository once, at the start of a project, in the folder that holds or will hold that project's files. You do not nest one repository inside another, and you do not turn your entire home directory into one. A repository wraps one project.

# Make a fresh project folder and step into it.
mkdir ml-project && cd ml-project  # 'mkdir' creates it, 'cd' enters it

# 'git init' turns THIS folder into a repository by creating the hidden '.git' folder.
git init                           # Initialized empty Git repository in /home/goku/ml-project/.git/

# 'ls -a' shows ALL entries, hidden ones included (names starting with '.').
ls -a                              # .  ..  .git

.git is the marker that this directory's history is being tracked, and you will essentially never edit anything inside it by hand. Delete it and the folder reverts to an untracked, ordinary directory with its files intact and its history gone.

# The boundary is real. One level up, Git has nothing to say.
cd ~ && git status  # fatal: not a git repository (or any of the parent directories): .git
cd ml-project       # back inside the boundary, every git command works again

DON'T:

  • DON'T run git init in your home directory or some huge shared folder. A repository should wrap exactly one project, and tracking your whole home folder by accident tracks every download, cache and log you own.
  • DON'T edit or delete files inside .git. That folder is the history, and tampering with it can corrupt every snapshot you have saved.

Working Tree vs. Repository

What. Your working tree is the set of live files in the project folder as they exist right now, the ones you open, edit and save. The repository (the history in .git) is the set of recorded snapshots of those files. The working tree is the present and editable, the repository is the recorded past, fixed.

Why. This split is the conceptual heart of the whole lesson and the source of most early confusion. Editing a file changes your working tree immediately, but it does not change the repository, and the recorded history is untouched until you explicitly tell Git to record a new snapshot. That gap is a feature: you can make, break and throw away changes freely, and the last good recorded state stays safe the entire time. git status is the command that constantly shows you the difference between the two.

When. You think in terms of the working tree every time you edit and save a file, that is ordinary work. You think in terms of the repository whenever you ask "what state is actually recorded?", before saving a snapshot, after an experiment you want to discard, or any time you want to know how far your live files have drifted from the last recorded version.

# Create a live file in the WORKING TREE. This does not touch history.
echo "rows,label" > dataset.csv  # 'echo ... >' writes one line into 'dataset.csv'

# 'git status' compares the working tree against the recorded history.
git status
# On branch main
# No commits yet
# Untracked files:
# ...
#         dataset.csv
# nothing added to commit but untracked files present (use "git add" to track)

dataset.csv is fully present in your folder and completely absent from the recorded history, and Git is not tracking it at all, which is what untracked means: a file stays untracked until git add puts it in the staging area (the next subtitle), the list of files Git follows. No commits yet says the history is still empty. The status report is not an error, it is the gap between the two sides made visible.

# Prove the two sides are independent: destroy the live file, then ask again.
rm dataset.csv  # gone from the working tree
git status      # No commits yet and nothing to commit, the history never knew it existed

# Put it back. The next subtitle needs it.
echo "rows,label" > dataset.csv

DON'T:

  • DON'T assume editing or saving a file changes the repository. It changes your working tree only, and the recorded history does not move until you commit (two subtitles down).
  • DON'T read "untracked" or a long git status as a problem. It is the report of how your live files currently differ from what is recorded, and a new file is always untracked until you say otherwise.

Staging Area

What. The staging area (also called the index) is a holding zone for exactly what the next commit will include. Before you record a snapshot, you place specific changes into the staging area with git add, and the commit that follows captures only what is staged, nothing more, nothing less.

Why. A commit should be a meaningful, focused snapshot, but at any moment your working tree may hold several unrelated changes, a fix to one file and a half-finished experiment in another. The staging area lets you choose which of those changes belong together in the next snapshot, instead of being forced to record everything at once. A change starts in the working tree, you promote the ones you want into the staging area, and only then do they become part of a commit. The deliberate gap between "edited" and "staged" is what gives you control over the contents of your history.

When. You stage files the moment you have a set of changes you want to record together as one logical snapshot. You deliberately leave other changes unstaged when they belong to a different snapshot, for example staging a finished data file while leaving a still-broken script alone.

# 'git add' moves a change from the WORKING TREE into the STAGING AREA.
git add dataset.csv  # queue 'dataset.csv' for the next commit

# git status now lists the file under Changes to be committed: staged, not yet recorded.
git status
# ...
# No commits yet
# Changes to be committed:
# ...
#         new file:   dataset.csv
# Make a SECOND file but stage ONLY the first. You decide what the snapshot holds.
echo "print('train')" > train.py  # a second live file in the working tree
git status
# ...
# Changes to be committed:
# ...
#         new file:   dataset.csv      <- will be in the next commit
# Untracked files:
# ...
#         train.py                     <- will NOT be in the next commit

dataset.csv is staged and train.py is left out, so the next commit will contain the data file and not the script. That selective control, recording some changes now and others later, is the entire reason the staging area exists between your live files and your history.

DON'T:

  • DON'T treat git add as saving. It only selects a change for the next commit, and staging without committing records nothing in history.
  • DON'T blindly stage everything with git add . when your working tree holds unrelated changes. Stage what belongs together, so each commit is one coherent snapshot.

Commit

What. A commit is a saved snapshot of everything in the staging area, stored permanently in the repository's history with a message describing it, stamped with your author identity and a timestamp. It is the unit of recorded history, and each commit is one fixed point you can always return to.

Why. This is the payoff of the lesson. A change you made in the working tree and selected in the staging area becomes a permanent, named point in your repository, and once committed, that snapshot does not change when you later edit your files. The message is what turns a pile of snapshots into a readable story: months later, "Add training data and split labels" tells you why a state exists, which raw file contents never could.

When. You commit when the staged changes form a complete, coherent unit of work, a feature added, a bug fixed, a dataset prepared. Commit often enough that each snapshot is small and meaningful, a commit per logical change is the habit to build, not one giant commit at the end.

# 'git commit' records the STAGED changes as a snapshot. '-m' gives the message.
git commit -m "Add dataset with header row"
# [main (root-commit) 3e1f2a7] Add dataset with header row   <- the id is yours
#  1 file changed, 1 insertion(+)
#  create mode 100644 dataset.csv

# 'git log' reads the recorded history back, newest first. '--oneline' compacts it.
git log --oneline  # 3e1f2a7 (HEAD -> main) Add dataset with header row

The first line names the new commit by a short id, a hexadecimal label Git computes from the snapshot together with its author, time and message, so yours differs from the one printed here and every id in this section is a stand-in for your own. root-commit marks it as the first commit in the history, and the history now holds exactly one recorded point. train.py, which you never staged, is not part of it.

# Record the script as its OWN snapshot, then check that the two sides agree.
git add train.py
git commit -m "Add training script"  # [main 9b4c0d2] Add training script
git status                           # nothing to commit, working tree clean

# Prove a snapshot is frozen: edit the live file, and the commit is unchanged.
echo "1,cat" >> dataset.csv          # '>>' APPENDS a line to the working-tree file
git status                           # Changes not staged for commit: then modified:   dataset.csv
git log --oneline                    # still two commits, the append recorded nothing

Clean does not mean empty, it means identical. working tree clean says every tracked file matches its committed version, and the append reopens the gap without moving a single commit, because the live file drifted away from the recorded version while the record stayed put. Committing again would capture the new state as a third snapshot. History grows, old snapshots stay.

DON'T:

  • DON'T write empty or useless messages like "fix", "stuff", or "asdf". The message is the whole point of a readable history, so describe what changed and why in a short, clear line.
  • DON'T expect a commit to include unstaged changes. A commit captures only the staging area at that instant, and anything you forgot to git add is left out of that snapshot.

Additional - the three states, in one line each

Hold the whole lesson as a single pipeline a change flows through: working tree (you edit a live file) → git add → staging area (you select it for the next snapshot) → git commit → repository (it becomes a permanent, named snapshot). git status shows you where every change currently sits in that pipeline, and git log reads back the snapshots already recorded. Every Git command you learn later plugs into this same three-state picture.


Lab Exercises

Lab Exercise 1: Turn a Folder Into a Repository and Find the Boundary

Create a new project folder, step into it, and initialize it as a repository. Confirm, using a command that reveals hidden entries, that the .git folder now exists. Then create a data file in the working tree and show, with the status command, that it is present in your live files and absent from history. Finally, step one directory up and run the same status command. In comments, state what git init created, what "untracked" means, and why the last command fails.

Lab Exercise 2: Walk One File Through All Three States

Inside the repository from Exercise 1, create a second file, a training script. Stage only the data file and check status. Then commit it and check status a second time, and read the history to confirm the snapshot exists. In comments, name the state each of the two files was in at each check, and say precisely which command put the data file into history.


Solution 1: Turn a Folder Into a Repository and Find the Boundary
# Create the project folder and enter it.
cd ~ && mkdir model-lab && cd model-lab  # 'mkdir' makes it, 'cd' moves into it

# 'git init' converts it into a repository by creating the hidden '.git' folder.
git init                                 # Initialized empty Git repository in /home/goku/model-lab/.git/
ls -a                                    # .  ..  .git, the marker that history is now being tracked

# A live file in the WORKING TREE. History has no record of it yet.
echo "alpha,beta" > features.csv         # a data file, present on disk only
git status                               # No commits yet and Untracked files:  features.csv

# One level up is OUTSIDE the boundary '.git' drew.
cd .. && git status                      # fatal: not a git repository (or any of the parent directories): .git
cd model-lab                             # back inside, for Exercise 2

"Untracked" means the file sits in the working tree but is not among the files the next commit will include. A staged file joins them the moment you stage it, even before its first commit, and a committed file stays among them until you remove it from Git. The git status one level up fails because Git looks for .git in the current directory and every parent, finds none above model-lab, and correctly reports that nothing here is tracked.

DON'T:

  • DON'T run git init twice expecting a second repository. It re-initializes the same one, and one folder holds one repository.
  • DON'T read "untracked" as broken. It accurately means "Git is not following this live file yet", the normal state of any new file.
Solution 2: Walk One File Through All Three States
# A second live file. Both files are now in the working tree, neither in history.
echo "print('fit')" > train.py

# Stage ONLY the data file, then check.
git add features.csv                 # WORKING TREE -> STAGING AREA
git status
# Changes to be committed:  new file:   features.csv   <- staged
# Untracked files:          train.py                   <- still only in the working tree

# Record the snapshot, then look again.
git commit -m "Add feature columns"  # STAGING AREA -> REPOSITORY
git status                           # Untracked files:  train.py, and 'features.csv' no longer listed at all
git log --oneline                    # c7d2e10 (HEAD -> main) Add feature columns, the snapshot exists

At the first check features.csv is staged and train.py is untracked. At the second, features.csv has vanished from the report because its working-tree copy and its committed copy are identical, while train.py is still untracked, exactly where it was. git commit is the only command of the three that wrote to history.

DON'T:

  • DON'T think git add recorded the file. It only staged it, and history contained nothing until the commit.
  • DON'T expect the second file to appear in the commit because it was sitting in the same folder. Only what was staged went in.

Theory Assessment

Conceptual reinforcement, not a coding section. All four questions with their hints first, then all four answers.

Q1: What it records, and what makes a folder one

State what version control records, and why manually copying files to train_v2.py and train_final.py is a poor substitute. Then explain what git init does to an ordinary directory, and what hidden item proves the folder is now tracked.

Hint: Ask what the copies fail to preserve besides the file contents themselves. For the second half, list the directory in a way that shows everything.

Q2: Live files, recorded snapshots, and the room between them

Define the working tree and the repository, and explain why editing a file does not, by itself, change the recorded history. Then explain what git add does, and why the staging area sits between editing and committing rather than being skipped.

Hint: One of the two is what you open and save, the other is what .git holds. For the second half, ask what you would lose if every edit in your working tree had to go into the same commit.

Q3: The full journey, and the label on the box

Name the three states a change passes through, in order, and the command that moves it between each. Then explain what git commit -m "..." actually records, and why the message matters more than it first appears.

Hint: Nothing jumps straight from live file to history, there is a deliberate middle. For the message, imagine reading your own log six months from now.

Q4: The two things Git will not do for you

You edit two files, git add only one, then commit. Explain why the second file is absent from that commit, and name a tool that would show you the change you left out. Then: some files, logs and large checkpoints and secret keys, should never enter history at all. Explain why, and name the mechanism that keeps them out permanently. Say what these two situations have in common. git diff shows tracked changes not yet staged or committed, and a .gitignore file lists path patterns Git should never track.

Hint: A commit captures the staging area exactly, no more and no less. For the second half, ask what happens to a secret once it is in history, even after you delete the file.


Answer 1:

Version control records the full history of changes to a set of files, every recorded state, with who changed what, when and why, rather than just the single current version on disk. Manual copies are a poor substitute because they clutter the folder, lose the order and relationship between versions, are easy to mislabel or overwrite, and capture none of the reasons a change was made.

git init turns an ordinary directory into a repository by creating a hidden subfolder named .git inside it. That folder holds every snapshot and all of Git's bookkeeping, so its presence is the proof the directory is tracked, and ls -a reveals it. The folder full of your files is the project, and the .git within it is the recorded history.

DON'T confuse saving a file with preserving its history, saving overwrites the previous content. DON'T assume your project files live inside .git, they stay in the working tree and .git is Git's own storage.

Answer 2:

The working tree is your set of live files as they exist right now, what you open, edit and save. The repository is the collection of recorded snapshots of those files, stored in .git. Editing changes only the working tree, and the repository stays exactly as it was until you explicitly record a new snapshot. Saving updates the present, not the recorded past.

git add moves a specific change from the working tree into the staging area, the holding zone for exactly what the next commit will contain. It sits in between because at any moment your working tree may hold several unrelated changes, and staging lets you choose which belong together. Without it you would be forced to record everything at once instead of building focused commits.

DON'T assume Git silently records edits as you save, the gap is intentional and it is what keeps your last recorded state safe. DON'T treat git add as a save, it only selects.

Answer 3:

The three states in order are working tree → staging area → repository. git add promotes a change from the first to the second, and git commit records the staged changes as a permanent snapshot in the third. git status shows where a change currently sits in that pipeline, and git log reads back what has already landed.

git commit -m "..." records everything currently staged as a permanent snapshot, labeled with your message and stamped with your identity and a timestamp. The message matters far more than it first appears because it is what turns a stack of snapshots into a readable history. Months later, "Add cleaned dataset" tells you why that state exists, which the raw file contents never could.

DON'T skip the staging step in your mental model. DON'T write throwaway messages like "fix" or "stuff", they make git log useless exactly when you most need it.

Answer 4:

A commit captures only what is in the staging area at the instant you run it. The second file was never git add-ed, so it was not in the staging area and is absent from the commit, still sitting in your working tree. git status lists it as unstaged, and git diff shows the actual content you left out.

Logs and large checkpoints bloat the repository and slow everything down, and secrets must never be recorded where they could leak. The mechanism is a .gitignore file listing path patterns Git will refuse to track, so they never even appear as untracked clutter.

What the two share is that Git does exactly what it was told and nothing more. It will not helpfully sweep in a nearby edit you forgot to stage, and it will not helpfully notice that a file looks like a secret. Both outcomes are decisions you make in advance, and Git's refusal to guess is the same property in both cases. The asymmetry is in the cost of being wrong: a forgotten git add is fixed by another commit, while a committed secret is in the history permanently, and deleting the file afterwards does not remove it from the snapshots that already recorded it.

DON'T assume Git includes nearby edits you forgot to stage. DON'T assume deleting a committed secret erases it, every earlier commit still contains it.


Assignment

Assignment: Selective Staging With an Ignored File

In a fresh repository, create three files: a data file you will commit, a script you will commit in a separate, later commit, and a log file that should never be tracked. Use an ignore mechanism so the log file does not even appear as untracked. Then make two focused commits, one for the data file and one for the script together with the ignore rule, verifying with status before each commit that only the intended files are staged, and afterwards that the log file is still invisible to status. Show the final history. In comments, explain why selective staging and ignoring produce a cleaner history than committing everything at once.

Hints:

  • Put a pattern like *.log into a file named .gitignore with echo "*.log" > .gitignore before you check status, so the log file is ignored from the start. The .gitignore file is itself a file in the working tree, so it shows up as untracked until you git add it.
  • Stage and commit the data file first, then stage and commit the script and .gitignore second, and check git status before each commit to confirm exactly what is queued.

Solution:
# '-q' keeps init quiet
cd ~ && mkdir clean-history && cd clean-history && git init -q

echo "rows,label" > dataset.csv         # to commit first
echo "print('train')" > train.py        # to commit second
echo "epoch 1 done" > run.log           # must never enter history

# Ignore the log BEFORE checking status, so it never appears as untracked.
echo "*.log" > .gitignore               # 'git status' and 'git add .' now pass over untracked '.log' files

# Status lists the three real files and NOT 'run.log'.
git status                              # Untracked files:  .gitignore  dataset.csv  train.py

# FIRST focused commit: the data file only.
git add dataset.csv
git status                              # Changes to be committed:  new file:   dataset.csv, exactly one file queued
git commit -q -m "Add cleaned dataset"  # '-q' prints nothing on success

# SECOND focused commit: the script and the ignore rule.
git add train.py .gitignore
git status                              # new file:   .gitignore and new file:   train.py queued, 'run.log' still absent
git commit -q -m "Add training script and ignore logs"

# Final history: two focused snapshots, and a clean tree with the log still on disk.
git log --oneline
# 8a2f6b1 (HEAD -> main) Add training script and ignore logs
# 5c9d3e4 Add cleaned dataset

git status                              # nothing to commit, working tree clean
ls                                      # dataset.csv  run.log  train.py, the log is on disk, 'git status' leaves it out, and 'git add run.log' is refused by name

Each commit tells a single story, so git log reads as "the data arrived, then the script arrived" instead of one undifferentiated blob that mixed the dataset, the script and a log line nobody will ever want back. The ignore rule makes the exclusion permanent and automatic rather than something you have to remember at every git add.

DON'T rely on memory to keep run.log out. Without the .gitignore rule, a single git add . would sweep the log straight into a commit, and the ignore file is what makes the exclusion hold every time.


Additional Resources

Optional, and nothing later depends on either. Worth a look only if this lesson left you curious.

  1. YouTube - "Git Tutorial for Beginners: Learn Git in 1 Hour" (Programming with Mosh). A thorough, beginner-paced walk through init, add, commit and status that reinforces this lesson's three states with worked examples.
  2. Pro Git book (Scott Chacon and Ben Straub) - Chapter 2, "Recording Changes to the Repository". The canonical, openly licensed explanation of the working tree, the staging area and commits.

Summary

Everything in this lesson reduces to one shift in how you treat a folder: from a place that holds only the latest version of your files to a place that remembers every version you chose to record. You began with the pain that makes version control necessary, the brittle pile of copied scripts that lose order, reasons and eventually trust, and replaced it with a single tracked history. A repository is nothing more exotic than an ordinary directory that has grown a hidden .git folder and, with it, a memory. Once that boundary is drawn, Git can answer the question that matters most when something breaks: what did this look like before?

The conceptual core, and the part worth slowing down on, is the separation between your working tree and your repository, bridged by the staging area. Your live files are the editable present, the recorded snapshots are the fixed past, and the staging area is the deliberate space in between where you decide what crosses from one into the other. That three-state pipeline, edit, stage, commit, is not Git being fussy. It is what hands you control over the quality of your own history: focused snapshots, each with a message that explains itself, instead of an undifferentiated dump of everything at once. git status is your constant readout of where each change sits in that flow, and git log is the recorded story you can always read back.

From here on, this is how your projects can exist. Anything you commit inside one of these tracked folders, a script, a dataset, an experiment, is covered, which means a bad change is never permanent and a working version is never more than one commit away from being safe. You have not just learned a few commands, you have learned the habit that lets you take real risks with your code, knowing the ground beneath you is recorded and recoverable.


Cheat Sheet

Part A - Reference Table

Concept What it is Command / marker Notes
Version control recorded full history of file changes git --version replaces manual _v2/_final copies
Repository a directory whose history is tracked git init → .git/ wraps exactly one project
Working tree your live, editable files right now git status the present, editing changes only this
Repository (history) the recorded snapshots in .git git log --oneline the fixed past, unchanged until you commit
Staging area holding zone for the next commit git add <file> you choose what the commit includes
Commit a saved snapshot plus a message git commit -m "..." permanent, named, timestamped point
git status where each change sits in the pipeline git status "working tree clean" = matches history

Part B - Compact Code Block

git --version                                # is Git installed?
git config --global user.name "goku"         # one-time identity
# stamped on every commit
git config --global user.email "goku@gmail.com"
git config --global init.defaultBranch main  # new repositories start on 'main'
mkdir proj && cd proj && git init            # ordinary folder -> repository ('.git')
ls -a                                        # confirm the hidden '.git' exists
echo "rows,label" > dataset.csv  # WORKING TREE: create/edit a live file
git status                       # see the gap: untracked / modified / clean
git add dataset.csv              # STAGING AREA: select it for the next commit
git commit -m "Add dataset"      # REPOSITORY: record the snapshot
git log --oneline                # read recorded history back, newest first
echo "*.log" > .gitignore        # never track files matching a pattern
←Previous: Section 2, Lesson 3
Next: Lesson 2→

Questions about this lesson: #module-0 on Discord

Found an error in this lesson? Report it

Your progress
Current streak
🔥 0
Lessons marked today
0 / 3
Longest streak
0

Sign in

The bootcamp

  • Curriculum
  • How To
  • Pricing
  • Changelog
  • Future

The community

  • Discord
  • Reviews
  • Contact

Follow

  • Instagram
  • X
  • TikTok
  • Reddit

Legal

  • Terms
  • Privacy
  • Refunds

Coming soon on November 16, 2026

Theme