Как объединить несколько коммитов в один

Ответ

Для объединения (squash) коммитов я использую интерактивный rebase (git rebase -i). Это стандартный способ привести историю в порядок перед мержем в основную ветку, например, в main или master, особенно в рамках Pull/Merge Request.

Типичный сценарий: У меня есть ветка feature/auth, в которой за время разработки накопилось несколько коммитов типа "WIP", "Fix typo", "Add tests". Перед созданием PR я хочу объединить их в один логичный коммит "Implement user authentication".

Процесс:

  1. Убедиться, что я на нужной ветке и рабочая директория чиста.
    git status
    git checkout feature/auth
  2. Запустить интерактивный rebase относительно точки, от которой ответвилась моя ветка (или указав количество коммитов).
    # Способ 1: Указать хэш коммита, ПРЕДШЕСТВУЮЩИЙ первому коммиту, который нужно сквошить
    git rebase -i main
    # Способ 2: Объединить последние N коммитов от HEAD
    git rebase -i HEAD~3
  3. В открывшемся редакторе я вижу список коммитов. Для коммитов, которые нужно объединить с предыдущим, я заменяю pick на squash (или просто s). Самый старый коммит в списке оставляю как pick — он станет основой.
    pick a1b2c3d Implement user authentication logic
    squash e4f5g6h Add unit tests for auth service
    squash i7j8k9l Fix typo in login error message
  4. После сохранения и закрытия редактора Git откроет еще один редактор для редактирования итогового сообщения коммита. Я удаляю автоматически сгенерированные сообщения от сквошенных коммитов и оставляю одно четкое, например:

    Implement user authentication
    
    - Add JWT-based login and registration endpoints
    - Integrate with PostgreSQL user table
    - Add comprehensive unit and integration tests
    - Update API documentation
  5. После сохранения коммит будет создан. Так как история переписана, для отправки на удаленный репозиторий потребуется force-push.
    git push origin feature/auth --force-with-lease

    Важное предупреждение: --force-with-lease безопаснее --force, так как проверит, что никто другой не пушил в эту ветку после вашего последнего fetch. Никогда не делайте force-push в общие ветки (main, develop).

Альтернатива для простых случаев: Если нужно просто объединить последние N коммитов без переупорядочивания, можно использовать git reset --soft:

git reset --soft HEAD~3  # Перемещает HEAD на 3 коммита назад, оставляя все изменения в staged состоянии
git commit -m "New combined commit message"

Ответ 18+ 🔞

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

Вот представь: ты наваял ветку feature/auth, а там коммитов, как говна за баней — овердохуища. "WIP", "Фикс опечатки", "Ещё один фикс той же опечатки", "Ой, забыл тесты". Выглядит, будто мартышлюшка на клавиатуре скакала. Такую хуйню в main пулить — это чих-пых тебя в сраку, тебя просто затрахают ревьюверы. Надо это всё в один красивый коммит сгруппировать.

Как нормальные люди делают:

  1. Первым делом, бля, проверь, где ты стоишь и не насрал ли ты мимо рабочей директории. Базовая гигиена, ебать.
    git status
    git checkout feature/auth
  2. Дальше — магия, она же интерактивный ребейз. Запускаешь и чувствуешь себя богом истории. Главное — правильно указать, откуда начинать.
    # Вариант норм: ребейз относительно ветки, от которой ты когда-то ответвился.
    git rebase -i main
    # Или проще: сквошни последние три коммита с головы.
    git rebase -i HEAD~3
  3. Откроется редактор, и тут начинается самое интересное. Видишь список своих "творений". Рядом с теми, которые надо вмазать в предыдущий, меняешь pick на squash (или просто s). Самый первый, старый коммит в этом списке оставляешь pick — он будет основным.
    pick a1b2c3d Реализовал логику аутентификации
    squash e4f5g6h Добавил тесты для сервиса
    squash i7j8k9l Пофиксил опечатку в сообщении об ошибке
  4. Сохранил, закрыл — и тут Git, хитрая жопа, спрашивает: "А какое сообщение к итоговому коммиту написать?". Тут надо не лениться. Выкинь нахуй все автогенерённые сообщения от мелких коммитов и напиши одно, но годное.

    Реализована аутентификация пользователя
    
    - Добавлены JWT эндпоинты для логина и регистрации
    - Интеграция с таблицей пользователей в PostgreSQL
    - Покрытие юнит- и интеграционными тестами
    - Обновлена документация API
  5. Всё, коммит готов. Но, ёпта, история-то переписана! Значит, пушить в удалённый репозиторий надо с силой. Только, блядь, не --force, а --force-with-lease! Это как предохранитель.
    git push origin feature/auth --force-with-lease

    Внимание, мудя! Силовой пуш — это оружие массового поражения. Ты им НИКОГДА не работаешь с общими ветками (main, develop). Только со своей feature/auth. Иначе будет вам хиросима и нигерсраки, когда кто-то ещё в эту ветку что-то закоммитил.

Лайфхак для ленивых: Если коммиты идут подряд и их просто надо слепить в кучу, без перетасовки, можно проще:

git reset --soft HEAD~3  # Откатывает HEAD на три коммита, но все изменения остаются как staged
git commit -m "Один большой и красивый коммит вместо трёх кривых"

Но это так, для бытовухи. Ребейз — мощнее и правильнее.