##// END OF EJS Templates
mergetools: add new conflict marker format with diffs in...
mergetools: add new conflict marker format with diffs in I use 3-way conflict markers. Often when I resolve them, I manually compare one the base with one side and apply the differences to the other side. That can be hard when the conflict marker is large. This patch introduces a new type of conflict marker, which I'm hoping will make it easier to resolve conflicts. The new format uses `<<<<<<<` and `>>>>>>>` to open and close the markers, just like our existing 2-way and 3-way conflict markers. Instead of having 2 or 3 snapshots (left+right or left+base+right), it has a sequence of diffs. A diff looks like this: ``` ------- base +++++++ left a -b +c d ``` A diff that adds one side ("diff from nothing") has a `=======` header instead and does not have have `+` prefixed on its lines. A regular 3-way merge can be viewed as adding one side plus a diff between the base and the other side. It thus has two ways of being represented, depending on which side is being diffed: ``` <<<<<<< ======= left contents on left ------- base +++++++ right contents on -left +right >>>>>>> ``` or ``` <<<<<<< ------- base +++++++ left contents on -right +left ======= right contents on right >>>>>>> ``` I've made it so the new merge tool tries to pick a version that has the most common lines (no difference in the example above). I've called the new tool "mergediff" to stick to the convention of starting with "merge" if the tool tries a regular 3-way merge. The idea came from my pet VCS (placeholder name `jj`), which has support for octopus merges and other ways of ending up with merges of more than 3 versions. I wanted to be able to represent such conflicts in the working copy and therefore thought of this format (although I have not yet implemented it in my VCS). I then attended a meeting with Larry McVoy, who said BitKeeper has an option (`bk smerge -g`) for showing a similar format, which reminded me to actually attempt this in Mercurial. Differential Revision: https://phab.mercurial-scm.org/D9551

File last commit:

r45605:6c22f3ab default
r46724:bdc2bf68 default
Show More
bid-merge.txt
115 lines | 5.3 KiB | text/plain | TextLexer
Bid merge is a feature introduced in Mercurial 3.0, a merge algorithm for
dealing with complicated merges.
Bid merge is controled by the `merge.preferancestor` configuration option. The
default is set to `merge.preferancetors=*` and enable bid merge. Mercurial will
perform a bid merge in the cases where a merge otherwise would emit a note:
using X as ancestor of X and X message.
Problem it is solving
=====================
Mercurial's core merge algorithm is the traditional "three-way merge". This
algorithm combines all the changes in two changesets relative to a common
ancestor. But with complex DAGs, it is often possible to have more than one
"best" common ancestor, with no easy way to distinguish between them.
For example, C and D has 2 common ancestors in the following graph::
C D
|\ /|
| x |
|/ \|
A B
\ /
R
Mercurial used to arbitrarily chooses the first of these, which can result in
various issues:
* unexpected hard 3-way merges that would have been completely trivial if
another ancestor had been used
* conflicts that have already been resolved may reappear
* changes that have been reversed can silently oscillate
One common problem is a merge which with the "right" ancestor would be trivial
to resolve because only one side changed. Using another ancestor where the same
lines are different, it will give an annoying 3-way merge.
Other systems like Git have attacked some of these problems with a so-called
"recursive" merge strategy, that internally merges all the possible ancestors
to produce a single "virtual" ancestor to merge against. This is awkward as the
internal merge itself may involve conflicts (and possibly even multiple levels
of recursion), which either requires choosing a conflict disposition (e.g.
always choose the local version) or exposing the user to extremely confusing
merge prompts for old revisions. Generating the virtual merge also potentially
involves invoking filters and extensions.
Concept
=======
(Bid merge is pretty much the same as Consensus merge.)
Bid merge is a strategy that attempts to sensibly combine the results of the
multiple possible three-way merges directly without producing a virtual
ancestor. The basic idea is that for each ancestor, we perform a top-level
manifest merge and generate a list of proposed actions, which we consider
"bids". We then make an "auction" among all the bids for each file and pick the
most favourable. Some files might be trivial to merge with one ancestor, other
files with another ancestor.
The most obvious advantage of considering multiple ancestors is the case where
some of the bids for a file is a "real" (interactive) merge but where one or
more bids just take on of the parent revisions. A bid for just taking an
existing revision is very simple and low risk and is an obvious winner.
The auction algorithm for merging the bids is so far very simple:
* If there is consensus from all the ancestors, there is no doubt what to do. A
clever result will be indistinguishable from just picking a random bid. The
consensus case is thus not only trivial, it is also already handled
perfectly.
* If "keep local" or "get from other" actions is an option (and there is only
one such option), just do it.
* If the auction doesn't have a single clear winner, pick one of the bids
"randomly" - just as it would have done if only one ancestor was considered.
This meta merge algorithm has room for future improvements, especially for
doing better than picking a random bid.
Some observations
=================
Experience with bid merge shows that many merges that actually have a very
simple solution (because only one side changed) only can be solved efficiently
when we start looking at file content in filemerge ... and it thus also
requires all ancestors passed to filemerge. That is because Mercurial includes
the history in filelog hashes. A file with changes that ends up not changing
the content (could be change + backout or graft + merge or criss cross merges)
still shows up as a changed file to manifestmerge. (The git data model has an
advantage here when it uses hashes of content without history.) One way to
handle that would be to refactor manifestmerge, mergestate/resolve and
filemerge so they become more of the same thing.
There is also cases where different conflicting chunks could benefit from using
multiple ancestors in filemerge - but that will require merge tools with fancy
support for using multiple ancestors in 3+-way merge. That is left as an
exercise for another day. That seems to be a case where "recursive merge" has
an advantage.
The current manifest merge actions are very low level imperative and not
symmetrical. They do not only describe how two manifests should be merged, they
also describe a strategy for changing a context from a state where it is one of
the parents to the state where it is the result of the merge with the other
parent. I can imagine that manifestmerge could be simplified (and made more
suitable for in memory merges) by separating the abstract merge actions from
the actual file system operation actions. A more clever wcontext could perhaps
also take care of some of the branchmerge special cases.
We assume that the definition of Mercurial manifest merge will make sure that
exactly the same files will be produced, no matter which ancestor is used. That
assumption might be wrong in very rare cases that really not is a problem.