module Darcs.Repository.Pending
( readPending
, siftForPending
, tentativelyRemoveFromPending
, finalizePending
, makeNewPending
, tentativelyAddToPending
, setTentativePending
, prepend
, pendingName
) where
import Prelude ()
import Darcs.Prelude
import Control.Applicative
import qualified Data.ByteString as B ( empty )
import Control.Exception ( catch, IOException )
import Data.Maybe ( fromMaybe )
import Darcs.Util.Printer ( errorDoc )
import Darcs.Util.Global ( darcsdir )
import Darcs.Util.Lock
( writeDocBinFile
, removeFileMayNotExist
)
import Darcs.Repository.InternalTypes ( Repository, withRepoLocation )
import Darcs.Repository.Flags
( UpdateWorking (..))
import Darcs.Patch
( readPatch, RepoPatch, PrimOf, tryToShrink
, primIsHunk, primIsBinary, commute, invert
, primIsAddfile, primIsAdddir, commuteFLorComplain
, effect, primIsSetpref, applyToTree )
import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd )
import Darcs.Patch.Progress (progressFL)
import Darcs.Patch.Permutations ( commuteWhatWeCanFL
, removeFL
)
import Darcs.Patch.Prim ( tryShrinkingInverse
, PrimPatch
)
import Darcs.Patch.Read ( ReadPatch(..), bracketedFL )
import Darcs.Patch.ReadMonads ( ParserM )
import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatchFor(ForStorage) )
import Darcs.Patch.Apply ( ApplyState )
import Darcs.Util.Tree ( Tree )
import Darcs.Util.Exception ( catchall )
import Darcs.Util.Workaround ( renameFile )
import Darcs.Patch.Witnesses.Eq ( EqCheck(..) )
import Darcs.Patch.Witnesses.Sealed
( Sealed(Sealed), mapSeal, seal
, FlippedSeal(FlippedSeal)
, flipSeal
)
import Darcs.Patch.Witnesses.Unsafe
( unsafeCoerceP, unsafeCoercePStart )
import Darcs.Patch.Witnesses.Ordered
( FL(..), RL(..), (:>)(..), (+>+)
, lengthFL, allFL, filterOutFLFL
, reverseFL, mapFL )
import Darcs.Util.ByteString ( gzReadFilePS )
import Darcs.Util.Printer ( Doc, ($$), (<>), text, vcat, (<+>) )
import Darcs.Util.Progress ( debugMessage )
pendingName :: String
pendingName = darcsdir ++ "/patches/pending"
newSuffix, tentativeSuffix :: String
newSuffix = ".new"
tentativeSuffix = ".tentative"
readPending :: RepoPatch p => Repository rt p wR wU wT
-> IO (Sealed (FL (PrimOf p) wT))
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
pend <- gzReadFilePS (pendingName ++ suffix) `catchall` return B.empty
return . maybe (Sealed NilFL) (mapSeal unFLM) . readPatch $ pend
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 m p wX . ParserM m
=> (forall wY . m (Sealed (p wY))) -> Char -> Char
-> m (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 wY -> 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 = pendingName ++ suffix
writePatch :: ShowPatchBasic p => FilePath -> p wX wY -> IO ()
writePatch f p = writeDocBinFile f $ showPatch ForStorage p <> text "\n"
siftForPending :: forall prim wX wY . PrimPatch prim => FL prim wX wY -> Sealed (FL prim wX)
siftForPending simple_ps =
if allFL (\p -> primIsAddfile p || primIsAdddir p) oldps
then seal oldps
else fromJust $ do
Sealed x <- return $ sift NilFL $ reverseFL oldps
return $ case tryToShrink x of
ps | lengthFL ps < lengthFL oldps -> siftForPending ps
| otherwise -> seal ps
where
oldps = fromMaybe simple_ps $ tryShrinkingInverse $ crudeSift simple_ps
sift :: FL prim wA wB -> RL prim wC wA -> Sealed (FL prim wC)
sift sofar NilRL = seal sofar
sift sofar (ps:<:p) | primIsHunk p || primIsBinary p =
case commuteFLorComplain (p :> sofar) of
Right (sofar' :> _) -> sift sofar' ps
Left _ -> sift (p:>:sofar) ps
sift sofar (ps:<:p) = sift (p:>:sofar) ps
crudeSift :: forall prim wX wY . PrimPatch prim => FL prim wX wY -> FL prim wX wY
crudeSift xs =
if isSimple xs then filterOutFLFL ishunkbinary xs else xs
where
ishunkbinary :: prim wA wB -> EqCheck wA wB
ishunkbinary x | primIsHunk x || primIsBinary x = unsafeCoerceP IsEq
| otherwise = NotEq
tentativelyRemoveFromPending :: forall rt p wR wU wT wX wY. (RepoPatch p)
=> Repository rt p wR wU wT
-> UpdateWorking
-> PatchInfoAnd rt p wX wY
-> IO ()
tentativelyRemoveFromPending _ NoUpdateWorking _ = return ()
tentativelyRemoveFromPending repo YesUpdateWorking p = do
Sealed pend <- readTentativePending repo
let effectp = if isSimple pend
then crudeSift $ effect p
else effect p
Sealed newpend <- return $ rmpend (progressFL "Removing from pending:" effectp)
(unsafeCoercePStart pend)
writeTentativePending repo (unsafeCoercePStart newpend)
where
rmpend :: FL (PrimOf p) wA wB -> FL (PrimOf p) wA wC -> Sealed (FL (PrimOf p) wB)
rmpend NilFL x = Sealed x
rmpend _ NilFL = Sealed NilFL
rmpend (x:>:xs) xys | Just ys <- removeFL x xys = rmpend xs ys
rmpend (x:>:xs) ys =
case commuteWhatWeCanFL (x:>xs) of
a:>x':>b -> case rmpend a ys of
Sealed ys' -> case commute (invert (x':>:b) :> ys') of
Just (ys'' :> _) -> seal ys''
Nothing -> seal $ invert (x':>:b)+>+ys'
isSimple :: PrimPatch prim => FL prim wX wY -> Bool
isSimple =
allFL isSimp
where
isSimp x = primIsHunk x || primIsBinary x || primIsSetpref x
makeNewPending :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdateWorking
-> FL (PrimOf p) wT wY
-> Tree IO
-> IO ()
makeNewPending _ NoUpdateWorking _ _ = return ()
makeNewPending repo YesUpdateWorking origp recordedState =
withRepoLocation repo $
do let newname = pendingName ++ ".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 = pendingName ++ "_buggy"
renameFile newname buggyname
errorDoc $ 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 pendingName
debugMessage $ "Finished writing new pending: " ++ newname
finalizePending :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdateWorking
-> Tree IO
-> IO ()
finalizePending repo NoUpdateWorking _ =
withRepoLocation repo $ removeFileMayNotExist pendingName
finalizePending repo updateWorking@YesUpdateWorking recordedState =
withRepoLocation repo $ do
Sealed tpend <- readTentativePending repo
Sealed new_pending <- return $ siftForPending tpend
makeNewPending repo updateWorking new_pending recordedState
tentativelyAddToPending :: forall rt p wR wU wT wX wY. RepoPatch p
=> Repository rt p wR wU wT
-> UpdateWorking
-> FL (PrimOf p) wX wY
-> IO ()
tentativelyAddToPending _ NoUpdateWorking _ = return ()
tentativelyAddToPending repo YesUpdateWorking patch =
withRepoLocation repo $ do
Sealed pend <- readTentativePending repo
FlippedSeal newpend_ <- return $
newpend (unsafeCoerceP pend :: FL (PrimOf p) wA wX) patch
writeTentativePending repo (unsafeCoercePStart newpend_)
where
newpend :: FL prim wA wB -> FL prim wB wC -> FlippedSeal (FL prim) wC
newpend NilFL patch_ = flipSeal patch_
newpend p patch_ = flipSeal $ p +>+ patch_
setTentativePending :: forall rt p wR wU wT wX wY. RepoPatch p
=> Repository rt p wR wU wT
-> UpdateWorking
-> FL (PrimOf p) wX wY
-> IO ()
setTentativePending _ NoUpdateWorking _ = return ()
setTentativePending repo YesUpdateWorking patch = do
Sealed prims <- return $ siftForPending patch
withRepoLocation repo $ writeTentativePending repo (unsafeCoercePStart prims)
prepend :: forall rt p wR wU wT wX wY. RepoPatch p
=> Repository rt p wR wU wT
-> UpdateWorking
-> FL (PrimOf p) wX wY
-> IO ()
prepend _ NoUpdateWorking _ = return ()
prepend repo YesUpdateWorking patch = do
Sealed pend <- readTentativePending repo
Sealed newpend_ <- return $ newpend (unsafeCoerceP pend) patch
writeTentativePending repo (unsafeCoercePStart $ crudeSift newpend_)
where
newpend :: FL prim wB wC -> FL prim wA wB -> Sealed (FL prim wA)
newpend NilFL patch_ = seal patch_
newpend p patch_ = seal $ patch_ +>+ p