Before I understood the staging area I committed everything at once
with git add .. The commits were a mess — a working change bundled
with a debug print bundled with a half-finished refactor. When I
looked back two weeks later the history told me nothing.
Git has three zones, not two.
The working tree is your filesystem — files as they exist on disk, as your editor sees them. The index (also called the staging area) is a snapshot you are assembling toward the next commit. The commit history is the permanent, append-only record. Changes move through these zones explicitly:
working tree → git add → index → git commit → historyThe index exists so you can commit exactly the changes that belong together, even if your working tree contains unrelated work in progress.
Initialise a repository
Create a directory and initialise it:
mkdir f02-practice && cd f02-practice
git initGit creates a .git/ directory — the repository itself. Everything
outside .git/ is your working tree.
Your first commit
Create a file:
echo "hello" > hello.txtCheck the state of the working tree:
git statusOutput:
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
hello.txthello.txt is in the working tree but not in the index. Stage it:
git add hello.txtgit status now shows it under "Changes to be committed". Commit:
git commit -m "add hello.txt"Write commit messages in the imperative present tense: "add", "fix", "remove", "update" — not "added" or "adding". The subject line describes what applying the commit does, not what you were doing when you wrote it.
Reading the log
git log
git log --onelinegit log --oneline is one line per commit: the short hash and the
message. As the history grows this is almost always what you want.
Seeing what changed
git diff compares the working tree to the index — what you have
changed but not yet staged. git diff --staged compares the index to
the last commit — what will go into the next commit.
echo "world" >> hello.txt
git diff # unstaged change
git add hello.txt
git diff --staged # staged change, waiting to be committed
git commit -m "append world"Before committing a change, git diff --staged is the last check.
It shows exactly what the commit will contain.
.gitignore
Some files should never be committed: compiled objects, editor swap
files, operating system metadata. Create .gitignore in the
repository root:
*.o
a.out
*.swpAny file matching a pattern in .gitignore is invisible to
git status and will never be staged by git add .. Commit
.gitignore itself — it belongs in the repository so that every
clone of the repo inherits the same rules.
Undoing changes
Discard an unstaged change — restore the working tree from the index (the file reverts to its last staged or committed state):
git restore hello.txtUnstage a file — remove it from the index without touching the working tree:
git restore --staged hello.txtUndo a committed change safely — git revert creates a new
commit that reverses an earlier one. HEAD means your current
commit, so git revert HEAD undoes the most recent commit while
keeping the history intact:
git revert HEADGit will open your editor so you can write a commit message for the revert. Save and close to complete it.
git reset rewrites history. It is powerful and occasionally the
right tool, but it is not the default undo. Treat it the way you
treat rm -rf: reach for it only when you know exactly what you
are removing.
Inspecting a commit
git show HEAD
git show <hash>git show displays the commit metadata and the diff it introduced —
the same unified diff format from The Patch page.