module Darcs.Patch.Depends
( getUncovered
, areUnrelatedRepos
, findCommonAndUncommon
, mergeThem
, findCommonWithThem
, countUsThem
, removeFromPatchSet
, slightlyOptimizePatchset
, splitOnTag
, patchSetUnion
, patchSetIntersection
, findUncommon
, cleanLatestTag
, contextPatches
) where
import Darcs.Prelude
import Data.List ( delete, intersect, (\\) )
import Data.Maybe ( fromMaybe )
import Darcs.Patch.Named ( getdeps )
import Darcs.Patch.Commute ( Commute )
import Darcs.Patch.Ident ( fastRemoveSubsequenceRL, merge2FL )
import Darcs.Patch.Info ( PatchInfo, isTag, displayPatchInfo )
import Darcs.Patch.Merge ( Merge )
import Darcs.Patch.Permutations ( partitionFL, partitionRL )
import Darcs.Patch.PatchInfoAnd( PatchInfoAnd, hopefully, hopefullyM, info )
import Darcs.Patch.Set
( PatchSet(..)
, Tagged(..)
, SealedPatchSet
, patchSet2RL
, appendPSFL
, patchSetSplit
, Origin
)
import Darcs.Patch.Progress ( progressRL )
import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePStart )
import Darcs.Patch.Witnesses.Eq ( Eq2 )
import Darcs.Patch.Witnesses.Ordered
( (:\/:)(..), (:/\:)(..), (:>)(..), Fork(..),
(+<<+), mapFL, RL(..), FL(..), isShorterThanRL, breakRL,
(+<+), reverseFL, reverseRL, mapRL )
import Darcs.Patch.Witnesses.Sealed
( Sealed(..), seal )
import Darcs.Util.Printer ( renderString, vcat )
taggedIntersection :: forall rt p wX wY . Commute p
=> PatchSet rt p Origin wX -> PatchSet rt p Origin wY ->
Fork (RL (Tagged rt p))
(RL (PatchInfoAnd rt p))
(RL (PatchInfoAnd rt p)) Origin wX wY
taggedIntersection (PatchSet NilRL ps1) s2 = Fork NilRL ps1 (patchSet2RL s2)
taggedIntersection s1 (PatchSet NilRL ps2) = Fork NilRL (patchSet2RL s1) ps2
taggedIntersection s1 (PatchSet (_ :<: Tagged t2 _ _) ps2)
| Just (PatchSet ts1 ps1) <- maybeSplitSetOnTag (info t2) s1 =
Fork ts1 ps1 (unsafeCoercePStart ps2)
taggedIntersection s1 s2@(PatchSet (ts2 :<: Tagged t2 _ t2ps) ps2) =
case hopefullyM t2 of
Just _ ->
taggedIntersection s1 (PatchSet ts2 (t2ps :<: t2 +<+ ps2))
Nothing ->
case splitOnTag (info t2) s1 of
Just (PatchSet com us) ->
Fork com us (unsafeCoercePStart ps2)
Nothing -> Fork NilRL (patchSet2RL s1) (patchSet2RL s2)
maybeSplitSetOnTag :: PatchInfo -> PatchSet rt p wStart wX
-> Maybe (PatchSet rt p wStart wX)
maybeSplitSetOnTag t0 origSet@(PatchSet (ts :<: Tagged t _ pst) ps)
| t0 == info t = Just origSet
| otherwise = do
PatchSet ts' ps' <- maybeSplitSetOnTag t0 (PatchSet ts (pst :<: t))
Just $ PatchSet ts' (ps' +<+ ps)
maybeSplitSetOnTag _ _ = Nothing
splitOnTag :: Commute p => PatchInfo -> PatchSet rt p wStart wX
-> Maybe (PatchSet rt p wStart wX)
splitOnTag t s@(PatchSet (_ :<: Tagged hp _ _) _) | info hp == t = Just s
splitOnTag t patchset@(PatchSet ts hps@(ps :<: hp)) | info hp == t =
if getUncovered patchset == [t]
then Just $ PatchSet (ts :<: Tagged hp Nothing ps) NilRL
else case partitionRL ((`notElem` (t : getdeps (hopefully hp))) . info) hps of
tagAndDeps@(ds' :<: hp') :> nonDeps ->
if getUncovered (PatchSet ts tagAndDeps) == [t]
then let tagged = Tagged hp' Nothing ds' in
return $ PatchSet (ts :<: tagged) nonDeps
else do
unfolded <- unwrapOneTagged $ PatchSet ts tagAndDeps
PatchSet xx yy <- splitOnTag t unfolded
return $ PatchSet xx (yy +<+ nonDeps)
_ -> error "impossible case"
splitOnTag t (PatchSet ts (ps :<: p)) = do
PatchSet ns xs <- splitOnTag t (PatchSet ts ps)
return $ PatchSet ns (xs :<: p)
splitOnTag t0 patchset@(PatchSet (_ :<: Tagged _ _ _s) NilRL) =
unwrapOneTagged patchset >>= splitOnTag t0
splitOnTag _ (PatchSet NilRL NilRL) = Nothing
cleanLatestTag :: Commute p
=> PatchSet rt p wStart wX
-> PatchSet rt p wStart wX
cleanLatestTag inp@(PatchSet ts ps) =
case breakRL (isTag . info) ps of
NilRL :> _ -> inp
(left@(_ :<: t) :> right) ->
case splitOnTag (info t) (PatchSet ts left) of
Just (PatchSet ts' ps') -> PatchSet ts' (ps' +<+ right)
_ -> error "impossible case"
unwrapOneTagged :: PatchSet rt p wX wY -> Maybe (PatchSet rt p wX wY)
unwrapOneTagged (PatchSet (ts :<: Tagged t _ tps) ps) =
Just $ PatchSet ts (tps :<: t +<+ ps)
unwrapOneTagged _ = Nothing
getUncovered :: PatchSet rt p wStart wX -> [PatchInfo]
getUncovered patchset = case patchset of
(PatchSet NilRL ps) -> findUncovered (mapRL infoAndExplicitDeps ps)
(PatchSet (_ :<: Tagged t _ _) ps) ->
findUncovered (mapRL infoAndExplicitDeps (NilRL :<: t +<+ ps))
where
findUncovered :: [(PatchInfo, Maybe [PatchInfo])] -> [PatchInfo]
findUncovered [] = []
findUncovered ((pi, Nothing) : rest) = pi : findUncovered rest
findUncovered ((pi, Just deps) : rest) =
pi : findUncovered (dropDepsIn deps rest)
dropDepsIn :: [PatchInfo] -> [(PatchInfo, Maybe [PatchInfo])]
-> [(PatchInfo, Maybe [PatchInfo])]
dropDepsIn [] pps = pps
dropDepsIn _ [] = []
dropDepsIn ds (hp : pps)
| fst hp `elem` ds =
let extraDeps = fromMaybe [] $ snd hp in
dropDepsIn (extraDeps ++ delete (fst hp) ds) pps
| otherwise = hp : dropDepsIn ds pps
infoAndExplicitDeps :: PatchInfoAnd rt p wX wY
-> (PatchInfo, Maybe [PatchInfo])
infoAndExplicitDeps p
| isTag (info p) = (info p, getdeps `fmap` hopefullyM p)
| otherwise = (info p, Nothing)
slightlyOptimizePatchset :: PatchSet rt p wStart wX -> PatchSet rt p wStart wX
slightlyOptimizePatchset (PatchSet ts0 ps0) =
go $ PatchSet ts0 (progressRL "Optimizing inventory" ps0)
where
go :: PatchSet rt p wStart wY -> PatchSet rt p wStart wY
go (PatchSet ts NilRL) = PatchSet ts NilRL
go s@(PatchSet ts (ps :<: hp))
| isTag (info hp)
, [info hp] == getUncovered s =
PatchSet (ts :<: Tagged hp Nothing ps) NilRL
| otherwise = appendPSFL (go (PatchSet ts ps)) (hp :>: NilFL)
removeFromPatchSet :: (Commute p, Eq2 p) => FL (PatchInfoAnd rt p) wX wY
-> PatchSet rt p wStart wY -> Maybe (PatchSet rt p wStart wX)
removeFromPatchSet bad (PatchSet ts ps) | all (`elem` mapRL info ps) (mapFL info bad) = do
ps' <- fastRemoveSubsequenceRL (reverseFL bad) ps
return (PatchSet ts ps')
removeFromPatchSet _ (PatchSet NilRL _) = Nothing
removeFromPatchSet bad (PatchSet (ts :<: Tagged t _ tps) ps) =
removeFromPatchSet bad (PatchSet ts (tps :<: t +<+ ps))
findCommonAndUncommon :: forall rt p wX wY . Commute p
=> PatchSet rt p Origin wX -> PatchSet rt p Origin wY
-> Fork (PatchSet rt p)
(FL (PatchInfoAnd rt p))
(FL (PatchInfoAnd rt p)) Origin wX wY
findCommonAndUncommon us them = case taggedIntersection us them of
Fork common us' them' ->
case partitionFL (infoIn them') $ reverseRL us' of
_ :> bad@(_ :>: _) :> _ ->
error $ "Failed to commute common patches:\n"
++ renderString
(vcat $ mapRL (displayPatchInfo . info) $ reverseFL bad)
(common2 :> NilFL :> only_ours) ->
case partitionFL (infoIn us') $ reverseRL them' of
_ :> bad@(_ :>: _) :> _ ->
error $ "Failed to commute common patches:\n"
++ renderString (vcat $
mapRL (displayPatchInfo . info) $ reverseFL bad)
_ :> NilFL :> only_theirs ->
Fork (PatchSet common (reverseFL common2))
only_ours (unsafeCoercePStart only_theirs)
where
infoIn inWhat = (`elem` mapRL info inWhat) . info
findCommonWithThem :: Commute p
=> PatchSet rt p Origin wX
-> PatchSet rt p Origin wY
-> (PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin wX
findCommonWithThem us them = case taggedIntersection us them of
Fork common us' them' ->
case partitionFL ((`elem` mapRL info them') . info) $ reverseRL us' of
_ :> bad@(_ :>: _) :> _ ->
error $ "Failed to commute common patches:\n"
++ renderString
(vcat $ mapRL (displayPatchInfo . info) $ reverseFL bad)
common2 :> _nilfl :> only_ours ->
PatchSet common (reverseFL common2) :> unsafeCoerceP only_ours
findUncommon :: Commute p
=> PatchSet rt p Origin wX -> PatchSet rt p Origin wY
-> (FL (PatchInfoAnd rt p) :\/: FL (PatchInfoAnd rt p)) wX wY
findUncommon us them =
case findCommonWithThem us them of
_common :> us' -> case findCommonWithThem them us of
_ :> them' -> unsafeCoercePStart us' :\/: them'
countUsThem :: Commute p
=> PatchSet rt p Origin wX
-> PatchSet rt p Origin wY
-> (Int, Int)
countUsThem us them =
case taggedIntersection us them of
Fork _ us' them' -> let uu = mapRL info us'
tt = mapRL info them' in
(length $ uu \\ tt, length $ tt \\ uu)
mergeThem :: (Commute p, Merge p)
=> PatchSet rt p Origin wX -> PatchSet rt p Origin wY
-> Sealed (FL (PatchInfoAnd rt p) wX)
mergeThem us them =
case taggedIntersection us them of
Fork _ us' them' ->
case merge2FL (reverseRL us') (reverseRL them') of
them'' :/\: _ -> Sealed them''
patchSetIntersection :: Commute p
=> [SealedPatchSet rt p Origin]
-> SealedPatchSet rt p Origin
patchSetIntersection [] = seal $ PatchSet NilRL NilRL
patchSetIntersection [x] = x
patchSetIntersection (Sealed y : ys) =
case patchSetIntersection ys of
Sealed z -> case taggedIntersection y z of
Fork common a b -> case mapRL info a `intersect` mapRL info b of
morecommon ->
case partitionRL (\e -> info e `notElem` morecommon) a of
commonps :> _ -> seal $ PatchSet common commonps
patchSetUnion :: (Commute p, Merge p, Eq2 p)
=> [SealedPatchSet rt p Origin]
-> SealedPatchSet rt p Origin
patchSetUnion [] = seal $ PatchSet NilRL NilRL
patchSetUnion [x] = x
patchSetUnion (Sealed y@(PatchSet tsy psy) : Sealed y2 : ys) =
case mergeThem y y2 of
Sealed p2 ->
patchSetUnion $ seal (PatchSet tsy (psy +<<+ p2)) : ys
areUnrelatedRepos :: Commute p
=> PatchSet rt p Origin wX
-> PatchSet rt p Origin wY -> Bool
areUnrelatedRepos us them =
case taggedIntersection us them of
Fork c u t -> checkit c u t
where
checkit (_ :<: Tagged{}) _ _ = False
checkit _ u t | t `isShorterThanRL` 5 = False
| u `isShorterThanRL` 5 = False
| otherwise = null $ intersect (mapRL info u) (mapRL info t)
contextPatches :: PatchSet rt p wX wY
-> (PatchSet rt p :> RL (PatchInfoAnd rt p)) wX wY
contextPatches = patchSetSplit . slightlyOptimizePatchset