Git, CI/CD & GitOpsVI · Index / Staging AreaIndex
git add mechanics — what staging actually does under the hood
What you'll learn
- Trace what git add writes to the object store and to the index
- Choose between git add <path>, git add ., and git add -A for a given intent
- Use --intent-to-add to register an empty file for tracking without staging content
- Use --chmod to set executable bits without a follow-up commit
- Recognise that git add does not follow renames; git status does
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git add looks like a single command but is doing two distinct
things on every invocation: writing a blob into the object store
and updating the entry in .git/index to point at that blob. The
consequence is that every byte that has ever been staged is still
in the object store as a reachable blob (assuming gc has not yet
pruned it), and the index is a complete manifest of the next
commit. Understanding the mechanics of git add is the
difference between a deliberate commit and one that ships a
stray file the engineer never reviewed.
What git add does, in order
For every path the command selects, git add performs the
following steps:
- Hash the working tree file. Git reads the bytes,
prefixes them with a header describing the object type
(
blob) and the byte length, and SHA-hashes the result. The hash is the blob’s OID. - Write the blob to the object store. If a blob with that
OID does not already exist under
.git/objects/, Git writes it. The object store is append-only; identical bytes share a single blob across the entire repository. - Update the index entry. The index entry for that path is
rewritten to point at the new OID, with the current stat()
cache so the next
git statuscan determine whether the working tree has been modified since staging.
sequenceDiagram
participant Dev as Engineer
participant WT as Working tree
participant OBJ as Object store\n(.git/objects)
participant IDX as Index\n(.git/index)
Dev->>WT: edit terraform/main.tf
Dev->>OBJ: git add hashes bytes, writes blob
Dev->>IDX: git add updates path -> blob OID
Note over IDX: index now reflects new bytes
The crucial observation: git add never touches the working
tree. The bytes on disk are not modified by staging. The index
is updated; the file you edited is still on disk exactly as you
left it. This is why a single file can be edited, partially
staged, edited again, and partially re-staged without losing
either version.
Pathspecs: <path>, ., and -A
git add selects files by pathspec. The three forms that come
up most in infrastructure repositories are:
git add terraform/main.tf # one explicit path
git add terraform/ # a directory and its tracked contents
git add . # the current directory and below
git add -A # the entire working tree (tracked + new + deleted)
The differences matter:
git add <path>stages only that path. Ifterraform/main.tfis the only file changed, only that file is staged. This is the safest form in an infrastructure repository.git add .stages modifications and deletions of tracked files under the current directory, plus new files that are not ignored. It does not stage deletions of tracked files outside the current directory.git add -Ais the broad form: it stages modifications, deletions, and new files across the entire working tree, regardless of the current directory. The-Aflag is short for--all.
flowchart TB
A["git add <path>"] --> B["one explicit path only"]
C["git add ."] --> D["cwd + subdirs\ntracked + new + deleted"]
E["git add -A"] --> F["entire working tree\ntracked + new + deleted"]
—intent-to-add: register without content
git add --intent-to-add <path> is a flag for the case where a
file exists in the working tree but is empty (or has no
intentional content yet) and you want Git to start tracking it.
The command adds the path to the index with an empty blob OID
and a special “intent-to-add” marker. git status will then
show the file as a new file staged for commit instead of as an
untracked file.
touch playbooks/rotate-creds.yml
git add --intent-to-add playbooks/rotate-creds.yml
git status
# Changes to be committed:
# new file: playbooks/rotate-creds.yml
The file’s bytes are not yet in the index. When the engineer
fills in the playbook and runs git add playbooks/rotate-creds.yml
without --intent-to-add, the intent-to-add marker is replaced
with the real blob and the file commits normally.
This flag is useful when a tooling step needs the file to be
visible to Git before the file has any content — for example, a
policy-as-code tool that must see the path in git diff --cached
to attach a plan, or a CI stage that lints every file in the
index even if the file is empty.
—chmod: change mode bits without a follow-up commit
git add --chmod=+x scripts/deploy.sh sets the executable bit
in the index entry for the file. Without --chmod, Git tracks
executable bits only as part of the file’s mode in the tree
object, and changing the bit on disk with chmod does not
update the index. The --chmod flag tells git add to update
the index entry’s mode without re-hashing the content (the blob
OID is unchanged because the content is unchanged).
chmod +x scripts/deploy.sh
git add --chmod=+x scripts/deploy.sh
git diff --cached
# old mode 100644 scripts/deploy.sh
# new mode 100755 scripts/deploy.sh
The inverse form, git add --chmod=-x, clears the executable
bit. Both forms are useful when an infrastructure repository
tracks executable hook scripts, deployment tools, or
provisioning binaries that must keep their mode bits across
checkouts on Windows or across tarball extractions that strip
the bit.
Renames and the index
git add does not follow renames. Renames in Git are a
post-hoc calculation: Git compares the blob OIDs of the
removed file and the added file and declares a rename when the
similarity score crosses a threshold (-M for files, --find-renames
with a custom threshold). The index sees the operation as two
distinct events: a deletion of the old path and an addition of
the new path.
git mv scripts/deploy.sh bin/deploy.sh
git status
# Changes to be committed:
# renamed: scripts/deploy.sh -> bin/deploy.sh
git mv is a convenience that performs the working-tree move
and a git add of both paths in one command. The renamed:
line in git status is computed by Git when status runs; the
index itself just records the deletion and the addition.
Production discipline
- Prefer explicit paths.
git add terraform/main.tfis safer thangit add .in any repository where a stray debug print or local override could leak into a commit. Use broad forms only aftergit statushas confirmed there are no surprises. - Use
--intent-to-adddeliberately. It is for files that must be visible to tooling before they have content. For every other new file, a plaingit add <path>after the content is written is sufficient and avoids the marker bookkeeping. - Stage chmod changes with
--chmod. Do not rely on the filesystem mode surviving a clone or a tar extraction. The index entry is the source of truth for executable bits.
Cross-course references
- Linux for Production Sysadmins - Part IX (Filesystem)
covers the inode mode bits that
--chmodmanipulates; the Git analogue is that the index entry holds the mode that will be committed, not the mode currently on disk. - Ansible for Production Sysadmins - Part XXXVII (RepoArch)
treats
git addas the boundary between untrusted working changes and the playbook that will be run. - Terraform for Production Sysadmins - Part X (Plan)
describes the equivalent of
git addfor Terraform: theterraform planoutput that the operator reviews beforeterraform apply. Both are deliberate staging events.
Quiz
Knowledge check · 4 questions
Q1. An engineer runs `git add .` from inside the `terraform/` subdirectory of a monorepo that also contains an `ansible/` directory. A tracked file has been deleted from `ansible/`. Which of the following is true?
Q2. `git add` modifies the working tree file in addition to updating the index.
Q3. Name the two storage locations `git add` writes to, and the third location it leaves unchanged.
Q4. Diagnose why an executable deploy script lost its executable bit when checked out on a new machine, and choose the right remediation.
A team commits a deploy script with the executable bit set on the engineer's macOS laptop. A new CI runner on Linux checks out the repository and finds the script non-executable, so the pipeline fails with 'Permission denied'. The engineer insists they committed the file with the bit set, and `git show HEAD:scripts/deploy.sh` on the laptop shows mode 100755.
Passing score: 75%. Answers are checked in this browser.