# Git Workflow And Versioning

> Выстраивает практики работы с git. Используй при любом изменении кода. Используй при коммитах, ветвлении, разрешении конфликтов или когда нужно организовать работу в нескольких параллельных потоках. Используй при выпуске релиза, выборе семантического поднятия версии, простановке тегов или написании changelog.

- Skill: `aleksandr-litvinenko/git-workflow-and-versioning` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aleksandr-litvinenko/git-workflow-and-versioning`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aleksandr-litvinenko/git-workflow-and-versioning/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: Aleksandr-Litvinenko (https://skillmd.com/u/aleksandr-litvinenko)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aleksandr-litvinenko/git-workflow-and-versioning

---


# Работа с git и версионирование

## Обзор

Git — твоя страховочная сетка. Относись к коммитам как к точкам сохранения, к веткам — как к песочницам, к истории — как к документации. Когда AI-агенты порождают код на высокой скорости, дисциплина контроля версий — это механизм, который держит изменения управляемыми, пригодными для ревью и обратимыми.

## Когда применять

Всегда. Любое изменение кода проходит через git.

## Базовые принципы

### Разработка вокруг ствола (рекомендуется)

Держи `main` всегда готовой к выкатке. Работай в короткоживущих ветках фич, которые вливаются обратно за 1–3 дня. Долгоживущие ветки разработки — это скрытые издержки: они расходятся, порождают конфликты слияния и откладывают интеграцию. Исследования DORA стабильно показывают, что разработка вокруг ствола коррелирует с высокоэффективными инженерными командами.

```
main ──●──●──●──●──●──●──●──●──●──  (всегда готова к выкатке)
        ╲      ╱  ╲    ╱
         ●──●─╱    ●──╱    ← короткоживущие ветки фич (1–3 дня)
```

Это рекомендуемое умолчание. Команды, использующие gitflow или долгоживущие ветки, могут адаптировать принципы (атомарные коммиты, мелкие изменения, описательные сообщения) под свою модель ветвления — дисциплина коммитов важнее конкретной стратегии ветвления.

- **Ветки разработки — это издержки.** Каждый день жизни ветки накапливает риск слияния.
- **Релизные ветки приемлемы.** Когда нужно стабилизировать релиз, пока main движется вперёд.
- **Фича-флаги > длинные ветки.** Лучше выкатывать незаконченную работу под флагом, чем неделями держать её в ветке.

### 1. Коммить рано, коммить часто

Каждый успешный инкремент получает свой коммит. Не копи большие незакоммиченные изменения.

```
Рабочий ритм:
  Реализовал срез → Тест → Проверил → Коммит → Следующий срез

А не так:
  Реализовал всё → Понадеялся, что работает → Гигантский коммит
```

Коммиты — это точки сохранения. Если следующее изменение что-то сломает, можно мгновенно откатиться к последнему заведомо рабочему состоянию.

### 2. Атомарные коммиты

Каждый коммит делает одну логическую вещь:

```
# Хорошо: каждый коммит самодостаточен
git log --oneline
a1b2c3d Add task creation endpoint with validation
d4e5f6g Add task creation form component
h7i8j9k Connect form to API and add loading state
m1n2o3p Add task creation tests (unit + integration)

# Плохо: всё смешано
git log --oneline
x1y2z3a Add task feature, fix sidebar, update deps, refactor utils
```

### 3. Описательные сообщения

Сообщения коммитов объясняют *почему*, а не только *что*:

```
# Хорошо: объясняет замысел
feat: add email validation to registration endpoint

Prevents invalid email formats from reaching the database.
Uses Zod schema validation at the route handler level,
consistent with existing validation patterns in auth.ts.

# Плохо: описывает то, что и так видно в диффе
update auth.ts
```

**Формат:**
```
<тип>: <короткое описание>

