gitlib-cmdline 1.2.1 → 1.3.1
raw patch · 2 files changed
+202/−267 lines, 2 filesdep −gitlib-utilsdep ~gitlibdep ~gitlib-cmdlinedep ~gitlib-testPVP ok
version bump matches the API change (PVP)
Dependencies removed: gitlib-utils
Dependency ranges changed: gitlib, gitlib-cmdline, gitlib-test
API changes (from Hackage documentation)
- Git.CmdLine: HashMapTree :: IORef (Maybe (TreeOid m)) -> IORef (HashMap Text (TreeEntry m)) -> HashMapTree m t
- Git.CmdLine: cliCloneTree :: MonadGit m => Tree m PersistentTree -> CmdLineRepository m (Tree m PersistentTree)
- Git.CmdLine: cliModifyTree :: MonadGit m => Tree m PersistentTree -> FilePath -> Bool -> (Maybe (TreeEntry m) -> ModifyTreeResult (CmdLineRepository m)) -> CmdLineRepository m (Tree m PersistentTree, Maybe (TreeEntry m))
- Git.CmdLine: cliNewTree :: MonadGit m => CmdLineRepository m (Tree m PersistentTree)
- Git.CmdLine: cliTreeContents :: HashMapTree m t -> IORef (HashMap Text (TreeEntry m))
- Git.CmdLine: cliTreeOid :: HashMapTree m t -> IORef (Maybe (TreeOid m))
- Git.CmdLine: cliWriteTree :: MonadGit m => Tree m PersistentTree -> CmdLineRepository m (TreeOid m)
- Git.CmdLine: data HashMapTree m t
- Git.CmdLine: type Oid = OidText
+ Git.CmdLine: cliBuilderEntryCount :: MonadGit m => EntryHashMap m -> CmdLineRepository m Int
+ Git.CmdLine: cliDropEntry :: MonadGit m => TreeBuilder m -> EntryHashMap m -> Text -> CmdLineRepository m (ModifiedBuilder m)
+ Git.CmdLine: cliLookupBuilderEntry :: MonadGit m => EntryHashMap m -> Text -> CmdLineRepository m (Maybe (TreeEntry m))
+ Git.CmdLine: cliNewTreeBuilder :: MonadGit m => Maybe (Tree m) -> CmdLineRepository m (TreeBuilder m)
+ Git.CmdLine: cliParseLsTree :: MonadGit m => Text -> CmdLineRepository m (Text, TreeEntry m)
+ Git.CmdLine: cliPutEntry :: MonadGit m => TreeBuilder m -> EntryHashMap m -> Text -> TreeEntry m -> CmdLineRepository m (ModifiedBuilder m)
+ Git.CmdLine: cliTreeEntry :: MonadGit m => Tree m -> FilePath -> CmdLineRepository m (Maybe (TreeEntry m))
+ Git.CmdLine: cliTreeEntryCount :: MonadGit m => Tree m -> CmdLineRepository m Int
+ Git.CmdLine: cliWriteBuilder :: MonadGit m => EntryHashMap m -> CmdLineRepository m (TreeRef m)
+ Git.CmdLine: type EntryHashMap m = HashMap Text (TreeEntry m)
+ Git.CmdLine: type ModifiedBuilder m = ModifiedBuilder (CmdLineRepository m)
+ Git.CmdLine: type TreeBuilder m = TreeBuilder (CmdLineRepository m)
- Git.CmdLine: cliExistsObject :: MonadGit m => Oid -> CmdLineRepository m Bool
+ Git.CmdLine: cliExistsObject :: MonadGit m => SHA -> CmdLineRepository m Bool
- Git.CmdLine: cliLookupTree :: MonadGit m => TreeOid m -> CmdLineRepository m (Tree m PersistentTree)
+ Git.CmdLine: cliLookupTree :: MonadGit m => TreeOid m -> CmdLineRepository m (Tree m)
- Git.CmdLine: cliMakeTree :: IORef (Maybe (TreeOid m)) -> IORef (HashMap Text (TreeEntry m)) -> Tree m PersistentTree
+ Git.CmdLine: cliMakeTree :: MonadGit m => EntryHashMap m -> TreeBuilder m
- Git.CmdLine: cliTraverseEntries :: MonadGit m => (FilePath -> TreeEntry m -> CmdLineRepository m b) -> Tree m PersistentTree -> CmdLineRepository m [b]
+ Git.CmdLine: cliTraverseEntries :: MonadGit m => (FilePath -> TreeEntry m -> CmdLineRepository m b) -> Tree m -> CmdLineRepository m [b]
- Git.CmdLine: getOid :: OidText -> Text
+ Git.CmdLine: getOid :: SHA -> Text
- Git.CmdLine: mkOid :: Text -> OidText
+ Git.CmdLine: mkOid :: Monad m => Text -> m SHA
- Git.CmdLine: nameAndShaToRef :: Text -> Text -> Reference m
+ Git.CmdLine: nameAndShaToRef :: MonadGit m => Text -> Text -> CmdLineRepository m (Reference m)
Files
- Git/CmdLine.hs +197/−260
- gitlib-cmdline.cabal +5/−7
Git/CmdLine.hs view
@@ -27,7 +27,6 @@ import Data.Function import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap-import Data.IORef import Data.List as L import qualified Data.Map as Map import Data.Maybe@@ -46,7 +45,6 @@ import qualified Filesystem as F import qualified Filesystem.Path.CurrentOS as F import qualified Git-import qualified Git.Utils as Git import Prelude hiding (FilePath) import Shelly hiding (trace) import System.Exit@@ -73,51 +71,42 @@ fromStrict = TL.fromStrict #endif -type BlobOid m = Git.BlobOid (CmdLineRepository m)-type TreeOid m = Git.TreeOid (CmdLineRepository m)-type CommitOid m = Git.CommitOid (CmdLineRepository m)-type TagOid m = Git.TagOid (CmdLineRepository m)+type BlobOid m = Git.BlobOid (CmdLineRepository m)+type TreeOid m = Git.TreeOid (CmdLineRepository m)+type CommitOid m = Git.CommitOid (CmdLineRepository m)+type TagOid m = Git.TagOid (CmdLineRepository m) -type Blob m = Git.Blob (CmdLineRepository m)-type Tree m = Git.Tree (CmdLineRepository m)-type TreeEntry m = Git.TreeEntry (CmdLineRepository m)-type Commit m = Git.Commit (CmdLineRepository m)-type Tag m = Git.Tag (CmdLineRepository m)+type Blob m = Git.Blob (CmdLineRepository m)+type Tree m = Git.Tree (CmdLineRepository m)+type TreeEntry m = Git.TreeEntry (CmdLineRepository m)+type Commit m = Git.Commit (CmdLineRepository m)+type Tag m = Git.Tag (CmdLineRepository m) -type TreeRef m = Git.TreeRef (CmdLineRepository m)-type CommitRef m = Git.CommitRef (CmdLineRepository m)-type CommitName m = Git.CommitName (CmdLineRepository m)+type TreeRef m = Git.TreeRef (CmdLineRepository m)+type CommitRef m = Git.CommitRef (CmdLineRepository m)+type CommitName m = Git.CommitName (CmdLineRepository m) -type Reference m = Git.Reference (CmdLineRepository m) (Commit m)-type Object m = Git.Object (CmdLineRepository m)+type Reference m = Git.Reference (CmdLineRepository m) (Commit m)+type Object m = Git.Object (CmdLineRepository m)+type TreeBuilder m = Git.TreeBuilder (CmdLineRepository m)+type ModifiedBuilder m = Git.ModifiedBuilder (CmdLineRepository m) instance Git.MonadGit m => Git.Repository (CmdLineRepository m) where- type Oid (CmdLineRepository m) =-#if MIN_VERSION_shelly(1, 0, 0)- Git.OidText-#else- Git.OidTextL-#endif- type TreeKind (CmdLineRepository m) = Git.PersistentTree- type Tree (CmdLineRepository m) = HashMapTree m-+ type Oid (CmdLineRepository m) = Git.SHA+ data Tree (CmdLineRepository m) = CmdLineTree (TreeOid m) data Options (CmdLineRepository m) = Options facts = return Git.RepositoryFacts { Git.hasSymbolicReferences = True } -#if MIN_VERSION_shelly(1, 0, 0)- parseOid = Git.parseOidText-#else- parseOid = Git.parseOidTextL-#endif+ parseOid = Git.textToSha - lookupRef = cliLookupRef- createRef = cliUpdateRef- updateRef = cliUpdateRef- deleteRef = cliDeleteRef- resolveRef = cliResolveRef- allRefs = cliAllRefs+ lookupReference = cliLookupRef+ createReference = cliUpdateRef+ updateReference = cliUpdateRef+ deleteReference = cliDeleteRef+ resolveReference = cliResolveRef+ allReferences = cliAllRefs lookupCommit = cliLookupCommit lookupTree = cliLookupTree lookupBlob = cliLookupBlob@@ -128,30 +117,23 @@ traverseCommits = cliTraverseCommits missingObjects = cliMissingObjects traverseObjects = error "Not defined: CmdLineRepository.traverseObjects"- newTree = cliNewTree- cloneTree = cliCloneTree+ newTreeBuilder = cliNewTreeBuilder+ getTreeEntry = cliTreeEntry traverseEntries = cliTraverseEntries- unsafeUpdateTree = cliModifyTree- writeTree = cliWriteTree+ treeOid = \(CmdLineTree toid) -> toid hashContents = cliHashContents createBlob = cliCreateBlob createCommit = cliCreateCommit createTag = cliCreateTag-- remoteFetch = error "Not defined: CmdLineRepository.remoteFetch"-+ remoteFetch = error "Not defined: CmdLineRepository.remoteFetch" deleteRepository = cliGet >>= liftIO . F.removeTree . Git.repoPath . repoOptions git :: [TL.Text] -> Sh TL.Text-git args = do- -- liftIO $ putStrLn $ "CmdLine: git " ++ show args- run "git" args+git = run "git" git_ :: [TL.Text] -> Sh ()-git_ args = do- -- liftIO $ putStrLn $ "CmdLine: git " ++ show args- run_ "git" args+git_ = run_ "git" doRunGit :: Git.MonadGit m => (FilePath -> [TL.Text] -> Sh a) -> [TL.Text] -> Sh ()@@ -160,8 +142,6 @@ repo <- cliGet shellyNoDir $ silently $ do act- -- liftIO $ putStrLn $ "CmdLine: git "- -- ++ show (["--git-dir", repoPath repo] <> args) f "git" $ ["--git-dir", repoPath repo] <> args runGit :: Git.MonadGit m@@ -219,7 +199,7 @@ . toStrict <$> lastStderr case merr of Nothing -> do- mcref <- Git.resolveRef remoteRefName+ mcref <- Git.resolveReference remoteRefName case mcref of Nothing -> failure (Git.BackendError $ "git push failed") Just cref -> return cref@@ -239,7 +219,7 @@ (Git.MergeResult (CmdLineRepository m)) cliPullCommitDirectly remoteNameOrURI remoteRefName user email msshCmd = do repo <- cliGet- leftHead <- Git.resolveRef "HEAD"+ leftHead <- Git.resolveReference "HEAD" eres <- shellyNoDir $ silently $ errExit False $ do case msshCmd of@@ -307,7 +287,7 @@ git_ [ "--git-dir", repoPath repo, "add", toTextIgnore fp ] getOid name = do- mref <- Git.resolveRef name+ mref <- Git.resolveReference name case mref of Nothing -> failure (Git.BackendError $ T.append "Reference missing: " name)@@ -341,23 +321,11 @@ (Just x, Just y) -> (x, y) (Nothing, Nothing) -> error "Both merge items cannot be Unchanged" -#if MIN_VERSION_shelly(1, 0, 0)-type Oid = Git.OidText--mkOid :: T.Text -> Git.OidText-mkOid = Git.OidText--getOid :: Git.OidText -> T.Text-getOid = Git.getOidT-#else-type Oid = Git.OidTextL--mkOid :: TL.Text -> Git.OidTextL-mkOid = Git.OidTextL+mkOid :: Monad m => TL.Text -> m Git.SHA+mkOid = Git.textToSha . toStrict -getOid :: Git.OidTextL -> TL.Text-getOid = Git.getOidTL-#endif+getOid :: Git.SHA -> TL.Text+getOid = fromStrict . Git.shaToText cliLookupBlob :: Git.MonadGit m => BlobOid m -> CmdLineRepository m (Blob m)@@ -384,11 +352,7 @@ <> ["-w" | persist] <> ["--stdin"]) bs if r == ExitSuccess- then return . Tagged- . mkOid- . fromStrict- . T.init- . T.decodeUtf8 $ out+ then Tagged <$> (mkOid . fromStrict . T.init . T.decodeUtf8 $ out) else failure Git.BlobCreateFailed cliHashContents :: Git.MonadGit m@@ -401,7 +365,7 @@ -> CmdLineRepository m (BlobOid m) cliCreateBlob b = cliDoCreateBlob b True -cliExistsObject :: Git.MonadGit m => Oid -> CmdLineRepository m Bool+cliExistsObject :: Git.MonadGit m => Git.SHA -> CmdLineRepository m Bool cliExistsObject (getOid -> sha) = do repo <- cliGet shellyNoDir $ silently $ errExit False $ do@@ -444,148 +408,96 @@ go x = failure (Git.BackendError $ "Unexpected output from git-log: " <> T.pack (show x)) -data HashMapTree m t = HashMapTree- { cliTreeOid :: IORef (Maybe (TreeOid m))- , cliTreeContents :: IORef (HashMap Text (TreeEntry m))- }--cliMakeTree :: IORef (Maybe (TreeOid m))- -> IORef (HashMap Text (TreeEntry m))- -> Tree m Git.PersistentTree-cliMakeTree oid contents = HashMapTree oid contents+type EntryHashMap m = HashMap Text (TreeEntry m) -cliNewTree :: Git.MonadGit m- => CmdLineRepository m (Tree m Git.PersistentTree)-cliNewTree = cliMakeTree <$> liftIO (newIORef Nothing)- <*> liftIO (newIORef HashMap.empty)+cliMakeTree :: Git.MonadGit m+ => EntryHashMap m -> TreeBuilder m+cliMakeTree entMap = mempty <> Git.TreeBuilder+ { Git.mtbBaseTreeRef = Nothing+ , Git.mtbPendingUpdates = mempty+ , Git.mtbNewBuilder = cliNewTreeBuilder+ , Git.mtbWriteContents = \tb -> (,) <$> pure (Git.BuilderUnchanged tb)+ <*> cliWriteBuilder entMap+ , Git.mtbLookupEntry = cliLookupBuilderEntry entMap+ , Git.mtbEntryCount = cliBuilderEntryCount entMap+ , Git.mtbPutEntry = flip cliPutEntry entMap+ , Git.mtbDropEntry = flip cliDropEntry entMap+ } -cliCloneTree :: Git.MonadGit m- => Tree m Git.PersistentTree- -> CmdLineRepository m (Tree m Git.PersistentTree)-cliCloneTree (HashMapTree oid contents) =- cliMakeTree <$> liftIO (newIORef =<< readIORef oid)- <*> liftIO (newIORef =<< readIORef contents)+-- | Create a new, empty tree.+--+-- Since empty trees cannot exist in Git, attempting to write out an empty+-- tree is a no-op.+cliNewTreeBuilder :: Git.MonadGit m+ => Maybe (Tree m) -> CmdLineRepository m (TreeBuilder m)+cliNewTreeBuilder mtree = do+ entMap <- case mtree of+ Nothing -> return HashMap.empty+ Just tree -> go (Git.treeOid tree)+ return $ (cliMakeTree entMap) { Git.mtbBaseTreeRef = Git.treeRef <$> mtree } -cliModifyTree :: Git.MonadGit m- => Tree m Git.PersistentTree- -> FilePath- -> Bool- -> (Maybe (TreeEntry m)- -> Git.ModifyTreeResult (CmdLineRepository m))- -> CmdLineRepository m (Tree m Git.PersistentTree, Maybe (TreeEntry m))-cliModifyTree t path createIfNotExist f =- fmap Git.fromModifyTreeResult- <$> doModifyTree t (Git.splitPath path) createIfNotExist where- -- Lookup the current name in this tree. If it doesn't exist, and there- -- are more names in the path and 'createIfNotExist' is True, create a new- -- Tree and descend into it. Otherwise, if it exists we'll have @Just- -- (TreeEntry {})@, and if not we'll have Nothing.- doModifyTree tr [] _ =- return (tr, Git.TreeEntryPersistent . Git.TreeEntry . Git.Known $ tr)- doModifyTree tr (name:names) createIfNotExist = do- y' <- doLookupTreeEntry tr [name]- y <- if isNothing y' && createIfNotExist && not (null names)- then Just . Git.TreeEntry . Git.Known <$> Git.newTree- else return y'- go tr name names y+ go (Tagged (getOid -> sha)) = do+ contents <- runGit ["ls-tree", "-z", sha]+ -- Even though the tree entries are separated by \NUL, for whatever+ -- reason @git ls-tree@ also outputs a newline at the end.+ HashMap.fromList+ <$> mapM cliParseLsTree (L.init (TL.splitOn "\NUL" contents)) - -- If there are no further names in the path, call the transformer- -- function, f. It receives a @Maybe TreeEntry@ to indicate if there was- -- a previous entry at this path. It should return a 'Left' value to- -- propagate out a user-defined error, or a @Maybe TreeEntry@ to indicate- -- whether the entry at this path should be deleted or replaced with- -- something new.- --- -- NOTE: There is no provision for leaving the entry unchanged! It is- -- assumed to always be changed, as we have no reliable method of testing- -- object equality that is not O(n).- go tr name [] y = do- let ze = f y- liftIO $ modifyIORef (cliTreeContents tr) $ case ze of- Git.TreeEntryNotFound -> id- Git.TreeEntryPersistent _ -> id- Git.TreeEntryDeleted -> HashMap.delete name- Git.TreeEntryMutated z' -> HashMap.insert name z'- return (tr, ze)+cliParseLsTree :: Git.MonadGit m+ => TL.Text -> CmdLineRepository m (Text, TreeEntry m)+cliParseLsTree line =+ let [prefix,path] = TL.splitOn "\t" line+ [mode,kind,sha] = TL.words prefix+ in liftM2 (,) (return (toStrict path)) $ case kind of+ "blob" -> do+ oid <- Tagged <$> mkOid sha+ return $ Git.BlobEntry oid $ case mode of+ "100644" -> Git.PlainBlob+ "100755" -> Git.ExecutableBlob+ "120000" -> Git.SymlinkBlob+ _ -> Git.UnknownBlob+ "commit" -> Git.CommitEntry . Tagged <$> mkOid sha+ "tree" -> Git.TreeEntry . Tagged <$> mkOid sha+ _ -> failure (Git.BackendError "This cannot happen") - go tr _ _ Nothing = return (tr, Git.TreeEntryNotFound)- go _ _ _ (Just Git.BlobEntry {}) = failure Git.TreeCannotTraverseBlob- go _ _ _ (Just Git.CommitEntry {}) = failure Git.TreeCannotTraverseCommit+cliPutEntry :: Git.MonadGit m+ => TreeBuilder m -> EntryHashMap m -> Text -> TreeEntry m+ -> CmdLineRepository m (ModifiedBuilder m)+cliPutEntry tb entMap key ent =+ return . Git.ModifiedBuilder $+ tb <> cliMakeTree (HashMap.insert key ent entMap) - -- If there are further names in the path, descend them now. If- -- 'createIfNotExist' was False and there is no 'Tree' under the current- -- name, or if we encountered a 'Blob' when a 'Tree' was required, throw- -- an exception to avoid colliding with user-defined 'Left' values.- go tr name names (Just (Git.TreeEntry st')) = do- st <- Git.resolveTreeRef st'- (st'', ze) <- doModifyTree st names createIfNotExist- case ze of- Git.TreeEntryNotFound -> return ()- Git.TreeEntryPersistent _ -> return ()- Git.TreeEntryDeleted -> postUpdate tr st'' name- Git.TreeEntryMutated _ -> postUpdate tr st'' name- return (tr, ze)+cliDropEntry :: Git.MonadGit m+ => TreeBuilder m -> EntryHashMap m -> Text+ -> CmdLineRepository m (ModifiedBuilder m)+cliDropEntry tb entMap key =+ return . Git.ModifiedBuilder $+ tb <> cliMakeTree (HashMap.delete key entMap) - -- Lookup the current name in this tree. If it doesn't exist, and there- -- are more names in the path and 'createIfNotExist' is True, create a new- -- Tree and descend into it. Otherwise, if it exists we'll have @Just- -- (TreeEntry {})@, and if not we'll have Nothing.- doLookupTreeEntry tr [] = return (Just (Git.treeEntry tr))- doLookupTreeEntry tr (name:names) = do- y <- liftIO $ HashMap.lookup name <$> readIORef (cliTreeContents tr)- if null names- then return y- else case y of- Just (Git.BlobEntry {}) -> failure Git.TreeCannotTraverseBlob- Just (Git.CommitEntry {}) -> failure Git.TreeCannotTraverseCommit- Just (Git.TreeEntry st) -> do- st' <- Git.resolveTreeRef st- doLookupTreeEntry st' names- _ -> return Nothing+cliLookupBuilderEntry :: Git.MonadGit m+ => EntryHashMap m -> Text+ -> CmdLineRepository m (Maybe (TreeEntry m))+cliLookupBuilderEntry entMap key = return $ HashMap.lookup key entMap - postUpdate tr st name = liftIO $ do- modifyIORef (cliTreeOid tr) (const Nothing)- stc <- readIORef (cliTreeContents st)- modifyIORef (cliTreeContents tr) $- if HashMap.null stc- then HashMap.delete name- else HashMap.insert name (Git.treeEntry st)+cliBuilderEntryCount :: Git.MonadGit m+ => EntryHashMap m -> CmdLineRepository m Int+cliBuilderEntryCount entMap = return $ HashMap.size entMap -cliLookupTree :: Git.MonadGit m- => TreeOid m -> CmdLineRepository m (Tree m Git.PersistentTree)-cliLookupTree oid@(Tagged (getOid -> sha)) = do- contents <- runGit ["ls-tree", "-z", sha]- oidRef <- liftIO $ newIORef (Just oid)- -- Even though the tree entries are separated by \NUL, for whatever reason- -- @git ls-tree@ also outputs a newline at the end.- contentsRef <- liftIO $ newIORef $ HashMap.fromList $- map parseLine (L.init (TL.splitOn "\NUL" contents))- return $ cliMakeTree oidRef contentsRef- where- parseLine line =- let [prefix,path] = TL.splitOn "\t" line- [mode,kind,sha] = TL.words prefix- in (toStrict path,- case kind of- "blob" -> Git.BlobEntry (Tagged (mkOid sha)) $- case mode of- "100644" -> Git.PlainBlob- "100755" -> Git.ExecutableBlob- "120000" -> Git.SymlinkBlob- _ -> Git.UnknownBlob- "commit" -> Git.CommitEntry (Tagged (mkOid sha))- "tree" -> Git.TreeEntry (Git.ByOid (Tagged (mkOid sha)))- _ -> error "This cannot happen")+cliTreeEntryCount :: Git.MonadGit m => Tree m -> CmdLineRepository m Int+cliTreeEntryCount t =+ L.length . L.init . TL.splitOn "\NUL"+ <$> runGit [ "ls-tree", "-z"+ , fromStrict (Git.renderObjOid (Git.treeOid t)) ] -cliWriteTree :: Git.MonadGit m- => Tree m Git.PersistentTree -> CmdLineRepository m (TreeOid m)-cliWriteTree tree = do- contents <- liftIO $ readIORef (cliTreeContents tree)- rendered <- mapM renderLine (HashMap.toList contents)+cliWriteBuilder :: Git.MonadGit m+ => EntryHashMap m+ -> CmdLineRepository m (TreeRef m)+cliWriteBuilder entMap = do+ rendered <- mapM renderLine (HashMap.toList entMap) oid <- doRunGit run ["mktree", "-z", "--missing"] $ setStdin $ TL.append (TL.intercalate "\NUL" rendered) "\NUL"- return (Tagged (mkOid (TL.init oid)))+ Git.ByOid . Tagged <$> mkOid (TL.init oid) where renderLine (path, Git.BlobEntry (Tagged (getOid -> sha)) kind) = return $ TL.concat [ case kind of@@ -598,38 +510,53 @@ return $ TL.concat [ "160000 commit " , fromStrict (Git.renderObjOid coid), "\t" , fromStrict path ]- renderLine (path, Git.TreeEntry tref) = do- treeOid <- Git.treeRefOid tref+ renderLine (path, Git.TreeEntry toid) = do return $ TL.concat [ "040000 tree "- , fromStrict (Git.renderObjOid treeOid), "\t"+ , fromStrict (Git.renderObjOid toid), "\t" , fromStrict path ] +cliLookupTree :: Git.MonadGit m => TreeOid m -> CmdLineRepository m (Tree m)+cliLookupTree oid@(Tagged (getOid -> sha)) = do+ repo <- cliGet+ ec <- shellyNoDir $ silently $ errExit False $ do+ git_ $ [ "--git-dir", repoPath repo, "cat-file", "-t", sha ]+ lastExitCode+ if ec == 0+ then return $ CmdLineTree oid+ else failure (Git.ObjectLookupFailed (toStrict sha) 40)++cliTreeEntry :: Git.MonadGit m => Tree m -> FilePath+ -> CmdLineRepository m (Maybe (TreeEntry m))+cliTreeEntry tree fp = do+ repo <- cliGet+ mentryLines <- shellyNoDir $ silently $ errExit False $ do+ contents <- git $ [ "--git-dir", repoPath repo+ , "ls-tree", "-z"+ , fromStrict (Git.renderObjOid (Git.treeOid tree))+ , "--", toTextIgnore fp ]+ ec <- lastExitCode+ return $ if ec == 0+ then Just $ L.init (TL.splitOn "\NUL" contents)+ else Nothing+ case mentryLines of+ Nothing -> return Nothing+ Just entryLines -> do+ entries <- mapM cliParseLsTree entryLines+ return $ case entries of+ [] -> Nothing+ ((_,x):_) -> Just x+ cliTraverseEntries :: Git.MonadGit m => (FilePath -> TreeEntry m -> CmdLineRepository m b)- -> Tree m Git.PersistentTree+ -> Tree m -> CmdLineRepository m [b] cliTraverseEntries f tree = do- Tagged (getOid -> sha) <- Git.writeTree tree- contents <- runGit ["ls-tree", "-t", "-r", "-z", sha]- -- Even though the tree entries are separated by \NUL, for whatever reason- -- @git ls-tree@ also outputs a newline at the end.- mapM (uncurry f) $ map parseLine (L.init (TL.splitOn "\NUL" contents))- where- parseLine line =- let [prefix,path] = TL.splitOn "\t" line- [mode,kind,sha] = TL.words prefix- in (fromText path,- case kind of- "blob" -> Git.BlobEntry (Tagged (mkOid sha)) $- case mode of- "100644" -> Git.PlainBlob- "100755" -> Git.ExecutableBlob- "120000" -> Git.SymlinkBlob- _ -> Git.UnknownBlob- "commit" -> Git.CommitEntry (Tagged (mkOid sha))- "tree" -> Git.TreeEntry (Git.ByOid (Tagged (mkOid sha)))- _ -> error "This cannot happen")+ contents <- runGit [ "ls-tree", "-t", "-r", "-z"+ , fromStrict (Git.renderObjOid (Git.treeOid tree)) ]+ mapM (uncurry go <=< cliParseLsTree) (L.init (TL.splitOn "\NUL" contents))+ where+ go = f . fromText . fromStrict parseCliTime :: String -> ZonedTime parseCliTime = fromJust . parseTime defaultTimeLocale "%s %z"@@ -645,30 +572,32 @@ cliLookupCommit (Tagged (getOid -> sha)) = do output <- doRunGit run ["cat-file", "--batch"] $ setStdin (TL.append sha "\n")- case parse parseOutput "" (TL.unpack output) of+ result <- runParserT parseOutput () "" (TL.unpack output)+ case result of Left e -> failure $ Git.CommitLookupFailed (T.pack (show e)) Right c -> return c where+ parseOutput :: Monad m => ParsecT String () (CmdLineRepository m) (Commit a) parseOutput = do- coid <- Tagged . mkOid . TL.pack- <$> manyTill alphaNum space+ coid <- manyTill alphaNum space _ <- string "commit " *> manyTill digit newline- treeOid <- string "tree " *>- (Tagged . mkOid . TL.pack- <$> manyTill anyChar newline)- parentOids <- many (string "parent " *>- (Tagged . mkOid . TL.pack- <$> manyTill anyChar newline))+ treeOid <- string "tree " *> manyTill anyChar newline+ parentOids <- many (string "parent " *> manyTill anyChar newline) author <- parseSignature "author" committer <- parseSignature "committer" message <- newline *> many anyChar++ coid' <- lift $ Tagged `liftM` mkOid (TL.pack coid)+ toid' <- lift $ Tagged `liftM` mkOid (TL.pack treeOid)+ poids' <- lift $ mapM (liftM Tagged . mkOid . TL.pack) parentOids+ return Git.Commit- { Git.commitOid = coid+ { Git.commitOid = coid' , Git.commitAuthor = author , Git.commitCommitter = committer , Git.commitLog = T.pack (init message)- , Git.commitTree = Git.ByOid treeOid- , Git.commitParents = map Git.ByOid parentOids+ , Git.commitTree = Git.ByOid toid'+ , Git.commitParents = map Git.ByOid poids' , Git.commitEncoding = "utf-8" } @@ -680,12 +609,16 @@ <*> (parseCliTime <$> manyTill anyChar newline) cliCreateCommit :: Git.MonadGit m- => [CommitRef m] -> TreeRef m- -> Git.Signature -> Git.Signature -> Text -> Maybe Text+ => [CommitRef m]+ -> TreeRef m+ -> Git.Signature+ -> Git.Signature+ -> Text+ -> Maybe Text -> CmdLineRepository m (Commit m)-cliCreateCommit parents tree author committer message ref = do- treeOid <- Git.treeRefOid tree- let parentOids = map Git.commitRefOid parents+cliCreateCommit parents tref author committer message ref = do+ let treeOid = Git.treeRefOid tref+ parentOids = map Git.commitRefOid parents oid <- doRunGit run (["commit-tree"] <> [fromStrict (Git.renderObjOid treeOid)]@@ -703,8 +636,9 @@ ] setStdin (fromStrict message) + coid <- Tagged <$> mkOid (TL.init oid) let commit = Git.Commit- { Git.commitOid = Tagged (mkOid (TL.init oid))+ { Git.commitOid = coid , Git.commitAuthor = author , Git.commitCommitter = committer , Git.commitLog = message@@ -738,10 +672,11 @@ $ TL.lines rev else Nothing -nameAndShaToRef :: TL.Text -> TL.Text -> Reference m+nameAndShaToRef :: Git.MonadGit m+ => TL.Text -> TL.Text -> CmdLineRepository m (Reference m) nameAndShaToRef name sha =- Git.Reference (toStrict name)- (Git.RefObj (Git.ByOid (Tagged (mkOid sha))))+ Git.Reference <$> pure (toStrict name)+ <*> (Git.RefObj . Git.ByOid . Tagged <$> mkOid sha) cliLookupRef :: Git.MonadGit m => Text -> CmdLineRepository m (Maybe (Reference m))@@ -749,8 +684,8 @@ xs <- cliShowRef (Just refName) let name = fromStrict refName ref = maybe Nothing (lookup name) xs- return $ maybe Nothing (Just . uncurry nameAndShaToRef .- \sha -> (name,sha)) ref+ maybe (return Nothing)+ (fmap Just . uncurry nameAndShaToRef . \sha -> (name,sha)) ref cliUpdateRef :: Git.MonadGit m => Text -> Git.RefTarget (CmdLineRepository m) (Commit m)@@ -771,9 +706,9 @@ => CmdLineRepository m [Reference m] cliAllRefs = do mxs <- cliShowRef Nothing- return $ case mxs of- Nothing -> []- Just xs -> map (uncurry nameAndShaToRef) xs+ case mxs of+ Nothing -> return []+ Just xs -> mapM (uncurry nameAndShaToRef) xs cliResolveRef :: Git.MonadGit m => Text -> CmdLineRepository m (Maybe (CommitRef m))@@ -784,9 +719,9 @@ , "rev-parse", "--quiet", "--verify" , fromStrict refName ] ec <- lastExitCode- return $ if ec == 0- then Just (Git.ByOid (Tagged (mkOid (TL.init rev))))- else Nothing+ if ec == 0+ then Just . Git.ByOid . Tagged <$> mkOid (TL.init rev)+ else return Nothing -- cliLookupTag :: TagOid -> CmdLineRepository Tag -- cliLookupTag oid = undefined@@ -804,7 +739,8 @@ <> TL.pack (formatTime defaultTimeLocale "%s %z" (Git.signatureWhen tagger)) , ""] <> TL.lines (fromStrict msg)- return $ Git.Tag (Tagged (mkOid (TL.init tsha))) (Git.ByOid oid)+ Git.Tag <$> (Tagged <$> mkOid (TL.init tsha))+ <*> pure (Git.ByOid oid) data Repository = Repository { repoOptions :: Git.RepositoryOptions@@ -882,7 +818,8 @@ case F.toText path of Left e -> failure (Git.BackendError e) Right p -> do- when (not exists && Git.repoAutoCreate opts) $+ when (not exists && Git.repoAutoCreate opts) $ do+ liftIO $ F.createTree (fromText (fromStrict p)) shellyNoDir $ silently $ git_ $ ["--git-dir", fromStrict p] <> ["--bare" | Git.repoIsBare opts]
gitlib-cmdline.cabal view
@@ -1,5 +1,5 @@ Name: gitlib-cmdline-Version: 1.2.1+Version: 1.3.1 Synopsis: Gitlib repository backend that uses the git command-line tool. Description: Gitlib repository backend that uses the git command-line tool. License-file: LICENSE@@ -20,8 +20,7 @@ ghc-options: -Wall build-depends: base >= 3 && < 5- , gitlib >= 1.1.0- , gitlib-utils >= 1.1.0+ , gitlib >= 1.3.1 , bytestring >= 0.9.2.1 , conduit >= 0.5.5 , containers >= 0.4.2.1@@ -50,10 +49,9 @@ hs-source-dirs: test build-depends: base >=3- , gitlib >= 1.1.0- , gitlib-utils >= 1.1.0- , gitlib-test >= 1.1.0- , gitlib-cmdline >= 1.1.0+ , gitlib >= 1.3.1+ , gitlib-test >= 1.3.1+ , gitlib-cmdline >= 1.3.1 , HUnit >= 1.2.5 , hspec >= 1.4.4 , hspec-expectations >= 0.3