-- Copyright (C) 2002-2003 David Roundy, 2010 Ganesh Sittampalam module Darcs.Patch.Conflict ( Conflict(..) , ConflictDetails(..) , Mangled , Unravelled , mangleOrFail , combineConflicts , findConflicting ) where import Darcs.Prelude import Darcs.Patch.Commute ( Commute(..), commuteFL, commuteRL ) import Darcs.Patch.CommuteFn ( commuterIdFL ) import Darcs.Patch.CommuteNoConflicts ( CommuteNoConflicts(..) ) import Darcs.Patch.Permutations () import Darcs.Patch.FromPrim ( PrimOf ) import Darcs.Patch.Prim ( PrimMangleUnravelled(..), Mangled, Unravelled ) import Darcs.Patch.Show ( ShowPatch(..), ShowPatchFor(ForStorage), showPatch ) import Darcs.Patch.Witnesses.Ordered ( (:>)(..), FL(..), RL(..), mapFL, (+<<+) ) import Darcs.Util.Printer ( renderString, text, vcat, ($$) ) data ConflictDetails prim wX = ConflictDetails { conflictMangled :: Maybe (Mangled prim wX), conflictParts :: Unravelled prim wX } -- | For one conflict (a connected set of conflicting prims), store the -- conflicting parts and, if possible, their mangled version. mangleOrFail :: PrimMangleUnravelled prim => Unravelled prim wX -> ConflictDetails prim wX mangleOrFail parts = ConflictDetails { conflictMangled = mangleUnravelled parts, conflictParts = parts } class Conflict p where isConflicted :: p wX wY -> Bool -- | The first parameter is a context containing all patches -- preceding the ones for which we want to calculate the conflict -- resolution, which is the second parameter. -- Each element of the result list represents the resolution -- of one maximal set of transitively conflicting alternatives, -- in other words, a connected subset of the conflict graph. -- But the elements themselves must not conflict with each other, -- guaranteeing that they can be cleanly merged into a single 'FL' of prims. resolveConflicts :: RL p wO wX -> RL p wX wY -> [ConflictDetails (PrimOf p) wY] -- | By definition, a conflicting patch is resolved if another patch -- (that is not itself conflicted) depends on the conflict. If the -- representation of conflicts is self-contained as it is for V1 and V2, -- then we can calculate the maximal set of conflicting alternatives for -- a conflict separately for each conflictor at the end of a repo. -- This function can then be used to lift this to an 'RL' of patches. -- -- So, when looking for conflicts in a list of patches, we go -- through the whole list looking for individual patches that represent -- a conflict. But then we try to commute them past all the -- patches we've already seen. If we fail, i.e. there's something -- that depends on the conflict, then we forget about the conflict; -- this is the Nothing case of the 'commuteNoConflictsFL' call. -- Otherwise the patch is now in the correct position to extract the -- conflicting alternatives. combineConflicts :: forall p wX wY. CommuteNoConflicts p => (forall wA wB. p wA wB -> [Unravelled (PrimOf p) wB]) -> RL p wX wY -> [Unravelled (PrimOf p) wY] combineConflicts resolveOne x = rcs x NilFL where rcs :: RL p wX wM -> FL p wM wY -> [Unravelled (PrimOf p) wY] rcs NilRL _ = [] rcs (ps :<: p) passedby | null (resolveOne p) = seq passedby rest -- TODO why seq here? | otherwise = case commuterIdFL commuteNoConflicts (p :> passedby) of Just (_ :> p') -> resolveOne p' ++ rest Nothing -> rest where rest = rcs ps (p :>: passedby) -- | Find all patches in the context that conflict with a given patch, -- commuting them to the head (past the patch in question). -- -- This actually works by commuting the patch and its dependencies backward -- until it becomes unconflicted, then minimizing the trailing patches by -- re-commuting them backward as long as that keeps the patch unconflicted. -- -- Precondition: the context must contain all conflicting patches. findConflicting :: forall p wX wY wZ . (Commute p, Conflict p, ShowPatch p) => RL p wX wY -> p wY wZ -> (RL p :> p :> RL p) wX wZ findConflicting context patch = go (context :> NilFL :> patch :> NilFL) where go :: (RL p :> FL p :> p :> FL p) wA wB -> (RL p :> p :> RL p) wA wB go (ctx :> deps :> p :> nondeps) | not (isConflicted p) = prune (ctx +<<+ deps :> p :> NilRL :> nondeps) go (NilRL :> deps :> p :> nondeps) = error $ renderString $ text "precondition violated:" $$ vcat (mapFL (showPatch ForStorage) deps) $$ text "===============" $$ text "patch:" $$ (showPatch ForStorage) p $$ text "===============" $$ vcat (mapFL (showPatch ForStorage) nondeps) go (cs :<: c :> deps :> p :> nondeps) = case commuteFL (c :> deps) of Nothing -> go (cs :> c :>: deps :> p :> nondeps) Just (deps' :> c') -> case commute (c' :> p) of Nothing -> go (cs :> c :>: deps :> p :> nondeps) Just (p' :> c'') -> go (cs :> deps' :> p' :> c'' :>: nondeps) prune :: (RL p :> p :> RL p :> FL p) wA wB -> (RL p :> p :> RL p) wA wB prune (ctx :> p :> rs :> NilFL) = ctx :> p :> rs prune (ctx :> p :> rs :> n :>: ns) | Just (n' :> rs') <- commuteRL (rs :> n) , Just (n'' :> p') <- commute (p :> n') , not (isConflicted p') = prune (ctx :<: n'' :> p' :> rs' :> ns) | otherwise = prune (ctx :> p :> rs :<: n :> ns)