A commit points to one root tree, the complete snapshot, and adds who, when and why. Create one from the tree with commit-tree, then read it back:
git commit-tree 2fdd726 -m "Add README and database schema"
git cat-file -p 653fc97Output
653fc97f05515c8f22fe7b7865e1ae16351816d0 tree 2fdd7266111b3645450e0a6b49c48eb3d4882077 author Sam Rivera <sam@example.com> 1789952400 +0800 committer Sam Rivera <sam@example.com> 1789952400 +0800 Add README and database schema
Times are Unix seconds plus a time-zone offset. The author wrote the change and the committer applied it; they differ after a rebase or cherry-pick. Using the hash your own commit-tree printed, point main at the commit, then commit again the everyday way:
git update-ref refs/heads/main 653fc97
echo 'A small bookshop API.' >> README.md
git commit -qam "Describe BookNest in the README"
git cat-file -p HEAD | head -2
git ls-tree HEADOutput
tree 64eb5254db7a4f751d2d72d53c4461fcc7fa1015 parent 653fc97f05515c8f22fe7b7865e1ae16351816d0 100644 blob 6f046deb11577fe2bc71307bf78265c602bf8f86 README.md 040000 tree b5d16e349146c9e8137e044d0dd6045880b30de3 db
The new commit names its parent (a merge names two or more). Its snapshot is complete, yet the unchanged db tree b5d16e3 is shared with the first commit, not copied (3). Diffs are computed on demand by comparing trees, and identical subtree hashes let Git 1,932 skip whole directories without opening them.
