Merge made by the art strategy

Merge made by the art strategy

Configure Git to merge using ort

On Monday (2021–08–16), the Git project announced the 2.33 release. For me, its headline feature is a new merge strategy called ort.

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

If you have Git 2.33 installed, you can try the new strategy out in a repository by running:

(that’s correct and covers normal merges whether they happen during a git pull or otherwise).

Now, when you locally merge in changes from another branch, your copy of Git uses the “ort” strategy. It’s basically the same as the existing default strategy, “recursive”, but fixes a lot of the known bugs. And it’s much faster for big merges.

Once you’re happy it works, you can use it everywhere on your PC:

I’ve already switched and for me it’s working fine. I don’t usually make a special effort to keep tools up to date with the very latest upstream features, but:

I didn’t have to co-ordinate with colleagues or customers for this. Git’s distributed model means you can switch to “ort” merges whilst the source control server and your colleagues are still on using “recursive”. Just imagine if you did need to all switch on the same day — that’d take a lot of planning.

You’ll start to see the “ort” merge mechanism making new Git features possible. For example, you can now easily detect when someone does a merge that’s different from how the automatic resolver would have done it. Someone unscrupulous might smuggle in some malicious changes during a manual merge and this will makes that kind of tampering much easier to spot.

Future enhancements

The same improved algorithm, that doesn’t rely on a working tree, unlocks a chance for SaaS providers (GitHub, etc) to offer a better remote rebase experience too. I’m really looking forward to it.

Naming

Wondering about that name, “ ort”? It’s a pun on the merge sort algorithm, coming from how you specify the strategy on the command line:

Gotta love a nerdy reference, right? If you really can’t get enough of those, check out the other names that were suggested.

At The Scale Factory, we believe in defining infrastructure as code, automated deployments, and effective code reviews. Whether you call it GitOps, DevOps, Site Reliability Engineering or just software development, we help teams build secure, highly available and cloud-native SaaS solutions.

Are you comfortable explaining what you know about git even if you can’t cover every single detail? If so, and especially if you can also switch from talking technical to discussing deliverables, deadlines, and business value, then please check out our careers page.

We’re friendly and inclusive, so if you have the skills and experience, I hope you would consider applying regardless of your age, gender, sexuality, race or physical ability.

Гайд по Git : Часть 2 : Ветки и слияние веток

В этой статье разбираемся с понятием веток в git. Как смержить одну ветку в другую, и чем отличается Merge от Rebase.

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

Ветки в Git, как и коммиты, невероятно легковесны. Ветка в Git — это простой перемещаемый указатель и ничего более. Вот почему многие фанаты Git повторяют мантру:

делай ветки сразу, делай ветки часто

Команда создания новой ветки

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

Команда git branch позволяет не только создавать ветки, но и просматривать существующие. Ветка, на которой вы находитесь помечается звездочкой.

Сообщим Git, что хотим переключиться на другую ветку:

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

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

Напоследок вот совет, как создать новую ветку и переключиться на неё с помощью одной команды:

Merge

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

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

Втащим все изменения из ветки newImage в ветку main :

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

А также посмотрим log, убедимся что появился новый коммит:

Rebase

Несмотря на то, что это звучит достаточно непонятно, преимущество rebase в том, что c его помощью можно делать чистые и красивые линейные последовательности коммитов. История коммитов будет чище, если вы применяете rebase.

Посмотрим, как это работает:

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategyGit дописал копию коммита C7 за коммитом C6 и перенес туда указатель

Удалим ветку bugFix и снова проверим log:

Заключение

Мы разобрались с ветками в Git и их слиянием. В идельном проекте все ветки стремятся влиться в одну. Не забывайте создавать новые ветки от основной ветки разработки для каждой самостоятельной задачи.

Напоследок вот вам шпаргалка по отличию Merge от Rebase:

Русские Блоги

Стратегия ветвей вводного руководства git

Рекурсивный режим ( recursive )

Создавайте и переключайтесь dev Перейти, вернуться после отправки версии master Разветвите, затем слейте dev Филиал, но на этот раз больше не используется git merge dev команда:

Приведенное выше содержание показывает, что на этот раз использование больше не fast forward Режим, но recursive Mode, давайте посмотрим на разницу в истории отправки!

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

Придерживаясь привычки гореть после прочтения, как только ветка будет объединена, она будет немедленно удалена, удалите сейчас dev Разделитесь и посмотрите, что получится:

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

Видно, что delete dev Только меньше после разветвления dev Это всего лишь ссылка, оказывается dev Все изменения, внесенные веткой, сохраняются!

Режим быстрой перемотки вперед ( fast forward )

Создавайте и переключайтесь dev Перейти, вернуться после отправки версии master Разветвите, затем слейте dev Филиал, используйте git merge dev команда:

Теперь вернитесь к master Ветвь, используйте значение по умолчанию git merge Слияние команд dev Филиал:

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

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

Аналогично теперь удаляем dev Branch, еще раз посмотрите историю коммитов:

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

Видно, что как только режим быстрой перемотки удаляет ветку, информация о ветке полностью теряется, даже из истории отправки, нет никаких следов, которые когда-то существовали!

Стратегия филиала

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

Merge made by the art strategy

Check your version of git by running

MERGE STRATEGIES

This can only resolve two heads (i.e. the current branch and another branch you pulled from) using a 3-way merge algorithm. It tries to carefully detect criss-cross merge ambiguities and is considered generally safe and fast.

This can only resolve two heads using a 3-way merge algorithm. When there is more than one common ancestor that can be used for 3-way merge, it creates a merged tree of the common ancestors and uses that as the reference tree for the 3-way merge. This has been reported to result in fewer merge conflicts without causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history. Additionally this can detect and handle merges involving renames. This is the default merge strategy when pulling or merging one branch.

The ‘recursive’ strategy can take the following options:

This option forces conflicting hunks to be auto-resolved cleanly by favoring ‘our’ version. Changes from the other tree that do not conflict with our side are reflected to the merge result. For a binary file, the entire contents are taken from our side.

This should not be confused with the ‘ours’ merge strategy, which does not even look at what the other tree contains at all. It discards everything the other tree did, declaring ‘our’ history contains all that happened in it.

This is the opposite of ‘ours’; note that, unlike ‘ours’, there is no ‘theirs’ merge strategy to confuse this merge option with.

ignore-space-change ignore-all-space ignore-space-at-eol ignore-cr-at-eol

If ‘their’ version only introduces whitespace changes to a line, ‘our’ version is used;

If ‘our’ version introduces whitespace changes but ‘their’ version includes a substantial change, ‘their’ version is used;

Otherwise, the merge proceeds in the usual way.

This runs a virtual check-out and check-in of all three stages of a file when resolving a three-way merge. This option is meant to be used when merging branches with different clean filters or end-of-line normalization rules. See «Merging branches with differing checkin/checkout attributes» in gitattributes[5] for details.

Disables the renormalize option. This overrides the merge.renormalize configuration variable.

This option is a more advanced form of ‘subtree’ strategy, where the strategy makes a guess on how two trees must be shifted to match with each other when merging. Instead, the specified path is prefixed (or stripped from the beginning) to make the shape of two trees to match.

This resolves cases with more than two heads, but refuses to do a complex merge that needs manual resolution. It is primarily meant to be used for bundling topic branch heads together. This is the default merge strategy when pulling or merging more than one branch.

This is a modified recursive strategy. When merging trees A and B, if B corresponds to a subtree of A, B is first adjusted to match the tree structure of A, instead of reading the trees at the same level. This adjustment is also done to the common ancestor tree.

