module Darcs.Repository.Pending
( readPending
, readTentativePending
, writeTentativePending
, siftForPending
, tentativelyRemoveFromPending
, tentativelyRemoveFromPW
, revertPending
, finalizePending
, makeNewPending
, tentativelyAddToPending
, setTentativePending
) where
import Darcs.Prelude
import Control.Applicative
import Control.Exception ( catch, IOException )
import System.Directory ( renameFile )
import Darcs.Patch
( PrimOf
, RepoPatch
, PrimPatch
, applyToTree
, readPatch
)
import Darcs.Patch.Apply ( ApplyState )
import Darcs.Patch.Commute ( Commute(..) )
import Darcs.Patch.Invert ( Invert(..) )
import Darcs.Patch.Permutations
( removeFL
, commuteWhatWeCanFL
, commuteWhatWeCanRL
)
import Darcs.Patch.Prim
( PrimSift(siftForPending)
, PrimCanonize(primDecoalesce)
)
import Darcs.Patch.Progress (progressFL)
import Darcs.Util.Parser ( Parser )
import Darcs.Patch.Read ( ReadPatch(..), bracketedFL )
import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatchFor(ForStorage) )
import Darcs.Patch.Show ( displayPatch )
import Darcs.Patch.Witnesses.Eq ( Eq2(..) )
import Darcs.Patch.Witnesses.Ordered
( RL(..), FL(..), (+>+), (+>>+), (:>)(..), mapFL, reverseFL )
import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), mapSeal )
import Darcs.Patch.Witnesses.Unsafe ( unsafeCoercePStart )
import Darcs.Repository.Flags ( UpdatePending (..))
import Darcs.Repository.InternalTypes ( Repository, withRepoLocation, unsafeCoerceT )
import Darcs.Repository.Paths ( pendingPath )
import Darcs.Util.ByteString ( gzReadFilePS )
import Darcs.Util.Exception ( catchNonExistence )
import Darcs.Util.Lock ( writeDocBinFile, removeFileMayNotExist )
import Darcs.Util.Printer ( Doc, ($$), text, vcat, (<+>), renderString )
import Darcs.Util.Progress ( debugMessage )
import Darcs.Util.Tree ( Tree )
newSuffix, tentativeSuffix :: String
newSuffix = ".new"
tentativeSuffix = ".tentative"
readPending :: RepoPatch p => Repository rt p wR wU wT
-> IO (Sealed (FL (PrimOf p) wR))
readPending = readPendingFile ""
readTentativePending :: RepoPatch p => Repository rt p wR wU wT
-> IO (Sealed (FL (PrimOf p) wT))
readTentativePending = readPendingFile tentativeSuffix
readNewPending :: RepoPatch p => Repository rt p wR wU wT
-> IO (Sealed (FL (PrimOf p) wT))
readNewPending = readPendingFile newSuffix
readPendingFile :: ReadPatch prim => String -> Repository rt p wR wU wT
-> IO (Sealed (FL prim wX))
readPendingFile suffix _ =
do
let filepath = pendingPath ++ suffix
raw <- gzReadFilePS filepath
case readPatch raw of
Right p -> return (mapSeal unFLM p)
Left e -> fail $ unlines ["Corrupt pending patch: " ++ show filepath, e]
`catchNonExistence` Sealed NilFL
newtype FLM p wX wY = FLM { unFLM :: FL p wX wY }
instance ReadPatch p => ReadPatch (FLM p) where
readPatch' = mapSeal FLM <$> readMaybeBracketedFL readPatch' '{' '}'
instance ShowPatchBasic p => ShowPatchBasic (FLM p) where
showPatch f = showMaybeBracketedFL (showPatch f) '{' '}' . unFLM
readMaybeBracketedFL :: (forall wY . Parser (Sealed (p wY))) -> Char -> Char
-> Parser (Sealed (FL p wX))
readMaybeBracketedFL parser pre post =
bracketedFL parser pre post <|> (mapSeal (:>:NilFL) <$> parser)
showMaybeBracketedFL :: (forall wX wY . p wX wY -> Doc) -> Char -> Char
-> FL p wA wB -> Doc
showMaybeBracketedFL _ pre post NilFL = text [pre] $$ text [post]
showMaybeBracketedFL printer _ _ (p :>: NilFL) = printer p
showMaybeBracketedFL printer pre post ps = text [pre] $$
vcat (mapFL printer ps) $$
text [post]
writeTentativePending :: RepoPatch p => Repository rt p wR wU wT
-> FL (PrimOf p) wT wY -> IO ()
writeTentativePending = writePendingFile tentativeSuffix
writeNewPending :: RepoPatch p => Repository rt p wR wU wT
-> FL (PrimOf p) wT wP -> IO ()
writeNewPending = writePendingFile newSuffix
writePendingFile :: ShowPatchBasic prim => String -> Repository rt p wR wU wT
-> FL prim wX wY -> IO ()
writePendingFile suffix _ = writePatch name . FLM
where
name = pendingPath ++ suffix
writePatch :: ShowPatchBasic p => FilePath -> p wX wY -> IO ()
writePatch f p = writeDocBinFile f $ showPatch ForStorage p <> text "\n"
tentativelyRemoveFromPending :: forall rt p wR wU wT wO. RepoPatch p
=> Repository rt p wR wU wT
-> FL (PrimOf p) wO wT
-> IO ()
tentativelyRemoveFromPending r ps = do
Sealed pend <- readTentativePending (unsafeCoerceT r :: Repository rt p wR wU wO)
Sealed newpend <-
return $ updatePending (progressFL "Removing from pending:" ps) pend NilFL
writeTentativePending r newpend
tentativelyRemoveFromPW :: forall rt p wR wO wT wP wU. RepoPatch p
=> Repository rt p wR wU wT
-> FL (PrimOf p) wO wT
-> FL (PrimOf p) wO wP
-> FL (PrimOf p) wP wU
-> IO ()
tentativelyRemoveFromPW r changes pending working = do
Sealed pending' <- return $
updatePending (progressFL "Removing from pending:" changes) pending working
writeTentativePending r pending'
updatePending :: (PrimPatch p)
=> FL p wA wB -> FL p wA wC -> FL p wC wD -> Sealed (FL p wB)
updatePending NilFL ys zs = removeRLFL (reverseFL ys) zs
updatePending _ NilFL _ = Sealed NilFL
updatePending xs ys NilFL = Sealed (invert xs +>+ ys)
updatePending (x:>:xs) ys zs | Just ys' <- removeFL x ys = updatePending xs ys' zs
updatePending (x:>:xs) ys zs
| ys' :> ix' :> deps <- commuteWhatWeCanFL (invert x :> ys)
, Just zs' <- removeFromWorking (invert (ix':>:deps)) zs = updatePending xs ys' zs'
where
removeFromWorking as bs = removeAllFL as bs <|> decoalesceAllFL bs as
updatePending (x:>:xs) ys zs =
case decoalesceFL ys x of
Just ys' -> updatePending xs ys' zs
Nothing ->
error $ renderString
$ text "cannot eliminate repo change:"
$$ displayPatch x
$$ text "from pending:"
$$ vcat (mapFL displayPatch ys)
$$ text "or working:"
$$ vcat (mapFL displayPatch zs)
removeRLFL :: (Commute p, Invert p, Eq2 p)
=> RL p wA wB -> FL p wB wC -> Sealed (FL p wA)
removeRLFL (ys:<:y) zs
| Just zs' <- removeFL (invert y) zs = removeRLFL ys zs'
| otherwise = case commuteWhatWeCanRL (ys :> y) of
deps :> y' :> ys' -> mapSeal ((deps:<:y') +>>+) $ removeRLFL ys' zs
removeRLFL NilRL _ = Sealed NilFL
removeAllFL :: (Commute p, Invert p, Eq2 p)
=> FL p wA wB -> FL p wA wC -> Maybe (FL p wB wC)
removeAllFL (y:>:ys) zs
| Just zs' <- removeFL y zs = removeAllFL ys zs'
| otherwise = Nothing
removeAllFL NilFL zs = Just zs
decoalesceAllFL :: (Commute p, Invert p, PrimCanonize p)
=> FL p wA wC -> FL p wA wB -> Maybe (FL p wB wC)
decoalesceAllFL zs (y:>:ys)
| Just zs' <- decoalesceFL zs y = decoalesceAllFL zs' ys
| otherwise = Nothing
decoalesceAllFL zs NilFL = Just zs
decoalesceFL :: (Commute p, Invert p, PrimCanonize p)
=> FL p wA wC -> p wA wB -> Maybe (FL p wB wC)
decoalesceFL NilFL y = Just (invert y :>: NilFL)
decoalesceFL (z :>: zs) y
| Just z' <- primDecoalesce z y = Just (z' :>: zs)
| otherwise = do
z' :> iy' <- commute (invert y :> z)
zs' <- decoalesceFL zs (invert iy')
return (z' :>: zs')
makeNewPending :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdatePending
-> FL (PrimOf p) wT wP
-> Tree IO
-> IO ()
makeNewPending _ NoUpdatePending _ _ = return ()
makeNewPending repo YesUpdatePending origp recordedState =
withRepoLocation repo $
do let newname = pendingPath ++ ".new"
debugMessage $ "Writing new pending: " ++ newname
Sealed sfp <- return $ siftForPending origp
writeNewPending repo sfp
Sealed p <- readNewPending repo
_ <- catch (applyToTree p recordedState) $ \(err :: IOException) -> do
let buggyname = pendingPath ++ "_buggy"
renameFile newname buggyname
error $ renderString
$ text ("There was an attempt to write an invalid pending! " ++ show err)
$$ text "If possible, please send the contents of" <+> text buggyname
$$ text "along with a bug report."
renameFile newname pendingPath
debugMessage $ "Finished writing new pending: " ++ newname
finalizePending :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdatePending
-> Tree IO
-> IO ()
finalizePending repo NoUpdatePending _ =
withRepoLocation repo $ removeFileMayNotExist pendingPath
finalizePending repo upe@YesUpdatePending recordedState =
withRepoLocation repo $ do
Sealed tpend <- readTentativePending repo
Sealed new_pending <- return $ siftForPending tpend
makeNewPending repo upe new_pending recordedState
revertPending :: RepoPatch p
=> Repository rt p wR wU wT
-> UpdatePending
-> IO ()
revertPending r upe = do
removeFileMayNotExist (pendingPath ++ ".tentative")
Sealed x <- readPending r
if upe == YesUpdatePending
then writeTentativePending (unsafeCoerceT r) x
else removeFileMayNotExist pendingPath
tentativelyAddToPending :: forall rt p wR wU wT wX wY. RepoPatch p
=> Repository rt p wR wU wT
-> FL (PrimOf p) wX wY
-> IO ()
tentativelyAddToPending repo patch =
withRepoLocation repo $ do
Sealed pend <- readTentativePending repo
writeTentativePending repo (pend +>+ unsafeCoercePStart patch)
setTentativePending :: forall rt p wR wU wT wP. RepoPatch p
=> Repository rt p wR wU wT
-> FL (PrimOf p) wT wP
-> IO ()
setTentativePending repo patch = do
Sealed prims <- return $ siftForPending patch
withRepoLocation repo $ writeTentativePending repo prims