{-# LANGUAGE DeriveDataTypeable #-} {-| Manipulating 'GitObject'. -} module System.Git ( gitPathToGitObject , GitError(..) , GitDir, GitPath , findGitDir , rootSha1 , rootCommitObj , gitPathToSha1 , gitPathToObj , sha1ToObjFile , sha1ToObj ) where import Control.Applicative import Control.Exception import Data.Git import Data.Typeable (Typeable) import Prelude hiding (catch) import System.Directory import System.FilePath import System.IO ---------------------------------------------------------------- {-| Type for the path to Git repository directories. -} type GitDir = FilePath {-| Type for the absolute path from the project root. -} type GitPath = FilePath data GitError = GitDirNotExist | GitEntryNotExist deriving (Show, Typeable) instance Exception GitError ---------------------------------------------------------------- {-| Getting 'GitObject' of 'GoBlob'/'GoTree' corresponding to 'GitPath'. -} gitPathToGitObject :: GitPath -> IO (Either SomeException GitObject) gitPathToGitObject path = pathtoobj `catch` errorhandle where pathtoobj = findGitDir >>= gitPathToObj path >>= return . Right errorhandle :: SomeException -> IO (Either SomeException GitObject) errorhandle = return . Left {-| Getting 'GitObject' of 'GoBlob'/'GoTree' corresponding to 'GitPath'. -} gitPathToObj :: GitPath -> GitDir -> IO GitObject gitPathToObj path gitDir = gitPathToSha1 path gitDir >>= flip sha1ToObj gitDir {-| Getting 'SHA1' corresponding to 'GitPath'. -} gitPathToSha1 :: GitPath -> GitDir -> IO SHA1 gitPathToSha1 path gitDir = do GoCommit _ commit <- rootCommitObj gitDir let sha1OfRootTreeObj = commitRef commit pathToSha1 ps sha1OfRootTreeObj gitDir where ps = tail $ splitFilePath path pathToSha1 :: [String] -> SHA1 -> GitDir -> IO SHA1 pathToSha1 [] sha _ = return sha pathToSha1 (f:fs) sha gitDir = do obj <- sha1ToObj sha gitDir case obj of GoTree _ ents -> case lokup f ents of Nothing -> throw GitEntryNotExist Just sha' -> pathToSha1 fs sha' gitDir _ -> throw GitEntryNotExist {- search :: GitDir -> [FilePath] -> GitObject -> IO GitObject search gitDir fs (GoCommit _ com) = sha1ToObj (commitRef com) gitDir >>= search gitDir fs search _ [] obj = return obj search gitDir (f:fs) (GoTree _ ents) = case lokup f ents of Nothing -> throw GitEntryNotExist Just sha -> sha1ToObj sha gitDir >>= search gitDir fs search _ _ _ = throw GitEntryNotExist -} lokup :: FilePath -> [GitTreeEntry] -> Maybe SHA1 lokup _ [] = Nothing lokup key (e:es) | key == fileName e = Just (fileRef e) | otherwise = lokup key es ---------------------------------------------------------------- {-| Getting 'GitObject' of 'GoBlob' corresponding to the project root. -} rootCommitObj :: GitDir -> IO GitObject rootCommitObj gitDir = rootSha1 gitDir >>= flip sha1ToObj gitDir {-| Getting 'SHA1' of the project root. -} rootSha1 :: GitDir -> IO SHA1 rootSha1 gitDir = SHA1 <$> (getRootRefFile gitDir >>= readFileLine) getRootRefFile :: GitDir -> IO FilePath getRootRefFile gitDir = fieldToFile <$> readFileLine headFile where fieldToFile field = gitDir drop 5 field headFile = gitDir "HEAD" ---------------------------------------------------------------- {-| Finding 'GitDir' by tracking from the current directory to the root of the file system. -} findGitDir :: IO GitDir findGitDir = getCurrentDirectory >>= loop where loop dir = do let gitDir = dir ".git" exist <- doesDirectoryExist gitDir if exist then return gitDir else if dir == "/" then throw GitDirNotExist else loop (takeDirectory dir) ---------------------------------------------------------------- {-| Getting 'GitObject' according to 'SHA1'. -} sha1ToObj :: SHA1 -> GitDir -> IO GitObject sha1ToObj sha gitDir = parseGitObject $ sha1ToObjFile sha gitDir {-| Getting 'FilePath' to the Git object file according to 'SHA1'. -} sha1ToObjFile :: SHA1 -> GitDir -> FilePath sha1ToObjFile (SHA1 hash) gitDir = gitDir "objects" take 2 hash drop 2 hash readFileLine :: FilePath -> IO String readFileLine file = withBinaryFile file ReadMode hGetLine ---------------------------------------------------------------- -- splitFilePath "/foo/bar/baz/" -> ["","foo","bar","baz"] splitFilePath :: FilePath -> [String] splitFilePath "" = [] splitFilePath path = case break ('/'==) path of (xs,"") -> [xs] (xs,_:ys) -> xs : splitFilePath ys