Git commands are divided into two categories:

  1. Porcelain: User-friendly commands you use every day (add, commit, push, pull).
  2. Plumbing: Low-level commands designed for scripts and automation (hash-object, update-index, write-tree).

Think of it like a car: Porcelain is the steering wheel and pedals. Plumbing is the engine valves and fuel injection. You usually drive with the wheel, but to fix the engine, you need to understand the valves.

Building a Commit Manually

We are going to create a commit without using git add or git commit. We will use plumbing commands to manipulate the object database directly.

The Steps

  1. hash-object: Create a Blob from file content.
  2. update-index: Add the file to the Index (Staging Area).
  3. write-tree: Create a Tree object from the Index.
  4. commit-tree: Create a Commit object pointing to the Tree.
  5. update-ref: Move the branch pointer (HEAD) to the new Commit.
git-plumbing-lab
Welcome to the Plumbing Lab.
Task: Create a commit manually.
 
Step 1: Create a blob object from text.
Type: echo "hello" | git hash-object -w --stdin
$

Working Dir

Index

Repository (Objects)

Command Breakdown

1. git hash-object -w

Takes content, computes the SHA-1, creates the blob header, and stores the compressed object in .git/objects.

  • -w: Write the object to the database (don’t just calculate hash).
  • --stdin: Read content from standard input.

2. git update-index

Modifies the binary index file directly.

  • --add: Add file if not present.
  • --cacheinfo <mode> <hash> <filename>: Manually register a file in the index using its hash, without needing it to be in the working directory.

3. git write-tree

Scans the current index and creates a tree object representing that directory state. Returns the SHA-1 of the new tree.

4. git commit-tree <tree-hash>

Creates a commit object.

  • Takes the tree hash as an argument.
  • Takes the commit message from stdin.
  • Returns the new commit SHA-1.
  • (Note: To add a parent, you would use -p <parent-hash>).

5. git update-ref

Updates a reference (like a branch pointer) to a new hash. This is safer than echo <hash> > .git/refs/heads/master because it follows symlinks and handles locking.

Summary

You just performed the exact steps git commit does for you!

  1. Hashes files (Blobs).
  2. Updates the Staging Area (Index).
  3. Creates a snapshot of the directory (Tree).
  4. Creates a history node (Commit).
  5. Moves the branch pointer (Ref).

Understanding this flow allows you to repair broken repos, script custom workflows, and truly master Git.