October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

How to Create Git Objects Manually: Blob, Tree, and Commit

Build a minimal Git commit by hand and see how blobs store content, trees add names and modes, commits record snapshots and history, and refs name commits.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

You can create a minimal Git commit without git add or git commit: write a blob with git hash-object, assemble a tree with git mktree, create a commit with git commit-tree, then point a branch reference at it with git update-ref. This plumbing-level exercise shows what each object contains—and why a blob itself has no filename.

What Git stores: content, directory structure, and history

Git has four object types: blobs, trees, commits, and annotated tags. This walkthrough builds the first three. Git’s data model describes their roles:

  • Blob: file content only. It does not store the filename or path.
  • Tree: a directory listing that associates entry names and modes with blob or subtree object IDs.
  • Commit: a pointer to a top-level tree, plus zero or more parent commit IDs, author and committer details and timestamps, and a message.

A root commit has no parent. Each object is identified by a hash of its type and content; changing the content or metadata creates a different object rather than modifying the original in place. A branch name is a ref that points to a commit—it is not part of the commit object.

How object IDs are formed

Git hashes an object’s type, its content length, a NUL byte, and the content itself. Hashing only the raw file bytes does not produce the Git blob ID. The Git hash-function transition document describes traditional SHA-1 object IDs as 40 hexadecimal characters and SHA-256 IDs as 64. The repository’s hash format determines the IDs used, so do not assume every repository uses SHA-1 or hard-code an ID length into generic instructions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build a blob, tree, and root commit

Run this as a learning exercise in a new, isolated repository. The commands below are a recipe, not a claim that a particular run was tested; the resulting IDs depend on the exact content, identity, timestamps, and repository format.

  1. Create a repository and set the identity for this exercise.
    mkdir git-object-lab
    cd git-object-lab
    git init
    git config user.name "Git Object Lab"
    git config user.email "[email protected]"

    Setting the identity locally avoids changing your global Git configuration. The email uses the reserved .invalid domain. Commit identity and timestamps become part of the commit contents; Git will supply timestamps when you create the commit.

  2. Write file content as a blob.
    blob=$(printf '%sn' 'Hello from a Git blob.' | git hash-object -w --stdin)
    printf '%sn' "$blob"

    --stdin reads the exact bytes supplied on standard input; the newline from printf is included. The default object type is blob, and -w writes it to the repository’s object database. The command prints the object ID, which the shell stores in blob. See the git-hash-object manual.

  3. Put the blob in a tree.
    tree=$(printf '100644 blob %streadme.txtn' "$blob" | git mktree)
    printf '%sn' "$tree"

    The record uses a regular-file mode, the object kind blob, the ID returned by the previous command, a tab, and the filename. git mktree expects this ls-tree-style format—not JSON or an ordinary shell listing. By default it checks that referenced objects exist and normalizes entry order. Use the actual blob ID; --missing exists but is not the normal route for this exercise. See the git-mktree manual.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  4. Create a root commit from that tree.
    commit=$(git commit-tree "$tree" -m "Create a tree from a blob")
    printf '%sn' "$commit"

    Without a -p parent option, this is a root commit. For a later commit, supply one or more parent IDs with -p. The command writes a commit object and prints its ID; it does not automatically move a branch. Git’s git-commit-tree manual cautions that this is “usually not what an end user wants to run directly.” For ordinary work, use git add and git commit.

  5. Inspect the three objects.
    git cat-file -t "$blob"
    git cat-file -p "$blob"
    git cat-file -t "$tree"
    git cat-file -p "$tree"
    git cat-file -t "$commit"
    git cat-file -p "$commit"
    git cat-file -e "$commit"

    -t reports an object’s type, -p displays its contents in a readable form, and -e checks that the object exists. The blob shows the text, the tree shows its mode, kind, ID, and name, and the commit shows its tree, identity and timestamp headers, and message. Consult the git-cat-file manual.

  6. Give the commit a branch name.
    git update-ref refs/heads/main "$commit"

    This updates the main branch ref to point to the commit. If you need to protect against changing a ref that has moved unexpectedly, git update-ref accepts an old object ID as an additional expected-value check. The exact form and behavior are documented in the git-update-ref manual.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What the inspection shows

The blob contains bytes, not a path

The blob’s payload is just the text supplied to hash-object. It does not know that the tree calls it readme.txt. The same blob can be referenced by different tree entries, including entries under different names.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The tree supplies names and modes

A tree represents one directory. The regular-file mode in this example is 100644. Other modes you may encounter include 100755 for an executable file, 120000 for a symbolic link, 040000 for a directory/tree, and 160000 for a gitlink or submodule. A gitlink points to a commit object. A directory is represented by another tree, not by a blob containing a folder listing.

The commit connects a snapshot to history

The commit points to the top-level tree and records the identities and timestamps of the author and committer, the message, and parent IDs if present. Changing the tree, message, identities, timestamps, or parent changes the commit content and therefore its ID. An amended commit is a new object, not an in-place edit.

Why a commit can exist without appearing on a branch

git commit-tree writes an object, but it does not by itself make a branch point to that object. The separate git update-ref step supplies that convenient name. Refs are how Git gives movable names to objects, most commonly commits; branches move as new commits are added, while tags are generally used as stable names.

How this differs from everyday Git

The index, or staging area, is not a fifth object type. It is a separate staging format: Git normally turns the staged entries into tree object(s) when making a commit. This exercise skips the index by constructing a tree directly with git mktree. The official index-format documentation describes the index’s on-disk structure, including its header, entries, extensions, and checksum; you do not need to encode that format for this exercise.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

These commands are plumbing: they expose low-level object and ref operations. The usual workflow is the higher-level porcelain of git add and git commit. For a broader explanation, Pro Git’s Git Internals chapter discusses Git objects. Its print edition, Pro Git Second Edition, was published in 2014; for current command flags, prefer the relevant Git manual.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.