With the strategies that use 3-way merge (including the default, ‘recursive’), if a change is made on both branches, but later reverted on one of the branches, that change will be present in the merged result; some people find this behavior confusing. It occurs because only the heads and the merge base are considered when performing a merge, not the individual commits. The merge algorithm therefore considers the reverted change as no change at all, and substitutes the changed version instead.

Merge made by ‘recursive’ strategy

I understood that git merge recursive actually happens when there is more than 1 common ancestor, and it will create a virtual commit to merge these common ancestors before proceeding to merge the more recent commits (sorry i am not sure whether there should be a term for this).

But I have been trying to find more information on how git merge recursive strategy actually works in detail but not much info can be found.

Can anyone explain in details how git merge recursive really perform, with examples and possibly flow maps to help visualizing better?

1 Answer 1

You can find a description here (see also part 2):

When is merge recursive needed?

What if we find «two common ancestors»? The branch explorer view below shows an alternative in which there are two possible «common ancestors».

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

Please note: the example is a little bit forced since there’s not a good reason – initially – for the developer merging from changeset 11 into 16 instead of merging from changeset 15 (the latest from the branch main at the point of the merge).
But let’s assume it has to be done for a reason, let’s say, changeset 11 was stable and 13 and 15 weren’t at the time, for instance.

The point is: between 15 and 16 there’s not a single unique ancestor, but rather, two ancestors at the same «distance»: 12 and 11.

While this won’t happen frequently, it is really likely to happen with long lived branches or complex branch topologies. (The case depicted above is the shortest one driving to the «multiple ancestor» problem, but it can happen too with several changesets and branches in between the «crossed» merges).

One solution is to «select» one of the ancestors as the valid one for the merge (which is the option Mercurial takes) but it has many drawbacks.

How merge recursive works?

When more than one valid ancestor is found, the recursive-merge strategy will create a new unique «virtual ancestor» merging the ones initially found.

The following image depicts the algorithm:

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

A new ancestor 2 will be used as «ancestor» to merge the «src» and «dst».

Merge made by the art strategy. Смотреть фото Merge made by the art strategy. Смотреть картинку Merge made by the art strategy. Картинка про Merge made by the art strategy. Фото Merge made by the art strategy

The «merge recursive strategy» is able to find a better solution than just «selecting one of the two» as I’ll describe below.

Note: the merge recursive strategy was initially the merge «fredrik» strategy (see commit e4cf17c, Sept. 2005, Git v0.99.7a), after Fredrik Kuivinen.
It was a python script, initiated in commit 720d150, and it illustrates the original algorithm.

(Yes, I don’t know either how to read this)

In case of conflict, the main idea of the algorithm is to simply leave the conflict markers in place when using the result as a base for further merges.
This means that earlier conflicts are properly propagated as well as conflicting changes in newer revisions.

A criss-cross merge is an ancestry graph in which minimal common ancestors are not unique.
The simplest example with scalars is something like:

The story one can tell here is that Bob and Claire made some change independently, then each merged the changes together.
They conflicted, and Bob (of course) decided his change was better, while Claire (typically) picked her version.
Now, we need to merge again. This should be a conflict.

Another possible solution is to first merge ‘ b1 ‘ and ‘ c1 ‘ to a temporary node (basically, imagine that the ‘ X ‘ in the diagram is actually a revision, not just edges crossing) and then use that as a base for merging ‘ b2 ‘ and ‘ c2 ‘.

Since both ‘ b2 ‘ and ‘ c2 ‘ had to resolve the same conflict, in the case they resolved it the same way they both remove the conflicts from ‘ X ‘ in the same way and a clean merge results; if they resolved it in different ways, the conflicts from ‘ X ‘ get propagated to the final merge result.

That is what torek described in «git merge: how did I get a conflict in BASE file?» as an «asymmetric result»:

Resuming from revctrl.org/CrissCrossMerge :

This is what «Git»‘s «recursive merge» strategy does.

