Lesson 98 of 12115 min read

How git add Works Internally: Packing Objects, Writing to the Index

See exactly what git add does under the hood: creating a blob object and updating the index file to reference it.

Author: CodersNexus

How git add Works Internally: Packing Objects, Writing to the Index

With the object model from the previous lesson established, this lesson connects it directly to a command you've used constantly since Module 1: `git add`. Understanding exactly what happens internally when you stage a file transforms `git add` from a memorized command into a fully understood mechanism.

Learning Objectives

  • Explain the two internal steps git add performs: creating a blob and updating the index.
  • Use git hash-object to see exactly how a blob's hash is computed before staging.
  • Understand that staging a file writes a new object to .git/objects immediately, before any commit occurs.
  • Recognize why staging the same content twice doesn't create duplicate objects.

Key Terms to Know Before Learning How git add Works Internally

  • git hash-object: A plumbing command that computes and optionally writes the blob hash for a given piece of content, exactly what git add uses internally.
  • Blob creation: The first internal step of git add — computing a new file's content hash and writing it as a blob object into .git/objects.
  • Index update: The second internal step of git add — recording, in the index file, that this specific blob hash is now associated with this filename, marking it staged.

How git add Actually Works Under the Hood

When you run `git add <file>`, two distinct internal operations happen in sequence, directly building on the previous lesson's object model:

**Step 1 — Blob creation.** Git computes the SHA hash of the file's exact current content and, if an object with that exact hash doesn't already exist in `.git/objects` (recall content-addressing: identical content always produces the identical hash), writes a new, compressed blob object there. This happens **immediately upon staging** — notably, *before* any commit is ever made. This is a subtle but important detail: your file's content is already safely stored as a real object in Git's database the moment you stage it, even if you never actually commit it.

You can perform this exact same blob-creation step manually, completely independent of `git add`, using the lower-level plumbing command `git hash-object`:

```
git hash-object -w app.js
```

The `-w` flag ('write') tells it to actually write the resulting blob into `.git/objects` (without `-w`, it just computes and prints the hash without storing anything) — this is literally the exact mechanism `git add` uses internally.

