linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Chapters
  • How it works
  • Lessons
  • Knowledge base
  • Interview prep
home/git/lessons/git-lab-04-1-plumbing-log

lesson ── git-labs ── ~20 мин ── 7 шагов

Reconstruct the log by hand with cat-file

The goal is to walk Git from the bottom up. You build a repo of a few commits with ordinary commands, then reach by hand from the branch to the commit, to the tree, to the blob, using only cat-file. Once you do this one time, every other plumbing command reads as obvious.

▶ интерактивный sandbox

Поднимется контейнер gitlab/git-base с git, bash, pre-commit. В браузере откроется терминал, можно сразу git init. Каждый шаг проверяется автоматически. Сеть air-gapped, github.com недоступен.

запустить sandbox →

stack ── git · bash · 256 MB RAM · air-gapped · самоуничтожается через 30 мин простоя

Шаги

  1. 01

    Create a small repo with three commits

    bash
    cd /home/student/work
    mkdir -p plumbing-log && cd plumbing-log
    git init -b main
    echo "v1" > version.txt
    git add . && git commit -m "init"
    echo "v2" > version.txt
    git commit -am "bump to v2"    # -a = stage already-tracked files, -m = inline message
    mkdir docs
    echo "# readme" > docs/README.md
    git add . && git commit -m "add docs"
    git log --oneline              # --oneline = one line per commit (short-sha + subject)

    Three commits: the second changes a file, the third adds a directory. Next you read all of this by hand.

    ✓ Three commits in place. Ready to dig.

  2. 02

    Find the commit's SHA through the branch file

    A branch is just a text file with a SHA. Read it:

    bash
    cd /home/student/work/plumbing-log
    cat .git/refs/heads/main

    Compare it with git rev-parse HEAD: the same SHA. So git rev-parse is cat plus resolving symbolic refs.

    ✓ The HEAD SHA is found. It points to the last commit.

  3. 03

    Read the commit object itself

    bash
    cd /home/student/work/plumbing-log
    SHA=$(cat .git/refs/heads/main)   # ref is a text file with a SHA, not magic
    git cat-file -t $SHA              # -t = object type (commit)
    git cat-file -p $SHA              # -p = pretty-print: tree, parent, author, message

    The type is commit. Inside are the fields tree <sha>, parent <sha>, author, committer, and the message. Remember the tree-SHA and the parent-SHA; you need them.

    подсказка

    Capture the tree-SHA: `TREE=$(git cat-file -p $SHA | head -1 | cut -d' ' -f2)`.

    ✓ The commit reads. You can see the tree and the parent.

  4. 04

    Open the tree of the last commit

    bash
    cd /home/student/work/plumbing-log
    SHA=$(cat .git/refs/heads/main)
    # the first output line is "tree <sha>", cut -f2 takes the second field
    TREE=$(git cat-file -p $SHA | head -1 | cut -d' ' -f2)
    git cat-file -t $TREE         # should be "tree"
    git cat-file -p $TREE         # list of entries: mode type sha name

    The tree holds two entries: version.txt (a blob) and docs (another tree). Look at the mode column: 100644 for a file, 040000 for a subdirectory.

    ✓ The tree is open. You can see the file and the subdirectory.

  5. 05

    Read the blob's content

    bash
    cd /home/student/work/plumbing-log
    SHA=$(cat .git/refs/heads/main)
    TREE=$(git cat-file -p $SHA | head -1 | cut -d' ' -f2)
    # awk '{print $3}' - tree-entry format is "<mode> <type> <sha> <name>", sha is the 3rd column
    BLOB=$(git cat-file -p $TREE | grep 'version.txt' | awk '{print $3}')
    git cat-file -p $BLOB         # content of version.txt

    It should print v2. Compare it with the working tree: cat version.txt. The same content, just a different path to it.

    ✓ The blob is read. This is the final leaf of the object tree.

  6. 06

    Take one step back along the parent

    The link between commits is the parent field. Step back by hand:

    bash
    cd /home/student/work/plumbing-log
    SHA=$(cat .git/refs/heads/main)
    # find the "parent <sha>" line in the commit, take the sha (2nd field)
    PARENT=$(git cat-file -p $SHA | grep '^parent' | head -1 | cut -d' ' -f2)
    git cat-file -p $PARENT       # this is the second commit from the log ("bump to v2")

    This is the second commit from the log. Its tree is different: there version.txt holds v2 and there is no docs/. Each commit is a snapshot, not a diff.

    ✓ One step back done. Now you hold the whole graph in your head.

  7. 07

    Write a script that reproduces git log

    Put it all together. A script that walks from HEAD along parent and prints the SHA plus the first word of the message:

    bash
    cd /home/student/work/plumbing-log
    # <<'EOF' (quoted) = heredoc with no $ interpolation inside
    cat > log.sh <<'EOF'
    #!/bin/bash
    cur=$(cat .git/refs/heads/main)           # start from the branch tip
    while [ -n "$cur" ]; do                   # while there is a parent, keep going
      msg=$(git cat-file -p $cur | tail -1)   # the last line of the commit is the message body
      echo "${cur:0:7} $msg"                  # ${cur:0:7} = short-sha (first 7 hex)
      cur=$(git cat-file -p $cur | grep '^parent' | head -1 | cut -d' ' -f2)
    done
    EOF
    chmod +x log.sh
    ./log.sh
    git log --oneline             # compare the output with your script: they should match

    Compare the outputs. They should match line by line (down to the short-sha).

    ✓ Your own git log is ready. The internals are no longer magic.

Что ты узнал

git cat-file -t gives an object's type, -p pretty-prints it. The chain: ref -> commit -> tree -> blob. Each link is one SHA.

команды

  • cat .git/refs/heads/mainthe SHA of the branch's current commit
  • git cat-file -p <commit-sha>print the commit (see the tree-sha)
  • git cat-file -p <tree-sha>print the tree (see the blob-sha)
  • git cat-file -p <blob-sha>see the file's content

концепции

  • · ref is a text file pointing to a commit
  • · commit -> tree (snapshot) + parent (history)
  • · tree -> blob (content) + mode + name

← предыдущая

Build a commit by hand with plumbing commands

следующая →

Three areas: working tree, index, repository

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies