-
Notifications
You must be signed in to change notification settings - Fork 507
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3-way merge #181
Comments
This was implemented but not made part of the top-level API f94da90 |
@gonzofish how to write it to file like git does? |
sorry @goodmind, I don't remember, I haven't used this library in over a year...sorry I cannot be more help |
I have done it, but I found |
Hmm. Just started looking at the merge.js code and besides the already-reported bugs I see more to dislike; |
I just had another few minutes to look at this. I've been reading the key bit of logic here and trying to reason about it:
Maybe I'm missing something, but just by inspection, I think I immediately see a bug in that |
Yeah, the bug I spotted in review is real. Here's a demo script:
There's a conflict between the two patches (the first one deletes
|
Plan:
If I also screw up and create something buggy, then I'll see if I can learn something from what's already there and synthesize a working implementation. |
Oh man - I just looked at tests and realised that the existing code intentionally tries to support the case where the two patches use a different base file (rather than just throwing an error in that situation). Ugh, why? I struggle to reason about the use case for that or figure out what correct behaviour is. I don't see how even attempting to do that can possibly be useful, because if you're allowing for the possibility that your patches are based on slightly different base files, then you've got to handle the possibility that two hunks that are really patching the "same" bit of the file start from different old line numbers. But that requires a completely different algorithm, that matches hunks from the two patches against each other based on (possibly fuzzy) matching of shared context (like when applying a patch), and for which "correct" results are often going to be outright different to an algorithm that doesn't attempt to support merging patches with different bases. It just seems ill-conceived to me; IMO if you're two patches make different claims (in context or removal lines) about what the old content of line 52 was, you should immediately throw an error saying "Patches disagree about old content of line 52". Treating this as a conflict, like when one patch wants to delete line 52 and another patch wants to insert a line after line 52, isn't correct; it's a fundamentally different situation that calls the validity of the entire merge into question. |
Hmm - on the one hand, trying to apply a patch to a file that doesn't exactly match its expectations is pretty common; that's what conflicts are. But on the other hand, if you're trying to apply to merge two patches into a coherent whole, they need some sort of common ancestor to start from. I can imagine a situation where you "zip" two branches together, merging the commits in chronological order - although that seems ill-advised in general - and then you might end up with two patches that both disagree about what the base should look like because neither takes into account the preceding partial merge. But at that point I think you just have to pick a patch to merge first, not try to merge both at the same time, so you're not really talking about a 3-way merge anymore. |
The more I think about this the more I think it's actually super-complicated. Here are six different tasks that you might characterise as merging two patches, but which are different from each other:
Each of these six different scenarios probably requires a slightly different algorithm to any of the other five, and they require different interfaces and ways of resolving conflicts, too, since in some cases you can present a file with merge conflicts in it like Git does, and in others you can't. Note also that even in the absence of conflicts, the behaviours of these functions have to differ. Suppose you are merging the following two patches, without a copy of the base file... Patch 1:
Patch 2:
If you are trying to perform task 1b - merging two patches that have the same base - then your code should "reason" along these lines:
And you end up with something like this:
But if you are trying to merge the very same two patches in order to perform task 2d - merging two patches whose bases are presumed to be non-identical, albeit with some common ancestor - then your code should "reason" about them differently. It should instead think to itself:
And then you get output something like this (albeit it's not clear what the line numbers should be - here I've averaged them!)
So two functions with similar looking interfaces - both take a pair of patches and no source file, and output a new patch - and which both are "merging" patches need different algorithms that will output fundamentally different results! And maybe both of these are potentially useful functions that should exist. But the code that currently exists in merge.js seems to be half-heartedly trying to fulfil both use cases at once, and it fundamentally can't succeed at that, because they require different algorithms that will output different results given the same inputs. |
Okay my new plan is to abandon this. :P I'm not gonna close any of the issues. But I'm not gonna do more work on it either. I don't have a good grasp of the use cases in which people want to merge patches, and I now realise it's not actually one potential feature, but (at least) six potential features in a trenchcoat, none of which are simple either algorithmically or in terms of API design. I welcome others taking a stab at this, but I don't personally want to sink time into it; apologies to anyone who saw me suggest I would and got excited! |
My scenario wasn't that difficult. In my case, it was used for racing save
operations.
Assume you opened a source file, and made a few fixes, and forgot to save.
Then in a different text editor, you fixed another thing, and saved it.
When you go back to that first editor, you realize you need all those
unsaved things, so you press save. If it allowed it to be saved, it would
clobber the edits you did in that other editor.
To handle it, I set base to the state of the file when the editor
initialized its content. That state is in both histories - you know that
the other editor with the racing edit was identical to the base at some
point in its history. So you give the initial editor content as base, you
give the content you tried to save as mine, and you give the conflicting
content the other editor saved as theirs. If there is no conflict, you end
up bringing their changes into the file you are attempting to save, then
you start over with content that includes the other editor's changes, now
only your changes that you forgot to save are different, and it saves
safely.
…On Sat, Jun 29, 2024 at 3:55 PM Mark Amery ***@***.***> wrote:
Okay my new plan is to abandon this. :P
I'm not gonna close any of the issues. But I'm not gonna do more work on
it either. I don't have a good grasp of the use cases in which people want
to merge patches, and I now realise it's not actually one potential
feature, but (at least) six potential features in a trenchcoat, *none* of
which are simple either algorithmically or in terms of API design. I
welcome others taking a stab at this, but I don't personally want to sink
time into it; apologies to anyone who saw me suggest I would and got
excited!
—
Reply to this email directly, view it on GitHub
<#181 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA23YELXNYXJ4Z5CP4I3WPTZJ4GKJAVCNFSM6AAAAABJ2C6DAWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJYGMZDCNRZGQ>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
This is a feature request.
Given a common ancestor and 2 sets of patches, provide a merged document that may include conflicts to resolve manually.
This would be useful to allow two or more users collaborate on the edition of a document, implementing an equivalent of git merge.
It is assumed that the most recent common ancestor is know and provided as a parameter:
Where x_patches and y_patches are Arrays of patches for the x and y branches from the common ancestor,
Options may be used to provide a manual merge patterns which could default to:
For the algorithm, see https://www.quora.com/How-does-Git-merge-work.
The text was updated successfully, but these errors were encountered: