“Сделать коммит рано и часто” – это популярная мантра в разработке программного обеспечения при использовании Git. Делая так, мы обеспечиваем, что каждый изменении хорошо задокументирован, улучшаем сотрудничество и facilitates easier tracking of the project’s evolution. Однако это также может привести к избытку коммитов.
Здесь и emerges важность объединения коммитов. Объединение коммитов – это процесс combineния нескольких записей коммитов в один целостный коммит.
Например, представим, что мы работаем над функцией реализации формы входа и создаем следующие четыре коммита:
Once the feature is completed, for the overall project, these commits are too detailed. We don’t need to know in the future that we ran into a bug that was fixed during development. To ensure a clean history in the main branch, we squash these commits into a single commit:
Как сжать коммиты в Git: Интерактивный рибейз
The most common method to squash commits is using an interactive rebase. We start it using the command:
git rebase -i HEAD~<number_of_commits>
Replace <number_of_commits>
with the number of commits we want to squash.
In our case, we have four commits, so the command is:
git rebase -i HEAD~4
Executing this command will open an interactive command-line editor:
Верхний раздел отображает коммиты, а нижний содержит комментарии о том, как объединить коммиты.
Мы видим четыре коммита. Для каждого из них我们必须 решить, какую команду выполнить. Нас интересуют команды pick
(p
) и squash
(s
). Чтобы объединить эти четыре коммита в один, мы можем выбрать первый и объединить оставшиеся три.
Мы применяем команды, изменяя текст перед каждым коммитом, конкретно меняя pick
на s
или squash
для второго, третьего и четвертого коммитов. Чтобы сделать эти правки, нам нужно войти в режим “ВВОДА” в текстовом редакторе командной строки, нажав клавишу i
на клавиатуре:
После нажатия i
, в нижней части появится текст -- INSERT --
, что indicates, что мы вошли в режим ввода. Теперь мы можем передвигать курсор с помощью клавиш со стрелками, удалять символы и набирать текст, как в стандартном текстовом редакторе:
Как только мы будем довольны изменениями, нам нужно выйти из режима вставки, нажав клавишу Esc
на клавиатуре. Следующим шагом будет сохранение наших изменений и выход из редактора. Для этого сначала нажимаем клавишу :
, чтобы сигнализировать редактору о намерении выполнить команду:
Внизу редактора теперь видим точку с запятой :
, предлагающую ввести команду. Чтобы сохранить изменения, используем команду w
, что означает “write” (записать). Чтобы закрыть редактор, используем q
, что означает “quit” (выйти). Эти команды можно объединить и ввести вместе wq
:
Для выполнения команды нажимаем клавишу Enter
. Это действие закроет текущий редактор и откроет новый, позволяя нам ввести сообщение для коммита, который мы только что объединили. Редактор отобразит defaultMessage, состоящее из сообщений четырех коммитов, которые мы объединяем:
Я рекомендую изменить сообщение, чтобы оно точно отражало изменения, внедренные этими объединенными коммитами — после всего, цель слияния заключается в поддержании чистой и легко читаемой истории.
Чтобы взаимодействовать с редактором и править сообщение, мы нажимаем i
еще раз, чтобы войти в режим редактирования и изменить сообщение по своему усмотрению.
В этом случае мы заменяем сообщение коммита на “Реализовать форму входа”. Чтобы выйти из режима редактирования, мы нажимаем Esc
. Затем сохраняем изменения, нажимая :
, вводя команду wq
и нажимая Enter
.
Как viewed the Commit History
Обычно, вспомнить всю историю коммитов может быть сложно. Чтобы viewed the commit history, мы можем использовать команду git log
. В упомянутом примере, перед выполнением слияния, выполнение команды git log
显示:
Для навигации по списку коммитов используйте клавиши со стрелками вверх и вниз. Чтобы выйти, нажмите q
.
Мы можем использовать git log
, чтобы confirmировать успешное слияние. Выполнение этой команды после слияния отобразит один коммит с новым сообщением:
Pushing squashed commit
Указанная команда будет действовать на локальном репозитории. Чтобы обновить удаленный репозиторий, нам нужно pushing наши изменения. Однако, поскольку мы изменили историю коммитов, нам нужно использовать force push с опцией --force
:
git push --force origin feature/login-form
Force pushing перезапишет историю коммитов в удаленной ветке и потенциально нарушит работу других, кто работает с этой веткой. Хорошей практикой является communicate с командой перед выполнением этого действия
Более безопасный способ принудительной отправки, который уменьшает риск нарушения работы коллег, заключается в использовании опции --force-with-lease
:
git push --force-with-lease origin feature/login-form
Эта опция обеспечивает принудительную отправку только в том случае, если удаленная ветка не была обновлена с момента последнего извлечения или обновления.
Слияние конкретных коммитов
Представьте, что у нас есть пять коммитов:
Предположим, мы хотим оставить коммиты 1, 2 и 5 и слиять коммиты 3 и 4.
При использовании интерактивного рибейса коммиты,marked для слияния, будутcombined с предыдущим коммитом. В данном случае это означает, что мы хотим слить Commit4
, чтобы он вошел в Commit3
.
Для этого我们必须 инициировать интерактивный рибейс, который включает эти два коммита. В этом случае достаточно трех коммитов, поэтому мы используем команду:
git rebase -i HEAD~3
Затем мы устанавливаем Commit4
в s
, чтобы он был слит с Commit3
:
После выполнения этой команды и перечисления коммитов, мы观察到, что коммиты 3 и 4 были слиты вместе, в то время как остальные остались неизменными.
Слияние с определенного коммита
В команде git rebase -i HEAD~3
часть HEAD
является сокращением для последнего коммита. Синтаксис ~3
используется для указания предка коммита. Например, HEAD~1
refers к родителю коммита HEAD
.
В интерактивной основе при переработке (rebase), рассматриваемые коммиты включают все предковые коммиты, ведущие к коммиту, указанному в команде. Обратите внимание, что указанный коммит не включается:
Instead of using HEAD, we can specify a commit hash directly. For example, Commit2
has a hash of dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
, so the command:
git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
будет начинать переработку, учитывая все коммиты, выполненные после Commit2
. Таким образом, если мы хотим начать переработку с определенного коммита и включить его, мы можем использовать команду:
git rebase -i <commit-hash>~1
Разрешение конфликтов при слиянии коммитов
Когда мы сжимаем коммиты, мы combine multiple commit changes into a single commit, что может привести к конфликтам, если изменения перекрываются или значительно расходятся. Вот некоторые типичные сценарии, где могут возникнуть конфликты:
- Перекрывающиеся изменения: Если два или более коммита, которые будут объединены, изменили одни и те же строки файла или близко расположенные строки, Git может не суметь автоматически примирить эти изменения.
- Различное состояние изменений: Если один коммит добавляет определенный фрагмент кода, а другой коммит модифицирует или удаляет этот же фрагмент кода, объединение этих коммитов может привести к конфликтам, которые необходимо разрешить.
- Переименование и модификация: Если коммит переименовывает файл, а последующие коммиты вносят изменения в старое имя, объединение этих коммитов может запутать Git, вызывая конфликт.
- Изменения в двоичных файлах: Двоичные файлы не хорошо объединяются с помощью текстовых инструментов diff. Если несколько коммитов изменяют один и тот же двоичный файл и мы пытаемся их объединить, может возникнуть конфликт, так как Git не может автоматически согласовать эти изменения.
- Сложная история: Если коммиты имеют сложную историю с множеством слияний, ветвлений или переработок между ними, объединение их может привести к конфликтам из-за нелинейного характера изменений.
При объединении Git попытается применить каждое изменение по одному. Если он сталкивается с конфликтами в процессе, он停 и позволяет нам разрешить их.
Конфликт будет помечен маркерами конфликта <<<<<<
и >>>>>>
. Чтобы обработать конфликты, нам нужно открыть файлы и вручную решить каждый, выбрав, какую часть кода мы хотим сохранить.
After resolving conflicts, we need to stage the resolved files using the git add
command. Then we can continue the rebase using the following command:
git rebase --continue
Для получения дополнительной информации о конфликтах в Git перейдите на этот учебник как разрешить конфликты слияния в Git.
Alternatives to Squashing with Rebase
Команда git merge --squash
является альтернативным методом для git rebase -i
для объединения нескольких коммитов в один. Эта команда особенно полезна, когда мы хотим объединить изменения из ветки в основную ветку,squashing все индивидуальные коммиты в один. Вот обзор того, как использовать git merge
для squashing:
- Мы переходим к целевой ветке, в которую хотим включить изменения.
- Мы выполняем команду
git merge --squash <название-ветки>
, заменив<название-ветки>
названием ветки. - Мы коммитим изменения с помощью
git commit
, чтобы создать один коммит, представляющий все изменения из ветки с функциональностью.
Например, предположим, что мы хотим интегрировать изменения ветки feature/login-form
в main
в виде одного коммита:
git checkout main git merge --squash feature-branch git commit -m "Implement login form"
Эти ограничения связаны с данным подходом по сравнению с git rebase -i
:
- Гранулярность управления: Меньше контроля над отдельными коммитами. При использовании rebase мы можем выбрать, какие коммиты объединить, в то время как merge заставляет объединить все изменения в один коммит.
- Intermediate history: При использовании слияния, индивидуальная история коммитов из веткиfeatures теряется в основной ветке. Это может усложнить отслеживание incremental изменений, внесенных во время разработки функции.
- Pre-commit review: Поскольку он стейджит все изменения как единый набор изменений, мы не можем reviewed или testaждый коммит individually перед squashing, в отличие от interactivererebase, где каждый коммит можно reviewed и tested поочередно.
Conclusion
Включение частых и мелких коммитов в рабочий процесс разработки способствует сотрудничеству и清楚的 документированию, но также может захламить историю проекта. Объединение коммитов достигает компромисса, сохраняя важные вехи, whilst eliminating шум minor итеративных изменений.
Чтобы узнать больше о Git, я рекомендую следующие ресурсы:
Source:
https://www.datacamp.com/tutorial/git-squash-commits