<необязательное тело, объясняющее почему, а не что>
```

**Типы:**
- `feat` — новая функциональность
- `fix` — исправление бага
- `refactor` — изменение кода, которое не чинит баг и не добавляет функциональности
- `test` — добавление или обновление тестов
- `docs` — только документация
- `chore` — инструменты, зависимости, конфигурация

### 4. Разделяй разные вещи

Не смешивай изменения форматирования с изменениями поведения. Не смешивай рефакторинг с фичами. Каждый тип изменения — отдельный коммит, а в идеале отдельный пулл-реквест:

```
# Хорошо: разное — раздельно
git commit -m "refactor: extract validation logic to shared utility"
git commit -m "feat: add phone number validation to registration"

# Плохо: всё вперемешку
git commit -m "refactor validation and add phone number field"
```

**Отделяй рефакторинг от работы над фичей.** Рефакторинг и фича — два разных изменения, отправляй их раздельно. Так каждое изменение легче отревьюить, откатить и понять в истории. Мелкие уборки (переименование переменной) можно на усмотрение ревьюера включить в коммит с фичей.

### 5. Соразмеряй изменения

Целься в ~100 строк на коммит/пулл-реквест. Изменения больше ~1000 строк надо разбивать. Как разбивать крупные изменения — см. стратегии разбиения в `code-review-and-quality`.

```
~100 строк   → Легко отревьюить, легко откатить
~300 строк   → Приемлемо для одного логического изменения
~1000 строк  → Разбить на более мелкие изменения
```

## Стратегия ветвления

### Ветки фич

```
main (всегда готова к выкатке)
  │
  ├── feature/task-creation    ← Одна фича на ветку
  ├── feature/user-settings    ← Параллельная работа
  └── fix/duplicate-tasks      ← Исправления багов
```

- Ответвляйся от `main` (или ветки по умолчанию, принятой в команде)
- Держи ветки короткоживущими (вливай за 1–3 дня) — долгоживущие ветки это скрытые издержки
- Удаляй ветки после вливания
- Для незаконченных фич предпочитай фича-флаги долгоживущим веткам

### Именование веток

```
feature/<короткое-описание>   → feature/task-creation
fix/<короткое-описание>       → fix/duplicate-tasks
chore/<короткое-описание>     → chore/update-deps
refactor/<короткое-описание>  → refactor/auth-module
```

## Работа с worktree

Для параллельной работы AI-агентов используй git worktree, чтобы вести несколько веток одновременно:

```bash
# Создать worktree под ветку фичи
git worktree add ../project-feature-a feature/task-creation
git worktree add ../project-feature-b feature/user-settings

# Каждый worktree — отдельный каталог со своей веткой
# Агенты могут работать параллельно, не мешая друг другу
ls ../
  project/              ← ветка main
  project-feature-a/    ← ветка task-creation
  project-feature-b/    ← ветка user-settings

# Когда закончил — влить и прибраться
git worktree remove ../project-feature-a
```

Преимущества:
- Несколько агентов могут одновременно работать над разными фичами
- Не нужно переключать ветки (у каждого каталога своя)
- Если эксперимент провалился — удали worktree, ничего не потеряно
- Изменения изолированы до явного слияния

## Паттерн точек сохранения

```
Агент начинает работу
    │
    ├── Вносит изменение
    │   ├── Тест прошёл? → Коммит → Дальше
    │   └── Тест упал? → Откат к последнему коммиту → Разбираться
    │
    ├── Вносит следующее изменение
    │   ├── Тест прошёл? → Коммит → Дальше
    │   └── Тест упал? → Откат к последнему коммиту → Разбираться
    │
    └── Фича готова → Все коммиты образуют чистую историю
```

Этот паттерн означает, что ты никогда не теряешь больше одного инкремента работы. Если агент пошёл вразнос, `git reset --hard HEAD` вернёт к последнему успешному состоянию.

## Сводки изменений

После любой правки давай структурированную сводку. Это упрощает ревью, документирует дисциплину границ и вскрывает непреднамеренные изменения:

```
ЧТО ИЗМЕНЕНО:
- src/routes/tasks.ts: добавлен middleware валидации на POST-эндпоинт
- src/lib/validation.ts: добавлена TaskCreateSchema на Zod

