hit 0.5.5 → 0.6.0
raw patch · 13 files changed
+243/−93 lines, 13 filesdep +hourglassdep +unixdep −timedep ~base
Dependencies added: hourglass, unix
Dependencies removed: time
Dependency ranges changed: base
Files
- Data/Git.hs +12/−1
- Data/Git/Config.hs +58/−0
- Data/Git/Named.hs +36/−17
- Data/Git/Path.hs +2/−0
- Data/Git/Repository.hs +77/−3
- Data/Git/Storage.hs +9/−3
- Data/Git/Storage/CacheFile.hs +8/−7
- Data/Git/Storage/Object.hs +9/−6
- Data/Git/Types.hs +7/−22
- Hit/Hit.hs +11/−8
- Tests/Repo.hs +0/−3
- Tests/Tests.hs +7/−19
- hit.cabal +7/−4
Data/Git.hs view
@@ -9,13 +9,14 @@ ( -- * Basic types Ref+ , RefName(..) , Commit(..) , Person(..) , CommitExtra(..) , Tree(..) , Blob(..) , Tag(..)- , GitTime(..)+ , GitTime , ModePerm(..) -- * Helper & type related to ModePerm@@ -51,6 +52,15 @@ -- * Set objects , setObject+ , toObject++ -- * Named refs+ , branchWrite+ , branchList+ , tagWrite+ , tagList+ , headSet+ , headGet ) where import Data.Git.Ref@@ -58,3 +68,4 @@ import Data.Git.Storage import Data.Git.Repository import Data.Git.Revision+import Data.Git.Storage.Object (toObject)
+ Data/Git/Config.hs view
@@ -0,0 +1,58 @@+-- |+-- Module : Data.Git.Config+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unix+--+-- config related types and methods.+--+module Data.Git.Config+ ( Config(..)+ , Section(..)+ -- * methods+ , readConfig+ ) where++import Control.Applicative+import Data.Git.Path+import Filesystem.Path.CurrentOS++newtype Config = Config [Section]+ deriving (Show,Eq)++data Section = Section+ { sectionName :: String+ , sectionKVs :: [(String, String)]+ } deriving (Show,Eq)++parseConfig :: String -> Config+parseConfig = Config . reverse . toSections . foldl accSections ([], Nothing) . lines+ where toSections (l,Nothing) = l+ toSections (l,Just s) = s : l++ -- a new section in the config file+ accSections (sections, mcurrent) ('[':sectNameE)+ | last sectNameE == ']' =+ let sectName = take (length sectNameE - 1) sectNameE+ in case mcurrent of+ Nothing -> (sections, Just $ Section sectName [])+ Just current -> (sectionFinalize current : sections, Just $ Section sectName [])+ | otherwise =+ (sections, mcurrent)+ -- a normal line without having any section defined yet+ accSections acc@(_, Nothing) _ = acc+ -- potentially a k-v line in an existing section+ accSections (sections, Just current) kvLine =+ case break (== '=') kvLine of+ (k,'=':v) -> (sections, Just $ sectionAppend current (strip k, strip v))+ (_,_) -> (sections, Just current) -- not a k = v line+ -- append a key-value+ sectionAppend (Section n l) kv = Section n (kv:l)+ sectionFinalize (Section n l) = Section n $ reverse l++ strip s = dropSpaces $ reverse $ dropSpaces $ reverse s+ where dropSpaces = dropWhile (\c -> c == ' ' || c == '\t')++readConfigPath filepath = parseConfig <$> readFile (encodeString filepath)+readConfig gitRepo = readConfigPath (configPath gitRepo)
Data/Git/Named.hs view
@@ -15,14 +15,15 @@ , RefContentTy(..) , RefName(..) , readPackedRefs+ , PackedRefs(..) -- * manipulating loosed name references , existsRefFile , writeRefFile , readRefFile -- * listings looses name references- , headsList- , tagsList- , remotesList+ , looseHeadsList+ , looseTagsList+ , looseRemotesList ) where import Control.Applicative ((<$>))@@ -119,17 +120,35 @@ toPath gitRepo RefFetchHead = gitRepo </> "FETCH_HEAD" toPath gitRepo (RefOther h) = gitRepo </> fromString h -readPackedRefs :: FilePath -> IO [(RefSpecTy, Ref)]-readPackedRefs gitRepo = do+data PackedRefs a = PackedRefs+ { packedRemotes :: a+ , packedBranchs :: a+ , packedTags :: a+ }++readPackedRefs :: FilePath+ -> ([(RefName, Ref)] -> a)+ -> IO (PackedRefs a)+readPackedRefs gitRepo constr = do exists <- F.isFile (packedRefsPath gitRepo)- if exists then readLines else return []- where readLines = foldl accu [] . BC.lines <$> F.readFile (packedRefsPath gitRepo)- accu a l+ if exists then readLines else return $ finalize emptyPackedRefs+ where emptyPackedRefs = PackedRefs [] [] []+ readLines = finalize . foldl accu emptyPackedRefs . BC.lines <$> F.readFile (packedRefsPath gitRepo)+ finalize (PackedRefs a b c) = PackedRefs (constr a) (constr b) (constr c)+ accu a l | "#" `BC.isPrefixOf` l = a- | otherwise = let (ref, r) = B.splitAt 40 l- name = FP.encodeString FP.posix $ pathDecode $ B.tail r- in (toRefTy name, fromHex ref) : a+ | otherwise =+ let (ref, r) = B.splitAt 40 l+ name = FP.encodeString FP.posix $ pathDecode $ B.tail r+ in case toRefTy name of+ -- accumulate tag, branch and remotes+ RefTag refname -> a { packedTags = (refname, fromHex ref) : packedTags a }+ RefBranch refname -> a { packedBranchs = (refname, fromHex ref) : packedBranchs a }+ RefRemote refname -> a { packedRemotes = (refname, fromHex ref) : packedRemotes a }+ -- anything else that shouldn't be there get dropped on the floor+ _ -> a +-- | list all the loose refs available recursively from a directory starting point listRefs :: FilePath -> IO [RefName] listRefs root = listRefsAcc [] root where listRefsAcc acc dir = do@@ -147,14 +166,14 @@ getRefsRecursively dir (extra ++ acc) xs stripRoot p = maybe (error "stripRoot invalid") id $ stripPrefix root p -headsList :: FilePath -> IO [RefName]-headsList gitRepo = listRefs (headsPath gitRepo)+looseHeadsList :: FilePath -> IO [RefName]+looseHeadsList gitRepo = listRefs (headsPath gitRepo) -tagsList :: FilePath -> IO [RefName]-tagsList gitRepo = listRefs (tagsPath gitRepo)+looseTagsList :: FilePath -> IO [RefName]+looseTagsList gitRepo = listRefs (tagsPath gitRepo) -remotesList :: FilePath -> IO [RefName]-remotesList gitRepo = listRefs (remotesPath gitRepo)+looseRemotesList :: FilePath -> IO [RefName]+looseRemotesList gitRepo = listRefs (remotesPath gitRepo) existsRefFile :: FilePath -> RefSpecTy -> IO Bool existsRefFile gitRepo specty = F.isFile $ toPath gitRepo specty
Data/Git/Path.hs view
@@ -14,6 +14,8 @@ import Data.Git.Ref import Data.String +configPath gitRepo = gitRepo </> "config"+ headsPath gitRepo = gitRepo </> "refs" </> "heads" </> "" tagsPath gitRepo = gitRepo </> "refs" </> "tags" </> "" remotesPath gitRepo = gitRepo </> "refs" </> "remotes" </> ""
Data/Git/Repository.hs view
@@ -10,8 +10,14 @@ -- module Data.Git.Repository ( Git+ -- * Config+ , configRead+ , Cfg.Config(..)+ , Cfg.Section(..)+ -- * Trees , HTree , HTreeEnt(..)+ , RefName(..) , getCommitMaybe , getCommit , getTreeMaybe@@ -23,6 +29,13 @@ , resolveRevision , initRepo , isRepo+ -- * named refs manipulation+ , branchWrite+ , branchList+ , tagWrite+ , tagList+ , headSet+ , headGet ) where import Control.Applicative ((<$>))@@ -43,8 +56,12 @@ import Data.Git.Storage.Loose import Data.Git.Storage.CacheFile import Data.Git.Ref+import qualified Data.Git.Config as Cfg +import Data.Set (Set)+ import qualified Data.Map as M+import qualified Data.Set as Set -- | hierarchy tree, either a reference to a blob (file) or a tree (directory). data HTreeEnt = TreeDir Ref HTree | TreeFile Ref@@ -100,9 +117,12 @@ RefDirect ref -> return $ Just ref RefLink refspecty -> followToRef onFailure refspecty _ -> error "cannot handle reference content"- else case M.lookup refty lookupCache of- Nothing -> onFailure- y -> return y+ else case refty of+ RefTag name -> mapLookup name $ packedTags lookupCache+ RefBranch name -> mapLookup name $ packedBranchs lookupCache+ RefRemote name -> mapLookup name $ packedRemotes lookupCache+ _ -> return Nothing+ where mapLookup name m = maybe onFailure (return . Just) $ M.lookup name m namedResolvers = case prefix of "HEAD" -> [ RefHead ]@@ -220,3 +240,57 @@ findEnt x = find (\(_, b, _) -> b == x) treeEntRef (_,_,r) = r++-- | Write a branch to point to a specific reference+branchWrite :: Git -- ^ repository+ -> RefName -- ^ the name of the branch to write+ -> Ref -- ^ the reference to set+ -> IO ()+branchWrite git branchName ref =+ writeRefFile (gitRepoPath git) (RefBranch branchName) (RefDirect ref)++-- | Return the list of branches+branchList :: Git -> IO (Set RefName)+branchList git = do+ ps <- Set.fromList . M.keys . packedBranchs <$> getCacheVal (packedNamed git)+ ls <- Set.fromList <$> looseHeadsList (gitRepoPath git)+ return $ Set.union ps ls++-- | Write a tag to point to a specific reference+tagWrite :: Git -- ^ repository+ -> RefName -- ^ the name of the tag to write+ -> Ref -- ^ the reference to set+ -> IO ()+tagWrite git tagname ref =+ writeRefFile (gitRepoPath git) (RefTag tagname) (RefDirect ref)++-- | Return the list of branches+tagList :: Git -> IO (Set RefName)+tagList git = do+ ps <- Set.fromList . M.keys . packedTags <$> getCacheVal (packedNamed git)+ ls <- Set.fromList <$> looseTagsList (gitRepoPath git)+ return $ Set.union ps ls++-- | Set head to point to either a reference or a branch name.+headSet :: Git -- ^ repository+ -> Either Ref RefName -- ^ either a raw reference or a branch name+ -> IO ()+headSet git (Left ref) =+ writeRefFile (gitRepoPath git) RefHead (RefDirect ref)+headSet git (Right refname) =+ writeRefFile (gitRepoPath git) RefHead (RefLink $ RefBranch refname)++-- | Get what the head is pointing to, or the reference otherwise+headGet :: Git+ -> IO (Either Ref RefName)+headGet git = do+ content <- readRefFile (gitRepoPath git) RefHead+ case content of+ RefLink (RefBranch b) -> return $ Right b+ RefLink spec -> error ("unknown content link in HEAD: " ++ show spec)+ RefDirect r -> return $ Left r+ RefContentUnknown bs -> error ("unknown content in HEAD: " ++ show bs)++-- | Read the Config+configRead :: Git -> IO Cfg.Config+configRead git = Cfg.readConfig (gitRepoPath git)
Data/Git/Storage.hs view
@@ -12,6 +12,7 @@ ( Git , packedNamed , gitRepoPath+ -- * opening repositories , openRepo , closeRepo , withRepo@@ -19,9 +20,12 @@ , findRepoMaybe , findRepo , isRepo+ -- * creating repositories , initRepo+ -- * repository accessors , getDescription , setDescription+ -- * iterators , iterateIndexes , findReference , findReferencesWithPrefix@@ -69,7 +73,7 @@ data PackIndexReader = PackIndexReader PackIndexHeader FileReader -- | this is a cache representation of the packed-ref file-type PackedRef = CacheFile (M.Map RefSpecTy Ref)+type CachedPackedRef = CacheFile (PackedRefs (M.Map RefName Ref)) -- | represent a git repo, with possibly already opened filereaders -- for indexes and packs@@ -77,13 +81,15 @@ { gitRepoPath :: FilePath , indexReaders :: IORef [(Ref, PackIndexReader)] , packReaders :: IORef [(Ref, FileReader)]- , packedNamed :: PackedRef+ , packedNamed :: CachedPackedRef } -- | open a new git repository context openRepo :: FilePath -> IO Git openRepo path = liftM3 (Git path) (newIORef []) (newIORef []) packedRef- where packedRef = newCacheVal (packedRefsPath path) (M.fromList <$> readPackedRefs path) M.empty+ where packedRef = newCacheVal (packedRefsPath path)+ (readPackedRefs path M.fromList)+ (PackedRefs M.empty M.empty M.empty) -- | close a git repository context, closing all remaining fileReaders. closeRepo :: Git -> IO ()
Data/Git/Storage/CacheFile.hs view
@@ -11,10 +11,11 @@ import Control.Applicative ((<$>)) import Control.Concurrent.MVar import qualified Control.Exception as E-import Data.Time.Clock-import Filesystem import Filesystem.Path+import Filesystem.Path.CurrentOS (encodeString) import Prelude hiding (FilePath)+import System.Posix.Files (getFileStatus, modificationTime)+import System.Posix.Types (EpochTime) data CacheFile a = CacheFile { cacheFilepath :: FilePath@@ -23,22 +24,22 @@ , cacheLock :: MVar (MTime, a) } -utcZero = UTCTime (toEnum 0) 0+timeZero = 0 newCacheVal :: FilePath -> IO a -> a -> IO (CacheFile a) newCacheVal path refresh initialVal =- CacheFile path refresh initialVal <$> newMVar (MTime utcZero, initialVal)+ CacheFile path refresh initialVal <$> newMVar (MTime timeZero, initialVal) getCacheVal :: CacheFile a -> IO a getCacheVal cachefile = modifyMVar (cacheLock cachefile) getOrRefresh where getOrRefresh s@(mtime, cachedVal) = do cMTime <- getMTime $ cacheFilepath cachefile case cMTime of- Nothing -> return ((MTime utcZero, cacheIniVal cachefile), cacheIniVal cachefile)+ Nothing -> return ((MTime timeZero, cacheIniVal cachefile), cacheIniVal cachefile) Just newMtime | newMtime > mtime -> cacheRefresh cachefile >>= \v -> return ((newMtime, v), v) | otherwise -> return (s, cachedVal) -newtype MTime = MTime UTCTime deriving (Eq,Ord)+newtype MTime = MTime EpochTime deriving (Eq,Ord) -getMTime filepath = (Just . MTime <$> getModified filepath)+getMTime filepath = (Just . MTime . modificationTime <$> getFileStatus (encodeString filepath)) `E.catch` \(_ :: E.SomeException) -> return Nothing
Data/Git/Storage/Object.hs view
@@ -57,6 +57,7 @@ import Data.List (intersperse) import Data.Monoid import Data.Word+import Data.Hourglass import Text.Printf #if MIN_VERSION_bytestring(0,10,0)@@ -232,9 +233,12 @@ _ <- string "> " time <- PC.decimal :: Parser Integer _ <- string " "- timezone <- PC.signed PC.decimal+ timezoneFmt <- PC.signed PC.decimal+ let timezoneSign = if timezoneFmt < 0 then negate else id+ let (h,m) = abs timezoneFmt `divMod` 100+ timezone = timezoneSign (h * 60 + m) skipChar '\n'- return $ Person name email (GitTime time timezone)+ return $ Person name email (gitTime time timezone) objectParseTree = ObjTree <$> treeParse objectParseCommit = ObjCommit <$> commitParse@@ -337,7 +341,6 @@ objectHash ty w lbs = hashLBS $ L.fromChunks (objectWriteHeader ty w : L.toChunks lbs) -- used for objectWrite for commit and tag-writeName label (Person name email (GitTime time tz)) =- B.concat [label, " ", name, " <", email, "> ", BC.pack (printf "%d %s%02d%02d" time tzs tzh tzm) ]- where tzs = if tz >= 0 then "+" else "-" :: String- (tzh,tzm) = abs (tz) `divMod` 100+writeName label (Person name email locTime) =+ B.concat [label, " ", name, " <", email, "> ", BC.pack timeStr]+ where timeStr = timePrint ("EPOCH TZHM" :: String) locTime
Data/Git/Types.hs view
@@ -24,10 +24,8 @@ , getPermission , getFiletype -- * time type- , GitTime(..)- , toUTCTime- , toPOSIXTime- , toZonedTime+ , GitTime+ , gitTime -- * Pack delta types , DeltaOfs(..) , DeltaRef(..)@@ -43,9 +41,7 @@ import Data.Git.Ref import Data.Git.Delta-import Data.Time.Clock-import Data.Time.LocalTime-import Data.Time.Clock.POSIX+import Data.Hourglass (Elapsed, LocalTime(..), TimezoneOffset(..)) import Data.Data @@ -60,22 +56,11 @@ deriving (Show,Eq,Data,Typeable) -- | Git time is number of seconds since unix epoch with a timezone-data GitTime = GitTime Integer Int- deriving (Show,Eq)--toUTCTime :: GitTime -> UTCTime-toUTCTime = zonedTimeToUTC . toZonedTime--toPOSIXTime :: GitTime -> POSIXTime-toPOSIXTime = utcTimeToPOSIXSeconds . toUTCTime+type GitTime = LocalTime Elapsed -toZonedTime :: GitTime -> ZonedTime-toZonedTime (GitTime epoch tzHourMin) = zonedTime- where tz = minutesToTimeZone (signum h * minutes)- (h,m) = tzHourMin `divMod` 100- minutes = abs h * 60 + m- utcTime = posixSecondsToUTCTime $ fromIntegral epoch- zonedTime = utcToZonedTime tz utcTime+gitTime :: Integer -> Int -> GitTime+gitTime seconds tzMins =+ LocalTime (fromIntegral seconds) (TimezoneOffset tzMins) -- | the enum instance is useful when marshalling to pack file. instance Enum ObjectType where
Hit/Hit.hs view
@@ -19,10 +19,11 @@ import Data.Git.Types import Data.Git.Ref import Data.Git.Repository-import Data.Git.Named import Data.Git.Revision import Data.Git.Diff+import Data.Hourglass import Data.Word+import qualified Data.Set as S import qualified Data.ByteString.Lazy.Char8 as LC import qualified Data.ByteString.Char8 as BC import Text.Printf@@ -47,7 +48,7 @@ -- or a list of delta to resolves packEnumerateObjects (gitRepoPath git) pref entries (setObj leftParsed refs offsets tree) readIORefAndReplace refs M.empty >>= dumpTree offsets tree- where+ where readIORefAndReplace ioref emptyVal = do v <- readIORef ioref writeIORef ioref emptyVal@@ -157,7 +158,7 @@ mapM_ putStrLn [ ("commit: " ++ show ref) , ("author: " ++ BC.unpack (personName author) ++ " <" ++ BC.unpack (personEmail author) ++ ">")- , ("date: " ++ show (toZonedTime $ personTime author) ++ " (" ++ show (toUTCTime $ personTime author) ++ ")")+ , ("date: " ++ timePrint ISO8601_DateAndTime (personTime author) ++ " (" ++ timePrint ISO8601_DateAndTime (personTime author) ++ ")") , "" , BC.unpack $ commitMessage commit ]@@ -215,12 +216,12 @@ showRefs git = do- putStrLn "[HEADS]"- heads <- headsList (gitRepoPath git)- mapM_ (putStrLn . show) heads+ putStrLn "[BRANCHES]"+ heads <- branchList git+ mapM_ (putStrLn . refNameRaw) $ S.toList heads putStrLn "[TAGS]"- tags <- tagsList (gitRepoPath git)- mapM_ (putStrLn . show) tags+ tags <- tagList git+ mapM_ (putStrLn . refNameRaw) $ S.toList tags main = do args <- getArgs@@ -232,7 +233,9 @@ ["rev-list",rev] -> withCurrentRepo $ revList (fromString rev) ["log",rev] -> withCurrentRepo $ getLog (fromString rev) ["diff",rev1,rev2] -> withCurrentRepo $ showDiff (fromString rev1) (fromString rev2)+ ["tag"] -> withCurrentRepo $ showRefs ["show-refs"] -> withCurrentRepo $ showRefs+ ["read-config"] -> withCurrentRepo $ \git -> configRead git >>= putStrLn . show cmd : [] -> error ("unknown command: " ++ cmd) [] -> error "no args" _ -> error "unknown command line arguments"
Tests/Repo.hs view
@@ -15,9 +15,6 @@ import Data.Git.Types import Data.Git.Repository -import Data.Time.LocalTime-import Data.Time.Clock-import Data.Time.Calendar import Data.Maybe import Text.Bytedump
Tests/Tests.hs view
@@ -12,9 +12,7 @@ import Data.Git.Storage.Loose import Data.Git.Ref import Data.Git.Types-import Data.Time.LocalTime-import Data.Time.Clock-import Data.Time.Calendar+import Data.Hourglass import Data.Maybe @@ -45,22 +43,12 @@ arbitraryEnt = liftM3 (,,) arbitrary (arbitraryBSno0 48) arbitrary arbitraryEnts = choose (1,2) >>= \i -> replicateM i arbitraryEnt -instance Arbitrary TimeZone where- arbitrary = hoursToTimeZone . rel <$> arbitrary- where rel a = (a `mod` 24) - 12--instance Arbitrary UTCTime where- arbitrary = UTCTime <$> (flip addDays b <$> choose (0, 365 * 40))- <*> (secondsToDiffTime <$> arbitrary)- where b = fromGregorian 1970 1 1--instance Arbitrary GitTime where- arbitrary = GitTime <$> (arbitrary `suchThat` \i -> i > 0) <*> arbitraryTz- where arbitraryTz = do- h <- choose (0, 23)- m <- (* 30) <$> choose (0,1)- return (h * 100 + m - 1200)-+instance Arbitrary TimezoneOffset where+ arbitrary = TimezoneOffset <$> choose (-11*60, 12*60)+instance Arbitrary Elapsed where+ arbitrary = Elapsed . Seconds <$> choose (0,2^32-1)+instance Arbitrary t => Arbitrary (LocalTime t) where+ arbitrary = LocalTime <$> arbitrary <*> arbitrary instance Arbitrary ModePerm where arbitrary = ModePerm <$> elements [ 0o644, 0o664, 0o755, 0 ]
hit.cabal view
@@ -1,5 +1,5 @@ Name: hit-Version: 0.5.5+Version: 0.6.0 Synopsis: Git operations in haskell Description: .@@ -46,7 +46,8 @@ , random , zlib , zlib-bindings >= 0.1 && < 0.2- , time+ , hourglass+ , unix , patience Exposed-modules: Data.Git Data.Git.Types@@ -62,6 +63,7 @@ Data.Git.Repository Data.Git.Diff Other-modules: Data.Git.Internal+ Data.Git.Config Data.Git.Storage.FileReader Data.Git.Storage.FileWriter Data.Git.Storage.CacheFile@@ -86,6 +88,7 @@ , filepath , directory , hit+ , hourglass , patience Buildable: True else@@ -101,7 +104,7 @@ , bytestring , test-framework >= 0.3 , test-framework-quickcheck2 >= 0.2- , time+ , hourglass , hit Test-Suite test-repository@@ -114,7 +117,7 @@ , bytestring , test-framework >= 0.3 , test-framework-quickcheck2 >= 0.2- , time+ , hourglass , bytedump >= 1.0 , hit