module Darcs.Patch.V1.Commute
(
merge,
merger, unravel,
publicUnravel,
)
where
import Prelude ()
import Darcs.Prelude
import Control.Monad ( MonadPlus, mplus, msum, mzero, guard )
import Control.Applicative ( Alternative(..) )
import Darcs.Patch.Commute ( selfCommuter )
import Darcs.Patch.CommuteFn ( commuterIdFL, commuterFLId )
import Darcs.Util.Path ( FileName )
import Darcs.Patch.Invert ( invertRL )
import Darcs.Patch.Merge ( Merge(..) )
import Darcs.Patch.Patchy ( Commute(..), PatchInspect(..), Invert(..) )
import Darcs.Patch.V1.Core ( RepoPatchV1(..),
isMerger,
mergerUndo )
import Darcs.Patch.Conflict
( Conflict(..), listConflictedFiles
, IsConflictedPrim(..), ConflictState(..), CommuteNoConflicts(..)
, mangleUnravelled
)
import Darcs.Patch.Effect ( Effect(..) )
import Darcs.Patch.FileHunk ( IsHunk(..) )
import Darcs.Patch.Prim ( FromPrim(..), PrimPatch,
is_filepatch, sortCoalesceFL,
)
import Darcs.Patch.Permutations ( headPermutationsRL, simpleHeadPermutationsFL )
import Darcs.Util.Printer ( text, vcat, ($$) )
import Darcs.Patch.V1.Show ( showPatch_ )
import Data.List ( nub, nubBy )
import Data.List.Ordered ( nubSort )
#include "impossible.h"
import Darcs.Patch.Witnesses.Sealed
( Sealed(..) , mapSeal, unseal, FlippedSeal(..), mapFlipped
, unsafeUnseal, unsafeUnsealFlipped )
import Darcs.Patch.Witnesses.Eq ( EqCheck(..), MyEq(..) )
import Darcs.Patch.Witnesses.Unsafe
( unsafeCoerceP, unsafeCoercePStart
, unsafeCoercePEnd )
import Darcs.Patch.Witnesses.Ordered
( mapFL_FL, mapFL,
FL(..), RL(..),
(:/\:)(..), (:\/:)(..), (:>)(..),
lengthFL, mapRL,
reverseFL, reverseRL, concatFL
)
data Perhaps a = Unknown | Failed | Succeeded a
instance Functor Perhaps where
fmap _ Unknown = Unknown
fmap _ Failed = Failed
fmap f (Succeeded x) = Succeeded (f x)
instance Applicative Perhaps where
pure = Succeeded
_ <*> Failed = Failed
_ <*> Unknown = Unknown
Failed <*> _ = Failed
Unknown <*> _ = Unknown
Succeeded f <*> Succeeded x = Succeeded (f x)
instance Monad Perhaps where
(Succeeded x) >>= k = k x
Failed >>= _ = Failed
Unknown >>= _ = Unknown
return = Succeeded
fail _ = Unknown
instance Alternative Perhaps where
empty = Unknown
Unknown <|> ys = ys
Failed <|> _ = Failed
(Succeeded x) <|> _ = Succeeded x
instance MonadPlus Perhaps where
mzero = Unknown
mplus = (<|>)
toMaybe :: Perhaps a -> Maybe a
toMaybe (Succeeded x) = Just x
toMaybe _ = Nothing
toPerhaps :: Maybe a -> Perhaps a
toPerhaps (Just x) = Succeeded x
toPerhaps Nothing = Failed
cleverCommute :: Invert prim => CommuteFunction prim -> CommuteFunction prim
cleverCommute c (p1 :> p2) = case c (p1 :> p2) of
Succeeded x -> Succeeded x
Failed -> Failed
Unknown -> case c (invert p2 :> invert p1) of
Succeeded (ip1' :> ip2') -> Succeeded (invert ip2' :> invert ip1')
Failed -> Failed
Unknown -> Unknown
speedyCommute :: PrimPatch prim => CommuteFunction prim
speedyCommute (p1 :> p2)
| Just m1 <- isFilepatchMerger p1
, Just m2 <- isFilepatchMerger p2
, m1 /= m2 = Succeeded (unsafeCoerceP p2 :> unsafeCoerceP p1)
| otherwise = Unknown
everythingElseCommute :: forall prim . PrimPatch prim => CommuteFunction prim
everythingElseCommute (PP p1 :> PP p2) = toPerhaps $ do
p2' :> p1' <- commute (p1 :> p2)
return (PP p2' :> PP p1')
everythingElseCommute ps =
msum [ cleverCommute commuteRecursiveMerger ps
, cleverCommute otherCommuteRecursiveMerger ps
]
unsafeMerger :: PrimPatch prim => String -> RepoPatchV1 prim wX wY -> RepoPatchV1 prim wX wZ -> RepoPatchV1 prim wA wB
unsafeMerger x p1 p2 = unsafeCoercePStart $ unsafeUnseal $ merger x p1 p2
mergerCommute :: PrimPatch prim
=> (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)
mergerCommute (pA :> Merger _ _ p1 p2)
| unsafeCompare pA p1 = Succeeded (unsafeCoercePStart p2 :> unsafeMerger "0.0" p2 p1)
| unsafeCompare pA (invert (unsafeMerger "0.0" p2 p1)) = Failed
mergerCommute (Merger _ _ b' c'' :> Merger _ _ (Merger _ _ c b) (Merger _ _ c' a))
| unsafeCompare b' b && unsafeCompare c c' && unsafeCompare c c'' =
Succeeded ( unsafeMerger "0.0" b (unsafeCoercePStart a) :>
unsafeMerger "0.0" (unsafeMerger "0.0" b (unsafeCoercePStart a)) (unsafeMerger "0.0" b c)
)
mergerCommute _ = Unknown
instance PrimPatch prim => Merge (RepoPatchV1 prim) where
merge (y :\/: z) =
case actualMerge (y:\/:z) of
Sealed y' -> case commute (z :> y') of
Nothing -> bugDoc $ text "merge_patches bug"
$$ showPatch_ y
$$ showPatch_ z
$$ showPatch_ y'
Just (_ :> z') -> unsafeCoercePStart z' :/\: y'
instance PrimPatch prim => Commute (RepoPatchV1 prim) where
commute x = toMaybe $ msum
[speedyCommute x,
(cleverCommute mergerCommute) x,
everythingElseCommute x
]
instance PrimPatch prim => PatchInspect (RepoPatchV1 prim) where
listTouchedFiles (Merger _ _ p1 p2) = nubSort $ listTouchedFiles p1
++ listTouchedFiles p2
listTouchedFiles c@(Regrem{}) = listTouchedFiles $ invert c
listTouchedFiles (PP p) = listTouchedFiles p
hunkMatches f (Merger _ _ p1 p2) = hunkMatches f p1 || hunkMatches f p2
hunkMatches f c@(Regrem{}) = hunkMatches f $ invert c
hunkMatches f (PP p) = hunkMatches f p
commuteNoMerger :: PrimPatch prim => MaybeCommute prim
commuteNoMerger x =
toMaybe $ msum [ speedyCommute x
, everythingElseCommute x
]
isFilepatchMerger :: PrimPatch prim => RepoPatchV1 prim wX wY -> Maybe FileName
isFilepatchMerger (PP p) = is_filepatch p
isFilepatchMerger (Merger _ _ p1 p2) = do
f1 <- isFilepatchMerger p1
f2 <- isFilepatchMerger p2
if f1 == f2 then return f1 else Nothing
isFilepatchMerger (Regrem und unw p1 p2)
= isFilepatchMerger (Merger und unw p1 p2)
commuteRecursiveMerger :: PrimPatch prim
=> (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)
commuteRecursiveMerger (pA :> p@(Merger _ _ p1 p2)) = toPerhaps $
do (_ :> pA') <- commuterIdFL selfCommuter (pA :> undo)
_ <- commuterIdFL selfCommuter (pA' :> invert undo)
(_ :> pAmid) <- commute (pA :> unsafeCoercePStart (invert p1))
(p1' :> pAx) <- commute (pAmid :> p1)
guard (pAx `unsafeCompare` pA)
(p2' :> _) <- commute (pAmid :> p2)
(p2o :> _) <- commute (invert pAmid :> p2')
guard (p2o `unsafeCompare` p2)
let p' = if unsafeCompare p1' p1 && unsafeCompare p2' p2
then unsafeCoerceP p
else unsafeMerger "0.0" p1' p2'
undo' = mergerUndo p'
(pAo :> _) <- commuterFLId selfCommuter (undo' :> pA')
guard (pAo `unsafeCompare` pA)
return (p' :> pA')
where undo = mergerUndo p
commuteRecursiveMerger _ = Unknown
otherCommuteRecursiveMerger :: PrimPatch prim
=> (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)
otherCommuteRecursiveMerger (p_old@(Merger _ _ p1' p2') :> pA') = toPerhaps $
do (pA :> _) <- commuterFLId selfCommuter (mergerUndo p_old :> pA')
(pAmid :> p1) <- commute (unsafeCoercePEnd p1' :> pA)
(_ :> pAmido) <- commute (pA :> invert p1)
guard (pAmido `unsafeCompare` pAmid)
(p2 :> _) <- commute (invert pAmid :> p2')
(p2o' :> _) <- commute (pAmid :> p2)
guard (p2o' `unsafeCompare` p2')
let p = if p1 `unsafeCompare` p1' && p2 `unsafeCompare` p2'
then unsafeCoerceP p_old
else unsafeMerger "0.0" p1 p2
undo = mergerUndo p
guard (not $ pA `unsafeCompare` p1)
(_ :> pAo') <- commuterIdFL selfCommuter (pA :> undo)
guard (pAo' `unsafeCompare` pA')
return (pA :> p)
otherCommuteRecursiveMerger _ = Unknown
type CommuteFunction prim = forall wX wY . (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)
type MaybeCommute prim = forall wX wY . (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Maybe ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)
commuteFLId :: MaybeCommute prim -> (RepoPatchV1 prim :> FL (RepoPatchV1 prim)) wX wY -> Maybe ((FL (RepoPatchV1 prim) :> RepoPatchV1 prim) wX wY)
commuteFLId _ (p :> NilFL) = return (NilFL :> p)
commuteFLId commuter (p :> (q :>: qs)) = do
q' :> p' <- commuter (p :> q)
qs' :> p'' <- commuteFLId commuter (p' :> qs)
return ((q' :>: qs') :> p'')
elegantMerge :: PrimPatch prim
=> (RepoPatchV1 prim :\/: RepoPatchV1 prim) wX wY
-> Maybe ((RepoPatchV1 prim :/\: RepoPatchV1 prim) wX wY)
elegantMerge (p1 :\/: p2) = do
p1' :> ip2' <- commute (invert p2 :> p1)
p1o :> _ <- commute (p2 :> p1')
guard $ unsafeCompare p1o p1
return $ invert ip2' :/\: p1'
actualMerge :: PrimPatch prim
=> (RepoPatchV1 prim :\/: RepoPatchV1 prim) wX wY -> Sealed (RepoPatchV1 prim wY)
actualMerge (p1 :\/: p2) = case elegantMerge (p1:\/:p2) of
Just (_ :/\: p1') -> Sealed p1'
Nothing -> merger "0.0" p2 p1
unwind :: RepoPatchV1 prim wX wY -> Sealed (RL (RepoPatchV1 prim) wX)
unwind (Merger _ unwindings _ _) = Sealed unwindings
unwind p = Sealed (NilRL :<: p)
trueUnwind :: PrimPatch prim
=> RepoPatchV1 prim wX wY -> Sealed (RL (RepoPatchV1 prim) wX)
trueUnwind p@(Merger _ _ p1 p2) =
case (unwind p1, unwind p2) of
(Sealed (p1s:<:_),Sealed (p2s:<:_)) ->
Sealed (unsafeUnsealFlipped (reconcileUnwindings p p1s (unsafeCoercePEnd p2s)) :<: unsafeCoerceP p1 :<: p)
_ -> impossible
trueUnwind _ = impossible
reconcileUnwindings :: PrimPatch prim
=> RepoPatchV1 prim wA wB -> RL (RepoPatchV1 prim) wX wZ -> RL (RepoPatchV1 prim) wY wZ -> FlippedSeal (RL (RepoPatchV1 prim)) wZ
reconcileUnwindings _ NilRL p2s = FlippedSeal p2s
reconcileUnwindings _ p1s NilRL = FlippedSeal p1s
reconcileUnwindings p (p1s:<:p1) p2s@(tp2s:<:p2) =
case [(p1s', p2s')|
p1s'@(_:<:hp1s') <- headPermutationsRL (p1s:<:p1),
p2s'@(_:<:hp2s') <- headPermutationsRL p2s,
hp1s' `unsafeCompare` hp2s'] of
((p1s':<:p1', p2s':<:_):_) ->
mapFlipped (:<:p1') $ reconcileUnwindings p p1s' (unsafeCoercePEnd p2s')
[] -> case reverseFL `fmap` putBefore p1 (reverseRL p2s) of
Just p2s' -> mapFlipped (:<:p1) $ reconcileUnwindings p p1s p2s'
Nothing ->
case fmap reverseFL $ putBefore p2 $
reverseRL (p1s:<:p1) of
Just p1s' -> mapFlipped (:<:p2) $
reconcileUnwindings p p1s' tp2s
Nothing ->
bugDoc $ text "in function reconcileUnwindings"
$$ text "Original patch:"
$$ showPatch_ p
_ -> bug "in reconcileUnwindings"
putBefore :: PrimPatch prim
=> RepoPatchV1 prim wY wZ -> FL (RepoPatchV1 prim) wX wZ -> Maybe (FL (RepoPatchV1 prim) wY wW)
putBefore p1 (p2:>:p2s) =
do p1' :> p2' <- commute (unsafeCoerceP p2 :> invert p1)
_ <- commute (p2' :> p1)
(unsafeCoerceP p2' :>:) `fmap` putBefore p1' (unsafeCoerceP p2s)
putBefore _ NilFL = Just (unsafeCoerceP NilFL)
instance PrimPatch prim => CommuteNoConflicts (RepoPatchV1 prim) where
commuteNoConflicts (x :> y) = do y' :> x' <- commuteNoMerger (x :> y)
return (y' :> x')
instance PrimPatch prim => Conflict (RepoPatchV1 prim) where
resolveConflicts patch = rcs NilFL (NilRL :<: patch)
where rcs :: FL (RepoPatchV1 prim) wY wW -> RL (RepoPatchV1 prim) wX wY -> [[Sealed (FL prim wW)]]
rcs _ NilRL = []
rcs passedby (ps:<:p@(Merger{})) =
case commuteFLId commuteNoMerger (p :> passedby) of
Just (_ :> p'@(Merger _ _ p1 p2)) ->
map Sealed (nubBy unsafeCompare $
effect (unsafeCoercePStart $ unsafeUnseal (glump09 p1 p2)) : map (unsafeCoercePStart . unsafeUnseal) (unravel p'))
: rcs (p :>: passedby) ps
Nothing -> rcs (p :>: passedby) ps
_ -> impossible
rcs passedby (ps:<:p) = seq passedby $
rcs (p :>: passedby) ps
conflictedEffect x =
case listConflictedFiles x of
[] -> mapFL (IsC Okay) $ effect x
_ -> mapFL (IsC Conflicted) $ effect x
publicUnravel :: PrimPatch prim => RepoPatchV1 prim wX wY -> [Sealed (FL prim wY)]
publicUnravel = map (mapSeal unsafeCoercePStart) . unravel
unravel :: PrimPatch prim => RepoPatchV1 prim wX wY -> [Sealed (FL prim wX)]
unravel p = nub $ map (mapSeal (sortCoalesceFL . concatFL . mapFL_FL effect)) $
getSupers $ map (mapSeal reverseRL) $ unseal (newUr p) $ unwind p
getSupers :: PrimPatch prim
=> [Sealed (FL (RepoPatchV1 prim) wX)] -> [Sealed (FL (RepoPatchV1 prim) wX)]
getSupers (x:xs) =
case filter (not.(x `isSuperpatchOf`)) xs of
xs' -> if any (`isSuperpatchOf` x) xs'
then getSupers xs'
else x : getSupers xs'
getSupers [] = []
isSuperpatchOf :: PrimPatch prim
=> Sealed (FL (RepoPatchV1 prim) wX) -> Sealed (FL (RepoPatchV1 prim) wX) -> Bool
Sealed x `isSuperpatchOf` Sealed y | lengthFL y > lengthFL x = False
Sealed x `isSuperpatchOf` Sealed y = x `iso` y
where iso :: PrimPatch prim => FL (RepoPatchV1 prim) wX wY -> FL (RepoPatchV1 prim) wX wZ -> Bool
_ `iso` NilFL = True
NilFL `iso` _ = False
a `iso` (b:>:bs) =
head $ ([as `iso` bs | (ah :>: as) <- simpleHeadPermutationsFL a, IsEq <- [ah =\/= b]] :: [Bool]) ++ [False]
merger :: PrimPatch prim
=> String -> RepoPatchV1 prim wX wY -> RepoPatchV1 prim wX wZ -> Sealed (RepoPatchV1 prim wY)
merger "0.0" p1 p2 = Sealed $ Merger undoit unwindings p1 p2
where fake_p = Merger NilFL NilRL p1 p2
unwindings = unsafeUnseal (trueUnwind fake_p)
p = Merger NilFL unwindings p1 p2
undoit =
case (isMerger p1, isMerger p2) of
(True ,True ) -> case unwind p of
Sealed (t:<:_) -> unsafeCoerceP $ invertRL t
_ -> impossible
(False,False) -> unsafeCoerceP $ invert p1 :>: NilFL
(True ,False) -> unsafeCoerceP NilFL
(False,True ) -> unsafeCoerceP $ invert p1 :>: mergerUndo p2
merger g _ _ =
error $ "Cannot handle mergers other than version 0.0\n"++g
++ "\nPlease use darcs optimize --modernize with an older darcs."
glump09 :: PrimPatch prim => RepoPatchV1 prim wX wY -> RepoPatchV1 prim wX wZ -> Sealed (FL (RepoPatchV1 prim) wY)
glump09 p1 p2 = mapSeal (mapFL_FL fromPrim) $ mangleUnravelled $ unseal unravel $ merger "0.0" p1 p2
instance PrimPatch prim => Effect (RepoPatchV1 prim) where
effect p@(Merger{}) = sortCoalesceFL $ effect $ mergerUndo p
effect p@(Regrem{}) = invert $ effect $ invert p
effect (PP p) = p :>: NilFL
instance IsHunk prim => IsHunk (RepoPatchV1 prim) where
isHunk p = do PP p' <- return p
isHunk p'
newUr :: PrimPatch prim
=> RepoPatchV1 prim wA wB -> RL (RepoPatchV1 prim) wX wY -> [Sealed (RL (RepoPatchV1 prim) wX)]
newUr p (ps :<: Merger _ _ p1 p2) =
case filter (\(_:<:pp) -> pp `unsafeCompare` p1) $ headPermutationsRL ps of
((ps':<:_):_) -> newUr p (ps':<:unsafeCoercePStart p1) ++ newUr p (ps':<:unsafeCoercePStart p2)
_ -> bugDoc $ text "in function newUr"
$$ text "Original patch:"
$$ showPatch_ p
$$ text "Unwound:"
$$ vcat (unseal (mapRL showPatch_) $ unwind p)
newUr op ps =
case filter (\(_:<:p) -> isMerger p) $ headPermutationsRL ps of
[] -> [Sealed ps]
(ps':_) -> newUr op ps'
instance Invert prim => Invert (RepoPatchV1 prim) where
invert (Merger undo unwindings p1 p2)
= Regrem undo unwindings p1 p2
invert (Regrem undo unwindings p1 p2)
= Merger undo unwindings p1 p2
invert (PP p) = PP (invert p)
instance MyEq prim => MyEq (RepoPatchV1 prim) where
unsafeCompare = eqPatches
instance MyEq prim => Eq (RepoPatchV1 prim wX wY) where
(==) = unsafeCompare
eqPatches :: MyEq prim => RepoPatchV1 prim wX wY -> RepoPatchV1 prim wW wZ -> Bool
eqPatches (PP p1) (PP p2) = unsafeCompare p1 p2
eqPatches (Merger _ _ p1a p1b) (Merger _ _ p2a p2b)
= eqPatches p1a p2a &&
eqPatches p1b p2b
eqPatches (Regrem _ _ p1a p1b) (Regrem _ _ p2a p2b)
= eqPatches p1a p2a &&
eqPatches p1b p2b
eqPatches _ _ = False