ЧЕГО Я НЕ ТРОГАЛ (намеренно):
- src/routes/auth.ts: там похожая дыра в валидации, но это вне задачи
- src/middleware/error.ts: формат ошибок можно улучшить (отдельная задача)

ВОЗМОЖНЫЕ ВОПРОСЫ:
- Схема Zod строгая — отклоняет лишние поля. Подтвердите, что так и надо.
- Добавлен zod как зависимость (72 КБ в gzip) — уже был в package.json
```

Этот паттерн рано ловит неверные допущения и даёт ревьюерам ясную карту изменения. Раздел «Чего я не трогал» особенно важен: он показывает, что ты соблюдал границы и не устроил самовольный ремонт.

## Гигиена перед коммитом

Перед каждым коммитом:

```bash
# 1. Посмотреть, что именно коммитишь
git diff --staged

# 2. Убедиться, что нет секретов
git diff --staged | grep -i "password\|secret\|api_key\|token"

# 3. Прогнать тесты
npm test

# 4. Прогнать линтер
npm run lint

# 5. Прогнать проверку типов
npx tsc --noEmit
```

Автоматизируй это через git-хуки:

```json
// package.json (через lint-staged + husky)
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md}": ["prettier --write"]
  }
}
```

## Что делать со сгенерированными файлами

- **Коммить сгенерированные файлы** только если проект этого ожидает (например, `package-lock.json`, миграции Prisma)
- **Не коммить** результат сборки (`dist/`, `.next/`), файлы окружения (`.env`) и настройки IDE (`.vscode/settings.json`, если они не общие)
- **Иметь `.gitignore`**, покрывающий: `node_modules/`, `dist/`, `.env`, `.env.local`, `*.pem`

## Использование git для отладки

```bash
# Найти коммит, который внёс баг
git bisect start
git bisect bad HEAD
git bisect good <known-good-commit>
# Git переключается на середины диапазона; на каждой гоняй свой тест, чтобы сузить

# Посмотреть, что изменилось недавно
git log --oneline -20
git diff HEAD~5..HEAD -- src/

# Найти, кто последним менял конкретную строку
git blame src/services/task.ts

# Поискать ключевое слово в сообщениях коммитов
git log --grep="validation" --oneline
```

## Релизы и версионирование

Коммиты — то, как отслеживаешь изменения *ты*; **версия** — то, как их отслеживают *твои потребители*. В тот момент, когда от твоего кода начинает зависеть что-то ещё — другая команда, опубликованный пакет, развёрнутый клиент, — «последнее из main» перестаёт быть достаточным ответом на вопрос «что у меня запущено и безопасно ли обновляться?». Номер версии и changelog — это контракт, который на него отвечает.

### Семантическое версионирование

Для всего, у чего есть потребители, версионируй как `MAJOR.MINOR.PATCH` и пусть число несёт смысл:

```
  MAJOR  ломающее изменение — потребителям придётся менять свой код, чтобы обновиться
  MINOR  новая функциональность, обратно совместимая — обновляться безопасно
  PATCH  исправление бага, обратно совместимое — обновляться безопасно
