module Darcs.Repository.Internal
( Repository(..)
, maybeIdentifyRepository
, identifyRepository
, identifyRepositoryFor
, IdentifyRepo(..)
, findRepository
, amInRepository
, amNotInRepository
, amInHashedRepository
, revertRepositoryChanges
, announceMergeConflicts
, setTentativePending
, checkUnrecordedConflicts
, readRepo
, readTentativeRepo
, readRepoUsingSpecificInventory
, prefsUrl
, withRecorded
, withTentative
, tentativelyAddPatch
, tentativelyRemovePatches
, tentativelyRemovePatches_
, tentativelyRemoveFromPending
, tentativelyAddToPending
, tentativelyAddPatch_
, tentativelyAddPatches_
, tentativelyReplacePatches
, finalizeRepositoryChanges
, unrevertUrl
, applyToWorking
, createPristineDirectoryTree
, createPartialsPristineDirectoryTree
, reorderInventory
, cleanRepository
, setScriptsExecutable
, setScriptsExecutablePatches
, UpdatePristine(..)
, MakeChanges(..)
, applyToTentativePristine
, makeNewPending
, seekRepo
, repoPatchType
, repoXor
) where
import Prelude ()
import Darcs.Prelude
import Darcs.Util.Printer ( putDocLn
, (<+>)
, text
, ($$)
, redText
, putDocLnWith
, ($$)
)
import Darcs.Util.Printer.Color (fancyPrinters)
import Darcs.Util.Crypt.SHA1 ( SHA1, sha1Xor, zero )
import Darcs.Repository.State ( readRecorded
, readWorking
, updateIndex
)
import Darcs.Repository.Pending
( readPending
, readTentativePending
, writeTentativePending
, readNewPending
, writeNewPending
, pendingName
)
import System.Exit ( exitSuccess )
import Darcs.Repository.ApplyPatches
( runTolerantly
, runSilently
, runDefault
)
import Darcs.Util.SignalHandler ( withSignalsBlocked )
import Darcs.Repository.Format ( RepoFormat
, RepoProperty( HashedInventory
, NoWorkingDir
)
, tryIdentifyRepoFormat
, formatHas
, readProblem
, transferProblem
)
import System.Directory ( doesDirectoryExist
, setCurrentDirectory
, createDirectoryIfMissing
, doesFileExist
)
import Control.Monad ( when
, unless
, filterM
, void
)
import Control.Exception ( catch, IOException )
import qualified Data.ByteString as B ( readFile
, isPrefixOf
)
import qualified Data.ByteString.Char8 as BC (pack)
import Data.List( foldl' )
import Data.List.Ordered ( nubSort )
import Data.Maybe ( fromMaybe )
import Darcs.Patch ( Effect
, primIsHunk
, primIsBinary
, description
, tryToShrink
, commuteFLorComplain
, commute
, fromPrim
, RepoPatch
, IsRepoType
, Patchy
, merge
, listConflictedFiles
, listTouchedFiles
, WrappedNamed
, commuteRL
, fromPrims
, readPatch
, effect
, invert
, primIsAddfile
, primIsAdddir
, primIsSetpref
, apply
, applyToTree
)
import Darcs.Patch.Dummy ( DummyPatch )
import Darcs.Patch.Apply ( ApplyState )
import Darcs.Patch.Inspect ( PatchInspect )
import Darcs.Patch.Prim ( PrimPatchBase
, PrimOf
, tryShrinkingInverse
, PrimPatch
)
import Darcs.Patch.Bundle ( scanBundle
, makeBundleN
)
import Darcs.Patch.Info ( isTag, makePatchname )
import Darcs.Patch.Named.Wrapped ( namedIsInternal )
import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd
, hopefully
, info
)
import Darcs.Patch.Type ( PatchType(..) )
import qualified Darcs.Repository.HashedRepo as HashedRepo
( revertTentativeChanges
, finalizeTentativeChanges
, removeFromTentativeInventory
, copyPristine
, copyPartialsPristine
, applyToTentativePristine
, addToTentativeInventory
, readRepo
, readTentativeRepo
, readRepoUsingSpecificInventory
, cleanPristine
, cleanInventories
, cleanPatches
)
import qualified Darcs.Repository.Old as Old
( revertTentativeChanges
, readOldRepo
, oldRepoFailMsg
)
import Darcs.Repository.Flags
( Compression, Verbosity(..), UseCache(..), UpdateWorking (..), AllowConflicts (..), ExternalMerge (..)
, WorkRepo (..), WithWorkingDir (WithWorkingDir) )
import Darcs.Patch.Witnesses.Eq ( EqCheck(..) )
import Darcs.Patch.Witnesses.Unsafe
( unsafeCoerceP, unsafeCoercePStart )
import Darcs.Patch.Witnesses.Ordered
( FL(..)
, RL(..)
, (:\/:)(..)
, (:/\:)(..)
, (:>)(..)
, (+>+)
, (+<+)
, lengthFL
, allFL
, filterOutFLFL
, reverseFL
, mapFL_FL
, concatFL
, reverseRL
, mapRL
)
import Darcs.Patch.Witnesses.Sealed
( Sealed(Sealed)
, seal
, FlippedSeal(FlippedSeal)
, flipSeal
, mapSeal
)
import Darcs.Patch.Permutations ( commuteWhatWeCanFL
, removeFL
)
import Darcs.Patch.Set ( PatchSet(..)
, SealedPatchSet
, newset2RL
, Origin
)
import Darcs.Patch.Depends ( removeFromPatchSet
, mergeThem
, splitOnTag
)
import Darcs.Patch.Show ( ShowPatch )
import Darcs.Util.Path
( FilePathLike
, AbsolutePath
, toFilePath
, ioAbsoluteOrRemote
, toPath
, anchorPath
)
import Darcs.Util.Exception ( catchall )
import Darcs.Util.File ( withCurrentDirectory )
import Darcs.Util.Prompt ( promptYorn )
import Darcs.Util.Progress ( debugMessage )
import Darcs.Patch.Progress (progressFL)
import Darcs.Util.URL ( isValidLocalPath )
import Darcs.Util.Workaround
( getCurrentDirectory
, renameFile
, setExecutable
)
import Darcs.Repository.Prefs ( getCaches )
import Darcs.Util.Lock
( writeDocBinFile
, removeFileMayNotExist
)
import Darcs.Repository.InternalTypes( Repository(..)
, Pristine(..)
)
import Darcs.Util.Global ( darcsdir )
import System.Mem( performGC )
import Darcs.Util.Tree ( Tree )
import qualified Darcs.Util.Tree as Tree
import Darcs.Repository.PatchIndex ( createOrUpdatePatchIndexDisk, doesPatchIndexExist )
#include "impossible.h"
data IdentifyRepo rt p wR wU wT
= BadRepository String
| NonRepository String
| GoodRepository (Repository rt p wR wU wT)
maybeIdentifyRepository :: UseCache -> String -> IO (IdentifyRepo rt p wR wU wT)
maybeIdentifyRepository useCache "." =
do darcs <- doesDirectoryExist darcsdir
if not darcs
then return (NonRepository $ "Missing " ++ darcsdir ++ " directory")
else do
repoFormatOrError <- tryIdentifyRepoFormat "."
here <- toPath `fmap` ioAbsoluteOrRemote "."
case repoFormatOrError of
Left err -> return $ NonRepository err
Right rf ->
case readProblem rf of
Just err -> return $ BadRepository err
Nothing -> do pris <- identifyPristine
cs <- getCaches useCache here
return $ GoodRepository $ Repo here rf pris cs
maybeIdentifyRepository useCache url' =
do url <- toPath `fmap` ioAbsoluteOrRemote url'
repoFormatOrError <- tryIdentifyRepoFormat url
case repoFormatOrError of
Left e -> return $ NonRepository e
Right rf -> case readProblem rf of
Just err -> return $ BadRepository err
Nothing -> do cs <- getCaches useCache url
return $ GoodRepository $ Repo url rf NoPristine cs
identifyPristine :: IO Pristine
identifyPristine =
do pristine <- doesDirectoryExist $ darcsdir++"/pristine"
current <- doesDirectoryExist $ darcsdir++"/current"
hashinv <- doesFileExist $ darcsdir++"/hashed_inventory"
case (pristine || current, hashinv) of
(False, False) -> return NoPristine
(True, False) -> return PlainPristine
(False, True ) -> return HashedPristine
_ -> fail "Multiple pristine trees."
identifyRepository :: forall rt p wR wU wT. UseCache -> String
-> IO (Repository rt p wR wU wT)
identifyRepository useCache url =
do er <- maybeIdentifyRepository useCache url
case er of
BadRepository s -> fail s
NonRepository s -> fail s
GoodRepository r -> return r
identifyRepositoryFor :: forall rt p wR wU wT vR vU vT. RepoPatch p
=> Repository rt p wR wU wT
-> UseCache
-> String
-> IO (Repository rt p vR vU vT)
identifyRepositoryFor (Repo _ source _ _) useCache url =
do Repo absurl target x c <- identifyRepository useCache url
case transferProblem target source of
Just e -> fail $ "Incompatibility with repository " ++ url ++ ":\n" ++ e
Nothing -> return $ Repo absurl target x c
amInRepository :: WorkRepo -> IO (Either String ())
amInRepository (WorkRepoDir d) = do
setCurrentDirectory d `catchall` fail ("can't set directory to "++d)
status <- maybeIdentifyRepository YesUseCache "."
case status of
GoodRepository _ -> return (Right ())
BadRepository e -> return (Left $ "While " ++ d ++ " looks like a repository directory, we have a problem with it:\n" ++ e)
NonRepository _ -> return (Left "You need to be in a repository directory to run this command.")
amInRepository _ =
fromMaybe (Left "You need to be in a repository directory to run this command.") <$> seekRepo
amInHashedRepository :: WorkRepo -> IO (Either String ())
amInHashedRepository wd
= do inrepo <- amInRepository wd
case inrepo of
Right _ -> do pristine <- identifyPristine
case pristine of
HashedPristine -> return (Right ())
_ -> return (Left Old.oldRepoFailMsg)
left -> return left
repoPatchType :: Repository rt p wR wU wT -> PatchType rt p
repoPatchType _ = PatchType
seekRepo :: IO (Maybe (Either String ()))
seekRepo = getCurrentDirectory >>= helper where
helper startpwd = do
status <- maybeIdentifyRepository YesUseCache "."
case status of
GoodRepository _ -> return . Just $ Right ()
BadRepository e -> return . Just $ Left e
NonRepository _ ->
do cd <- toFilePath `fmap` getCurrentDirectory
setCurrentDirectory ".."
cd' <- toFilePath `fmap` getCurrentDirectory
if cd' /= cd
then helper startpwd
else do setCurrentDirectory startpwd
return Nothing
amNotInRepository :: WorkRepo -> IO (Either String ())
amNotInRepository (WorkRepoDir d) = do
createDirectoryIfMissing False d
`catchall` (performGC >> createDirectoryIfMissing False d)
setCurrentDirectory d
amNotInRepository WorkRepoCurrentDir
amNotInRepository _ = do
status <- maybeIdentifyRepository YesUseCache "."
case status of
GoodRepository _ -> return (Left "You may not run this command in a repository.")
BadRepository e -> return (Left $ "You may not run this command in a repository.\nBy the way, we have a problem with it:\n" ++ e)
NonRepository _ -> return (Right ())
findRepository :: WorkRepo -> IO (Either String ())
findRepository (WorkRepoPossibleURL d) | isValidLocalPath d =
do setCurrentDirectory d `catchall` fail ("can't set directory to "++d)
findRepository WorkRepoCurrentDir
findRepository (WorkRepoDir d) =
do setCurrentDirectory d `catchall` fail ("can't set directory to "++d)
findRepository WorkRepoCurrentDir
findRepository _ = fromMaybe (Right ()) <$> seekRepo
readNewPendingLL :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT -> IO (Sealed ((FL p) wT))
readNewPendingLL repo = mapSeal (mapFL_FL fromPrim) `fmap` readNewPending repo
makeNewPending :: forall rt p wR wU wT wY. (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdateWorking
-> FL (PrimOf p) wT wY
-> IO ()
makeNewPending _ NoUpdateWorking _ = return ()
makeNewPending repo@(Repo r _ _ _) YesUpdateWorking origp =
withCurrentDirectory r $
do let newname = pendingName ++ ".new"
debugMessage $ "Writing new pending: " ++ newname
Sealed sfp <- return $ siftForPending origp
writeNewPending repo sfp
cur <- readRecorded repo
Sealed p <- readNewPendingLL repo
_ <- catch (applyToTree p cur) $ \(err :: IOException) -> do
let buggyname = pendingName ++ "_buggy"
renameFile newname buggyname
bugDoc $ 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
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
readTentativeRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> IO (PatchSet rt p Origin wT)
readTentativeRepo repo@(Repo r rf _ _)
| formatHas HashedInventory rf = HashedRepo.readTentativeRepo repo r
| otherwise = fail Old.oldRepoFailMsg
readRepoUsingSpecificInventory :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> String
-> Repository rt p wR wU wT
-> IO (PatchSet rt p Origin wT)
readRepoUsingSpecificInventory invPath repo@(Repo r rf _ _)
| formatHas HashedInventory rf =
HashedRepo.readRepoUsingSpecificInventory invPath repo r
| otherwise = fail Old.oldRepoFailMsg
prefsUrl :: Repository rt p wR wU wT -> String
prefsUrl (Repo r _ _ _) = r ++ "/"++darcsdir++"/prefs"
unrevertUrl :: Repository rt p wR wU wT -> String
unrevertUrl (Repo r _ _ _) = r ++ "/"++darcsdir++"/patches/unrevert"
applyToWorking :: (ApplyState (PrimOf p) ~ Tree, RepoPatch p)
=> Repository rt p wR wU wT -> Verbosity -> FL (PrimOf p) wU wY
-> IO (Repository rt p wR wY wT)
applyToWorking (Repo r rf t c) verb patch =
do
unless (formatHas NoWorkingDir rf) $
withCurrentDirectory r $ if verb == Quiet
then runSilently $ apply patch
else runTolerantly $ apply patch
return (Repo r rf t c)
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
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
data HashedVsOld a = HvsO { old, hashed :: a }
decideHashedOrNormal :: Monad m => RepoFormat -> HashedVsOld (m a) -> m a
decideHashedOrNormal rf (HvsO { hashed = h, old = o })
| formatHas HashedInventory rf = h
| otherwise = o
data MakeChanges = MakeChanges | DontMakeChanges deriving ( Eq )
announceMergeConflicts :: (PrimPatch p, PatchInspect p)
=> String
-> AllowConflicts
-> ExternalMerge
-> FL p wX wY
-> IO Bool
announceMergeConflicts cmd allowConflicts externalMerge resolved_pw =
case nubSort $ listTouchedFiles resolved_pw of
[] -> return False
cfs -> if allowConflicts `elem` [YesAllowConflicts,YesAllowConflictsAndMark]
|| externalMerge /= NoExternalMerge
then do putDocLnWith fancyPrinters $
redText "We have conflicts in the following files:" $$ text (unlines cfs)
return True
else do putDocLnWith fancyPrinters $
redText "There are conflicts in the following files:" $$ text (unlines cfs)
fail $ "Refusing to "++cmd++" patches leading to conflicts.\n"++
"If you would rather apply the patch and mark the conflicts,\n"++
"use the --mark-conflicts or --allow-conflicts options to "++cmd++"\n"++
"These can set as defaults by adding\n"++
" "++cmd++" mark-conflicts\n"++
"to "++darcsdir++"/prefs/defaults in the target repo. "
checkUnrecordedConflicts :: forall rt p wT wY. RepoPatch p
=> UpdateWorking
-> FL (WrappedNamed rt p) wT wY
-> IO Bool
checkUnrecordedConflicts NoUpdateWorking _
= return False
checkUnrecordedConflicts _ pc =
do repository <- identifyRepository NoUseCache "."
cuc repository
where cuc :: Repository rt p wR wU wT -> IO Bool
cuc r = do Sealed (mpend :: FL (PrimOf p) wT wX) <- readPending r :: IO (Sealed (FL (PrimOf p) wT))
case mpend of
NilFL -> return False
pend ->
case merge (fromPrims_ pend :\/: fromPrims_ (concatFL $ mapFL_FL effect pc)) of
_ :/\: pend' ->
case listConflictedFiles pend' of
[] -> return False
fs -> do putStrLn ("You have conflicting local changes to:\n"
++ unwords fs)
confirmed <- promptYorn "Proceed?"
unless confirmed $
do putStrLn "Cancelled."
exitSuccess
return True
fromPrims_ :: FL (PrimOf p) wA wB -> FL p wA wB
fromPrims_ = fromPrims
tentativelyAddPatch :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> Compression
-> Verbosity
-> UpdateWorking
-> PatchInfoAnd rt p wT wY
-> IO (Repository rt p wR wU wY)
tentativelyAddPatch = tentativelyAddPatch_ UpdatePristine
data UpdatePristine = UpdatePristine
| DontUpdatePristine
| DontUpdatePristineNorRevert deriving Eq
tentativelyAddPatches_
:: forall rt p wR wU wT wY
. (RepoPatch p, ApplyState p ~ Tree)
=> UpdatePristine
-> Repository rt p wR wU wT
-> Compression
-> Verbosity
-> UpdateWorking
-> FL (PatchInfoAnd rt p) wT wY
-> IO (Repository rt p wR wU wY)
tentativelyAddPatches_ _up r _compr _verb _uw NilFL = return r
tentativelyAddPatches_ up r compr verb uw (p:>:ps) = do
r' <- tentativelyAddPatch_ up r compr verb uw p
tentativelyAddPatches_ up r' compr verb uw ps
tentativelyAddPatch_ :: forall rt p wR wU wT wY
. (RepoPatch p, ApplyState p ~ Tree)
=> UpdatePristine
-> Repository rt p wR wU wT
-> Compression
-> Verbosity
-> UpdateWorking
-> PatchInfoAnd rt p wT wY
-> IO (Repository rt p wR wU wY)
tentativelyAddPatch_ up r@(Repo dir rf t c) compr verb uw p =
withCurrentDirectory dir $ do
decideHashedOrNormal rf HvsO {
hashed = void $ HashedRepo.addToTentativeInventory c compr p,
old = fail Old.oldRepoFailMsg}
when (up == UpdatePristine) $ do debugMessage "Applying to pristine cache..."
applyToTentativePristine r verb p
debugMessage "Updating pending..."
tentativelyRemoveFromPending r uw p
return (Repo dir rf t c)
applyToTentativePristine :: (ApplyState q ~ Tree, Effect q, Patchy q, ShowPatch q, PrimPatchBase q)
=> Repository rt p wR wU wT
-> Verbosity
-> q wT wY
-> IO ()
applyToTentativePristine (Repo dir rf _ _) verb p =
withCurrentDirectory dir $
do when (verb == Verbose) $ putDocLn $ text "Applying to pristine..." <+> description p
decideHashedOrNormal rf HvsO {hashed = HashedRepo.applyToTentativePristine p,
old = fail Old.oldRepoFailMsg}
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@(Repo dir _ _ _) YesUpdateWorking patch =
withCurrentDirectory dir $ 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@(Repo dir _ _ _) YesUpdateWorking patch = do
Sealed prims <- return $ siftForPending patch
withCurrentDirectory dir $ 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
tentativelyRemovePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> Compression
-> UpdateWorking
-> FL (PatchInfoAnd rt p) wX wT
-> IO (Repository rt p wR wU wX)
tentativelyRemovePatches = tentativelyRemovePatches_ UpdatePristine
tentativelyRemovePatches_ :: forall rt p wR wU wT wX
. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> UpdatePristine
-> Repository rt p wR wU wT
-> Compression
-> UpdateWorking
-> FL (PatchInfoAnd rt p) wX wT
-> IO (Repository rt p wR wU wX)
tentativelyRemovePatches_ up repository@(Repo dir rf t c) compr uw ps =
withCurrentDirectory dir $ do
when (up == UpdatePristine) $ do debugMessage "Adding changes to pending..."
prepend repository uw $ effect ps
unless (up == DontUpdatePristineNorRevert) $ removeFromUnrevertContext repository ps
debugMessage "Removing changes from tentative inventory..."
if formatHas HashedInventory rf
then do HashedRepo.removeFromTentativeInventory repository compr ps
when (up == UpdatePristine) $
HashedRepo.applyToTentativePristine $
progressFL "Applying inverse to pristine" $ invert ps
else fail Old.oldRepoFailMsg
return (Repo dir rf t c)
tentativelyReplacePatches :: forall rt p wR wU wT wX
. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> Compression
-> UpdateWorking
-> Verbosity
-> FL (PatchInfoAnd rt p) wX wT
-> IO ()
tentativelyReplacePatches repository compr uw verb ps =
do let ps' = filterOutFLFL (namedIsInternal . hopefully) ps
repository' <- tentativelyRemovePatches_ DontUpdatePristineNorRevert repository compr uw ps'
mapAdd repository' ps'
where mapAdd :: Repository rt p wM wL wI
-> FL (PatchInfoAnd rt p) wI wJ
-> IO ()
mapAdd _ NilFL = return ()
mapAdd r (a:>:as) =
do r' <- tentativelyAddPatch_ DontUpdatePristine r compr verb uw a
mapAdd r' as
finalizePending :: (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdateWorking
-> IO ()
finalizePending (Repo dir _ _ _) NoUpdateWorking =
withCurrentDirectory dir $ removeFileMayNotExist pendingName
finalizePending repository@(Repo dir _ _ _) updateWorking@YesUpdateWorking =
withCurrentDirectory dir $ do
Sealed tpend <- readTentativePending repository
Sealed new_pending <- return $ siftForPending tpend
makeNewPending repository updateWorking new_pending
finalizeRepositoryChanges :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> UpdateWorking
-> Compression
-> IO ()
finalizeRepositoryChanges repository@(Repo dir rf _ _) updateWorking compr
| formatHas HashedInventory rf =
withCurrentDirectory dir $ do
debugMessage "Finalizing changes..."
withSignalsBlocked $ do
HashedRepo.finalizeTentativeChanges repository compr
finalizePending repository updateWorking
debugMessage "Done finalizing changes..."
doesPatchIndexExist dir >>= (`when` createOrUpdatePatchIndexDisk repository)
updateIndex repository
| otherwise = fail Old.oldRepoFailMsg
revertRepositoryChanges :: RepoPatch p
=> Repository rt p wR wU wT
-> UpdateWorking
-> IO ()
revertRepositoryChanges r@(Repo dir rf _ _) uw =
withCurrentDirectory dir $
do removeFileMayNotExist (pendingName ++ ".tentative")
Sealed x <- readPending r
setTentativePending r uw x
when (uw == NoUpdateWorking) $ removeFileMayNotExist pendingName
decideHashedOrNormal rf HvsO { hashed = HashedRepo.revertTentativeChanges,
old = Old.revertTentativeChanges }
removeFromUnrevertContext :: forall rt p wR wU wT wX
. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> FL (PatchInfoAnd rt p) wX wT
-> IO ()
removeFromUnrevertContext repository ps = do
Sealed bundle <- unrevert_patch_bundle `catchall` return (seal (PatchSet NilRL NilRL))
remove_from_unrevert_context_ bundle
where unrevert_impossible =
do confirmed <- promptYorn "This operation will make unrevert impossible!\nProceed?"
if confirmed then removeFileMayNotExist (unrevertUrl repository)
else fail "Cancelled."
unrevert_patch_bundle :: IO (SealedPatchSet rt p Origin)
unrevert_patch_bundle = do pf <- B.readFile (unrevertUrl repository)
case scanBundle pf of
Right foo -> return foo
Left err -> fail $ "Couldn't parse unrevert patch:\n" ++ err
remove_from_unrevert_context_ :: PatchSet rt p Origin wZ -> IO ()
remove_from_unrevert_context_ (PatchSet NilRL NilRL) = return ()
remove_from_unrevert_context_ bundle =
do debugMessage "Adjusting the context of the unrevert changes..."
debugMessage $ "Removing "++ show (lengthFL ps) ++
" patches in removeFromUnrevertContext!"
ref <- readTentativeRepo repository
let withSinglet :: Sealed (FL ppp wXxx)
-> (forall wYyy . ppp wXxx wYyy -> IO ()) -> IO ()
withSinglet (Sealed (x :>: NilFL)) j = j x
withSinglet _ _ = return ()
withSinglet (mergeThem ref bundle) $ \h_us ->
case commuteRL (reverseFL ps :> h_us) of
Nothing -> unrevert_impossible
Just (us' :> _) ->
case removeFromPatchSet ps ref of
Nothing -> unrevert_impossible
Just common ->
do debugMessage "Have now found the new context..."
bundle' <- makeBundleN Nothing common (hopefully us':>:NilFL)
writeDocBinFile (unrevertUrl repository) bundle'
debugMessage "Done adjusting the context of the unrevert changes!"
cleanRepository :: RepoPatch p => Repository rt p wR wU wT -> IO ()
cleanRepository repository@(Repo _ rf _ _) =
decideHashedOrNormal rf
HvsO { hashed = cleanHashedRepo repository,
old = fail Old.oldRepoFailMsg}
where
cleanHashedRepo r = do
HashedRepo.cleanPristine r
HashedRepo.cleanInventories r
HashedRepo.cleanPatches r
createPristineDirectoryTree :: RepoPatch p => Repository rt p wR wU wT -> FilePath -> WithWorkingDir -> IO ()
createPristineDirectoryTree (Repo r rf _ c) reldir wwd
| formatHas HashedInventory rf =
do createDirectoryIfMissing True reldir
withCurrentDirectory reldir $ HashedRepo.copyPristine c r (darcsdir++"/hashed_inventory") wwd
| otherwise = fail Old.oldRepoFailMsg
createPartialsPristineDirectoryTree :: (FilePathLike fp, RepoPatch p)
=> Repository rt p wR wU wT
-> [fp]
-> FilePath
-> IO ()
createPartialsPristineDirectoryTree (Repo r rf _ c) prefs dir
| formatHas HashedInventory rf =
do createDirectoryIfMissing True dir
withCurrentDirectory dir $
HashedRepo.copyPartialsPristine c r (darcsdir++"/hashed_inventory") prefs
| otherwise = fail Old.oldRepoFailMsg
withRecorded :: RepoPatch p
=> Repository rt p wR wU wT
-> ((AbsolutePath -> IO a) -> IO a)
-> (AbsolutePath -> IO a)
-> IO a
withRecorded repository mk_dir f
= mk_dir $ \d -> do createPristineDirectoryTree repository (toFilePath d) WithWorkingDir
f d
withTentative :: forall rt p a wR wU wT. (RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> ((AbsolutePath -> IO a) -> IO a)
-> (AbsolutePath -> IO a)
-> IO a
withTentative (Repo dir rf _ c) mk_dir f
| formatHas HashedInventory rf =
mk_dir $ \d -> do HashedRepo.copyPristine
c
dir
(darcsdir++"/tentative_pristine")
WithWorkingDir
f d
withTentative repository@(Repo dir _ _ _) mk_dir f =
withRecorded repository mk_dir $ \d ->
do Sealed ps <- read_patches (dir ++ "/"++darcsdir++"/tentative_pristine")
runDefault $ apply ps
f d
where read_patches :: FilePath -> IO (Sealed (FL p wX))
read_patches fil = do ps <- B.readFile fil
return $ fromMaybe (seal NilFL) $ readPatch ps
setScriptsExecutable_ :: PatchInspect p => Maybe (p wX wY) -> IO ()
setScriptsExecutable_ pw = do
debugMessage "Making scripts executable"
tree <- readWorking
paths <- case pw of
Just ps -> filterM doesFileExist $ listTouchedFiles ps
Nothing -> return [ anchorPath "." p | (p, Tree.File _) <- Tree.list tree ]
let setExecutableIfScript f =
do contents <- B.readFile f
when (BC.pack "#!" `B.isPrefixOf` contents) $ do
debugMessage ("Making executable: " ++ f)
setExecutable f True
mapM_ setExecutableIfScript paths
setScriptsExecutable :: IO ()
setScriptsExecutable = setScriptsExecutable_ (Nothing :: Maybe (FL DummyPatch wX wY))
setScriptsExecutablePatches :: PatchInspect p => p wX wY -> IO ()
setScriptsExecutablePatches = setScriptsExecutable_ . Just
reorderInventory :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wR
-> Compression
-> UpdateWorking
-> Verbosity
-> IO ()
reorderInventory repository@(Repo _ rf _ _) compr uw verb =
decideHashedOrNormal rf HvsO {
hashed = do
debugMessage "Reordering the inventory."
PatchSet _ ps <- misplacedPatches `fmap` readRepo repository
tentativelyReplacePatches repository compr uw verb $ reverseRL ps
HashedRepo.finalizeTentativeChanges repository compr
debugMessage "Done reordering the inventory.",
old = fail Old.oldRepoFailMsg }
misplacedPatches :: forall rt p wS wX . RepoPatch p
=> PatchSet rt p wS wX
-> PatchSet rt p wS wX
misplacedPatches ps =
case filter isTag $ mapRL info $ newset2RL ps of
[] -> ps
(lt:_) ->
case splitOnTag lt ps of
Just (PatchSet ts xs :> r) -> PatchSet ts (xs+<+r)
_ -> impossible
readRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT
-> IO (PatchSet rt p Origin wR)
readRepo repo@(Repo r rf _ _)
| formatHas HashedInventory rf = HashedRepo.readRepo repo r
| otherwise = do Sealed ps <- Old.readOldRepo r
return $ unsafeCoerceP ps
repoXor :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wR -> IO SHA1
repoXor repo = do
hashes <- mapRL (makePatchname . info) . newset2RL <$> readRepo repo
return $ foldl' sha1Xor zero hashes