The Add/Commit Cycle

Updated

October 30, 2026

The Add/Commit Cycle

After this page you can take a file from the working tree to a commit on main.

The loop

edit files  →  git add  →  git commit  →  git status (clean)

That is the whole product: copy chosen files into the index, snapshot the index, name the snapshot with a message, slide main forward.

 working tree          index             HEAD / main
 ┌──────────┐      ┌──────────┐      ┌──────────┐
 │ README.md│ -add→│ README.md│-commit→│ commit C │
 │ ping.py  │      │          │      │ tree  T  │
 └──────────┘      └──────────┘      └──────────┘
                                          ▲
                                     main ─┘

One file, one commit

From an initialized desk-api (previous page):

cd ~/desk/desk-api

cat > README.md <<'EOF'
# desk-api

HTTP API for the desk.
EOF

git add README.md
git status
git commit -m "Add README"
git log --oneline

git add stages README.md. git commit -m writes the commit without opening an editor. git log --oneline prints one line: a short hash and the message.

status is now clean. Working tree, index, and HEAD match.

A second commit

mkdir -p src
cat > src/ping.py <<'EOF'
def ping():
    return "ok"
EOF

git add src/ping.py
git commit -m "Add ping"
git log --oneline

Two commits. src/ exists in the tree object because Git stored a tree named src pointing at the ping.py blob. You did not git add the directory separately; adding the file is enough.

What -m is doing

Every commit needs a message. -m "Add README" is the short form. Without -m, Git opens core.editor. Multi-paragraph messages:

git commit -m "Add ping" -m "Returns a fixed ok string so the health check has a function to call."

The first -m is the subject. The second is the body. Chapter 6 is Conventional Commits (feat:, fix:). For Part I, a short imperative sentence is enough: Add ping, Fix empty name, Document env vars.

git add shapes

git add README.md              # one file
git add src/ping.py src/app.py # several
git add src                    # a directory, recursively
git add .                      # everything from here down, still respects .gitignore
git add -p README.md           # hunk by hunk (interactive)

.gitignore is a file of patterns Git will leave untracked. Create it when you have build output or secrets to skip:

cat > .gitignore <<'EOF'
__pycache__/
.env
EOF
git add .gitignore
git commit -m "Ignore bytecode and env files"

Patterns are matched from the file’s directory downward. /build at the repo root is only build/ at the root; build/ is every directory named build.

commit -a

git commit -a -m "Touch tracked files"

Stages already tracked files and commits. New files still need git add. Prefer explicit git add until that is automatic in your head.

After the cycle

git status
git log --oneline

Clean status, two or three commits. Next page: how to read what you just did (status, log, diff).