Three places your changes live
Recall from lesson 2-1 that pancakes.txt is sitting in the repository as an untracked file. Getting it into a commit means passing through Git's most misunderstood idea, the staging area.
Every Git repository has three zones:
- The working directory: the ordinary files you see and edit. Your scratchpad.
- The staging area, also called the index: the list of exactly what will go into the next commit. Nothing enters a commit without being staged first, so it works as a waiting room between editing and saving.
- The repository: the
.gitdatabase of permanent snapshots from lesson 1-2.
The obvious question is why the middle step exists at all, since a tool that simply saved everything would be simpler to explain. The answer is that you rarely want to save everything you touched. A typical afternoon leaves a real bug fix in one file, a half-finished experiment in another, and a debugging print statement in a third.
Staging lets you pick out the bug fix, commit it on its own with an honest message, and leave the rest for later. That produces a history where each commit does one comprehensible thing, which is what makes the history worth reading months afterwards.
git add in action
git add <file> puts a file's current contents into the staging area. Watch how git status changes:
$ git add pancakes.txt
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: pancakes.txtpancakes.txt moved from "Untracked files" to "Changes to be committed". That phrase is the staging area's real meaning: the list of changes the next commit will contain.
Useful forms:
git add pancakes.txt waffles.txtstages several filesgit add .stages everything changed in the current folder and below (convenient, but checkgit statusfirst so junk doesn't sneak in)
Staging copies contents, it does not link the file
git add copies the file's contents as they are at that exact moment into the staging area. It does not set up a live connection to the file.
That single rule explains the moment that confuses every beginner. Edit pancakes.txt again after staging it and git status lists the file in two places at once, under "Changes to be committed" for the older staged version and under "Changes not staged for commit" for the newer edit.
Committing at that point saves only the staged version, and the newer edit stays in the working directory, still uncommitted. Getting the latest contents into the commit means running git add pancakes.txt again, which overwrites the staged copy.
Nothing is broken when a file appears twice in git status. The report is simply telling you that the file was edited after it was staged, and the two entries describe two genuinely different versions of it.
A recorded session
This session walks the add flow. The repo already contains pancakes.txt, but Git isn't tracking it yet.
Each step below shows the command and the output it printed.
Step 1. Check what Git sees.
~/recipe-book $ git status On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) pancakes.txt nothing added to commit but untracked files present (use "git add" to track)
Step 2. Stage pancakes.txt.
~/recipe-book $ git add pancakes.txt
The same thing can be written as git add ..
Step 3. Check status again, the file should now be waiting in the staging area.
~/recipe-book $ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: pancakes.txtgit add pancakes.txt copies the file's current contents into the staging area, marking it for inclusion in the next commit.
The verb to attach to add is stage, not save. Nothing enters the history until git commit runs, so a staged file is still one command away from being permanent.
The word current carries the other half of the meaning. The command captures the contents as they are at that instant, so editing the file afterwards leaves the new work unstaged until another git add picks it up.
The commit contains the version of the file as it was when git add ran, and the extra line stays in the working directory, uncommitted.
The sequence explains itself once staging is understood as a copy. git add cake.txt copied the contents at that moment into the staging area, the later edit changed only the working directory, and git commit saved exactly what was staged.
Nothing was lost. The extra line is still in the file on disk and git status will keep reporting it as an unstaged change until it is either staged and committed or deliberately discarded. Running git add cake.txt a second time before committing would have included it.
With edits in both soup.txt and TODO.txt but only the soup change belonging in the next commit, the command is git add soup.txt.
Naming the file explicitly stages only that file. TODO.txt remains an unstaged change in the working directory, so the next commit contains the soup change and nothing else.
This selective picking is the entire reason the staging area exists. The tempting alternative, git add ., would stage both files and produce a commit whose message could only be half true, which is exactly the habit that makes a history unreadable later.