git-annex/Git/CatFile.hs

114 lines
3.3 KiB
Haskell
Raw Normal View History

{- git cat-file interface
-
- Copyright 2011, 2013 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Git.CatFile (
CatFileHandle,
catFileStart,
2013-10-20 21:50:51 +00:00
catFileStart',
catFileStop,
catFile,
catFileDetails,
2013-09-19 19:58:35 +00:00
catTree,
2012-06-10 23:58:34 +00:00
catObject,
catObjectDetails,
) where
import System.IO
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
2013-10-20 21:50:51 +00:00
import Data.Tuple.Utils
2013-09-19 19:58:35 +00:00
import Numeric
import System.Posix.Types
2011-12-20 18:37:53 +00:00
import Common
import Git
2011-12-14 19:30:14 +00:00
import Git.Sha
2011-12-14 19:56:11 +00:00
import Git.Command
import Git.Types
import Git.FilePath
2012-02-20 19:20:36 +00:00
import qualified Utility.CoProcess as CoProcess
data CatFileHandle = CatFileHandle CoProcess.CoProcessHandle Repo
catFileStart :: Repo -> IO CatFileHandle
2013-10-20 21:50:51 +00:00
catFileStart = catFileStart' True
catFileStart' :: Bool -> Repo -> IO CatFileHandle
catFileStart' restartable repo = do
coprocess <- CoProcess.rawMode =<< gitCoProcessStart restartable
[ Param "cat-file"
, Param "--batch"
] repo
return $ CatFileHandle coprocess repo
catFileStop :: CatFileHandle -> IO ()
catFileStop (CatFileHandle p _) = CoProcess.stop p
{- Reads a file from a specified branch. -}
catFile :: CatFileHandle -> Branch -> FilePath -> IO L.ByteString
catFile h branch file = catObject h $ Ref $
fromRef branch ++ ":" ++ toInternalGitPath file
catFileDetails :: CatFileHandle -> Branch -> FilePath -> IO (Maybe (L.ByteString, Sha, ObjectType))
catFileDetails h branch file = catObjectDetails h $ Ref $
fromRef branch ++ ":" ++ toInternalGitPath file
{- Uses a running git cat-file read the content of an object.
- Objects that do not exist will have "" returned. -}
catObject :: CatFileHandle -> Ref -> IO L.ByteString
2013-10-20 21:50:51 +00:00
catObject h object = maybe L.empty fst3 <$> catObjectDetails h object
2012-06-10 23:58:34 +00:00
2013-10-20 21:50:51 +00:00
catObjectDetails :: CatFileHandle -> Ref -> IO (Maybe (L.ByteString, Sha, ObjectType))
catObjectDetails (CatFileHandle hdl _) object = CoProcess.query hdl send receive
2012-12-13 04:24:19 +00:00
where
query = fromRef object
send to = hPutStrLn to query
2012-12-13 04:24:19 +00:00
receive from = do
header <- hGetLine from
case words header of
[sha, objtype, size]
2013-10-20 21:50:51 +00:00
| length sha == shaSize ->
case (readObjectType objtype, reads size) of
(Just t, [(bytes, "")]) -> readcontent t bytes from sha
2012-12-13 04:24:19 +00:00
_ -> dne
| otherwise -> dne
_
| header == fromRef object ++ " missing" -> dne
| otherwise -> error $ "unknown response from git cat-file " ++ show (header, query)
2013-10-20 21:50:51 +00:00
readcontent objtype bytes from sha = do
2012-12-13 04:24:19 +00:00
content <- S.hGet from bytes
2013-05-11 20:32:34 +00:00
eatchar '\n' from
2013-10-20 21:50:51 +00:00
return $ Just (L.fromChunks [content], Ref sha, objtype)
2012-12-13 04:24:19 +00:00
dne = return Nothing
2013-05-11 20:32:34 +00:00
eatchar expected from = do
c <- hGetChar from
when (c /= expected) $
error $ "missing " ++ (show expected) ++ " from git cat-file"
2013-09-19 19:58:35 +00:00
{- Gets a list of files and directories in a tree. (Not recursive.) -}
catTree :: CatFileHandle -> Ref -> IO [(FilePath, FileMode)]
catTree h treeref = go <$> catObjectDetails h treeref
where
2013-10-20 21:50:51 +00:00
go (Just (b, _, TreeObject)) = parsetree [] b
go _ = []
2013-09-19 19:58:35 +00:00
parsetree c b = case L.break (== 0) b of
(modefile, rest)
| L.null modefile -> c
| otherwise -> parsetree
(parsemodefile modefile:c)
(dropsha rest)
-- these 20 bytes after the NUL hold the file's sha
-- TODO: convert from raw form to regular sha
dropsha = L.drop 21
parsemodefile b =
let (modestr, file) = separate (== ' ') (decodeBS b)
2013-09-19 19:58:35 +00:00
in (file, readmode modestr)
readmode = fst . fromMaybe (0, undefined) . headMaybe . readOct