Git squash что это
Перейти к содержимому

Git squash что это

  • автор:

Изучаем Git. Урок 16.
Как склеивать коммиты,
git rebase —interactive и git reflow

Краткое содержание урока, основные инструкции для командной строки, полезные ссылки и советы.

Зачем склеивать коммиты

Причины могут быть разные:
1. Несколько коммитов с мелкими правками багов
2. Правка замечаний на код-ревью
3. Соглашения в команде о мердже в мастер только одного коммита
4. Решение в процессе работы объединить логически связанные коммиты

Как склеивать коммиты, используя функционал git reset

Пример, нам нужно склеить 3 последних коммита. Можно сделать это так. Сначала сбрасываем 3 коммита без удаления его содержимого

А затем коммитим изменения с новым commit message

Что такое git rebase в интерактивном режиме

Это мощный инструмент редактирования истории коммитов. Он позволяет не только склеивать (сквошить) коммиты, но переименовывать их, редактировать содержимое коммитов и удалять коммиты.

Как запустить git rebase

Числом указываем количество коммитом, которое хотим затронуть. Дальше открывается текстовый редактор, где у коммитов нужно проставить нужные опции

Самые популярные опции редактирования коммитов

p, pick — означает использовать коммит или оставить его, то есть не трогать

r, reword — переименовать коммит

e, edit — редактировать коммит. Здесь имеется в виду редактирование содержимого коммита

s, squash — сквош или склеивание коммитов

f, fixup — это то же самое, что и сквош, только с той разницей, что гит не будет предлагать самим написать commit message, а возьмет его из того коммита, к которому приклеиваются остальные

d, drop — удалить коммит

git reflog

Что делать, если мы провели ошибочные операции git rebase -i и теперь хотим все отменить?

Все операции переписывания коммитов git заносит в отдельный журнал, который можно посмотреть с помощью git reflog

Пример журнала git reflog

Отмена операций ребейза

С помощью git reflog нужно найти хэш коммита до ошибочной операции git rebase -i. В примере выше такой может быть строка (подробности в видео)

Чтобы откатить историю коммитов к этому состоянию, нужно выполнить git reset —hard с указанием нужного хэша, то есть

Совет от автора

Используйте инструмент git reflow только в крайних случаях. В журнале reflow легко ошибиться и выбрать не тот хэш

Возможно, лучше вместо редактирования коммита стоит докинуть нужные правки поверх, а вместо удаления коммита сделать git revert

Спасибо за внимание и до встречи!

Все уроки курса

  • Вводный урок
  • 1. Установка и базовая настройка git
  • 2. Создание и клонирование репозитория git
  • 3. Делаем первые изменения, git status и git diff
  • 4. Коммиты и история коммитов, git commit, git log и git show
  • 5. Подробнее об истории коммитов. Путешествие по истории
  • 6. Работа с сервером, git push и git pull
  • 7. Ветки — главная фишка git, git branch и git checkout
  • 8. Работа с ветками на сервере, git fetch
  • 9. Слияния или мерджи веток, git merge
  • 10. Конфликты и их разрешение
  • Платная часть курса. Презентация
  • * 11. Работа с gitignore и git exclude
  • * 12. Буфер обмена git, git stash
  • * 13. Копирование коммитов, git cherry-pick
  • * 14. Отмена и редактирование последнего коммита
  • * 15. Отмена произвольного коммита, git revert
  • 16. Склеивание коммитов, git rebase —interactive и git reflog
  • * 17. Зачем склеивать коммиты. Плюсы и минусы сквоша
  • * 18. Работа с git rebase. Отличия от merge
  • * 19. Что такое git push —force и как с ним работать
  • * 20. Ищем баги с помощью git, git bisect
  • * 21. Как и зачем работать с тегами git
  • * 22. Процессы: github flow и git flow
  • * 23. Псевдонимы в git
  • 24. Мердж-реквесты
  • * 25. Форки

Функция, которую мне хотелось бы видеть в Git: группы коммитов

Почти все любят Git. Я тоже. Он работает, он эффективен, в нём изумительная модель данных, и в нём есть все возможные инструменты. За 13 лет использования не было случая, чтобы я не находил в Git нужный мне инструмент. До недавнего времени. Но сначала давайте поговорим о GitHub.

Есть три группы пользователей GitHub, которые различаются по предпочитаемому способу внесения запросов на внесение изменений (pull request):

Merge-коммит, squash коммитов в один или rebase? На этот вопрос нет однозначного ответа. При выборе стратегии слияния приходится учитывать ряд факторов: тип и размер проекта, рабочий процесс и предпочтения команды, бизнес-условия и прочее. Возможно, у вас есть собственные предпочтения, если вы использовали GitHub для работы с командой.

Мы немного поговорим о достоинствах и недостатках каждого подхода. Но сначала опишу исходные условия. Пусть у вашего проекта есть ветка main, от которой когда-то ответвилась ветка feature. C тех пор обе ветки развивались, сейчас feature уже проверена, протестирована и готова к слиянию с main:

Создаём merge-коммит

Merge-коммиты — это исходный способ объединения изменений в Git. У такого коммита две и более родительских веток, он собирает все изменения из них и их предшественников:

В этом примере Git создал новый коммит под номером 9, в котором объединились коммиты 6 и 8. На него теперь ссылается ветка main, в этом коммите содержатся все изменения из коммитов 1—8.

Merge-коммиты универсальны и хорошо масштабируются, особенно в случае со сложными рабочими процессами с многочисленными участниками, каждый из которых отвечает за свою часть кода. Например, такие коммиты повсеместно используют разработчики ядра Linux. Но для маленьких Agile-команд (особенно в контексте бизнеса) они избыточны и могут доставить неприятности. В подобных командах обычно используют одну вечную ветку, на основе которой создают эксплуатационные релизы и в которую добавляют изменения из временных веток. При такой организации работы трудно оценить развитие проекта. GitFlow, популярный инструмент для работы с Git, рекомендует везде использовать merge-коммиты, и люди с этим не согласны. Приведу визуальный аргумент из одной темы:

Отбросив факт, что эта история наполнена merge-коммитами, автор обращает наше внимание на то, что найти что-либо в таком запутанном графе практически невозможно. Правда это или нет, решайте сами, но доводы в пользу линейной истории изменений определённо есть.

Есть и другая, часто упускаемая из виду особенность. Взгляните ещё раз на схему с merge-коммитом под номером 9. Можете ли вы сказать, какой коммит был последним в ветке main до слияния? Конечно, это должен быть 8, потому что он изображён на серой линии, верно? Ага, на картинке это показано так. Но если вы взглянете на сам коммит, это будет вовсе не так очевидно. Вот что в нём говорится:

Merge: 8 6 

Коммит сообщает, что эти две родительские ветки объединены, но здесь не сказано, какой коммит относится к ветке main. Вы можете предположить, что это 8, поскольку значение находится слева, но вы не можете быть уверены (ветки в Git — это лишь номера коммитов). Единственный (известный мне) способ удостовериться, это использовать reflog, но он не долговечен: Git время от времени удаляет из reflog старые записи. То есть вы не можете с уверенностью ответить на вопрос «какая фича была выпущена в заданный период времени?» или «каково было состояние main на указанную дату?» В том числе и поэтому вы не можете применить команду git revert к коммиту слияния, если не скажете Git’у, какие из родительских коммитов вы хотите сохранить, а какие отбросить.

Слияние без связи с источником

В случае с merge-коммитами мы не переписываем историю: сделанный коммит остаётся неизменным, репозиторий лишь разрастается. Другие два вида коммитов используют возможность Git переписывать историю. Как мы увидим ниже, суть у них та же, а отличаются эти коммиты только подробностью изменений.

Вернёмся к нашему примеру. В случае со слиянием без связи с источником (squash) мы объединяем изменения из коммитов 4, 5 и 6 в один коммит (S), а затем применяем его поверх main.

Ветка feature осталась, но я не показал её на иллюстрации, потому что он больше не релевантна и обычно удаляется при слиянии (что может быть не лучшим решением, как мы увидим ниже). У этого подхода много достоинств, и некоторые команды его популяризируют. Пожалуй, самым заметным и значительным достоинством является то, что история становится очень удобочитаемой. Она линейна, между коммитами в main и запросами на внесение изменений есть прямое соответствие (как и в случае с большинством фич и исправлений багов). Такая история очень помогает в управлении проектом: можно очень легко ответить на вопросы, на которые практически невозможно дать ответ в случае с merge-коммитами.

Накат изменений из одной ветки в другую

Этот подход аналогичен предыдущему, только мы не объединяем коммиты 4—6, а накатываем их прямо поверх main.

Но сначала длинное отступление. По скриншоту в начале статьи вы могли предположить, что я придерживаюсь этого подхода, и будете правы. Раньше я пользовался squash-коммитами, но перешёл на rebase после того как появилась фича, больше всего повлиявшая на качество моей работы за последние годы: я начал писать осмысленные коммит-сообщения.

Ещё недавно мои сообщения были однострочными, например, как в истории Skyscraper. Эти первые строки не сильно изменились, но теперь я стараюсь добавлять в них объяснения причин изменений. При исправлении бага объясняю, что его вызвало и как изменения исправили баг. При реализации фичи я подчёркиваю особенности реализации. Возможно, я не стал писать больше кода, но точно стал писать больше текста: я вполне могу написать два-три абзаца про какое нибудь изменение +1/−1. То есть теперь мои коммит-сообщения выглядят так (взял случайный недавний пример из репозитория приложения Fy!):

app/tests: allow to mock config Tests expected the code-push events to fire, but now that I’ve disabled CP in dev, and the tests are built with the dev aero profile, they’d fail. This could have been fixed by building them with AERO_PROFILE=staging in CI, but it doesn’t feel right: I think tests shouldn’t depend on varying configuration. If a test requires a given bit of configuration to be present, it’s better to configure it that way explicitly. Hence this commit. It adds a wrap-config mock and a corresponding :extra-config fixture, which, when present (and it is by default), will merge the value onto generated-config. 

Я очень серьёзно отношусь к чистоте истории. Стараюсь делать каждый коммит маленьким (не больше +20/−20 строк) и вносить последовательное, логичное изменение. Конечно, не всегда так разрабатываю. Если применить git log к моей рабочей ветке, то можно увидеть подобное:

5d64b71 wip 392b1e0 wip 0a3ad89 more wip 3db02d3 wip 

Но прежде чем объявлять pull request готовым к рецензированию, я удаляю эту историю (с помощью git reset —mixed $(git merge-base feature main) ) и заново коммичу изменения, делю их на логические блоки и пишу обоснования, бит за битом. В результате неукоснительного следования этой методике вы можете применять git annotate где угодно и понять, почему любая строка кода написана именно так.

Я не могу передать словами, насколько велико влияние этой возможности на благополучие разработчика. Когда я спустя недели или месяцы читаю подобные сообщения в коммитах, работая над чем-то совсем другим, но связанным с этим кодом, то словно читаю маленькие любовные записки себе сегодняшнему от себя из прошлого. Эти комментарии снижают до нуля время поиска нужной информации.

Они также помогают и при проверке кода. В моих примечаниях к запросам на внесение изменений обычно написано: «пожалуйста, читайте каждый коммит отдельно». Я выяснил, что разобраться в pull request’e легче, если он рассказывает тебе какую-то историю, и каждый коммит — это новый указатель на твоём пути.

Заканчиваю своё отступление: теперь вы понимаете, почему вместо слияния без связи с источником (squash) я предпочитаю накат изменений из одной ветки в другую? Потому что несмотря на все преимущества, в первом случае безвозвратно теряется контекст.

И теперь вместо того, чтобы каждая строка вносила маленькое изменение, +20/−20, вы можете сказать лишь, что это часть набора подобных изменений. Возможно, десятка, или пятидесяти. Неизвестно. Конечно, можно посмотреть исходную ветку, но это уже перебор, и что если её вообще удалили? Так что да, повсеместное размещение этих любовных записок, каждая из которых тщательно написана и не привязана к другим, — это слишком большая удача, чтобы её упускать. Однако у накатки изменения из ветки в ветку есть и недостатки.

К примеру, тоже сложно сказать, сколько фич было разработано за определённый период времени. Более того, откатить их даже сложнее: обычно хочется действовать на уровне фич. При squash-коммитах для отката баговой фичи достаточно команды git revert , а в случае с rebase нужно знать диапазон.

Ещё хуже то, что squash-коммиты с большей вероятностью будут отменены (или вырезаны) чище, чем серия маленьких коммитов. Иногда я намеренно коммичу ошибочные или незавершённые решения, которые изменяю в последующих коммитах, лишь для более убедительного рассказа истории; каждое из этих изменений может доставить неприятности, но они отменяют друг друга при squash-коммитах.

В общем, у меня есть претензии к каждому из трёх подходов. И это привело меня к идее четвёртого подхода, который в Git отсутствует (пока?):

Накатка изменений из ветки в ветку, группирование и слияние

Вам известна функция «группирования» в векторных графических редакторах? Рисуешь несколько объектов, выделяешь их, группируешь вместе, а затем разом трансформируешь всю группу, оперируя ею как единым объектом. И при необходимости можно её «разгруппировать» и работать с исходными объектами по отдельности. Эта функция полезна, потому что иногда нужно видеть более «высокоуровневую» картину, а иногда приходится углубляться в подробности. Это нормально, всё зависит от обстоятельств, с которыми мы сталкиваемся.

Мне хотелось бы увидеть воплощение этой идеи применительно к Git. Группы коммитов могли бы представлять собой именованные и аннотированные диапазоны коммитов: feature-a может быть тем же, что и 5d64b71..3db02d3 . Каждая Git-команда, которая сейчас принимает диапазон коммитов, могла бы принимать и названия групп. У групп могут быть описания, чтобы git log , git blame и т.д. могли принимать опции —grouped или —ungrouped и работать соответствующе.

Конечно, нужно ещё продумать подробности (могут ли группы пересекаться? может ли группа быть частью другой группы?), я не знаком с устройством Git и не могу с уверенностью утверждать, что идея реализуема. Но чем больше я о ней думаю, тем больше она мне нравится. Я считаю, что группирование в сочетании с rebase-коммитами поможет взять всё лучшее из трёх подходов.

  • git
  • никто не читает теги
  • Блог компании VK
  • Программирование
  • Git
  • Системы управления версиями
  • GitHub

Сократите последние X коммитов с помощью Git

Мы часто слышим слово «сквош», когда говорим о рабочих процессах Git .

В этом уроке мы кратко расскажем, что такое сжатие Git. Затем мы поговорим о том, когда нам нужно раздавить коммиты. Наконец, мы более подробно рассмотрим, как сквошить коммиты.

2. Что такое Git Squashing?​

Когда мы говорим «сквош» в Git, это означает объединение нескольких непрерывных коммитов в один.

Давайте посмотрим на пример:

 ┌───┐ ┌───┐ ┌───┐ ┌───┐  ... │ A │◄─────┤ B │◄────┤ C │◄─────┤ D │  └───┘ └───┘ └───┘ └───┘   After Squashing commits B, C, and D:   ┌───┐ ┌───┐  ... │ A │◄─────┤ E │  └───┘ └───┘   ( The commit E includes the changes in B, C, and D.) 

В этом примере мы объединили коммиты B, C и D в E.

Далее мы обсудим, когда нам следует подавлять коммиты.

3. Когда скрывать коммиты?​

Проще говоря, мы используем сжатие, чтобы граф ветвлений оставался чистым.

Давайте представим, как мы реализуем новую функцию. Обычно мы фиксируем несколько раз, прежде чем достигнем удовлетворительного результата, например, некоторые исправления и тесты.

Однако, когда мы реализовали эту функцию, эти промежуточные коммиты выглядят излишними. Итак, мы можем захотеть объединить наши коммиты в один.

Другим распространенным сценарием, когда мы хотим раздавить коммиты, является слияние веток.

Скорее всего, когда мы начнем работать над новой фичей, мы запустим ветку фичи. Допустим, мы сделали свою работу с 20 фиксациями в нашей функциональной ветке.

Итак, когда мы объединяем ветку feature с веткой master, мы хотим объединить 20 коммитов в один. Таким образом, мы сохраняем ветку master в чистоте.

4. Как раздавить коммиты?​

Сегодня некоторые современные IDE, такие как IntelliJ и Eclipse , имеют встроенную поддержку общих операций Git. Это позволяет нам сжимать коммиты из графического интерфейса.

Например, в IntelliJ мы можем выбрать коммиты, которые мы хотим раздавить, и выбрать «Squash Commits» в контекстном меню, вызываемом правой кнопкой мыши:

./15eeaf846ad0acef82ac97f289a918a8.png

Однако в этом руководстве мы сосредоточимся на сквошинге с помощью команд Git.

Следует отметить, что squash не является командой Git, даже если это обычная операция Git. То есть « git squash… » — недопустимая команда Git.

Мы рассмотрим два разных подхода к раздавливанию коммитов:

  • Интерактивная перебазировка: git rebase -i …
  • Слияние с параметром –squash : git merge –squash «

Далее давайте посмотрим на них в действии.

5. Сжатие с помощью Interactive Rebase​

Прежде чем мы начнем, давайте создадим псевдоним Git slog (расшифровывается как короткий журнал), чтобы отображать журналы коммитов Git в компактном виде:

 git config --global alias.slog = log --graph --all --topo-order --pretty='format:%h %ai %s%d (%an)' 

В качестве примера мы подготовили репозиторий Git:

 $ git slog * ac7dd5f 2021-08-23 23:29:15 +0200 Commit D (HEAD -> master) (Kai Yuan)  * 5de0b6f 2021-08-23 23:29:08 +0200 Commit C (Kai Yuan)  * 54a204d 2021-08-23 23:29:02 +0200 Commit B (Kai Yuan)  * c407062 2021-08-23 23:28:56 +0200 Commit A (Kai Yuan)  * 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)  * 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)  * cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan) 

Интерактивная перебазировка Git отобразит все соответствующие коммиты в редакторе по умолчанию. В данном случае это те коммиты, которые мы хотим раздавить.

Затем мы можем управлять каждым коммитом и сообщением коммита по своему усмотрению и сохранять изменения в редакторе.

Далее, давайте раздавим последние четыре коммита.

Стоит отметить, что когда мы говорим «последние X коммитов», мы говорим о последних X коммитах из HEAD .

Итак, в данном случае это последние четыре коммита:

 * ac7dd5f ... Commit D (HEAD -> master)  * 5de0b6f ... Commit C  * 54a204d ... Commit B  * c407062 ... Commit A 

Более того, если мы раздавили уже отправленные коммиты и хотим опубликовать раздавленный результат, мы должны сделать принудительную отправку.

Стоит отметить, что принудительная отправка в общедоступный репозиторий может быть опасной операцией, поскольку она может перезаписать чужие коммиты.

Кроме того, когда мы действительно хотим сделать принудительную отправку, мы должны убедиться, что принудительно протолкнули только требуемую ветку.

Например, мы можем установить для свойства push.default значение current , чтобы только текущая ветвь была отправлена/принудительно отправлена в удаленный репозиторий.

В качестве альтернативы мы можем принудительно отправить только одну ветку, добавив «+» перед refspec для отправки. Например, git push origin +feature принудительно отправит ветку функций .

5.1. Раздавить последние X коммитов​

Вот синтаксис для удаления последних X коммитов с помощью интерактивной перебазировки:

 git rebase -i HEAD~[X] 

Итак, вот что мы должны запустить:

 git rebase -i HEAD~4 

После того, как мы выполним команду, Git запустит системный редактор по умолчанию (редактор Vim в этом примере) с коммитами, которые мы хотим удалить, и интерактивной справочной информацией по перебазированию:

./968e4a513767adc9c225cbaee22c5e6c.png

Как видно на снимке экрана выше, все четыре коммита, которые мы хотим сжать, перечислены в редакторе с помощью команды pick .

Подробное руководство о том, как управлять каждой фиксацией и сообщением о фиксации, приведено в последующих строках с комментариями.

Например, мы можем изменить команду выбора коммитов на s или squash , чтобы раздавить их:

./6359fa5dd9ce2b6fba9f6fa60c0b3b5e.png

Если мы сохраним изменения и выйдем из редактора, Git выполнит перебазирование, следуя нашим инструкциям:

 $ git rebase -i HEAD~4  [detached HEAD f9a9cd5] Commit A  Date: Mon Aug 23 23:28:56 2021 +0200  1 file changed, 1 insertion(+), 1 deletion(-)  Successfully rebased and updated refs/heads/master. 

Теперь вот что мы увидим, если еще раз проверим журнал коммитов Git:

 $ git slog * f9a9cd5 2021-08-23 23:28:56 +0200 Commit A (HEAD -> master) (Kai Yuan)  * 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)  * 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)  * cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan) 

Как видно из вывода slog , мы сжали последние четыре коммита в один новый коммит, f9a9cd5 .

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

 $ git log -1  commit f9a9cd50a0d11b6312ba4e6308698bea46e10cf1 (HEAD -> master)  Author: Kai Yuan Date: 2021-08-23 23:28:56 +0200   Commit A   Commit B   Commit C   Commit D 

5.2. Когда X относительно большой​

Мы узнали, что команда git rebase -i HEAD~X довольно проста для уничтожения последних X коммитов.

Однако подсчет большего числа X может быть проблемой, когда в нашей ветке довольно много коммитов. Более того, он подвержен ошибкам.

Когда X подсчитать непросто, мы можем найти хэш коммита, который мы хотим перебазировать «на», и запустить команду git rebase -i hash_onto .

Давайте посмотрим, как это работает:

 $ git slog e7cb693 2021-08-24 15:00:56 +0200 Commit F (HEAD -> master) (Kai Yuan)  2c1aa63 2021-08-24 15:00:45 +0200 Commit E (Kai Yuan)  ac7dd5f 2021-08-23 23:29:15 +0200 Commit D (Kai Yuan)  5de0b6f 2021-08-23 23:29:08 +0200 Commit C (Kai Yuan)  54a204d 2021-08-23 23:29:02 +0200 Commit B (Kai Yuan)  c407062 2021-08-23 23:28:56 +0200 Commit A (Kai Yuan)  29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)  34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)  cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan) 

Как видно из слога git , в этой ветке у нас есть несколько коммитов.

Теперь предположим, что мы хотели бы удалить все коммиты и выполнить ребазинг на коммит 29976c5 с сообщением: BugFix #1 .

Таким образом, нам не нужно считать, сколько коммитов нам нужно раздавить. Вместо этого мы можем просто выполнить команду git rebase -i 29976c5.

Мы узнали, что нам нужно изменить команды выбора на сквош в редакторе, и Git выполнит сжатие, как мы и ожидали:

 $ git rebase -i 29976c5  [detached HEAD aabf37e] Commit A  Date: Mon Aug 23 23:28:56 2021 +0200  1 file changed, 1 insertion(+), 1 deletion(-)  Successfully rebased and updated refs/heads/master.  $ git slog * aabf37e 2021-08-23 23:28:56 +0200 Commit A (HEAD -> master) (Kai Yuan)  * 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)  * 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)  * cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan) 

6. Сквош путем слияния с параметром –squash ​

Мы увидели, как использовать интерактивную перебазировку Git для сглаживания коммитов. Это может эффективно очистить граф коммитов в ветке.

Однако иногда мы делаем много коммитов в нашей функциональной ветке, работая над ней. После того, как мы разработали функцию, мы обычно хотим объединить ветвь функции с основной ветвью, скажем, «мастер».

Мы хотим, чтобы граф основной ветки оставался чистым, например, одна фича — одна фиксация. Но нас не волнует, сколько коммитов в нашей функциональной ветке.

В этом случае мы можем использовать команду commit git merge –squash для достижения этой цели.

Давайте разберемся на примере:

 $ git slog * 0ff435a 2021-08-24 15:28:07 +0200 finally, it works. phew! (HEAD -> feature) (Kai Yuan)  * cb5fc72 2021-08-24 15:27:47 +0200 fix a typo (Kai Yuan)  * 251f01c 2021-08-24 15:27:38 +0200 fix a bug (Kai Yuan)  * e8e53d7 2021-08-24 15:27:13 +0200 implement Feature2 (Kai Yuan)   | * 204b03f 2021-08-24 15:30:29 +0200 Urgent HotFix2 (master) (Kai Yuan)   | * 8a58dd4 2021-08-24 15:30:15 +0200 Urgent HotFix1 (Kai Yuan)   |/  * 172d2ed 2021-08-23 23:28:56 +0200 BugFix #2 (Kai Yuan)  * 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)  * 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)  * cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan) 

Как видно из приведенного выше вывода, в этом репозитории Git мы реализовали «Feature2» в ветке функций .

В нашей функциональной ветке мы сделали четыре коммита.

Теперь мы хотим объединить результат обратно в основную ветку с помощью одной единственной фиксации, чтобы сохранить основную ветку в чистоте:

 $ git checkout master Switched to branch 'master'   $ git merge --squash feature Squash commit -- not updating HEAD Automatic merge went well; stopped before committing as requested 

В отличие от обычного слияния, когда мы выполняем команду git merge с параметром –squash , Git не будет автоматически создавать фиксацию слияния.

Вместо этого он превращает все изменения из исходной ветки ( в данном сценарии в ветку функций ) в локальные изменения в рабочей копии :

 $ git status On branch master Changes to be committed:  (use "git restore --staged . " to unstage)   modified: readme.md 

В этом примере все изменения «Feature2» относятся к файлу readme.md .

Нам нужно зафиксировать изменения, чтобы завершить слияние:

 $ git commit -am'Squashed and merged the Feature2 branch'   [master 565b254] Squashed and merged the Feature2 branch  1 file changed, 4 insertions(+) 

Теперь давайте проверим граф ветвления:

 $ git slog * 565b254 2021-08-24 15:53:05 +0200 Squashed and merged the Feature2 branch (HEAD -> master) (Kai Yuan)  * 204b03f 2021-08-24 15:30:29 +0200 Urgent HotFix2 (Kai Yuan)  * 8a58dd4 2021-08-24 15:30:15 +0200 Urgent HotFix1 (Kai Yuan)   | * 0ff435a 2021-08-24 15:28:07 +0200 finally, it works. phew! (feature) (Kai Yuan)   | * cb5fc72 2021-08-24 15:27:47 +0200 fix a typo (Kai Yuan)   | * 251f01c 2021-08-24 15:27:38 +0200 fix a bug (Kai Yuan)   | * e8e53d7 2021-08-24 15:27:13 +0200 implement Feature2 (Kai Yuan)   |/  * 172d2ed 2021-08-23 23:28:56 +0200 BugFix #2 (Kai Yuan)  * 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)  * 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)  * cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan) 

Мы видим, что мы объединили все изменения в ветке feature с веткой master , и у нас есть один единственный коммит, 565b254 , в ветке master .

С другой стороны, в ветке feature у нас по-прежнему четыре коммита.

7. Заключение​

В этой статье мы говорили о том, что такое сквоширование Git и когда нам следует подумать об его использовании.

Мы также узнали, как сквошить коммиты в Git.

Как сжимать коммиты в Git с помощью git squash

Для начала давайте разберемся: что же это вообще такое — git squash .

Git squash — это прием, который помогает взять серию коммитов и уплотнить ее. Например, предположим: у вас есть серия из N коммитов и вы можете путем сжатия преобразовать ее в один-единственный коммит. Сжатие через git squash в основном применяется, чтобы превратить большое число малозначимых коммитов в небольшое число значимых. Так становится легче отслеживать историю Git.

Также этот прием используется при объединении ветвей. Чаще всего вам будут советовать всегда сжимать коммиты и выполнять перебазирование с родительской ветвью (например, master или develop ). В таком случае история главной ветки будет содержать только значимые коммиты, без ненужной детализации.

Как именно делать git squash

Возьмем для примера следующую историю Git:

Здесь видны последние три коммита. В сообщениях к ним поясняется, что мы добавили новый файл и какое-то содержимое. Лучше заменить их одним единственным коммитом о том, что произошло добавление нового файла с некоторым содержимым. Итак, давайте посмотрим, как сжать последние три коммита в один:

git rebase -i HEAD~3

git rebase -i — интерактивный инструмент, который помогает сжимать коммиты. У него много возможностей, но давайте сосредоточимся на git squash .

Если вы не очень хорошо знакомы с перебазированием — не волнуйтесь. Сжатие коммитов — одна из самых простых операций, которые выполняются через интерактивный git-rebase (т.е. git rebase -i ). HEAD~3 означает, что мы берем последние три коммита.

Далее откроется редактор. Посмотрите, rebase -i охватывает только последние три коммита, и обратите внимание на количество опций. Нас, однако, интересует только squash . Давайте же приведем все к одному коммиту.

Как видите, для сжатия мы отметили последние два коммита с помощью команд squash или s .

В примере, приведенном выше, коммиты, предназначенные для сжатия, будут слиты с основным коммитом — тем, который отмечен командой pick . Отметив коммиты, сохраните изменения в редакторе.

Далее rebase -i снова откроет редактор для ввода сообщения о коммите, как на картинке ниже:

Отредактировав и сохранив сообщения, можете закрыть редактор. Обратите внимание: строки, которые начинаются с # , будут проигнорированы. После этого журнал Git будет выглядеть следующим образом:

Здесь изменилось сообщение о коммите, и обратите внимание: три коммита “склеились” в один. Также изменился хэш коммита. Через git rebase всегда создается новый коммит, содержащий соответствующие изменения.

Так что используйте этот инструмент с осторожностью.

Помните: сжатие коммитов меняет историю Git, поэтому не рекомендуется сжимать ветвь, если вы уже отправили ее в удаленный репозиторий. Всегда выполняйте сжатие до того, как отправить пуш с изменениями.

Применение fixup для сжатия коммитов

Для того, чтобы сжимать коммиты, также можно воспользоваться опцией fixup . Это то же самое, что и squash , только без возможности редактировать сообщение о коммите. Сообщение о коммите в таком случае будет целиком взято из “целевого” коммита — того, который был выбран с помощью pick . Давайте посмотрим на примере:

Воспользуйтесь командой fixup или f , чтобы выбрать коммиты. После этого сохраните изменения в редакторе. Инструмент интерактивного перебазирования сохранит сообщение о коммите. В результате история Git будет выглядеть так:

Обратите внимание: хэш коммита также изменился, а сообщение о коммите берется из “основного” коммита, с которым были сжаты другие два.

Применение сжатия при слиянии ветвей

Git также предоставляет возможность сжатия при объединении ветвей. Во многих случаях она может оказаться полезной. Чтобы добавить новый функционал или поправить какие-то баги, нам понадобится то и дело что-то изменять в ветвях. Поэтому, когда мы будем готовы слить эти изменения в основную ветвь ( master или develop ), для начала следует применить сжатие.

Давайте рассмотрим следующую команду:

git merge --squash target_branch_name

Она берет все коммиты из целевой ветви, сжимает их и сохраняет все изменения в текущей ветви. Затем вы можете зафиксировать все изменения в одном коммите. На примере это выглядит так:

Здесь мы провели слияние нашей ветви с develop с помощью —squash . Git воспримет и сохранит сделанные таким образом изменения.

Сжатие коммитов через Github

Возможность сжимать коммиты предоставляет и Github. Эта функция особенно полезна при пулл-реквесте.

Сначала производится сжатие, затем — слияние. Так что вместо пяти коммитов получится один.

Как видно на картинке, выполняется всего один коммит с соответствующим сообщением.

Спасибо за чтение! Надеюсь, вы почерпнули из статьи что-то новое 🙂

  • Простой способ взлома сайта для получения его Git-данных
  • GitHub-репозитории, о которых должен знать каждый разработчик
  • Пять алиасов Git, без которых мне не прожить

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *