Пара 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.
Linux долго принимал изменения в виде патчей и архивов. С 2002 года проект использовал проприетарный распределённый BitKeeper. В 2005-м отношения между сообществом Linux и владельцем инструмента нарушились, бесплатный режим использования прекратился. 6 апреля 2005 года в письме Kernel SCM saga Торвальдс сообщил о поиске замены после неудачных попыток разрешить конфликт вокруг использования BitKeeper. Он отдельно просил не сводить ситуацию к обвинению BitMover. Это создало практическую задачу быстро заменить важную часть процесса разработки. Торвальдс начал Git; дальнейшее развитие стало коллективным, а поддержка позже перешла к Джунио Хамано. Не сводим историю к «команда что-то не поделила»: конфликт касался условий и зависимости от внешнего проприетарного инструмента. Новый VCS должен был быстро работать с большой историей, поддерживать распределённую нелинейную разработку и локальные операции. Поэтому Git хранит историю локально и имеет дешёвые указатели ветвей. GitHub и GitLab — платформы вокруг Git, а не сам VCS.
Практическое следствие: Git решает задачу учёта и обмена изменениями, а не резервного копирования секретов или доставки сервиса сам по себе.
Commit — узел графа снимков
Branch — имя указателя; файлы и trees хранятся как объекты.
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
- Создайте репозиторий в своей копии lab и проверьте .gitignore.
- Измените README: добавьте диагностику неверного порта.
- Просмотрите diff, подготовьте только README и выполните docs: commit.
- Измените файл после 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 прекращает дальнейшее отслеживание, но при реальной утечке требуется ротация секрета и отдельная работа с историей.
Основные выводы
- Содержимое index, а не автоматически все текущие правки.
- Нет, Git — система контроля версий, GitHub — платформа вокруг репозиториев.
Самостоятельная работа
Оформить историю Linux-чекпоинта и передать SHA проверяемого состояния.