linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it works
  • Уроки
  • База знаний
  • Собеседование
home/git/lessons/git-lab-03-1-manual-commit

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

Собери коммит руками через plumbing-команды

Цель - увидеть руками, что Git это key-value хранилище объектов поверх файловой системы. Соберёшь полный коммит без git add и git commit, только через plumbing: hash-object, write-tree, commit-tree, update-ref. Когда увидишь, что porcelain - это просто обёртка, страх перед Git проходит.

▶ интерактивный 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

    Создай пустой репо

    bash
    cd /home/student/work
    mkdir -p plumbing && cd plumbing
    git init -b main             # -b main = ветка по умолчанию main, не master
    ls -la .git/                 # увидеть внутреннюю структуру репо

    Внутри .git/ посмотри на objects/ и refs/. Сейчас они почти пусты. По ходу урока ты их заполнишь сам.

    ✓ Репо инициализирован. objects/ пустой.

  2. 02

    Запиши содержимое файла как blob

    hash-object -w берёт текст и записывает его в .git/objects/, возвращая SHA. Сам файл в working tree не появляется.

    bash
    echo "Hello, Git internals" > /tmp/payload
    SHA=$(git hash-object -w /tmp/payload)    # -w записывает blob в object store, возвращает SHA
    echo "blob: $SHA"
    ls .git/objects/${SHA:0:2}/               # ${SHA:0:2} = первые 2 символа SHA (имя папки)

    Внутри .git/objects/aa/bbcc... появился файл. Это и есть blob - zlib-сжатое содержимое.

    подсказка

    SHA из 40 hex-символов. Первые 2 - имя папки, остальные - имя файла.

    ✓ Blob лежит в object store. Имени у него пока нет.

  3. 03

    Прочти blob обратно через cat-file

    У объекта нет имени файла - только SHA. Прочесть содержимое:

    bash
    cd /home/student/work/plumbing
    # awk-однострочник склеивает имя папки + имя файла = полный SHA объекта
    SHA=$(find .git/objects -type f | head -1 | awk -F/ '{print $(NF-1)$NF}')
    git cat-file -t $SHA          # -t = тип объекта (blob/tree/commit/tag)
    git cat-file -p $SHA          # -p = pretty-print содержимого

    -t показывает тип объекта (blob), -p - содержимое. Это первый слой Git: контент-адресованное хранилище.

    ✓ Объект - blob, содержит твою строку.

  4. 04

    Положи blob в index под именем

    Blob по себе - анонимный. Чтобы он стал «файлом» с именем, нужна запись в index (tree-будущее).

    bash
    cd /home/student/work/plumbing
    SHA=$(find .git/objects -type f | head -1 | awk -F/ '{print $(NF-1)$NF}')
    # --cacheinfo <mode> <sha> <path>: положить готовый blob в index под именем
    git update-index --add --cacheinfo 100644 $SHA hello.txt
    git ls-files --stage          # увидеть содержимое index

    100644 - mode regular file. git ls-files --stage показывает содержимое index: SHA + имя.

    ✓ Index знает: hello.txt = твой blob.

  5. 05

    Слепи tree из текущего index

    write-tree берёт state из index и материализует его как tree-объект.

    bash
    cd /home/student/work/plumbing
    TREE=$(git write-tree)        # читает index, пишет tree-объект, возвращает его SHA
    echo "tree: $TREE"
    git cat-file -p $TREE         # увидеть содержимое tree (список записей)

    Результат - таблица: 100644 blob <sha> hello.txt. Это и есть tree: список записей.

    ✓ Tree сформирован. Внутри - запись на hello.txt.

  6. 06

    Сделай commit-объект через commit-tree

    Commit - это tree + сообщение + автор + (опционально) parent. Поскольку это первый коммит, parent'а нет.

    bash
    cd /home/student/work/plumbing
    TREE=$(git write-tree)
    # commit-tree <tree-sha>: сделать commit, читая сообщение со stdin
    # (без -p тут потому что это первый коммит, parent'а нет)
    COMMIT=$(echo "Первый коммит руками" | git commit-tree $TREE)
    echo "commit: $COMMIT"
    git cat-file -p $COMMIT       # увидеть поля commit'а

    Видишь поля: tree <sha>, author ... <email>, committer ..., пустая строка, сообщение. Это всё, что в commit'е.

    ✓ Commit-объект создан. У него есть SHA, но ветка про него ещё не знает.

  7. 07

    Двинь ветку main на этот коммит

    Сейчас git log пуст: HEAD указывает на main, а main не существует. Создай ref.

    bash
    cd /home/student/work/plumbing
    TREE=$(git write-tree)
    COMMIT=$(echo "Первый коммит руками" | git commit-tree $TREE)
    # update-ref <ref> <sha>: атомарно записать SHA в файл ветки
    git update-ref refs/heads/main $COMMIT
    git log --oneline             # теперь log не пуст - main существует
    cat .git/refs/heads/main      # ветка - это обычный текстовый файл с SHA

    git log теперь показывает твой коммит. .git/refs/heads/main - обычный текстовый файл с SHA.

    подсказка

    Если log пустой - убедись что HEAD указывает на main: `cat .git/HEAD`.

    ✓ Ветка двинута, коммит виден в логе. Ты собрал коммит вручную.

  8. 08

    Сделай checkout, чтобы файл появился в working tree

    Сейчас файла hello.txt физически в working tree нет - он только в objects + index. Проверь:

    bash
    cd /home/student/work/plumbing
    ls                                  # пусто - hello.txt существует только в объектах
    git checkout HEAD -- hello.txt      # достать blob из object store и положить на диск
    cat hello.txt                       # вот тот же контент, что и в blob'е

    checkout достал blob из object store и положил его на диск. Цикл замкнут: контент -> blob -> index -> tree -> commit -> ref -> checkout -> файл на диске.

    ✓ Файл на месте. Дальше - в главе 4 пройдёшь это же снизу вверх, читая существующие объекты.

Что ты узнал

Git хранит три типа объектов: blob (содержимое), tree (директория), commit (снимок + метаданные). Каждый объект адресуется SHA-1 от его содержимого. Plumbing-команды дают прямой доступ ко всем трём слоям.

команды

  • git hash-object -w fileзаписать blob в object store
  • git update-index --add --cacheinfo 100644 <sha> pathположить blob в index
  • git write-treeслепить tree из текущего index
  • git commit-tree <tree> -m '...'сделать commit-объект
  • git update-ref refs/heads/main <sha>двинуть ветку на commit

концепции

  • · blob = содержимое файла без имени
  • · tree = таблица записей name -> mode + sha
  • · commit = указатель на tree + parent + author + message

следующая →

Реконструируй log руками через cat-file

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки