Lesson 04 / 15
Working Directory and Staging Area
The definition of the three-region model; the two separate writes `git add` makes to the object database and the index; verifying the blob object and content-addressed storage by eye.
Contents
The repository is set up and empty. The question of what happens when a file is
created was left open: does the file go straight into history? The answer is no. There
is a third region between the working directory and history, and this region is the
fundamental design decision that sets working with git apart from many other version
control tools.
This lesson defines that region, shows its counterpart on disk, and verifies by computation how a file’s content is named in the object database.
Three Regions
working directory staging area object database
(files) (.git/index) (.git/objects)
│ │ │
│──── git add ─────────>│ │
│ │──── git commit ─────────>│
The working directory is the editable state of the files — what the text editor sees. The staging area is the intermediate region where the content headed into the next commit is gathered. The object database is where the permanent record is kept.
The intermediate region’s existence is not a convenience, it is an authority: a commit’s content stops being at the mercy of the working directory’s current state. If five files have changed in the working directory, two of them can be staged and recorded as a separate commit. The atomic-commit habit covered in the next lesson rests on this authority.
The First Files
Two files are added to the example repository. The first is README.md, which
introduces the project:
# Sözlük Bilgisayar bilimi terimlerinin Türkçe karşılıklarını tutan küçük bir liste. ## Biçim `terimler.txt` dosyasında her satır şu biçimdedir: turkce | english
The second is the term list itself, terimler.txt:
yığıt | stack kuyruk | queue çizge | graph
The repository does not know about these files yet:
git status
On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) README.md terimler.txt nothing added to commit but untracked files present (use "git add" to track)
An untracked file is a file with no record in the staging area. The repository sees it but does not track it; it does not report even if its content changes.
git add Does Two Jobs
git add README.md
git status --short
A README.md ?? terimler.txt
In the short format, every line begins with two columns. The first column shows the
difference between the staging area and the last commit, the second column shows
the difference between the working directory and the staging area. A means
“added”, M “modified”, D “deleted”, ?? “not tracked”. Since A is in the first
column and the second column is empty: the file has been staged and matches its state
in the working directory.
The command made two separate writes. The first is to the object database:
find .git/objects -type f
.git/objects/49/daf9067744c72d7f5427d163f983c326ff37b5
The file’s content was stored permanently, even though no commit has been written yet. This object is a blob: an object type that holds only a byte sequence, with no file name or permission information. Its name is computed from its content; the directory name is the hash’s first two characters, the file name is the rest.
The second write is to the staging area:
git ls-files -s
100644 49daf9067744c72d7f5427d163f983c326ff37b5 0 README.md
This line is one entry in the staging area: file mode, blob ID, stage number, and
path. File mode 100644 denotes an ordinary file, 100755 an executable one. The
stage number is used in merge conflicts and is zero in the ordinary case.
The second file is added too:
git add terimler.txt git ls-files -s
100644 49daf9067744c72d7f5427d163f983c326ff37b5 0 README.md 100644 9b9ef7052d54292add1ed75823a454c1fe66e3bb 0 terimler.txt
Forms of Staging
Besides writing files one by one, there are also batch forms, and their scopes differ:
| Form | Scope |
|---|---|
git add <path> |
The given path |
git add . |
The current directory and below |
git add -A |
The entire repository, independent of the current directory |
git add -p |
Selecting changes hunk by hunk |
The difference between the first two forms becomes clear in subdirectories. Suppose a
file has been deleted at the root, and a file has been created in a subdirectory named
new. A command run from the subdirectory does not cover the deletion:
cd new git add . git status --short
D ../terimler.txt A a.txt
The delete mark stayed in the second column: it was not staged. The -A form, run
from the root, picks up both:
git add -A git status --short
D terimler.txt A new/a.txt
The fourth form lets part of the changes in a single file be staged: the command presents each hunk of the diff in turn and asks whether to stage it. The selection authority the staging area grants here drops from the file level to the line level; if two independent fixes were made in one file, they can be split into separate commits.
The Staging Area Is a File
The staging area is not an abstract concept; it is a binary file called .git/index —
the index. It holds one entry for every tracked path: the path name, the mode, the
blob ID, and a few cache fields read from the file system.
These last fields determine how fast git status runs. Instead of rehashing the
content of every file in the working directory, the file’s size and modification time
are compared against the record in the staging area; if both match, the file is
treated as unchanged. As the record count grows, this cache’s value grows with it.
Notice that what the staging area holds is a sorted file list: it says which path is bound to which content. This structure is the direct precursor of the tree object covered in the next lesson.
Where the ID Comes From
A blob’s ID is not the raw hash of the file’s content. A header stating the object’s type and length is prepended to the object, and the hash is computed over that whole. The header’s format is: the object type, a space, the length in bytes, and a zero byte.
This computation can be done from outside. Take an eight-byte piece of content:
printf 'merhaba\n' | git hash-object --stdin
e995d8e1c89654fd0a6453c2da61fb6e9da262e6
The same value can also be obtained by adding the header by hand and using a general-purpose hashing tool:
printf 'blob 8\0merhaba\n' | shasum
e995d8e1c89654fd0a6453c2da61fb6e9da262e6 -
The two values match. The shasum command produces the SHA-1 digest; on the GNU
toolset, the same job is done with sha1sum.
This computation has two consequences. First, even if the same content is stored under two different names, it produces a single object; the ID depends on the content, not the file name. Second, if a single byte of the object changes, its ID changes beyond recognition — the avalanche effect defined in the Data Structures course.
The object’s type and size can be queried directly:
git cat-file -t 49daf9067744c72d7f5427d163f983c326ff37b5 git cat-file -s 49daf9067744c72d7f5427d163f983c326ff37b5
blob 187
This ID belongs to README.md’s content at that moment; unless you write the file
letter for letter the same in your own repository, you will see a different value.
Every object ID shown throughout the course was produced by this same rule.
Summary
- The working directory holds the files being edited, the staging area holds the content headed into the next commit, the object database holds the permanent records.
git adddoes two jobs: it writes the content into the object database as a blob, and it places an entry in the staging area binding the path to the blob ID.- The staging area is the
.git/indexfile; it holds entries made up of path, mode, and blob ID. - A blob’s ID is the hash of the content with a type-and-length header prepended; if the content is the same, the ID is the same.
- In the short status display, the first column reports the staging area, the second the working directory.
Next Step
The staging area now has two entries, but history is still empty; running git log
would get no response. The next lesson turns the staging area’s content into a
permanent record, opens up and examines the tree and commit objects created in the
process, and defines when a commit counts as “a single piece of work.”
To keep your progress and take notes, Log in
My notes
Log in to take notes.