---
title: 'Working Directory and Staging Area'
source: 'https://academia.sh/en/courses/introduction-to-version-control/working-directory-and-staging-area'
course: 'Introduction to Version Control'
language: en
updated: '2026-08-17T18:10:47+00:00'
license: 'CC BY-SA 4.0'
---

# 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.

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:

```markdown
# 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:

```bash
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

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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."
