lesson ── git-labs ── ~20 мин ── 7 шагов
Цель - пройти Git «снизу вверх». Соберёшь репо из нескольких коммитов
обычными командами, потом руками доберёшься от ветки до коммита, до
tree, до blob - используя только cat-file. Когда сделаешь это раз,
все остальные plumbing-команды читаются как очевидные.
интерактивный sandbox
Поднимется контейнер gitlab/git-base с git, bash, pre-commit. В браузере откроется терминал, можно сразу git init. Каждый шаг проверяется автоматически. Сеть air-gapped, github.com недоступен.
stack ── git · bash · 256 MB RAM · air-gapped · самоуничтожается через 30 мин простоя
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 = staged уже отслеживаемых, -m = inline-сообщение
mkdir docs
echo "# readme" > docs/README.md
git add . && git commit -m "add docs"
git log --oneline # --oneline = одна строка на коммит (short-sha + subject)
Три коммита, второй меняет файл, третий добавляет директорию. Дальше всё это будем читать руками.
✓ Три коммита на месте. Готово к раскопкам.
Ветка - это просто текстовый файл с SHA. Прочти его:
cd /home/student/work/plumbing-log
cat .git/refs/heads/main
Сравни с git rev-parse HEAD - один и тот же SHA. То есть
git rev-parse - это cat плюс resolve символических ссылок.
✓ SHA HEAD найден. Это указатель на последний коммит.
cd /home/student/work/plumbing-log
SHA=$(cat .git/refs/heads/main) # ref - текстовый файл с SHA, не магия
git cat-file -t $SHA # -t = тип объекта (commit)
git cat-file -p $SHA # -p = pretty-print: tree, parent, author, message
Тип - commit. Внутри - поля: tree <sha>, parent <sha>,
author, committer, сообщение. Запомни tree-SHA и parent-SHA -
они тебе нужны.
Зафиксируй tree-SHA: `TREE=$(git cat-file -p $SHA | head -1 | cut -d' ' -f2)`.
✓ Коммит читается. Видны tree и parent.
cd /home/student/work/plumbing-log
SHA=$(cat .git/refs/heads/main)
# первая строка вывода - "tree <sha>", cut -f2 берёт второе поле
TREE=$(git cat-file -p $SHA | head -1 | cut -d' ' -f2)
git cat-file -t $TREE # должно быть "tree"
git cat-file -p $TREE # список записей: mode type sha name
Внутри tree - две записи: version.txt (blob) и docs (другой
tree). Обрати внимание на колонку mode: 100644 для файла,
040000 для подкаталога.
✓ Tree раскрыт. Видно файл и подкаталог.
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-записи: "<mode> <type> <sha> <name>", sha в 3-й колонкеBLOB=$(git cat-file -p $TREE | grep 'version.txt' | awk '{print $3}')git cat-file -p $BLOB # содержимое файла version.txt
Должно вывести v2. Сравни с тем, что в working tree:
cat version.txt. То же содержимое - просто другой путь к нему.
✓ Blob прочитан. Это финальный лист дерева объектов.
Связь между коммитами - поле parent. Сделай шаг назад руками:
cd /home/student/work/plumbing-log
SHA=$(cat .git/refs/heads/main)
# ищем строку "parent <sha>" в commit'е, берём sha (2-е поле)
PARENT=$(git cat-file -p $SHA | grep '^parent' | head -1 | cut -d' ' -f2)
git cat-file -p $PARENT # это второй коммит из лога ("bump to v2")Это второй коммит из лога. Tree у него другой - там version.txt
содержит v2, но нет docs/. Каждый коммит - snapshot, не diff.
✓ Шаг назад сделан. Теперь у тебя в голове есть весь граф.
Собери всё вместе. Скрипт, который от HEAD идёт по parent и печатает SHA + первое слово сообщения:
cd /home/student/work/plumbing-log
# <<'EOF' (с кавычками) = heredoc без интерполяции $ внутри
cat > log.sh <<'EOF'
#!/bin/bash
cur=$(cat .git/refs/heads/main) # стартуем с tip ветки
while [ -n "$cur" ]; do # пока есть parent - идём дальше
msg=$(git cat-file -p $cur | tail -1) # последняя строка commit'а - тело сообщения
echo "${cur:0:7} $msg" # ${cur:0:7} = short-sha (первые 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 # сравни вывод с твоим скриптом - должны совпасть
Сравни выводы. Должно совпасть построчно (с точностью до short-sha).
✓ Свой git log готов. Внутренности больше не магия.
git cat-file -t - тип объекта, -p - его pretty-print.
Цепочка: ref -> commit -> tree -> blob. Каждое звено - один SHA.
команды
cat .git/refs/heads/mainSHA текущего коммита веткиgit cat-file -p <commit-sha>распечатать commit (увидеть tree-sha)git cat-file -p <tree-sha>распечатать tree (увидеть blob-sha)git cat-file -p <blob-sha>увидеть содержимое файлаконцепции