darcs 2.16.3 → 2.16.4
raw patch · 34 files changed
+748/−417 lines, 34 filesbinary-addedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Darcs.Patch.Annotate: instance GHC.Classes.Eq (Darcs.Patch.Annotate.Annotated2 [] ((,) GHC.Types.Int))
- Darcs.Patch.Annotate: instance GHC.Classes.Eq (Darcs.Patch.Annotate.Content2 [] ((,) GHC.Types.Int))
- Darcs.Patch.Annotate: instance GHC.Show.Show (Darcs.Patch.Annotate.Annotated2 [] ((,) GHC.Types.Int))
- Darcs.Patch.Annotate: instance GHC.Show.Show (Darcs.Patch.Annotate.Content2 [] ((,) GHC.Types.Int))
- Darcs.Patch.ApplyPatches: applyPatches :: (MonadProgress m, ApplyMonad (ApplyState p) m, Apply p) => FL (PatchInfoAnd rt p) wX wY -> m ()
- Darcs.Patch.Index.Types: PInvalid :: a -> PatchMod a
- Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Ident.PatchId p Data.Type.Equality.~ Darcs.Patch.Info.PatchInfo, Darcs.Patch.Commute.Commute p) => Darcs.Patch.Commute.Commute (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
- Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Ident.PatchId p Data.Type.Equality.~ Darcs.Patch.Info.PatchInfo, Darcs.Patch.Merge.CleanMerge p) => Darcs.Patch.Merge.CleanMerge (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
- Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Ident.PatchId p Data.Type.Equality.~ Darcs.Patch.Info.PatchInfo, Darcs.Patch.Merge.Merge p) => Darcs.Patch.Merge.Merge (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
- Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Read.ReadPatch p, Darcs.Patch.Ident.Ident p, Darcs.Patch.Ident.PatchId p Data.Type.Equality.~ Darcs.Patch.Info.PatchInfo) => Darcs.Patch.Read.ReadPatch (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
- Darcs.Patch.Prim.V1.Show: instance (Darcs.Patch.Apply.ApplyState Darcs.Patch.Prim.V1.Core.Prim Data.Type.Equality.~ Darcs.Util.Tree.Tree) => Darcs.Patch.Prim.Class.PrimShow Darcs.Patch.Prim.V1.Core.Prim
- Darcs.Patch.Witnesses.Sealed: data FreeLeft p
- Darcs.Patch.Witnesses.Sealed: data FreeRight p
- Darcs.Util.Index: readIndex :: FilePath -> (Tree IO -> Hash) -> IO Index
- Darcs.Util.Index: updateIndex :: Index -> IO (Tree IO)
- Darcs.Util.Tree.Monad: class Monad m => TreeRO m
- Darcs.Util.Tree.Monad: class TreeRO m => TreeRW m
- Darcs.Util.Tree.Monad: currentDirectory :: TreeRO m => m AnchoredPath
- Darcs.Util.Tree.Monad: initialState :: Tree m -> (TreeItem m -> m Hash) -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m)) -> TreeState m
- Darcs.Util.Tree.Monad: instance GHC.Base.Monad m => Darcs.Util.Tree.Monad.TreeRO (Darcs.Util.Tree.Monad.TreeMonad m)
- Darcs.Util.Tree.Monad: instance GHC.Base.Monad m => Darcs.Util.Tree.Monad.TreeRW (Darcs.Util.Tree.Monad.TreeMonad m)
- Darcs.Util.Tree.Monad: replaceItem :: Monad m => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m ()
- Darcs.Util.Tree.Monad: tree :: TreeState m -> Tree m
- Darcs.Util.Tree.Monad: withDirectory :: TreeRO m => AnchoredPath -> m a -> m a
+ Darcs.Patch.Annotate: instance GHC.Classes.Eq Darcs.Patch.Annotate.Annotated
+ Darcs.Patch.Annotate: instance GHC.Classes.Eq Darcs.Patch.Annotate.Content
+ Darcs.Patch.Annotate: instance GHC.Show.Show Darcs.Patch.Annotate.Annotated
+ Darcs.Patch.Annotate: instance GHC.Show.Show Darcs.Patch.Annotate.Content
+ Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Ident.PatchId p GHC.Types.~ Darcs.Patch.Info.PatchInfo, Darcs.Patch.Commute.Commute p) => Darcs.Patch.Commute.Commute (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
+ Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Ident.PatchId p GHC.Types.~ Darcs.Patch.Info.PatchInfo, Darcs.Patch.Merge.CleanMerge p) => Darcs.Patch.Merge.CleanMerge (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
+ Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Ident.PatchId p GHC.Types.~ Darcs.Patch.Info.PatchInfo, Darcs.Patch.Merge.Merge p) => Darcs.Patch.Merge.Merge (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
+ Darcs.Patch.PatchInfoAnd: instance (Darcs.Patch.Read.ReadPatch p, Darcs.Patch.Ident.Ident p, Darcs.Patch.Ident.PatchId p GHC.Types.~ Darcs.Patch.Info.PatchInfo) => Darcs.Patch.Read.ReadPatch (Darcs.Patch.PatchInfoAnd.PatchInfoAndG rt p)
+ Darcs.Patch.Prim.V1.Show: instance (Darcs.Patch.Apply.ApplyState Darcs.Patch.Prim.V1.Core.Prim GHC.Types.~ Darcs.Util.Tree.Tree) => Darcs.Patch.Prim.Class.PrimShow Darcs.Patch.Prim.V1.Core.Prim
+ Darcs.Patch.Witnesses.Sealed: FLInternal :: Poly (Compose Sealed p) -> FreeLeft p
+ Darcs.Patch.Witnesses.Sealed: FRInternal :: Poly (FlippedSeal p) -> FreeRight p
+ Darcs.Patch.Witnesses.Sealed: Poly :: (forall wX. a wX) -> Poly a
+ Darcs.Patch.Witnesses.Sealed: [unPoly] :: Poly a -> forall wX. a wX
+ Darcs.Patch.Witnesses.Sealed: newtype FreeLeft p
+ Darcs.Patch.Witnesses.Sealed: newtype FreeRight p
+ Darcs.Patch.Witnesses.Sealed: newtype Poly a
+ Darcs.Patch.Witnesses.Sealed: unsafeUnseal :: Sealed a -> a wX
+ Darcs.Patch.Witnesses.Sealed: unsafeUnseal2 :: Sealed2 a -> a wX wY
+ Darcs.Prelude: class Monad m => MonadFail (m :: Type -> Type)
+ Darcs.Util.Index: openIndex :: FilePath -> (Tree IO -> Hash) -> IO Index
+ Darcs.Util.Index: treeFromIndex :: Index -> IO (Tree IO)
- Darcs.Patch.Format: ListFormatDefault :: ListFormat
+ Darcs.Patch.Format: ListFormatDefault :: ListFormat (p :: * -> * -> *)
- Darcs.Patch.Format: ListFormatV1 :: ListFormat
+ Darcs.Patch.Format: ListFormatV1 :: ListFormat (p :: * -> * -> *)
- Darcs.Patch.Format: ListFormatV2 :: ListFormat
+ Darcs.Patch.Format: ListFormatV2 :: ListFormat (p :: * -> * -> *)
- Darcs.Patch.Format: ListFormatV3 :: ListFormat
+ Darcs.Patch.Format: ListFormatV3 :: ListFormat (p :: * -> * -> *)
- Darcs.Patch.Named.Wrapped: [RebaseP] :: (PrimPatchBase p, FromPrim p, Effect p) => !PatchInfo -> !Suspended p wX wX -> WrappedNamed ( 'RepoType 'IsRebase) p wX wX
+ Darcs.Patch.Named.Wrapped: [RebaseP] :: (PrimPatchBase p, FromPrim p, Effect p) => !PatchInfo -> !Suspended p wX wX -> WrappedNamed ('RepoType 'IsRebase) p wX wX
- Darcs.Patch.Prim.FileUUID.Apply: ObjectMap :: (UUID -> m (Maybe (Object m))) -> (UUID -> Object m -> m (ObjectMap m)) -> m [UUID] -> ObjectMap
+ Darcs.Patch.Prim.FileUUID.Apply: ObjectMap :: (UUID -> m (Maybe (Object m))) -> (UUID -> Object m -> m (ObjectMap m)) -> m [UUID] -> ObjectMap (m :: * -> *)
- Darcs.Patch.Prim.FileUUID.Apply: [getObject] :: ObjectMap -> UUID -> m (Maybe (Object m))
+ Darcs.Patch.Prim.FileUUID.Apply: [getObject] :: ObjectMap (m :: * -> *) -> UUID -> m (Maybe (Object m))
- Darcs.Patch.Prim.FileUUID.Apply: [listObjects] :: ObjectMap -> m [UUID]
+ Darcs.Patch.Prim.FileUUID.Apply: [listObjects] :: ObjectMap (m :: * -> *) -> m [UUID]
- Darcs.Patch.Prim.FileUUID.Apply: [putObject] :: ObjectMap -> UUID -> Object m -> m (ObjectMap m)
+ Darcs.Patch.Prim.FileUUID.Apply: [putObject] :: ObjectMap (m :: * -> *) -> UUID -> Object m -> m (ObjectMap m)
- Darcs.Patch.Prim.FileUUID.Core: Blob :: m FileContent -> !Hash -> Object
+ Darcs.Patch.Prim.FileUUID.Core: Blob :: m FileContent -> !Hash -> Object (m :: * -> *)
- Darcs.Patch.Prim.FileUUID.Core: Directory :: DirContent -> Object
+ Darcs.Patch.Prim.FileUUID.Core: Directory :: DirContent -> Object (m :: * -> *)
- Darcs.Patch.Prim.FileUUID.ObjectMap: Blob :: m FileContent -> !Hash -> Object
+ Darcs.Patch.Prim.FileUUID.ObjectMap: Blob :: m FileContent -> !Hash -> Object (m :: * -> *)
- Darcs.Patch.Prim.FileUUID.ObjectMap: Directory :: DirContent -> Object
+ Darcs.Patch.Prim.FileUUID.ObjectMap: Directory :: DirContent -> Object (m :: * -> *)
- Darcs.Patch.Prim.FileUUID.ObjectMap: ObjectMap :: (UUID -> m (Maybe (Object m))) -> (UUID -> Object m -> m (ObjectMap m)) -> m [UUID] -> ObjectMap
+ Darcs.Patch.Prim.FileUUID.ObjectMap: ObjectMap :: (UUID -> m (Maybe (Object m))) -> (UUID -> Object m -> m (ObjectMap m)) -> m [UUID] -> ObjectMap (m :: * -> *)
- Darcs.Patch.Prim.FileUUID.ObjectMap: [getObject] :: ObjectMap -> UUID -> m (Maybe (Object m))
+ Darcs.Patch.Prim.FileUUID.ObjectMap: [getObject] :: ObjectMap (m :: * -> *) -> UUID -> m (Maybe (Object m))
- Darcs.Patch.Prim.FileUUID.ObjectMap: [listObjects] :: ObjectMap -> m [UUID]
+ Darcs.Patch.Prim.FileUUID.ObjectMap: [listObjects] :: ObjectMap (m :: * -> *) -> m [UUID]
- Darcs.Patch.Prim.FileUUID.ObjectMap: [putObject] :: ObjectMap -> UUID -> Object m -> m (ObjectMap m)
+ Darcs.Patch.Prim.FileUUID.ObjectMap: [putObject] :: ObjectMap (m :: * -> *) -> UUID -> Object m -> m (ObjectMap m)
- Darcs.Patch.Rebase.Change: rcToPia :: RebaseChange prim wX wY -> Sealed2 (PatchInfoAnd ( 'RepoType 'NoRebase) prim)
+ Darcs.Patch.Rebase.Change: rcToPia :: RebaseChange prim wX wY -> Sealed2 (PatchInfoAnd ('RepoType 'NoRebase) prim)
- Darcs.Patch.Rebase.Change: toRebaseChanges :: FL (RebaseChange prim) wX wY -> FL (PatchInfoAndG ( 'RepoType 'IsRebase) (RebaseChange prim)) wX wY
+ Darcs.Patch.Rebase.Change: toRebaseChanges :: FL (RebaseChange prim) wX wY -> FL (PatchInfoAndG ('RepoType 'IsRebase) (RebaseChange prim)) wX wY
- Darcs.Patch.RepoType: [SIsRebase] :: SRebaseType 'IsRebase
+ Darcs.Patch.RepoType: [SIsRebase] :: SRebaseType 'IsRebase
- Darcs.Patch.RepoType: [SNoRebase] :: SRebaseType 'NoRebase
+ Darcs.Patch.RepoType: [SNoRebase] :: SRebaseType 'NoRebase
- Darcs.Patch.RepoType: [SRepoType] :: SRebaseType rebaseType -> SRepoType ( 'RepoType rebaseType)
+ Darcs.Patch.RepoType: [SRepoType] :: SRebaseType rebaseType -> SRepoType ('RepoType rebaseType)
- Darcs.Prelude: (!!) :: () => [a] -> Int -> a
+ Darcs.Prelude: (!!) :: [a] -> Int -> a
- Darcs.Prelude: ($!) :: () => (a -> b) -> a -> b
+ Darcs.Prelude: ($!) :: forall (r :: RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b
- Darcs.Prelude: ($) :: () => (a -> b) -> a -> b
+ Darcs.Prelude: ($) :: forall (r :: RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b
- Darcs.Prelude: (++) :: () => [a] -> [a] -> [a]
+ Darcs.Prelude: (++) :: [a] -> [a] -> [a]
- Darcs.Prelude: (.) :: () => (b -> c) -> (a -> b) -> a -> c
+ Darcs.Prelude: (.) :: (b -> c) -> (a -> b) -> a -> c
- Darcs.Prelude: asTypeOf :: () => a -> a -> a
+ Darcs.Prelude: asTypeOf :: a -> a -> a
- Darcs.Prelude: break :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Darcs.Prelude: break :: (a -> Bool) -> [a] -> ([a], [a])
- Darcs.Prelude: const :: () => a -> b -> a
+ Darcs.Prelude: const :: a -> b -> a
- Darcs.Prelude: curry :: () => ((a, b) -> c) -> a -> b -> c
+ Darcs.Prelude: curry :: ((a, b) -> c) -> a -> b -> c
- Darcs.Prelude: cycle :: () => [a] -> [a]
+ Darcs.Prelude: cycle :: [a] -> [a]
- Darcs.Prelude: drop :: () => Int -> [a] -> [a]
+ Darcs.Prelude: drop :: Int -> [a] -> [a]
- Darcs.Prelude: dropWhile :: () => (a -> Bool) -> [a] -> [a]
+ Darcs.Prelude: dropWhile :: (a -> Bool) -> [a] -> [a]
- Darcs.Prelude: either :: () => (a -> c) -> (b -> c) -> Either a b -> c
+ Darcs.Prelude: either :: (a -> c) -> (b -> c) -> Either a b -> c
- Darcs.Prelude: error :: HasCallStack => [Char] -> a
+ Darcs.Prelude: error :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => [Char] -> a
- Darcs.Prelude: errorWithoutStackTrace :: () => [Char] -> a
+ Darcs.Prelude: errorWithoutStackTrace :: forall (r :: RuntimeRep) (a :: TYPE r). [Char] -> a
- Darcs.Prelude: fail :: Monad m => String -> m a
+ Darcs.Prelude: fail :: MonadFail m => String -> m a
- Darcs.Prelude: filter :: () => (a -> Bool) -> [a] -> [a]
+ Darcs.Prelude: filter :: (a -> Bool) -> [a] -> [a]
- Darcs.Prelude: flip :: () => (a -> b -> c) -> b -> a -> c
+ Darcs.Prelude: flip :: (a -> b -> c) -> b -> a -> c
- Darcs.Prelude: fst :: () => (a, b) -> a
+ Darcs.Prelude: fst :: (a, b) -> a
- Darcs.Prelude: head :: () => [a] -> a
+ Darcs.Prelude: head :: [a] -> a
- Darcs.Prelude: id :: () => a -> a
+ Darcs.Prelude: id :: a -> a
- Darcs.Prelude: infixl 6 -
+ Darcs.Prelude: infixl 6 +
- Darcs.Prelude: init :: () => [a] -> [a]
+ Darcs.Prelude: init :: [a] -> [a]
- Darcs.Prelude: ioError :: () => IOError -> IO a
+ Darcs.Prelude: ioError :: IOError -> IO a
- Darcs.Prelude: iterate :: () => (a -> a) -> a -> [a]
+ Darcs.Prelude: iterate :: (a -> a) -> a -> [a]
- Darcs.Prelude: last :: () => [a] -> a
+ Darcs.Prelude: last :: [a] -> a
- Darcs.Prelude: map :: () => (a -> b) -> [a] -> [b]
+ Darcs.Prelude: map :: (a -> b) -> [a] -> [b]
- Darcs.Prelude: maybe :: () => b -> (a -> b) -> Maybe a -> b
+ Darcs.Prelude: maybe :: b -> (a -> b) -> Maybe a -> b
- Darcs.Prelude: readParen :: () => Bool -> ReadS a -> ReadS a
+ Darcs.Prelude: readParen :: Bool -> ReadS a -> ReadS a
- Darcs.Prelude: repeat :: () => a -> [a]
+ Darcs.Prelude: repeat :: a -> [a]
- Darcs.Prelude: replicate :: () => Int -> a -> [a]
+ Darcs.Prelude: replicate :: Int -> a -> [a]
- Darcs.Prelude: reverse :: () => [a] -> [a]
+ Darcs.Prelude: reverse :: [a] -> [a]
- Darcs.Prelude: scanl :: () => (b -> a -> b) -> b -> [a] -> [b]
+ Darcs.Prelude: scanl :: (b -> a -> b) -> b -> [a] -> [b]
- Darcs.Prelude: scanl1 :: () => (a -> a -> a) -> [a] -> [a]
+ Darcs.Prelude: scanl1 :: (a -> a -> a) -> [a] -> [a]
- Darcs.Prelude: scanr :: () => (a -> b -> b) -> b -> [a] -> [b]
+ Darcs.Prelude: scanr :: (a -> b -> b) -> b -> [a] -> [b]
- Darcs.Prelude: scanr1 :: () => (a -> a -> a) -> [a] -> [a]
+ Darcs.Prelude: scanr1 :: (a -> a -> a) -> [a] -> [a]
- Darcs.Prelude: seq :: () => a -> b -> b
+ Darcs.Prelude: seq :: forall (r :: RuntimeRep) a (b :: TYPE r). a -> b -> b
- Darcs.Prelude: snd :: () => (a, b) -> b
+ Darcs.Prelude: snd :: (a, b) -> b
- Darcs.Prelude: span :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Darcs.Prelude: span :: (a -> Bool) -> [a] -> ([a], [a])
- Darcs.Prelude: splitAt :: () => Int -> [a] -> ([a], [a])
+ Darcs.Prelude: splitAt :: Int -> [a] -> ([a], [a])
- Darcs.Prelude: tail :: () => [a] -> [a]
+ Darcs.Prelude: tail :: [a] -> [a]
- Darcs.Prelude: take :: () => Int -> [a] -> [a]
+ Darcs.Prelude: take :: Int -> [a] -> [a]
- Darcs.Prelude: takeWhile :: () => (a -> Bool) -> [a] -> [a]
+ Darcs.Prelude: takeWhile :: (a -> Bool) -> [a] -> [a]
- Darcs.Prelude: uncurry :: () => (a -> b -> c) -> (a, b) -> c
+ Darcs.Prelude: uncurry :: (a -> b -> c) -> (a, b) -> c
- Darcs.Prelude: undefined :: HasCallStack => a
+ Darcs.Prelude: undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a
- Darcs.Prelude: until :: () => (a -> Bool) -> (a -> a) -> a -> a
+ Darcs.Prelude: until :: (a -> Bool) -> (a -> a) -> a -> a
- Darcs.Prelude: unzip :: () => [(a, b)] -> ([a], [b])
+ Darcs.Prelude: unzip :: [(a, b)] -> ([a], [b])
- Darcs.Prelude: unzip3 :: () => [(a, b, c)] -> ([a], [b], [c])
+ Darcs.Prelude: unzip3 :: [(a, b, c)] -> ([a], [b], [c])
- Darcs.Prelude: zip :: () => [a] -> [b] -> [(a, b)]
+ Darcs.Prelude: zip :: [a] -> [b] -> [(a, b)]
- Darcs.Prelude: zip3 :: () => [a] -> [b] -> [c] -> [(a, b, c)]
+ Darcs.Prelude: zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
- Darcs.Prelude: zipWith :: () => (a -> b -> c) -> [a] -> [b] -> [c]
+ Darcs.Prelude: zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
- Darcs.Prelude: zipWith3 :: () => (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
+ Darcs.Prelude: zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
- Darcs.Repository: OldRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository: OldRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository: RebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository: RebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository: StartRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository: StartRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository: V1Job :: (forall wR wU. Repository ( 'RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository: V1Job :: (forall wR wU. Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository: [EmptyRepository] :: (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'NoRebase) p Origin Origin Origin -> EmptyRepository
+ Darcs.Repository: [EmptyRepository] :: (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'NoRebase) p Origin Origin Origin -> EmptyRepository
- Darcs.Repository: createRepositoryV1 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ( 'RepoType 'NoRebase) (RepoPatchV1 Prim) Origin Origin Origin)
+ Darcs.Repository: createRepositoryV1 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) Origin Origin Origin)
- Darcs.Repository: createRepositoryV2 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ( 'RepoType 'NoRebase) (RepoPatchV2 Prim) Origin Origin Origin)
+ Darcs.Repository: createRepositoryV2 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ('RepoType 'NoRebase) (RepoPatchV2 Prim) Origin Origin Origin)
- Darcs.Repository.Create: [EmptyRepository] :: (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'NoRebase) p Origin Origin Origin -> EmptyRepository
+ Darcs.Repository.Create: [EmptyRepository] :: (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'NoRebase) p Origin Origin Origin -> EmptyRepository
- Darcs.Repository.Create: createRepositoryV1 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ( 'RepoType 'NoRebase) (RepoPatchV1 Prim) Origin Origin Origin)
+ Darcs.Repository.Create: createRepositoryV1 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) Origin Origin Origin)
- Darcs.Repository.Create: createRepositoryV2 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ( 'RepoType 'NoRebase) (RepoPatchV2 Prim) Origin Origin Origin)
+ Darcs.Repository.Create: createRepositoryV2 :: WithWorkingDir -> WithPatchIndex -> UseCache -> IO (Repository ('RepoType 'NoRebase) (RepoPatchV2 Prim) Origin Origin Origin)
- Darcs.Repository.Job: OldRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository.Job: OldRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository.Job: RebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository.Job: RebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository.Job: StartRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository.Job: StartRebaseJob :: (forall p wR wU. (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository.Job: V1Job :: (forall wR wU. Repository ( 'RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a) -> RepoJob a
+ Darcs.Repository.Job: V1Job :: (forall wR wU. Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a) -> RepoJob a
- Darcs.Repository.Job: checkRepoIsNoRebase :: forall rt p wR wU wT. IsRepoType rt => Repository rt p wR wU wT -> Maybe (Repository ( 'RepoType 'NoRebase) p wR wU wT)
+ Darcs.Repository.Job: checkRepoIsNoRebase :: forall rt p wR wU wT. IsRepoType rt => Repository rt p wR wU wT -> Maybe (Repository ('RepoType 'NoRebase) p wR wU wT)
- Darcs.Repository.Rebase: checkOldStyleRebaseStatus :: RepoPatch p => SRebaseType rebaseType -> Repository ( 'RepoType rebaseType) p wR wU wR -> IO ()
+ Darcs.Repository.Rebase: checkOldStyleRebaseStatus :: RepoPatch p => SRebaseType rebaseType -> Repository ('RepoType rebaseType) p wR wU wR -> IO ()
- Darcs.Repository.Rebase: maybeDisplaySuspendedStatus :: RepoPatch p => SRebaseType rebaseType -> Repository ( 'RepoType rebaseType) p wR wU wR -> IO ()
+ Darcs.Repository.Rebase: maybeDisplaySuspendedStatus :: RepoPatch p => SRebaseType rebaseType -> Repository ('RepoType rebaseType) p wR wU wR -> IO ()
- Darcs.Repository.Rebase: rebaseJob :: (RepoPatch p, ApplyState p ~ Tree) => (Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a
+ Darcs.Repository.Rebase: rebaseJob :: (RepoPatch p, ApplyState p ~ Tree) => (Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> Repository ('RepoType 'IsRebase) p wR wU wR -> IO a
- Darcs.Repository.Rebase: startRebaseJob :: (RepoPatch p, ApplyState p ~ Tree) => (Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a) -> Repository ( 'RepoType 'IsRebase) p wR wU wR -> IO a
+ Darcs.Repository.Rebase: startRebaseJob :: (RepoPatch p, ApplyState p ~ Tree) => (Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) -> Repository ('RepoType 'IsRebase) p wR wU wR -> IO a
- Darcs.UI.ApplyPatches: PatchProxy :: PatchProxy
+ Darcs.UI.ApplyPatches: PatchProxy :: PatchProxy (p :: * -> * -> *)
- Darcs.Util.Tree.Monad: copy :: TreeRW m => AnchoredPath -> AnchoredPath -> m ()
+ Darcs.Util.Tree.Monad: copy :: Monad m => AnchoredPath -> AnchoredPath -> TreeMonad m ()
- Darcs.Util.Tree.Monad: createDirectory :: TreeRW m => AnchoredPath -> m ()
+ Darcs.Util.Tree.Monad: createDirectory :: Monad m => AnchoredPath -> TreeMonad m ()
- Darcs.Util.Tree.Monad: directoryExists :: TreeRO m => AnchoredPath -> m Bool
+ Darcs.Util.Tree.Monad: directoryExists :: Monad m => AnchoredPath -> TreeMonad m Bool
- Darcs.Util.Tree.Monad: exists :: TreeRO m => AnchoredPath -> m Bool
+ Darcs.Util.Tree.Monad: exists :: Monad m => AnchoredPath -> TreeMonad m Bool
- Darcs.Util.Tree.Monad: fileExists :: TreeRO m => AnchoredPath -> m Bool
+ Darcs.Util.Tree.Monad: fileExists :: Monad m => AnchoredPath -> TreeMonad m Bool
- Darcs.Util.Tree.Monad: readFile :: TreeRO m => AnchoredPath -> m ByteString
+ Darcs.Util.Tree.Monad: readFile :: Monad m => AnchoredPath -> TreeMonad m ByteString
- Darcs.Util.Tree.Monad: rename :: TreeRW m => AnchoredPath -> AnchoredPath -> m ()
+ Darcs.Util.Tree.Monad: rename :: Monad m => AnchoredPath -> AnchoredPath -> TreeMonad m ()
- Darcs.Util.Tree.Monad: runTreeMonad :: Monad m => TreeMonad m a -> TreeState m -> m (a, Tree m)
+ Darcs.Util.Tree.Monad: runTreeMonad :: Monad m => TreeMonad m a -> Tree m -> (TreeItem m -> m Hash) -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m)) -> m (a, Tree m)
- Darcs.Util.Tree.Monad: type TreeMonad m = RWST AnchoredPath () (TreeState m) m
+ Darcs.Util.Tree.Monad: type TreeMonad m = RWST (TreeEnv m) () (TreeState m) m
- Darcs.Util.Tree.Monad: unlink :: TreeRW m => AnchoredPath -> m ()
+ Darcs.Util.Tree.Monad: unlink :: Monad m => AnchoredPath -> TreeMonad m ()
- Darcs.Util.Tree.Monad: writeFile :: TreeRW m => AnchoredPath -> ByteString -> m ()
+ Darcs.Util.Tree.Monad: writeFile :: Monad m => AnchoredPath -> ByteString -> TreeMonad m ()
Files
- CHANGELOG +51/−0
- contrib/_darcs.zsh +33/−17
- contrib/darcs_completion +38/−32
- darcs.cabal +3/−3
- harness/Darcs/Test/HashedStorage.hs +2/−2
- release/distributed-context +1/−1
- src/Darcs/Patch/ApplyPatches.hs +0/−22
- src/Darcs/Patch/Index/Monad.hs +10/−14
- src/Darcs/Patch/Index/Types.hs +0/−4
- src/Darcs/Patch/Prim/V1/Apply.hs +18/−1
- src/Darcs/Repository/ApplyPatches.hs +40/−28
- src/Darcs/Repository/Create.hs +18/−11
- src/Darcs/Repository/Hashed.hs +5/−1
- src/Darcs/Repository/Identify.hs +16/−10
- src/Darcs/Repository/PatchIndex.hs +6/−10
- src/Darcs/Repository/Repair.hs +2/−2
- src/Darcs/Repository/State.hs +72/−46
- src/Darcs/UI/Commands/Move.hs +6/−2
- src/Darcs/UI/Commands/ShowIndex.hs +2/−2
- src/Darcs/UI/Commands/Util/Tree.hs +17/−28
- src/Darcs/UI/Options/All.hs +2/−2
- src/Darcs/Util/Index.hs +21/−25
- src/Darcs/Util/Tree/Hashed.hs +2/−2
- src/Darcs/Util/Tree/Monad.hs +161/−150
- tests/broken_move.sh +117/−0
- tests/data/broken_move.tgz binary
- tests/data/rebase-0.0-conflict.tgz binary
- tests/data/rebase-0.0.tgz binary
- tests/data/rebase-0.2.tgz binary
- tests/issue2674-moving-unadded-files.sh +20/−0
- tests/rebase-0.0-compat.sh +54/−0
- tests/rebase-0.2-compat.sh +28/−0
- tests/rmdir.sh +1/−1
- tests/trackdown-bisect.sh +2/−1
CHANGELOG view
@@ -1,3 +1,54 @@+Darcs 2.16.4, 20 May 2021++ This release is mostly to fix http://bugs.darcs.net/issue2674 which can+ lead to repository corruption. This is not quite as bad as it sounds,+ since the broken changes that you could have recorded were consistently+ ignored when applying the patch. This bug has been in darcs for a very+ long time and even our own repos contain (ancient) patches with broken+ move changes, and so far it hasn't caused us any trouble.++ That said, there are certain patch commutations that will erroneously (and+ unexpectedly) fail when such a patch is involved. We therefore recommend+ to upgrade. You may also (after upgrading) run 'darcs check' on your+ repositories to see if you are affected, and 'darcs repair' them if that+ is the case. Fortunately, the broken move changes can be safely eliminated+ from existing patches, and the improved repair command now does exactly+ that.++ Thanks to lemming@henning-thielemann.de for bringing this bug to our+ attention.++ * resolve issue2674: moving unadded files+ * add a check/repair rule for bad move patches+ This drops a move patch with either non-existing source or existing target.++ Also related to issue2674:++ * improve error message when runDefault fails due to an IO error+ * remove catching of exceptions for bad moves in DefaultIO+ * fail in Darcs.Util.Tree.Monad.rename if source does not exist+ * test that we cannot record patches that depend on broken moves++ The rest of the changes are minor bug fixes plus a few dependencies.++ * resolve issue2670: convert "." to absolute path when creating a repo+ This bug was introduced by fix for issue2668.+ * resolve issue2668: createDirectory: permission denied+ * resolve issue2667: darcs init failed with permission denied+ * bugfix: --leave-test-dir should be off by default+ * bash_completion: use '--list-options' before any other option+ This is so to avoid that, e.g.:+ $ darcs record -m <Tab><Tab>+ results in a patch named '--list-options' being recorded.+ * zsh completion: use '--list-options' before any other option+ See the corresponding change in the bash completion for details.+ * zsh completion: improve the get/clone case+ Moving the special case for get/clone down into the catch-all case for the+ current word allows it to complete options and local paths.+ * bugfix: --leave-test-dir should be off by default+ * bugfix in readPendingAndMovesAndUnrecorded+ * print patch application warnings to stderr, not stdout+ Darcs 2.16.3, 22 October 2020 * Fix building with `-f curl` (issue2655)
contrib/_darcs.zsh view
@@ -4,35 +4,51 @@ ## Originally derived from a version by ## Copyright (C) 2009 Nicolas Pouillard -local -a darcs_options darcs_non_options darcs_arguments+local -a darcs_options darcs_non_options darcs_arguments darcs_list_options if (($CURRENT == 2)); then compadd -- $(darcs --commands) else- case "${words[2]}"; in- get|clone)- _urls- ;;- esac- case "${words[$CURRENT]}"; in+ # advanced zsh (array) parameter expansion fu:+ # - ${(f)...} means split into array elements at line endings+ # instead of white space+ # - ${arr:#pat} drops elements matching pat from arr, whereas+ # ${(M)arr:#pat} drops non-matching elements+ # - ${arr/pat/repl} replaces pat with repl for all elements of arr+ # - ${arr[(i)val]} is reverse indexing: returns index of first+ # occurrence of val in arr++ # save current word+ local current_word=$words[$CURRENT]+ # delete it from words+ words[$CURRENT]=()+ # find the index of the first option argument+ local first_opt=${words[(i)-*]}+ # insert --list-options right before the first option argument+ darcs_list_options=($words[1,$(($first_opt - 1))] --list-options $words[$first_opt,-1])+ # debugging help+ #print -l $darcs_list_options > ./debug_darcs_completion+ # execute command line with --list-options inserted and+ # split the result (stdout) at line endings+ darcs_arguments=(${(f)"$($darcs_list_options 2>/dev/null)"})+ case $current_word; in /*|./*|\~*|../*) _files ;; -*)- # advanced zsh (array) parameter expansion fu:- # - ${(f)...} means split into array elements at line endings- # instead of white space- # - ${arr:#pat} drops elements matching pat from arr, whereas- # ${(M)arr:#pat} drops non-matching elements- # - ${arr/pat/repl} replaces pat with repl for all elements of arr- darcs_arguments=(${(f)"$(words[$CURRENT]=--list-options && $words 2>/dev/null)"}) darcs_options=(${${(M)darcs_arguments:#-*}/;/:}) _describe '' darcs_options ;; *)- darcs_arguments=(${(f)"$(words[$CURRENT]=--list-options && $words 2>/dev/null)"})- darcs_non_options=(${darcs_arguments:#-*})- _multi_parts -i -S ' ' / darcs_non_options+ case "${words[2]}"; in+ get|clone)+ _urls+ ;;+ *)+ darcs_non_options=(${darcs_arguments:#-*})+ _multi_parts -i -S ' ' / darcs_non_options+ ;;+ esac ;; esac fi
contrib/darcs_completion view
@@ -16,51 +16,57 @@ return 0 fi - # Store the whole command line substituting the (possibly empty)- # to-be-completed word with '--list-options'.- local -a words=("${COMP_WORDS[@]}")- words[$COMP_CWORD]="--list-options"+ # Top-level options do not accept any further options+ case "${COMP_WORDS[1]}" in+ (-*) COMPREPLY=''; return 0;;+ esac - # Options are processed from left to right, so avoid to display the help- # page when trying to complete a command line that includes '--help'. It- # could be tricked by things like '--repodir --hell', but, come on... you- # don't deserve a working completion if you name a directory '--hell'.- for w in "${words[@]}"; do- case "$w" in- (--he*) return 0;;+ # Build a new command line copying all the tokens and inserting+ # '--list-options' before the first option (a token starting with '-').+ local -a cmdline+ local i=0+ local m=$(( ${#COMP_WORDS[@]} - 1 ))+ for token in "${COMP_WORDS[@]:0:${m}}"+ do+ case "$token" in+ (-*) cmdline+=("--list-options" "${COMP_WORDS[@]:${i}}");+ break;;+ (*) cmdline+=("$token");+ i=$(( $i + 1 ));; esac done+ test $i -eq $m && cmdline+=("--list-options") # So that the following "command-output to array" operation splits only at # newlines, not at each space, tab or newline. local IFS=$'\n'- COMPREPLY=( $( "${words[@]}" 2>/dev/null |\+ COMPREPLY=( $( "${cmdline[@]}" 2>/dev/null |\ command grep "^${cur//./\\.}" | cut -d ';' -f 1) ) - # Then, we adapt the resulting strings to be reusable by bash. If we don't- # do this, in the case where we have two repositories named- # ~/space in there-0.1 and ~/space in there-0.2, the first completion will- # give us:- # bash> darcs push ~/space in there-0.- # ~/space in there-0.1 ~/space in there-0.2- # and we have introduced two spaces in the command line (if we try to- # recomplete that, it won't find anything, as it doesn't know anything- # starting with "there-0.").- # printf %q will gracefully add the necessary backslashes.- #- # Bash also interprets colon as a separator. If we didn't handle it- # specially, completing http://example.org/repo from http://e would - # give us:- # bash> darcs pull http:http://example.org/repo- # An option would be to require the user to escape : as \: and we- # would do the same here. Instead, we return only the part after- # the last colon that is already there, and thus fool bash. The- # downside is that bash only shows this part to the user.+ # Then, we adapt the resulting strings to be reusable by bash. If we don't+ # do this, in the case where we have two repositories named+ # ~/space in there-0.1 and ~/space in there-0.2, the first completion will+ # give us:+ # bash> darcs push ~/space in there-0.+ # ~/space in there-0.1 ~/space in there-0.2+ # and we have introduced two spaces in the command line (if we try to+ # recomplete that, it won't find anything, as it doesn't know anything+ # starting with "there-0.").+ # printf %q will gracefully add the necessary backslashes.+ #+ # Bash also interprets colon as a separator. If we didn't handle it+ # specially, completing http://example.org/repo from http://e would+ # give us:+ # bash> darcs pull http:http://example.org/repo+ # An option would be to require the user to escape : as \: and we+ # would do the same here. Instead, we return only the part after+ # the last colon that is already there, and thus fool bash. The+ # downside is that bash only shows this part to the user. local i=${#COMPREPLY[*]} local colonprefixes=${cur%"${cur##*:}"} while [ $((--i)) -ge 0 ]; do COMPREPLY[$i]=`printf %q "${COMPREPLY[$i]}"`- COMPREPLY[$i]=${COMPREPLY[$i]#"$colonprefixes"} + COMPREPLY[$i]=${COMPREPLY[$i]#"$colonprefixes"} done return 0 }
darcs.cabal view
@@ -1,6 +1,6 @@-Cabal-Version: 2.2+Cabal-Version: 2.4 Name: darcs-version: 2.16.3+version: 2.16.4 License: GPL-2.0-or-later License-file: COPYING Author: David Roundy <droundy@darcs.net>, <darcs-devel@darcs.net>@@ -117,6 +117,7 @@ manual: True flag rts+ description: Support RTS options default: False manual: True @@ -151,7 +152,6 @@ Darcs.Patch.Annotate Darcs.Patch.Apply Darcs.Patch.ApplyMonad- Darcs.Patch.ApplyPatches Darcs.Patch.Bracketed Darcs.Patch.Bundle Darcs.Patch.Choices
harness/Darcs/Test/HashedStorage.hs view
@@ -149,7 +149,7 @@ exist <- doesFileExist "_darcs/index" performGC -- required in win32 to trigger file close when exist $ removeFile "_darcs/index"- idx <- updateIndex =<< updateIndexFrom "_darcs/index" darcsTreeHash x+ idx <- treeFromIndex =<< updateIndexFrom "_darcs/index" darcsTreeHash x return (x, idx) check_index = extractRepoAndRun $ do (pris, idx) <- build_index@@ -280,7 +280,7 @@ check_diffTrees = extractRepoAndRun $ do Prelude.writeFile "foo_dir/foo_a" "b\n" working_plain <- filter nondarcs `fmap` readPlainTree "."- working <- updateIndex =<<+ working <- treeFromIndex =<< updateIndexFrom "_darcs/index" darcsTreeHash working_plain pristine <- readDarcsPristine "." (working', pristine') <- diffTrees working pristine
release/distributed-context view
@@ -1,1 +1,1 @@-Just "\nContext:\n\n\n[TAG 2.16.3\nBen Franksen <ben.franksen@online.de>**20201022094642\n Ignore-this: bd76d7e31488721e4a5ce7267115e71c3b68d680c155c75c3dd275d9e54933a607d208fab502a143\n] \n"+Just "\nContext:\n\n\n[TAG 2.16.4\nBen Franksen <ben.franksen@online.de>**20210520120106\n Ignore-this: 6ad053de84a2b3fcd9dc62076e0fbbad24d8c25ef4a659a163cadc1832b39686aa41a2b7a12928df\n] \n"
− src/Darcs/Patch/ApplyPatches.hs
@@ -1,22 +0,0 @@-module Darcs.Patch.ApplyPatches- ( applyPatches- ) where--import Darcs.Patch.Info ( displayPatchInfo )-import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )-import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully, info )-import Darcs.Patch.Apply ( Apply(..) )-import Darcs.Patch.MonadProgress ( MonadProgress, ProgressAction(..), runProgressActions)--import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL )-import Darcs.Util.Printer ( text, ($$) )--applyPatches :: (MonadProgress m, ApplyMonad (ApplyState p) m, Apply p)- => FL (PatchInfoAnd rt p) wX wY -> m ()-applyPatches ps = runProgressActions "Applying patch" (mapFL doApply ps)- where- doApply hp = ProgressAction { paAction = apply (hopefully hp)- , paMessage = displayPatchInfo (info hp)- , paOnError = text "Unapplicable patch:" $$- displayPatchInfo (info hp)- }
src/Darcs/Patch/Index/Monad.hs view
@@ -71,20 +71,16 @@ mRemoveDirectory = remove mRename a b = do fns <- gets fst- if S.notMember a fns then- addMod (PInvalid a) -- works around some old repo inconsistencies- else- do -- we have to account for directory moves- addMod (PRename a b)- modifyFps (S.delete a)- addFile b- forM_ (S.toList fns) $ \fn ->- when (a `isPrefix` fn && a /= fn) $ do- modifyFps (S.delete fn)- let newfn = movedirfilename a b fn- addFile newfn- addMod (PRename fn newfn)-+ -- we have to account for directory moves+ addMod (PRename a b)+ modifyFps (S.delete a)+ addFile b+ forM_ (S.toList fns) $ \fn ->+ when (a `isPrefix` fn && a /= fn) $ do+ modifyFps (S.delete fn)+ let newfn = movedirfilename a b fn+ addFile newfn+ addMod (PRename fn newfn) mModifyFilePS f _ = addMod (PTouch f) -- ---------------------------------------------------------------------
src/Darcs/Patch/Index/Types.hs view
@@ -56,10 +56,6 @@ | PRename a a | PRemove a- | PInvalid a- -- ^ This is an invalid patch- -- e.g. there is a patch 'Move Autoconf.lhs Autoconf.lhs.in'- -- where there is no Autoconf.lhs in the darcs repo | PDuplicateTouch a -- ^ this is used for duplicate patches that don't -- have any effect, but we still want to keep
src/Darcs/Patch/Prim/V1/Apply.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE MultiWayIf #-} module Darcs.Patch.Prim.V1.Apply () where import Darcs.Prelude@@ -105,7 +106,23 @@ FP f (Binary x new) :>: NilFL ) else return Nothing- applyAndTryToFixFL p = do apply p; return Nothing+ applyAndTryToFixFL p@(Move old new) =+ do old_is_file <- mDoesFileExist old+ old_is_dir <- mDoesDirectoryExist old+ new_is_file <- mDoesFileExist new+ new_is_dir <- mDoesDirectoryExist new+ if | not (old_is_file || old_is_dir) ->+ return $+ Just ("WARNING: Dropping move patch with non-existing source "++ap2fp old,+ unsafeCoercePStart NilFL+ )+ | new_is_file || new_is_dir ->+ return $+ Just ("WARNING: Dropping move patch with existing target "++ap2fp old,+ unsafeCoercePStart NilFL+ )+ | otherwise -> apply p >> return Nothing+ applyAndTryToFixFL p = apply p >> return Nothing instance PrimApply Prim where applyPrimFL NilFL = return ()
src/Darcs/Repository/ApplyPatches.hs view
@@ -25,35 +25,49 @@ , DefaultIO, runDefault ) where -import Control.Exception ( catch, SomeException, IOException )+import Control.Exception ( IOException, SomeException, catch )+import Control.Monad ( unless )+import qualified Data.ByteString as B ( empty, null, readFile ) import Data.Char ( toLower ) import Data.List ( isSuffixOf )-import System.IO ( stderr )-import System.IO.Error ( isDoesNotExistError, isPermissionError )-import Control.Monad ( unless, mplus )-import System.Directory ( createDirectory,- removeDirectory, removeFile,- renameFile, renameDirectory,- doesDirectoryExist, doesFileExist- )+import System.Directory+ ( createDirectory+ , doesDirectoryExist+ , doesFileExist+ , removeDirectory+ , removeFile+ , renamePath+ )+import System.IO ( hPutStrLn, stderr )+import System.IO.Error ( catchIOError, isDoesNotExistError, isPermissionError ) import Darcs.Prelude -import Darcs.Patch.ApplyMonad( ApplyMonad(..), ApplyMonadTree(..) )-import Darcs.Patch.ApplyPatches ( applyPatches )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) )+import Darcs.Patch.Info ( displayPatchInfo ) import Darcs.Patch.MonadProgress ( MonadProgress(..), ProgressAction(..) )-import Darcs.Repository.Prefs( changePrefval )-import Darcs.Util.Lock ( writeAtomicFilePS )-+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully, info )+import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL )+import Darcs.Repository.Prefs ( changePrefval ) import Darcs.Util.Exception ( prettyException )-import Darcs.Util.Progress ( beginTedious, endTedious, tediousSize, finishedOneIO )-import Darcs.Util.Printer ( hPutDocLn, renderString ) import Darcs.Util.External ( backupByCopying, backupByRenaming )+import Darcs.Util.Lock ( writeAtomicFilePS ) import Darcs.Util.Path ( AnchoredPath, anchorPath )-import qualified Data.ByteString as B (empty, null, readFile)--import Darcs.Util.Tree( Tree )+import Darcs.Util.Printer ( hPutDocLn, renderString )+import Darcs.Util.Printer ( text, ($$) )+import Darcs.Util.Progress ( beginTedious, endTedious, finishedOneIO, tediousSize )+import Darcs.Util.Tree ( Tree ) +applyPatches :: (MonadProgress m, ApplyMonad (ApplyState p) m, Apply p)+ => FL (PatchInfoAnd rt p) wX wY -> m ()+applyPatches ps = runProgressActions "Applying patch" (mapFL doApply ps)+ where+ doApply hp = ProgressAction { paAction = apply (hopefully hp)+ , paMessage = displayPatchInfo (info hp)+ , paOnError = text "Unapplicable patch:" $$+ displayPatchInfo (info hp)+ } ap2fp :: AnchoredPath -> FilePath ap2fp = anchorPath ""@@ -95,13 +109,7 @@ fail $ "Cannot remove non-empty file "++fp removeFile fp mRemoveDirectory = DefaultIO . removeDirectory . ap2fp- mRename a b = DefaultIO $- catch- (renameDirectory x y `mplus` renameFile x y)- -- We need to catch does not exist errors, since older- -- versions of darcs allowed users to rename nonexistent- -- files. :(- (\e -> unless (isDoesNotExistError e) $ ioError e)+ mRename a b = DefaultIO $ renamePath x y where x = ap2fp a y = ap2fp b @@ -114,7 +122,7 @@ deriving (Functor, Applicative, Monad) instance TolerantMonad TolerantIO where- warning io = TIO $ io `catch` \e -> putStrLn $ "Warning: " ++ prettyException e+ warning io = TIO $ io `catch` \e -> hPutStrLn stderr $ "Warning: " ++ prettyException e runIO (TIO io) = io runTM = TIO @@ -140,7 +148,11 @@ -- | The default mode of applying patches: fail if the directory is not -- as we expect runDefault :: DefaultIO a -> IO a-runDefault = runDefaultIO+runDefault action =+ catchIOError (runDefaultIO action) $ \e ->+ fail $ "Cannot apply some patch:\n"++show e+++ "\nYou may want to run 'darcs check' to find out if there are broken"+++ "\npatches in your repo, and perhaps 'darcs repair' to fix them." instance TolerantMonad m => ApplyMonad Tree (TolerantWrapper m) where type ApplyMonadBase (TolerantWrapper m) = IO
src/Darcs/Repository/Create.hs view
@@ -76,6 +76,7 @@ ( writeBinFile , writeDocBinFile )+import Darcs.Util.Path ( ioAbsoluteOrRemote, toPath ) import Darcs.Util.Tree( Tree, emptyTree ) import Darcs.Util.Tree.Hashed( writeDarcsHashed, darcsAddMissingHashes ) @@ -112,30 +113,34 @@ createRepository patchfmt withWorkingDir withPatchIndex useCache = do rfmt <- createRepositoryFiles patchfmt withWorkingDir cache <- getCaches useCache here+ rdir <- toPath <$> ioAbsoluteOrRemote here repo@(EmptyRepository r) <- case patchfmt of- PatchFormat1 -> return $ EmptyRepository $ mkRepoV1 rfmt cache- PatchFormat2 -> return $ EmptyRepository $ mkRepoV2 rfmt cache- PatchFormat3 -> return $ EmptyRepository $ mkRepoV3 rfmt cache+ PatchFormat1 -> return $ EmptyRepository $ mkRepoV1 rdir rfmt cache+ PatchFormat2 -> return $ EmptyRepository $ mkRepoV2 rdir rfmt cache+ PatchFormat3 -> return $ EmptyRepository $ mkRepoV3 rdir rfmt cache maybeCreatePatchIndex withPatchIndex r return repo mkRepoV1- :: RepoFormat+ :: FilePath+ -> RepoFormat -> Cache -> Repository ('RepoType 'NoRebase) (RepoPatchV1 V1.Prim) Origin Origin Origin-mkRepoV1 repofmt cache = mkRepo here repofmt HashedPristine cache+mkRepoV1 rdir repofmt cache = mkRepo rdir repofmt HashedPristine cache mkRepoV2- :: RepoFormat+ :: FilePath+ -> RepoFormat -> Cache -> Repository ('RepoType 'NoRebase) (RepoPatchV2 V2.Prim) Origin Origin Origin-mkRepoV2 repofmt cache = mkRepo here repofmt HashedPristine cache+mkRepoV2 rdir repofmt cache = mkRepo rdir repofmt HashedPristine cache mkRepoV3- :: RepoFormat+ :: FilePath+ -> RepoFormat -> Cache -> Repository ('RepoType 'NoRebase) (RepoPatchV3 V2.Prim) Origin Origin Origin-mkRepoV3 repofmt cache = mkRepo here repofmt HashedPristine cache+mkRepoV3 rdir repofmt cache = mkRepo rdir repofmt HashedPristine cache createRepositoryV1 :: WithWorkingDir -> WithPatchIndex -> UseCache@@ -143,7 +148,8 @@ createRepositoryV1 withWorkingDir withPatchIndex useCache = do rfmt <- createRepositoryFiles PatchFormat1 withWorkingDir cache <- getCaches useCache here- let repo = mkRepoV1 rfmt cache+ rdir <- toPath <$> ioAbsoluteOrRemote here+ let repo = mkRepoV1 rdir rfmt cache maybeCreatePatchIndex withPatchIndex repo return repo @@ -153,7 +159,8 @@ createRepositoryV2 withWorkingDir withPatchIndex useCache = do rfmt <- createRepositoryFiles PatchFormat2 withWorkingDir cache <- getCaches useCache here- let repo = mkRepoV2 rfmt cache+ rdir <- toPath <$> ioAbsoluteOrRemote here+ let repo = mkRepoV2 rdir rfmt cache maybeCreatePatchIndex withPatchIndex repo return repo
src/Darcs/Repository/Hashed.hs view
@@ -626,7 +626,11 @@ let r' = unsafeCoerceR r debugMessage "Done finalizing changes..." ps <- readRepo r'- doesPatchIndexExist (repoLocation r') >>= (`when` createOrUpdatePatchIndexDisk r' ps)+ pi_exists <- doesPatchIndexExist (repoLocation r')+ when pi_exists $+ createOrUpdatePatchIndexDisk r' ps+ `catchIOError` \e ->+ hPutStrLn stderr $ "Cannot create or update patch index: "++ show e updateIndex r' return r' | otherwise = fail Old.oldRepoFailMsg
src/Darcs/Repository/Identify.hs view
@@ -33,6 +33,7 @@ , listDirectory ) import System.FilePath.Posix ( (</>) )+import System.IO ( hPutStrLn, stderr ) import System.IO.Error ( catchIOError ) import Data.Maybe ( fromMaybe ) @@ -164,7 +165,7 @@ -- | hunt upwards for the darcs repository -- This keeps changing up one parent directory, testing at each--- step if the current directory is a repository or not. $+-- step if the current directory is a repository or not. -- The result is: -- Nothing, if no repository found -- Just (Left errorMessage), if bad repository found@@ -172,19 +173,24 @@ -- WARNING this changes the current directory for good if matchFn succeeds seekRepo :: IO (Maybe (Either String ())) seekRepo = getCurrentDirectory >>= helper where- helper startpwd = do+ helper startpwd = do status <- maybeIdentifyRepository YesUseCache "." case status of GoodRepository _ -> return . Just $ Right ()- BadRepository e -> return . Just $ Left e+ 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+ catchIOError+ (do cd <- toFilePath `fmap` getCurrentDirectory+ setCurrentDirectory ".."+ cd' <- toFilePath `fmap` getCurrentDirectory+ if cd' /= cd+ then helper startpwd+ else do+ setCurrentDirectory startpwd+ return Nothing)+ (\e -> do+ hPutStrLn stderr ("Warning: " ++ show e)+ return Nothing) -- The performGC in this function is a workaround for a library/GHC bug, -- http://hackage.haskell.org/trac/ghc/ticket/2924 -- (doesn't seem to be a
src/Darcs/Repository/PatchIndex.hs view
@@ -180,7 +180,6 @@ insertTouch fid pid stopFidSpan fn pid stopFpSpan fid pid- go (_, PInvalid _) = return () -- just ignore invalid changes go (pid, PDuplicateTouch fn) = do fidm <- gets fidspans case M.lookup fn fidm of@@ -323,14 +322,11 @@ PRemove f -> [f]; _ -> []} touched_all = listTouchedFiles p touched_effect = concatMap touched pmods_effect- touched_invalid = [ f | (PInvalid f) <- pmods_effect] -- listTouchedFiles returns all files that touched by these -- patches, even if they have no effect, e.g. by duplicate patches pmods_dup = map PDuplicateTouch . S.elems $ S.difference (S.fromList touched_all)- (S.fromList touched_invalid- `S.union`- S.fromList touched_effect)+ (S.fromList touched_effect) -- | return set of current filenames in patch index fpSpans2fileNames :: FilePathSpans -> Set AnchoredPath@@ -388,7 +384,7 @@ let newpatches = drop len_common $ mapFL seal2 flpatches newpmods = patches2patchMods newpatches filenames inv_hash <- getInventoryHash repodir- storePatchIndex repodir cdir inv_hash (applyPatchMods newpmods pindex')+ storePatchIndex cdir inv_hash (applyPatchMods newpmods pindex') where -- return uncommon suffixes and length of common prefix of as and bs uncommon = uncommon' 0@@ -403,7 +399,7 @@ -> [(PatchId, [PatchMod AnchoredPath])] -> IO () createPatchIndexFrom repo pmods = do inv_hash <- getInventoryHash repodir- storePatchIndex repodir cdir inv_hash (applyPatchMods pmods emptyPatchIndex)+ storePatchIndex cdir inv_hash (applyPatchMods pmods emptyPatchIndex) where repodir = repoLocation repo cdir = repodir </> indexDir emptyPatchIndex = PatchIndex [] M.empty M.empty M.empty@@ -518,10 +514,10 @@ else return False -- | Stores patch-index on disk.-storePatchIndex :: FilePath -> FilePath -> String -> PatchIndex -> IO ()-storePatchIndex repodir cdir inv_hash (PatchIndex pids fidspans fpspans infom) = do+storePatchIndex :: FilePath -> String -> PatchIndex -> IO ()+storePatchIndex cdir inv_hash (PatchIndex pids fidspans fpspans infom) = do createDirectory cdir `catch` \(_ :: IOError) -> return ()- tmpdir <- withPermDir repodir $ \dir -> do+ tmpdir <- withPermDir cdir $ \dir -> do debugMessage "About to create patch index..." let tmpdir = toFilePath dir storeRepoState (tmpdir </> repoStateFile) inv_hash
src/Darcs/Repository/Repair.hs view
@@ -76,7 +76,7 @@ import Darcs.Util.Tree.Monad( TreeIO ) import Darcs.Util.Tree.Hashed( darcsUpdateHashes, hashedTreeIO ) import Darcs.Util.Tree.Plain( readPlainTree )-import Darcs.Util.Index( updateIndex )+import Darcs.Util.Index( treeFromIndex ) import qualified Data.ByteString.Char8 as BC @@ -243,7 +243,7 @@ -> Bool -> IO Bool checkIndex repo quiet = do- index <- updateIndex =<< readIndex repo+ index <- treeFromIndex =<< readIndex repo pristine <- expand =<< readRecordedAndPending repo working <- expand =<< restrict pristine <$> readPlainTree "." working_hashed <- darcsUpdateHashes working
src/Darcs/Repository/State.hs view
@@ -39,7 +39,7 @@ import Darcs.Prelude -import Control.Monad ( when, foldM, forM )+import Control.Monad ( when, foldM, forM, void ) import Control.Monad.State ( StateT, runStateT, get, put, liftIO ) import Control.Exception ( catch, IOException ) import Data.Maybe ( isJust )@@ -47,13 +47,8 @@ import Data.List ( sortBy, union, delete ) import Text.Regex( matchRegex ) -import System.Directory( removeFile, doesFileExist, doesDirectoryExist, renameFile )-import System.FilePath- ( (</>)-#if mingw32_HOST_OS- , (<.>)-#endif- )+import System.Directory( doesFileExist, doesDirectoryExist, renameFile )+import System.FilePath ( (<.>), (</>) ) import System.IO ( hPutStrLn, stderr ) import System.IO.Error ( catchIOError ) @@ -100,6 +95,8 @@ , indexInvalidPath ) +import Darcs.Util.File ( removeFileMayNotExist )+import Darcs.Util.Global ( debugMessage ) import Darcs.Util.Path ( AnchoredPath , anchorPath@@ -115,7 +112,13 @@ import qualified Darcs.Util.Tree.Plain as PlainTree ( readPlainTree ) import Darcs.Util.Tree.Hashed ( darcsTreeHash, readDarcsHashed, decodeDarcsHash, decodeDarcsSize )-import qualified Darcs.Util.Index as I+import Darcs.Util.Index+ ( Index+ , indexFormatValid+ , openIndex+ , treeFromIndex+ , updateIndexFrom+ ) import qualified Darcs.Util.Tree as Tree import Darcs.Util.Index ( listFileIDs, getFileID ) @@ -245,14 +248,18 @@ IsEq <- return $ workDirLessRepoWitness r return (NilFL :> NilFL) readPendingAndWorking (useidx, scan, diffalg) lfm lfr repo mbpaths = do+ debugMessage "readPendingAndWorking: start" (pending_tree, working_tree, (pending :> moves)) <- readPendingAndMovesAndUnrecorded repo useidx scan lfm mbpaths+ debugMessage "readPendingAndWorking: after readPendingAndMovesAndUnrecorded" (pending_tree_with_replaces, Sealed replaces) <- getReplaces lfr diffalg repo pending_tree working_tree+ debugMessage "readPendingAndWorking: after getReplaces" ft <- filetypeFunction wrapped_diff <- treeDiff diffalg ft pending_tree_with_replaces working_tree case unFreeLeft wrapped_diff of Sealed diff -> do+ debugMessage "readPendingAndWorking: done" return $ unsafeCoercePEnd $ pending :> (moves +>+ replaces +>+ diff) readPendingAndMovesAndUnrecorded@@ -267,17 +274,28 @@ , (FL (PrimOf p) :> FL (PrimOf p)) wR wU -- pending :> moves ) readPendingAndMovesAndUnrecorded repo useidx scan lfm mbpaths = do+ debugMessage "readPendingAndMovesAndUnrecorded: start" (pending_tree, Sealed pending) <- readPending repo moves <- getMoves lfm repo mbpaths- let pending' = pending +>+ moves- relevant <- maybeRestrictSubpaths pending' repo mbpaths- pending_tree' <-+ -- we want to include any user specified paths before and after pending+ -- and detected moves+ relevant <- maybeRestrictSubpaths (pending +>+ moves) repo mbpaths+ pending_tree_with_moves <- applyTreeFilter relevant <$> applyToTree moves pending_tree+ debugMessage "readPendingAndMovesAndUnrecorded: before readIndexOrPlainTree"+ -- the moves are detected i.e. they are already applied in the working tree;+ -- also note that we have to use the amended pending tree to restrict the+ -- working tree in case we don't use the index (here and below)+ index <- readIndexOrPlainTree repo useidx relevant pending_tree_with_moves+ debugMessage "readPendingAndMovesAndUnrecorded: before filteredWorking"+ -- TODO this conditional looks wrong; so if we do have detected moves,+ -- then we cannot use the index to read the working state? Why not? let useidx' = if nullFL moves then useidx else IgnoreIndex- index <-- applyToTree moves =<< readIndexOrPlainTree repo useidx relevant pending_tree- working_tree <- filteredWorking repo useidx' scan relevant index pending_tree'- return (pending_tree', working_tree, unsafeCoercePEnd (pending :> moves))+ working_tree <-+ filteredWorking repo useidx' scan relevant index pending_tree_with_moves+ debugMessage "readPendingAndMovesAndUnrecorded: done"+ return+ (pending_tree_with_moves, working_tree, unsafeCoercePEnd (pending :> moves)) -- | @filteredWorking useidx scan relevant index pending_tree@ reads the -- working tree and filters it according to options and @relevant@ file paths.@@ -389,7 +407,7 @@ #if TEST_INDEX readIndexOrPlainTree repo useidx treeFilter pending_tree = do indexTree <-- I.updateIndex =<< applyTreeFilter treeFilter <$> readIndex repo+ treeFromIndex =<< applyTreeFilter treeFilter <$> readIndex repo plainTree <- do guide <- expand pending_tree expand =<< applyTreeFilter treeFilter . restrict guide <$> readPlainTree repo@@ -400,7 +418,7 @@ IgnoreIndex -> plainTree #else readIndexOrPlainTree repo UseIndex treeFilter pending_tree =- (I.updateIndex =<< applyTreeFilter treeFilter <$> readIndex repo)+ (treeFromIndex =<< applyTreeFilter treeFilter <$> readIndex repo) `catchIOError` \e -> do hPutStrLn stderr ("Warning, cannot access the index:\n" ++ show e) readIndexOrPlainTree repo IgnoreIndex treeFilter pending_tree@@ -455,48 +473,56 @@ return (pristine, seal NilFL) -- | Mark the existing index as invalid. This has to be called whenever the--- listing of pristine changes and will cause darcs to update the index next--- time it tries to read it. (NB. This is about files added and removed from+-- listing of pristine+pending changes and will cause darcs to update the index.+-- This will happen either when we call updateIndex in finalizeRepositoryChanges+-- or else when we try to read the index the next time.+-- (NB. This is about files added and removed from -- pristine: changes to file content in either pristine or working are handled -- transparently by the index reading code.) invalidateIndex :: t -> IO () invalidateIndex _ = B.writeFile indexInvalidPath B.empty readIndex :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wR -> IO I.Index+ => Repository rt p wR wU wR -> IO Index readIndex repo = do- (invalid, exists, formatValid) <- checkIndex- if not exists || invalid || not formatValid- then do pris <- readRecordedAndPending repo- idx <- I.updateIndexFrom indexPath darcsTreeHash pris- when invalid $ removeFile indexInvalidPath- return idx- else I.readIndex indexPath darcsTreeHash+ okay <- checkIndex+ if not okay+ then internalUpdateIndex repo+ else openIndex indexPath darcsTreeHash +-- | Update the index so that it matches pristine+pending. If the index does+-- not exist or is invalid, create a new one. Returns the updated index.+internalUpdateIndex :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wR -> IO Index+internalUpdateIndex repo = do+ pris <- readRecordedAndPending repo+ idx <- updateIndexFrom indexPath darcsTreeHash pris+ removeFileMayNotExist indexInvalidPath+ return idx++-- | Update the index so that it matches pristine+pending. If the index does+-- not exist or is invalid, create a new one. updateIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wR -> IO () updateIndex repo = do- (invalid, _, _) <- checkIndex- pris <- readRecordedAndPending repo- _ <- I.updateIndexFrom indexPath darcsTreeHash pris- when invalid $ removeFile indexInvalidPath+ -- call checkIndex to throw away the index if it is invalid;+ -- this can happen if we are called with --ignore-times+ -- TODO make this impossible i.e. honor UseIndex here+ void checkIndex+ void $ internalUpdateIndex repo -checkIndex :: IO (Bool, Bool, Bool)+-- | Check if we have a valid index. This means that the index file exists, is+-- readable, and can be mmapped, /and/ that indexInvalidPath does /not/ exist.+-- We do not yet remove indexInvalidPath in case updating the index fails.+checkIndex :: IO Bool checkIndex = do invalid <- doesFileExist $ indexInvalidPath- exists <- doesFileExist indexPath- formatValid <- if exists- then I.indexFormatValid indexPath- else return True- when (exists && not formatValid) $ do--- TODO this conditional logic (rename or delete) is mirrored in--- Darcs.Util.Index.updateIndexFrom and should be refactored-#if mingw32_HOST_OS- renameFile indexPath (indexPath <.> "old")-#else- removeFile indexPath-#endif- return (invalid, exists, formatValid)+ formatValid <- indexFormatValid indexPath+ exist <- doesFileExist indexPath+ -- this fails with a permission (access denied) error on windows+ -- if we use removeFileMayNotExist instead of renameFile+ when (exist && not formatValid) $ renameFile indexPath (indexPath <.> "old")+ return (not invalid && formatValid) -- |Remove any patches (+dependencies) from a sequence that -- conflict with the recorded or unrecorded changes in a repo
src/Darcs/UI/Commands/Move.hs view
@@ -202,8 +202,12 @@ moveFilesToDir :: [DarcsFlag] -> [AnchoredPath] -> AnchoredPath -> IO () moveFilesToDir opts froms to =- withRepoAndState opts $ \(repo, work, cur, _) ->- moveToDir repo opts cur work froms to+ withRepoAndState opts $ \(repo, work, cur, _) -> do+ froms_exist <- and <$> forM froms (treeHas cur)+ if froms_exist then+ moveToDir repo opts cur work froms to+ else+ fail "Some of the paths you want to move aren't know to darcs. Use `darcs add` to add them first." withRepoAndState :: [DarcsFlag] -> (forall rt p wR wU .
src/Darcs/UI/Commands/ShowIndex.hs view
@@ -38,7 +38,7 @@ import Darcs.Util.Hash( encodeBase16, Hash( NoHash ) ) import Darcs.Util.Tree( list, expand, itemHash, Tree, TreeItem( SubTree ) )-import Darcs.Util.Index( updateIndex, listFileIDs )+import Darcs.Util.Index( treeFromIndex, listFileIDs ) import Darcs.Util.Path( anchorPath, AbsolutePath, floatPath ) import Darcs.Util.Printer ( Doc, text ) @@ -98,7 +98,7 @@ showIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () showIndexCmd _ opts _ = withRepository (useCache ? opts) $ RepoJob $ \repo -> do index <- readIndex repo- index_tree <- updateIndex index+ index_tree <- treeFromIndex index fileids <- (M.fromList . map (\((a,_),b) -> (anchorPath "" a,b))) <$> listFileIDs index dump opts (Just fileids) index_tree
src/Darcs/UI/Commands/Util/Tree.hs view
@@ -26,41 +26,30 @@ import Darcs.Prelude -import Control.Monad ( forM )-import Control.Monad.State.Strict( gets )-import Data.Maybe ( fromMaybe )+import Data.List ( find ) +import Darcs.Util.Path ( AnchoredPath(..), eqAnycase )+import Darcs.Util.Tree ( Tree, TreeItem(..), listImmediate ) import qualified Darcs.Util.Tree.Monad as TM- ( TreeMonad, withDirectory, fileExists, directoryExists- , virtualTreeMonad, currentDirectory, exists, tree )-import Darcs.Util.Tree ( Tree, listImmediate, findTree )--import Darcs.Util.Path- ( AnchoredPath(..), eqAnycase )+ ( directoryExists+ , exists+ , fileExists+ , virtualTreeMonad+ ) treeHasAnycase :: Monad m => Tree m -> AnchoredPath -> m Bool-treeHasAnycase tree path =- fst `fmap` TM.virtualTreeMonad (existsAnycase path) tree---existsAnycase :: Monad m- => AnchoredPath- -> TM.TreeMonad m Bool-existsAnycase (AnchoredPath []) = return True-existsAnycase (AnchoredPath (x:xs)) = do- wd <- TM.currentDirectory- tree <- fromMaybe (error "invalid path passed to existsAnycase") <$>- gets (flip findTree wd . TM.tree)- let subs = [ AnchoredPath [n] | (n, _) <- listImmediate tree,- eqAnycase n x ]- or `fmap` forM subs (\path -> do- file <- TM.fileExists path- if file then return True- else TM.withDirectory path (existsAnycase $ AnchoredPath xs))-+treeHasAnycase tree (AnchoredPath names) = go names (SubTree tree)+ where+ go [] _ = return True+ go ns (Stub mkTree _) = mkTree >>= go ns . SubTree+ go _ (File _) = return False+ go (n:ns) (SubTree t) =+ case find (eqAnycase n . fst) (listImmediate t) of+ Nothing -> return False+ Just (_,i) -> go ns i treeHas :: Monad m => Tree m -> AnchoredPath -> m Bool treeHas tree path = fst `fmap` TM.virtualTreeMonad (TM.exists path) tree
src/Darcs/UI/Options/All.hs view
@@ -839,7 +839,7 @@ testChanges :: PrimDarcsOption TestChanges testChanges = imap (Iso fw bw) $ runTest ^ leaveTestDir where- fw k NoTestChanges = k NoRunTest {- undefined -} YesLeaveTestDir+ fw k NoTestChanges = k NoRunTest NoLeaveTestDir fw k (YesTestChanges ltd) = k YesRunTest ltd bw k NoRunTest _ = k NoTestChanges bw k YesRunTest ltd = k (YesTestChanges ltd)@@ -850,7 +850,7 @@ , RawNoArg [] ["no-test"] F.NoTest NoRunTest "don't run the test script" ] leaveTestDir :: PrimDarcsOption LeaveTestDir-leaveTestDir = withDefault YesLeaveTestDir+leaveTestDir = withDefault NoLeaveTestDir [ RawNoArg [] ["leave-test-directory"] F.LeaveTestDir YesLeaveTestDir "don't remove the test directory" , RawNoArg [] ["remove-test-directory"]
src/Darcs/Util/Index.hs view
@@ -70,7 +70,7 @@ -- index, so we can efficiently skip reading the whole subtree starting at a -- given directory (by just seeking aux bytes forward). The lines are -- pre-ordered with respect to directory structure -- the directory comes first--- and after it come all its items. Cf. 'readIndex''.+-- and after it come all its items. Cf. 'openIndex''. -- -- For files, the aux field holds a timestamp. --@@ -93,10 +93,10 @@ -- paths. module Darcs.Util.Index- ( readIndex+ ( openIndex , updateIndexFrom , indexFormatValid- , updateIndex+ , treeFromIndex , listFileIDs , Index , filter@@ -146,12 +146,8 @@ import System.IO ( hPutStrLn, stderr ) import System.IO.MMap( mmapFileForeignPtr, Mode(..) ) import System.Directory( doesFileExist, getCurrentDirectory, doesDirectoryExist )-#if mingw32_HOST_OS import System.Directory( renameFile ) import System.FilePath( (<.>) )-#else-import System.Directory( removeFile )-#endif #ifdef WIN32 import System.Win32.File@@ -187,7 +183,7 @@ -- used by git...) It turns out that it's hard to efficiently read a flat index -- with our internal data structures -- we need to turn the flat index into a -- recursive Tree object, which is rather expensive... As a bonus, we can also--- efficiently implement subtree queries this way (cf. 'readIndex').+-- efficiently implement subtree queries this way (cf. 'openIndex'). data Item = Item { iBase :: !(Ptr ()) , iHashAndDescriptor :: !B.ByteString } deriving Show@@ -575,18 +571,18 @@ -- * Reading and writing 'Tree's from/to the index -- | Read an index and build up a 'Tree' object from it, referring to current--- working directory. The initial Index object returned by readIndex is not+-- working directory. The initial Index object returned by openIndex is not -- directly useful. However, you can use 'Tree.filter' on it. Either way, to -- obtain the actual Tree object, call update. -- -- The usual use pattern is this: ----- > do (idx, update) <- readIndex+-- > do (idx, update) <- openIndex -- > tree <- update =<< filter predicate idx -- -- The resulting tree will be fully expanded.-readIndex :: FilePath -> (Tree IO -> Hash) -> IO Index-readIndex indexpath ht = do+openIndex :: FilePath -> (Tree IO -> Hash) -> IO Index+openIndex indexpath ht = do (mmap_ptr, mmap_size) <- mmapIndex indexpath 0 base <- getCurrentDirectory return $ if mmap_size == 0 then EmptyIndex@@ -638,32 +634,32 @@ -- does not exist in a plain form in current working directory). updateIndexFrom :: FilePath -> (Tree IO -> Hash) -> Tree IO -> IO Index updateIndexFrom indexpath hashtree' ref =- do old_idx <- updateIndex =<< readIndex indexpath hashtree'+ do old_tree <- treeFromIndex =<< openIndex indexpath hashtree' reference <- expand ref let len_root = itemAllocSize anchoredRoot len = len_root + sum [ itemAllocSize p | (p, _) <- list reference ] exist <- doesFileExist indexpath--- TODO this conditional logic (rename or delete) is mirrored in--- Darcs.Repository.State.checkIndex and should be refactored-#if mingw32_HOST_OS+ -- Note that the file is still open via the mmaped pointer in+ -- the open index, and we /are/ going to write the index using+ -- that pointer. If we could rely on posix semantics,+ -- we would just delete the file. However, on windows this+ -- would fail, so instead we rename it. when exist $ renameFile indexpath (indexpath <.> "old")-#else- when exist $ removeFile indexpath -- to avoid clobbering oldidx-#endif (mmap_ptr, _) <- mmapIndex indexpath len- formatIndex mmap_ptr old_idx reference- readIndex indexpath hashtree'+ formatIndex mmap_ptr old_tree reference+ openIndex indexpath hashtree' -updateIndex :: Index -> IO (Tree IO)-updateIndex EmptyIndex = return emptyTree-updateIndex index =+-- | Read the index, starting with the root, to create a 'Tree'.+treeFromIndex :: Index -> IO (Tree IO)+treeFromIndex EmptyIndex = return emptyTree+treeFromIndex index = do let initial = State { start = size_header , dirlength = 0 , path = anchoredRoot } res <- readItem index initial case treeitem res of Just (SubTree tree) -> return $ filter (predicate index) tree- _ -> fail "Unexpected failure in updateIndex!"+ _ -> fail "Unexpected failure in treeFromIndex!" -- | Check that a given file is an index file with a format we can handle. You -- should remove and re-create the index whenever this is not true.
src/Darcs/Util/Tree/Hashed.hs view
@@ -63,7 +63,7 @@ , updateSubtrees , updateTree )-import Darcs.Util.Tree.Monad (TreeIO, initialState, runTreeMonad)+import Darcs.Util.Tree.Monad (TreeIO, runTreeMonad) --------------------------------------------------------------------- -- Utilities for coping with the darcs directory format.@@ -237,7 +237,7 @@ -> FilePath -- ^ directory -> IO (a, Tree IO) hashedTreeIO action t dir =- runTreeMonad action $ initialState t darcsHash updateItem+ runTreeMonad action t darcsHash updateItem where updateItem _ (File b) = File <$> updateFile b updateItem _ (SubTree s) = SubTree <$> updateSub s updateItem _ x = return x
src/Darcs/Util/Tree/Monad.hs view
@@ -2,7 +2,7 @@ -- -- BSD3 --- | An experimental monadic interface to Tree mutation. The main idea is to+-- | A monadic interface to Tree mutation. The main idea is to -- simulate IO-ish manipulation of real filesystem (that's the state part of -- the monad), and to keep memory usage down by reasonably often dumping the -- intermediate data to disk and forgetting it. The monad interface itself is@@ -10,14 +10,27 @@ -- provides just 'virtualTreeIO' that never writes any changes, but may trigger -- filesystem reads as appropriate. module Darcs.Util.Tree.Monad- ( virtualTreeIO, virtualTreeMonad- , readFile, writeFile, createDirectory, rename, copy, unlink- , fileExists, directoryExists, exists, withDirectory- , currentDirectory- , tree, TreeState, TreeMonad, TreeIO, runTreeMonad- , initialState, replaceItem+ ( -- * 'TreeMonad'+ TreeMonad+ , TreeState(tree)+ , runTreeMonad+ , virtualTreeMonad+ -- * Specializing to 'IO'+ , TreeIO+ , virtualTreeIO+ -- * Read actions+ , readFile+ , exists+ , directoryExists+ , fileExists+ -- * Write actions+ , writeFile+ , createDirectory+ , unlink+ , rename+ , copy+ -- * Other actions , findM, findFileM, findTreeM- , TreeRO, TreeRW ) where import Darcs.Prelude hiding ( readFile, writeFile )@@ -35,112 +48,96 @@ import Control.Monad.RWS.Strict import qualified Data.Map as M +-- | Keep track of the size and age of changes to the tree. type Changed = M.Map AnchoredPath (Int64, Int64) -- size, age --- | Internal state of the 'TreeIO' monad. Keeps track of the current Tree--- content, unsync'd changes and a current working directory (of the monad).-data TreeState m = TreeState { tree :: !(Tree m)- , changed :: !Changed- , changesize :: !Int64- , maxage :: !Int64- , updateHash :: TreeItem m -> m Hash- , update :: AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m) }+-- | Internal state of the 'TreeMonad'. Keeps track of the current 'Tree'+-- content and unsync'd changes.+data TreeState m = TreeState+ { tree :: !(Tree m)+ , changed :: !Changed+ , changesize :: !Int64+ , maxage :: !Int64+ } --- | A 'TreeIO' monad. A sort of like IO but it keeps a 'TreeState' around as well,--- which is a sort of virtual filesystem. Depending on how you obtained your--- 'TreeIO', the actions in your virtual filesystem get somehow reflected in the--- actual real filesystem. For 'virtualTreeIO', nothing happens in real--- filesystem, however with 'plainTreeIO', the plain tree will be updated every--- now and then, and with 'hashedTreeIO' a darcs-style hashed tree will get--- updated.-type TreeMonad m = RWST AnchoredPath () (TreeState m) m-type TreeIO = TreeMonad IO+data TreeEnv m = TreeEnv+ { updateHash :: TreeItem m -> m Hash+ , update :: AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m)+ } -class Monad m => TreeRO m where- currentDirectory :: m AnchoredPath- withDirectory :: AnchoredPath -> m a -> m a- expandTo :: AnchoredPath -> m AnchoredPath- -- | Grab content of a file in the current Tree at the given path.- readFile :: AnchoredPath -> m BL.ByteString- -- | Check for existence of a node (file or directory, doesn't matter).- exists :: AnchoredPath -> m Bool- -- | Check for existence of a directory.- directoryExists ::AnchoredPath -> m Bool- -- | Check for existence of a file.- fileExists :: AnchoredPath -> m Bool+-- | A monad transformer that adds state of type 'TreeState' and an environment+-- of type 'AnchoredPath' (for the current directory).+type TreeMonad m = RWST (TreeEnv m) () (TreeState m) m -class TreeRO m => TreeRW m where- -- | Change content of a file at a given path. The change will be- -- eventually flushed to disk, but might be buffered for some time.- writeFile :: AnchoredPath -> BL.ByteString -> m ()- createDirectory :: AnchoredPath -> m ()- unlink :: AnchoredPath -> m ()- rename :: AnchoredPath -> AnchoredPath -> m ()- copy :: AnchoredPath -> AnchoredPath -> m ()+-- | 'TreeMonad' specialized to 'IO'+type TreeIO = TreeMonad IO -initialState :: Tree m- -> (TreeItem m -> m Hash)- -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m))- -> TreeState m-initialState t uh u = TreeState { tree = t- , changed = M.empty- , changesize = 0- , updateHash = uh- , maxage = 0- , update = u }+initialEnv :: (TreeItem m -> m Hash)+ -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m))+ -> TreeEnv m+initialEnv uh u = TreeEnv {updateHash = uh, update = u} -flush :: (Monad m) => TreeMonad m ()+initialState :: Tree m -> TreeState m+initialState t =+ TreeState {tree = t, changed = M.empty, changesize = 0, maxage = 0}++flush :: Monad m => TreeMonad m () flush = do changed' <- map fst . M.toList <$> gets changed dirs' <- gets tree >>= \t -> return [ path | (path, SubTree _) <- list t ] modify $ \st -> st { changed = M.empty, changesize = 0 } forM_ (changed' ++ dirs' ++ [AnchoredPath []]) flushItem -runTreeMonad' :: (Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m)-runTreeMonad' action initial = do- (out, final, _) <- runRWST action (AnchoredPath []) initial+runTreeMonad' :: Monad m => TreeMonad m a -> TreeEnv m -> TreeState m -> m (a, Tree m)+runTreeMonad' action initEnv initState = do+ (out, final, _) <- runRWST action initEnv initState return (out, tree final) -runTreeMonad :: (Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m)-runTreeMonad action initial = do+runTreeMonad :: Monad m+ => TreeMonad m a+ -> Tree m+ -> (TreeItem m -> m Hash)+ -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m))+ -> m (a, Tree m)+runTreeMonad action t uh u = do let action' = do x <- action flush return x- runTreeMonad' action' initial+ runTreeMonad' action' (initialEnv uh u) (initialState t) --- | Run a TreeIO action without storing any changes. This is useful for--- running monadic tree mutations for obtaining the resulting Tree (as opposed+-- | Run a 'TreeMonad' action without storing any changes. This is useful for+-- running monadic tree mutations for obtaining the resulting 'Tree' (as opposed -- to their effect of writing a modified tree to disk). The actions can do both -- read and write -- reads are passed through to the actual filesystem, but the--- writes are held in memory in a form of modified Tree.-virtualTreeMonad :: (Monad m) => TreeMonad m a -> Tree m -> m (a, Tree m)+-- writes are held in memory in the form of a modified 'Tree'.+virtualTreeMonad :: Monad m => TreeMonad m a -> Tree m -> m (a, Tree m) virtualTreeMonad action t =- runTreeMonad' action $ initialState t (\_ -> return NoHash) (\_ x -> return x)+ runTreeMonad action t (\_ -> return NoHash) (\_ x -> return x) +-- | 'virtualTreeMonad' specialized to 'IO' virtualTreeIO :: TreeIO a -> Tree IO -> IO (a, Tree IO) virtualTreeIO = virtualTreeMonad -- | Modifies an item in the current Tree. This action keeps an account of the -- modified data, in changed and changesize, for subsequent flush -- operations. Any modifications (as in "modifyTree") are allowed.-modifyItem :: (Monad m)+modifyItem :: Monad m => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m () modifyItem path item = do- path' <- (`catPaths` path) `fmap` currentDirectory age <- gets maxage changed' <- gets changed- let getsize (Just (File b)) = lift (BL.length `fmap` readBlob b)+ let getsize (Just (File b)) = lift (BL.length <$> readBlob b) getsize _ = return 0 size <- getsize item- let change = case M.lookup path' changed' of+ let change = case M.lookup path changed' of Nothing -> size Just (oldsize, _) -> size - oldsize - modify $ \st -> st { tree = modifyTree (tree st) path' item- , changed = M.insert path' (size, age) (changed st)+ modify $ \st -> st { tree = modifyTree (tree st) path item+ , changed = M.insert path (size, age) (changed st) , maxage = age + 1 , changesize = changesize st + change } -renameChanged :: (Monad m)+renameChanged :: Monad m => AnchoredPath -> AnchoredPath -> TreeMonad m () renameChanged from to = modify $ \st -> st {changed = rename' $ changed st} where@@ -156,38 +153,37 @@ -- 'sync' implementation for a particular storage format. The presumed use-case -- is that an existing in-memory Blob is replaced with a one referring to an -- on-disk file.-replaceItem :: (Monad m)+replaceItem :: Monad m => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m () replaceItem path item = do- path' <- (`catPaths` path) `fmap` currentDirectory- modify $ \st -> st { tree = modifyTree (tree st) path' item }+ modify $ \st -> st { tree = modifyTree (tree st) path item } -flushItem :: forall m. (Monad m) => AnchoredPath -> TreeMonad m ()+flushItem :: forall m. Monad m => AnchoredPath -> TreeMonad m () flushItem path = do current <- gets tree case find current path of Nothing -> return () -- vanished, do nothing Just x -> do y <- fixHash x- new <- gets update >>= ($ y) . ($ path)+ new <- asks update >>= ($ y) . ($ path) replaceItem path (Just new) where fixHash :: TreeItem m -> TreeMonad m (TreeItem m) fixHash f@(File (Blob con NoHash)) = do- hash <- gets updateHash >>= \x -> lift $ x f+ hash <- asks updateHash >>= \x -> lift $ x f return $ File $ Blob con hash fixHash (SubTree s) | treeHash s == NoHash =- gets updateHash >>= \f -> SubTree <$> lift (addMissingHashes f s)+ asks updateHash >>= \f -> SubTree <$> lift (addMissingHashes f s) fixHash x = return x -- | If buffers are becoming large, sync, otherwise do nothing.-flushSome :: (Monad m) => TreeMonad m ()+flushSome :: Monad m => TreeMonad m () flushSome = do x <- gets changesize when (x > megs 100) $ do remaining <- go =<< sortBy age . M.toList <$> gets changed modify $ \s -> s { changed = M.fromList remaining } where go [] = return [] go ((path, (size, _)):chs) = do- x <- (\s -> s - size) <$> gets changesize+ x <- subtract size <$> gets changesize flushItem path modify $ \s -> s { changesize = x } if x > megs 50 then go chs@@ -195,90 +191,105 @@ megs = (* (1024 * 1024)) age (_, (_, a)) (_, (_, b)) = compare a b -instance (Monad m) => TreeRO (TreeMonad m) where- expandTo p =- do t <- gets tree- p' <- (`catPaths` p) `fmap` ask- t' <- lift $ expandPath t p'- modify $ \st -> st { tree = t' }- return p'+-- read only actions - fileExists p =- do p' <- expandTo p- (isJust . (`findFile` p')) `fmap` gets tree+expandTo :: Monad m => AnchoredPath -> TreeMonad m ()+expandTo p =+ do t <- gets tree+ t' <- lift $ expandPath t p+ modify $ \st -> st { tree = t' } - directoryExists p =- do p' <- expandTo p- (isJust . (`findTree` p')) `fmap` gets tree+-- | Check for existence of a file.+fileExists :: Monad m => AnchoredPath -> TreeMonad m Bool+fileExists p =+ do expandTo p+ (isJust . (`findFile` p)) <$> gets tree - exists p =- do p' <- expandTo p- (isJust . (`find` p')) `fmap` gets tree+-- | Check for existence of a directory.+directoryExists :: Monad m => AnchoredPath -> TreeMonad m Bool+directoryExists p =+ do expandTo p+ (isJust . (`findTree` p)) <$> gets tree - readFile p =- do p' <- expandTo p- t <- gets tree- let f = findFile t p'- case f of- Nothing -> throw $ userError $ "No such file " ++ show p'- Just x -> lift (readBlob x)+-- | Check for existence of a node (file or directory, doesn't matter).+exists :: Monad m => AnchoredPath -> TreeMonad m Bool+exists p =+ do expandTo p+ isJust . (`find` p) <$> gets tree - currentDirectory = ask- withDirectory dir act = do- dir' <- expandTo dir- local (const dir') act+-- | Grab content of a file in the current Tree at the given path.+readFile :: Monad m => AnchoredPath -> TreeMonad m BL.ByteString+readFile p =+ do expandTo p+ t <- gets tree+ let f = findFile t p+ case f of+ Nothing -> throw $ userError $ "No such file " ++ displayPath p+ Just x -> lift (readBlob x) -instance (Monad m) => TreeRW (TreeMonad m) where- writeFile p con =- do _ <- expandTo p- modifyItem p (Just blob)- flushSome- where blob = File $ Blob (return con) hash- hash = NoHash -- we would like to say "sha256 con" here, but due- -- to strictness of Hash in Blob, this would often- -- lead to unnecessary computation which would then- -- be discarded anyway; we rely on the sync- -- implementation to fix up any NoHash occurrences+-- | Change content of a file at a given path. The change will be+-- eventually flushed to disk, but might be buffered for some time.+writeFile :: Monad m => AnchoredPath -> BL.ByteString -> TreeMonad m ()+writeFile p con =+ do expandTo p+ modifyItem p (Just blob)+ flushSome+ where blob = File $ Blob (return con) hash+ hash = NoHash -- we would like to say "sha256 con" here, but due+ -- to strictness of Hash in Blob, this would often+ -- lead to unnecessary computation which would then+ -- be discarded anyway; we rely on the sync+ -- implementation to fix up any NoHash occurrences - createDirectory p =- do _ <- expandTo p- modifyItem p $ Just $ SubTree emptyTree+-- | Create a directory.+createDirectory :: Monad m => AnchoredPath -> TreeMonad m ()+createDirectory p =+ do expandTo p+ modifyItem p $ Just $ SubTree emptyTree - unlink p =- do _ <- expandTo p- modifyItem p Nothing+-- | Remove the item at a path.+unlink :: Monad m => AnchoredPath -> TreeMonad m ()+unlink p =+ do expandTo p+ modifyItem p Nothing - rename from to =- do from' <- expandTo from- to' <- expandTo to- tr <- gets tree- let item = find tr from'- found_to = find tr to'- unless (isNothing found_to) $- throw $ userError $ "Error renaming: destination " ++ show to ++ " exists."- unless (isNothing item) $ do- modifyItem from Nothing- modifyItem to item- renameChanged from to+-- | Rename the item at a path.+rename :: Monad m => AnchoredPath -> AnchoredPath -> TreeMonad m ()+rename from to =+ do expandTo from+ expandTo to+ tr <- gets tree+ let item = find tr from+ found_to = find tr to+ unless (isNothing found_to) $+ throw $ userError $ "Error renaming: destination " ++ displayPath to ++ " exists."+ if isJust item then do+ modifyItem from Nothing+ modifyItem to item+ renameChanged from to+ else+ throw $ userError $ "Error renaming: source " ++ displayPath from ++ " does not exist." - copy from to =- do from' <- expandTo from- _ <- expandTo to- tr <- gets tree- let item = find tr from'- unless (isNothing item) $ modifyItem to item+-- | Copy an item from some path to another path.+copy :: Monad m => AnchoredPath -> AnchoredPath -> TreeMonad m ()+copy from to =+ do expandTo from+ expandTo to+ tr <- gets tree+ let item = find tr from+ unless (isNothing item) $ modifyItem to item -findM' :: forall m a . (Monad m)+findM' :: forall m a . Monad m => (Tree m -> AnchoredPath -> a) -> Tree m -> AnchoredPath -> m a findM' what t path = fst <$> virtualTreeMonad (look path) t where look :: AnchoredPath -> TreeMonad m a- look = expandTo >=> \p' -> flip what p' <$> gets tree+ look p = expandTo p >> flip what p <$> gets tree -findM :: (Monad m) => Tree m -> AnchoredPath -> m (Maybe (TreeItem m))+findM :: Monad m => Tree m -> AnchoredPath -> m (Maybe (TreeItem m)) findM = findM' find -findTreeM :: (Monad m) => Tree m -> AnchoredPath -> m (Maybe (Tree m))+findTreeM :: Monad m => Tree m -> AnchoredPath -> m (Maybe (Tree m)) findTreeM = findM' findTree -findFileM :: (Monad m) => Tree m -> AnchoredPath -> m (Maybe (Blob m))+findFileM :: Monad m => Tree m -> AnchoredPath -> m (Maybe (Blob m)) findFileM = findM' findFile
+ tests/broken_move.sh view
@@ -0,0 +1,117 @@+#!/usr/bin/env bash+# This tests that even if broken move patches (caused by issue2674)+# are present in a repo, we can safely eliminate them because no further+# patches can depend on the target of the move. In fact, applying such a+# bad move is a no-op.++. lib++# We could create three different archives for darcs-1, darcs-2, and darcs-3+# but since this problem concerns only the Prim.V1 layer this seems excessive.+only-format darcs-2++# Script to create the test repo. It is no longer used now that issue2674 has+# been fixed and we use the tar ball. Its only remaining purpose is to+# document what patches the broken_move repository consists of.+cat >create_broken_moves.sh <<EOF+ #!/usr/bin/env bash+ . lib++ rm -rf broken_move+ darcs init broken_move+ cd broken_move++ mkdir d1+ darcs record -l d1 -am 'add d1'++ touch f1 f2+ echo text > f1+ darcs move f1 f2 d1+ darcs record -am 'bad first move'++ # this commutes with the two bad moves+ darcs move d1 d2+ darcs record -am 'move d1 to d2'++ darcs move d2/f1 d2/f2 .+ darcs record -am 'bad second move'++ mkdir d3 d4+ darcs move d3 d4 d2+ darcs record -am 'bad third move'++ cd ..++ tar zcf broken_move.tgz broken_move+EOF++chmod +x create_broken_moves.sh++# if create_broken_moves.sh fails, then issue2674 has been fixed,+# so instead we unpack the repo from an archive instead+if ! ./create_broken_moves.sh; then+ unpack_testdata broken_move+fi++cd broken_move++# test that commutes either work or we can repair+echo y | darcs amend -a -p 'move d1 to d2' -m 'edited move d1 to d2'+if ! (echo y | darcs amend -a -p 'bad second move' -m 'edited bad second move' 2> LOG); then+ grep -i "Error renaming" LOG+ # but then we should be able to repair it+ not darcs check | grep 'Dropping move patch with non-existing source'+ rm -rf ../repaired+ darcs clone . ../repaired+ darcs repair --repodir=../repaired+fi+darcs obliterate -a -p 'move d1 to d2'+if ! (darcs obliterate -a -p 'bad second move' 2>LOG); then+ grep -i "Error renaming" LOG+ # but then we should be able to repair it+ not darcs check | grep 'Dropping move patch with non-existing source'+ rm -rf ../repaired+ darcs clone . ../repaired+ darcs repair --repodir=../repaired+fi+# test that unapplying patches either works or we can repair the repo+rm -rf ../S+if ! (darcs clone . ../S --to-patch 'add d1' 2>LOG); then+ grep -i "Error renaming" LOG+ # but then we should be able to repair it+ not darcs check | grep 'Dropping move patch with non-existing source'+ rm -rf ../repaired+ darcs clone . ../repaired+ darcs repair --repodir=../repaired+fi++# test that we cannot record a change that depends on the target path+# nor the source path (because both are unadded)+echo text > f1+not darcs record f1 -am impossible+echo text > d1/f1+not darcs record d1/f1 -am impossible++# same with a bad move of a directory+touch d1/d3/f+# this will ask us to add d1/d3, too, so we have to say no first+echo ny | darcs record -l d1/d3/f -m impossible >LOG+grep "you don't want to record anything" LOG++cd ..++# make a clone to get a fresh working tree equal to the pristine tree+rm -rf R+darcs clone broken_move R+cd R++# check that only d1 exists in pristine, i.e. the bad moves are ignored+cat <<EOF > log.expected+.+./d1+EOF+# the tr hack is to make the test work on Windows+darcs show files | tr -d $'\r' > log+diff log.expected log >&2++cd ..
+ tests/data/broken_move.tgz view
binary file changed (absent → 6840 bytes)
+ tests/data/rebase-0.0-conflict.tgz view
binary file changed (absent → 5822 bytes)
+ tests/data/rebase-0.0.tgz view
binary file changed (absent → 5666 bytes)
+ tests/data/rebase-0.2.tgz view
binary file changed (absent → 6179 bytes)
+ tests/issue2674-moving-unadded-files.sh view
@@ -0,0 +1,20 @@+. lib++rm -rf R+darcs init R+cd R+echo added >added.txt+echo unadded >unadded.txt+mkdir newdir+darcs add added.txt newdir/+not darcs mv added.txt unadded.txt newdir/ > LOG+not grep unadded LOG+# i.e. not this:+#Finished moving: ./added.txt ./unadded.txt to: ./newdir+darcs whatsnew -s > LOG+not grep unadded LOG+# i.e. not this:+# A ./newdir/+# ./unadded.txt -> ./newdir/unadded.txt+# A ./newdir/added.txt+cd ..
+ tests/rebase-0.0-compat.sh view
@@ -0,0 +1,54 @@+#!/usr/bin/env bash++## Test that we can upgrade rebase format 0.0 repos++. lib++rm -rf R log+unpack_testdata rebase-0.0++cd R+darcs rebase log 2>../log+grep 'Rebase in progress: 1 suspended patch' ../log++darcs rebase unsuspend -a++cat > file.expected << END+v v v v v v v+change 1+=============+change 2+*************+initial content+^ ^ ^ ^ ^ ^ ^+END++diff -u file.expected file++cd ..++# In format 0.0 it is legal to store a suspended patch containing a conflict,+# whereas the plan for future formats is to unwind the conflict.+# So make sure that old repos can be updated successfully.++rm -rf R log+unpack_testdata rebase-0.0-conflict++cd R+darcs rebase log 2>../log+grep 'Rebase in progress: 1 suspended patch' ../log++darcs rebase unsuspend -a+cat > file.expected << END+v v v v v v v+initial content+=============+content A+*************+content B+^ ^ ^ ^ ^ ^ ^+END++diff -u file.expected file++cd ..
+ tests/rebase-0.2-compat.sh view
@@ -0,0 +1,28 @@+#!/usr/bin/env bash++## Test that we can upgrade rebase format 0.2 repos++. lib++rm -rf R log+unpack_testdata rebase-0.2++cd R+darcs rebase log 2>../log+grep 'Rebase in progress: 1 suspended patch' ../log++darcs rebase unsuspend -a++cat > file.expected << END+v v v v v v v+change 1+=============+change 2+*************+initial content+^ ^ ^ ^ ^ ^ ^+END++diff -u file.expected file++cd ..
tests/rmdir.sh view
@@ -36,7 +36,7 @@ grep foo dirs cd .. -darcs pull -a --repodir=temp2 > pullresult+darcs pull -a --repodir=temp2 2> pullresult cat pullresult grep 'Warning: .ot deleting' pullresult
tests/trackdown-bisect.sh view
@@ -12,7 +12,8 @@ exit 0 fi -ghc -o trackdown-bisect-helper $TESTBIN/trackdown-bisect-helper.hs+cp $TESTBIN/trackdown-bisect-helper.hs .+ghc -o trackdown-bisect-helper trackdown-bisect-helper.hs function make_repo_with_test { rm -fr temp1