```

Число — это обещание, поэтому пусть код ему соответствует. «Патч», меняющий поведение, на которое полагались потребители, — это мажорное изменение в маскировке (закон Хайрама, см. скилл `api-and-interface-design`). Когда неясно, ломающее ли изменение, считай, что ломающее: неожиданный мажор гораздо дешевле сломанного потребителя.

### Помечай релиз тегом, и пусть тег будет источником истины

Релиз — неизменяемая точка в истории, а не движущаяся ветка. Помечай его тегом, чтобы его всегда можно было воспроизвести:

```bash
git tag -a v1.4.0 -m "Release 1.4.0"
git push origin v1.4.0
```

Выводи версию из тега, а не правь её руками в разбросанных файлах, — тогда артефакт, тег и changelog никогда не разойдутся.

### Веди changelog, написанный для людей

Changelog — это не `git log`. Это отобранный, обращённый к потребителю ответ на вопрос «что изменилось и важно ли мне это?» — сгруппированный по разделам `Добавлено / Изменено / Исправлено / Устарело / Удалено / Безопасность`, свежее сверху, каждая запись сформулирована через влияние на пользователя, а не через внутреннюю механику.

```markdown
## [1.4.0] - 2025-06-12
### Добавлено
- Массовый импорт задач через CSV
### Исправлено
- Сдвиг часовых поясов в сроках повторяющихся задач
### Устарело
- `GET /v1/tasks/all` — используйте постраничный `GET /v1/tasks` (удаление в 2.0)
```

Пиши запись в том же изменении, которое её вызывает, пока влияние свежо, а не восстанавливай раскопками по коммитам во время релиза. Ломающие изменения получают заметку о миграции и окно устаревания (по скиллу `deprecation-and-migration`); собственно выкатка релиза — работа скилла `shipping-and-launch`, а этот раздел — контракт версионирования, который его питает.

## Типовые самооправдания

| Самооправдание | Как на самом деле |
|---|---|
| «Закоммичу, когда фича будет готова» | Один гигантский коммит невозможно отревьюить, отладить или откатить. Коммить каждый срез. |
| «Сообщение не важно» | Сообщения — это документация. Будущему тебе (и будущим агентам) понадобится понять, что изменилось и почему. |
| «Потом всё сожму в один коммит» | Сжатие уничтожает повествование о разработке. Лучше сразу делать чистые инкрементальные коммиты. |
| «Ветки — лишние накладные расходы» | Короткоживущие ветки бесплатны и не дают конфликтующей работе столкнуться. Проблема в долгоживущих — вливай за 1–3 дня. |
| «Разобью это изменение потом» | Крупные изменения труднее ревьюить, рискованнее выкатывать и труднее откатывать. Разбивай до отправки, а не после. |
| «Мне не нужен .gitignore» | До того момента, пока не закоммитится `.env` с продакшн-секретами. Настрой немедленно. |
| «Это же мелкая правка, подниму патч» | Проверь, что могут наблюдать потребители. Изменение поведения, на которое они полагались, — это мажор, каким бы маленьким ни был дифф. |
| «Changelog — это просто лог коммитов» | Коммиты для тебя, changelog для потребителей и отобран по влиянию. Сгенерированный из сырых коммитов, он хоронит главное. |
| «Напишем changelog во время релиза» | К тому моменту влияние восстанавливается по памяти, и половина теряется. Пиши запись вместе с изменением. |

## Тревожные признаки

- Копятся большие незакоммиченные изменения
- Сообщения коммитов вида «fix», «update», «misc»
- Изменения форматирования вперемешку с изменениями поведения
- В проекте нет `.gitignore`
- Коммитятся `node_modules/`, `.env` или артефакты сборки
- Долгоживущие ветки, сильно разошедшиеся с main
- Принудительная отправка (force-push) в общие ветки
- Ломающее изменение выпущено под минорным или патч-поднятием версии
- Релиз без тега или номер версии, правленный руками и разошедшийся с тегом
- Пользовательский релиз без записи в changelog или changelog, куда просто свалены сообщения коммитов

## Проверка

Для каждого коммита:

- [ ] Коммит делает одну логическую вещь
- [ ] Сообщение объясняет «почему» и следует соглашениям о типах
- [ ] Тесты проходят до коммита
- [ ] В диффе нет секретов
- [ ] Изменения только форматирования не смешаны с изменениями поведения
- [ ] `.gitignore` покрывает стандартные исключения

Для каждого релиза (всего, у чего есть потребители):

- [ ] Поднятие версии соответствует изменению: ломающее → мажор, добавляющее → минор, исправление → патч
- [ ] Релиз помечен тегом, а версия выведена из тега, а не правлена руками вразнобой
- [ ] В changelog есть отобранная, читаемая человеком запись для этой версии, сгруппированная по влиянию

