Работа с git и репозиториями
Повседневные правила: что попадает в историю, как выглядит коммит, как ведутся ветки и версии, что делать при ошибке. Границы репозиториев и выделение переиспользуемых единиц — в REUSE; здесь то, что происходит внутри репозитория каждый день.
5 минутПовседневные правила: что попадает в историю, как выглядит коммит, как ведутся ветки и версии, что делать при ошибке. Границы репозиториев и выделение переиспользуемых единиц — в REUSE; здесь то, что происходит внутри репозитория каждый день.
Место в цепочке
| Откуда пришли | Этот документ | Куда ведёт |
|---|---|---|
| решено, где границы репозиториев | что коммитим, как ведём ветки и версии | сборка релиза из зафиксированного состояния |
Что этот документ оставляет следующему: помеченное версией состояние, из которого воспроизводимо собирается релиз. Сборка из репозитория с незакоммиченными правками невоспроизводима — по артефакту нельзя понять, что в нём.
Что попадает в историю
| Попадает | Не попадает |
|---|---|
| исходный код | секреты, пароли, ключи, токены |
| конфигурация без секретов | файлы окружения с реальными значениями |
| миграции схемы | результаты сборки, каталоги зависимостей |
| документация проекта | временные и системные файлы редакторов |
| примеры файлов окружения без значений | большие бинарные артефакты (для них — отдельное хранилище) |
Файл .gitignore заводится первым, до первого коммита: файл, попавший в историю, оттуда не
исчезает при последующем добавлении в игнор — он остаётся во всех прошлых состояниях.
Рядом с файлом окружения держат его пример с теми же ключами и пустыми значениями: по нему видно, что нужно задать при развёртывании.
Коммит
Коммит — законченная единица: он делает одно изменение целиком и оставляет проект в рабочем состоянии. Коммит, который «доломает следующий», ломает и возможность откатиться на него.
Сообщение отвечает на вопрос «что меняется и зачем», а не пересказывает диф:
fix(gateway): не терять заголовок авторизации при перенаправлении
Перенаправление собирало новый запрос без заголовков, и клиент получал 401
после первого же редиректа.- первая строка — суть, в настоящем времени, без точки в конце;
- тело — причина и следствие, если они не очевидны;
- одно изменение — один коммит: смешанные в одном коммите правка ошибки и переименование файлов невозможно ни просмотреть, ни откатить по отдельности.
Служебные пометки об инструментах и соавторстве в сообщение не добавляются, если это не оговорено: история должна отражать содержание изменения.
Ветки
Основная ветка всегда в рабочем состоянии: из неё в любой момент собирается релиз.
Работа ведётся в отдельной ветке и вливается, когда закончена и проверена. Долгоживущие ветки избегают: чем дольше ветка живёт, тем дороже слияние, и тем позже обнаруживается конфликт замысла, а не только текста.
Ветка называется по задаче: feat/<что>, fix/<что>, chore/<что>.
Постоянные ветки под конкретного потребителя общего кода не создаются — это форк, который расходится с источником. Различия потребителей выражаются настройкой, а не веткой.
Версии и метки
Готовое к поставке состояние помечается меткой. Метка неизменяема: она указывает на конкретное состояние, и переставлять её на другое нельзя — иначе «та же версия» будет означать разный код.
Нумерация по смыслу изменения:
| Часть | Меняется, когда |
|---|---|
| старшая | контракт сломан, потребителю нужно менять код |
| средняя | добавлена возможность, старое продолжает работать |
| младшая | исправление без изменения поведения |
Для переиспользуемых единиц это обязательно: потребитель закрепляет версию и должен понимать, чем грозит обновление.
Просмотр изменений
Изменение просматривается до вливания. Что проверяется: соответствие архитектурным границам, отсутствие секретов, наличие проверок, понятность имён.
Просмотр — про содержание, а не про вкус. Замечание формулируется как проблема и её следствие («здесь бизнес-логика попала в клиент — при смене транспорта её придётся переносить»), а не как предпочтение.
Ошибки и как из них выходить
Секрет попал в репозиторий. Удаление файла следующим коммитом не помогает: значение остаётся в истории и в клонах. Порядок действий: отозвать и заменить значение (оно скомпрометировано), затем при необходимости переписать историю и предупредить всех, у кого есть клоны. Отзыв обязателен, чистка истории — вторична.
Ошибочный коммит уже в общей ветке. Исправляется обратным коммитом, а не переписыванием общей истории: перезапись ломает работу всем, у кого есть клоны.
Изменения потерялись. Локально почти ничего не пропадает бесследно: git reflog показывает
состояния, на которых был репозиторий, включая те, что уже не видны из веток.
Тяжёлый файл попал в историю. Репозиторий не уменьшится сам: объект остаётся во всех прошлых состояниях. Крупные файлы кладут в отдельное хранилище с самого начала.
Обязательное перед выкладыванием
- в истории нет секретов — проверяется поиском по репозиторию и по истории, а не по текущим файлам;
- в репозитории лежит пример файла окружения, а не сам файл;
- рабочее дерево чисто, состояние помечено;
- описание проекта отвечает, что это, как запустить и как проверить, что запустилось.
Типичные отказы
| Признак | Причина | Что делать |
|---|---|---|
| сборка релиза отказывается стартовать | незакоммиченные правки | зафиксировать или убрать; релиз собирается из чистого состояния |
файл в .gitignore, но продолжает отслеживаться | попал в историю раньше игнора | убрать из индекса; при секрете — отозвать значение |
| репозиторий разросся до гигабайтов | бинарные артефакты в истории | вынести в отдельное хранилище; историю чистить отдельной операцией |
| слияние ветки превращается в отдельную работу | ветка жила слишком долго | вливать чаще, дробить задачу |
| «та же версия» ведёт себя по-разному | метку переставили | метки неизменяемы, выпустить новую |
| откат на прошлый коммит ломает сборку | коммиты не самодостаточны | один коммит — одно законченное изменение |
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.