darcs-2.16.1: src/Darcs/Patch/Rebase/Suspended.hs
{-# 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)