Пара 11: Git: история, объекты и управление версиями

90 минут · 3 курс, ML.

Содержание и результат

Создать репозиторий, проверить diff и записать осмысленный атомарный commit.

План занятия

0–10: контекст и исходная задача. 10–40: устройство и механизмы. 40–55: демонстрация команд. 55–80: лабораторная работа. 80–90: разбор результата и фиксация исправлений.

Практика выполняется в своей учебной папке и на localhost. Подготовка окружения описана в lab/README.md.

Commit — снимок и объяснение

История связывает состояние проекта с намерением изменения.

Git хранит историю содержимого проекта и связей между commit. Commit имеет идентификатор, автора и сообщение. Это не только резервная копия: история позволяет выяснить, что и зачем изменилось. Commit не доказывает, что код тестировался или был развёрнут. Для ML-проекта полезно отдельно фиксировать версию кода, зависимости, параметры и идентификатор артефакта; один SHA кода не воспроизводит неизвестный датасет. В учебном проекте JSON-артефакт мал и лежит рядом с кодом; реальные большие веса и данные требуют отдельного хранилища.

Аналогия: подписанные страницы лабораторного журнала вместо папки final_final.

Три области Git

Рабочая папка, index и история — разные состояния.

Редактирование меняет рабочую папку. git add выбирает содержимое для следующего commit в index. git commit записывает выбранный снимок. Файл можно изменить после add: тогда staged и working tree будут различаться. git diff сравнивает рабочую папку с index; git diff --staged показывает подготовленные изменения. Перед commit смотрим обе проверки и status. Не используем git add . без осмотра в проекте, где могут оказаться секреты и большие файлы. История остаётся локальной до push; удалённый хостинг не является самим Git.

Аналогия: стол с черновиками, конверт на отправку и зарегистрированное письмо.

Что хранить в репозитории

Храним исходники и рецепты восстановления среды.

В .gitignore включаем .venv, pycache, .env, временные логи и результаты запусков. .gitignore не убирает уже отслеживаемый файл и не стирает историю. README, Dockerfile, тесты и небольшой демонстрационный артефакт полезно хранить. Данные студентов, ключи и настоящие токены в учебный репозиторий не попадут. Сообщение commit должно объяснять изменение: feat:, fix:, docs: — понятное соглашение Conventional Commits. Не требуем идеальной истории ради оценки, но поощряем маленькие изменения с одной целью.

Аналогия: в книге рецептов хранится инструкция, а не холодильник со всеми продуктами.

Подробный разбор: Git: от истории Linux к графу версий

Git возник из требований разработки Linux

Разрыв с BitKeeper ускорил создание свободного распределённого VCS.

Git возник из требований разработки Linux

Linux долго принимал изменения в виде патчей и архивов. С 2002 года проект использовал проприетарный распределённый BitKeeper. В 2005-м отношения между сообществом Linux и владельцем инструмента нарушились, бесплатный режим использования прекратился. 6 апреля 2005 года в письме Kernel SCM saga Торвальдс сообщил о поиске замены после неудачных попыток разрешить конфликт вокруг использования BitKeeper. Он отдельно просил не сводить ситуацию к обвинению BitMover. Это создало практическую задачу быстро заменить важную часть процесса разработки. Торвальдс начал Git; дальнейшее развитие стало коллективным, а поддержка позже перешла к Джунио Хамано. Не сводим историю к «команда что-то не поделила»: конфликт касался условий и зависимости от внешнего проприетарного инструмента. Новый VCS должен был быстро работать с большой историей, поддерживать распределённую нелинейную разработку и локальные операции. Поэтому Git хранит историю локально и имеет дешёвые указатели ветвей. GitHub и GitLab — платформы вокруг Git, а не сам VCS.

Практическое следствие: Git решает задачу учёта и обмена изменениями, а не резервного копирования секретов или доставки сервиса сам по себе.

Commit — узел графа снимков

Branch — имя указателя; файлы и trees хранятся как объекты.

Commit — узел графа снимков

Blob хранит содержимое файла, tree связывает имена и режимы с blobs и вложенными trees, commit связывает корневой tree, родителей и metadata. Ветка — ref на commit, HEAD указывает на текущую ветку или прямо на commit в detached состоянии. Индекс/staging area описывает будущий снимок отдельно от текущего рабочего дерева. Поэтому git add выбирает состояние для commit, а последующая правка файла не меняет уже staged-версию автоматически. История — DAG: обычный commit имеет одного родителя, merge может иметь несколько. Git может эффективно хранить переиспользуемое содержимое, но пользовательская модель — снимки, а не копии папок branch-name. Hash идентифицирует объект согласно формату репозитория; это не универсальная подпись личности автора. Для практики создаём локальный репозиторий и смотрим cat-file, не начинаем с внешнего сервиса.

Практическое следствие: git cat-file -p HEAD показывает commit; git ls-tree HEAD — snapshot tree; git diff --staged — содержимое следующего снимка.

Опыт: связать рисунок с наблюдением

git log --oneline --graph --all
git cat-file -p HEAD
git ls-tree HEAD
git diff
git diff --staged

Сравните working tree, index и HEAD на одном маленьком файле. Не коммитьте .env, бинарный dataset и ключи доступа.

Источники подробного разбора

Команды и наблюдения

Окружение: Ubuntu / Bash, lab.

cd ~/devops-course/lab
git init -b main
git config user.name "Student"
git config user.email "student@example.invalid"
git status
git add app.py models/model.json Dockerfile compose.yaml README.md .gitignore
git diff --staged --stat
git commit -m "feat: add reproducible StudyPulse service"
git log --oneline -3

Имя и email настраиваются локально для учебного репозитория; не меняем глобальные настройки ноутбука.

Ожидаемый результат: main содержит первый commit; .venv и .env отсутствуют среди отслеживаемых файлов.

Вариант для macOS

Git-команды переносимы. Первый запуск git может предложить установку Apple Command Line Tools; выполните её заранее. Сохраняйте .gitignore и проверку секретов.

git --version
git status
git diff
git diff --staged
git log --oneline -3

Практика: История исправления README

  1. Создайте репозиторий в своей копии lab и проверьте .gitignore.
  2. Измените README: добавьте диагностику неверного порта.
  3. Просмотрите diff, подготовьте только README и выполните docs: commit.
  4. Измените файл после add в отдельном опыте; покажите различие diff и diff --staged.

Результат: Два осмысленных commit и пример staged/unstaged различия.

Проверка: git ls-files не содержит .venv, .env или личных данных; изменения README понятны.

Неисправность для разбора: Студент считает git add сохранением всей последующей работы.

Решение и диагностика

Проверка: git status --short; git diff; git diff --staged; git ls-files. Для отдельного commit: git add README.md; git commit -m "docs: explain port diagnostics". Если .env уже отслеживается, git rm --cached .env прекращает дальнейшее отслеживание, но при реальной утечке требуется ротация секрета и отдельная работа с историей.

Основные выводы

Самостоятельная работа

Оформить историю Linux-чекпоинта и передать SHA проверяемого состояния.

Источники