gitlib 1.2.1 → 1.3.1
raw patch · 2 files changed
+659/−146 lines, 2 filesdep +base16-bytestringdep +binarydep +hashablePVP ok
version bump matches the API change (PVP)
Dependencies added: base16-bytestring, binary, hashable, system-fileio, unordered-containers
API changes (from Hackage documentation)
- Git: allRefNames :: Repository m => m [Text]
- Git: allRefs :: Repository m => m [Reference m (Commit m)]
- Git: cloneTree :: Repository m => RepositoryTree m -> m (RepositoryTree m)
- Git: commitEntryRef :: TreeEntry m -> !(CommitOid m)
- Git: createRef :: Repository m => Text -> RefTarget m (Commit m) -> m (Reference m (Commit m))
- Git: createRef_ :: Repository m => Text -> RefTarget m (Commit m) -> m ()
- Git: data MutableTree
- Git: data PersistentTree
- Git: deleteRef :: Repository m => Text -> m ()
- Git: instance (Functor m, Monad m) => Applicative (TreeT t m)
- Git: instance (Functor m, MonadPlus m) => Alternative (TreeT t m)
- Git: instance Functor m => Functor (TreeT t m)
- Git: instance Monad m => Monad (TreeT t m)
- Git: instance MonadFix m => MonadFix (TreeT t m)
- Git: instance MonadIO m => MonadIO (TreeT t m)
- Git: instance MonadPlus m => MonadPlus (TreeT t m)
- Git: instance MonadTrans (TreeT t)
- Git: lookupRef :: Repository m => Text -> m (Maybe (Reference m (Commit m)))
- Git: newTree :: Repository m => m (RepositoryTree m)
- Git: refName :: Reference m a -> !Text
- Git: refTarget :: Reference m a -> !(RefTarget m a)
- Git: resolveRef :: Repository m => Text -> m (Maybe (CommitRef m))
- Git: treeEntryRef :: TreeEntry m -> !(TreeRef m)
- Git: type MutableTreeT m a = TreeT (Tree m MutableTree) m a
- Git: type PersistentTreeT m a = TreeT (Tree m PersistentTree) m a
- Git: type RepositoryTree m = Tree m (TreeKind m)
- Git: type RepositoryTreeT m a = TreeT (RepositoryTree m) m a
- Git: unsafeGetTree :: Monad m => RepositoryTreeT m (RepositoryTree m)
- Git: unsafeMutateTree :: Repository m => Tree m MutableTree -> MutableTreeT m a -> m (Tree m MutableTree)
- Git: unsafeMutateTree_ :: Repository m => Tree m MutableTree -> MutableTreeT m a -> m ()
- Git: unsafePutTree :: Monad m => RepositoryTree m -> RepositoryTreeT m ()
- Git: unsafeUpdateTree :: Repository m => RepositoryTree m -> FilePath -> Bool -> (Maybe (TreeEntry m) -> ModifyTreeResult m) -> m (RepositoryTree m, Maybe (TreeEntry m))
- Git: unsafeWithTree :: Repository m => Tree m MutableTree -> MutableTreeT m a -> m (a, Tree m MutableTree)
- Git: updateRef :: Repository m => Text -> RefTarget m (Commit m) -> m (Reference m (Commit m))
- Git: updateRef_ :: Repository m => Text -> RefTarget m (Commit m) -> m ()
- Git: writeTree :: Repository m => RepositoryTree m -> m (TreeOid m)
+ Git: BuilderUnchanged :: (TreeBuilder m) -> ModifiedBuilder m
+ Git: ModifiedBuilder :: (TreeBuilder m) -> ModifiedBuilder m
+ Git: PinnedEntry :: Oid m -> Commit m -> TreeEntry m -> PinnedEntry m
+ Git: SHA :: ByteString -> SHA
+ Git: TreeBuilder :: Maybe (TreeRef m) -> HashMap Text (TreeBuilder m) -> (Maybe (Tree m) -> m (TreeBuilder m)) -> (TreeBuilder m -> m (ModifiedBuilder m, TreeRef m)) -> (Text -> m (Maybe (TreeEntry m))) -> m Int -> (TreeBuilder m -> Text -> TreeEntry m -> m (ModifiedBuilder m)) -> (TreeBuilder m -> Text -> m (ModifiedBuilder m)) -> TreeBuilder m
+ Git: allMissingObjects :: Repository m => [Object m] -> m [Object m]
+ Git: allReferenceNames :: Repository m => m [Text]
+ Git: allReferences :: Repository m => m [Reference m (Commit m)]
+ Git: blobContentsToByteString :: Repository m => BlobContents m -> m ByteString
+ Git: blobToByteString :: Repository m => Blob m -> m ByteString
+ Git: catBlob :: Repository m => Text -> m ByteString
+ Git: catBlobUtf8 :: Repository m => Text -> m Text
+ Git: commitEntryHistory :: Repository m => Commit m -> FilePath -> m [PinnedEntry m]
+ Git: commitEntryOid :: TreeEntry m -> !(CommitOid m)
+ Git: commitHistoryFirstParent :: Repository m => Commit m -> m [Commit m]
+ Git: commitTreeEntry :: Repository m => Commit m -> FilePath -> m (Maybe (TreeEntry m))
+ Git: copyBlob :: (Repository m, Repository (t m), MonadTrans t) => BlobRef m -> HashSet Text -> t m (BlobOid (t m), HashSet Text)
+ Git: copyCommit :: (Repository m, Repository (t m), MonadTrans t) => CommitRef m -> Maybe Text -> HashSet Text -> t m (CommitRef (t m), HashSet Text)
+ Git: copyTree :: (Repository m, Repository (t m), MonadTrans t) => TreeRef m -> HashSet Text -> t m (TreeRef (t m), HashSet Text)
+ Git: copyTreeEntry :: (Repository m, Repository (t m), MonadTrans t) => TreeEntry m -> HashSet Text -> t m (TreeEntry (t m), HashSet Text)
+ Git: createBlobUtf8 :: Repository m => Text -> m (BlobOid m)
+ Git: createReference :: Repository m => Text -> RefTarget m (Commit m) -> m (Reference m (Commit m))
+ Git: createReference_ :: Repository m => Text -> RefTarget m (Commit m) -> m ()
+ Git: currentTree :: Repository m => TreeT m (Tree m)
+ Git: currentTreeRef :: Repository m => TreeT m (TreeRef m)
+ Git: data ModifiedBuilder m
+ Git: data PinnedEntry m
+ Git: data TreeBuilder m
+ Git: deleteReference :: Repository m => Text -> m ()
+ Git: emptyTreeId :: Text
+ Git: genericPushCommit :: (Repository m, Repository (t m), MonadTrans t) => CommitName m -> Text -> t m (CommitRef (t m))
+ Git: getCommitParents :: Repository m => Commit m -> m [Commit m]
+ Git: getTreeEntryOid :: Repository m => TreeEntry m -> Oid m
+ Git: identifyEntry :: Repository m => Commit m -> TreeEntry m -> m (PinnedEntry m)
+ Git: instance (Functor m, Monad m) => Applicative (TreeT m)
+ Git: instance (Functor m, MonadPlus m) => Alternative (TreeT m)
+ Git: instance Binary SHA
+ Git: instance Eq BuilderAction
+ Git: instance Eq SHA
+ Git: instance Functor m => Functor (TreeT m)
+ Git: instance Hashable SHA
+ Git: instance IsOid SHA
+ Git: instance Monad m => Monad (TreeT m)
+ Git: instance Monad m => Monoid (TreeBuilder m)
+ Git: instance MonadFix m => MonadFix (TreeT m)
+ Git: instance MonadIO m => MonadIO (TreeT m)
+ Git: instance MonadPlus m => MonadPlus (TreeT m)
+ Git: instance MonadTrans TreeT
+ Git: instance Monoid (ModifiedBuilder m)
+ Git: instance Ord SHA
+ Git: instance Read SHA
+ Git: instance Show BuilderAction
+ Git: instance Show SHA
+ Git: lookupReference :: Repository m => Text -> m (Maybe (Reference m (Commit m)))
+ Git: mtbBaseTreeRef :: TreeBuilder m -> Maybe (TreeRef m)
+ Git: mtbDropEntry :: TreeBuilder m -> TreeBuilder m -> Text -> m (ModifiedBuilder m)
+ Git: mtbEntryCount :: TreeBuilder m -> m Int
+ Git: mtbLookupEntry :: TreeBuilder m -> Text -> m (Maybe (TreeEntry m))
+ Git: mtbNewBuilder :: TreeBuilder m -> Maybe (Tree m) -> m (TreeBuilder m)
+ Git: mtbPendingUpdates :: TreeBuilder m -> HashMap Text (TreeBuilder m)
+ Git: mtbPutEntry :: TreeBuilder m -> TreeBuilder m -> Text -> TreeEntry m -> m (ModifiedBuilder m)
+ Git: mtbWriteContents :: TreeBuilder m -> TreeBuilder m -> m (ModifiedBuilder m, TreeRef m)
+ Git: mutateTreeRef :: Repository m => TreeRef m -> TreeT m a -> m (TreeRef m)
+ Git: newTreeBuilder :: Repository m => Maybe (Tree m) -> m (TreeBuilder m)
+ Git: newtype SHA
+ Git: pinnedCommit :: PinnedEntry m -> Commit m
+ Git: pinnedEntry :: PinnedEntry m -> TreeEntry m
+ Git: pinnedOid :: PinnedEntry m -> Oid m
+ Git: referenceName :: Reference m a -> !Text
+ Git: referenceTarget :: Reference m a -> !(RefTarget m a)
+ Git: resolveReference :: Repository m => Text -> m (Maybe (CommitRef m))
+ Git: resolveReferenceTree :: Repository m => Text -> m (Maybe (Tree m))
+ Git: shaToText :: SHA -> Text
+ Git: textToSha :: Monad m => Text -> m SHA
+ Git: treeBlobEntries :: Repository m => Tree m -> m [(FilePath, TreeEntry m)]
+ Git: treeOid :: Repository m => Tree m -> TreeOid m
+ Git: updateReference :: Repository m => Text -> RefTarget m (Commit m) -> m (Reference m (Commit m))
+ Git: updateReference_ :: Repository m => Text -> RefTarget m (Commit m) -> m ()
+ Git: withNewRepository :: (Repository (t m), MonadGit (t m), MonadBaseControl IO m, MonadIO m, MonadTrans t) => RepositoryFactory t m c -> FilePath -> t m a -> m a
+ Git: withNewRepository' :: (Repository (t m), MonadGit (t m), MonadBaseControl IO m, MonadIO m, MonadTrans t) => RepositoryFactory t m c -> FilePath -> t m a -> m a
+ Git: withTreeRef :: Repository m => TreeRef m -> TreeT m a -> m (a, TreeRef m)
- Git: TreeEntry :: !(TreeRef m) -> TreeEntry m
+ Git: TreeEntry :: !(TreeOid m) -> TreeEntry m
- Git: class (Eq o, Ord o, Show o) => IsOid o where renderOid = renderObjOid . Tagged renderObjOid = renderOid . unTagged
+ Git: class (Eq o, Ord o, Show o) => IsOid o where renderOid = renderObjOid . Tagged renderObjOid = renderOid . untag
- Git: class (Applicative m, Monad m, Failure GitException m, IsOid (Oid m)) => Repository m where type family Oid m :: * type family TreeKind m :: * type family Tree m :: * -> * data family Options m parseObjOid sha = Tagged <$> parseOid sha createRef_ = (void .) . createRef updateRef_ = (void .) . updateRef allRefs = catMaybes <$> (mapM lookupRef =<< allRefNames) allRefNames = map refName <$> allRefs resolveRef name = lookupRef name >>= referenceToRef (Just name) traverseObjects_ = (void .) . traverseObjects traverseCommits_ = (void .) . traverseCommits traverseEntries_ = (void .) . traverseEntries buildPackFile _ _ = failure (BackendError "Backend does not support building pack files") buildPackIndex _ _ = failure (BackendError "Backend does not support building pack indexes") writePackFile _ = failure (BackendError "Backend does not support writing pack files")
+ Git: class (Applicative m, Monad m, Failure GitException m, IsOid (Oid m)) => Repository m where type family Oid m :: * data family Tree m :: * data family Options m :: * parseObjOid sha = Tagged <$> parseOid sha createReference_ = (void .) . createReference updateReference_ = (void .) . updateReference allReferences = catMaybes <$> (mapM lookupReference =<< allReferenceNames) allReferenceNames = map referenceName <$> allReferences resolveReference name = lookupReference name >>= referenceToRef (Just name) traverseObjects_ = (void .) . traverseObjects traverseCommits_ = (void .) . traverseCommits traverseEntries_ = (void .) . traverseEntries buildPackFile _ _ = failure (BackendError "Backend does not support building pack files") buildPackIndex _ _ = failure (BackendError "Backend does not support building pack indexes") writePackFile _ = failure (BackendError "Backend does not support writing pack files")
- Git: copyOid :: (Repository m, MonadGit m, Repository n, MonadGit n) => Oid m -> n (Oid n)
+ Git: copyOid :: (Repository m, Repository n) => Oid m -> n (Oid n)
- Git: createTree :: Repository m => RepositoryTreeT m a -> m (RepositoryTree m)
+ Git: createTree :: Repository m => TreeT m a -> m (TreeRef m)
- Git: data TreeT t m a
+ Git: data TreeT m a
- Git: dropEntry :: Repository m => FilePath -> RepositoryTreeT m ()
+ Git: dropEntry :: Repository m => FilePath -> TreeT m ()
- Git: getEntry :: Repository m => FilePath -> RepositoryTreeT m (Maybe (TreeEntry m))
+ Git: getEntry :: Repository m => FilePath -> TreeT m (Maybe (TreeEntry m))
- Git: getTreeEntry :: Repository m => RepositoryTree m -> FilePath -> m (Maybe (TreeEntry m))
+ Git: getTreeEntry :: Repository m => Tree m -> FilePath -> m (Maybe (TreeEntry m))
- Git: lookupTree :: Repository m => TreeOid m -> m (RepositoryTree m)
+ Git: lookupTree :: Repository m => TreeOid m -> m (Tree m)
- Git: mutateTree :: Repository m => RepositoryTree m -> RepositoryTreeT m a -> m (RepositoryTree m)
+ Git: mutateTree :: Repository m => Tree m -> TreeT m a -> m (TreeRef m)
- Git: putBlob :: Repository m => FilePath -> BlobOid m -> RepositoryTreeT m ()
+ Git: putBlob :: Repository m => FilePath -> BlobOid m -> TreeT m ()
- Git: putBlob' :: Repository m => FilePath -> BlobOid m -> BlobKind -> RepositoryTreeT m ()
+ Git: putBlob' :: Repository m => FilePath -> BlobOid m -> BlobKind -> TreeT m ()
- Git: putCommit :: Repository m => FilePath -> CommitOid m -> RepositoryTreeT m ()
+ Git: putCommit :: Repository m => FilePath -> CommitOid m -> TreeT m ()
- Git: putEntry :: Repository m => FilePath -> TreeEntry m -> RepositoryTreeT m ()
+ Git: putEntry :: Repository m => FilePath -> TreeEntry m -> TreeT m ()
- Git: putTree :: Repository m => FilePath -> TreeRef m -> RepositoryTreeT m ()
+ Git: putTree :: Repository m => FilePath -> TreeOid m -> TreeT m ()
- Git: resolveTreeRef :: Repository m => TreeRef m -> m (RepositoryTree m)
+ Git: resolveTreeRef :: Repository m => TreeRef m -> m (Tree m)
- Git: traverseEntries :: Repository m => (FilePath -> TreeEntry m -> m a) -> RepositoryTree m -> m [a]
+ Git: traverseEntries :: Repository m => (FilePath -> TreeEntry m -> m a) -> Tree m -> m [a]
- Git: traverseEntries_ :: Repository m => (FilePath -> TreeEntry m -> m a) -> RepositoryTree m -> m ()
+ Git: traverseEntries_ :: Repository m => (FilePath -> TreeEntry m -> m a) -> Tree m -> m ()
- Git: treeEntry :: Repository m => RepositoryTree m -> TreeEntry m
+ Git: treeEntry :: Repository m => Tree m -> TreeEntry m
- Git: treeEntryOid :: Repository m => TreeEntry m -> m (Oid m)
+ Git: treeEntryOid :: TreeEntry m -> !(TreeOid m)
- Git: treeRef :: RepositoryTree m -> TreeRef m
+ Git: treeRef :: Tree m -> TreeRef m
- Git: treeRefOid :: Repository m => TreeRef m -> m (TreeOid m)
+ Git: treeRefOid :: Repository m => TreeRef m -> TreeOid m
- Git: type TreeOid m = Tagged (RepositoryTree m) (Oid m)
+ Git: type TreeOid m = Tagged (Tree m) (Oid m)
- Git: type TreeRef m = ObjRef m (RepositoryTree m)
+ Git: type TreeRef m = ObjRef m (Tree m)
- Git: withNewTree :: Repository m => RepositoryTreeT m a -> m (a, RepositoryTree m)
+ Git: withNewTree :: Repository m => TreeT m a -> m (a, TreeRef m)
- Git: withTree :: Repository m => RepositoryTree m -> RepositoryTreeT m a -> m (a, RepositoryTree m)
+ Git: withTree :: Repository m => Tree m -> TreeT m a -> m (a, TreeRef m)
Files
- Git.hs +652/−144
- gitlib.cabal +7/−2
Git.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ViewPatterns #-} -- | Interface for working with Git repositories. module Git@@ -24,6 +25,9 @@ , Object(..) , ObjRef(..) , objectOid+ , SHA(..)+ , textToSha+ , shaToText , Blob(..) , BlobOid@@ -34,29 +38,29 @@ , blobEntry , blobRefOid , resolveBlobRef+ , createBlobUtf8+ , catBlob+ , catBlobUtf8+ , copyBlob+ , blobContentsToByteString+ , blobToByteString , TreeT- , RepositoryTree- , RepositoryTreeT- , MutableTree- , MutableTreeT- , PersistentTree- , PersistentTreeT+ , TreeBuilder(..)+ , ModifiedBuilder(..) , TreeEntry(..) , TreeOid , TreeRef , createTree , withNewTree , mutateTree+ , mutateTreeRef+ , currentTree+ , currentTreeRef , withTree- , unsafeMutateTree- , unsafeMutateTree_- , unsafeWithTree- , unsafeGetTree- , unsafePutTree+ , withTreeRef , dropEntry , getEntry- , getTreeEntry , putBlob , putBlob' , putCommit@@ -64,12 +68,16 @@ , putTree , resolveTreeRef , treeEntry- , treeEntryOid+ , getTreeEntryOid , treeRef , treeRefOid , ModifyTreeResult(..) , fromModifyTreeResult , toModifyTreeResult+ , emptyTreeId+ , treeBlobEntries+ , copyTreeEntry+ , copyTree , Commit(..) , CommitOid@@ -81,12 +89,21 @@ , commitRef , commitRefOid , commitRefTarget+ , getCommitParents , copyCommitName , copyCommitOid , nameOfCommit , renderCommitName , resolveCommitRef+ , commitTreeEntry+ , copyCommit+ , genericPushCommit + , PinnedEntry(..)+ , commitHistoryFirstParent+ , commitEntryHistory+ , identifyEntry+ , Tag(..) , TagOid , TagRef@@ -95,6 +112,7 @@ , RefTarget(..) , Reference(..) , referenceToRef+ , resolveReferenceTree , GitException(..) , ModificationKind(..)@@ -102,6 +120,9 @@ , MergeResult(..) , mergeStatus , copyConflict+ , allMissingObjects+ , withNewRepository+ , withNewRepository' ) where import Control.Applicative@@ -112,17 +133,32 @@ import Control.Monad.IO.Class import Control.Monad.Trans.Class import Control.Monad.Trans.State+import qualified Data.Binary as Bin import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Base16 as B16 import Data.Conduit+import qualified Data.Conduit.List as CList import Data.Default+import Data.Function+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap+import Data.HashSet (HashSet)+import qualified Data.HashSet as HashSet+import Data.Hashable+import Data.List import Data.Map (Map) import Data.Maybe+import Data.Monoid import Data.Tagged import Data.Text (Text) import qualified Data.Text as T+import qualified Data.Text.Encoding as T import Data.Time+import Data.Traversable hiding (mapM, forM, sequence) import Data.Typeable-import Filesystem.Path.CurrentOS+import Filesystem (removeTree, isDirectory)+import Filesystem.Path.CurrentOS hiding (null, concat) import Prelude hiding (FilePath) import System.Mem (performGC) @@ -138,17 +174,15 @@ renderOid :: o -> Text renderOid = renderObjOid . Tagged renderObjOid :: Tagged a o -> Text- renderObjOid = renderOid . unTagged+ renderObjOid = renderOid . untag -- | 'Repository' is the central point of contact between user code and -- Git data objects. Every object must belong to some repository. class (Applicative m, Monad m, Failure GitException m, IsOid (Oid m)) => Repository m where- type Oid m :: *- type TreeKind m :: *- type Tree m :: * -> *-- data Options m+ type Oid m :: *+ data Tree m :: *+ data Options m :: * facts :: m RepositoryFacts @@ -157,27 +191,29 @@ parseObjOid sha = Tagged <$> parseOid sha -- References- createRef :: Text -> RefTarget m (Commit m) -> m (Reference m (Commit m))- createRef_ :: Text -> RefTarget m (Commit m) -> m ()- createRef_ = (void .) . createRef- lookupRef :: Text -> m (Maybe (Reference m (Commit m)))- updateRef :: Text -> RefTarget m (Commit m) -> m (Reference m (Commit m))- updateRef_ :: Text -> RefTarget m (Commit m) -> m ()- updateRef_ = (void .) . updateRef- deleteRef :: Text -> m ()+ createReference :: Text -> RefTarget m (Commit m)+ -> m (Reference m (Commit m))+ createReference_ :: Text -> RefTarget m (Commit m) -> m ()+ createReference_ = (void .) . createReference+ lookupReference :: Text -> m (Maybe (Reference m (Commit m)))+ updateReference :: Text -> RefTarget m (Commit m)+ -> m (Reference m (Commit m))+ updateReference_ :: Text -> RefTarget m (Commit m) -> m ()+ updateReference_ = (void .) . updateReference+ deleteReference :: Text -> m () - allRefs :: m [Reference m (Commit m)]- allRefs = catMaybes <$> (mapM lookupRef =<< allRefNames)+ allReferences :: m [Reference m (Commit m)]+ allReferences = catMaybes <$> (mapM lookupReference =<< allReferenceNames) - allRefNames :: m [Text]- allRefNames = map refName <$> allRefs+ allReferenceNames :: m [Text]+ allReferenceNames = map referenceName <$> allReferences - resolveRef :: Text -> m (Maybe (CommitRef m))- resolveRef name = lookupRef name >>= referenceToRef (Just name)+ resolveReference :: Text -> m (Maybe (CommitRef m))+ resolveReference name = lookupReference name >>= referenceToRef (Just name) -- Lookup lookupCommit :: CommitOid m -> m (Commit m)- lookupTree :: TreeOid m -> m (RepositoryTree m)+ lookupTree :: TreeOid m -> m (Tree m) lookupBlob :: BlobOid m -> m (Blob m) lookupTag :: TagOid m -> m (Tag m) @@ -204,19 +240,13 @@ -> m [Object m] -- ^ All the objects in between -- Object creation- newTree :: m (RepositoryTree m)- cloneTree :: RepositoryTree m -> m (RepositoryTree m)- traverseEntries :: (FilePath -> TreeEntry m -> m a) -> RepositoryTree m -> m [a]- traverseEntries_ :: (FilePath -> TreeEntry m -> m a) -> RepositoryTree m -> m ()- traverseEntries_ = (void .) . traverseEntries- writeTree :: RepositoryTree m -> m (TreeOid m)+ newTreeBuilder :: Maybe (Tree m) -> m (TreeBuilder m) - unsafeUpdateTree- :: RepositoryTree m- -> FilePath -- path within the tree- -> Bool -- create subtree's leading up to path?- -> (Maybe (TreeEntry m) -> ModifyTreeResult m)- -> m (RepositoryTree m, Maybe (TreeEntry m))+ treeOid :: Tree m -> TreeOid m+ getTreeEntry :: Tree m -> FilePath -> m (Maybe (TreeEntry m))+ traverseEntries :: (FilePath -> TreeEntry m -> m a) -> Tree m -> m [a]+ traverseEntries_ :: (FilePath -> TreeEntry m -> m a) -> Tree m -> m ()+ traverseEntries_ = (void .) . traverseEntries hashContents :: BlobContents m -> m (BlobOid m) createBlob :: BlobContents m -> m (BlobOid m)@@ -289,7 +319,7 @@ {- $oids -} type BlobOid m = Tagged (Blob m) (Oid m)-type TreeOid m = Tagged (RepositoryTree m) (Oid m)+type TreeOid m = Tagged (Tree m) (Oid m) type CommitOid m = Tagged (Commit m) (Oid m) type TagOid m = Tagged (Tag m) (Oid m) @@ -297,8 +327,8 @@ data RefTarget m a = RefObj !(ObjRef m a) | RefSymbolic !Text data Reference m a = Reference- { refName :: !Text- , refTarget :: !(RefTarget m a) }+ { referenceName :: !Text+ , referenceTarget :: !(RefTarget m a) } data CommitName m = CommitObjectId !(CommitOid m) | CommitRefName !Text@@ -307,23 +337,22 @@ instance Repository m => Show (CommitName m) where show (CommitObjectId coid) = T.unpack (renderObjOid coid) show (CommitRefName name) = show name- show (CommitReference ref) = show (refName ref)+ show (CommitReference ref) = show (referenceName ref) nameOfCommit :: Commit m -> CommitName m nameOfCommit = CommitObjectId . commitOid commitNameToRef :: Repository m => CommitName m -> m (Maybe (CommitRef m)) commitNameToRef (CommitObjectId coid) = return (Just (ByOid coid))-commitNameToRef (CommitRefName name) = resolveRef name+commitNameToRef (CommitRefName name) = resolveReference name commitNameToRef (CommitReference ref) = referenceToRef Nothing (Just ref) renderCommitName :: Repository m => CommitName m -> Text renderCommitName (CommitObjectId coid) = renderObjOid coid renderCommitName (CommitRefName name) = name-renderCommitName (CommitReference ref) = refName ref+renderCommitName (CommitReference ref) = referenceName ref -copyOid :: (Repository m, MonadGit m, Repository n, MonadGit n)- => Oid m -> n (Oid n)+copyOid :: (Repository m, Repository n) => Oid m -> n (Oid n) copyOid = parseOid . renderOid copyCommitOid :: (Repository m, MonadGit m, Repository n, MonadGit n)@@ -338,13 +367,13 @@ Just . CommitObjectId . Tagged <$> parseOid (renderObjOid coid) copyCommitName (CommitRefName name) = return (Just (CommitRefName name)) copyCommitName (CommitReference ref) =- fmap CommitReference <$> lookupRef (refName ref)+ fmap CommitReference <$> lookupReference (referenceName ref) {- $objects -} data ObjRef m a = ByOid !(Tagged a (Oid m)) | Known !a type BlobRef m = ObjRef m (Blob m)-type TreeRef m = ObjRef m (RepositoryTree m)+type TreeRef m = ObjRef m (Tree m) type CommitRef m = ObjRef m (Commit m) type TagRef m = ObjRef m (Tag m) @@ -354,10 +383,10 @@ | TagObj !(TagRef m) objectOid :: Repository m => Object m -> m (Oid m)-objectOid (BlobObj ref) = return . unTagged $ blobRefOid ref-objectOid (TreeObj ref) = unTagged <$> treeRefOid ref-objectOid (CommitObj ref) = return . unTagged $ commitRefOid ref-objectOid (TagObj ref) = return . unTagged $ tagRefOid ref+objectOid (BlobObj ref) = return . untag $ blobRefOid ref+objectOid (TreeObj ref) = return . untag $ treeRefOid ref+objectOid (CommitObj ref) = return . untag $ commitRefOid ref+objectOid (TagObj ref) = return . untag $ tagRefOid ref {- $blobs -} data Blob m = Blob { blobOid :: !(BlobOid m)@@ -385,133 +414,128 @@ _ == _ = False {- $trees -}-newtype TreeT t m a = TreeT { runTreeT :: StateT t m a }--data MutableTree-type MutableTreeT m a = TreeT (Tree m MutableTree) m a--data PersistentTree-type PersistentTreeT m a = TreeT (Tree m PersistentTree) m a--type RepositoryTree m = Tree m (TreeKind m)-type RepositoryTreeT m a = TreeT (RepositoryTree m) m a+newtype TreeT m a = TreeT { runTreeT :: StateT (TreeBuilder m) m a } -instance Functor m => Functor (TreeT t m) where+instance Functor m => Functor (TreeT m) where fmap f (TreeT t) = TreeT (fmap f t) -instance Monad m => Monad (TreeT t m) where+instance Monad m => Monad (TreeT m) where return x = TreeT (return x) TreeT x >>= f = TreeT (x >>= runTreeT . f) -instance (Functor m, Monad m) => Applicative (TreeT t m) where+instance (Functor m, Monad m) => Applicative (TreeT m) where pure = return (<*>) = ap -instance (Functor m, MonadPlus m) => Alternative (TreeT t m) where+instance (Functor m, MonadPlus m) => Alternative (TreeT m) where empty = mzero (<|>) = mplus -instance (MonadPlus m) => MonadPlus (TreeT t m) where+instance (MonadPlus m) => MonadPlus (TreeT m) where mzero = TreeT $ mzero m `mplus` n = TreeT $ runTreeT m `mplus` runTreeT n -instance (MonadFix m) => MonadFix (TreeT t m) where+instance (MonadFix m) => MonadFix (TreeT m) where mfix f = TreeT $ mfix $ \ ~a -> runTreeT (f a) -instance MonadTrans (TreeT t) where+instance MonadTrans TreeT where lift m = TreeT $ lift m -instance (MonadIO m) => MonadIO (TreeT t m) where+instance (MonadIO m) => MonadIO (TreeT m) where liftIO = lift . liftIO -unsafeGetTree :: Monad m => RepositoryTreeT m (RepositoryTree m)-unsafeGetTree = TreeT get+getBuilder :: Monad m => TreeT m (TreeBuilder m)+getBuilder = TreeT get -unsafePutTree :: Monad m => RepositoryTree m -> RepositoryTreeT m ()-unsafePutTree = TreeT . put+putBuilder :: Monad m => TreeBuilder m -> TreeT m ()+putBuilder = TreeT . put -getEntry :: Repository m => FilePath -> RepositoryTreeT m (Maybe (TreeEntry m))+data BuilderAction = GetEntry | PutEntry | DropEntry+ deriving (Eq, Show)++getEntry :: Repository m => FilePath -> TreeT m (Maybe (TreeEntry m)) getEntry path = do- tr <- unsafeGetTree- snd <$> lift (unsafeUpdateTree tr path False+ tb <- getBuilder+ snd <$> lift (queryTreeBuilder tb path GetEntry (toModifyTreeResult TreeEntryPersistent)) -getTreeEntry :: Repository m => RepositoryTree m -> FilePath -> m (Maybe (TreeEntry m))-getTreeEntry tree path =- fst <$> (flip runStateT tree $ runTreeT (getEntry path))--putEntry :: Repository m => FilePath -> TreeEntry m -> RepositoryTreeT m ()+putEntry :: Repository m => FilePath -> TreeEntry m -> TreeT m () putEntry path ent = do- tr <- unsafeGetTree- tr' <- fst <$> lift (unsafeUpdateTree tr path True+ tb <- getBuilder+ tb' <- fst <$> lift (queryTreeBuilder tb path PutEntry (const (TreeEntryMutated ent)))- unsafePutTree tr'+ putBuilder tb' -dropEntry :: Repository m => FilePath -> RepositoryTreeT m ()+dropEntry :: Repository m => FilePath -> TreeT m () dropEntry path = do- tr <- unsafeGetTree- tr' <- fst <$> lift (unsafeUpdateTree tr path False+ tb <- getBuilder+ tb' <- fst <$> lift (queryTreeBuilder tb path DropEntry (const TreeEntryDeleted))- unsafePutTree tr'+ putBuilder tb' -putBlob' :: Repository m => FilePath -> BlobOid m -> BlobKind -> RepositoryTreeT m ()+putBlob' :: Repository m => FilePath -> BlobOid m -> BlobKind -> TreeT m () putBlob' path b kind = putEntry path (BlobEntry b kind) -putBlob :: Repository m => FilePath -> BlobOid m -> RepositoryTreeT m ()+putBlob :: Repository m => FilePath -> BlobOid m -> TreeT m () putBlob path b = putBlob' path b PlainBlob -putTree :: Repository m => FilePath -> TreeRef m -> RepositoryTreeT m ()-putTree path ent = putEntry path (TreeEntry ent)+putTree :: Repository m => FilePath -> TreeOid m -> TreeT m ()+putTree path t = putEntry path (TreeEntry t) -putCommit :: Repository m => FilePath -> CommitOid m -> RepositoryTreeT m ()+putCommit :: Repository m => FilePath -> CommitOid m -> TreeT m () putCommit path c = putEntry path (CommitEntry c) -withNewTree :: Repository m => RepositoryTreeT m a -> m (a, RepositoryTree m)-withNewTree (TreeT action) = runStateT action =<< newTree+doWithTree :: Repository m => Maybe (Tree m) -> TreeT m a -> m (a, TreeRef m)+doWithTree mtr act =+ fst <$> (runStateT (runTreeT go) =<< newTreeBuilder mtr)+ where+ go = liftM2 (,) act currentTreeRef -createTree :: Repository m => RepositoryTreeT m a -> m (RepositoryTree m)-createTree (TreeT action) = execStateT action =<< newTree+withTree :: Repository m => Tree m -> TreeT m a -> m (a, TreeRef m)+withTree tr = doWithTree (Just tr) -withTree :: Repository m- => RepositoryTree m- -> RepositoryTreeT m a- -> m (a, RepositoryTree m)-withTree tr (TreeT action) = runStateT action =<< cloneTree tr+withTreeRef :: Repository m => TreeRef m -> TreeT m a -> m (a, TreeRef m)+withTreeRef ref action = do+ tree <- resolveTreeRef ref+ doWithTree (Just tree) action -unsafeWithTree :: Repository m- => Tree m MutableTree -> MutableTreeT m a- -> m (a, Tree m MutableTree)-unsafeWithTree tr (TreeT action) = runStateT action tr+mutateTree :: Repository m => Tree m -> TreeT m a -> m (TreeRef m)+mutateTree tr action = snd <$> withTree tr action -mutateTree :: Repository m- => RepositoryTree m -> RepositoryTreeT m a- -> m (RepositoryTree m)-mutateTree tr (TreeT action) = execStateT action =<< cloneTree tr+mutateTreeRef :: Repository m => TreeRef m -> TreeT m a -> m (TreeRef m)+mutateTreeRef tr action = snd <$> withTreeRef tr action -unsafeMutateTree :: Repository m- => Tree m MutableTree -> MutableTreeT m a- -> m (Tree m MutableTree)-unsafeMutateTree tr (TreeT action) = execStateT action tr+currentTreeRef :: Repository m => TreeT m (TreeRef m)+currentTreeRef = do+ tb <- getBuilder+ (tb', tref) <- lift $ writeTreeBuilder tb+ putBuilder tb'+ return tref -unsafeMutateTree_ :: Repository m- => Tree m MutableTree -> MutableTreeT m a -> m ()-unsafeMutateTree_ = (void .) . unsafeMutateTree+currentTree :: Repository m => TreeT m (Tree m)+currentTree = lift . resolveTreeRef =<< currentTreeRef +withNewTree :: Repository m => TreeT m a -> m (a, TreeRef m)+withNewTree = doWithTree Nothing++createTree :: Repository m => TreeT m a -> m (TreeRef m)+createTree action = snd <$> withNewTree action+ data TreeEntry m = BlobEntry { blobEntryOid :: !(BlobOid m) , blobEntryKind :: !BlobKind }- | TreeEntry { treeEntryRef :: !(TreeRef m) }- | CommitEntry { commitEntryRef :: !(CommitOid m) }+ | TreeEntry { treeEntryOid :: !(TreeOid m) }+ | CommitEntry { commitEntryOid :: !(CommitOid m) } -treeEntryOid :: Repository m => TreeEntry m -> m (Oid m)-treeEntryOid (BlobEntry boid _) = return $ unTagged boid-treeEntryOid (TreeEntry tref) = unTagged <$> treeRefOid tref-treeEntryOid (CommitEntry coid) = return $ unTagged coid+getTreeEntryOid :: Repository m => TreeEntry m -> Oid m+getTreeEntryOid (BlobEntry boid _) = untag boid+getTreeEntryOid (TreeEntry toid) = untag toid+getTreeEntryOid (CommitEntry coid) = untag coid blobEntry :: Repository m => BlobOid m -> BlobKind -> TreeEntry m blobEntry = BlobEntry -treeEntry :: Repository m => RepositoryTree m -> TreeEntry m-treeEntry = TreeEntry . treeRef+treeEntry :: Repository m => Tree m -> TreeEntry m+treeEntry = TreeEntry . treeOid commitEntry :: Repository m => Commit m -> TreeEntry m commitEntry = CommitEntry . commitOid@@ -534,14 +558,14 @@ toModifyTreeResult f (Just x) = f x -- | A 'Tree' is anything that is "treeish".-treeRef :: RepositoryTree m -> TreeRef m+treeRef :: Tree m -> TreeRef m treeRef = Known -treeRefOid :: Repository m => TreeRef m -> m (TreeOid m)-treeRefOid (ByOid x) = return x-treeRefOid (Known x) = writeTree x+treeRefOid :: Repository m => TreeRef m -> TreeOid m+treeRefOid (ByOid x) = x+treeRefOid (Known x) = treeOid x -resolveTreeRef :: Repository m => TreeRef m -> m (RepositoryTree m)+resolveTreeRef :: Repository m => TreeRef m -> m (Tree m) resolveTreeRef (ByOid oid) = lookupTree oid resolveTreeRef (Known obj) = return obj @@ -595,12 +619,12 @@ referenceToRef mname mref = case mref of Nothing -> return Nothing- Just (Reference { refTarget = RefObj x }) ->+ Just (Reference { referenceTarget = RefObj x }) -> return (Just x)- Just ref@(Reference { refTarget = RefSymbolic name' }) ->+ Just ref@(Reference { referenceTarget = RefSymbolic name' }) -> if fromMaybe name' mname /= name'- then resolveRef name'- else failure (ReferenceLookupFailed (refName ref))+ then resolveReference name'+ else failure (ReferenceLookupFailed (referenceName ref)) {- $tags -} @@ -694,6 +718,490 @@ <*> pure cs {- $miscellaneous -}++newtype SHA = SHA B.ByteString deriving (Eq, Ord, Read)++instance Show SHA where+ show = T.unpack . shaToText++instance Bin.Binary SHA where+ put (SHA t) = Bin.put t+ get = SHA <$> Bin.get++instance Hashable SHA where+ hashWithSalt salt (SHA bs) = hashWithSalt salt bs++instance IsOid SHA where+ renderOid = shaToText++shaToText :: SHA -> Text+shaToText (SHA bs) = T.decodeUtf8 (B16.encode bs)++textToSha :: Monad m => Text -> m SHA+textToSha t =+ case B16.decode $ T.encodeUtf8 t of+ (bs, "") -> return (SHA bs)+ _ -> fail "Invalid base16 encoding"++createBlobUtf8 :: Repository m => Text -> m (BlobOid m)+createBlobUtf8 = createBlob . BlobString . T.encodeUtf8++catBlob :: Repository m => Text -> m ByteString+catBlob str =+ if len == 40+ then do+ oid <- parseOid str+ lookupBlob (Tagged oid) >>= blobToByteString++ else do+ obj <- lookupObject str+ case obj of+ BlobObj (ByOid oid) -> lookupBlob oid >>= blobToByteString+ BlobObj (Known x) -> blobToByteString x+ _ -> failure (ObjectLookupFailed str len)+ where+ len = T.length str++catBlobUtf8 :: Repository m => Text -> m Text+catBlobUtf8 = catBlob >=> return . T.decodeUtf8++blobContentsToByteString :: Repository m => BlobContents m -> m ByteString+blobContentsToByteString (BlobString bs) = return bs+blobContentsToByteString (BlobStream bs) = do+ strs <- bs $$ CList.consume+ return (B.concat strs)+blobContentsToByteString (BlobSizedStream bs _) = do+ strs <- bs $$ CList.consume+ return (B.concat strs)++blobToByteString :: Repository m => Blob m -> m ByteString+blobToByteString (Blob _ contents) = blobContentsToByteString contents++splitPath :: FilePath -> [Text]+splitPath path = T.splitOn "/" text+ where text = case toText path of+ Left x -> error $ "Invalid path: " ++ T.unpack x+ Right y -> y++data ModifiedBuilder m = ModifiedBuilder (TreeBuilder m)+ | BuilderUnchanged (TreeBuilder m)++instance Monoid (ModifiedBuilder m) where+ mempty = BuilderUnchanged (error "ModifiedBuilder is a semigroup")+ BuilderUnchanged _ `mappend` BuilderUnchanged b2 = BuilderUnchanged b2+ ModifiedBuilder b1 `mappend` BuilderUnchanged _ = ModifiedBuilder b1+ BuilderUnchanged _ `mappend` ModifiedBuilder b2 = ModifiedBuilder b2+ ModifiedBuilder _ `mappend` ModifiedBuilder b2 = ModifiedBuilder b2++fromBuilderMod :: ModifiedBuilder m -> TreeBuilder m+fromBuilderMod (BuilderUnchanged tb) = tb+fromBuilderMod (ModifiedBuilder tb) = tb++data TreeBuilder m = TreeBuilder+ { mtbBaseTreeRef :: Maybe (TreeRef m)+ , mtbPendingUpdates :: HashMap Text (TreeBuilder m)+ , mtbNewBuilder :: Maybe (Tree m) -> m (TreeBuilder m)+ , mtbWriteContents :: TreeBuilder m -> m (ModifiedBuilder m, TreeRef m)+ , mtbLookupEntry :: Text -> m (Maybe (TreeEntry m))+ , mtbEntryCount :: m Int+ , mtbPutEntry :: TreeBuilder m -> Text -> TreeEntry m+ -> m (ModifiedBuilder m)+ , mtbDropEntry :: TreeBuilder m -> Text -> m (ModifiedBuilder m)+ }++instance Monad m => Monoid (TreeBuilder m) where+ mempty = TreeBuilder+ { mtbBaseTreeRef = Nothing+ , mtbPendingUpdates = HashMap.empty+ , mtbNewBuilder = error "Implement TreeBuilder.mtbNewBuilder"+ , mtbWriteContents = error "Implement TreeBuilder.mtbWriteContents"+ , mtbLookupEntry = \_ -> return Nothing+ , mtbEntryCount = return 0+ , mtbPutEntry = \tb _ _ -> return (BuilderUnchanged tb)+ , mtbDropEntry = \tb _ -> return (BuilderUnchanged tb)+ }+ tb1 `mappend` tb2 = tb2+ { mtbBaseTreeRef = mtbBaseTreeRef tb1+ , mtbPendingUpdates = mtbPendingUpdates tb1+ }++emptyTreeId :: Text+emptyTreeId = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"++-- | Perform a query action on a TreeBuilder using the supplied action kind+-- and user function.+--+-- This is a complex algorithm which has been rewritten many times, so I+-- will try to guide you through it as best I can.+queryTreeBuilder :: Repository m+ => TreeBuilder m+ -> FilePath+ -> BuilderAction+ -> (Maybe (TreeEntry m) -> ModifyTreeResult m)+ -> m (TreeBuilder m, Maybe (TreeEntry m))+queryTreeBuilder builder path kind f = do+ (mtb, mtresult) <- walk (BuilderUnchanged builder) (splitPath path)+ return (fromBuilderMod mtb, fromModifyTreeResult mtresult)+ where+ walk _ [] = error "queryTreeBuilder called without a path"+ walk bm (name:names) = do+ let tb = fromBuilderMod bm+ y <- case HashMap.lookup name (mtbPendingUpdates tb) of+ Just x -> return $ Left (BuilderUnchanged x)+ Nothing -> do+ mentry <- mtbLookupEntry tb name+ case mentry of+ Nothing+ | kind == PutEntry && not (null names) ->+ Left . ModifiedBuilder+ <$> mtbNewBuilder tb Nothing+ | otherwise -> return $ Right Nothing+ Just x -> return $ Right (Just x)+ update bm name names y++ doUpdate GetEntry bm name sbm = do+ (_, tref) <- writeTreeBuilder (fromBuilderMod sbm)+ returnTree bm name $ f (Just (TreeEntry (treeRefOid tref)))+ doUpdate _ bm name _ = returnTree bm name (f Nothing)++ update bm name [] (Left sbm) = doUpdate kind bm name sbm+ update bm name [] (Right y) = returnTree bm name (f y)++ update bm _ _ (Right Nothing) = return (bm, TreeEntryNotFound)+ update _ _ _ (Right (Just BlobEntry {})) =+ failure TreeCannotTraverseBlob+ update _ _ _ (Right (Just CommitEntry {})) =+ failure TreeCannotTraverseCommit++ update bm name names arg = do+ sbm <- case arg of+ Left sbm' -> return sbm'+ Right (Just (TreeEntry st')) -> do+ tree <- lookupTree st'+ ModifiedBuilder+ <$> mtbNewBuilder (fromBuilderMod bm) (Just tree)+ _ -> error "queryTreeBuilder encountered the impossible"++ (sbm', z) <- walk sbm names+ let bm' = bm <> postUpdate bm sbm' name+ return $ bm' `seq` (bm', z)++ returnTree bm@(fromBuilderMod -> tb) n z = do+ bm' <- case z of+ TreeEntryNotFound -> return bm+ TreeEntryPersistent _ -> return bm+ TreeEntryDeleted -> do+ bm' <- mtbDropEntry tb tb n+ let tb' = fromBuilderMod bm'+ upds' = mtbPendingUpdates tb'+ return $+ if HashMap.member n upds'+ then ModifiedBuilder tb'+ { mtbPendingUpdates = HashMap.delete n upds' }+ else bm'+ TreeEntryMutated z' -> mtbPutEntry tb tb n z'+ let bm'' = bm <> bm'+ return $ bm'' `seq` (bm'', z)++ postUpdate bm (BuilderUnchanged _) _ = bm+ postUpdate (fromBuilderMod -> tb) (ModifiedBuilder sbm) name =+ ModifiedBuilder $ tb+ { mtbPendingUpdates =+ HashMap.insert name sbm (mtbPendingUpdates tb) }++-- | Write out a tree to its repository. If it has already been written,+-- nothing will happen.+writeTreeBuilder :: Repository m+ => TreeBuilder m+ -> m (TreeBuilder m, TreeRef m)+writeTreeBuilder builder = do+ (bm, mtref) <- go (BuilderUnchanged builder)+ tref <- case mtref of+ Nothing -> do+ emptyTreeOid <- parseObjOid emptyTreeId+ return $ ByOid emptyTreeOid+ Just tref -> return tref+ return (fromBuilderMod bm, tref)+ where+ go bm = do+ let upds = mtbPendingUpdates (fromBuilderMod bm)+ bm' <- if HashMap.size upds == 0+ then return bm+ else do+ bm' <- foldM update bm $ HashMap.toList upds+ return $ ModifiedBuilder (fromBuilderMod bm')+ { mtbPendingUpdates = HashMap.empty }+ let tb' = fromBuilderMod bm'+ cnt <- mtbEntryCount tb'+ if cnt == 0+ then return (bm', Nothing)+ else do+ (bm'', tref) <- mtbWriteContents tb' tb'+ return (bm' <> bm'', Just tref)++ update bm (k,v) = do+ let tb = fromBuilderMod bm+ -- The intermediate TreeBuilder will be dropped after this fold is+ -- completed, by setting mtbPendingUpdates to HashMap.empty, above.+ (_,mtref) <- go (BuilderUnchanged v)+ bm' <- case mtref of+ Nothing -> mtbDropEntry tb tb k+ Just tref -> mtbPutEntry tb tb k (TreeEntry (treeRefOid tref))+ return $ bm <> bm'++treeBlobEntries :: Repository m => Tree m -> m [(FilePath,TreeEntry m)]+treeBlobEntries tree =+ mconcat <$> traverseEntries go tree+ where+ go fp e@(BlobEntry _ PlainBlob) = return [(fp,e)]+ go fp e@(BlobEntry _ ExecutableBlob) = return [(fp,e)]+ go _ _ = return []++commitTreeEntry :: Repository m+ => Commit m+ -> FilePath+ -> m (Maybe (TreeEntry m))+commitTreeEntry c path =+ flip getTreeEntry path =<< resolveTreeRef (commitTree c)++copyBlob :: (Repository m, Repository (t m), MonadTrans t)+ => BlobRef m+ -> HashSet Text+ -> t m (BlobOid (t m), HashSet Text)+copyBlob blobr needed = do+ let oid = untag (blobRefOid blobr)+ sha = renderOid oid+ oid2 <- parseOid (renderOid oid)+ if HashSet.member sha needed+ then do+ bs <- lift $ blobToByteString+ =<< resolveBlobRef (ByOid (Tagged oid))+ boid <- createBlob (BlobString bs)++ let x = HashSet.delete sha needed+ return $ boid `seq` x `seq` (boid, x)++ else return (Tagged oid2, needed)++copyTreeEntry :: (Repository m, Repository (t m), MonadTrans t)+ => TreeEntry m+ -> HashSet Text+ -> t m (TreeEntry (t m), HashSet Text)+copyTreeEntry (BlobEntry oid kind) needed = do+ (b,needed') <- copyBlob (ByOid oid) needed+ return (BlobEntry b kind, needed')+copyTreeEntry (CommitEntry oid) needed = do+ coid <- parseOid (renderObjOid oid)+ return (CommitEntry (Tagged coid), needed)+copyTreeEntry (TreeEntry _) _ = error "This should never be called"++copyTree :: (Repository m, Repository (t m), MonadTrans t)+ => TreeRef m+ -> HashSet Text+ -> t m (TreeRef (t m), HashSet Text)+copyTree tr needed = do+ let oid = untag (treeRefOid tr)+ sha = renderOid oid+ oid2 <- parseOid (renderOid oid)+ if HashSet.member sha needed+ then do+ tree <- lift $ resolveTreeRef tr+ entries <- lift $ traverseEntries (curry return) tree+ (needed', tref) <- withNewTree $ foldM doCopyTreeEntry needed entries++ let x = HashSet.delete sha needed'+ return $ tref `seq` x `seq` (tref, x)++ else return (ByOid (Tagged oid2), needed)+ where+ doCopyTreeEntry :: (Repository m, Repository (t m), MonadTrans t)+ => HashSet Text -> (FilePath, TreeEntry m)+ -> TreeT (t m) (HashSet Text)+ doCopyTreeEntry needed' (_,TreeEntry {}) = return needed'+ doCopyTreeEntry needed' (fp,ent) = do+ (ent2,needed'') <- lift $ copyTreeEntry ent needed'+ putEntry fp ent2+ return needed''++copyCommit :: (Repository m, Repository (t m), MonadTrans t)+ => CommitRef m+ -> Maybe Text+ -> HashSet Text+ -> t m (CommitRef (t m), HashSet Text)+copyCommit cr mref needed = do+ let oid = untag (commitRefOid cr)+ sha = renderOid oid+ commit <- lift $ resolveCommitRef cr+ oid2 <- parseOid sha+ if HashSet.member sha needed+ then do+ let parents = commitParents commit+ (parentRefs,needed') <- foldM copyParent ([],needed) parents+ (tr,needed'') <- copyTree (commitTree commit) needed'++ commit' <- createCommit (reverse parentRefs) tr+ (commitAuthor commit)+ (commitCommitter commit)+ (commitLog commit)+ mref++ let cref = commitRef $! commit'+ x = HashSet.delete sha needed''+ return $ cref `seq` x `seq` (cref, x)++ else return (ByOid (Tagged oid2), needed)+ where+ copyParent (prefs,needed') cref = do+ (cref2,needed'') <- copyCommit cref Nothing needed'+ let x = cref2 `seq` (cref2:prefs)+ return $ x `seq` needed'' `seq` (x,needed'')++-- | Given a list of objects (commit and top-level trees) return by+-- 'missingObjects', expand it to include all subtrees and blobs as well.+-- Ordering is preserved.+allMissingObjects :: Repository m => [Object m] -> m [Object m]+allMissingObjects objs =+ fmap concat . forM objs $ \obj -> case obj of+ TreeObj ref -> do+ tr <- resolveTreeRef ref+ subobjss <- flip traverseEntries tr $ \_ ent ->+ return $ case ent of+ BlobEntry oid _ -> [BlobObj (ByOid oid)]+ TreeEntry oid -> [TreeObj (ByOid oid)]+ _ -> []+ return (obj:concat subobjss)+ _ -> return [obj]++-- | Fast-forward push a reference between repositories using a recursive+-- copy. This can be extremely slow, but always works.+genericPushCommit :: (Repository m, Repository (t m), MonadTrans t)+ => CommitName m -> Text -> t m (CommitRef (t m))+genericPushCommit cname remoteRefName = do+ mrref <- lookupReference remoteRefName+ commits1 <- lift $ traverseCommits crefToSha cname+ fastForward <- case mrref of+ Just rref -> do+ mrsha <- referenceSha rref+ case mrsha of+ Nothing -> failure (PushNotFastForward $+ "Could not find SHA for " <> remoteRefName)+ Just rsha+ | rsha `elem` commits1 -> do+ roid <- lift $ parseOid rsha+ return $ Just (Just (CommitObjectId (Tagged roid)))+ | otherwise -> do+ failure (PushNotFastForward $+ "SHA " <> rsha+ <> " not found in remote")+ Nothing -> return (Just Nothing)+ case fastForward of+ Nothing -> failure (PushNotFastForward "unexpected")+ Just liftedMrref -> do+ objs <- lift $ allMissingObjects+ =<< missingObjects liftedMrref cname+ shas <- mapM (\obj -> renderOid <$> lift (objectOid obj)) objs+ mref <- lift $ commitNameToRef cname+ case mref of+ Nothing -> failure (ReferenceLookupFailed (T.pack (show cname)))+ Just ref -> do+ (cref,_) <- copyCommit ref Nothing (HashSet.fromList shas)+ -- jww (2013-04-18): This is something the user must+ -- decide to do+ -- updateRef_ remoteRefName (RefObj cref)+ return cref+ where+ referenceSha ref = do+ r <- referenceToRef Nothing (Just ref)+ return $ renderObjOid . commitRefOid <$> r++ crefToSha cref = return (renderObjOid (commitRefOid cref))++commitHistoryFirstParent :: Repository m => Commit m -> m [Commit m]+commitHistoryFirstParent c =+ case commitParents c of+ [] -> return [c]+ (p:_) -> do ps <- commitHistoryFirstParent =<< resolveCommitRef p+ return (c:ps)++data PinnedEntry m = PinnedEntry+ { pinnedOid :: Oid m+ , pinnedCommit :: Commit m+ , pinnedEntry :: TreeEntry m+ }++identifyEntry :: Repository m => Commit m -> TreeEntry m -> m (PinnedEntry m)+identifyEntry co x = do+ let oid = case x of+ BlobEntry oid' _ -> untag oid'+ TreeEntry oid' -> untag oid'+ CommitEntry oid' -> untag oid'+ return (PinnedEntry oid co x)++commitEntryHistory :: Repository m => Commit m -> FilePath -> m [PinnedEntry m]+commitEntryHistory c path =+ map head . filter (not . null) . groupBy ((==) `on` pinnedOid) <$> go c+ where+ go co = do+ entry <- getCommitTreeEntry co+ rest <- case commitParents co of+ [] -> return []+ (p:_) -> go =<< resolveCommitRef p+ return $ maybe rest (:rest) entry++ getCommitTreeEntry co = do+ ce <- commitTreeEntry co path+ case ce of+ Nothing -> return Nothing+ Just ce' -> Just <$> identifyEntry co ce'++getCommitParents :: Repository m => Commit m -> m [Commit m]+getCommitParents = traverse resolveCommitRef . commitParents++resolveReferenceTree :: Repository m => Text -> m (Maybe (Tree m))+resolveReferenceTree refName = do+ c <- resolveReference refName+ case c of+ Nothing -> return Nothing+ Just c' ->+ Just <$> (resolveCommitRef c' >>= resolveTreeRef . commitTree)++withNewRepository :: (Repository (t m), MonadGit (t m),+ MonadBaseControl IO m, MonadIO m, MonadTrans t)+ => RepositoryFactory t m c+ -> FilePath -> t m a -> m a+withNewRepository factory path action = do+ liftIO $ do+ exists <- isDirectory path+ when exists $ removeTree path++ -- we want exceptions to leave the repo behind+ a <- withRepository' factory (defaultOptions factory)+ { repoPath = path+ , repoIsBare = True+ , repoAutoCreate = True+ } action++ liftIO $ do+ exists <- isDirectory path+ when exists $ removeTree path++ return a++withNewRepository' :: (Repository (t m), MonadGit (t m),+ MonadBaseControl IO m, MonadIO m, MonadTrans t)+ => RepositoryFactory t m c -> FilePath -> t m a -> m a+withNewRepository' factory path action =+ Exc.bracket_ recover recover $+ withRepository' factory (defaultOptions factory)+ { repoPath = path+ , repoIsBare = True+ , repoAutoCreate = True+ } action+ where+ recover = liftIO $ do+ exists <- isDirectory path+ when exists $ removeTree path+ data RepositoryOptions = RepositoryOptions { repoPath :: !FilePath
gitlib.cabal view
@@ -1,5 +1,5 @@ Name: gitlib-Version: 1.2.1+Version: 1.3.1 Synopsis: API library for working with Git repositories License-file: LICENSE License: MIT@@ -40,17 +40,22 @@ ghc-options: -Wall build-depends: base >= 3 && < 5+ , base16-bytestring >= 0.1.1.5+ , binary >= 0.5.1.0 , bytestring >= 0.9.2.1 , conduit >= 1.0.0 , containers >= 0.4.2.1 , data-default >= 0.5.0 , failure >= 0.2.0.1+ , hashable >= 1.1.2.5 , lifted-base >= 0.2+ , system-fileio >= 0.3.11 , system-filepath >= 0.4.7 , tagged >= 0.2.3.1- , transformers >= 0.3.0.0 , text >= 0.11.2 , time >= 1.4+ , transformers >= 0.3.0.0+ , unordered-containers >= 0.2.3.0 exposed-modules: Git Git.Tutorial