diff --git a/Data/Git.hs b/Data/Git.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git.hs
@@ -0,0 +1,29 @@
+{-|
+  Types for Git objects and a parser of Git object files.
+-}
+
+module Data.Git (parseGitObject, module Data.Git.Types) where
+
+import Codec.Zlib.Enum
+import Data.Attoparsec.Enumerator
+import Data.ByteString (ByteString)
+import Data.Enumerator hiding (drop)
+import qualified Data.Enumerator.Binary as EB
+import Data.Git.Parser
+import Data.Git.Types
+
+{-|
+  Parsing a Git file to 'GitObject'.
+  This parser based on attoparsec-enumerator.
+-}
+parseGitObject :: FilePath -> IO GitObject
+parseGitObject file = run_ $ EB.enumFile file
+                          $$ decompress defaultWindowBits
+                          =$ iterGitObject
+
+iterGitObject :: Iteratee ByteString IO GitObject
+iterGitObject = iterParser gitObject
+
+infixr 0 =$
+(=$) :: Monad m => Enumeratee ao ai m b -> Iteratee ai m b -> Iteratee ao m b
+ee =$ ie = joinI $ ee $$ ie
diff --git a/Data/Git/Parser.hs b/Data/Git/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Parser.hs
@@ -0,0 +1,130 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Data.Git.Parser (gitObject) where
+
+import Control.Applicative hiding (many)
+import qualified Data.Attoparsec as AP (takeWhile)
+import qualified Data.Attoparsec.Char8 as AP (take)
+import Data.Attoparsec.Char8 hiding (take)
+import Data.Bits
+import qualified Data.ByteString as BS (foldl', foldr)
+import qualified Data.ByteString.Char8 as BS (unpack)
+import Data.Git.Types
+import Data.Char
+
+----------------------------------------------------------------
+
+gitObject :: Parser GitObject
+gitObject = do
+    (typ,len) <- header
+    case typ of
+        GtBlob   -> GoBlob   len <$> blob len
+        GtTree   -> GoTree   len <$> tree
+        GtCommit -> GoCommit len <$> commit
+        GtTag    -> GoTag    len <$> tag
+
+----------------------------------------------------------------
+
+header :: Parser (GitType, Int)
+header = (,) <$> (gitType <* spc) <*> (decimal <* nul)
+
+gitType :: Parser GitType
+gitType = GtBlob   <$ string "blob"
+      <|> GtTree   <$ string "tree"
+      <|> GtCommit <$ string "commit"
+      <|> GtTag    <$ string "tag"
+
+----------------------------------------------------------------
+
+blob :: Int -> Parser Blob
+blob = AP.take
+
+----------------------------------------------------------------
+
+tree :: Parser [GitTreeEntry]
+tree = many1 entry
+
+entry :: Parser GitTreeEntry
+entry = GitTreeEntry <$> (filetype <* spc)
+                     <*> (filepath <* nul)
+                     <*> binarySha1
+  where
+    filepath = many1 $ noneOf "\0"
+
+filetype :: Parser FileType
+filetype = getType . fromIntegral <$> octal
+  where
+    getMode x = x .&. 0o7777
+    getType x
+      | isBitSet x 0o0040000 = Directory
+      | isBitSet x 0o0120000 = SymbolicLink
+      | isBitSet x 0o0160000 = GitLink
+      | otherwise            = RegularFile (getMode x)
+    isBitSet x mask = x .&. mask == mask
+
+----------------------------------------------------------------
+
+commit :: Parser GitCommit
+commit = GitCommit <$> tre <*> parents <*> author <*> owner <*> logmsg
+  where
+    tre       = string "tree "   *> sha1 <* endOfLine
+    parents   = many parent
+    parent    = string "parent " *> sha1 <* endOfLine
+    author    = string "author "    *> line
+    owner     = string "committer " *> line
+    logmsg    = endOfLine *> AP.takeWhile (const True) <* endOfInput
+    line = AP.takeWhile (not.isEndOfLine) <* endOfLine
+
+----------------------------------------------------------------
+
+tag :: Parser GitTag
+tag = GitTag <$> obj <*> typ <*> name <*> owner <*> tagmsg
+  where
+    obj    = string "object " *> sha1 <* endOfLine
+    typ    = string "type "   *> line
+    name   = string "tag "    *> line
+    owner  = string "tagger " *> line
+    tagmsg = endOfLine *> AP.takeWhile (const True) <* endOfInput
+    line = AP.takeWhile (not.isEndOfLine) <* endOfLine
+
+----------------------------------------------------------------
+
+binarySha1 :: Parser SHA1
+binarySha1 = SHA1 . (flip toASCII "") <$> AP.take 20
+  where
+    toASCII = BS.foldr (\w ss -> toHex w . ss) id
+    toHex w = let (u,d) = w `divMod` 16
+                  hex2 = toH u : toH d : []
+              in (hex2 ++)
+    toH w
+      | w >= 10   = chr $ baseA + (w' - 10)
+      | otherwise = chr $ base0 + w'
+      where
+        w' = fromIntegral w
+    baseA = ord 'a'
+    base0 = ord '0'
+
+sha1 :: Parser SHA1
+sha1 = SHA1 . BS.unpack <$> AP.take 40
+
+octal :: Parser Int
+octal = BS.foldl' step 0 <$> AP.takeWhile isDig
+  where
+    isDig w  = 48 <= w && w <= 55
+    step a w = a * 8 + fromIntegral (w - 48)
+
+----------------------------------------------------------------
+
+{-
+oneOf :: String -> Parser Char
+oneOf = satisfy . inClass
+-}
+
+noneOf :: String -> Parser Char
+noneOf = satisfy . notInClass
+
+spc :: Parser ()
+spc = () <$ char ' '
+
+nul :: Parser ()
+nul = () <$ char '\0'
diff --git a/Data/Git/Types.hs b/Data/Git/Types.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Types.hs
@@ -0,0 +1,62 @@
+module Data.Git.Types where
+
+import Data.ByteString (ByteString)
+import System.Posix.Types (FileMode)
+
+----------------------------------------------------------------
+
+data GitType = GtBlob | GtTree | GtCommit | GtTag deriving (Eq,Show)
+
+----------------------------------------------------------------
+
+type Size = Int
+data GitObject = GoBlob   Size Blob
+               | GoTree   Size [GitTreeEntry]
+               | GoCommit Size GitCommit
+               | GoTag    Size GitTag
+               deriving (Eq,Show)
+
+----------------------------------------------------------------
+
+type Blob = ByteString
+
+----------------------------------------------------------------
+
+data GitTreeEntry = GitTreeEntry {
+    fileType :: FileType
+  , fileName :: FilePath
+  , fileRef  :: SHA1
+  } deriving (Eq,Show)
+
+data FileType = RegularFile FileMode
+              | Directory
+              | SymbolicLink
+              | GitLink
+              deriving (Eq,Show)
+
+----------------------------------------------------------------
+
+data GitCommit = GitCommit {
+    commitRef     :: SHA1
+  , commitParents :: [SHA1]
+  , commitAuthor  :: ByteString
+  , committer     :: ByteString
+  , commitLog     :: ByteString
+  } deriving (Eq,Show)
+
+----------------------------------------------------------------
+
+data GitTag = GitTag {
+    tagRef :: SHA1
+  , tagType :: ByteString
+  , tagName :: ByteString
+  , tagger  :: ByteString
+  , tagLog  :: ByteString
+  } deriving (Eq,Show)
+
+----------------------------------------------------------------
+
+newtype SHA1 = SHA1 String deriving Eq
+
+instance Show SHA1 where
+    show (SHA1 x) = x
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2011, IIJ Innovation Institute Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+  * Neither the name of the copyright holders nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/System/Git.hs b/System/Git.hs
new file mode 100644
--- /dev/null
+++ b/System/Git.hs
@@ -0,0 +1,164 @@
+{-# 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
diff --git a/git-object.cabal b/git-object.cabal
new file mode 100644
--- /dev/null
+++ b/git-object.cabal
@@ -0,0 +1,30 @@
+Name:                   git-object
+Version:                0.0.0
+Author:                 Kazu Yamamoto <kazu@iij.ad.jp>
+Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>
+License:                BSD3
+License-File:           LICENSE
+Synopsis:               Git object and its parser
+Description:            This package provides data types for
+                        Git objects, enumerator-based parser
+                        of Git object files and manipulation
+                        functions.
+Homepage:               http://www.mew.org/~kazu/
+Category:               Data
+Cabal-Version:          >= 1.6
+Build-Type:             Simple
+library
+  if impl(ghc >= 6.12)
+    GHC-Options:        -Wall -fno-warn-unused-do-bind
+  else
+    GHC-Options:        -Wall
+  Exposed-Modules:      Data.Git
+                        System.Git
+  Other-Modules:        Data.Git.Types
+                        Data.Git.Parser
+  Build-Depends:        base >= 4 && < 5, bytestring, zlib-enum,
+                        filepath, directory,
+                        attoparsec, enumerator, attoparsec-enumerator
+Source-Repository head
+  Type:                 git
+  Location:             git://github.com/kazu-yamamoto/git-object.git
