diff --git a/Data/Git/Config.hs b/Data/Git/Config.hs index 82e9de82f5dabdd7222cfd7a175f9b0d48e81996..03c7198067092b8fab28c317e7ccefc11801818f 100644 --- a/Data/Git/Config.hs +++ b/Data/Git/Config.hs @@ -61,9 +61,13 @@ parseConfig = Config . reverse . toSections . foldl accSections ([], Nothing) . strip s = dropSpaces $ reverse $ dropSpaces $ reverse s where dropSpaces = dropWhile (\c -> c == ' ' || c == '\t') +readConfigPath :: LocalPath -> IO Config readConfigPath filepath = parseConfig <$> readTextFile filepath + +readConfig :: LocalPath -> IO Config readConfig gitRepo = readConfigPath (configPath gitRepo) +readGlobalConfig :: IO Config readGlobalConfig = getHomeDirectory >>= readConfigPath . (\homeDir -> homeDir </> ".gitconfig") listSections :: [Config] -> [String] diff --git a/Data/Git/Delta.hs b/Data/Git/Delta.hs index 020f0d26f792a907d8ec64085de6572da5c7e6a3..3757f0c53fb23d5bc7d95bcb0b714efab0a5fa06 100644 --- a/Data/Git/Delta.hs +++ b/Data/Git/Delta.hs @@ -36,6 +36,7 @@ data DeltaCmd = -- * if first byte MSB is set, we copy from source. -- * otherwise, we copy from delta. -- * extensions are not handled. +deltaParse :: P.Parser Delta deltaParse = do srcSize <- getDeltaHdrSize resSize <- getDeltaHdrSize diff --git a/Data/Git/OS.hs b/Data/Git/OS.hs index ca3c089b6a8bbb68d712d9f0719f5a0953c31a48..9c1b70c412fea8eeb31cc1a287f5fec59fbb8a11 100644 --- a/Data/Git/OS.hs +++ b/Data/Git/OS.hs @@ -33,6 +33,7 @@ module Data.Git.OS , valid , getSize , MTime(..) + , timeZero , getMTime , withFile , rename @@ -60,19 +61,28 @@ import qualified Data.ByteString.Lazy as L type LocalPath = FilePath +listDirectoryFilename :: LocalPath -> IO [String] listDirectoryFilename dir = map (Rules.encodeString Rules.posix . filename) <$> listDirectory dir +createParentDirectory :: LocalPath -> IO () createParentDirectory filepath = createTree $ parent filepath +readTextFile :: LocalPath -> IO String readTextFile filepath = Prelude.readFile (encodeString filepath) +writeTextFile :: LocalPath -> String -> IO () writeTextFile filepath = Prelude.writeFile (encodeString filepath) newtype MTime = MTime EpochTime deriving (Eq,Ord) +timeZero :: EpochTime +timeZero = 0 + +getMTime :: LocalPath -> IO MTime getMTime filepath = MTime . modificationTime <$> getFileStatus (encodeString filepath) +getEnvAsPath :: String -> IO LocalPath getEnvAsPath envName = Rules.decodeString Rules.posix <$> getEnv envName localPathDecode :: B.ByteString -> LocalPath diff --git a/Data/Git/Parser.hs b/Data/Git/Parser.hs index f57b9cf63046d225f51075002825837e5e1991eb..095705be847e7c8f36554d773457f9fc61fb6495 100644 --- a/Data/Git/Parser.hs +++ b/Data/Git/Parser.hs @@ -32,9 +32,10 @@ module Data.Git.Parser ) where import qualified Data.ByteArray.Parse as P +import Data.ByteArray (ByteArray) import Data.Bits -import Data.Word (Word8) +import Data.Word (Word8, Word32) import Data.Char (isDigit) import qualified Data.ByteString as B @@ -52,8 +53,10 @@ vlf = do l <- P.anyByte return $ (map (\w -> w `clearBit` 7) $ B.unpack bs) ++ [l] +word32 :: Parser Word32 word32 = be32 <$> P.take 4 +ref, referenceBin, referenceHex :: Parser Ref ref = referenceBin referenceBin = fromBinary <$> P.take 20 referenceHex = fromHex <$> P.take 40 @@ -77,6 +80,7 @@ maybeParseChunks p (i:is) = loop (P.parse p i) is loop (P.ParseMore c) (x:xs) = loop (c $ Just x) xs loop _ _ = Nothing +toMaybe :: P.Result t a -> Maybe a toMaybe (P.ParseOK _ a) = Just a toMaybe (P.ParseMore c) = toMaybe (c Nothing) toMaybe _ = Nothing @@ -93,10 +97,12 @@ eitherParseChunks p (i:is) = loop (P.parse p i) is loop (P.ParseMore c) (x:xs) = loop (c $ Just x) xs loop ps l = Left ("eitherParseChunk: error: " <> show ps <> " : " <> show l) +toEither :: P.Result t b -> Either String b toEither (P.ParseOK _ a) = Right a toEither (P.ParseFail e) = Left e toEither (P.ParseMore c) = toEither (c Nothing) +takeUntilASCII :: ByteArray byteArray => Char -> P.Parser byteArray byteArray takeUntilASCII char = P.takeWhile (\c -> if fromEnum c < 0x80 then fromEnum c /= fromEnum char else True) tillEOL :: Parser B.ByteString @@ -115,6 +121,7 @@ skipASCII c asciiEOL :: Word8 asciiEOL = fromIntegral $ fromEnum '\n' +isByte :: ByteArray byteArray => (Word8 -> Bool) -> P.Parser byteArray Word8 isByte predicate = do b <- P.anyByte if predicate b then return b else fail "isByte" diff --git a/Data/Git/Path.hs b/Data/Git/Path.hs index ff846ae2ceaa22d0c5396885455a61e7e37d4a09..277cf638584118af23240590b727def8cc4f23c2 100644 --- a/Data/Git/Path.hs +++ b/Data/Git/Path.hs @@ -14,6 +14,7 @@ import Data.Git.Imports import Data.Git.OS import Data.String +configPath, headsPath, tagsPath, remotesPath, packedRefsPath :: LocalPath -> LocalPath configPath gitRepo = gitRepo </> "config" headsPath gitRepo = gitRepo </> "refs" </> "heads" </> "" @@ -21,25 +22,33 @@ tagsPath gitRepo = gitRepo </> "refs" </> "tags" </> "" remotesPath gitRepo = gitRepo </> "refs" </> "remotes" </> "" packedRefsPath gitRepo = gitRepo </> "packed-refs" +headPath, tagPath, remotePath, specialPath :: LocalPath -> String -> LocalPath headPath gitRepo name = headsPath gitRepo </> fromString name tagPath gitRepo name = tagsPath gitRepo </> fromString name remotePath gitRepo name = remotesPath gitRepo </> fromString name specialPath gitRepo name = gitRepo </> fromString name +remoteEntPath :: LocalPath -> String -> String -> LocalPath remoteEntPath gitRepo name ent = remotePath gitRepo name </> fromString ent +packDirPath :: LocalPath -> LocalPath packDirPath repoPath = repoPath </> "objects" </> "pack" +indexPath, packPath :: LocalPath -> Ref -> LocalPath indexPath repoPath indexRef = packDirPath repoPath </> fromString ("pack-" ++ toHexString indexRef ++ ".idx") packPath repoPath packRef = packDirPath repoPath </> fromString ("pack-" ++ toHexString packRef ++ ".pack") +objectPath :: LocalPath -> String -> String -> LocalPath objectPath repoPath d f = repoPath </> "objects" </> fromString d </> fromString f + +objectPathOfRef :: LocalPath -> Ref -> LocalPath objectPathOfRef repoPath ref = objectPath repoPath d f where (d,f) = toFilePathParts ref +objectTemporaryPath :: LocalPath -> IO LocalPath objectTemporaryPath repoPath = do r <- fst . random <$> getStdGen :: IO Int return (repoPath </> "objects" </> fromString ("tmp-" ++ show r ++ ".tmp")) diff --git a/Data/Git/Ref.hs b/Data/Git/Ref.hs index 02778462be72407eb4ad897bb76e1cf428af81f6..53ebcceb90de475ad716ef1afac13ba2c7a62de3 100644 --- a/Data/Git/Ref.hs +++ b/Data/Git/Ref.hs @@ -63,7 +63,10 @@ data RefNotFound = RefNotFound Ref instance Exception RefInvalid instance Exception RefNotFound +isHex :: ByteString -> Bool isHex = and . map isHexDigit . BC.unpack + +isHexString :: String -> Bool isHexString = and . map isHexDigit -- | take a hexadecimal bytestring that represent a reference diff --git a/Data/Git/Repository.hs b/Data/Git/Repository.hs index b294fef9de14695dba7b037d02067a7d51596c23..244ae1db9f1d927c5f1d8aaf9736443855871a6c 100644 --- a/Data/Git/Repository.hs +++ b/Data/Git/Repository.hs @@ -75,6 +75,7 @@ data InvalidType = InvalidType Ref ObjectType instance Exception InvalidType -- should be a standard function that do that... +mapJustM :: Monad m => (t -> m (Maybe a)) -> Maybe t -> m (Maybe a) mapJustM f (Just o) = f o mapJustM _ Nothing = return Nothing diff --git a/Data/Git/Revision.hs b/Data/Git/Revision.hs index d13095b70939a85b7179fb6f793bf2b322b8668a..42fb472546159bd87eddb733d54200011ed53dad 100644 --- a/Data/Git/Revision.hs +++ b/Data/Git/Revision.hs @@ -87,11 +87,13 @@ revFromString s = either (error.show) fst $ runStream parser s RevModAtDate <$> many (noneOf "}") -- combinator -char c = eatRet (\x -> if x == c then Just c else Nothing) -string s = prefix (\x -> if isPrefixOf s x then Just (s, length s) else Nothing) -digit = eatRet (\x -> if isDigit x then Just x else Nothing) -noneOf l = eatRet (\x -> if not (x `elem` l) then Just x else Nothing) + char c = eatRet (\x -> if x == c then Just c else Nothing) + string str = prefix (\x -> if isPrefixOf str x then Just (str, length str) else Nothing) + digit = eatRet (\x -> if isDigit x then Just x else Nothing) + noneOf l = eatRet (\x -> if not (x `elem` l) then Just x else Nothing) + +prefix :: ([elem] -> Maybe (a, Int)) -> Stream elem a prefix predicate = Stream $ \el -> case el of [] -> Left ("empty stream: prefix") @@ -109,14 +111,6 @@ eatRet predicate = Stream $ \el -> Just a -> Right (a, xs) Nothing -> Left ("unexpected atom got: " ++ show x) -eat :: Show elem => (elem -> Bool) -> Stream elem () -eat predicate = Stream $ \el -> - case el of - [] -> Left ("empty stream: eating") - x:xs - | predicate x -> Right ((), xs) - | otherwise -> Left ("unexpected atom got: " ++ show x) - newtype Stream elem a = Stream { runStream :: [elem] -> Either String (a, [elem]) } instance Functor (Stream elem) where fmap f s = Stream $ \e1 -> case runStream s e1 of diff --git a/Data/Git/Storage.hs b/Data/Git/Storage.hs index 2d22bdb127ef50c911c79b991ddc957434304bd3..f5cbad8d4a641bb6c9e7587f132824c836ddfb31 100644 --- a/Data/Git/Storage.hs +++ b/Data/Git/Storage.hs @@ -141,6 +141,7 @@ findRepo = do if e then return filepath else checkDir (n+1) (if absolute wd then parent wd else wd </> "..") -- | execute a function f with a git context. +withRepo :: LocalPath -> (Git -> IO c) -> IO c withRepo path f = bracket (openRepo path) closeRepo f -- | execute a function on the current repository. @@ -187,6 +188,9 @@ setDescription git desc = do writeTextFile descriptionPath desc where descriptionPath = (gitRepoPath git) </> "description" +iterateIndexes :: Git + -> (b -> (Ref, PackIndexReader) -> IO (b, Bool)) + -> b -> IO b iterateIndexes git f initAcc = do allIndexes <- packIndexEnumerate (gitRepoPath git) readers <- readIORef (indexReaders git) diff --git a/Data/Git/Storage/CacheFile.hs b/Data/Git/Storage/CacheFile.hs index 3bfb33fe0e3103064d7131aeb043caa3761d86ba..4b53e68a5e6cc1187ae0bd79f56b2cfa2f237103 100644 --- a/Data/Git/Storage/CacheFile.hs +++ b/Data/Git/Storage/CacheFile.hs @@ -20,8 +20,6 @@ data CacheFile a = CacheFile , cacheLock :: MVar (MTime, a) } -timeZero = 0 - newCacheVal :: LocalPath -> IO a -> a -> IO (CacheFile a) newCacheVal path refresh initialVal = CacheFile path refresh initialVal <$> newMVar (MTime timeZero, initialVal) @@ -35,4 +33,5 @@ getCacheVal cachefile = modifyMVar (cacheLock cachefile) getOrRefresh Just newMtime | newMtime > mtime -> cacheRefresh cachefile >>= \v -> return ((newMtime, v), v) | otherwise -> return (s, cachedVal) +tryGetMTime :: LocalPath -> IO (Maybe MTime) tryGetMTime filepath = (Just <$> getMTime filepath) `E.catch` \(_ :: E.SomeException) -> return Nothing diff --git a/Data/Git/Storage/FileReader.hs b/Data/Git/Storage/FileReader.hs index 5f7262f4d2f2b69cfbb2cffab8e313554a5b017d..f08aa2ae1a8f1ef6a5f96fc71c4612b8e152a864 100644 --- a/Data/Git/Storage/FileReader.hs +++ b/Data/Git/Storage/FileReader.hs @@ -168,12 +168,13 @@ fileReaderInflateToSize fb@(FileReader { fbRemaining = ref }) outputSize = do augmentAndRaise left exn = throwIO $ InflateException outputSize left (show exn) -- lowlevel helpers to inflate only to a specific size. - +inflateNew :: IO (ForeignPtr ZStreamStruct) inflateNew = do zstr <- zstreamNew inflateInit2 zstr defaultWindowBits newForeignPtr c_free_z_stream_inflate zstr +inflateToSize :: ForeignPtr ZStreamStruct -> Int -> Bool -> ByteString -> IO ByteString -> IO (ByteString, ByteString) inflateToSize inflate sz isLastBlock ibs nextBs = withForeignPtr inflate $ \zstr -> do let boundSz = min defaultChunkSize sz -- create an output buffer diff --git a/Data/Git/Storage/FileWriter.hs b/Data/Git/Storage/FileWriter.hs index 3a9074df8ab03a032ec83ae98267af0f0e96c7b1..4eda47379f828840c0d547815aae32107e7b3979 100644 --- a/Data/Git/Storage/FileWriter.hs +++ b/Data/Git/Storage/FileWriter.hs @@ -16,6 +16,7 @@ import Control.Exception (bracket) import Crypto.Hash +defaultCompression :: Int defaultCompression = 6 -- this is a copy of modifyIORef' found in base 4.6 (ghc 7.6), @@ -32,6 +33,7 @@ data FileWriter = FileWriter , writerDigest :: IORef (Context SHA1) } +fileWriterNew :: Handle -> IO FileWriter fileWriterNew handle = do deflate <- initDeflate defaultCompression defaultWindowBits digest <- newIORef hashInit @@ -41,17 +43,22 @@ fileWriterNew handle = do , writerDigest = digest } +withFileWriter :: LocalPath -> (FileWriter -> IO c) -> IO c withFileWriter path f = bracket (openFile path WriteMode) (hClose) $ \handle -> bracket (fileWriterNew handle) (fileWriterClose) f +postDeflate :: Handle -> Maybe B.ByteString -> IO () postDeflate handle = maybe (return ()) (B.hPut handle) +fileWriterOutput :: FileWriter -> B.ByteString -> IO () fileWriterOutput (FileWriter { writerHandle = handle, writerDigest = digest, writerDeflate = deflate }) bs = do modifyIORefStrict digest (\ctx -> hashUpdate ctx bs) (>>= postDeflate handle) =<< feedDeflate deflate bs +fileWriterClose :: FileWriter -> IO () fileWriterClose (FileWriter { writerHandle = handle, writerDeflate = deflate }) = postDeflate handle =<< finishDeflate deflate +fileWriterGetDigest :: FileWriter -> IO Ref fileWriterGetDigest (FileWriter { writerDigest = digest }) = (fromDigest . hashFinalize) `fmap` readIORef digest diff --git a/Data/Git/Storage/Loose.hs b/Data/Git/Storage/Loose.hs index 997e0dc861f98e4adda25c400989aec31f61c79a..ed7137f891883a929474eb0a2d6936783423b669 100644 --- a/Data/Git/Storage/Loose.hs +++ b/Data/Git/Storage/Loose.hs @@ -57,6 +57,7 @@ readZippedFile fp = Zipped <$> readBinaryFileLazy fp dezip :: Zipped -> L.ByteString dezip = decompress . getZippedData +isObjectPrefix :: [Char] -> Bool isObjectPrefix [a,b] = isHexDigit a && isHexDigit b isObjectPrefix _ = False @@ -70,6 +71,7 @@ parseHeader = do data HeaderType = HeaderTree | HeaderTag | HeaderCommit | HeaderBlob +parseTreeHeader, parseTagHeader, parseCommitHeader, parseBlobHeader :: P.Parser HeaderType parseTreeHeader = P.string "tree " >> parseLength >> P.byte 0 >> return HeaderTree parseTagHeader = P.string "tag " >> parseLength >> P.byte 0 >> return HeaderTag parseCommitHeader = P.string "commit " >> parseLength >> P.byte 0 >> return HeaderCommit @@ -78,13 +80,6 @@ parseBlobHeader = P.string "blob " >> parseLength >> P.byte 0 >> return Header parseLength :: P.Parser Int parseLength = P.decimal -{- -parseTree = parseTreeHeader >> objectParseTree -parseTag = parseTagHeader >> objectParseTag -parseCommit = parseCommitHeader >> objectParseCommit -parseBlob = parseBlobHeader >> objectParseBlob --} - parseObject :: L.ByteString -> Object parseObject = parseSuccess getOne where @@ -122,6 +117,7 @@ looseUnmarshallZippedRaw :: Zipped -> (ObjectHeader, ObjectData) looseUnmarshallZippedRaw = looseUnmarshallRaw . dezip -- | read a specific ref from a loose object and returns an header and data. +looseReadRaw :: LocalPath -> Ref -> IO (ObjectHeader, ObjectData) looseReadRaw repoPath ref = looseUnmarshallZippedRaw <$> readZippedFile (objectPathOfRef repoPath ref) -- | read only the header of a loose object. @@ -131,12 +127,15 @@ looseReadHeader repoPath ref = toHeader <$> readZippedFile (objectPathOfRef repo toHeader = either (error . ("parseHeader: " ++)) id . P.eitherParseChunks parseHeader . L.toChunks . dezip -- | read a specific ref from a loose object and returns an object +looseRead :: LocalPath -> Ref -> IO Object looseRead repoPath ref = looseUnmarshallZipped <$> readZippedFile (objectPathOfRef repoPath ref) -- | check if a specific ref exists as loose object +looseExists :: LocalPath -> Ref -> IO Bool looseExists repoPath ref = isFile (objectPathOfRef repoPath ref) -- | enumarate all prefixes available in the object store. +looseEnumeratePrefixes :: LocalPath -> IO [[Char]] looseEnumeratePrefixes repoPath = filter isObjectPrefix <$> getDirectoryContents (repoPath </> fromString "objects") -- | enumerate all references available with a specific prefix. @@ -152,6 +151,7 @@ looseEnumerateWithPrefix repoPath prefix = looseEnumerateWithPrefixFilter repoPath prefix (const True) -- | marshall as lazy bytestring an object except deltas. +looseMarshall :: Object -> L.ByteString looseMarshall obj | objectIsDelta obj = error "cannot write delta object loose" | otherwise = L.concat [ L.fromChunks [hdrB], objData ] @@ -161,6 +161,7 @@ looseMarshall obj -- | create a new blob on a temporary location and on success move it to -- the object store with its digest name. +looseWriteBlobFromFile :: LocalPath -> LocalPath -> IO Ref looseWriteBlobFromFile repoPath file = do fsz <- getSize file let hdr = objectWriteHeader TypeBlob (fromIntegral fsz) @@ -183,6 +184,7 @@ looseWriteBlobFromFile repoPath file = do -- | write an object to disk as a loose reference. -- use looseWriteBlobFromFile for efficiently writing blobs when being commited from a file. +looseWrite :: LocalPath -> Object -> IO Ref looseWrite repoPath obj = createParentDirectory path >> isFile path >>= \exists -> unless exists (writeFileLazy path $ compress content) @@ -193,4 +195,5 @@ looseWrite repoPath obj = createParentDirectory path ref = hashLBS content writeFileLazy p bs = withFile p WriteMode (\h -> L.hPut h bs) +getDirectoryContents :: LocalPath -> IO [String] getDirectoryContents p = listDirectoryFilename p diff --git a/Data/Git/Storage/Object.hs b/Data/Git/Storage/Object.hs index 564d4304e4d5d0d9ca329275d83b9f715a0a0914..38f067a63266f26a72a15fc0fb1ee1e57b565dff 100644 --- a/Data/Git/Storage/Object.hs +++ b/Data/Git/Storage/Object.hs @@ -165,15 +165,18 @@ modeperm :: P.Parser ModePerm modeperm = ModePerm . fromIntegral <$> octal -- | parse a tree content +treeParse :: P.Parser Tree treeParse = Tree <$> parseEnts where parseEnts = P.hasMore >>= \b -> if b then liftM2 (:) parseEnt parseEnts else return [] parseEnt = liftM3 (,,) modeperm parseEntName (P.byte 0 >> P.referenceBin) parseEntName = entName <$> (P.skipASCII ' ' >> P.takeWhile (/= 0)) -- | parse a blob content +blobParse :: P.Parser Blob blobParse = (Blob . L.fromChunks . (:[]) <$> P.takeAll) -- | parse a commit content +commitParse :: P.Parser Commit commitParse = do tree <- P.string "tree " >> P.referenceHex P.skipEOL @@ -202,6 +205,7 @@ commitParse = do concatLines = B.concat . intersperse (B.pack [0xa]) -- | parse a tag content +tagParse :: P.Parser Tag tagParse = do object <- P.string "object " >> P.referenceHex P.skipEOL @@ -214,6 +218,7 @@ tagParse = do signature <- P.takeAll return $ Tag object type_ tag tagger signature +parsePerson :: P.Parser Person parsePerson = do name <- B.init <$> P.takeUntilASCII '<' P.skipASCII '<' @@ -228,6 +233,7 @@ parsePerson = do P.skipEOL return $ Person name email (gitTime time timezone) +ascii :: Char -> P.Parser () ascii c = P.byte (asciiChar c) asciiChar :: Char -> Word8 @@ -236,6 +242,7 @@ asciiChar c | otherwise = error ("char " <> show c <> " not valid ASCII") where cp = fromEnum c +objectParseTree, objectParseCommit, objectParseTag, objectParseBlob :: P.Parser Object objectParseTree = ObjTree <$> treeParse objectParseCommit = ObjCommit <$> commitParse objectParseTag = ObjTag <$> tagParse @@ -252,6 +259,7 @@ objectWrite (ObjBlob blob) = blobWrite blob objectWrite (ObjTree tree) = treeWrite tree objectWrite _ = error "delta cannot be marshalled" +treeWrite :: Tree -> L.ByteString treeWrite (Tree ents) = toLazyByteString $ mconcat $ concatMap writeTreeEnt ents where writeTreeEnt (ModePerm perm,name,ref) = [ string7 (printf "%o" perm) @@ -261,6 +269,7 @@ treeWrite (Tree ents) = toLazyByteString $ mconcat $ concatMap writeTreeEnt ents , byteString $ toBinary ref ] +commitWrite :: Commit -> L.ByteString commitWrite (Commit tree parents author committer encoding extra msg) = toLazyByteString $ mconcat els where @@ -283,6 +292,7 @@ commitWrite (Commit tree parents author committer encoding extra msg) = ,byteString msg ] +tagWrite :: Tag -> L.ByteString tagWrite (Tag ref ty tag tagger signature) = toLazyByteString $ mconcat els where els = [ string7 "object ", byteString (toHex ref), eol @@ -293,8 +303,10 @@ tagWrite (Tag ref ty tag tagger signature) = , byteString signature ] +eol :: Builder eol = string7 "\n" +blobWrite :: Blob -> L.ByteString blobWrite (Blob bData) = bData instance Objectable Blob where @@ -337,6 +349,7 @@ objectHash :: ObjectType -> Word64 -> L.ByteString -> Ref objectHash ty w lbs = hashLBS $ L.fromChunks (objectWriteHeader ty w : L.toChunks lbs) -- used for objectWrite for commit and tag +writeName :: ByteString -> Person -> ByteString writeName label (Person name email locTime) = B.concat [label, " ", name, " <", email, "> ", BC.pack timeStr] where timeStr = show locTime diff --git a/Data/Git/Storage/Pack.hs b/Data/Git/Storage/Pack.hs index ff923c861f32c5d408ab207c1227cc8e2e79a649..2a0b996e3d6debaf213f85dbb91f566eb260c3d7 100644 --- a/Data/Git/Storage/Pack.hs +++ b/Data/Git/Storage/Pack.hs @@ -55,6 +55,7 @@ data PackedObjectInfo = PackedObjectInfo } deriving (Show,Eq) -- | Enumerate the pack refs available in this repository. +packEnumerate :: LocalPath -> IO [Ref] packEnumerate repoPath = map onlyHash . filter isPackFile <$> listDirectoryFilename (repoPath </> "objects" </> "pack") where isPackFile :: String -> Bool @@ -71,6 +72,7 @@ packClose :: FileReader -> IO () packClose = fileReaderClose -- | return the number of entries in this pack +packReadHeader :: LocalPath -> Ref -> IO Word32 packReadHeader repoPath packRef = withFileReader (packPath repoPath packRef) $ \filereader -> fileReaderParse filereader parseHeader @@ -82,6 +84,7 @@ packReadHeader repoPath packRef = P.word32 -- | read an object at a specific position using a map function on the objectData +packReadMapAtOffset :: FileReader -> Word64 -> (L.ByteString -> L.ByteString) -> IO (Maybe Object) packReadMapAtOffset fr offset mapData = fileReaderSeek fr offset >> getNextObject fr mapData -- | read an object at a specific position @@ -93,6 +96,7 @@ packReadRawAtOffset :: FileReader -> Word64 -> IO (PackedObjectRaw) packReadRawAtOffset fr offset = fileReaderSeek fr offset >> getNextObjectRaw fr -- | enumerate all objects in this pack and callback to f for reach raw objects +packEnumerateObjects :: LocalPath -> Ref -> Int -> (PackedObjectRaw -> IO a) -> IO () packEnumerateObjects repoPath packRef entries f = withFileReader (packPath repoPath packRef) $ \filebuffer -> do fileReaderSeek filebuffer 12 @@ -106,9 +110,11 @@ getNextObject :: FileReader -> (L.ByteString -> L.ByteString) -> IO (Maybe Objec getNextObject fr mapData = packedObjectToObject . second mapData <$> getNextObjectRaw fr +packedObjectToObject :: (PackedObjectInfo, L.ByteString) -> Maybe Object packedObjectToObject (PackedObjectInfo { poiType = ty, poiExtra = extra }, objData) = packObjectFromRaw (ty, extra, objData) +packObjectFromRaw :: (ObjectType, Maybe ObjectPtr, L.ByteString) -> Maybe Object packObjectFromRaw (TypeCommit, Nothing, objData) = P.maybeParseChunks objectParseCommit (L.toChunks objData) packObjectFromRaw (TypeTree, Nothing, objData) = P.maybeParseChunks objectParseTree (L.toChunks objData) packObjectFromRaw (TypeBlob, Nothing, objData) = P.maybeParseChunks objectParseBlob (L.toChunks objData) diff --git a/Data/Git/Storage/PackIndex.hs b/Data/Git/Storage/PackIndex.hs index 258e0d59aaed6d1e8cac9f68b46929f632827667..3982a35225b2d9c9a21385ef1ab15566a83982a7 100644 --- a/Data/Git/Storage/PackIndex.hs +++ b/Data/Git/Storage/PackIndex.hs @@ -28,6 +28,7 @@ module Data.Git.Storage.PackIndex import Data.List import Data.Bits import Data.Word +import Data.ByteString (ByteString) import Data.Vector (Vector, (!)) import qualified Data.Vector as V @@ -53,6 +54,7 @@ data PackIndex = PackIndex } -- | enumerate every indexes file in the pack directory +packIndexEnumerate :: LocalPath -> IO [Ref] packIndexEnumerate repoPath = map onlyHash . filter isPackFile <$> listDirectoryFilename (repoPath </> "objects" </> "pack") where isPackFile :: String -> Bool @@ -69,6 +71,7 @@ packIndexClose :: FileReader -> IO () packIndexClose = fileReaderClose -- | variant of withFile on the index file and with a FileReader +withPackIndex :: LocalPath -> Ref -> (FileReader -> IO a) -> IO a withPackIndex repoPath indexRef = withFileReader (indexPath repoPath indexRef) -- | returns the number of references, referenced in this index. @@ -133,6 +136,7 @@ packIndexGetReferencesWithPrefix idxHdr fr prefix = refprefix = read ("0x" ++ take 2 prefix) -- | returns absolute offset in the index file of the sha1s, the crcs and the packfiles offset. +packIndexOffsets :: PackIndexHeader -> (Word32, Word32, Word32) packIndexOffsets idx = (packIndexSha1sOffset, packIndexCRCsOffset, packIndexPackOffOffset) where packIndexPackOffOffset = packIndexCRCsOffset + crcsTableSz @@ -143,6 +147,7 @@ packIndexOffsets idx = (packIndexSha1sOffset, packIndexCRCsOffset, packIndexPack sz = packIndexHeaderGetSize idx -- | parse index header +parsePackIndexHeader :: P.Parser PackIndexHeader parsePackIndexHeader = do magic <- P.word32 when (magic /= 0xff744f63) $ error "wrong magic number for packIndex" @@ -160,6 +165,7 @@ packIndexGetHeader :: LocalPath -> Ref -> IO PackIndexHeader packIndexGetHeader repoPath indexRef = withPackIndex repoPath indexRef $ packIndexReadHeader -- | read all index +packIndexRead :: LocalPath -> Ref -> IO (PackIndexHeader, (Vector Ref, Vector Word32, Vector Word32, [ByteString], Ref, Ref)) packIndexRead repoPath indexRef = do withPackIndex repoPath indexRef $ \fr -> do idx <- fileReaderParse fr parsePackIndexHeader diff --git a/git.cabal b/git.cabal index a330f0328d7ac29e36b40972de5cb018617b28fd..f917c13a6d0d10a32862520f2b5f61edd6e629ab 100644 --- a/git.cabal +++ b/git.cabal @@ -75,7 +75,8 @@ Library Data.Git.Path Data.Git.Parser Data.Git.WorkTree - ghc-options: -Wall -fno-warn-unused-imports -fno-warn-missing-signatures + ghc-options: -Wall -fno-warn-unused-imports + -- -fno-warn-missing-signatures Test-Suite test-unit