**Step 2 — Index update.** Once the blob object exists, Git updates the `index` file (from the previous lesson's `.git` tour) to record that this specific filename is now associated with this specific blob hash, formally marking it as staged. This is what `git status` and `git diff --staged` actually read from to report what's currently staged — the index is quite literally a list of filename-to-blob-hash mappings representing the next commit's proposed tree.

This two-step process directly explains a detail from Module 1's three-states lesson: staging is a **snapshot at a specific moment**. If you edit the file again after staging, that new content produces a **different** blob hash — but the index still points at the *original* blob hash from when you staged it. This is exactly why, as covered in Module 1's `git add` lesson, further edits after staging require running `git add` again: doing so creates a brand-new blob for the new content and updates the index to point at that new hash instead.

An elegant consequence of content-addressing, worth calling back to directly: if you stage a file, then later stage an entirely different, unrelated file that happens to have byte-for-byte identical content (or even the exact same file staged again with unchanged content after some other operation), **no new blob object is created at all** — Git recognizes the hash already exists in `objects/` and simply reuses it, updating only the index's filename-to-hash mapping. Staging is remarkably cheap and storage-efficient specifically because of this content-addressed reuse.

git add Internals: Visual Walkthrough

Draw a two-step flow. Step 1 labeled 'Blob Creation': a file icon 'app.js' with content 'console.log(1)' → arrow labeled 'git add app.js (internally: git hash-object -w)' → a new blob object appears in '.git/objects/' with hash 'a1b2c3d'. Step 2 labeled 'Index Update': an arrow from the new blob to the 'index' file, showing a new/updated entry: 'app.js -> a1b2c3d (staged)'. Add a note beneath: 'If app.js is edited AGAIN after this, a NEW blob with a DIFFERENT hash would be needed — the index still points at a1b2c3d until git add is run again.'

git add Internal Steps: Quick Reference Table

StepWhat HappensCommand Performing This Internally
1. Blob creationComputes content's SHA hash; writes a new blob object if one doesn't already existgit hash-object -w (used internally by git add)
2. Index updateRecords the filename-to-blob-hash mapping, marking it stagedgit update-index (used internally by git add)

git add Internals: Command Syntax and Examples

# Manually perform the exact same blob-creation step git add uses internally
echo "console.log('hello');" > app.js
git hash-object app.js
# a1b2c3d4e5f6...   (computed hash, NOT yet written to .git/objects)

git hash-object -w app.js
# a1b2c3d4e5f6...   (same hash, this time ACTUALLY written to .git/objects)

# Confirm the blob object now exists
git cat-file -p a1b2c3d
# console.log('hello');

# Compare: running the normal git add and checking the index
git add app.js
git ls-files --stage
# 100644 a1b2c3d4e5f6... 0    app.js   <- exactly the same blob hash

Breaking Down the git add Internals Example

`git hash-object app.js` (without `-w`) computes the blob hash that *would* result from this content, without actually storing anything — a dry run. Adding `-w` performs the real write, creating the actual blob object in `.git/objects`, confirmed by successfully reading it back with `git cat-file -p`. The final comparison demonstrates that running the normal, everyday `git add app.js` produces the **exact same blob hash**, confirmed via `git ls-files --stage` (which reads directly from the index) — concrete proof that `git add` is, internally, doing precisely this same hash-object-then-update-index sequence, just packaged into one convenient, everyday command.

How Understanding git add Internals Helps in Real Debugging

  • Understanding that staged content is immediately written as a real object — even before committing — explains why, in some recovery scenarios, staged-but-never-committed work can sometimes still be found via advanced object inspection, though this isn't a guaranteed or convenient recovery path.
  • Git's automatic blob deduplication (a direct consequence of this staging mechanism) is part of why repositories with many similar or duplicated files across their history remain surprisingly storage-efficient.
  • Engineers building custom tooling on top of Git sometimes use git hash-object and git update-index directly (Git's lower-level 'plumbing' commands) for programmatic, scripted staging operations rather than shelling out to the higher-level git add.
  • This exact internal mechanism is a very common, genuinely revealing 'how does Git actually work' interview question for roles emphasizing deep technical fundamentals, distinguishing command memorization from real understanding.

git add Internals Interview Questions and Answers

Q1. What are the two internal steps git add performs when you stage a file?

First, it computes the file's content hash and writes a new blob object into .git/objects if one with that exact hash doesn't already exist (this is exactly what git hash-object -w does). Second, it updates the index file to record that this filename is now associated with that specific blob hash, formally marking it as staged.

Q2. Is a staged file's content actually stored in Git's object database before you commit?

Yes. The blob object is created and written to .git/objects immediately upon staging, as the very first step of what git add does internally — well before any commit is ever made. Committing later simply creates a tree and commit object referencing that already-existing blob, rather than creating the blob at that point.

Q3. Why does editing a file again after staging it require running git add a second time?

Because staging captures a snapshot: the index points at the specific blob hash that matched the file's content at the moment git add ran. Further edits produce different content with a different hash, but the index still references the original, now-outdated blob hash until git add is run again to create a new blob for the updated content and update the index accordingly.

git add Internals Quiz: Test Your Understanding

1. What is the first internal step git add performs when staging a file?

  1. Creating a commit object
  2. Computing the file's content hash and writing a new blob object if needed
  3. Updating the HEAD reference
  4. Deleting the previous version of the file

Answer: B. Computing the file's content hash and writing a new blob object if needed

Explanation: git add first computes the content's SHA hash and writes a new blob object to .git/objects (if an identical one doesn't already exist), exactly what git hash-object -w does directly.

2. What plumbing command performs the exact blob-creation step that git add uses internally?

  1. git commit
  2. git hash-object -w
  3. git log
  4. git branch

Answer: B. git hash-object -w

Explanation: git hash-object -w computes a content hash and writes the resulting blob object to .git/objects, which is precisely the mechanism git add relies on internally when staging a file.

3. Is a file's content stored as a real Git object before you ever run git commit?

  1. No, nothing is stored until commit
  2. Yes — the blob object is created and written immediately upon staging with git add
  3. Only if the file is smaller than 1KB
  4. Only for files tracked since the initial commit

Answer: B. Yes — the blob object is created and written immediately upon staging with git add

Explanation: Staging a file's blob creation happens immediately as part of git add's internal process, well before any commit — committing later just references that already-existing blob via a new tree and commit object.

Common Misunderstandings About git add Internals

  • Assuming nothing is actually stored in Git's database until a commit is made, when staging alone already writes a real blob object.
  • Not understanding why re-editing a staged file requires staging again — forgetting that the index points at a specific, now-outdated blob hash from the moment of the original git add.
  • Confusing git hash-object (a low-level plumbing command creating a blob) with git add (the everyday porcelain command that also updates the index) — related but not identical in scope.
  • Assuming staging identical content twice (in different files, or the same file at different times) wastefully creates duplicate objects, when content-addressing means it's automatically reused instead.

git add Internals: Exam-Ready Quick Notes

  • git add internally performs two steps: (1) blob creation via git hash-object -w, (2) index update recording the filename-to-blob-hash mapping.
  • Blob objects are written immediately upon staging, before any commit occurs.
  • Staging is a snapshot — further edits require re-running git add to update the index with a new blob hash.
  • Identical content is automatically deduplicated — no new blob is created if that exact hash already exists.

git add Internals: Key Takeaways

  • git add is internally just a convenient combination of blob creation (git hash-object -w) and index updating.
  • A staged file's content is already a real, permanent object in Git's database the moment it's staged, independent of whether a commit ever follows.
  • Understanding this mechanism explains, at a mechanical level, exactly why re-staging is required after further edits to an already-staged file.

Frequently Asked Questions About git add Internals

Q1. What does git add actually do internally?

It performs two steps: computing the staged file's content hash and writing a new blob object to .git/objects (if one doesn't already exist), and then updating the index file to record that the filename is now associated with that blob hash, marking it as staged.

