module Darcs.UI.Commands.Unrevert ( unrevert, writeUnrevert ) where
import Darcs.Prelude
import System.Exit ( exitSuccess )
import Darcs.Util.Tree( Tree )
import Darcs.UI.Commands
( DarcsCommand(..)
, withStdOpts
, nodefaults
, amInHashedRepository
, putFinished
)
import Darcs.UI.Completion ( noArgs )
import Darcs.UI.Flags
( diffingOpts, verbosity, useCache, umask, compress, diffAlgorithm
, isInteractive, withContext )
import Darcs.Repository.Flags
( UseIndex(..), ScanKnown (..), Reorder(..), AllowConflicts(..), ExternalMerge(..)
, WantGuiPause(..), UpdatePending(..), DryRun(NoDryRun) )
import Darcs.UI.Flags ( DarcsFlag )
import Darcs.UI.Options ( (^), odesc, ocheck, defaultFlags, (?) )
import qualified Darcs.UI.Options.All as O
import Darcs.Repository ( SealedPatchSet, Repository, withRepoLock, RepoJob(..),
considerMergeToWorking,
tentativelyAddToPending, finalizeRepositoryChanges,
readRepo,
readRecorded,
applyToWorking, unrecordedChanges )
import Darcs.Repository.Paths ( unrevertPath )
import Darcs.Patch ( IsRepoType, RepoPatch, PrimOf, commute )
import Darcs.Patch.Apply( ApplyState )
import Darcs.Patch.Info ( patchinfo )
import Darcs.Patch.Named ( infopatch )
import Darcs.Patch.Set ( PatchSet, Origin )
import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) )
import Darcs.Patch.Witnesses.Ordered ( Fork(..), FL(..), (:>)(..), (+>+) )
import Darcs.UI.SelectChanges
( WhichChanges(First)
, runInvertibleSelection
, selectionConfigPrim
)
import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) )
import qualified Data.ByteString as B
import Darcs.Util.Lock ( writeDocBinFile, removeFileMayNotExist )
import Darcs.Patch.Depends ( mergeThem )
import Darcs.Util.Exception ( catchall )
import Darcs.Util.Prompt ( askUser )
import Darcs.Patch.Bundle ( parseBundle, interpretBundle, makeBundle )
import Darcs.Util.IsoDate ( getIsoDateTime )
import Darcs.Util.SignalHandler ( withSignalsBlocked )
import Darcs.Util.Printer ( Doc, text )
import Darcs.Util.Progress ( debugMessage )
import Darcs.Util.Path ( AbsolutePath )
unrevertDescription :: String
unrevertDescription =
"Undo the last revert."
unrevertHelp :: Doc
unrevertHelp = text $
"Unrevert is a rescue command in case you accidentally reverted\n" ++
"something you wanted to keep (for example, typing `darcs rev -a`\n" ++
"instead of `darcs rec -a`).\n" ++
"\n" ++
"This command may fail if the repository has changed since the revert\n" ++
"took place. Darcs will ask for confirmation before executing an\n" ++
"interactive command that will DEFINITELY prevent unreversion.\n"
patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions
patchSelOpts flags = S.PatchSelectionOptions
{ S.verbosity = verbosity ? flags
, S.matchFlags = []
, S.interactive = isInteractive True flags
, S.selectDeps = O.PromptDeps
, S.withSummary = O.NoSummary
, S.withContext = withContext ? flags
}
unrevert :: DarcsCommand
unrevert = DarcsCommand
{ commandProgramName = "darcs"
, commandName = "unrevert"
, commandHelp = unrevertHelp
, commandDescription = unrevertDescription
, commandExtraArgs = 0
, commandExtraArgHelp = []
, commandCommand = unrevertCmd
, commandPrereq = amInHashedRepository
, commandCompleteArgs = noArgs
, commandArgdefaults = nodefaults
, commandAdvancedOptions = odesc unrevertAdvancedOpts
, commandBasicOptions = odesc unrevertBasicOpts
, commandDefaults = defaultFlags unrevertOpts
, commandCheckOptions = ocheck unrevertOpts
}
where
unrevertBasicOpts
= O.useIndex
^ O.interactive
^ O.repoDir
^ O.withContext
^ O.diffAlgorithm
unrevertAdvancedOpts = O.umask
unrevertOpts = unrevertBasicOpts `withStdOpts` unrevertAdvancedOpts
unrevertCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()
unrevertCmd _ opts [] =
withRepoLock NoDryRun (useCache ? opts) YesUpdatePending (umask ? opts) $ RepoJob $ \_repository -> do
us <- readRepo _repository
Sealed them <- unrevertPatchBundle us
recorded <- readRecorded _repository
unrecorded <- unrecordedChanges (diffingOpts opts )
O.NoLookForMoves O.NoLookForReplaces _repository Nothing
Sealed h_them <- return $ mergeThem us them
Sealed pw <- considerMergeToWorking _repository "unrevert"
YesAllowConflictsAndMark
NoExternalMerge NoWantGuiPause
(compress ? opts) (verbosity ? opts) NoReorder
( UseIndex, ScanKnown, diffAlgorithm ? opts )
(Fork us NilFL h_them)
let selection_config =
selectionConfigPrim
First "unrevert" (patchSelOpts opts)
Nothing Nothing (Just recorded)
(p :> skipped) <- runInvertibleSelection pw selection_config
tentativelyAddToPending _repository p
withSignalsBlocked $
do _repository <- finalizeRepositoryChanges _repository YesUpdatePending (compress ? opts)
_ <- applyToWorking _repository (verbosity ? opts) p
debugMessage "I'm about to writeUnrevert."
writeUnrevert _repository skipped recorded (unrecorded+>+p)
putFinished opts "unreverting"
unrevertCmd _ _ _ = error "impossible case"
writeUnrevert :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)
=> Repository rt p wR wU wT -> FL (PrimOf p) wX wY
-> Tree IO -> FL (PrimOf p) wR wX -> IO ()
writeUnrevert _ NilFL _ _ = removeFileMayNotExist unrevertPath
writeUnrevert repository ps recorded pend =
case commute (pend :> ps) of
Nothing -> do really <- askUser "You will not be able to unrevert this operation! Proceed? "
case really of ('y':_) -> return ()
_ -> exitSuccess
writeUnrevert repository NilFL recorded pend
Just (p' :> _) -> do
rep <- readRepo repository
date <- getIsoDateTime
info <- patchinfo date "unrevert" "anon" []
let np = infopatch info p'
bundle <- makeBundle (Just recorded) rep (np :>: NilFL)
writeDocBinFile unrevertPath bundle
unrevertPatchBundle :: RepoPatch p
=> PatchSet rt p Origin wR
-> IO (SealedPatchSet rt p Origin)
unrevertPatchBundle us = do
pf <- B.readFile unrevertPath
`catchall` fail "There's nothing to unrevert!"
case parseBundle pf of
Right (Sealed bundle) -> do
case interpretBundle us bundle of
Left msg -> fail msg
Right ps -> return (Sealed ps)
Left err -> fail $ "Couldn't parse unrevert patch:\n" ++ err