Squash and merge что это
Тонкости благополучного git-merge
Вступительное слово
Основными командами пользовательского уровня для ветвления в Git являются git-branch, git-checkout, git-rebase, git-log и, конечно же, git-merge. Для себя я считаю git-merge зоной наибольшей ответственности, точкой огромной магической энергии и больших возможностей. Но это достаточно сложная команда, и даже достаточно длительный опыт работы с Git порой бывает недостаточным для освоение всех ее тонкостей и умения применить ее наиболее эффективно в какой-либо нестандартной ситуации.
Попробуем же разобраться в тонкостях git-merge и приручить эту великую магию.
Здесь я хочу рассмотреть только случай благополучного слияния, под которым я понимаю слияние без конфликтов. Обработка и разрешение конфликтов — отдельная интересная тема, достойная отдельной статьи. Я очень рекомендую так же ознакомиться со статьей Внутреннее устройство Git: хранение данных и merge, содержащей много важной информации, на которую я опираюсь.
Анатомия команды
Если верить мануалу, команда имеет следующий синтаксис:
По большому счету, в Git есть два вида слияния: перемотка (fast-forward merge) и «истинное» слияние (true merge). Рассмотрим несколько примеров обоих случаев.
«Истинное» слияние (true merge)
Мы отклоняемся от ветки master, чтобы внести несколько багов улучшений. История коммитов у нас получилась следующая:
Выполним на ветке master git merge feature :
Посмотрим историю коммитов в тестовом репозитории, который я создал специально для этого случая:
А теперь посмотрим информацию о коммите (M):
Мы видим двух родителей, объект-дерево, соответствующее данному состоянию файлов репозитория, а так же информацию о том, кто виновен в коммите.
Посмотрим, куда ссылается указатель master:
Действительно, он теперь передвинут на коммит (M).
Squash и no-commit
В случае применения такого слияния коммиты ветки feature не будут включены в нашу историю, но коммит Sq будет содержать все их изменения:
Позже, в случае выполнения «классического» git merge feature можно исправить это. Тогда история примет следующий вид:
Перемотка (fast-forward merge)
Рассмотрим другой случай истории коммитов:
Все как и в прошлый раз, но теперь в ветке master нет коммитов после ответвления. В этом случае происходит слияние fast-forward (перемотка). В этом случае отсутствует коммит слияния, указатель (ветка) master просто устанавливается на коммит Y, туда же указывает и ветка feature:
Стратегии слияния
Стратегия resolve
Здесь C — общий коммит двух веток, дерево файлов, соответствующее этому коммиту, принимается за общего предка. Анализируются изменения, произведенные в ветках master и feature со времен этого коммита, после чего для коммита (M) создается новая версия дерева файлов в соответствии с пунктами 4 и 5 нашего условного алгоритма.
Стратегия recursive
Для иллюстрации этой стратегии позаимствуем пример из статьи Merge recursive strategy из блога «The plasticscm blog»:
Итак, у нас есть две ветки: main и task001. И так вышло, что наши разработчики знают толк в извращениях: они слили коммит 15 из ветки main с коммитом 12 из ветки task001, а так же коммит 16 с коммитом 11. Когда нам понадобилось слить ветки, оказалось, что поиск реального предка — дело неблагодарное, но стратегия recursive с ее конструированием «виртуального» предка нам поможет. В результате мы получим следующую картину:
]
Выполнение рекурсивного слияния с этой опцией будет более продвинутым вариантом стратегии subtree, где алгоритм основывается на предположении, как деревья должны совместиться при слиянии. Вместо этого в этом случае указывается конкретный вариант.
Стратегия octopus
Эта стратегия используется для слияние более чем двух веток. Получившийся в итоге коммит будет иметь, соответственно, больше двух родителей.
Данная стратегия предполагает большую осторожность относительно потенциальных конфликтов. В связи с этим порой можно получить отказ в слиянии при применении стратегии octopus.
Стратегия ours
Не следует путать стратегию ours и опцию ours стратегии recursive.
Стратегия ours — более радикальное средство.
Стратегия subtree
Для иллюстрации данной стратегии возьмем пример из главы Слияние поддеревьев книги «Pro Git».
Добавим в наш проект новые удаленный репозиторий, rack:
Ясно, что ветки master и rack_branch имеют абсолютно разные рабочие каталоги. Добавим файлы из rack_branch в master с использованием squash, чтобы избежать засорения истории ненужными нам фактами:
Теперь файлы проекта rack у нас в рабочем каталоге.
Как сжимать коммиты в Git с помощью git squash
Aug 31, 2020 · 5 min read
Git squash — это прием, который помогает взять серию коммитов и уплотнить ее. Например, предположим: у вас есть серия из N коммитов и вы можете путем сжатия преобразовать ее в один-единственный коммит. Сжатие через git squash в основном применяется, чтобы превратить большое число малозначимых коммитов в небольшое число значимых. Так становится легче отслеживать историю Git.
Также этот прием используется при объединении ветвей. Чаще всего вам будут советовать всегда сжимать коммиты и выполнять перебазирование с родительской ветвью (например, master или develop ). В таком случае история главной ветки будет содержать только значимые коммиты, без ненужной детализации.
Как именно делать git squash
Возьмем для примера следующую историю Git:
Здесь видны последние три коммита. В сообщениях к ним поясняется, что мы добавили новый файл и какое-то содержимое. Лучше заменить их одним единственным коммитом о том, что произошло добавление нового файла с некоторым содержимым. Итак, давайте посмотрим, как сжать последние три коммита в один:
3 означает, что мы берем последние три коммита.
Здесь изменилось сообщение о коммите, и обратите внимание: три коммита “склеились” в один. Также изменился хэш коммита. Через git rebase всегда создается новый коммит, содержащий соответствующие изменения.
Так что используйте этот инструмент с осторожностью.
Помните: сжатие коммитов меняет историю Git, поэтому не рекомендуется сжимать ветвь, если вы уже отправили ее в удаленный репозиторий. Всегда выполняйте сжатие до того, как отправить пуш с изменениями.
Применение fixup для сжатия коммитов
Обратите внимание: хэш коммита также изменился, а сообщение о коммите берется из “основного” коммита, с которым были сжаты другие два.
Применение сжатия при слиянии ветвей
Git также предоставляет возможность сжатия при объединении ветвей. Во многих случаях она может оказаться полезной. Чтобы добавить новый функционал или поправить какие-то баги, нам понадобится то и дело что-то изменять в ветвях. Поэтому, когда мы будем готовы слить эти изменения в основную ветвь ( master или develop ), для начала следует применить сжатие.
Давайте рассмотрим следующую команду:
Она берет все коммиты из целевой ветви, сжимает их и сохраняет все изменения в текущей ветви. Затем вы можете зафиксировать все изменения в одном коммите. На примере это выглядит так:
Сжатие коммитов через Github
Возможность сжимать коммиты предоставляет и Github. Эта функция особенно полезна при пулл-реквесте.
Сначала производится сжатие, затем — слияние. Так что вместо пяти коммитов получится один.
Как видно на картинке, выполняется всего один коммит с соответствующим сообщением.
Спасибо за чтение! Надеюсь, вы почерпнули из статьи что-то новое 🙂
Как сжимать коммиты в Git с помощью git squash
Git squash — это прием, который помогает взять серию коммитов и уплотнить ее. Например, предположим: у вас есть серия из N коммитов и вы можете путем сжатия преобразовать ее в один-единственный коммит. Сжатие через git squash в основном применяется, чтобы превратить большое число малозначимых коммитов в небольшое число значимых. Так становится легче отслеживать историю Git.
Также этот прием используется при объединении ветвей. Чаще всего вам будут советовать всегда сжимать коммиты и выполнять перебазирование с родительской ветвью (например, master или develop ). В таком случае история главной ветки будет содержать только значимые коммиты, без ненужной детализации.
Как именно делать git squash
Возьмем для примера следующую историю Git:
Здесь видны последние три коммита. В сообщениях к ним поясняется, что мы добавили новый файл и какое-то содержимое. Лучше заменить их одним единственным коммитом о том, что произошло добавление нового файла с некоторым содержимым. Итак, давайте посмотрим, как сжать последние три коммита в один:
3 означает, что мы берем последние три коммита.
Как видите, для сжатия мы отметили последние два коммита с помощью команд squash или s .
Здесь изменилось сообщение о коммите, и обратите внимание: три коммита “склеились” в один. Также изменился хэш коммита. Через git rebase всегда создается новый коммит, содержащий соответствующие изменения.
Так что используйте этот инструмент с осторожностью.
Помните: сжатие коммитов меняет историю Git, поэтому не рекомендуется сжимать ветвь, если вы уже отправили ее в удаленный репозиторий. Всегда выполняйте сжатие до того, как отправить пуш с изменениями.
Применение fixup для сжатия коммитов
Обратите внимание: хэш коммита также изменился, а сообщение о коммите берется из “основного” коммита, с которым были сжаты другие два.
Применение сжатия при слиянии ветвей
Git также предоставляет возможность сжатия при объединении ветвей. Во многих случаях она может оказаться полезной. Чтобы добавить новый функционал или поправить какие-то баги, нам понадобится то и дело что-то изменять в ветвях. Поэтому, когда мы будем готовы слить эти изменения в основную ветвь ( master или develop ), для начала следует применить сжатие.
Давайте рассмотрим следующую команду:
Она берет все коммиты из целевой ветви, сжимает их и сохраняет все изменения в текущей ветви. Затем вы можете зафиксировать все изменения в одном коммите. На примере это выглядит так:
Сжатие коммитов через Github
Возможность сжимать коммиты предоставляет и Github. Эта функция особенно полезна при пулл-реквесте.
Сначала производится сжатие, затем — слияние. Так что вместо пяти коммитов получится один.
Как видно на картинке, выполняется всего один коммит с соответствующим сообщением.
Спасибо за чтение! Надеюсь, вы почерпнули из статьи что-то новое 🙂
Я новичок в Git, и я пытаюсь понять разницу между сквошем и ребэзом. Насколько я понимаю, вы выполняете сквош при выполнении ребазинга.
а затем удаляя tmp ветку.
воспроизводит некоторые или все ваши коммиты на новой базе, позволяя вам раздавить (или совсем недавно «исправить», см. этот вопрос ), перейдя непосредственно к:
Объединить коммиты: сохраняет все коммиты в вашей ветке и чередует их с коммитами в базовой ветке
Merge Squash: сохраняет изменения, но пропускает отдельные коммиты из истории
Rebase: это перемещает всю ветвь объекта, чтобы начать с кончика мастер-ветви, эффективно объединяя все новые коммиты в master
Merge squash объединяет дерево (последовательность коммитов) в один коммит. То есть он подавляет все изменения, внесенные в н коммитах, в один коммит.
При интерактивном перебазировании вам предоставляется возможность либо раздавить, выбрать, отредактировать или пропустить коммиты, которые вы собираетесь перебазировать.
Надеюсь, это было ясно!
Давайте начнем со следующего примера:
Теперь у нас есть 3 варианта объединить изменения признака филиала Into мастер филиала :
Слияние коммитов
Сохранит историю всех коммитов ветви функций и переместит их в основную ветку.
Добавит дополнительный фиктивный коммит.
Перебазирование и слияние Добавит
всю историю коммитов ветви функций в начало главной ветки,
НЕ добавит дополнительный фиктивный коммит.
Сквош и слияние
Сгруппирует все коммиты ветвей объектов в один коммит, затем добавит его перед главной ветвью.
Добавит дополнительный фиктивный коммит.
Ниже вы можете увидеть, как главная ветвь будет выглядеть после каждого из них.
Merge strategies and squash merge
Azure Repos | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018-TFS 2017
The commit history on your main branch (or other default branch) doesn’t follow a straight line, because of the related topic branch history. As a project grows larger, the number of topic branches worked on at the same time increases, making the default branch history increasingly harder to follow.
The default branch is an accurate representation of the history of each topic branch, but it’s difficult to use to answer broader questions about your project’s development.
Squash merge
Squash merging is a merge option that allows you to condense the Git history of topic branches when you complete a pull request. Instead of each commit on the topic branch being added to the history of the default branch, a squash merge adds all the file changes to a single new commit on the default branch.
A simple way to think about this is that squash merge gives you just the file changes, and a regular merge gives you the file changes and the commit history.
How is a squash merge helpful?
Squash merging keeps your default branch histories clean and easy to follow without demanding any workflow changes on your team. Contributors to the topic branch work how they want in the topic branch, and the default branches keep a linear history through the use of squash merges. The commit history of a main branch updated with squash merges has one commit for each merged branch. You can step through this history to find out exactly when work was done.
Considerations when squash merging
Squash merging condenses the history of changes in your default branch, so it’s important to work with your team to decide when you should squash merge or when you want to keep the full commit history of a topic branch. When squash merging, it’s a good practice to delete the source branch. Deleting the source branch prevents confusion as the topic branch itself doesn’t have a commit merging it into the default branch.
Complete pull requests with squash merge
You can choose to squash merge when completing a pull request in Azure Repos.
Choose Squash commit under Merge type in the Complete pull request dialog to squash merge the topic branch.
Complete pull requests with squash merge
You can choose to squash merge when completing a pull request in Azure Repos.
Choose Squash changes when merging on the Complete pull request dialog to squash merge the topic branch.
Multiple merge bases
The Files tab in a pull request detects diffs by a three-side comparison. The algorithm takes into account the last commit in the target branch, the last commit in the source branch, and their common merge base. The algorithm is a fast, cost-efficient, and reliable method of detecting changes. Unfortunately, in some cases, there’s more than one true base. In most repositories this situation is rare, but in large repositories with many active users, it can be common.
The following scenarios can cause multiple bases:
Multiple merge base detection is part of security awareness. If there are multiple merge bases, the file-diff algorithm for the user interface might not properly detect file changes, depending on which merge base it chooses. If the files in the pull request have different versions between the merge bases, a multiple merge base warning occurs.
Potential security risks of merging from multiple bases
How to resolve the multiple merge bases issue
Having multiple merge bases isn’t necessarily bad, but you should double-check that everything is fine. To get rid of multiple merge bases, tie branches to a single common ancestor. Either rebase your branch on target, or merge target into main. Those actions get rid of the warning message and help you check if the actual changes are fine.
One approach is to soft reset and stash your progress before rebasing or merging. You can then create a new branch or rebase an empty one, and apply your changes from a clear point. This process might require a force push to remote if your changes are already there.
How to avoid the multiple merge bases issue
Here are general tips for avoiding the multiple merge base issue:
What to do if the multiple merge bases issue reappears
In large repos with many active contributors, this issue can be especially inconvenient. Even if you get rid of multiple bases via merge, the situation might reappear. If someone closes a longstanding pull request, that can recreate the situation. Even though build policies and tests are running, you have no means to complete the pull request. Resetting and starting a new branch might help. If nothing is changed, your changes are probably clear, even if the situation repeats itself.