Hi again,

sorry that this took a little longer than I have expected... Thanks
for the reply as well, now let me try to address your comments. And
pretty please, pardon my ignorance (no irony) -- I am pretty
unexperienced with the theory and it may very well be that there is
some fundamental problem in what I am trying to describe that I don't
see and that you are trying to fix and I just don't "get" it.

David Roundy <[EMAIL PROTECTED]> writes:
>> The step, where user selects "taken" and "cancelled" branches would
>> record a patch of kind "R" (as in resolution), with this shape:
>> 
>>     R({A,B,...},C)
>> 
>> Where the set represents the cancelled branch (which we will see later
>> can contain more than a single patch, but it is probably better
>> thought of as a set than a list) and C represents the taken branch
>> (which is necessarily a single patch only). It should be probably
>> noted, that we record "resolution" patch for each "primitive"
>> conflict, not a big single resolution patch.
>> 
>> Normally, darcs handles conflicts by not applying any of the patches
>> involved in conflict. Say, A and B are in conflict here:
>> 
>>     A B
>> 
>> neither of A and B have any effect on working copy. If we add a resolution:
>> 
>>     A B R({A},B)
>> 
>> (B was selected as the taken branch), the R here has same effect as B
>> (and thus the whole sequence, since A B alone have no effect at all).
>> 
>> Fairly obviously, B R(_,B) fail to commute. For
>> 
>>     A R(x,_) where A `elem` x
>> 
>> we cannot fail the commute, since we want the
>> 
>>     inv(R(x,_)) A
>> 
>> commute to succeed (I think this is a requirement of patch theory, but
>> even if it was not, we will need it). Therefore, we define a new patch
>> type to be a result of these commutes:
>> 
>>     A R(x,B) <-> R(x - A,B) D(A,B)
>
> I don't think we actually want either of these commutes to succeed.  What
> this commute means (if it succeeds) is that the resolution patch does not
> depend on the conflicting patch, which is highly counter-intuitive (at
> least to me).  Making
Now, I have to disagree here. The resolution patch only depends on the
actual selected (taken) branch, but not on the cancelled branch. I
feel this is a good choice, because it makes unpulling cancelled
patches possible as well as pulling in to-be-cancelled patches
(ie. patches, that are involved in the "same conflict" -- are
dependencies of a conflicted-and-cancelled patch).

>
>   A R(x,_) where A `elem` x
>
> fail to commute should eliminate the D-style patch, which (I think) will
> greatly simplify the picture.
>
> Why is it that you think we'll need for the commute of
>
>    inv(R(x,_)) A
>
> to succeed?
Because I want to push A, which needs to be cancelled, back through,
without creating a new conflict (on the patch level, at least). That
way, I can "slip" the patch that would further the conflict past the
resolution patch for that conflict, making the resolution patch a
resolution patch valid for the new (bigger) conflict as well.

> For the moment, I'm going to skim over the discussion of D-patch
> commutation, since I don't see why we would want to allow these patch
> types.
These actually facilitate the mechanism of pushing the
newly-conflicted patches past the resolution, which is essential for
the mechanism to work as intended. I hopefully explained better what
is the intention elsewhere in this mail...

> I should perhaps point out that in darcs (either darcs-1 or darcs-2),
> merging of conflicted patches does *not* occur by a commutation, since
> it'll affect things below.
I am not sure I understand this. Could you please elaborate, if
possible? I am not sure what you mean with "merging of conflicted
patches" or whether that means that commutation is not involved at all
or is involved in a different manner than how I am using it?

>> The D-style patch carries both A (the cancelled patch) and B (which
>> identifies the R-patch which caused the disablement). The D(_,B) patch
>> has no effect and commutes freely with all non-R(_,B) patches (since
>> it has no effect, ie. is a noop patch).
>> 
>> Now, we need the inverse-resolution commutation for D patches:
>> 
>>     inv(R(x,B)) A <-> D(A,B) inv(R(x \cup A,B))
>>         iff
>>             A fails to commute with any member of the set x
>>         and
>>     inv(R(x,B)) A <-> A inv(R(x,B))
>>         otherwise.
>> 
>> There are now two possibilities with regards to D patches: we can
>> either disallow their persistence, by forcing them to commute past
>> their respective R-patch, which turns them into non-D patches again,
>> or we could possibly try to retain them and define their inverses and
>> commutations (although this is not required, since their inverses
>> never actually exist if we require them to be commuted past the
>> R-patch).
>> 
>> With "persistent D patches", we would get:
>> 
>>     inv(D(A,B)) X <-> D(X,B) inv(D(A,B))
>>         iff
>>             X and A fail to commute
>>         and
>>     inv(D(A,B)) X <-> X inv(D(A,B))
>>         otherwise.
>> 
>> We see that the D-patches can effectively encode the same information
>> that the "cancelled set" in R-patches, but the R-patches have to have
>> this information and have to commute with D-patches as described above
>> regardless of this, which makes it favourable to remove D patches by
>> commutation and not ever store (or invert) them.
>
> Here's an interesting test case below: (but I'll leave my comments for
> after you come to your conclusion below)
>
>> Now, take that scenario we had above:
>> 
>>     A B R({A},B)
>> 
>> and let's say, we want to merge C, from a repository:
>> 
>>     A C (where C depends on A)
>> 
>> we first get:
>> 
>>     A B R({A},B) inv(R({A},B) inv(B) C
>> 
>> now we need another commute, namely
>> 
>>     inv(R({A},B)) inv(B) <-> inv(B) inv(R({A},B))
>> 
>> (Sidenote: I spot a problem here, since B R(_,B) fail to commute, but
>> inv(R(_,B)) inv(B) is required to commute and I am not sure what
>> problems is this going to cause elsewhere. This is required to get C
>> past inv(B) safely, even though they fail to commute by themselves.)
>> 
>> There, we get:
>> 
>>     A B R({A},B) inv(B) inv(R({A},B) C
>> 
>> now, we use the inv-R commutation:
>> 
>>     inv(R(x,B)) A <-> D(A,B) inv(R(x \cup A,B))
>> 
>> to get:
>> 
>>     A B R({A},B) inv(B) D(C,B) inv(R({A,B},B)
>> 
>> since D commutes freely, we now get past inv(B):
>> 
>>     A B R({A},B) D(C,B) inv(B) inv(R({A,B},B)
>> 
>> and the R/D commute gives:
>> 
>>     A B C R({A,C},B) inv(B) inv(R({A,B},B)
>> 
>> and provides a merge:
>> 
>>     A B C R({A,C},B)
>> 
>> which is a non-conflicted repository again. This demonstrates how the
>> cancellation works and how it propagates to patches that depend on the
>> cancelled branch.
>
> I believe this will be a real problem.  Much better behavior would be to
> have the merging of C have the result that the conflict is re-invigorated.
> Having the change C silently disappear is likely to be confusing to users,
> who are now pulling a separate change distinct from the one they previously
> "resolved", and find that pulling this change has no effect, and doesn't
> involve a conflict either!
Now, this is indeed true, and is what "unilateral resolution" means,
as I understand it. It does not neccessarily have to be silent -- you
can definitely print a warning when new patches are pulled that are
immediately cancelled by some of your conflict resolution... Say:

Finished pulling and applying.
Patches have been pulled that were cancelled due to conflict resolution:

Fri Jan 18 12:28:04 CET 2008 [EMAIL PROTECTED]
  * Resolve foo vs. bar conflict (in favour of foo).

Cancelled patches:

Fri Jan 25 00:22:14 CET 2008 [EMAIL PROTECTED]
  * Wibblify the bars.

And also have darcs changes --cancelled or such, that would print a
list of patches that are in the repository but have been cancelled due
to conflict resolutions (these are the patches that can be found in
the set that is the first parameter in R-type patches).

In fact, since as I understand it, darcs 2 currently works on the
primitive-patch level, it should only cancel the actual-conflicting
bits, not whole named patches. This would however need that UI is
available for presenting partially-cancelled patches and such.

> I should point out that you've got a considerably-simplified notation which
> ignores conflicts (i.e. you're using B to describe the primitive change B
> as well as the conflicted patch B that has been merged with the conflicting
> change A).  This obscures some of the behavior.
>
> I would write the repository containing A and B as
>
> A X([A] B)
Does this mean, that X([A] B) is a different patch from B? Or is this
just notational? In the latter case, it shouldn't impact things too
badly. I will assume, that the sequence A X([A] B) is a noop and that
X([A] B) cancels A's changes and that the commutation is:

    A X([A] B) <-> B X([B] A)

> where the bit in parentheses indicate that it is the change B which has
> conflicted with the change A.
>
> then the repository with A, B and C (but no resolution) would look like
>
> A X([A] B) X({A,B}A:C)
>
> where the second conflict is more confusing:  it indicates that C is
> in a conflict involving A and B, and C depends on A.  And that A and B have
> both already conflicted (with each other), so we don't need to reverse
> their effects.  (Note: mentioning A twice in X({A,B}A:C) is indeed
> redundant, but seems to make certain operations easier.)
>
> At this point, we can imaging merging *this* X({A,B}A:C) with your
> R({A}B).
>
> The merge could put them in either order, and a commute must swap between
> those orders.
>
> I'd vote for a merge result that looks something like:
>
> A X([A]B) X({A,B}A:C) X({A:C} R({A}B))
>
> which is to say that the C patch should conflict with the resolution
> patch.  I think this will be nicer behavior for our users.  Then a commute
> would need to turn this into something like:
>
> A X([A]B) R({A}B) X([R({A}B)] A:C)
>
> But I haven't worked out how this would all work.
This however reintroduces the conflict proliferation problem... With
these modifications, it would be neccessary to record a new conflict
resolution for each single patch depending on a cancelled patch, which
would probably again lead to the conflict fight scenarios.

How this works is basically this: with each conflict resolution, we
remember the "winning side". When pulling patches, the default action
(without recording any new patches into the repository) is to reuse
the decision that has already been made. The user would have to
explicitly ask to change his previous decision, by recording a new
resolution patch for the same conflict.

The other solution to avoid repeated manual resolution of the same
conflict would be automatic record of new resolution, but this leads
to the same issues that all others DVCS have with explicitly recording
merges, albeit darcs would only have them in the conflicted scenario
(it already does, it just also needs manual intervention). My
observation is that the darcs philosophy is to avoid merge-records
when possible, since it makes pulling patches between branches much
easier and much cheaper.

Yours,
    Peter.

-- 
Peter Rockai | me()mornfall!net | prockai()redhat!com
 http://blog.mornfall.net | http://web.mornfall.net

"In My Egotistical Opinion, most people's C programs should be
 indented six feet downward and covered with dirt."
     -- Blair P. Houghton on the subject of C program indentation
_______________________________________________
darcs-devel mailing list
darcs-devel@darcs.net
http://lists.osuosl.org/mailman/listinfo/darcs-devel

Reply via email to