[darcs-devel] Bug in get_extra :)

Anthony Towns aj at azure.humbug.org.au
Sun Jan 2 09:16:01 PST 2005


David Roundy wrote:
> Alas, it does seem that for all its complexity, the merger code is broken.
> :(

:(

I'm not actually trying to find bugs in darcs, I'm actually just trying 
to work out how mergers are supposed to work. At least when darcs is 
confused about it too, I don't feel so bad about not getting it :)

Anyway, the reason I'm trying to find out the tricky bits is so I can 
fill in the bits of the patch theory that don't make sense to me. I did 
come up with something that almost works as an algorithm for commuting 
mergers that doesn't need all the unwinds and glumping; but 
unfortunately there's a catch. The algorithm's current draft is 
something like:

	commute :: Patch -> Patch -> Maybe(Patch, Patch)

	-- x might be a Merger itself
	commute x (Merger True y z) =
		do (x', y') <- unmerge x y
		   (_, z')  <- commute x' z
                    let newZ = cM y' z'
		   in return (newZ, (cM newZ x))

	-- z is definitely not a Merger
	commute (Merger True x y) z =
		do (x',z') = merge x z
		   return (z', cM x' (cM z y))

	cM x y :: Patch -> Patch -> Patch
	cM x y = case merge x y in
		       Just(_, y') -> y'
		       Nothing     -> Merger(x,y)

But the catch is that while "commute" and "merge" can fail instead of 
introducing mergers (I think), unmerge has to be able to deal with 
things like unmerging (M(A,B), M(B,A)) to (B,A), which regular commute 
can't do. :(

I'm not sure that's solvable; and I'm also not sure the above algorithm 
even terminates reliably -- while the unmerger/commute are okay on their 
own, the cM can build up even more complicated patches, potentially ones 
that'll later be reduced... :(

It's completely untested, of course :)

Still, on the offchance it might be useful there it is. :)

> The way this is implemented is that I introduce a "force_commute" which
> always succeeds, and which creates a pair of conflictors (one "forward"
> conflictor, and one "inverse" conflictor) when you use it to commute two
> non-commuting patches.  The result is that when you use force_commute, the
> merge theorem always works (since all the relevant commutations must
> succeed).  This places stronger constraints on the commutation properties
> of conflictors than there were on the commutation properties of mergers.
> What remains to be seen is whether those constraints can be satisfied.

Neat. That might solve my unmerge problem. :-/

> In case you didn't make the connection, once I get the new code up and
> running, I'll definitely appreciate testing of the sort you're now doing on
> the old code.  I'm afraid, however, that the old code may be fundamentally
> flawed here, so fixing these bugs in the old code may not be possible.  :(

So, the other half-baked thought I had was related to resolving 
conflicts. I think it'd work really well to introduce a new "resolution" 
patch type, R(<history>,patch), such that:

	applying R(<history>,patch) doesn't change the repository
	applying patch to <history> gives the same contents as the
		current repository

	commute R(<h>, p), NamedP(n,q) =
		do (_, p') = commute p q
                    return (NamedP(n,q), R(<h,n>, p'))

	commute NamedP(n,p) R(<h>,q)
		| n `elem` h = do (q', _) = commute p q
                                   return (R(<h'>,q'), NamedP(n,p))
		| otherwise = Nothing

and applying a patch to a repo with ...,R(<h>,p) is reduced to applying 
a patch to <h>,p.

It's half baked because:
	(a) I still don't grok mergers :)
	(b) I'm not sure how to work out when to merge against a
	    Resolving patch instead of the real history

There are two really nice things about it, though: one is that it lets 
the user tell darcs how to ignore historical mergers that're getting 
annoying to commuter around; and the other is it lets you tell darcs 
explicitly how to merge things, so if you happened to change "foo" to 
"bar" by hand instead of using replace, and then a few weeks later start 
getting patches against the old version, you could add a resolving patch 
R(<oldtag>, "replace foo.txt [A-z0-9] foo bar") and be happy.

Resolving a merge conflict would take the form:

	H, A, M(A,B), {P, R(<H:A>, Pa), R(<H:B>, Pb)}

where Pa ~= diff(H,A ; H,A,M(A,B),P) and Pb likewise.

Cheers,
aj




More information about the darcs-devel mailing list