Skip to content
academia.sh

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 add does 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/index file; 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.

Start typing to search.

↑↓ Esc navigate · open · close