Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Squash в Git: объединение коммитов
Squash — это операция в Git, которая объединяет несколько коммитов в один. Она полезна для очистки истории, перед тем как мерджить pull request в основную ветку. Squash убирает лишние intermediate коммиты и создаёт чистую историю разработки.
Основная идея
Вместо истории вроде:
commit 3: Fix typo
commit 2: WIP: refactoring
commit 1: Add new feature
Squash создаёт:
commit 1: Add new feature (includes fixes and refactoring)
Все изменения остаются, но история становится линейной и понятной.
Типы Squash операций
1. Interactive Rebase (самый гибкий)
# Объединить последние 3 коммита
git rebase -i HEAD~3
Отворится интерактивный редактор:
pick 1234567 Add new feature
pick 2345678 WIP: refactoring
pick 3456789 Fix typo
Изменяем на:
pick 1234567 Add new feature
squash 2345678 WIP: refactoring # или 's' вместо 'squash'
squash 3456789 Fix typo
Сохраняем (:wq в Vim), и Git просит новое сообщение коммита:
# Combine commits
Add new feature
WIP: refactoring
Fix typo
Редактируем в итоговое:
Add new feature with refactoring
Результат:
$ git log --oneline -3
1234567 Add new feature with refactoring
2. Soft Reset (простой способ)
# Вернуться на 3 коммита назад, но сохранить изменения
git reset --soft HEAD~3
# Теперь все файлы в staging area (green в 'git status')
git status
# modified: file1.py
# modified: file2.py
# Коммитим один раз
git commit -m "Add new feature with refactoring and fixes"
Это эквивалент squash, но без interactive rebase.
3. GitHub Squash при Merge
На GitHub можно squash автоматически при мерже PR:
┌─────────────────────┐
│ Pull Request #123 │
│ "Add feature X" │
├─────────────────────┤
│ Commits: │
│ - WIP: start │
│ - fix bug │
│ - refactor │
│ - final polish │
└─────────────────────┘
↓ (merge with "Squash and merge")
┌─────────────────────┐
│ Main branch │
│ - Add feature X │ ← Все 4 коммита в один
└─────────────────────┘
На GitHub:
- Open Pull Request
- Нажимаем на "Squash and merge" (вместо обычного "Merge")
- GitHub автоматически объединит все коммиты
Реальный пример: разработка feature
До Squash (история грязная)
$ git log --oneline
a1b2c3d Fix typo in docstring
5f6g7h8 Refactor payment validation
9i0j1k2 WIP: payment module
3l4m5n6 Add payment support
main Previous release
Squash процесс
# Находимся на feature ветке
git checkout feature/payment
# Начинаем interactive rebase от main
git rebase -i main
# или
git rebase -i HEAD~4 # последних 4 коммита
В редакторе:
pick 3l4m5n6 Add payment support
s 9i0j1k2 WIP: payment module # squash
s 5f6g7h8 Refactor payment validation # squash
s a1b2c3d Fix typo in docstring # squash
Сохраняем, редактируем message:
Add payment module with validation
- Implement payment processing
- Add validation for payment data
- Fix documentation
Результат:
$ git log --oneline
newsha123 Add payment module with validation
main Previous release
Пушим на GitHub
# Нужно force-push, так как переписали историю
git push origin feature/payment --force-with-lease
Важно: --force-with-lease безопаснее чем --force, потому что проверит, что никто не запушил новые коммиты с тех пор.
Squash vs Merge vs Rebase
| Операция | Коммиты | История | Когда использовать |
|---|---|---|---|
| Merge | Оба ветки | Две ветви | Долгоживущие feature ветки |
| Rebase | Переписываются | Линейная | Чистая история, малые фичи |
| Squash | Один новый | Очень чистая | Pull Requests, готовый код |
Merge:
o--o--o (main)
| \
| o--o--o (feature)
| |
o--o--o--------o (merge commit)
Rebase:
o--o--o (main)
|
o--o--o (feature, перезаписано)
Squash:
o--o--o (main)
|
| o (один коммит со всеми изменениями)
| /
o--o (merged)
Практические примеры
Пример 1: Разработчик перед PR
# На локальной ветке 10 коммитов, но хотим отправить один PR
git log --oneline
head~9 Initial idea
head~8 WIP
head~7 Fix issue
head~6 Refactor
head~5 Add tests
head~4 Fix tests
head~3 Review changes
head~2 Final touches
head~1 Typo fix
head Done
# Squashим
git rebase -i main
# Все коммиты после main делаем 'squash'
# Результат: один красивый коммит в PR
Пример 2: Очистка грязной истории
# Текущая ветка
git log --oneline | head -5
a1b2c3d oops, forgot this
9i0j1k2 WIP WIP WIP
5f6g7h8 Actually this
3l4m5n6 Start here
# Squashим последние 4 коммита
git reset --soft HEAD~4
git commit -m "Implement feature properly"
# История чистая
git log --oneline | head -1
newsha1 Implement feature properly
Пример 3: GitHub через Web UI
1. Открываем PR
2. Все коммиты видны в "Commits" табе
3. Нажимаем кнопку "Squash and merge"
4. GitHub автоматически объединит коммиты
5. Ветка удаляется (если включено)
Опасности и как их избежать
Опасность 1: Потеря истории при --force
# Опасно! Может затереть работу других
git push origin feature --force
# Безопасно! Проверит, что ничего не потеряется
git push origin feature --force-with-lease
Опасность 2: Squash в общей ветке
# Плохо: squash commits в main
# Это переписывает историю, которая уже на сервере!
git checkout main
git rebase -i HEAD~3 # ← НИКОГДА так не делай
# Хорошо: squash только в own feature ветке
git checkout feature/my-feature
git rebase -i main # ← Тут OK
Опасность 3: Забыть --soft
# Потеряешь все изменения!
git reset --hard HEAD~3 # ← ОПАСНО!
# Правильно для squash
git reset --soft HEAD~3 # ← Сохраняет изменения
Лучшие практики
- Squash перед PR: Очищай историю перед отправкой на review
- Используй --force-with-lease: Безопаснее чем --force
- Никогда squash не в feature ветке: Только в своих ветках
- Хорошее commit message: После squash напиши ясный message
- На GitHub используй "Squash and merge": Вместо manual rebase
Отмена squash
# Если случайно squashил и хочешь отмену
git reflog # Найди старый HEAD
git reset --hard a1b2c3d # Вернись к старому состоянию
Squash — мощный инструмент для сохранения чистоты Git истории. Его главная цель — сделать историю проекта понятной и легко читаемой при просмотре git log!