Introduction
Git is split into two layers. Plumbing commands are low-level building blocks operating on the object database, refs, and the index. Porcelain commands are user-friendly wrappers built from plumbing. git commit is porcelain; git hash-object, git write-tree, and git update-ref are plumbing.
Why the split
Porcelain output is for humans and may change between versions. Plumbing output is stable, parseable, and intended for scripts. Mixing them up is the most common cause of fragile Git scripts.
Common porcelain commands
git add,git commit,git status,git loggit switch,git restore,git branchgit merge,git rebase,git cherry-pickgit fetch,git pull,git push,git clone
Common plumbing commands
git hash-object: compute the SHA of content, optionally writing it.git cat-file: read an object's type, size, or content.git ls-tree,git ls-files: list tree or index contents.git rev-parse: resolve a name to a SHA.git rev-list: list commit SHAs from a graph traversal.git update-ref,git symbolic-ref: ref manipulation.git read-tree,git write-tree,git commit-tree: index/tree/commit creation.git update-index: low-level index manipulation.git pack-objects,git unpack-objects,git index-pack.
Building a commit by hand
To see plumbing in action, build a commit without porcelain:
BLOB=$(echo "hello" | git hash-object --stdin -w)
TREE=$(printf "100644 blob $BLOB\thello.txt\n" | git mktree)
COMMIT=$(echo "first" | git commit-tree $TREE)
git update-ref refs/heads/main $COMMIT
git log --oneline
That is essentially what git commit does internally.
Stable scripting interfaces
Some porcelain commands have a "porcelain" mode with a stable parseable format:
git status --porcelain=v2
git diff --raw
git ls-files --stage
git for-each-ref --format='%(refname) %(objectname)'
Use these in scripts; never parse git status human output.
Reading the man page
Porcelain man pages are listed in git help -a under "Main Porcelain Commands"; plumbing under "Manipulators", "Interrogators", "Synching repositories", "Internal helpers". For internals tutorials run git help gitcore-tutorial and git help gitrepository-layout.
When to use plumbing
- Writing reliable scripts that survive Git upgrades.
- Building tooling that integrates with Git's data model.
- Debugging porcelain commands by reproducing their internal steps.
- Implementing custom workflows where porcelain does not fit.
Common mistakes
Parsing git status or git log human output in scripts and breaking on a Git upgrade or locale change. Use --porcelain formats and --pretty=format: with explicit placeholders. Calling git update-ref without the three-argument compare-and-set form in concurrent scripts and racing with another process. Mixing low-level git update-index with high-level git add in the same workflow and ending up with surprising index state. Finally, assuming porcelain is always "safer" than plumbing; some plumbing operations (like git update-ref) are far more deterministic than their porcelain counterparts. The right tool depends on what you are building.