With Git 2.29 (Q4 2020), in preparation for a new merge strategy backend, does provide a good description of conflicts and the role of a recursive merge strategy:

t6425 : be more flexible with rename/delete conflict messages

Signed-off-by: Elijah Newren

First, there’s a basic conflict type known as modify/delete, which is a content conflict.
It occurs when one side deletes a file, but the other modifies it.

There is also a path conflict known as a rename/delete.
This occurs when one side deletes a path, and the other renames it.
This is not a content conflict, it is a path conflict.
It will often occur in combination with a content conflict, though, namely a modify/delete.
As such, these two were often combined.

Another type of conflict that can exist is a directory/file conflict. For example, one side adds a new file at some path, and the other side of history adds a directory at the same path.
The path that was «added» could have been put there by a rename, though.
Thus, we have the possibility of a single path being affected by a modify/delete, a rename/delete, and a directory/file conflict.

In part, this was a natural by-product of merge-recursive’s design.
Since it was doing a four way merge with the contents of the working tree being the fourth factor it had to consider, it had working tree handling spread all over the code.
It also had directory/file conflict handling spread everywhere through all the other types of conflicts.

A natural outgrowth of this kind of structure is conflict messages that combine all the different types that the current codepath is considering.

However, if we want to make the different conflict types orthogonal and avoid repeating ourselves and getting very brittle code, then we need to split the messages from these different conflict types apart.
Besides, trying to determine all possible permutations is a royal mess.
The code to handle the rename/delete/directory/file conflict output is already somewhat hard to parse, and is somewhat brittle.
But if we really wanted to go that route, then we’d have to have special handling for the following types of combinations:

Let’s back away from this path of insanity, and allow the different types of conflicts to be handled by separate pieces of non-repeated code by allowing the conflict messages to be split into their separate types. (If multiple conflict types affect a single path, the conflict messages can be printed sequentially.) Start this path with a simple change: modify this test to be more flexible and accept the output either merge backend (recursive or the new ort) will produce.

Note that Git 2.22 (Q2 2019) will improve that recursive merge strategy, since git merge-recursive» backend recently (Git 2.18) learned a new heuristics to infer file movement based on how other files in the same directory moved.

As this is inherently less robust heuristics than the one based on the content similarity of the file itself (rather than based on what its neighbours are doing), it sometimes gives an outcome unexpected by the end users. This has been toned down to leave the renamed paths in higher/conflicted stages in the index so that the user can examine and confirm the result.

merge-recursive : switch directory rename detection default

Note that there is also a third possibility here:

C) There are different answers depending on the context and content that cannot be determined by Git, so this is a conflict.
Use a higher stage in the index to record the conflict and notify the user of the potential issue instead of silently selecting a resolution for them.

With Git 2.31 (Q1 2021), the «ORT» merge strategy (that I presented here) impacts the legacy recursive strategy.

merge-ort : add modify/delete handling and delayed output processing

Signed-off-by: Elijah Newren

At this stage, it is probably not clear why I am opting for delayed output processing.
There are multiple reasons:

Merges are supposed to abort if they would overwrite dirty changes in the working tree.
We cannot correctly determine whether changes would be overwritten until both rename detection has occurred and full processing of entries with the renames has finalized.
Warning/conflict/notice messages come up at intermediate codepaths along the way, so unless we want spurious conflict/warning messages being printed when the merge will be aborted anyway, we need tosave these messages and only print them when relevant.

There can be multiple messages for a single path, and we want all messages for a give path to appear together instead of having them grouped by conflict/warning type.
This was a problem already with merge-recursive.c but became even more important due to the splitting apart of conflict types as discussed in the commit message for 1f3c9ba707 (» t6425 : be more flexible with rename/delete conflict messages», 2020-08-10, Git 2.29)

Some callers might want to avoid showing the output in certain cases, such as if the end result is a clean merge.
Rebases have typically done this.

Источники информации:

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

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