{-# LANGUAGE UndecidableInstances #-} module Darcs.Patch.Rebase.Suspended ( Suspended(..) , countToEdit, simplifyPush, simplifyPushes , addFixupsToSuspended, removeFixupsFromSuspended , addToEditsToSuspended ) where import Darcs.Prelude import Darcs.Patch.Effect ( Effect(..) ) import Darcs.Patch.Format ( PatchListFormat(..) ) import Darcs.Patch.Invert ( invert ) import Darcs.Patch.Named ( Named(..) ) import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.Info ( replaceJunk ) import Darcs.Patch.Inspect ( PatchInspect(..) ) import Darcs.Patch.Read ( ReadPatch(..) ) import Darcs.Patch.FromPrim ( PrimPatchBase(..), FromPrim(..), FromPrim(..) ) import Darcs.Patch.Read ( bracketedFL ) import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..), namedToFixups ) import Darcs.Patch.Rebase.Name ( RebaseName(..) ) import Darcs.Patch.RepoPatch ( RepoPatch ) import qualified Darcs.Patch.Rebase.Change as Change ( simplifyPush, simplifyPushes ) import Darcs.Patch.Rebase.Change ( RebaseChange(..), addNamedToRebase ) import qualified Darcs.Patch.Rebase.Legacy.Item as Item ( toRebaseChanges, RebaseItem ) import Darcs.Patch.Show ( ShowPatchBasic(..) ) import Darcs.Util.Parser ( lexString, lexWord ) import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed import Darcs.Patch.Witnesses.Show ( Show1, Show2 ) import Darcs.Util.Printer ( vcat, text, blueText, ($$), (<+>) ) import qualified Darcs.Util.Diff as D ( DiffAlgorithm(MyersDiff) ) import Control.Applicative ( (<|>) ) import qualified Data.ByteString.Char8 as BC ( pack ) -- | A single @Suspended@ patch contains the entire rebase state, in the form -- of 'RebaseItem's. -- -- The witnesses are such that a @Suspended@ appears to have no effect. -- This behaviour is only kept so we can read old-style rebase patches, -- where the entire rebase state was kept in a single patch on disk. -- data Suspended p wX wY where Items :: FL (RebaseChange (PrimOf p)) wX wY -> Suspended p wX wX deriving instance (Show2 p, Show2 (PrimOf p)) => Show (Suspended p wX wY) instance (Show2 p, Show2 (PrimOf p)) => Show1 (Suspended p wX) instance (Show2 p, Show2 (PrimOf p)) => Show2 (Suspended p) instance (PrimPatchBase p, PatchInspect p) => PatchInspect (Suspended p) where listTouchedFiles (Items ps) = listTouchedFiles ps hunkMatches f (Items ps) = hunkMatches f ps instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (Suspended p) where showPatch f (Items ps) = blueText "rebase" <+> text "0.2" <+> blueText "{" $$ vcat (mapFL (showPatch f) ps) $$ blueText "}" instance (PrimPatchBase p, PatchListFormat p, ReadPatch p, RepoPatch p) => ReadPatch (Suspended p) where readPatch' = do lexString (BC.pack "rebase") version <- lexWord case () of _ | version == BC.pack "0.2" -> (lexString (BC.pack "{}") >> return (seal (Items NilFL))) <|> (unseal (Sealed . Items) <$> bracketedFL readPatch' '{' '}') -- version 0.1 was a very temporary intermediate state on the way to 0.2 -- and we don't offer an upgrade path for it. | version == BC.pack "0.0" -> -- Note that if we have an "old-style" rebase, i.e. the first rebase implementation in -- darcs, characterised by the format string "rebase-in-progress", then only version -- 0.0 is possible here. On the other hand, the more recent implementation could use any -- version including 0.0. -- Unlike version 0.2, version 0.0 rebase patches on disk can contain conflicts. These are -- removed when reading by Item.toRebaseChanges, which ultimately calls 'fullUnwind', the -- same machinery that is used when version 0.2 patches are created from scratch. let itemsToSuspended :: Sealed (FL (Item.RebaseItem p) wX) -> Sealed (Suspended p wX) itemsToSuspended (Sealed ps) = case Item.toRebaseChanges ps of Sealed ps' -> Sealed (Items ps') in (lexString (BC.pack "{}") >> return (seal (Items NilFL))) <|> itemsToSuspended <$> bracketedFL readPatch' '{' '}' | otherwise -> error $ "can't handle rebase version " ++ show version countToEdit :: Suspended p wX wY -> Int countToEdit (Items ps) = lengthFL ps onSuspended :: (forall wZ . FL (RebaseChange (PrimOf p)) wY wZ -> Sealed (FL (RebaseChange (PrimOf p)) wX)) -> Suspended p wY wY -> Suspended p wX wX onSuspended f (Items ps) = unseal Items (f ps) -- |add fixups for the name and effect of a patch to a 'Suspended' addFixupsToSuspended :: (PrimPatchBase p, Commute p, FromPrim p, Effect p) => Named p wX wY -> Suspended p wY wY -> Suspended p wX wX addFixupsToSuspended p = simplifyPushes D.MyersDiff (namedToFixups p) -- |remove fixups (actually, add their inverse) for the name and effect of a patch to a 'Suspended' removeFixupsFromSuspended :: (PrimPatchBase p, Commute p, FromPrim p, Effect p) => Named p wX wY -> Suspended p wX wX -> Suspended p wY wY removeFixupsFromSuspended p = simplifyPushes D.MyersDiff (invert (namedToFixups p)) -- | Add 'Named' patches for editing to a 'Suspended'. The patches to be -- suspended are renamed by replacing the junk in their 'Patchinfo'. -- -- The reason we rename patches immediately when suspending them is that -- the user may pull an identical copy from a clone, Which means we have -- the same patch name twice, once in the normal repo and once suspended. -- Furthermore, they can again suspend that copy, leaving us with multiple -- copies of the same patch in the rebase state. This is bad because it -- invalidates most of the invariants for RebaseName fixups. See issue2445 -- and tests/rebase-repull.sh for examples which lead to crashes when we -- don't do the renaming here. addToEditsToSuspended :: RepoPatch p => D.DiffAlgorithm -> FL (Named p) wX wY -> Suspended p wY wY -> IO (Suspended p wX wX) addToEditsToSuspended _ NilFL items = return items addToEditsToSuspended da (NamedP old ds ps :>: qs) items = do items' <- addToEditsToSuspended da qs items new <- replaceJunk old case simplifyPush da (NameFixup (Rename new old)) items' of Items items'' -> case addNamedToRebase da (NamedP new ds ps) items'' of Sealed items''' -> return $ Items items''' simplifyPush :: (PrimPatchBase p, Commute p, FromPrim p, Effect p) => D.DiffAlgorithm -> RebaseFixup (PrimOf p) wX wY -> Suspended p wY wY -> Suspended p wX wX simplifyPush da fixups = onSuspended (Change.simplifyPush da fixups) simplifyPushes :: (PrimPatchBase p, Commute p, FromPrim p, Effect p) => D.DiffAlgorithm -> FL (RebaseFixup (PrimOf p)) wX wY -> Suspended p wY wY -> Suspended p wX wX simplifyPushes da fixups = onSuspended (Change.simplifyPushes da fixups)