Что Git сохраняет: файл, снимок и история
Зачем нужна история
Вы пишете описание сервиса, затем меняете правило работы очереди. Через день выясняется, что прежнее правило было правильным. Копии plan-final, plan-final-new и plan-final-really-new не объясняют, кто и зачем их сделал. Git позволяет сохранить выбранное состояние файлов вместе с сообщением и позже сравнить его с другой версией. В этом курсе мы будем менять маленький текстовый проект: для первого знакомства не нужно уметь программировать.
Git работает на вашем компьютере. GitHub — отдельный сервис для размещения репозиториев и обсуждения изменений. Коммит можно сделать без интернета, аккаунта и удалённого сервера. Но коммит, оставшийся только на одном диске, не заменяет резервную копию. Если диск исчезнет вместе с репозиторием, локальная история тоже исчезнет.
Три области
Рабочее дерево, или worktree, — файлы, которые вы открываете в редакторе. Индекс, или staging area, — выбранное содержимое для следующего коммита. Репозиторий хранит уже созданные коммиты и ссылки на них, обычно в скрытой папке .git. Редактирование меняет рабочее дерево; git add обновляет индекс; git commit записывает подготовленный снимок. Это три разные действия.
Снимок описывает состояние отслеживаемых файлов целиком. Внутри Git одинаковое содержимое может использоваться повторно, поэтому модель снимков не означает физическую копию всей папки для каждого коммита. У коммита есть идентификатор, сообщение, автор, сведения о времени и ссылки на родителей. Разница между коммитами вычисляется при сравнении. Когда позже появится слияние, у одного коммита смогут быть два родителя.
Имя ветки указывает на коммит. HEAD обычно указывает на текущую ветку: это ответ на вопрос, где продолжать историю. Файл, который Git пока не отслеживает, называется untracked. Он существует на диске, но сам по себе не попадёт в снимок. У Git нет обещания автоматически сохранять всё, что вы напечатали. Определения областей и ссылок полезны, если названия начинают смешиваться.
Первый наблюдаемый опыт
Нужен Git 2.28 или новее и оболочка bash: на Windows откройте Git Bash, на macOS/Linux — терминал. Установку подробно разберём в следующей главе. mktemp создаёт новую временную папку; команды ниже выполняйте в ней, а не в своём рабочем проекте. $lab — переменная с путём. printf записывает текст, > заменяет содержимое файла, >> дописывает его.
lab=$(mktemp -d "${TMPDIR:-/tmp}/git-01.XXXXXX")
cd "$lab"
git init -b main
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
git config commit.gpgsign false
printf 'queue=16\n' > plan.txt
git status --short
git add plan.txt
printf 'workers=2\n' >> plan.txt
git diff -- plan.txt
git diff --cached -- plan.txt
git commit -m "Describe queue capacity"
git show HEAD:plan.txt
cat plan.txtПосле первого status видно ?? plan.txt. После add индекс содержит только queue=16. Обычный diff показывает добавление workers относительно индекса; diff --cached — подготовленный файл относительно последнего коммита, которого ещё нет. Коммит содержит одну строку, хотя рабочий файл уже содержит две. git show HEAD:plan.txt и cat должны вывести разные варианты. Так вы наблюдаете границу сохранения, а не верите названию команды.
Хеш в выводе у вас будет другим: не копируйте его из чужого скриншота. Дата и автор влияют на идентификатор коммита. Для наших проверок важны содержимое и отношения родителей, а не конкретный набор символов.
Проверка понимания до следующего шага
Вы подготовили version A, заменили рабочий файл на version B и сделали commit без повторного add. Сосед говорит, что Git сохранил B, потому что она была открыта в редакторе последней. Как показать ошибку этого рассуждения? Прочитайте снимок через show и сравните с рабочим файлом. В коммит попала A: важен выбранный индекс, а не момент последнего открытия. Теперь представьте, что редактор вообще не сохранил B на диск. Тогда Git не увидит даже рабочую B; сначала должно произойти обычное сохранение файла. Это отдельная граница между редактором и файловой системой, которую кнопка commit не заменяет.
Самостоятельная лаборатория
Практика займёт примерно 20–35 минут. Создайте во временном репозитории файл notes.txt со строкой first. Подготовьте её через add, затем допишите second, но не вызывайте add повторно. Предскажите содержимое следующего коммита и проверьте прогноз. Затем сохраните оставшуюся строку вторым коммитом. Добавьте третий файл и оставьте его неотслеживаемым.
Запишите таблицу из трёх строк: рабочий файл, индекс, последний коммит. Для каждой укажите, чем вы прочитали содержимое. Не подменяйте чтение индекса обычным cat: он всегда показывает рабочий файл. Индекс можно посмотреть через git show :notes.txt, последний снимок — через git show HEAD:notes.txt.
Критерии готовности
Вы создали два коммита, объяснили разницу между add и commit и показали файл, отсутствующий в истории. Перед каждым сохранением можете сказать, какие строки попадут в снимок. Чистый status без понимания трёх областей сам по себе недостаточен: можно случайно сохранить не те файлы и тоже получить чистое дерево.
Разбор
Первый коммит лаборатории должен содержать first, второй — обе строки. Неотслеживаемый файл не появляется в git ls-tree --name-only HEAD. Если первая запись уже содержит second, вы повторно добавили изменённый файл или использовали другую команду подготовки. Это исправляется повторением опыта в новой папке, а не удалением непонятной истории из чужого проекта.
Дальше настроим Git и сделаем осмысленный первый коммит. Модель команд проверяется по git add, а просмотр снимка — по git ls-tree. Учебный принцип «сначала модель, затем команды» обсуждается в MIT Missing Semester; здесь вы применили его к собственному опыту.