module Darcs.Repository.Job
( RepoJob(..)
, withRepoLock
, withRepoLockCanFail
, withRepository
, withRepositoryDirectory
) where
import Prelude ()
import Darcs.Prelude
import Darcs.Util.Global ( darcsdir )
import Darcs.Patch.Apply ( ApplyState )
import Darcs.Patch.V1 ( RepoPatchV1 )
import Darcs.Patch.V2 ( RepoPatchV2 )
import Darcs.Patch.Prim.V1 ( Prim )
import Darcs.Patch.Prim ( PrimOf )
import Darcs.Patch.RepoPatch ( RepoPatch )
import Darcs.Patch.RepoType
( RepoType(..), SRepoType(..), IsRepoType
, RebaseType(..), SRebaseType(..), IsRebaseType
)
import Darcs.Repository.Flags
( UseCache(..), UpdateWorking(..), DryRun(..), UMask (..)
)
import Darcs.Repository.Format
( RepoProperty( Darcs2
, RebaseInProgress
)
, formatHas
, writeProblem
)
import Darcs.Repository.Internal
( identifyRepository
, revertRepositoryChanges
)
import Darcs.Repository.InternalTypes ( Repository(..) )
import Darcs.Repository.Rebase
( RebaseJobFlags
, startRebaseJob
, rebaseJob
)
import qualified Darcs.Repository.Rebase as Rebase ( maybeDisplaySuspendedStatus )
import Darcs.Util.Lock ( withLock, withLockCanFail )
import Darcs.Util.Progress ( debugMessage )
import Control.Monad ( when )
import Control.Exception ( bracket_, finally )
import Data.List ( intercalate )
import Foreign.C.String ( CString, withCString )
import Foreign.C.Error ( throwErrno )
import Foreign.C.Types ( CInt(..) )
import Darcs.Util.Tree ( Tree )
#include "impossible.h"
getUMask :: UMask -> Maybe String
getUMask (YesUMask s) = Just s
getUMask NoUMask = Nothing
withUMaskFlag :: UMask -> IO a -> IO a
withUMaskFlag = maybe id withUMask . getUMask
foreign import ccall unsafe "umask.h set_umask" set_umask
:: CString -> IO CInt
foreign import ccall unsafe "umask.h reset_umask" reset_umask
:: CInt -> IO CInt
withUMask :: String
-> IO a
-> IO a
withUMask umask job =
do rc <- withCString umask set_umask
when (rc < 0) (throwErrno "Couldn't set umask")
bracket_
(return ())
(reset_umask rc)
job
data RepoJob a
=
RepoJob (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)
=> Repository rt p wR wU wR -> IO a)
| V1Job (forall wR wU . Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a)
| V2Job (forall rt wR wU . Repository rt (RepoPatchV2 Prim) wR wU wR -> IO a)
| PrimV1Job (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, PrimOf p ~ Prim)
=> Repository rt p wR wU wR -> IO a)
| RebaseAwareJob RebaseJobFlags (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => Repository rt p wR wU wR -> IO a)
| RebaseJob RebaseJobFlags (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a)
| StartRebaseJob RebaseJobFlags (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a)
onRepoJob :: RepoJob a
-> (forall rt p wR wU . (RepoPatch p, ApplyState p ~ Tree) => (Repository rt p wR wU wR -> IO a) -> Repository rt p wR wU wR -> IO a)
-> RepoJob a
onRepoJob (RepoJob job) f = RepoJob (f job)
onRepoJob (V1Job job) f = V1Job (f job)
onRepoJob (V2Job job) f = V2Job (f job)
onRepoJob (PrimV1Job job) f = PrimV1Job (f job)
onRepoJob (RebaseAwareJob flags job) f = RebaseAwareJob flags (f job)
onRepoJob (RebaseJob flags job) f = RebaseJob flags (f job)
onRepoJob (StartRebaseJob flags job) f = StartRebaseJob flags (f job)
withRepository :: UseCache -> RepoJob a -> IO a
withRepository useCache = withRepositoryDirectory useCache "."
data RepoPatchType p where
RepoV1 :: RepoPatchType (RepoPatchV1 Prim)
RepoV2 :: RepoPatchType (RepoPatchV2 Prim)
data IsTree p where
IsTree :: (ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => IsTree p
checkTree :: RepoPatchType p -> IsTree p
checkTree RepoV1 = IsTree
checkTree RepoV2 = IsTree
data UsesPrimV1 p where
UsesPrimV1 :: (ApplyState p ~ Tree, PrimOf p ~ Prim) => UsesPrimV1 p
checkPrimV1 :: RepoPatchType p -> UsesPrimV1 p
checkPrimV1 RepoV1 = UsesPrimV1
checkPrimV1 RepoV2 = UsesPrimV1
withRepositoryDirectory :: UseCache -> String -> RepoJob a -> IO a
withRepositoryDirectory useCache url repojob = do
repo@(Repo _ rf _ _) <- identifyRepository useCache url
let
startRebase =
case repojob of
StartRebaseJob {} -> True
_ -> False
runJob1
:: IsRebaseType rebaseType
=> SRebaseType rebaseType -> Repository rtDummy pDummy wR wU wR -> RepoJob a -> IO a
runJob1 isRebase =
if formatHas Darcs2 rf
then runJob RepoV2 (SRepoType isRebase)
else runJob RepoV1 (SRepoType isRebase)
runJob2 :: Repository rtDummy pDummy wR wU wR -> RepoJob a -> IO a
runJob2 =
if startRebase || formatHas RebaseInProgress rf
then runJob1 SIsRebase
else runJob1 SNoRebase
runJob2 repo repojob
runJob
:: forall rt p rtDummy pDummy wR wU a
. (IsRepoType rt, RepoPatch p)
=> RepoPatchType p -> SRepoType rt -> Repository rtDummy pDummy wR wU wR -> RepoJob a -> IO a
runJob patchType (SRepoType isRebase) (Repo dir rf t c) repojob = do
let
therepo = Repo dir rf t c :: Repository rt p wR wU wR
patchTypeString :: String
patchTypeString =
case patchType of
RepoV2 -> "darcs-2"
RepoV1 -> "darcs-1"
repoAttributes :: [String]
repoAttributes =
case isRebase of
SIsRebase -> ["rebase"]
SNoRebase -> []
repoAttributesString :: String
repoAttributesString =
case repoAttributes of
[] -> ""
_ -> " " ++ intercalate "+" repoAttributes
debugMessage $ "Identified " ++ patchTypeString ++ repoAttributesString ++ " repo: " ++ dir
case repojob of
RepoJob job ->
case checkTree patchType of
IsTree ->
job therepo
`finally`
Rebase.maybeDisplaySuspendedStatus isRebase therepo
PrimV1Job job ->
case checkPrimV1 patchType of
UsesPrimV1 -> do
job therepo
`finally`
Rebase.maybeDisplaySuspendedStatus isRebase therepo
V2Job job ->
case (patchType, isRebase) of
(RepoV2, SNoRebase) -> job therepo
(RepoV1, _ ) ->
fail $ "This repository contains darcs v1 patches,"
++ " but the command requires darcs v2 patches."
(RepoV2, SIsRebase) ->
fail "This command is not supported while a rebase is in progress."
V1Job job ->
case (patchType, isRebase) of
(RepoV1, SNoRebase) -> job therepo
(RepoV2, _ ) ->
fail $ "This repository contains darcs v2 patches,"
++ " but the command requires darcs v1 patches."
(RepoV1, SIsRebase) ->
fail "This command is not supported while a rebase is in progress."
RebaseAwareJob flags job ->
case (checkTree patchType, isRebase) of
(IsTree, SNoRebase) -> job therepo
(IsTree, SIsRebase) -> rebaseJob job therepo flags
RebaseJob flags job ->
case (checkTree patchType, isRebase) of
(_ , SNoRebase) -> fail "No rebase in progress. Try 'darcs rebase suspend' first."
(IsTree, SIsRebase) -> rebaseJob job therepo flags
StartRebaseJob flags job ->
case (checkTree patchType, isRebase) of
(_ , SNoRebase) -> impossible
(IsTree, SIsRebase) -> startRebaseJob job therepo flags
withRepoLock :: DryRun -> UseCache -> UpdateWorking -> UMask -> RepoJob a -> IO a
withRepoLock dry useCache uw um repojob =
withRepository useCache $ onRepoJob repojob $ \job repository@(Repo _ rf _ _) ->
do maybe (return ()) fail $ writeProblem rf
let name = "./"++darcsdir++"/lock"
withUMaskFlag um $
if dry == YesDryRun
then job repository
else withLock name (revertRepositoryChanges repository uw >> job repository)
withRepoLockCanFail :: UseCache -> UpdateWorking -> UMask -> RepoJob () -> IO ()
withRepoLockCanFail useCache uw um repojob =
withRepository useCache $ onRepoJob repojob $ \job repository@(Repo _ rf _ _) ->
do maybe (return ()) fail $ writeProblem rf
let name = "./"++darcsdir++"/lock"
withUMaskFlag um $ do
eitherDone <- withLockCanFail name (revertRepositoryChanges repository uw >> job repository)
case eitherDone of
Left _ -> debugMessage "Lock could not be obtained, not doing the job."
Right _ -> return ()