Q2. Is my staged content actually saved anywhere before I commit?

Yes. The blob object representing your staged content's exact bytes is written to .git/objects immediately when you run git add, well before any commit — committing later just creates a tree and commit object that reference this already-existing blob.

Q3. What command performs the same blob-creation step that git add uses internally?

git hash-object -w <file> performs exactly this step directly — computing the content's hash and writing the resulting blob object into .git/objects, which is precisely what happens inside git add's own internal process.

Q4. Why do I need to run git add again after editing a file I already staged?

Because staging is a snapshot at a specific moment — the index points at the exact blob hash matching your content when you first ran git add. Further edits produce different content with a different hash, and re-running git add is what creates the new blob and updates the index to point at it instead.

Q5. Does staging the same content twice create duplicate objects in Git?

No. Because Git uses content-addressed storage, if an object with that exact content's hash already exists, staging simply reuses it rather than creating a duplicate — only the index's filename-to-hash mapping gets updated.

Summary

`git add` performs two internal steps, directly built on the object model from the previous lesson. First, it computes the staged content's SHA hash and writes a new blob object into `.git/objects` if one with that exact hash doesn't already exist — the same operation performed directly by the lower-level plumbing command `git hash-object -w`. Second, it updates the `index` file to record that the given filename is now associated with that specific blob hash, formally marking it as staged. This means a staged file's content is already stored as a real, permanent object in Git's database immediately upon staging, well before any commit is ever made. Because staging captures a snapshot at that specific moment, further edits to the file afterward produce different content with a different hash, but the index still references the original blob until `git add` is run again — the mechanical explanation for exactly why re-staging is required after continuing to edit an already-staged file, first covered conceptually back in Module 1.

Frequently Asked Questions

It performs two steps: computing the staged file's content hash and writing a new blob object to .git/objects (if one doesn't already exist), and then updating the index file to record that the filename is now associated with that blob hash, marking it as staged.

Yes. The blob object representing your staged content's exact bytes is written to .git/objects immediately when you run git add, well before any commit — committing later just creates a tree and commit object that reference this already-existing blob.

git hash-object -w <file> performs exactly this step directly — computing the content's hash and writing the resulting blob object into .git/objects, which is precisely what happens inside git add's own internal process.

Because staging is a snapshot at a specific moment — the index points at the exact blob hash matching your content when you first ran git add. Further edits produce different content with a different hash, and re-running git add is what creates the new blob and updates the index to point at it instead.

No. Because Git uses content-addressed storage, if an object with that exact content's hash already exists, staging simply reuses it rather than creating a duplicate — only the index's filename-to-hash mapping gets updated.