diff --git a/Data/Git.hs b/Data/Git.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git.hs
@@ -0,0 +1,50 @@
+-- |
+-- Module      : Data.Git
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Data.Git
+    (
+    -- * Basic types
+      Ref
+    , Commit(..)
+    , Tree(..)
+    , Blob(..)
+    , Tag(..)
+
+    -- * Revision
+    , Revision
+    , resolveRevision
+
+    -- * Object resolution
+    , resolveTreeish
+    , resolvePath
+
+    -- * repo context
+    , withCurrentRepo
+    , withRepo
+    , findRepo
+
+    -- * Repository queries and creation
+    , initRepo
+    , isRepo
+
+    -- * Context operations
+    , rewrite
+
+    -- * Get objects
+    , getObject
+    , getCommit
+    , getTree
+
+    -- * Set objects
+    , setObject
+    ) where
+
+import Data.Git.Ref
+import Data.Git.Types
+import Data.Git.Storage
+import Data.Git.Repository
+import Data.Git.Revision
diff --git a/Data/Git/Delta.hs b/Data/Git/Delta.hs
--- a/Data/Git/Delta.hs
+++ b/Data/Git/Delta.hs
@@ -6,12 +6,12 @@
 -- Portability : unix
 --
 module Data.Git.Delta
-	( Delta(..)
-	, DeltaCmd(..)
-	, deltaParse
-	, deltaRead
-	, deltaApply
-	) where
+        ( Delta(..)
+        , DeltaCmd(..)
+        , deltaParse
+        , deltaRead
+        , deltaApply
+        ) where
 
 import Data.Attoparsec
 import qualified Data.Attoparsec as A
@@ -26,13 +26,13 @@
 
 -- | a delta is a source size, a destination size and a list of delta cmd
 data Delta = Delta Word64 Word64 [DeltaCmd]
-	deriving (Show,Eq)
+        deriving (Show,Eq)
 
 -- | possible commands in a delta
 data DeltaCmd =
-	  DeltaCopy ByteString   -- command to insert this bytestring
-	| DeltaSrc Word64 Word64 -- command to copy from source (offset, size)
-	deriving (Show,Eq)
+          DeltaCopy ByteString   -- command to insert this bytestring
+        | DeltaSrc Word64 Word64 -- command to copy from source (offset, size)
+        deriving (Show,Eq)
 
 -- | parse a delta.
 -- format is 2 variable sizes, followed by delta cmds. for each cmd:
@@ -40,35 +40,35 @@
 -- * otherwise, we copy from delta.
 -- * extensions are not handled.
 deltaParse = do
-	srcSize <- getDeltaHdrSize
-	resSize <- getDeltaHdrSize
-	dcmds   <- many (anyWord8 >>= parseWithCmd)
-	return $ Delta srcSize resSize dcmds
-	where
-		getDeltaHdrSize = do
-			z <- A.takeWhile (\w -> w `testBit` 7)
-			l <- anyWord8
-			return $ unbytes 0 $ (map (\w -> w `clearBit` 7) (B.unpack z) ++ [l])
-		-- use a foldl ..
-		unbytes _  []     = 0
-		unbytes sh (x:xs) = (fromIntegral x) `shiftL` sh + unbytes (sh+7) xs
-		-- parse one command, either an extension, a copy from src, or a copy from delta.
-		parseWithCmd cmd
-			| cmd == 0        = error "delta extension not supported"
-			| cmd `testBit` 7 = do
-				o1 <- word8cond (cmd `testBit` 0) 0
-				o2 <- word8cond (cmd `testBit` 1) 8
-				o3 <- word8cond (cmd `testBit` 2) 16 
-				o4 <- word8cond (cmd `testBit` 3) 24
-				s1 <- word8cond (cmd `testBit` 4) 0
-				s2 <- word8cond (cmd `testBit` 5) 8
-				s3 <- word8cond (cmd `testBit` 6) 16 
-				let offset = o1 .|. o2 .|. o3 .|. o4
-				let size   = s1 .|. s2 .|. s3
-				return $ DeltaSrc offset (if size == 0 then 0x10000 else size)
-			| otherwise       = DeltaCopy <$> A.take (fromIntegral cmd)
-		word8cond cond sh = do
-			if cond then (flip shiftL sh . fromIntegral) <$> anyWord8 else return 0
+        srcSize <- getDeltaHdrSize
+        resSize <- getDeltaHdrSize
+        dcmds   <- many (anyWord8 >>= parseWithCmd)
+        return $ Delta srcSize resSize dcmds
+        where
+                getDeltaHdrSize = do
+                        z <- A.takeWhile (\w -> w `testBit` 7)
+                        l <- anyWord8
+                        return $ unbytes 0 $ (map (\w -> w `clearBit` 7) (B.unpack z) ++ [l])
+                -- use a foldl ..
+                unbytes _  []     = 0
+                unbytes sh (x:xs) = (fromIntegral x) `shiftL` sh + unbytes (sh+7) xs
+                -- parse one command, either an extension, a copy from src, or a copy from delta.
+                parseWithCmd cmd
+                        | cmd == 0        = error "delta extension not supported"
+                        | cmd `testBit` 7 = do
+                                o1 <- word8cond (cmd `testBit` 0) 0
+                                o2 <- word8cond (cmd `testBit` 1) 8
+                                o3 <- word8cond (cmd `testBit` 2) 16 
+                                o4 <- word8cond (cmd `testBit` 3) 24
+                                s1 <- word8cond (cmd `testBit` 4) 0
+                                s2 <- word8cond (cmd `testBit` 5) 8
+                                s3 <- word8cond (cmd `testBit` 6) 16 
+                                let offset = o1 .|. o2 .|. o3 .|. o4
+                                let size   = s1 .|. s2 .|. s3
+                                return $ DeltaSrc offset (if size == 0 then 0x10000 else size)
+                        | otherwise       = DeltaCopy <$> A.take (fromIntegral cmd)
+                word8cond cond sh = do
+                        if cond then (flip shiftL sh . fromIntegral) <$> anyWord8 else return 0
 
 -- | read one delta from a lazy bytestring.
 deltaRead = AL.maybeResult . AL.parse deltaParse
@@ -76,9 +76,9 @@
 -- | apply a delta on a lazy bytestring, returning a new bytestring.
 deltaApply :: L.ByteString -> Delta -> L.ByteString
 deltaApply src (Delta srcSize _ deltaCmds)
-	| L.length src /= fromIntegral srcSize = error "source size do not match"
-	| otherwise                            = -- FIXME use a bytestring builder here.
-		L.fromChunks $ concatMap resolve deltaCmds where
-			resolve (DeltaSrc o s) = L.toChunks $ takeAt (fromIntegral s) (fromIntegral o) src
-			resolve (DeltaCopy b)  = [b]
-			takeAt sz at = L.take sz . L.drop at
+        | L.length src /= fromIntegral srcSize = error "source size do not match"
+        | otherwise                            = -- FIXME use a bytestring builder here.
+                L.fromChunks $ concatMap resolve deltaCmds where
+                        resolve (DeltaSrc o s) = L.toChunks $ takeAt (fromIntegral s) (fromIntegral o) src
+                        resolve (DeltaCopy b)  = [b]
+                        takeAt sz at = L.take sz . L.drop at
diff --git a/Data/Git/FileReader.hs b/Data/Git/FileReader.hs
deleted file mode 100644
--- a/Data/Git/FileReader.hs
+++ /dev/null
@@ -1,193 +0,0 @@
--- |
--- Module      : Data.Git.FileReader
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-module Data.Git.FileReader
-	( FileReader
-	, fileReaderNew
-	, fileReaderClose
-	, withFileReader
-	, withFileReaderDecompress
-	, fileReaderGetPos
-	, fileReaderGet
-	, fileReaderGetLBS
-	, fileReaderGetBS
-	, fileReaderGetVLF
-	, fileReaderSeek
-	, fileReaderParse
-
-	, fileReaderInflateToSize
-	) where
-
-
-import Control.Applicative ((<$>))
-import Control.Exception (bracket, throwIO)
-import Control.Monad
-
-import Data.Attoparsec (parseWith, Parser, IResult(..))
-import qualified Data.Attoparsec as A
-import Data.Bits
-import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as L
-import Data.ByteString.Lazy.Internal (defaultChunkSize)
-import Data.IORef
-
-import Data.Word
-
-import Codec.Zlib
-import Codec.Zlib.Lowlevel
-import Foreign.ForeignPtr
-
-import System.IO
-
-data FileReader = FileReader
-	{ fbHandle     :: Handle
-	, fbUseInflate :: Bool
-	, fbInflate    :: Inflate
-	, fbRemaining  :: IORef ByteString
-	, fbPos        :: IORef Word64
-	}
-
-fileReaderNew :: Bool -> Handle -> IO FileReader
-fileReaderNew decompress handle = do
-	ref <- newIORef B.empty
-	pos <- newIORef 0
-	inflate <- initInflate defaultWindowBits
-	return $ FileReader handle decompress inflate ref pos
-
-fileReaderClose :: FileReader -> IO ()
-fileReaderClose = hClose . fbHandle
-
-withFileReader :: FilePath -> (FileReader -> IO a) -> IO a
-withFileReader path f =
-	bracket (openFile path ReadMode) (hClose) $ \handle ->
-		bracket (fileReaderNew False handle) (\_ -> return ()) f
-
-withFileReaderDecompress :: FilePath -> (FileReader -> IO a) -> IO a
-withFileReaderDecompress path f =
-	bracket (openFile path ReadMode) (hClose) $ \handle ->
-		bracket (fileReaderNew True handle) (\_ -> return ()) f
-
-fileReaderGetNext :: FileReader -> IO ByteString
-fileReaderGetNext fb = do
-	bs <- if fbUseInflate fb then inflateTillPop else B.hGet (fbHandle fb) 8192
-	modifyIORef (fbPos fb) (\pos -> pos + (fromIntegral $ B.length bs))
-	return bs
-	where inflateTillPop = do
-		b <- B.hGet (fbHandle fb) 4096
-		if B.null b
-			then finishInflate (fbInflate fb)
-			else maybe inflateTillPop return =<< withInflateInput (fbInflate fb) b id
-
-fileReaderGetPos :: FileReader -> IO Word64
-fileReaderGetPos fr = do
-	storeLeft <- B.length <$> readIORef (fbRemaining fr)
-	pos       <- readIORef (fbPos fr)
-	return (pos - fromIntegral storeLeft)
-
-fileReaderFill :: FileReader -> IO ()
-fileReaderFill fb = fileReaderGetNext fb >>= writeIORef (fbRemaining fb)
-
-fileReaderGet :: Int -> FileReader -> IO [ByteString]
-fileReaderGet size fb@(FileReader { fbRemaining = ref }) = loop size
-	where loop left = do
-		b <- readIORef ref
-		if B.length b >= left
-			then do
-				let (b1, b2) = B.splitAt left b
-				writeIORef ref b2
-				return [b1]
-			else do
-				let nleft = left - B.length b
-				fileReaderFill fb
-				liftM (b :) (loop nleft)
-
-fileReaderGetLBS :: Int -> FileReader -> IO L.ByteString
-fileReaderGetLBS size fb = L.fromChunks <$> fileReaderGet size fb
-
-fileReaderGetBS :: Int -> FileReader -> IO ByteString
-fileReaderGetBS size fb = B.concat <$> fileReaderGet size fb
-
--- | seek in a handle, and reset the remaining buffer to empty.
-fileReaderSeek :: FileReader -> Word64 -> IO ()
-fileReaderSeek (FileReader { fbHandle = handle, fbRemaining = ref, fbPos = pos }) absPos = do
-	writeIORef ref B.empty >> writeIORef pos absPos >> hSeek handle AbsoluteSeek (fromIntegral absPos)
-
--- | parse from a filebuffer
-fileReaderParse :: FileReader -> Parser a -> IO a
-fileReaderParse fr@(FileReader { fbRemaining = ref }) parseF = do
-	initBS <- readIORef ref
-	result <- parseWith (fileReaderGetNext fr) parseF initBS
-	case result of
-		Done remaining a -> writeIORef ref remaining >> return a
-		Partial _        -> error "parsing failed: partial with a handle, reached EOF ?"
-		Fail _ ctxs err  -> error ("parsing failed: " ++ show ctxs ++ " : " ++ show err)
-
--- | get a Variable Length Field. get byte as long as MSB is set, and one byte after
-fileReaderGetVLF :: FileReader -> IO [Word8]
-fileReaderGetVLF fr = fileReaderParse fr $ do
-	bs <- A.takeWhile (\w -> w `testBit` 7)
-	l  <- A.anyWord8
-	return $ (map (\w -> w `clearBit` 7) $ B.unpack bs) ++ [l]
-
-fileReaderInflateToSize :: FileReader -> Word64 -> IO L.ByteString
-fileReaderInflateToSize fb@(FileReader { fbRemaining = ref }) outputSize = do
-	--pos <- fileReaderGetPos fb
-	--putStrLn ("inflate to size " ++ show outputSize ++ " " ++ show pos)
-	inflate <- inflateNew
-	l       <- loop inflate outputSize
-	--posend <- fileReaderGetPos fb
-	--putStrLn ("inflated input " ++ show posend)
-	return $ L.fromChunks l
-	where loop inflate left = do
-		rbs <- readIORef ref
-		let maxToInflate = min left (16 * 1024)
-		let lastBlock = if left == maxToInflate then True else False
-		(dbs,remaining) <- inflateToSize inflate (fromIntegral maxToInflate) lastBlock rbs (fileReaderGetNext fb)
-		writeIORef ref remaining
-		let nleft = left - fromIntegral (B.length dbs)
-		if nleft > 0
-			then liftM (dbs:) (loop inflate nleft)
-			else return [dbs]
-
--- lowlevel helpers to inflate only to a specific size.
-
-inflateNew = do
-	zstr <- zstreamNew
-	inflateInit2 zstr defaultWindowBits
-	newForeignPtr c_free_z_stream_inflate zstr
-
-inflateToSize inflate sz isLastBlock ibs nextBs = withForeignPtr inflate $ \zstr -> do
-	let boundSz = min defaultChunkSize sz
-	-- create an output buffer
-	fbuff <- mallocForeignPtrBytes boundSz
-	withForeignPtr fbuff $ \buff -> do
-		c_set_avail_out zstr buff (fromIntegral boundSz)
-		rbs <- loop zstr ibs
-		bs  <- B.packCStringLen (buff, boundSz)
-		return (bs, rbs)
-	where
-		loop zstr nbs = do
-			(ai, streamEnd) <- inflateOneInput zstr nbs
-			ao <- c_get_avail_out zstr
-			if (isLastBlock && streamEnd) || (not isLastBlock && ao == 0)
-				then return $ bsTakeLast ai nbs
-				else do
-					--when (ai /= 0) $ error ("input not consumed completly: ai" ++ show ai)
-					(if ai == 0
-						then nextBs
-						else return (bsTakeLast ai nbs)) >>= loop zstr
-
-		inflateOneInput zstr bs = unsafeUseAsCStringLen bs $ \(istr, ilen) -> do
-			c_set_avail_in zstr istr $ fromIntegral ilen
-			r <- c_call_inflate_noflush zstr
-			when (r < 0 && r /= (-5)) $ do
-				throwIO $ ZlibException $ fromIntegral r
-			ai <- c_get_avail_in zstr
-			return (ai, r == 1)
-		bsTakeLast len bs = B.drop (B.length bs - fromIntegral len) bs
diff --git a/Data/Git/FileWriter.hs b/Data/Git/FileWriter.hs
deleted file mode 100644
--- a/Data/Git/FileWriter.hs
+++ /dev/null
@@ -1,52 +0,0 @@
--- |
--- Module      : Data.Git.FileWriter
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-module Data.Git.FileWriter where
-
-import Data.Git.Ref
-import Data.IORef
-import qualified Data.ByteString as B
-import Codec.Zlib
-import Control.Exception (bracket)
-
-import qualified Crypto.Hash.SHA1 as SHA1
-
-import System.IO
-
-defaultCompression = 6
-
-data FileWriter = FileWriter
-	{ writerHandle  :: Handle
-	, writerDeflate :: Deflate
-	, writerDigest  :: IORef SHA1.Ctx
-	}
-
-fileWriterNew handle = do
-	deflate <- initDeflate defaultCompression defaultWindowBits
-	digest  <- newIORef SHA1.init
-	return $ FileWriter
-		{ writerHandle  = handle
-		, writerDeflate = deflate
-		, writerDigest  = digest
-		}
-
-withFileWriter path f =
-	bracket (openFile path WriteMode) (hClose) $ \handle ->
-		bracket (fileWriterNew handle) (fileWriterClose) f
-
-postDeflate _      Nothing    = return ()
-postDeflate handle (Just dbs) = B.hPut handle dbs
-
-fileWriterOutput (FileWriter { writerHandle = handle, writerDigest = digest, writerDeflate = deflate }) bs = do
-	putStrLn ("outputing" ++ show bs)
-	modifyIORef digest (\ctx -> SHA1.update ctx bs)
-	postDeflate handle =<< withDeflateInput deflate bs id
-
-fileWriterClose (FileWriter { writerHandle = handle, writerDeflate = deflate }) = do
-	postDeflate handle =<< finishDeflate deflate id
-
-fileWriterGetDigest (FileWriter { writerDigest = digest }) = (fromBinary . SHA1.finalize) `fmap` readIORef digest
diff --git a/Data/Git/Index.hs b/Data/Git/Index.hs
deleted file mode 100644
--- a/Data/Git/Index.hs
+++ /dev/null
@@ -1,182 +0,0 @@
--- |
--- Module      : Data.Git.Index
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-{-# LANGUAGE OverloadedStrings, BangPatterns #-}
-module Data.Git.Index
-	( IndexHeader(..)
-	, Index(..)
-
-	-- * handles and enumeration
-	, indexOpen
-	, indexClose
-	, withIndex
-	, indexEnumerate
-
-	-- * read from index
-	, indexHeaderGetNbWithPrefix
-	, indexGetReferenceLocation
-	, indexGetReferencesWithPrefix
-	, indexReadHeader
-	, indexRead
-	, indexGetHeader
-	) where
-
-import Control.Applicative ((<$>))
-import Control.Monad
-
-import System.FilePath
-import System.Directory
-import System.IO
-
-import Data.List
-import Data.Bits
-import Data.Word
-
-import Data.Vector (Vector, (!))
-import qualified Data.Vector as V
-
-import qualified Data.Attoparsec as A
-
-import Data.Git.Internal
-import Data.Git.FileReader
-import Data.Git.Path
-import Data.Git.Ref
-
--- | represent an index header with the version and the fanout table
-data IndexHeader = IndexHeader !Word32 !(Vector Word32)
-	deriving (Show,Eq)
-
-data Index = Index
-	{ indexSha1s        :: Vector Ref
-	, indexCRCs         :: Vector Word32
-	, indexPackoffs     :: Vector Word32
-	, indexPackChecksum :: Ref
-	, indexChecksum     :: Ref
-	}
-
--- | enumerate every indexes file in the pack directory
-indexEnumerate repoPath = map onlyHash . filter isPackFile <$> getDirectoryContents (repoPath </> "objects" </> "pack")
-	where
-		isPackFile x = ".idx" `isSuffixOf` x && "pack-" `isPrefixOf` x
-		onlyHash = fromHexString . takebut 4 . drop 5
-		takebut n l = take (length l - n) l
-
--- | open an index
-indexOpen :: FilePath -> Ref -> IO FileReader
-indexOpen repoPath indexRef = openFile (indexPath repoPath indexRef) ReadMode >>= fileReaderNew False
-
--- | close an index
-indexClose :: FileReader -> IO ()
-indexClose = fileReaderClose
-
--- | variant of withFile on the index file and with a FileReader
-withIndex repoPath indexRef = withFileReader (indexPath repoPath indexRef)
-
--- | returns the number of references, referenced in this index.
-indexHeaderGetSize :: IndexHeader -> Word32
-indexHeaderGetSize (IndexHeader _ indexes) = indexes ! 255
-
--- | byte size of an index header.
-indexHeaderByteSize :: Int
-indexHeaderByteSize = 2*4 {- header -} + 256*4 {- fanout table -}
-
--- | get the number of reference in this index with a specific prefix
-indexHeaderGetNbWithPrefix :: IndexHeader -> Int -> Word32
-indexHeaderGetNbWithPrefix (IndexHeader _ indexes) n
-	| n < 0 || n > 255 = 0
-	| n == 0           = indexes ! 0
-	| otherwise        = (indexes ! n) - (indexes ! (n-1))
-
--- | fold on refs with a specific prefix
-indexHeaderFoldRef :: IndexHeader -> FileReader -> Int -> (a -> Word32 -> Ref -> (a, Bool)) -> a -> IO a
-indexHeaderFoldRef idxHdr@(IndexHeader _ indexes) fr refprefix f initAcc
-	| nb == 0   = return initAcc
-	| otherwise = do
-		let spos = (indexes ! refprefix) - nb
-		fileReaderSeek fr (fromIntegral (sha1Offset + spos * 20))
-		loop nb initAcc
-	where
-		loop 0 acc = return acc
-		loop n acc = do
-			b <- fromBinary <$> fileReaderGetBS 20 fr
-			let (!nacc, terminate) = f acc (nb-n) b
-			if terminate
-				then return nacc
-				else loop (n-1) nacc
-		nb         = indexHeaderGetNbWithPrefix idxHdr refprefix
-		(sha1Offset,_,_) = indexOffsets idxHdr
-
--- | return the reference offset in the packfile if found
-indexGetReferenceLocation :: IndexHeader -> FileReader -> Ref -> IO (Maybe Word64)
-indexGetReferenceLocation idxHdr@(IndexHeader _ indexes) fr ref = do
-	mrpos <- indexHeaderFoldRef idxHdr fr refprefix f Nothing
-	case mrpos of
-		Nothing   -> return Nothing
-		Just rpos -> do
-			let spos = (indexes ! refprefix) - nb
-			fileReaderSeek fr (fromIntegral (packOffset + 4 * (spos+rpos)))
-			Just . fromIntegral . be32 <$> fileReaderGetBS 4 fr
-	where
-		f acc rpos rref = if ref == rref then (Just rpos,True) else (acc,False)
-		refprefix  = refPrefix ref
-		nb         = indexHeaderGetNbWithPrefix idxHdr refprefix
-		(_,_,packOffset) = indexOffsets idxHdr
-
--- | get all references that start by prefix.
-indexGetReferencesWithPrefix :: IndexHeader -> FileReader -> String -> IO [Ref]
-indexGetReferencesWithPrefix idxHdr fr prefix =
-	indexHeaderFoldRef idxHdr fr refprefix f []
-	where
-		f acc _ ref = case cmpPrefix prefix ref of
-			GT -> (acc    ,False)
-			EQ -> (ref:acc,False)
-			LT -> (acc    ,True)
-		refprefix   = read ("0x" ++ take 2 prefix)
-
--- | returns absolute offset in the index file of the sha1s, the crcs and the packfiles offset.
-indexOffsets idx = (indexSha1sOffset, indexCRCsOffset, indexPackOffOffset)
-	where
-		indexPackOffOffset = indexCRCsOffset + crcsTableSz
-		indexCRCsOffset    = indexSha1sOffset + sha1TableSz
-		indexSha1sOffset   = fromIntegral indexHeaderByteSize
-		crcsTableSz        = 4 * sz
-		sha1TableSz        = 20 * sz
-		sz                 = indexHeaderGetSize idx
-
--- | parse index header
-parseIndexHeader = do
-	magic   <- be32 <$> A.take 4
-	when (magic /= 0xff744f63) $ error "wrong magic number for index"
-	ver     <- be32 <$> A.take 4
-	when (ver /= 2) $ error "unsupported index version"
-	fanouts <- V.replicateM 256 (be32 <$> A.take 4)
-	return $ IndexHeader ver fanouts
-
--- | read index header from an index filereader
-indexReadHeader :: FileReader -> IO IndexHeader
-indexReadHeader fr = fileReaderSeek fr 0 >> fileReaderParse fr parseIndexHeader
-
--- | get index header from an index reference
-indexGetHeader :: FilePath -> Ref -> IO IndexHeader
-indexGetHeader repoPath indexRef = withIndex repoPath indexRef $ indexReadHeader
-
--- | read all index
-indexRead repoPath indexRef = do
-	withIndex repoPath indexRef $ \fr -> do
-		idx <- fileReaderParse fr parseIndexHeader
-		liftM2 (,) (return idx) (fileReaderParse fr (parseIndex $ indexHeaderGetSize idx))
-	where parseIndex sz = do
-		sha1s     <- V.replicateM (fromIntegral sz) (fromBinary <$> A.take 20)
-		crcs      <- V.replicateM (fromIntegral sz) (be32 <$> A.take 4)
-		packoffs  <- V.replicateM (fromIntegral sz) (be32 <$> A.take 4)
-		let nbLarge = length $ filter (== True) $ map (\packoff -> packoff `testBit` 31) $ V.toList packoffs
-		largeoffs <- replicateM nbLarge (A.take 4)
-		packfileChecksum <- fromBinary <$> A.take 20
-		idxfileChecksum  <- fromBinary <$> A.take 20
-		-- large packfile offsets
-		-- trailer
-		return (sha1s, crcs, packoffs, largeoffs, packfileChecksum, idxfileChecksum)
diff --git a/Data/Git/Internal.hs b/Data/Git/Internal.hs
--- a/Data/Git/Internal.hs
+++ b/Data/Git/Internal.hs
@@ -6,8 +6,9 @@
 -- Portability : unix
 --
 module Data.Git.Internal
-	( be32
-	) where
+        ( be32
+        , be16
+        ) where
 
 import Data.Bits
 import Data.Word
@@ -18,3 +19,7 @@
        + fromIntegral (B.index b 1) `shiftL` 16
        + fromIntegral (B.index b 2) `shiftL` 8
        + fromIntegral (B.index b 3)
+
+be16 :: B.ByteString -> Word16
+be16 b = fromIntegral (B.index b 0) `shiftL` 8
+       + fromIntegral (B.index b 1)
diff --git a/Data/Git/Loose.hs b/Data/Git/Loose.hs
deleted file mode 100644
--- a/Data/Git/Loose.hs
+++ /dev/null
@@ -1,152 +0,0 @@
--- |
--- Module      : Data.Git.Loose
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
-module Data.Git.Loose
-	(
-	-- * marshall from and to lazy bytestring
-	  looseUnmarshall
-	, looseUnmarshallRaw
-	, looseMarshall
-	-- * read and check object existence
-	, looseRead
-	, looseReadHeader
-	, looseReadRaw
-	, looseExists
-	-- * write objects
-	, looseWriteBlobFromFile
-	, looseWrite
-	-- * enumeration of loose objects
-	, looseEnumeratePrefixes
-	, looseEnumerateWithPrefixFilter
-	, looseEnumerateWithPrefix
-	) where
-
-import Codec.Compression.Zlib
-import Data.Git.Ref
-import Data.Git.Path
-import Data.Git.FileWriter
-import Data.Git.Object
-
-import System.FilePath
-import System.Directory
-import System.IO (openFile, hFileSize, IOMode(..))
-
-import qualified Data.ByteString.Char8 as BC
-import qualified Data.ByteString.Lazy as L
-
-import Data.Attoparsec.Lazy
-import qualified Data.Attoparsec.Char8 as PC
-import Control.Applicative ((<$>), (<|>))
-import Control.Monad
-import Control.Exception (onException, SomeException)
-import qualified Control.Exception as E
-
-import Data.Char (isHexDigit)
-
-isObjectPrefix [a,b] = isHexDigit a && isHexDigit b
-isObjectPrefix _     = False
-
-decimal :: Parser Int
-decimal = PC.decimal
-
--- loose object parsing
-parseHeader = do
-	h <- takeWhile1 ((/=) 0x20)
-	_ <- word8 0x20
-	sz <- decimal
-	return (objectTypeUnmarshall $ BC.unpack h, fromIntegral sz, Nothing)
-
-parseTreeHeader   = string "tree " >> decimal >> word8 0
-parseTagHeader    = string "tag " >> decimal >> word8 0
-parseCommitHeader = string "commit " >> decimal >> word8 0
-parseBlobHeader   = string "blob " >> decimal >> word8 0
-
-parseTree   = parseTreeHeader >> objectParseTree
-parseTag    = parseTagHeader >> objectParseTag
-parseCommit = parseCommitHeader >> objectParseCommit
-parseBlob   = parseBlobHeader >> objectParseBlob
-
-parseObject :: L.ByteString -> Object
-parseObject = parseSuccess (parseTree <|> parseBlob <|> parseCommit <|> parseTag)
-	where parseSuccess p = either error id . eitherResult . parse p
-
--- | unmarshall an object (with header) from a lazy bytestring.
-looseUnmarshall :: L.ByteString -> Object
-looseUnmarshall = parseObject . decompress
-
--- | unmarshall an object as (header, data) tuple from a lazy bytestring.
-looseUnmarshallRaw :: L.ByteString -> (ObjectHeader, ObjectData)
-looseUnmarshallRaw l =
-	let dl = decompress l in
-	let i = L.findIndex ((==) 0) dl in
-	case i of
-		Nothing  -> error "object not right format. missing 0"
-		Just idx ->
-			let (h, r) = L.splitAt (idx+1) dl in
-			case maybeResult $ parse parseHeader h of
-				Nothing  -> error "cannot open object"
-				Just hdr -> (hdr, r)
-
--- | read a specific ref from a loose object and returns an header and data.
-looseReadRaw repoPath ref = looseUnmarshallRaw <$> L.readFile (objectPathOfRef repoPath ref)
-
--- | read only the header of a loose object.
-looseReadHeader repoPath ref = toHeader <$> L.readFile (objectPathOfRef repoPath ref)
-	where toHeader = either error id . eitherResult . parse parseHeader . decompress
-
--- | read a specific ref from a loose object and returns an object
-looseRead repoPath ref = looseUnmarshall <$> L.readFile (objectPathOfRef repoPath ref)
-
--- | check if a specific ref exists as loose object
-looseExists repoPath ref = doesFileExist (objectPathOfRef repoPath ref)
-
--- | enumarate all prefixes available in the object store.
-looseEnumeratePrefixes repoPath = filter isObjectPrefix <$> getDirectoryContents (repoPath </> "objects")
-
--- | enumerate all references available with a specific prefix.
-looseEnumerateWithPrefixFilter :: FilePath -> String -> (Ref -> Bool) -> IO [Ref]
-looseEnumerateWithPrefixFilter repoPath prefix filterF =
-	filter filterF . map (fromHexString . (prefix ++)) . filter isRef <$> getDir (repoPath </> "objects" </> prefix)
-	where
-		getDir p = E.catch (getDirectoryContents p) (\(_::SomeException) -> return [])
-		isRef l = length l == 38
-
-looseEnumerateWithPrefix repoPath prefix =
-	looseEnumerateWithPrefixFilter repoPath prefix (const True)
-
--- | marshall as lazy bytestring an object except deltas.
-looseMarshall obj
-	| objectIsDelta obj = error "cannot write delta object loose"
-	| otherwise         = compress $ L.concat [ L.fromChunks [hdrB], objData ]
-	where
-		objData = objectWrite obj
-		hdrB    = objectWriteHeader (objectToType obj) (fromIntegral $ L.length objData)
-
--- | create a new blob on a temporary location and on success move it to
--- the object store with its digest name.
-looseWriteBlobFromFile repoPath file = do
-	fsz <- openFile file ReadMode >>= hFileSize
-	let hdr = objectWriteHeader TypeBlob (fromIntegral fsz)
-	tmpPath <- objectTemporaryPath repoPath
-	flip onException (removeFile tmpPath) $ do
-		npath <- withFileWriter tmpPath $ \fw -> do
-			fileWriterOutput fw hdr
-			chunks <- L.toChunks <$> L.readFile file
-			mapM_ (fileWriterOutput fw) chunks
-			digest <- fileWriterGetDigest fw
-			return $ objectPathOfRef repoPath digest
-		exists <- doesFileExist npath
-		when exists $ error "destination already exists"
-		renameFile tmpPath npath
-
--- | write an object to disk as a loose reference.
--- use looseWriteBlobFromFile for efficiently writing blobs when being commited from a file.
-looseWrite repoPath obj = L.writeFile (objectPathOfRef repoPath ref) content
-	where
-		content = looseMarshall obj
-		ref     = hashLBS content
diff --git a/Data/Git/Named.hs b/Data/Git/Named.hs
--- a/Data/Git/Named.hs
+++ b/Data/Git/Named.hs
@@ -7,19 +7,19 @@
 -- Portability : unix
 --
 module Data.Git.Named
-	( headList
-	, headExists
-	, headRead
-	, headWrite
-	, remotesList
-	, remoteList
-	, tagList
-	, tagExists
-	, tagRead
-	, tagWrite
-	, specialRead
-	, specialExists
-	) where
+        ( headsList
+        , headExists
+        , headRead
+        , headWrite
+        , remotesList
+        , remoteList
+        , tagsList
+        , tagExists
+        , tagRead
+        , tagWrite
+        , specialRead
+        , specialExists
+        ) where
 
 import Control.Applicative ((<$>))
 
@@ -32,10 +32,10 @@
 import qualified Data.ByteString.Char8 as BC
 
 getDirectoryContentNoDots path = filter noDot <$> getDirectoryContents path
-	where noDot = (not . isPrefixOf ".")
+        where noDot = (not . isPrefixOf ".")
 
-headList gitRepo = getDirectoryContentNoDots (headsPath gitRepo)
-tagList gitRepo = getDirectoryContentNoDots (tagsPath gitRepo)
+headsList gitRepo = getDirectoryContentNoDots (headsPath gitRepo)
+tagsList gitRepo = getDirectoryContentNoDots (tagsPath gitRepo)
 remotesList gitRepo = getDirectoryContentNoDots (remotesPath gitRepo)
 remoteList gitRepo remote = getDirectoryContentNoDots (remotePath gitRepo remote)
 
@@ -43,12 +43,12 @@
 readRef path = fromHex . B.take 40 <$> B.readFile path
 
 readRefAndFollow gitRepo path = do
-	content <- B.readFile path
-	if "ref: " `B.isPrefixOf` content
-		then do -- BC.unpack should be utf8.toString, and the whole thing is really fragile. need to do the proper thing.
-			let file = BC.unpack $ BC.init $ B.drop 5 content
-			readRefAndFollow gitRepo (gitRepo ++ "/" ++ file)
-		else return (fromHex $ B.take 40 content)
+        content <- B.readFile path
+        if "ref: " `B.isPrefixOf` content
+                then do -- BC.unpack should be utf8.toString, and the whole thing is really fragile. need to do the proper thing.
+                        let file = BC.unpack $ BC.init $ B.drop 5 content
+                        readRefAndFollow gitRepo (gitRepo ++ "/" ++ file)
+                else return (fromHex $ B.take 40 content)
 
 headExists gitRepo name    = doesFileExist (headPath gitRepo name)
 headRead gitRepo name      = readRef (headPath gitRepo name)
diff --git a/Data/Git/Object.hs b/Data/Git/Object.hs
deleted file mode 100644
--- a/Data/Git/Object.hs
+++ /dev/null
@@ -1,346 +0,0 @@
-{-# LANGUAGE ExistentialQuantification #-}
--- |
--- Module      : Data.Git.Object
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-{-# LANGUAGE OverloadedStrings #-}
-module Data.Git.Object
-	( ObjectLocation(..)
-	, ObjectType(..)
-	, ObjectHeader
-	, ObjectData
-	, ObjectPtr(..)
-	, Object(..)
-	, Tree(..)
-	, Commit(..)
-	, Blob(..)
-	, Tag(..)
-	, DeltaOfs(..)
-	, DeltaRef(..)
-	, ObjectInfo(..)
-	, objectWrap
-	, objectToType
-	, objectTypeMarshall
-	, objectTypeUnmarshall
-	, objectTypeIsDelta
-	, objectIsDelta
-	, objectToTree
-	, objectToCommit
-	, objectToTag
-	, objectToBlob
-	-- * parsing function
-	, treeParse
-	, commitParse
-	, tagParse
-	, blobParse
-	, objectParseTree
-	, objectParseCommit
-	, objectParseTag
-	, objectParseBlob
-	-- * writing function
-	, objectWriteHeader
-	, objectWrite
-	, objectHash
-	) where
-
-import Data.Git.Ref
-import Data.Git.Delta
-
-import Data.ByteString (ByteString)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Char8 as BC
-import qualified Data.ByteString.Lazy as L
-
-import Data.Attoparsec.Lazy
-import qualified Data.Attoparsec.Lazy as P
-import qualified Data.Attoparsec.Char8 as PC
-import Control.Applicative ((<$>), many)
-import Control.Monad
-
-import Data.Word
-import Text.Printf
-
-{-
-import Blaze.ByteString.Builder
-import Blaze.ByteString.Builder.ByteString
--}
-
--- | location of an object in the database
-data ObjectLocation = NotFound | Loose Ref | Packed Ref Word64
-	deriving (Show,Eq)
-
--- | represent one entry in the tree
--- (permission,file or directory name,blob or tree ref)
--- name should maybe a filepath, but not sure about the encoding.
-type TreeEnt = (Int,ByteString,Ref)
-
--- | an author or committer line
--- has the format: name <email> time timezone
--- FIXME: should be a string, but I don't know if the data is stored
--- consistantly in one encoding (UTF8)
-type Name = (ByteString,ByteString,Int,Int)
-
--- | type of a git object.
-data ObjectType =
-	  TypeTree
-	| TypeBlob
-	| TypeCommit
-	| TypeTag
-	| TypeDeltaOff
-	| TypeDeltaRef
-	deriving (Show,Eq)
-
--- | Delta objects points to some others objects in the database
--- either as offset in the pack or as a direct reference.
-data ObjectPtr = PtrRef Ref | PtrOfs Word64 deriving (Show,Eq)
-
-type ObjectHeader = (ObjectType, Word64, Maybe ObjectPtr)
-
-type ObjectData = L.ByteString
-
--- | Raw objects infos have an header (type, size, ptr),
--- the data and a pointers chains to parents for resolved objects.
-data ObjectInfo = ObjectInfo
-	{ oiHeader :: ObjectHeader
-	, oiData   :: ObjectData
-	, oiChains :: [ObjectPtr]
-	} deriving (Show,Eq)
-
-class Objectable a where
-	getType :: a -> ObjectType
-	getRaw  :: a -> L.ByteString
-	isDelta :: a -> Bool
-
-	toCommit :: a -> Maybe Commit
-	toCommit = const Nothing
-	toTree   :: a -> Maybe Tree
-	toTree = const Nothing
-	toTag    :: a -> Maybe Tag
-	toTag = const Nothing
-	toBlob   :: a -> Maybe Blob
-	toBlob = const Nothing
-
--- | describe a git object, that could of 6 differents types:
--- tree, blob, commit, tag and deltas (offset or ref).
--- the deltas one are only available through packs.
-data Object = forall a . Objectable a => Object a
-
-objectWrap :: Objectable a => a -> Object
-objectWrap a = Object a
-
-data Tree = Tree { treeGetEnts :: [TreeEnt] } deriving (Show,Eq)
-data Blob = Blob { blobGetContent :: L.ByteString } deriving (Show,Eq)
-data Commit = Commit
-	{ commitTreeish   :: Ref
-	, commitParents   :: [Ref]
-	, commitAuthor    :: Name
-	, commitCommitter :: Name
-	, commitMessage   :: ByteString
-	} deriving (Show,Eq)
-data Tag = Tag
-	{ tagRef        :: Ref
-	, tagObjectType :: ObjectType
-	, tagBlob       :: ByteString
-	, tagName       :: Name
-	, tagS          :: ByteString
-	} deriving (Show,Eq)
-data DeltaOfs = DeltaOfs Word64 Delta
-	deriving (Show,Eq)
-data DeltaRef = DeltaRef Ref Delta
-	deriving (Show,Eq)
-
-objectToType :: Object -> ObjectType
-objectToType (Object a) = getType a
-
-objectTypeMarshall :: ObjectType -> String
-objectTypeMarshall TypeTree   = "tree"
-objectTypeMarshall TypeBlob   = "blob"
-objectTypeMarshall TypeCommit = "commit"
-objectTypeMarshall TypeTag    = "tag"
-objectTypeMarshall _          = error "deltas cannot be marshalled"
-
-objectTypeUnmarshall :: String -> ObjectType
-objectTypeUnmarshall "tree"   = TypeTree
-objectTypeUnmarshall "blob"   = TypeBlob
-objectTypeUnmarshall "commit" = TypeCommit
-objectTypeUnmarshall "tag"    = TypeTag
-objectTypeUnmarshall _        = error "unknown object type"
-
-objectTypeIsDelta :: ObjectType -> Bool
-objectTypeIsDelta TypeDeltaOff = True
-objectTypeIsDelta TypeDeltaRef = True
-objectTypeIsDelta _            = False
-
-objectIsDelta :: Object -> Bool
-objectIsDelta (Object a) = isDelta a
-
-objectToTree :: Object -> Maybe Tree
-objectToTree (Object a) = toTree a
-
-objectToCommit :: Object -> Maybe Commit
-objectToCommit (Object a) = toCommit a
-
-objectToTag :: Object -> Maybe Tag
-objectToTag (Object a) = toTag a
-
-objectToBlob :: Object -> Maybe Blob
-objectToBlob (Object a) = toBlob a
-
--- | the enum instance is useful when marshalling to pack file.
-instance Enum ObjectType where
-	fromEnum TypeCommit   = 0x1
-	fromEnum TypeTree     = 0x2
-	fromEnum TypeBlob     = 0x3
-	fromEnum TypeTag      = 0x4
-	fromEnum TypeDeltaOff = 0x6
-	fromEnum TypeDeltaRef = 0x7
-
-	toEnum 0x1 = TypeCommit
-	toEnum 0x2 = TypeTree
-	toEnum 0x3 = TypeBlob
-	toEnum 0x4 = TypeTag
-	toEnum 0x6 = TypeDeltaOff
-	toEnum 0x7 = TypeDeltaRef
-	toEnum n   = error ("not a valid object: " ++ show n)
-
-octal :: Parser Int
-octal = B.foldl' step 0 `fmap` takeWhile1 isOct where
-	isOct w = w >= 0x30 && w <= 0x37
-	step a w = a * 8 + fromIntegral (w - 0x30)
-
-skipChar :: Char -> Parser ()
-skipChar c = PC.char c >> return ()
-
-referenceHex = fromHex <$> P.take 40
-referenceBin = fromBinary <$> P.take 20
-
--- | parse a tree content
-treeParse = (Tree <$> parseEnts) where
-	parseEnts = atEnd >>= \end -> if end then return [] else liftM2 (:) parseEnt parseEnts
-	parseEnt = liftM3 (,,) octal (PC.char ' ' >> takeTill ((==) 0)) (word8 0 >> referenceBin)
--- | parse a blob content
-blobParse = (Blob <$> takeLazyByteString)
-
--- | parse a commit content
-commitParse = do
-	tree <- string "tree " >> referenceHex
-	skipChar '\n'
-	parents   <- many parseParentRef
-	author    <- string "author " >> parseName
-	committer <- string "committer " >> parseName
-	skipChar '\n'
-	message <- takeByteString
-	return $ Commit tree parents author committer message
-	where
-		parseParentRef = do
-			tree <- string "parent " >> referenceHex
-			skipChar '\n'
-			return tree
-		
--- | parse a tag content
-tagParse = do
-	object <- string "object " >> referenceHex
-	skipChar '\n'
-	type_ <- objectTypeUnmarshall . BC.unpack <$> (string "type " >> takeTill ((==) 0x0a))
-	skipChar '\n'
-	tag   <- string "tag " >> takeTill ((==) 0x0a)
-	skipChar '\n'
-	tagger <- string "tagger " >> parseName
-	skipChar '\n'
-	signature <- takeByteString
-	return $ Tag object type_ tag tagger signature
-
-parseName = do
-	name <- B.init <$> PC.takeWhile ((/=) '<')
-	skipChar '<'
-	email <- PC.takeWhile ((/=) '>')
-	_ <- string "> "
-	time <- PC.decimal
-	_ <- string " "
-	timezone <- PC.signed PC.decimal
-	skipChar '\n'
-	return (name, email, time, timezone)
-
-objectParseTree = objectWrap <$> treeParse
-objectParseCommit = objectWrap <$> commitParse
-objectParseTag = objectWrap <$> tagParse
-objectParseBlob = objectWrap <$> blobParse
-
--- header of loose objects, but also useful for any object to determine object's hash
-objectWriteHeader :: ObjectType -> Word64 -> ByteString
-objectWriteHeader ty sz = BC.pack (objectTypeMarshall ty ++ " " ++ show sz ++ [ '\0' ])
-
-objectWrite :: Object -> L.ByteString
-objectWrite (Object a) = getRaw a
-
-treeWrite (Tree ents) = L.fromChunks $ concat $ map writeTreeEnt ents where
-	writeTreeEnt (perm,name,ref) =
-		[ BC.pack $ printf "%o" perm
-		, BC.singleton ' '
-		, name
-		, B.singleton 0
-		, toBinary ref
-		]
-
-commitWrite (Commit tree parents author committer msg) =
-	L.fromChunks [BC.unlines ls, B.singleton 0xa, msg]
-	where
-		ls = [ "tree " `BC.append` (toHex tree) ] ++
-		     map (BC.append "parent " . toHex) parents ++
-		     [ writeName "author" author
-		     , writeName "committer" committer ]
-
-tagWrite (Tag ref ty tag tagger signature) =
-	L.fromChunks [BC.unlines ls, B.singleton 0xa, signature]
-	where
-		ls = [ "object " `BC.append` (toHex ref)
-		     , "type " `BC.append` (BC.pack $ objectTypeMarshall ty)
-		     , "tag " `BC.append` tag
-		     , writeName "tagger" tagger ]
-
-blobWrite (Blob bData) = bData
-
-instance Objectable Blob where
-	getType _ = TypeBlob
-	getRaw    = blobWrite
-	isDelta   = const False
-	toBlob t  = Just t
-
-instance Objectable Commit where
-	getType _  = TypeCommit
-	getRaw     = commitWrite
-	isDelta    = const False
-	toCommit t = Just t
-
-instance Objectable Tag where
-	getType _ = TypeTag
-	getRaw    = tagWrite
-	isDelta   = const False
-	toTag t   = Just t
-
-instance Objectable Tree where
-	getType _ = TypeTree
-	getRaw    = treeWrite
-	isDelta   = const False
-	toTree t  = Just t
-
-instance Objectable DeltaOfs where
-	getType _ = TypeDeltaOff
-	getRaw    = error "delta offset cannot be marshalled"
-	isDelta   = const True
-
-instance Objectable DeltaRef where
-	getType _ = TypeDeltaRef
-	getRaw    = error "delta ref cannot be marshalled"
-	isDelta   = const True
-
-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 label (name, email, time, tz) =
-	B.concat [label, " ", name, " <", email, "> ", BC.pack (show time), " ", BC.pack (showtz tz)]
-	where showtz i = (if i > 0 then "+" else "") ++ show i
diff --git a/Data/Git/Pack.hs b/Data/Git/Pack.hs
deleted file mode 100644
--- a/Data/Git/Pack.hs
+++ /dev/null
@@ -1,152 +0,0 @@
--- |
--- Module      : Data.Git.Pack
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-module Data.Git.Pack
-	( PackedObjectInfo(..)
-	, PackedObjectRaw
-	-- * Enumerators of packs
-	, packEnumerate
-	-- * Helpers to process packs
-	, packOpen
-	, packClose
-	-- * Command for the content of a pack
-	, packReadHeader
-	, packReadMapAtOffset
-	, packReadAtOffset
-	, packReadRawAtOffset
-	, packEnumerateObjects
-	-- * turn a packed object into a 
-	, packedObjectToObject
-	, packObjectFromRaw
-	) where
-
-import Control.Applicative ((<$>))
-import Control.Arrow (second)
-import Control.Monad
-
-import System.IO
-import System.FilePath
-import System.Directory
-
-import Data.Bits
-import Data.List
-import qualified Data.ByteString.Lazy as L
-
-import Data.Attoparsec (anyWord8)
-import qualified Data.Attoparsec as A
-import qualified Data.Attoparsec.Lazy as AL
-
-import Data.Git.Internal
-import Data.Git.Path
-import Data.Git.Object
-import Data.Git.Delta
-import Data.Git.Ref
-import Data.Git.FileReader
-
-import Data.Word
-
-type PackedObjectRaw = (PackedObjectInfo, L.ByteString)
-
-data PackedObjectInfo = PackedObjectInfo
-	{ poiType       :: ObjectType
-	, poiOffset     :: Word64
-	, poiSize       :: Word64
-	, poiActualSize :: Word64
-	, poiExtra      :: Maybe ObjectPtr
-	} deriving (Show,Eq)
-
--- | Enumerate the pack refs available in this repository.
-packEnumerate repoPath = map onlyHash . filter isPackFile <$> getDirectoryContents (repoPath </> "objects" </> "pack")
-	where
-		isPackFile x = ".pack" `isSuffixOf` x
-		onlyHash = fromHexString . takebut 5 . drop 5
-		takebut n l = take (length l - n) l
-
--- | open a pack
-packOpen :: FilePath -> Ref -> IO FileReader
-packOpen repoPath packRef = openFile (packPath repoPath packRef) ReadMode >>= fileReaderNew False
-
--- | close a pack
-packClose :: FileReader -> IO ()
-packClose = fileReaderClose
-
--- | return the number of entries in this pack
-packReadHeader repoPath packRef =
-	withFileReader (packPath repoPath packRef) $ \filereader ->
-		fileReaderParse filereader parseHeader
-	where parseHeader = do
-		packMagic <- be32 <$> A.take 4
-		when (packMagic /= 0x5041434b) $ error "not a git packfile"
-		ver <- be32 <$> A.take 4
-		when (ver /= 2) $ error ("pack file version not supported: " ++ show ver)
-		be32 <$> A.take 4
-
--- | read an object at a specific position using a map function on the objectData
-packReadMapAtOffset fr offset mapData = fileReaderSeek fr offset >> getNextObject fr mapData
-
--- | read an object at a specific position
-packReadAtOffset :: FileReader -> Word64 -> IO (Maybe Object)
-packReadAtOffset fr offset = packReadMapAtOffset fr offset id
-
--- | read a raw representation at a specific position
-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 repoPath packRef entries f =
-	withFileReader (packPath repoPath packRef) $ \filebuffer -> do
-		fileReaderSeek filebuffer 12
-		parseNext filebuffer entries
-	where
-		parseNext :: FileReader -> Int -> IO ()
-		parseNext _  0    = return ()
-		parseNext fr ents = getNextObjectRaw fr >>= f >> parseNext fr (ents-1)
-
-getNextObject :: FileReader -> (L.ByteString -> L.ByteString) -> IO (Maybe Object)
-getNextObject fr mapData =
-	packedObjectToObject . second mapData <$> getNextObjectRaw fr
-
-packedObjectToObject (PackedObjectInfo { poiType = ty, poiExtra = extra }, objData) =
-	packObjectFromRaw (ty, extra, objData)
-
-packObjectFromRaw (TypeCommit, Nothing, objData) = AL.maybeResult $ AL.parse objectParseCommit objData
-packObjectFromRaw (TypeTree, Nothing, objData)   = AL.maybeResult $ AL.parse objectParseTree objData
-packObjectFromRaw (TypeBlob, Nothing, objData)   = AL.maybeResult $ AL.parse objectParseBlob objData
-packObjectFromRaw (TypeTag, Nothing, objData)    = AL.maybeResult $ AL.parse objectParseTag objData
-packObjectFromRaw (TypeDeltaOff, Just (PtrOfs o), objData) = objectWrap . DeltaOfs o <$> deltaRead objData
-packObjectFromRaw (TypeDeltaRef, Just (PtrRef r), objData) = objectWrap . DeltaRef r <$> deltaRead objData
-packObjectFromRaw _                              = error "can't happen unless someone change getNextObjectRaw"
-
-getNextObjectRaw :: FileReader -> IO PackedObjectRaw
-getNextObjectRaw fr = do
-	sobj      <- fileReaderGetPos fr
-	(ty, size) <- fileReaderParse fr parseObjectHeader
-	extra      <- case ty of
-		TypeDeltaRef -> Just . PtrRef . fromBinary <$> fileReaderGetBS 20 fr
-		TypeDeltaOff -> Just . PtrOfs . deltaOffFromList <$> fileReaderGetVLF fr
-		_            -> return Nothing
-	objData    <- fileReaderInflateToSize fr size
-	eobj       <- fileReaderGetPos fr
-
-	return (PackedObjectInfo ty sobj (eobj - sobj) size extra, objData)
-	where
-		parseObjectHeader = do
-			(m, ty, sz) <- splitFirst <$> anyWord8
-			size <- if m then (sz +) <$> getNextSize 4 else return sz
-			return (ty, size)
-			where
-				getNextSize n = do
-					(c, sz) <- splitOther n <$> anyWord8
-					if c then (sz +) <$> getNextSize (n+7) else return sz
-
-				splitFirst :: Word8 -> (Bool, ObjectType, Word64)
-				splitFirst w = (w `testBit` 7, toEnum $ fromIntegral ((w `shiftR` 4) .&. 0x7), fromIntegral (w .&. 0xf))
-				splitOther n w = (w `testBit` 7, fromIntegral (w .&. 0x7f) `shiftL` n)
-
-		deltaOffFromList (x:xs) = foldl' acc (fromIntegral (x `clearBit` 7)) xs
-			where acc a w = ((a+1) `shiftL` 7) + fromIntegral (w `clearBit` 7)
-		deltaOffFromList [] = error "cannot happen"
diff --git a/Data/Git/Path.hs b/Data/Git/Path.hs
--- a/Data/Git/Path.hs
+++ b/Data/Git/Path.hs
@@ -26,15 +26,15 @@
 packDirPath repoPath = repoPath </> "objects" </> "pack"
 
 indexPath repoPath indexRef =
-	packDirPath repoPath </> ("pack-" ++ toHexString indexRef ++ ".idx")
+        packDirPath repoPath </> ("pack-" ++ toHexString indexRef ++ ".idx")
 
 packPath repoPath packRef =
-	packDirPath repoPath </> ("pack-" ++ toHexString packRef ++ ".pack")
+        packDirPath repoPath </> ("pack-" ++ toHexString packRef ++ ".pack")
 
 objectPath repoPath d f = repoPath </> "objects" </> d </> f
 objectPathOfRef repoPath ref = objectPath repoPath d f
-	where (d,f) = toFilePathParts ref
+        where (d,f) = toFilePathParts ref
 
 objectTemporaryPath repoPath = do
-	r <- fst . random <$> getStdGen :: IO Int
-	return (repoPath </> "objects" </> ("tmp-" ++ show r ++ ".tmp"))
+        r <- fst . random <$> getStdGen :: IO Int
+        return (repoPath </> "objects" </> ("tmp-" ++ show r ++ ".tmp"))
diff --git a/Data/Git/Ref.hs b/Data/Git/Ref.hs
--- a/Data/Git/Ref.hs
+++ b/Data/Git/Ref.hs
@@ -6,21 +6,21 @@
 -- Portability : unix
 --
 module Data.Git.Ref
-	( Ref
-	, isHex
-	, isHexString
-	, fromHex
-	, fromHexString
-	, fromBinary
-	, toBinary
-	, toHex
-	, toHexString
-	, refPrefix
-	, cmpPrefix
-	, toFilePathParts
-	, hash
-	, hashLBS
-	) where
+        ( Ref
+        , isHex
+        , isHexString
+        , fromHex
+        , fromHexString
+        , fromBinary
+        , toBinary
+        , toHex
+        , toHexString
+        , refPrefix
+        , cmpPrefix
+        , toFilePathParts
+        , hash
+        , hashLBS
+        ) where
 
 import Control.Monad (forM_)
 import qualified Crypto.Hash.SHA1 as SHA1
@@ -36,10 +36,10 @@
 
 -- | represent a git reference (SHA1)
 newtype Ref = Ref ByteString
-	deriving (Eq,Ord)
+        deriving (Eq,Ord)
 
 instance Show Ref where
-	show = BC.unpack . toHex
+        show = BC.unpack . toHex
 
 isHex = and . map isHexDigit . BC.unpack
 isHexString = and . map isHexDigit
@@ -48,36 +48,36 @@
 -- and turn into a ref
 fromHex :: ByteString -> Ref
 fromHex s
-	| B.length s == 40 = Ref $ B.unsafeCreate 20 populateRef
-	| otherwise        = error ("not a valid hex ref: " ++ show s)
-	where 
-		populateRef ptr = forM_ [0..19] $ \i -> do
-			let v = (unhex (B.unsafeIndex s (i*2+0)) `shiftL` 4) .|. unhex (B.unsafeIndex s (i*2+1))
-			pokeElemOff ptr (i+0) v
+        | B.length s == 40 = Ref $ B.unsafeCreate 20 populateRef
+        | otherwise        = error ("not a valid hex ref: " ++ show s)
+        where 
+                populateRef ptr = forM_ [0..19] $ \i -> do
+                        let v = (unhex (B.unsafeIndex s (i*2+0)) `shiftL` 4) .|. unhex (B.unsafeIndex s (i*2+1))
+                        pokeElemOff ptr (i+0) v
 
-		unhex 0x30 = 0  -- '0'
-		unhex 0x31 = 1
-		unhex 0x32 = 2
-		unhex 0x33 = 3
-		unhex 0x34 = 4
-		unhex 0x35 = 5
-		unhex 0x36 = 6
-		unhex 0x37 = 7
-		unhex 0x38 = 8
-		unhex 0x39 = 9  -- '9'
-		unhex 0x41 = 10 -- 'A'
-		unhex 0x42 = 11
-		unhex 0x43 = 12
-		unhex 0x44 = 13
-		unhex 0x45 = 14
-		unhex 0x46 = 15 -- 'F'
-		unhex 0x61 = 10 -- 'a'
-		unhex 0x62 = 11
-		unhex 0x63 = 12
-		unhex 0x64 = 13
-		unhex 0x65 = 14
-		unhex 0x66 = 15 -- 'f'
-		unhex _    = error "error fromHex: not a valid hex character"
+                unhex 0x30 = 0  -- '0'
+                unhex 0x31 = 1
+                unhex 0x32 = 2
+                unhex 0x33 = 3
+                unhex 0x34 = 4
+                unhex 0x35 = 5
+                unhex 0x36 = 6
+                unhex 0x37 = 7
+                unhex 0x38 = 8
+                unhex 0x39 = 9  -- '9'
+                unhex 0x41 = 10 -- 'A'
+                unhex 0x42 = 11
+                unhex 0x43 = 12
+                unhex 0x44 = 13
+                unhex 0x45 = 14
+                unhex 0x46 = 15 -- 'F'
+                unhex 0x61 = 10 -- 'a'
+                unhex 0x62 = 11
+                unhex 0x63 = 12
+                unhex 0x64 = 13
+                unhex 0x65 = 14
+                unhex 0x66 = 15 -- 'f'
+                unhex _    = error "error fromHex: not a valid hex character"
 
 -- | take a hexadecimal string that represent a reference
 -- and turn into a ref
@@ -87,15 +87,15 @@
 -- | transform a ref into an hexadecimal bytestring
 toHex :: Ref -> ByteString
 toHex (Ref bs) = B.unsafeCreate 40 populateHex
-	where
-		populateHex ptr = forM_ [0..19] $ \i -> do
-			let (a,b) = B.unsafeIndex bs i `divMod` 16
-			pokeElemOff ptr (i*2+0) (hex a)
-			pokeElemOff ptr (i*2+1) (hex b)
-		hex i
-			| i >= 0 && i <= 9   = 0x30 + i
-			| i >= 10 && i <= 15 = 0x61 + i - 10
-			| otherwise          = 0
+        where
+                populateHex ptr = forM_ [0..19] $ \i -> do
+                        let (a,b) = B.unsafeIndex bs i `divMod` 16
+                        pokeElemOff ptr (i*2+0) (hex a)
+                        pokeElemOff ptr (i*2+1) (hex b)
+                hex i
+                        | i >= 0 && i <= 9   = 0x30 + i
+                        | i >= 10 && i <= 15 = 0x61 + i - 10
+                        | otherwise          = 0
 
 -- | transform a ref into an hexadecimal string
 toHexString :: Ref -> String
@@ -105,8 +105,8 @@
 -- and returns a ref.
 fromBinary :: ByteString -> Ref
 fromBinary b
-	| B.length b == 20 = Ref b
-	| otherwise        = error "not a valid binary ref"
+        | B.length b == 20 = Ref b
+        | otherwise        = error "not a valid binary ref"
 
 -- | turn a reference into a binary bytestring
 toBinary :: Ref -> ByteString
diff --git a/Data/Git/Repository.hs b/Data/Git/Repository.hs
--- a/Data/Git/Repository.hs
+++ b/Data/Git/Repository.hs
@@ -8,374 +8,191 @@
 -- Portability : unix
 --
 module Data.Git.Repository
-	( Git
-	, HTree
-	, HTreeEnt(..)
-	, gitRepoPath
-	, openRepo
-	, closeRepo
-	, withRepo
-	, findRepo
-	, findReference
-	, findReferencesWithPrefix
-	, findObjectRaw
-	, findObjectRawAt
-	, findObject
-	, findCommit
-	, findTree
-	, findObjectAt
-	, buildHTree
-	, resolvePath
-	, resolveTreeish
-	, resolveRevision
-	, initRepo
-	, isRepo
-	) where
-
-import System.Directory
-import System.FilePath
-import System.Environment
+        ( Git
+        , HTree
+        , HTreeEnt(..)
+        , getCommit
+        , getTree
+        , rewrite
+        , buildHTree
+        , resolvePath
+        , resolveTreeish
+        , resolveRevision
+        , initRepo
+        , isRepo
+        ) where
 
 import Control.Applicative ((<$>))
-import Control.Exception
-import qualified Control.Exception as E
 import Control.Monad
 
-import Data.Word
-import Data.IORef
-import Data.List ((\\), find, isPrefixOf)
+import Data.Maybe (fromMaybe)
+import Data.List (find)
 
 import Data.ByteString (ByteString)
 
-import Data.Git.Delta
-import Data.Git.FileReader
-import Data.Git.Index
-import Data.Git.Pack
 import Data.Git.Named
-import Data.Git.Object
+import Data.Git.Types
+import Data.Git.Storage.Object
+import Data.Git.Storage
 import Data.Git.Revision
-import Data.Git.Loose
+import Data.Git.Storage.Loose
 import Data.Git.Ref
 
-data IndexReader = IndexReader IndexHeader FileReader
-
--- | represent an git repo, with possibly already opened filereaders
--- for indexes and packs
-data Git = Git
-	{ gitRepoPath  :: FilePath
-	, indexReaders :: IORef [(Ref, IndexReader)]
-	, packReaders  :: IORef [(Ref, FileReader)]
-	}
-
 -- | hierarchy tree, either a reference to a blob (file) or a tree (directory).
 data HTreeEnt = TreeDir Ref HTree | TreeFile Ref
 type HTree = [(Int,ByteString,HTreeEnt)]
 
--- | open a new git repository context
-openRepo :: FilePath -> IO Git
-openRepo path = liftM2 (Git path) (newIORef []) (newIORef [])
-
--- | close a git repository context, closing all remaining fileReaders.
-closeRepo :: Git -> IO ()
-closeRepo (Git { indexReaders = ireaders, packReaders = preaders }) = do
-	mapM_ (closeIndexReader . snd) =<< readIORef ireaders
-	mapM_ (fileReaderClose . snd) =<< readIORef preaders
-	where closeIndexReader (IndexReader _ fr) = fileReaderClose fr
-
--- | find the git repository from the current directory.
-findRepo :: IO FilePath
-findRepo = do
-	menvDir <- E.catch (Just <$> getEnv "GIT_DIR") (\(_:: SomeException) -> return Nothing)
-	case menvDir of
-		Nothing     -> checkDir 0
-		Just envDir -> do
-			e <- isRepo envDir
-			when (not e) $ error "environment GIT_DIR is not a git repository" 
-			return envDir
-	where
-		checkDir 128 = error "not a git repository"
-		checkDir n   = do
-			let filepath = concat (replicate n ("../") ++ [".git"])
-			e <- isRepo filepath
-			if e then return filepath else checkDir (n+1)
-
--- | execute a function f with a git context.
-withRepo path f = bracket (openRepo path) closeRepo f
-
---iterateIndexes :: Git -> (a -> (IdxRef, IndexReader) -> IO (a,Bool)) -> a -> IO a
-iterateIndexes git f initAcc = do
-	allIndexes    <- indexEnumerate (gitRepoPath git)
-	readers       <- readIORef (indexReaders git)
-	(a,terminate) <- loop initAcc readers
-	if terminate
-		then return a
-		else readRemainingIndexes a (allIndexes \\ map fst readers)
-	where
-		loop acc []     = return (acc, False)
-		loop acc (r:rs) = do
-			(nacc, terminate) <- f acc r
-			if terminate
-				then return (nacc,True)
-				else loop nacc rs
-
-		readRemainingIndexes acc []            = return acc
-		readRemainingIndexes acc (idxref:idxs) = do
-			fr <- indexOpen (gitRepoPath git) idxref
-			idx <- indexReadHeader fr
-			let idxreader = IndexReader idx fr
-			let r = (idxref, idxreader)
-			modifyIORef (indexReaders git) (\l -> r : l)
-			(nacc, terminate) <- f acc r
-			if terminate
-				then return nacc
-				else readRemainingIndexes nacc idxs
-
-findReference :: Git -> Ref -> IO ObjectLocation
-findReference git ref = maybe NotFound id <$> (findLoose `mplusIO` findInIndexes)
-	where
-		findLoose :: IO (Maybe ObjectLocation)
-		findLoose = do
-			isLoose <- looseExists (gitRepoPath git) ref
-			if isLoose then return (Just $ Loose ref) else return Nothing
-
-		findInIndexes :: IO (Maybe ObjectLocation)
-		findInIndexes = iterateIndexes git isinIndex Nothing --f -> (a -> IndexReader -> IO (a,Bool)) -> a -> IO a
-
-		isinIndex acc (idxref, (IndexReader idxhdr indexreader)) = do
-			mloc <- indexGetReferenceLocation idxhdr indexreader ref
-			case mloc of
-				Nothing  -> return (acc, False)
-				Just loc -> return (Just $ Packed idxref loc, True)
-
-		mplusIO :: IO (Maybe a) -> IO (Maybe a) -> IO (Maybe a)
-		mplusIO f g = f >>= \vopt -> case vopt of
-			Nothing -> g
-			Just v  -> return $ Just v
-
--- | get all the references that start by a specific prefix
-findReferencesWithPrefix :: Git -> String -> IO [Ref]
-findReferencesWithPrefix git pre
-	| invalidLength         = error "not a valid prefix"
-	| not (isHexString pre) = error "reference prefix contains non hexchar"
-	| otherwise             = do
-		looseRefs  <- looseEnumerateWithPrefixFilter (gitRepoPath git) (take 2 pre) matchRef
-		packedRefs <- concat <$> iterateIndexes git idxPrefixMatch []
-		return (looseRefs ++ packedRefs)
-	where
-		-- not very efficient way to do that... will do for now.
-		matchRef ref = pre `isPrefixOf` toHexString ref
-		invalidLength = length pre < 2 || length pre > 39 
-
-		idxPrefixMatch acc (_, (IndexReader idxhdr indexreader)) = do
-			refs <- indexGetReferencesWithPrefix idxhdr indexreader pre
-			return (refs:acc,False)
-
-readRawFromPack :: Git -> Ref -> Word64 -> IO (FileReader, PackedObjectRaw)
-readRawFromPack git pref offset = do
-	readers <- readIORef (packReaders git)
-	reader <- case lookup pref readers of
-		Just r  -> return r
-		Nothing -> do
-			p <- packOpen (gitRepoPath git) pref
-			modifyIORef (packReaders git) ((pref, p):)
-			return p
-	po <- packReadRawAtOffset reader offset
-	return (reader, po)
-
-readFromPack :: Git -> Ref -> Word64 -> Bool -> IO (Maybe ObjectInfo)
-readFromPack git pref o resolveDelta = do
-	(reader, x) <- readRawFromPack git pref o
-	if resolveDelta then resolve reader o x else return $ Just $ generifyHeader x
-	where
-		generifyHeader :: PackedObjectRaw -> ObjectInfo
-		generifyHeader (po, objData) = ObjectInfo { oiHeader = hdr, oiData = objData, oiChains = [] }
-			where hdr = (poiType po, poiActualSize po, poiExtra po)
-
-		resolve :: FileReader -> Word64 -> PackedObjectRaw -> IO (Maybe ObjectInfo)
-		resolve reader offset (po, objData) = do
-			case (poiType po, poiExtra po) of
-				(TypeDeltaOff, Just ptr@(PtrOfs doff)) -> do
-					let delta = deltaRead objData
-					let noffset = offset - doff
-					base <- resolve reader noffset =<< packReadRawAtOffset reader noffset
-					return $ addToChain ptr $ applyDelta delta base
-				(TypeDeltaRef, Just ptr@(PtrRef bref)) -> do
-					let delta = deltaRead objData
-					base <- findObjectRaw git bref True
-					return $ addToChain ptr $ applyDelta delta base
-				_                                    -> return $ Just $ generifyHeader (po, objData)
-
-		addToChain ptr (Just oi) = Just (oi { oiChains = ptr : oiChains oi })
-		addToChain _   Nothing   = Nothing
-
-		applyDelta :: Maybe Delta -> Maybe ObjectInfo -> Maybe ObjectInfo
-		applyDelta (Just delta@(Delta _ rSize _)) (Just objInfo) = Just $ objInfo
-			{ oiHeader = (\(a,_,c) -> (a,rSize,c)) $ oiHeader objInfo
-			, oiData   = deltaApply (oiData objInfo) delta
-			}
-		applyDelta _ _                                      = Nothing
-
-
--- | get an object from repository
-findObjectRawAt :: Git -> ObjectLocation -> Bool -> IO (Maybe ObjectInfo)
-findObjectRawAt _   NotFound    _ = return Nothing
-findObjectRawAt git (Loose ref) _ = Just . (\(h,d)-> ObjectInfo h d[]) <$> looseReadRaw (gitRepoPath git) ref
-findObjectRawAt git (Packed pref o) resolveDelta = readFromPack git pref o resolveDelta
-
--- | get an object from repository
-findObjectRaw :: Git -> Ref -> Bool -> IO (Maybe ObjectInfo)
-findObjectRaw git ref resolveDelta = do
-	loc <- findReference git ref
-	findObjectRawAt git loc resolveDelta
-
--- | get an object type from repository
-findObjectType :: Git -> Ref -> IO (Maybe ObjectType)
-findObjectType git ref = findReference git ref >>= findObjectTypeAt
-	where
-		findObjectTypeAt NotFound        = return Nothing
-		findObjectTypeAt (Loose _)       = Just . (\(t,_,_) -> t) <$> looseReadHeader (gitRepoPath git) ref
-		findObjectTypeAt (Packed pref o) =
-			fmap ((\(ty,_,_) -> ty) . oiHeader) <$> readFromPack git pref o True
-
--- | get an object from repository using a location to reference it.
-findObjectAt :: Git -> ObjectLocation -> Bool -> IO (Maybe Object)
-findObjectAt git loc resolveDelta = maybe Nothing toObject <$> findObjectRawAt git loc resolveDelta
-	where
-		toObject (ObjectInfo { oiHeader = (ty, _, extra), oiData = objData }) = packObjectFromRaw (ty, extra, objData)
-
--- | get an object from repository using a ref.
-findObject :: Git -> Ref -> Bool -> IO (Maybe Object)
-findObject git ref resolveDelta = maybe Nothing toObject <$> findObjectRaw git ref resolveDelta
-	where
-		toObject (ObjectInfo { oiHeader = (ty, _, extra), oiData = objData }) = packObjectFromRaw (ty, extra, objData)
-
 -- should be a standard function that do that...
 mapJustM f (Just o) = f o
 mapJustM _ Nothing  = return Nothing
 
-findCommit :: Git -> Ref -> IO (Maybe Commit)
-findCommit git ref = findObject git ref True >>= mapJustM unwrap
-	where
-		unwrap (objectToCommit -> Just c@(Commit _ _ _ _ _)) = return $ Just c
-		unwrap _                                             = return Nothing
+-- | get a specified commit
+getCommit :: Git -> Ref -> IO (Maybe Commit)
+getCommit git ref = getObject git ref True >>= mapJustM unwrap
+        where
+                unwrap (objectToCommit -> Just c@(Commit _ _ _ _ _)) = return $ Just c
+                unwrap _                                             = return Nothing
 
-findTree :: Git -> Ref -> IO (Maybe Tree)
-findTree git ref = findObject git ref True >>= mapJustM unwrap
-	where
-		unwrap (objectToTree -> Just c@(Tree _ )) = return $ Just c
-		unwrap _                                  = return Nothing
+-- | get a specified tree
+getTree :: Git -> Ref -> IO (Maybe Tree)
+getTree git ref = getObject git ref True >>= mapJustM unwrap
+        where
+                unwrap (objectToTree -> Just c@(Tree _ )) = return $ Just c
+                unwrap _                                  = return Nothing
 
 -- | try to resolve a string to a specific commit ref
 -- for example: HEAD, HEAD^, master~3, shortRef
 resolveRevision :: Git -> Revision -> IO (Maybe Ref)
 resolveRevision git (Revision prefix modifiers) = resolvePrefix >>= modf modifiers
-	where
-		resolvePrefix :: IO Ref
-		resolvePrefix = resolveNamedPrefix fs >>= maybe resolvePrePrefix (return . Just) >>= maybe (return $ fromHexString prefix) return
+        where
+                resolvePrefix :: IO Ref
+                resolvePrefix = resolveNamedPrefix fs >>= maybe resolvePrePrefix (return . Just) >>= maybe (return $ fromHexString prefix) return
 
-		resolvePrePrefix :: IO (Maybe Ref)
-		resolvePrePrefix = do
-			refs <- findReferencesWithPrefix git prefix
-			case refs of
-				[]  -> return Nothing
-				[r] -> return (Just r)
-				_   -> error "multiple references with this prefix"
+                resolvePrePrefix :: IO (Maybe Ref)
+                resolvePrePrefix = do
+                        refs <- findReferencesWithPrefix git prefix
+                        case refs of
+                                []  -> return Nothing
+                                [r] -> return (Just r)
+                                _   -> error "multiple references with this prefix"
 
-		fs = [ (specialExists, specialRead), (tagExists, tagRead), (headExists, headRead) ]
+                fs = [ (specialExists, specialRead), (tagExists, tagRead), (headExists, headRead) ]
 
-		resolveNamedPrefix []     = return Nothing
-		resolveNamedPrefix (x:xs) = do
-			exists <- (fst x) (gitRepoPath git) prefix
-			if exists
-				then Just <$> (snd x) (gitRepoPath git) prefix
-				else resolveNamedPrefix xs
-	
-		modf [] ref                  = return (Just ref)
-		modf (RevModParent i:xs) ref = do
-			parentRefs <- getParentRefs ref
-			case i of
-				0 -> error "revision modifier ^0 is not implemented"
-				_ -> case drop (i - 1) parentRefs of
-					[]    -> error "no such parent"
-					(p:_) -> modf xs p
+                resolveNamedPrefix []     = return Nothing
+                resolveNamedPrefix (x:xs) = do
+                        exists <- (fst x) (gitRepoPath git) prefix
+                        if exists
+                                then Just <$> (snd x) (gitRepoPath git) prefix
+                                else resolveNamedPrefix xs
+        
+                modf [] ref                  = return (Just ref)
+                modf (RevModParent i:xs) ref = do
+                        parentRefs <- getParentRefs ref
+                        case i of
+                                0 -> error "revision modifier ^0 is not implemented"
+                                _ -> case drop (i - 1) parentRefs of
+                                        []    -> error "no such parent"
+                                        (p:_) -> modf xs p
 
-		modf (RevModParentFirstN 1:xs) ref = modf (RevModParent 1:xs) ref
-		modf (RevModParentFirstN n:xs) ref = do
-			parentRefs <- getParentRefs ref
-			modf (RevModParentFirstN (n-1):xs) (head parentRefs)
-		modf (_:_) _ = error "unimplemented revision modifier"
+                modf (RevModParentFirstN 1:xs) ref = modf (RevModParent 1:xs) ref
+                modf (RevModParentFirstN n:xs) ref = do
+                        parentRefs <- getParentRefs ref
+                        modf (RevModParentFirstN (n-1):xs) (head parentRefs)
+                modf (_:_) _ = error "unimplemented revision modifier"
 
-		getParentRefs ref = do
-			obj <- findCommit git ref
-			case obj of
-				Just (Commit _ parents _ _ _) -> return parents
-				Nothing -> error "reference in commit chain doesn't exists"
+                getParentRefs ref = do
+                        obj <- getCommit git ref
+                        case obj of
+                                Just (Commit _ parents _ _ _) -> return parents
+                                Nothing -> error "reference in commit chain doesn't exists"
 
 -- | returns a tree from a ref that might be either a commit, a tree or a tag.
 resolveTreeish :: Git -> Ref -> IO (Maybe Tree)
-resolveTreeish git ref = findObject git ref True >>= mapJustM recToTree where
-	recToTree (objectToCommit -> Just (Commit tree _ _ _ _)) = resolveTreeish git tree
-	recToTree (objectToTag    -> Just (Tag tref _ _ _ _))    = resolveTreeish git tref
-	recToTree (objectToTree   -> Just t@(Tree _))            = return $ Just t
-	recToTree _                                              = return Nothing
+resolveTreeish git ref = getObject git ref True >>= mapJustM recToTree where
+        recToTree (objectToCommit -> Just (Commit tree _ _ _ _)) = resolveTreeish git tree
+        recToTree (objectToTag    -> Just (Tag tref _ _ _ _))    = resolveTreeish git tref
+        recToTree (objectToTree   -> Just t@(Tree _))            = return $ Just t
+        recToTree _                                              = return Nothing
 
+
+-- | Rewrite a set of commits from a revision and returns the new ref.
+--
+-- If during revision traversal (diving) there's a commit with zero or multiple
+-- parents then the traversal will stop regardless of the amount of parent requested.
+--
+-- calling "rewrite f 2 (revisionOf d)" on the following tree:
+--
+--          a <-- b <-- c <-- d
+--
+-- result in the following tree after mapping with f:
+--
+--          a <-- f(b) <-- f(c) <-- f(d)
+--
+rewrite :: Git                   -- ^ Repository
+        -> (Commit -> IO Commit) -- ^ Mapping function
+        -> Revision              -- ^ revision to start from
+        -> Int                   -- ^ the number of parents to map
+        -> IO Ref                -- ^ return the new head REF
+rewrite git mapCommit revision nbParent = do
+    ref <- fromMaybe (error "revision cannot be found") <$> resolveRevision git revision
+    resolveParents nbParent ref >>= process . reverse
+
+    where resolveParents :: Int -> Ref -> IO [ (Ref, Commit) ]
+          resolveParents 0 ref = (:[]) . (,) ref . fromMaybe (error "commit cannot be found") <$> getCommit git ref
+          resolveParents n ref = do commit <- fromMaybe (error "commit cannot be found") <$> getCommit git ref
+                                    case commitParents commit of
+                                         [parentRef] -> liftM ((ref,commit) :) (resolveParents (n-1) parentRef)
+                                         _           -> return [(ref,commit)]
+
+          process [] = error "nothing to rewrite"
+          process ((_,commit):next) =
+                      mapCommit commit >>= looseWrite (gitRepoPath git) . objectWrap >>= flip rewriteOne next
+
+          rewriteOne prevRef [] = return prevRef
+          rewriteOne prevRef ((_,commit):next) = do
+                      newCommit <- mapCommit $ commit { commitParents = [prevRef] }
+                      ref       <- looseWrite (gitRepoPath git) (objectWrap newCommit)
+                      rewriteOne ref next
+
 -- | build a hierarchy tree from a tree object
 buildHTree :: Git -> Tree -> IO HTree
 buildHTree git (Tree ents) = mapM resolveTree ents
-	where resolveTree (perm, ent, ref) = do
-		obj <- findObjectType git ref
-		case obj of
-			Just TypeBlob -> return (perm, ent, TreeFile ref)
-			Just TypeTree -> do
-				ctree <- findTree git ref
-				case ctree of
-					Nothing -> error "unknown reference in tree object: no such child"
-					Just t  -> do
-						dir   <- buildHTree git t
-						return (perm, ent, TreeDir ref dir)
-			Just _        -> error "wrong type embedded in tree object"
-			Nothing       -> error "unknown reference in tree object"
+        where resolveTree (perm, ent, ref) = do
+                obj <- getObjectType git ref
+                case obj of
+                        Just TypeBlob -> return (perm, ent, TreeFile ref)
+                        Just TypeTree -> do
+                                ctree <- getTree git ref
+                                case ctree of
+                                        Nothing -> error "unknown reference in tree object: no such child"
+                                        Just t  -> do
+                                                dir   <- buildHTree git t
+                                                return (perm, ent, TreeDir ref dir)
+                        Just _        -> error "wrong type embedded in tree object"
+                        Nothing       -> error "unknown reference in tree object"
 
 -- | resolve the ref (tree or blob) related to a path at a specific commit ref
-resolvePath :: Git -> Ref -> [ByteString] -> IO (Maybe Ref)
+resolvePath :: Git            -- ^ repository
+            -> Ref            -- ^ commit reference
+            -> [ByteString]   -- ^ paths
+            -> IO (Maybe Ref)
 resolvePath git commitRef paths = do
-	commit <- findCommit git commitRef
-	case commit of
-		Just (Commit tree _ _ _ _) -> resolve tree paths
-		Nothing                    -> error ("not a valid ref: " ++ show commitRef)
-	where
-		resolve :: Ref -> [ByteString] -> IO (Maybe Ref)
-		resolve treeRef []     = return $ Just treeRef
-		resolve treeRef (x:xs) = do
-			tree <- findTree git treeRef
-			case tree of
-				Just (Tree ents) -> do
-					let cEnt = treeEntRef <$> findEnt x ents
-					if xs == []
-						then return cEnt
-						else maybe (return Nothing) (\z -> resolve z xs) cEnt
-				Nothing          -> error ("not a valid ref: " ++ show treeRef)
-
-		findEnt x = find (\(_, b, _) -> b == x)
-		treeEntRef (_,_,r) = r
-	
--- | basic checks to see if a specific path looks like a git repo.
-isRepo :: FilePath -> IO Bool
-isRepo path = do
-	dir     <- doesDirectoryExist path
-	subDirs <- mapM (doesDirectoryExist . (path </>))
-		["branches","hooks","info"
-		,"logs","objects","refs"
-		,"refs"</>"heads","refs"</>"tags"]
-	return $ and ([dir] ++ subDirs)
+        commit <- getCommit git commitRef
+        case commit of
+                Just (Commit tree _ _ _ _) -> resolve tree paths
+                Nothing                    -> error ("not a valid commit ref: " ++ show commitRef)
+        where
+                resolve :: Ref -> [ByteString] -> IO (Maybe Ref)
+                resolve treeRef []     = return $ Just treeRef
+                resolve treeRef (x:xs) = do
+                        tree <- getTree git treeRef
+                        case tree of
+                                Just (Tree ents) -> do
+                                        let cEnt = treeEntRef <$> findEnt x ents
+                                        if xs == []
+                                                then return cEnt
+                                                else maybe (return Nothing) (\z -> resolve z xs) cEnt
+                                Nothing          -> error ("not a valid tree ref: " ++ show treeRef)
 
--- | initialize a new repository at a specific location.
-initRepo :: FilePath -> IO ()
-initRepo path = do
-	exists <- doesDirectoryExist path
-	when exists $ error "destination directory already exists"
-	createDirectory path
-	mapM_ (createDirectory . (path </>))
-		["branches","hooks","info"
-		,"logs","objects","refs"
-		,"refs"</>"heads","refs"</>"tags"]
+                findEnt x = find (\(_, b, _) -> b == x)
+                treeEntRef (_,_,r) = r
diff --git a/Data/Git/Revision.hs b/Data/Git/Revision.hs
--- a/Data/Git/Revision.hs
+++ b/Data/Git/Revision.hs
@@ -1,51 +1,55 @@
 -- |
--- Module      : Data.Git.Delta
+-- Module      : Data.Git.Revision
 -- License     : BSD-style
 -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
 -- Stability   : experimental
 -- Portability : unix
 --
 module Data.Git.Revision
-	( Revision(..)
-	, RevModifier(..)
-	, revFromString
-	) where
+        ( Revision(..)
+        , RevModifier(..)
+        , fromString
+        ) where
 
 import Text.Parsec
+import Data.String
 
 data RevModifier =
-	  RevModParent Int       -- ^ parent accessor ^<n> and ^
-	| RevModParentFirstN Int -- ^ parent accessor ~<n>
-	| RevModAtType String    -- ^ @{type} accessor
-	| RevModAtDate String    -- ^ @{date} accessor
-	| RevModAtN Int          -- ^ @{n} accessor
-	deriving (Eq)
+          RevModParent Int       -- ^ parent accessor ^<n> and ^
+        | RevModParentFirstN Int -- ^ parent accessor ~<n>
+        | RevModAtType String    -- ^ @{type} accessor
+        | RevModAtDate String    -- ^ @{date} accessor
+        | RevModAtN Int          -- ^ @{n} accessor
+        deriving (Eq)
 
 data Revision = Revision String [RevModifier]
-	deriving (Eq)
+        deriving (Eq)
 
+instance IsString Revision where
+    fromString = revFromString
+
 revFromString s = either (error.show) id $ parse parser "" s where
-	parser = do
-		p    <- many (noneOf "^~@")
-		mods <- many (choice [parseParent, parseFirstParent, parseAt])
-		return $ Revision p mods
-	parseParent = try $ do
-		_ <- char '^'
-		n <- optionMaybe (many1 digit)
-		case n of
-			Nothing -> return $ RevModParent 1
-			Just d  -> return $ RevModParent (read d)
-	parseFirstParent = try $
-		char '~' >> many1 digit >>= return . RevModParentFirstN . read
-	parseAt = try $ do
-		_  <- char '@' >> char '{'
-		at <- choice [ parseAtType, parseAtDate, parseAtN ]
-		_  <- char '}'
-		return at
-	parseAtType = try $ do
-		ty <- choice $ map string ["tree","commit","blob","tag"]
-		return $ RevModAtType ty
-	parseAtN = try $ do
-		many1 digit >>= return . RevModAtN . read
-	parseAtDate = try $ do
-		many (noneOf "}") >>= return . RevModAtDate
+        parser = do
+                p    <- many (noneOf "^~@")
+                mods <- many (choice [parseParent, parseFirstParent, parseAt])
+                return $ Revision p mods
+        parseParent = try $ do
+                _ <- char '^'
+                n <- optionMaybe (many1 digit)
+                case n of
+                        Nothing -> return $ RevModParent 1
+                        Just d  -> return $ RevModParent (read d)
+        parseFirstParent = try $
+                char '~' >> many1 digit >>= return . RevModParentFirstN . read
+        parseAt = try $ do
+                _  <- char '@' >> char '{'
+                at <- choice [ parseAtType, parseAtDate, parseAtN ]
+                _  <- char '}'
+                return at
+        parseAtType = try $ do
+                ty <- choice $ map string ["tree","commit","blob","tag"]
+                return $ RevModAtType ty
+        parseAtN = try $ do
+                many1 digit >>= return . RevModAtN . read
+        parseAtDate = try $ do
+                many (noneOf "}") >>= return . RevModAtDate
diff --git a/Data/Git/Storage.hs b/Data/Git/Storage.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage.hs
@@ -0,0 +1,277 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+-- |
+-- Module      : Data.Git.Storage
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+
+module Data.Git.Storage
+    ( Git
+    , gitRepoPath
+    , openRepo
+    , closeRepo
+    , withRepo
+    , withCurrentRepo
+    , findRepo
+    , isRepo
+    , initRepo
+    , iterateIndexes
+    , findReference
+    , findReferencesWithPrefix
+    -- * getting objects
+    , getObjectRaw
+    , getObjectRawAt
+    , getObject
+    , getObjectAt
+    , getObjectType
+    -- * setting objects
+    , setObject
+    ) where
+
+import System.Directory
+import System.FilePath
+import System.Environment
+
+import Control.Applicative ((<$>))
+import Control.Exception
+import qualified Control.Exception as E
+import Control.Monad
+
+import Data.List ((\\), isPrefixOf)
+import Data.IORef
+import Data.Word
+
+import Data.Git.Delta
+import Data.Git.Storage.FileReader
+import Data.Git.Storage.PackIndex
+import Data.Git.Storage.Object
+import Data.Git.Storage.Pack
+import Data.Git.Storage.Loose
+import Data.Git.Ref
+
+data PackIndexReader = PackIndexReader PackIndexHeader FileReader
+
+-- | represent an git repo, with possibly already opened filereaders
+-- for indexes and packs
+data Git = Git
+        { gitRepoPath  :: FilePath
+        , indexReaders :: IORef [(Ref, PackIndexReader)]
+        , packReaders  :: IORef [(Ref, FileReader)]
+        }
+
+-- | open a new git repository context
+openRepo :: FilePath -> IO Git
+openRepo path = liftM2 (Git path) (newIORef []) (newIORef [])
+
+-- | close a git repository context, closing all remaining fileReaders.
+closeRepo :: Git -> IO ()
+closeRepo (Git { indexReaders = ireaders, packReaders = preaders }) = do
+        mapM_ (closeIndexReader . snd) =<< readIORef ireaders
+        mapM_ (fileReaderClose . snd) =<< readIORef preaders
+        where closeIndexReader (PackIndexReader _ fr) = fileReaderClose fr
+
+-- | Find the git repository from the current directory.
+--
+-- If the environment variable GIT_DIR is set then it's used,
+-- otherwise iterate from current directory, up to 128 parents for a .git directory
+findRepo :: IO FilePath
+findRepo = do
+        menvDir <- E.catch (Just <$> getEnv "GIT_DIR") (\(_:: SomeException) -> return Nothing)
+        case menvDir of
+                Nothing     -> checkDir 0
+                Just envDir -> do
+                        e <- isRepo envDir
+                        when (not e) $ error "environment GIT_DIR is not a git repository" 
+                        return envDir
+        where
+                checkDir 128 = error "not a git repository"
+                checkDir n   = do
+                        let filepath = concat (replicate n ("../") ++ [".git"])
+                        e <- isRepo filepath
+                        if e then return filepath else checkDir (n+1)
+
+-- | execute a function f with a git context.
+withRepo path f = bracket (openRepo path) closeRepo f
+
+-- | execute a function on the current repository.
+--
+-- check findRepo to see how the git repository is found.
+withCurrentRepo :: (Git -> IO a) -> IO a
+withCurrentRepo f = findRepo >>= \path -> withRepo path f
+
+-- | basic checks to see if a specific path looks like a git repo.
+isRepo :: FilePath -> IO Bool
+isRepo path = do
+        dir     <- doesDirectoryExist path
+        subDirs <- mapM (doesDirectoryExist . (path </>))
+                ["branches","hooks","info"
+                ,"logs","objects","refs"
+                ,"refs"</>"heads","refs"</>"tags"]
+        return $ and ([dir] ++ subDirs)
+
+-- | initialize a new repository at a specific location.
+initRepo :: FilePath -> IO ()
+initRepo path = do
+        exists <- doesDirectoryExist path
+        when exists $ error "destination directory already exists"
+        createDirectory path
+        mapM_ (createDirectory . (path </>))
+                ["branches","hooks","info"
+                ,"logs","objects","refs"
+                ,"refs"</>"heads","refs"</>"tags"]
+
+iterateIndexes git f initAcc = do
+        allIndexes    <- packIndexEnumerate (gitRepoPath git)
+        readers       <- readIORef (indexReaders git)
+        (a,terminate) <- loop initAcc readers
+        if terminate
+                then return a
+                else readRemainingIndexes a (allIndexes \\ map fst readers)
+        where
+                loop acc []     = return (acc, False)
+                loop acc (r:rs) = do
+                        (nacc, terminate) <- f acc r
+                        if terminate
+                                then return (nacc,True)
+                                else loop nacc rs
+
+                readRemainingIndexes acc []            = return acc
+                readRemainingIndexes acc (idxref:idxs) = do
+                        fr <- packIndexOpen (gitRepoPath git) idxref
+                        idx <- packIndexReadHeader fr
+                        let idxreader = PackIndexReader idx fr
+                        let r = (idxref, idxreader)
+                        modifyIORef (indexReaders git) (\l -> r : l)
+                        (nacc, terminate) <- f acc r
+                        if terminate
+                                then return nacc
+                                else readRemainingIndexes nacc idxs
+
+-- | Get the object location of a specific reference
+findReference :: Git -> Ref -> IO ObjectLocation
+findReference git ref = maybe NotFound id <$> (findLoose `mplusIO` findInIndexes)
+        where
+                findLoose :: IO (Maybe ObjectLocation)
+                findLoose = do
+                        isLoose <- looseExists (gitRepoPath git) ref
+                        if isLoose then return (Just $ Loose ref) else return Nothing
+
+                findInIndexes :: IO (Maybe ObjectLocation)
+                findInIndexes = iterateIndexes git isinIndex Nothing --f -> (a -> IndexReader -> IO (a,Bool)) -> a -> IO a
+
+                isinIndex acc (idxref, (PackIndexReader idxhdr indexreader)) = do
+                        mloc <- packIndexGetReferenceLocation idxhdr indexreader ref
+                        case mloc of
+                                Nothing  -> return (acc, False)
+                                Just loc -> return (Just $ Packed idxref loc, True)
+
+                mplusIO :: IO (Maybe a) -> IO (Maybe a) -> IO (Maybe a)
+                mplusIO f g = f >>= \vopt -> case vopt of
+                        Nothing -> g
+                        Just v  -> return $ Just v
+
+-- | get all the references that start by a specific prefix
+findReferencesWithPrefix :: Git -> String -> IO [Ref]
+findReferencesWithPrefix git pre
+        | invalidLength         = error "not a valid prefix"
+        | not (isHexString pre) = error "reference prefix contains non hexchar"
+        | otherwise             = do
+                looseRefs  <- looseEnumerateWithPrefixFilter (gitRepoPath git) (take 2 pre) matchRef
+                packedRefs <- concat <$> iterateIndexes git idxPrefixMatch []
+                return (looseRefs ++ packedRefs)
+        where
+                -- not very efficient way to do that... will do for now.
+                matchRef ref = pre `isPrefixOf` toHexString ref
+                invalidLength = length pre < 2 || length pre > 39 
+
+                idxPrefixMatch acc (_, (PackIndexReader idxhdr indexreader)) = do
+                        refs <- packIndexGetReferencesWithPrefix idxhdr indexreader pre
+                        return (refs:acc,False)
+
+readRawFromPack :: Git -> Ref -> Word64 -> IO (FileReader, PackedObjectRaw)
+readRawFromPack git pref offset = do
+        readers <- readIORef (packReaders git)
+        reader  <- maybe getDefault return $ lookup pref readers
+        po <- packReadRawAtOffset reader offset
+        return (reader, po)
+    where getDefault = do p <- packOpen (gitRepoPath git) pref
+                          modifyIORef (packReaders git) ((pref, p):)
+                          return p
+
+readFromPack :: Git -> Ref -> Word64 -> Bool -> IO (Maybe ObjectInfo)
+readFromPack git pref o resolveDelta = do
+        (reader, x) <- readRawFromPack git pref o
+        if resolveDelta then resolve reader o x else return $ Just $ generifyHeader x
+        where
+                generifyHeader :: PackedObjectRaw -> ObjectInfo
+                generifyHeader (po, objData) = ObjectInfo { oiHeader = hdr, oiData = objData, oiChains = [] }
+                        where hdr = (poiType po, poiActualSize po, poiExtra po)
+
+                resolve :: FileReader -> Word64 -> PackedObjectRaw -> IO (Maybe ObjectInfo)
+                resolve reader offset (po, objData) = do
+                        case (poiType po, poiExtra po) of
+                                (TypeDeltaOff, Just ptr@(PtrOfs doff)) -> do
+                                        let delta = deltaRead objData
+                                        let noffset = offset - doff
+                                        base <- resolve reader noffset =<< packReadRawAtOffset reader noffset
+                                        return $ addToChain ptr $ applyDelta delta base
+                                (TypeDeltaRef, Just ptr@(PtrRef bref)) -> do
+                                        let delta = deltaRead objData
+                                        base <- getObjectRaw git bref True
+                                        return $ addToChain ptr $ applyDelta delta base
+                                _                                    -> return $ Just $ generifyHeader (po, objData)
+
+                addToChain ptr (Just oi) = Just (oi { oiChains = ptr : oiChains oi })
+                addToChain _   Nothing   = Nothing
+
+                applyDelta :: Maybe Delta -> Maybe ObjectInfo -> Maybe ObjectInfo
+                applyDelta (Just delta@(Delta _ rSize _)) (Just objInfo) = Just $ objInfo
+                        { oiHeader = (\(a,_,c) -> (a,rSize,c)) $ oiHeader objInfo
+                        , oiData   = deltaApply (oiData objInfo) delta
+                        }
+                applyDelta _ _                                      = Nothing
+
+-- | get an object from repository
+getObjectRawAt :: Git -> ObjectLocation -> Bool -> IO (Maybe ObjectInfo)
+getObjectRawAt _   NotFound    _ = return Nothing
+getObjectRawAt git (Loose ref) _ = Just . (\(h,d)-> ObjectInfo h d[]) <$> looseReadRaw (gitRepoPath git) ref
+getObjectRawAt git (Packed pref o) resolveDelta = readFromPack git pref o resolveDelta
+
+-- | get an object from repository
+getObjectRaw :: Git -> Ref -> Bool -> IO (Maybe ObjectInfo)
+getObjectRaw git ref resolveDelta = do
+        loc <- findReference git ref
+        getObjectRawAt git loc resolveDelta
+
+-- | get an object type from repository
+getObjectType :: Git -> Ref -> IO (Maybe ObjectType)
+getObjectType git ref = findReference git ref >>= getObjectTypeAt
+        where
+                getObjectTypeAt NotFound        = return Nothing
+                getObjectTypeAt (Loose _)       = Just . (\(t,_,_) -> t) <$> looseReadHeader (gitRepoPath git) ref
+                getObjectTypeAt (Packed pref o) =
+                        fmap ((\(ty,_,_) -> ty) . oiHeader) <$> readFromPack git pref o True
+
+-- | get an object from repository using a location to reference it.
+getObjectAt :: Git -> ObjectLocation -> Bool -> IO (Maybe Object)
+getObjectAt git loc resolveDelta = maybe Nothing toObject <$> getObjectRawAt git loc resolveDelta
+        where
+                toObject (ObjectInfo { oiHeader = (ty, _, extra), oiData = objData }) = packObjectFromRaw (ty, extra, objData)
+
+-- | get an object from repository using a ref.
+getObject :: Git               -- ^ repository
+          -> Ref               -- ^ the object's reference to
+          -> Bool              -- ^ whether to resolve deltas if found
+          -> IO (Maybe Object) -- ^ returned object if found
+getObject git ref resolveDelta = maybe Nothing toObject <$> getObjectRaw git ref resolveDelta
+        where
+                toObject (ObjectInfo { oiHeader = (ty, _, extra), oiData = objData }) = packObjectFromRaw (ty, extra, objData)
+
+-- | set an object in the store and returns the new ref
+-- this is always going to create a loose object.
+setObject :: Git
+          -> Object
+          -> IO Ref
+setObject git obj = looseWrite (gitRepoPath git) obj
diff --git a/Data/Git/Storage/FileReader.hs b/Data/Git/Storage/FileReader.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage/FileReader.hs
@@ -0,0 +1,193 @@
+-- |
+-- Module      : Data.Git.Storage.FileReader
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Data.Git.Storage.FileReader
+        ( FileReader
+        , fileReaderNew
+        , fileReaderClose
+        , withFileReader
+        , withFileReaderDecompress
+        , fileReaderGetPos
+        , fileReaderGet
+        , fileReaderGetLBS
+        , fileReaderGetBS
+        , fileReaderGetVLF
+        , fileReaderSeek
+        , fileReaderParse
+
+        , fileReaderInflateToSize
+        ) where
+
+
+import Control.Applicative ((<$>))
+import Control.Exception (bracket, throwIO)
+import Control.Monad
+
+import Data.Attoparsec (parseWith, Parser, IResult(..))
+import qualified Data.Attoparsec as A
+import Data.Bits
+import Data.ByteString (ByteString)
+import Data.ByteString.Unsafe
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import Data.ByteString.Lazy.Internal (defaultChunkSize)
+import Data.IORef
+
+import Data.Word
+
+import Codec.Zlib
+import Codec.Zlib.Lowlevel
+import Foreign.ForeignPtr
+
+import System.IO
+
+data FileReader = FileReader
+        { fbHandle     :: Handle
+        , fbUseInflate :: Bool
+        , fbInflate    :: Inflate
+        , fbRemaining  :: IORef ByteString
+        , fbPos        :: IORef Word64
+        }
+
+fileReaderNew :: Bool -> Handle -> IO FileReader
+fileReaderNew decompress handle = do
+        ref <- newIORef B.empty
+        pos <- newIORef 0
+        inflate <- initInflate defaultWindowBits
+        return $ FileReader handle decompress inflate ref pos
+
+fileReaderClose :: FileReader -> IO ()
+fileReaderClose = hClose . fbHandle
+
+withFileReader :: FilePath -> (FileReader -> IO a) -> IO a
+withFileReader path f =
+        bracket (openFile path ReadMode) (hClose) $ \handle ->
+                bracket (fileReaderNew False handle) (\_ -> return ()) f
+
+withFileReaderDecompress :: FilePath -> (FileReader -> IO a) -> IO a
+withFileReaderDecompress path f =
+        bracket (openFile path ReadMode) (hClose) $ \handle ->
+                bracket (fileReaderNew True handle) (\_ -> return ()) f
+
+fileReaderGetNext :: FileReader -> IO ByteString
+fileReaderGetNext fb = do
+        bs <- if fbUseInflate fb then inflateTillPop else B.hGet (fbHandle fb) 8192
+        modifyIORef (fbPos fb) (\pos -> pos + (fromIntegral $ B.length bs))
+        return bs
+        where inflateTillPop = do
+                b <- B.hGet (fbHandle fb) 4096
+                if B.null b
+                        then finishInflate (fbInflate fb)
+                        else (>>= maybe inflateTillPop return) =<< feedInflate (fbInflate fb) b
+
+fileReaderGetPos :: FileReader -> IO Word64
+fileReaderGetPos fr = do
+        storeLeft <- B.length <$> readIORef (fbRemaining fr)
+        pos       <- readIORef (fbPos fr)
+        return (pos - fromIntegral storeLeft)
+
+fileReaderFill :: FileReader -> IO ()
+fileReaderFill fb = fileReaderGetNext fb >>= writeIORef (fbRemaining fb)
+
+fileReaderGet :: Int -> FileReader -> IO [ByteString]
+fileReaderGet size fb@(FileReader { fbRemaining = ref }) = loop size
+        where loop left = do
+                b <- readIORef ref
+                if B.length b >= left
+                        then do
+                                let (b1, b2) = B.splitAt left b
+                                writeIORef ref b2
+                                return [b1]
+                        else do
+                                let nleft = left - B.length b
+                                fileReaderFill fb
+                                liftM (b :) (loop nleft)
+
+fileReaderGetLBS :: Int -> FileReader -> IO L.ByteString
+fileReaderGetLBS size fb = L.fromChunks <$> fileReaderGet size fb
+
+fileReaderGetBS :: Int -> FileReader -> IO ByteString
+fileReaderGetBS size fb = B.concat <$> fileReaderGet size fb
+
+-- | seek in a handle, and reset the remaining buffer to empty.
+fileReaderSeek :: FileReader -> Word64 -> IO ()
+fileReaderSeek (FileReader { fbHandle = handle, fbRemaining = ref, fbPos = pos }) absPos = do
+        writeIORef ref B.empty >> writeIORef pos absPos >> hSeek handle AbsoluteSeek (fromIntegral absPos)
+
+-- | parse from a filebuffer
+fileReaderParse :: FileReader -> Parser a -> IO a
+fileReaderParse fr@(FileReader { fbRemaining = ref }) parseF = do
+        initBS <- readIORef ref
+        result <- parseWith (fileReaderGetNext fr) parseF initBS
+        case result of
+                Done remaining a -> writeIORef ref remaining >> return a
+                Partial _        -> error "parsing failed: partial with a handle, reached EOF ?"
+                Fail _ ctxs err  -> error ("parsing failed: " ++ show ctxs ++ " : " ++ show err)
+
+-- | get a Variable Length Field. get byte as long as MSB is set, and one byte after
+fileReaderGetVLF :: FileReader -> IO [Word8]
+fileReaderGetVLF fr = fileReaderParse fr $ do
+        bs <- A.takeWhile (\w -> w `testBit` 7)
+        l  <- A.anyWord8
+        return $ (map (\w -> w `clearBit` 7) $ B.unpack bs) ++ [l]
+
+fileReaderInflateToSize :: FileReader -> Word64 -> IO L.ByteString
+fileReaderInflateToSize fb@(FileReader { fbRemaining = ref }) outputSize = do
+        --pos <- fileReaderGetPos fb
+        --putStrLn ("inflate to size " ++ show outputSize ++ " " ++ show pos)
+        inflate <- inflateNew
+        l       <- loop inflate outputSize
+        --posend <- fileReaderGetPos fb
+        --putStrLn ("inflated input " ++ show posend)
+        return $ L.fromChunks l
+        where loop inflate left = do
+                rbs <- readIORef ref
+                let maxToInflate = min left (16 * 1024)
+                let lastBlock = if left == maxToInflate then True else False
+                (dbs,remaining) <- inflateToSize inflate (fromIntegral maxToInflate) lastBlock rbs (fileReaderGetNext fb)
+                writeIORef ref remaining
+                let nleft = left - fromIntegral (B.length dbs)
+                if nleft > 0
+                        then liftM (dbs:) (loop inflate nleft)
+                        else return [dbs]
+
+-- lowlevel helpers to inflate only to a specific size.
+
+inflateNew = do
+        zstr <- zstreamNew
+        inflateInit2 zstr defaultWindowBits
+        newForeignPtr c_free_z_stream_inflate zstr
+
+inflateToSize inflate sz isLastBlock ibs nextBs = withForeignPtr inflate $ \zstr -> do
+        let boundSz = min defaultChunkSize sz
+        -- create an output buffer
+        fbuff <- mallocForeignPtrBytes boundSz
+        withForeignPtr fbuff $ \buff -> do
+                c_set_avail_out zstr buff (fromIntegral boundSz)
+                rbs <- loop zstr ibs
+                bs  <- B.packCStringLen (buff, boundSz)
+                return (bs, rbs)
+        where
+                loop zstr nbs = do
+                        (ai, streamEnd) <- inflateOneInput zstr nbs
+                        ao <- c_get_avail_out zstr
+                        if (isLastBlock && streamEnd) || (not isLastBlock && ao == 0)
+                                then return $ bsTakeLast ai nbs
+                                else do
+                                        --when (ai /= 0) $ error ("input not consumed completly: ai" ++ show ai)
+                                        (if ai == 0
+                                                then nextBs
+                                                else return (bsTakeLast ai nbs)) >>= loop zstr
+
+                inflateOneInput zstr bs = unsafeUseAsCStringLen bs $ \(istr, ilen) -> do
+                        c_set_avail_in zstr istr $ fromIntegral ilen
+                        r <- c_call_inflate_noflush zstr
+                        when (r < 0 && r /= (-5)) $ do
+                                throwIO $ ZlibException $ fromIntegral r
+                        ai <- c_get_avail_in zstr
+                        return (ai, r == 1)
+                bsTakeLast len bs = B.drop (B.length bs - fromIntegral len) bs
diff --git a/Data/Git/Storage/FileWriter.hs b/Data/Git/Storage/FileWriter.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage/FileWriter.hs
@@ -0,0 +1,50 @@
+-- |
+-- Module      : Data.Git.Storage.FileWriter
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Data.Git.Storage.FileWriter where
+
+import Data.Git.Ref
+import Data.IORef
+import qualified Data.ByteString as B
+import Codec.Zlib
+import Control.Exception (bracket)
+
+import qualified Crypto.Hash.SHA1 as SHA1
+
+import System.IO
+
+defaultCompression = 6
+
+data FileWriter = FileWriter
+        { writerHandle  :: Handle
+        , writerDeflate :: Deflate
+        , writerDigest  :: IORef SHA1.Ctx
+        }
+
+fileWriterNew handle = do
+        deflate <- initDeflate defaultCompression defaultWindowBits
+        digest  <- newIORef SHA1.init
+        return $ FileWriter
+                { writerHandle  = handle
+                , writerDeflate = deflate
+                , writerDigest  = digest
+                }
+
+withFileWriter path f =
+        bracket (openFile path WriteMode) (hClose) $ \handle ->
+                bracket (fileWriterNew handle) (fileWriterClose) f
+
+postDeflate handle = maybe (return ()) (B.hPut handle)
+
+fileWriterOutput (FileWriter { writerHandle = handle, writerDigest = digest, writerDeflate = deflate }) bs = do
+        modifyIORef digest (\ctx -> SHA1.update ctx bs)
+        (>>= postDeflate handle) =<< feedDeflate deflate bs
+
+fileWriterClose (FileWriter { writerHandle = handle, writerDeflate = deflate }) =
+        postDeflate handle =<< finishDeflate deflate
+
+fileWriterGetDigest (FileWriter { writerDigest = digest }) = (fromBinary . SHA1.finalize) `fmap` readIORef digest
diff --git a/Data/Git/Storage/Loose.hs b/Data/Git/Storage/Loose.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage/Loose.hs
@@ -0,0 +1,158 @@
+-- |
+-- Module      : Data.Git.Storage.Loose
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
+module Data.Git.Storage.Loose
+        (
+        -- * marshall from and to lazy bytestring
+          looseUnmarshall
+        , looseUnmarshallRaw
+        , looseMarshall
+        -- * read and check object existence
+        , looseRead
+        , looseReadHeader
+        , looseReadRaw
+        , looseExists
+        -- * write objects
+        , looseWriteBlobFromFile
+        , looseWrite
+        -- * enumeration of loose objects
+        , looseEnumeratePrefixes
+        , looseEnumerateWithPrefixFilter
+        , looseEnumerateWithPrefix
+        ) where
+
+import Codec.Compression.Zlib
+import Data.Git.Ref
+import Data.Git.Path
+import Data.Git.Storage.FileWriter
+import Data.Git.Storage.Object
+
+import System.FilePath
+import System.Directory
+import System.IO (openFile, hFileSize, IOMode(..))
+
+import qualified Data.ByteString.Char8 as BC
+import qualified Data.ByteString.Lazy as L
+
+import Data.Attoparsec.Lazy
+import qualified Data.Attoparsec.Char8 as PC
+import Control.Applicative ((<$>), (<|>))
+import Control.Monad
+import Control.Exception (onException, SomeException)
+import qualified Control.Exception as E
+
+import Data.Char (isHexDigit)
+
+isObjectPrefix [a,b] = isHexDigit a && isHexDigit b
+isObjectPrefix _     = False
+
+decimal :: Parser Int
+decimal = PC.decimal
+
+-- loose object parsing
+parseHeader = do
+        h <- takeWhile1 ((/=) 0x20)
+        _ <- word8 0x20
+        sz <- decimal
+        return (objectTypeUnmarshall $ BC.unpack h, fromIntegral sz, Nothing)
+
+parseTreeHeader   = string "tree " >> decimal >> word8 0
+parseTagHeader    = string "tag " >> decimal >> word8 0
+parseCommitHeader = string "commit " >> decimal >> word8 0
+parseBlobHeader   = string "blob " >> decimal >> word8 0
+
+parseTree   = parseTreeHeader >> objectParseTree
+parseTag    = parseTagHeader >> objectParseTag
+parseCommit = parseCommitHeader >> objectParseCommit
+parseBlob   = parseBlobHeader >> objectParseBlob
+
+parseObject :: L.ByteString -> Object
+parseObject = parseSuccess (parseTree <|> parseBlob <|> parseCommit <|> parseTag)
+        where parseSuccess p = either error id . eitherResult . parse p
+
+-- | unmarshall an object (with header) from a lazy bytestring.
+looseUnmarshall :: L.ByteString -> Object
+looseUnmarshall = parseObject . decompress
+
+-- | unmarshall an object as (header, data) tuple from a lazy bytestring.
+looseUnmarshallRaw :: L.ByteString -> (ObjectHeader, ObjectData)
+looseUnmarshallRaw l =
+        let dl = decompress l in
+        let i = L.findIndex ((==) 0) dl in
+        case i of
+                Nothing  -> error "object not right format. missing 0"
+                Just idx ->
+                        let (h, r) = L.splitAt (idx+1) dl in
+                        case maybeResult $ parse parseHeader h of
+                                Nothing  -> error "cannot open object"
+                                Just hdr -> (hdr, r)
+
+-- | read a specific ref from a loose object and returns an header and data.
+looseReadRaw repoPath ref = looseUnmarshallRaw <$> L.readFile (objectPathOfRef repoPath ref)
+
+-- | read only the header of a loose object.
+looseReadHeader repoPath ref = toHeader <$> L.readFile (objectPathOfRef repoPath ref)
+        where toHeader = either error id . eitherResult . parse parseHeader . decompress
+
+-- | read a specific ref from a loose object and returns an object
+looseRead repoPath ref = looseUnmarshall <$> L.readFile (objectPathOfRef repoPath ref)
+
+-- | check if a specific ref exists as loose object
+looseExists repoPath ref = doesFileExist (objectPathOfRef repoPath ref)
+
+-- | enumarate all prefixes available in the object store.
+looseEnumeratePrefixes repoPath = filter isObjectPrefix <$> getDirectoryContents (repoPath </> "objects")
+
+-- | enumerate all references available with a specific prefix.
+looseEnumerateWithPrefixFilter :: FilePath -> String -> (Ref -> Bool) -> IO [Ref]
+looseEnumerateWithPrefixFilter repoPath prefix filterF =
+        filter filterF . map (fromHexString . (prefix ++)) . filter isRef <$> getDir (repoPath </> "objects" </> prefix)
+        where
+                getDir p = E.catch (getDirectoryContents p) (\(_::SomeException) -> return [])
+                isRef l = length l == 38
+
+looseEnumerateWithPrefix :: FilePath -> String -> IO [Ref]
+looseEnumerateWithPrefix repoPath prefix =
+        looseEnumerateWithPrefixFilter repoPath prefix (const True)
+
+-- | marshall as lazy bytestring an object except deltas.
+looseMarshall obj
+        | objectIsDelta obj = error "cannot write delta object loose"
+        | otherwise         = L.concat [ L.fromChunks [hdrB], objData ]
+        where
+                objData = objectWrite obj
+                hdrB    = objectWriteHeader (objectToType obj) (fromIntegral $ L.length objData)
+
+-- | create a new blob on a temporary location and on success move it to
+-- the object store with its digest name.
+looseWriteBlobFromFile repoPath file = do
+        fsz <- openFile file ReadMode >>= hFileSize
+        let hdr = objectWriteHeader TypeBlob (fromIntegral fsz)
+        tmpPath <- objectTemporaryPath repoPath
+        flip onException (removeFile tmpPath) $ do
+                (ref, npath) <- withFileWriter tmpPath $ \fw -> do
+                        fileWriterOutput fw hdr
+                        chunks <- L.toChunks <$> L.readFile file
+                        mapM_ (fileWriterOutput fw) chunks
+                        digest <- fileWriterGetDigest fw
+                        return (digest, objectPathOfRef repoPath digest)
+                exists <- doesFileExist npath
+                when exists $ error "destination already exists"
+                renameFile tmpPath npath
+                return ref
+
+-- | write an object to disk as a loose reference.
+-- use looseWriteBlobFromFile for efficiently writing blobs when being commited from a file.
+looseWrite repoPath obj = createDirectoryIfMissing True (takeDirectory path)
+                       >> doesFileExist path
+                       >>= \exists -> unless exists (L.writeFile path $ compress content)
+                       >> return ref
+        where
+                path    = objectPathOfRef repoPath ref
+                content = looseMarshall obj
+                ref     = hashLBS content
diff --git a/Data/Git/Storage/Object.hs b/Data/Git/Storage/Object.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage/Object.hs
@@ -0,0 +1,287 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE OverloadedStrings #-}
+-- |
+-- Module      : Data.Git.Storage.Object
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Data.Git.Storage.Object
+        ( ObjectLocation(..)
+        , ObjectType(..)
+        , ObjectHeader
+        , ObjectData
+        , ObjectPtr(..)
+        , Object(..)
+        , ObjectInfo(..)
+        , objectWrap
+        , objectToType
+        , objectTypeMarshall
+        , objectTypeUnmarshall
+        , objectTypeIsDelta
+        , objectIsDelta
+        , objectToTree
+        , objectToCommit
+        , objectToTag
+        , objectToBlob
+        -- * parsing function
+        , treeParse
+        , commitParse
+        , tagParse
+        , blobParse
+        , objectParseTree
+        , objectParseCommit
+        , objectParseTag
+        , objectParseBlob
+        -- * writing function
+        , objectWriteHeader
+        , objectWrite
+        , objectHash
+        ) where
+
+import Data.Git.Ref
+import Data.Git.Types
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+import qualified Data.ByteString.Lazy as L
+
+import Data.Attoparsec.Lazy
+import qualified Data.Attoparsec.Lazy as P
+import qualified Data.Attoparsec.Char8 as PC
+import Control.Applicative ((<$>), many)
+import Control.Monad
+
+import Data.Word
+import Text.Printf
+
+import Data.Time.LocalTime
+import Data.Time.Clock.POSIX
+
+{-
+import Blaze.ByteString.Builder
+import Blaze.ByteString.Builder.ByteString
+-}
+
+-- | location of an object in the database
+data ObjectLocation = NotFound | Loose Ref | Packed Ref Word64
+        deriving (Show,Eq)
+
+-- | Delta objects points to some others objects in the database
+-- either as offset in the pack or as a direct reference.
+data ObjectPtr = PtrRef Ref | PtrOfs Word64 deriving (Show,Eq)
+
+type ObjectHeader = (ObjectType, Word64, Maybe ObjectPtr)
+
+type ObjectData = L.ByteString
+
+-- | Raw objects infos have an header (type, size, ptr),
+-- the data and a pointers chains to parents for resolved objects.
+data ObjectInfo = ObjectInfo
+        { oiHeader :: ObjectHeader
+        , oiData   :: ObjectData
+        , oiChains :: [ObjectPtr]
+        } deriving (Show,Eq)
+
+class Objectable a where
+        getType :: a -> ObjectType
+        getRaw  :: a -> L.ByteString
+        isDelta :: a -> Bool
+
+        toCommit :: a -> Maybe Commit
+        toCommit = const Nothing
+        toTree   :: a -> Maybe Tree
+        toTree = const Nothing
+        toTag    :: a -> Maybe Tag
+        toTag = const Nothing
+        toBlob   :: a -> Maybe Blob
+        toBlob = const Nothing
+
+-- | describe a git object, that could of 6 differents types:
+-- tree, blob, commit, tag and deltas (offset or ref).
+-- the deltas one are only available through packs.
+data Object = forall a . Objectable a => Object a
+
+objectWrap :: Objectable a => a -> Object
+objectWrap a = Object a
+
+objectToType :: Object -> ObjectType
+objectToType (Object a) = getType a
+
+objectTypeMarshall :: ObjectType -> String
+objectTypeMarshall TypeTree   = "tree"
+objectTypeMarshall TypeBlob   = "blob"
+objectTypeMarshall TypeCommit = "commit"
+objectTypeMarshall TypeTag    = "tag"
+objectTypeMarshall _          = error "deltas cannot be marshalled"
+
+objectTypeUnmarshall :: String -> ObjectType
+objectTypeUnmarshall "tree"   = TypeTree
+objectTypeUnmarshall "blob"   = TypeBlob
+objectTypeUnmarshall "commit" = TypeCommit
+objectTypeUnmarshall "tag"    = TypeTag
+objectTypeUnmarshall _        = error "unknown object type"
+
+objectTypeIsDelta :: ObjectType -> Bool
+objectTypeIsDelta TypeDeltaOff = True
+objectTypeIsDelta TypeDeltaRef = True
+objectTypeIsDelta _            = False
+
+objectIsDelta :: Object -> Bool
+objectIsDelta (Object a) = isDelta a
+
+objectToTree :: Object -> Maybe Tree
+objectToTree (Object a) = toTree a
+
+objectToCommit :: Object -> Maybe Commit
+objectToCommit (Object a) = toCommit a
+
+objectToTag :: Object -> Maybe Tag
+objectToTag (Object a) = toTag a
+
+objectToBlob :: Object -> Maybe Blob
+objectToBlob (Object a) = toBlob a
+
+octal :: Parser Int
+octal = B.foldl' step 0 `fmap` takeWhile1 isOct where
+        isOct w = w >= 0x30 && w <= 0x37
+        step a w = a * 8 + fromIntegral (w - 0x30)
+
+skipChar :: Char -> Parser ()
+skipChar c = PC.char c >> return ()
+
+referenceHex = fromHex <$> P.take 40
+referenceBin = fromBinary <$> P.take 20
+
+-- | parse a tree content
+treeParse = (Tree <$> parseEnts) where
+        parseEnts = atEnd >>= \end -> if end then return [] else liftM2 (:) parseEnt parseEnts
+        parseEnt = liftM3 (,,) octal (PC.char ' ' >> takeTill ((==) 0)) (word8 0 >> referenceBin)
+-- | parse a blob content
+blobParse = (Blob <$> takeLazyByteString)
+
+-- | parse a commit content
+commitParse = do
+        tree <- string "tree " >> referenceHex
+        skipChar '\n'
+        parents   <- many parseParentRef
+        author    <- string "author " >> parseName
+        committer <- string "committer " >> parseName
+        skipChar '\n'
+        message <- takeByteString
+        return $ Commit tree parents author committer message
+        where
+                parseParentRef = do
+                        tree <- string "parent " >> referenceHex
+                        skipChar '\n'
+                        return tree
+                
+-- | parse a tag content
+tagParse = do
+        object <- string "object " >> referenceHex
+        skipChar '\n'
+        type_ <- objectTypeUnmarshall . BC.unpack <$> (string "type " >> takeTill ((==) 0x0a))
+        skipChar '\n'
+        tag   <- string "tag " >> takeTill ((==) 0x0a)
+        skipChar '\n'
+        tagger <- string "tagger " >> parseName
+        skipChar '\n'
+        signature <- takeByteString
+        return $ Tag object type_ tag tagger signature
+
+parseName = do
+        name <- B.init <$> PC.takeWhile ((/=) '<')
+        skipChar '<'
+        email <- PC.takeWhile ((/=) '>')
+        _ <- string "> "
+        time <- PC.decimal :: Parser Integer
+        _ <- string " "
+        timezone <- PC.signed PC.decimal
+        let (hours,minutes) = timezone `divMod` 100
+        skipChar '\n'
+        let tz = minutesToTimeZone (hours*60 + (if hours > 0 then minutes else (-minutes)))
+        return (name, email, posixSecondsToUTCTime $ fromIntegral time, tz)
+
+objectParseTree = objectWrap <$> treeParse
+objectParseCommit = objectWrap <$> commitParse
+objectParseTag = objectWrap <$> tagParse
+objectParseBlob = objectWrap <$> blobParse
+
+-- header of loose objects, but also useful for any object to determine object's hash
+objectWriteHeader :: ObjectType -> Word64 -> ByteString
+objectWriteHeader ty sz = BC.pack (objectTypeMarshall ty ++ " " ++ show sz ++ [ '\0' ])
+
+objectWrite :: Object -> L.ByteString
+objectWrite (Object a) = getRaw a
+
+treeWrite (Tree ents) = L.fromChunks $ concat $ map writeTreeEnt ents where
+        writeTreeEnt (perm,name,ref) =
+                [ BC.pack $ printf "%o" perm
+                , BC.singleton ' '
+                , name
+                , B.singleton 0
+                , toBinary ref
+                ]
+
+commitWrite (Commit tree parents author committer msg) =
+        L.fromChunks [BC.unlines ls, B.singleton 0xa, msg]
+        where
+                ls = [ "tree " `BC.append` (toHex tree) ] ++
+                     map (BC.append "parent " . toHex) parents ++
+                     [ writeName "author" author
+                     , writeName "committer" committer ]
+
+tagWrite (Tag ref ty tag tagger signature) =
+        L.fromChunks [BC.unlines ls, B.singleton 0xa, signature]
+        where
+                ls = [ "object " `BC.append` (toHex ref)
+                     , "type " `BC.append` (BC.pack $ objectTypeMarshall ty)
+                     , "tag " `BC.append` tag
+                     , writeName "tagger" tagger ]
+
+blobWrite (Blob bData) = bData
+
+instance Objectable Blob where
+        getType _ = TypeBlob
+        getRaw    = blobWrite
+        isDelta   = const False
+        toBlob t  = Just t
+
+instance Objectable Commit where
+        getType _  = TypeCommit
+        getRaw     = commitWrite
+        isDelta    = const False
+        toCommit t = Just t
+
+instance Objectable Tag where
+        getType _ = TypeTag
+        getRaw    = tagWrite
+        isDelta   = const False
+        toTag t   = Just t
+
+instance Objectable Tree where
+        getType _ = TypeTree
+        getRaw    = treeWrite
+        isDelta   = const False
+        toTree t  = Just t
+
+instance Objectable DeltaOfs where
+        getType _ = TypeDeltaOff
+        getRaw    = error "delta offset cannot be marshalled"
+        isDelta   = const True
+
+instance Objectable DeltaRef where
+        getType _ = TypeDeltaRef
+        getRaw    = error "delta ref cannot be marshalled"
+        isDelta   = const True
+
+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 label (name, email, time, tz) =
+        B.concat [label, " ", name, " <", email, "> ", BC.pack (printf "%d %s" timeSeconds (timeZoneOffsetString tz)) ]
+        where timeSeconds :: Integer
+              timeSeconds = truncate (realToFrac $ utcTimeToPOSIXSeconds time :: Double)
diff --git a/Data/Git/Storage/Pack.hs b/Data/Git/Storage/Pack.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage/Pack.hs
@@ -0,0 +1,153 @@
+-- |
+-- Module      : Data.Git.Storage.Pack
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Data.Git.Storage.Pack
+        ( PackedObjectInfo(..)
+        , PackedObjectRaw
+        -- * Enumerators of packs
+        , packEnumerate
+        -- * Helpers to process packs
+        , packOpen
+        , packClose
+        -- * Command for the content of a pack
+        , packReadHeader
+        , packReadMapAtOffset
+        , packReadAtOffset
+        , packReadRawAtOffset
+        , packEnumerateObjects
+        -- * turn a packed object into a 
+        , packedObjectToObject
+        , packObjectFromRaw
+        ) where
+
+import Control.Applicative ((<$>))
+import Control.Arrow (second)
+import Control.Monad
+
+import System.IO
+import System.FilePath
+import System.Directory
+
+import Data.Bits
+import Data.List
+import qualified Data.ByteString.Lazy as L
+
+import Data.Attoparsec (anyWord8)
+import qualified Data.Attoparsec as A
+import qualified Data.Attoparsec.Lazy as AL
+
+import Data.Git.Internal
+import Data.Git.Path
+import Data.Git.Storage.Object
+import Data.Git.Delta
+import Data.Git.Ref
+import Data.Git.Types
+import Data.Git.Storage.FileReader
+
+import Data.Word
+
+type PackedObjectRaw = (PackedObjectInfo, L.ByteString)
+
+data PackedObjectInfo = PackedObjectInfo
+        { poiType       :: ObjectType
+        , poiOffset     :: Word64
+        , poiSize       :: Word64
+        , poiActualSize :: Word64
+        , poiExtra      :: Maybe ObjectPtr
+        } deriving (Show,Eq)
+
+-- | Enumerate the pack refs available in this repository.
+packEnumerate repoPath = map onlyHash . filter isPackFile <$> getDirectoryContents (repoPath </> "objects" </> "pack")
+        where
+                isPackFile x = ".pack" `isSuffixOf` x
+                onlyHash = fromHexString . takebut 5 . drop 5
+                takebut n l = take (length l - n) l
+
+-- | open a pack
+packOpen :: FilePath -> Ref -> IO FileReader
+packOpen repoPath packRef = openFile (packPath repoPath packRef) ReadMode >>= fileReaderNew False
+
+-- | close a pack
+packClose :: FileReader -> IO ()
+packClose = fileReaderClose
+
+-- | return the number of entries in this pack
+packReadHeader repoPath packRef =
+        withFileReader (packPath repoPath packRef) $ \filereader ->
+                fileReaderParse filereader parseHeader
+        where parseHeader = do
+                packMagic <- be32 <$> A.take 4
+                when (packMagic /= 0x5041434b) $ error "not a git packfile"
+                ver <- be32 <$> A.take 4
+                when (ver /= 2) $ error ("pack file version not supported: " ++ show ver)
+                be32 <$> A.take 4
+
+-- | read an object at a specific position using a map function on the objectData
+packReadMapAtOffset fr offset mapData = fileReaderSeek fr offset >> getNextObject fr mapData
+
+-- | read an object at a specific position
+packReadAtOffset :: FileReader -> Word64 -> IO (Maybe Object)
+packReadAtOffset fr offset = packReadMapAtOffset fr offset id
+
+-- | read a raw representation at a specific position
+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 repoPath packRef entries f =
+        withFileReader (packPath repoPath packRef) $ \filebuffer -> do
+                fileReaderSeek filebuffer 12
+                parseNext filebuffer entries
+        where
+                parseNext :: FileReader -> Int -> IO ()
+                parseNext _  0    = return ()
+                parseNext fr ents = getNextObjectRaw fr >>= f >> parseNext fr (ents-1)
+
+getNextObject :: FileReader -> (L.ByteString -> L.ByteString) -> IO (Maybe Object)
+getNextObject fr mapData =
+        packedObjectToObject . second mapData <$> getNextObjectRaw fr
+
+packedObjectToObject (PackedObjectInfo { poiType = ty, poiExtra = extra }, objData) =
+        packObjectFromRaw (ty, extra, objData)
+
+packObjectFromRaw (TypeCommit, Nothing, objData) = AL.maybeResult $ AL.parse objectParseCommit objData
+packObjectFromRaw (TypeTree, Nothing, objData)   = AL.maybeResult $ AL.parse objectParseTree objData
+packObjectFromRaw (TypeBlob, Nothing, objData)   = AL.maybeResult $ AL.parse objectParseBlob objData
+packObjectFromRaw (TypeTag, Nothing, objData)    = AL.maybeResult $ AL.parse objectParseTag objData
+packObjectFromRaw (TypeDeltaOff, Just (PtrOfs o), objData) = objectWrap . DeltaOfs o <$> deltaRead objData
+packObjectFromRaw (TypeDeltaRef, Just (PtrRef r), objData) = objectWrap . DeltaRef r <$> deltaRead objData
+packObjectFromRaw _                              = error "can't happen unless someone change getNextObjectRaw"
+
+getNextObjectRaw :: FileReader -> IO PackedObjectRaw
+getNextObjectRaw fr = do
+        sobj      <- fileReaderGetPos fr
+        (ty, size) <- fileReaderParse fr parseObjectHeader
+        extra      <- case ty of
+                TypeDeltaRef -> Just . PtrRef . fromBinary <$> fileReaderGetBS 20 fr
+                TypeDeltaOff -> Just . PtrOfs . deltaOffFromList <$> fileReaderGetVLF fr
+                _            -> return Nothing
+        objData    <- fileReaderInflateToSize fr size
+        eobj       <- fileReaderGetPos fr
+
+        return (PackedObjectInfo ty sobj (eobj - sobj) size extra, objData)
+        where
+                parseObjectHeader = do
+                        (m, ty, sz) <- splitFirst <$> anyWord8
+                        size <- if m then (sz +) <$> getNextSize 4 else return sz
+                        return (ty, size)
+                        where
+                                getNextSize n = do
+                                        (c, sz) <- splitOther n <$> anyWord8
+                                        if c then (sz +) <$> getNextSize (n+7) else return sz
+
+                                splitFirst :: Word8 -> (Bool, ObjectType, Word64)
+                                splitFirst w = (w `testBit` 7, toEnum $ fromIntegral ((w `shiftR` 4) .&. 0x7), fromIntegral (w .&. 0xf))
+                                splitOther n w = (w `testBit` 7, fromIntegral (w .&. 0x7f) `shiftL` n)
+
+                deltaOffFromList (x:xs) = foldl' acc (fromIntegral (x `clearBit` 7)) xs
+                        where acc a w = ((a+1) `shiftL` 7) + fromIntegral (w `clearBit` 7)
+                deltaOffFromList [] = error "cannot happen"
diff --git a/Data/Git/Storage/PackIndex.hs b/Data/Git/Storage/PackIndex.hs
new file mode 100644
--- /dev/null
+++ b/Data/Git/Storage/PackIndex.hs
@@ -0,0 +1,182 @@
+-- |
+-- Module      : Data.Git.Storage.PackIndex
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+{-# LANGUAGE OverloadedStrings, BangPatterns #-}
+module Data.Git.Storage.PackIndex
+        ( PackIndexHeader(..)
+        , PackIndex(..)
+
+        -- * handles and enumeration
+        , packIndexOpen
+        , packIndexClose
+        , withPackIndex
+        , packIndexEnumerate
+
+        -- * read from packIndex
+        , packIndexHeaderGetNbWithPrefix
+        , packIndexGetReferenceLocation
+        , packIndexGetReferencesWithPrefix
+        , packIndexReadHeader
+        , packIndexRead
+        , packIndexGetHeader
+        ) where
+
+import Control.Applicative ((<$>))
+import Control.Monad
+
+import System.FilePath
+import System.Directory
+import System.IO
+
+import Data.List
+import Data.Bits
+import Data.Word
+
+import Data.Vector (Vector, (!))
+import qualified Data.Vector as V
+
+import qualified Data.Attoparsec as A
+
+import Data.Git.Internal
+import Data.Git.Storage.FileReader
+import Data.Git.Path
+import Data.Git.Ref
+
+-- | represent an packIndex header with the version and the fanout table
+data PackIndexHeader = PackIndexHeader !Word32 !(Vector Word32)
+        deriving (Show,Eq)
+
+data PackIndex = PackIndex
+        { packIndexSha1s        :: Vector Ref
+        , packIndexCRCs         :: Vector Word32
+        , packIndexPackoffs     :: Vector Word32
+        , packIndexPackChecksum :: Ref
+        , packIndexChecksum     :: Ref
+        }
+
+-- | enumerate every indexes file in the pack directory
+packIndexEnumerate repoPath = map onlyHash . filter isPackFile <$> getDirectoryContents (repoPath </> "objects" </> "pack")
+        where
+                isPackFile x = ".idx" `isSuffixOf` x && "pack-" `isPrefixOf` x
+                onlyHash = fromHexString . takebut 4 . drop 5
+                takebut n l = take (length l - n) l
+
+-- | open an index
+packIndexOpen :: FilePath -> Ref -> IO FileReader
+packIndexOpen repoPath indexRef = openFile (indexPath repoPath indexRef) ReadMode >>= fileReaderNew False
+
+-- | close an index
+packIndexClose :: FileReader -> IO ()
+packIndexClose = fileReaderClose
+
+-- | variant of withFile on the index file and with a FileReader
+withPackIndex repoPath indexRef = withFileReader (indexPath repoPath indexRef)
+
+-- | returns the number of references, referenced in this index.
+packIndexHeaderGetSize :: PackIndexHeader -> Word32
+packIndexHeaderGetSize (PackIndexHeader _ indexes) = indexes ! 255
+
+-- | byte size of an packIndex header.
+packIndexHeaderByteSize :: Int
+packIndexHeaderByteSize = 2*4 {- header -} + 256*4 {- fanout table -}
+
+-- | get the number of reference in this index with a specific prefix
+packIndexHeaderGetNbWithPrefix :: PackIndexHeader -> Int -> Word32
+packIndexHeaderGetNbWithPrefix (PackIndexHeader _ indexes) n
+        | n < 0 || n > 255 = 0
+        | n == 0           = indexes ! 0
+        | otherwise        = (indexes ! n) - (indexes ! (n-1))
+
+-- | fold on refs with a specific prefix
+packIndexHeaderFoldRef :: PackIndexHeader -> FileReader -> Int -> (a -> Word32 -> Ref -> (a, Bool)) -> a -> IO a
+packIndexHeaderFoldRef idxHdr@(PackIndexHeader _ indexes) fr refprefix f initAcc
+        | nb == 0   = return initAcc
+        | otherwise = do
+                let spos = (indexes ! refprefix) - nb
+                fileReaderSeek fr (fromIntegral (sha1Offset + spos * 20))
+                loop nb initAcc
+        where
+                loop 0 acc = return acc
+                loop n acc = do
+                        b <- fromBinary <$> fileReaderGetBS 20 fr
+                        let (!nacc, terminate) = f acc (nb-n) b
+                        if terminate
+                                then return nacc
+                                else loop (n-1) nacc
+                nb         = packIndexHeaderGetNbWithPrefix idxHdr refprefix
+                (sha1Offset,_,_) = packIndexOffsets idxHdr
+
+-- | return the reference offset in the packfile if found
+packIndexGetReferenceLocation :: PackIndexHeader -> FileReader -> Ref -> IO (Maybe Word64)
+packIndexGetReferenceLocation idxHdr@(PackIndexHeader _ indexes) fr ref = do
+        mrpos <- packIndexHeaderFoldRef idxHdr fr refprefix f Nothing
+        case mrpos of
+                Nothing   -> return Nothing
+                Just rpos -> do
+                        let spos = (indexes ! refprefix) - nb
+                        fileReaderSeek fr (fromIntegral (packOffset + 4 * (spos+rpos)))
+                        Just . fromIntegral . be32 <$> fileReaderGetBS 4 fr
+        where
+                f acc rpos rref = if ref == rref then (Just rpos,True) else (acc,False)
+                refprefix  = refPrefix ref
+                nb         = packIndexHeaderGetNbWithPrefix idxHdr refprefix
+                (_,_,packOffset) = packIndexOffsets idxHdr
+
+-- | get all references that start by prefix.
+packIndexGetReferencesWithPrefix :: PackIndexHeader -> FileReader -> String -> IO [Ref]
+packIndexGetReferencesWithPrefix idxHdr fr prefix =
+        packIndexHeaderFoldRef idxHdr fr refprefix f []
+        where
+                f acc _ ref = case cmpPrefix prefix ref of
+                        GT -> (acc    ,False)
+                        EQ -> (ref:acc,False)
+                        LT -> (acc    ,True)
+                refprefix   = read ("0x" ++ take 2 prefix)
+
+-- | returns absolute offset in the index file of the sha1s, the crcs and the packfiles offset.
+packIndexOffsets idx = (packIndexSha1sOffset, packIndexCRCsOffset, packIndexPackOffOffset)
+        where
+                packIndexPackOffOffset = packIndexCRCsOffset + crcsTableSz
+                packIndexCRCsOffset    = packIndexSha1sOffset + sha1TableSz
+                packIndexSha1sOffset   = fromIntegral packIndexHeaderByteSize
+                crcsTableSz        = 4 * sz
+                sha1TableSz        = 20 * sz
+                sz                 = packIndexHeaderGetSize idx
+
+-- | parse index header
+parsePackIndexHeader = do
+        magic   <- be32 <$> A.take 4
+        when (magic /= 0xff744f63) $ error "wrong magic number for packIndex"
+        ver     <- be32 <$> A.take 4
+        when (ver /= 2) $ error "unsupported packIndex version"
+        fanouts <- V.replicateM 256 (be32 <$> A.take 4)
+        return $ PackIndexHeader ver fanouts
+
+-- | read index header from an index filereader
+packIndexReadHeader :: FileReader -> IO PackIndexHeader
+packIndexReadHeader fr = fileReaderSeek fr 0 >> fileReaderParse fr parsePackIndexHeader
+
+-- | get index header from an index reference
+packIndexGetHeader :: FilePath -> Ref -> IO PackIndexHeader
+packIndexGetHeader repoPath indexRef = withPackIndex repoPath indexRef $ packIndexReadHeader
+
+-- | read all index
+packIndexRead repoPath indexRef = do
+        withPackIndex repoPath indexRef $ \fr -> do
+                idx <- fileReaderParse fr parsePackIndexHeader
+                liftM2 (,) (return idx) (fileReaderParse fr (parsePackIndex $ packIndexHeaderGetSize idx))
+        where parsePackIndex sz = do
+                sha1s     <- V.replicateM (fromIntegral sz) (fromBinary <$> A.take 20)
+                crcs      <- V.replicateM (fromIntegral sz) (be32 <$> A.take 4)
+                packoffs  <- V.replicateM (fromIntegral sz) (be32 <$> A.take 4)
+                let nbLarge = length $ filter (== True) $ map (\packoff -> packoff `testBit` 31) $ V.toList packoffs
+                largeoffs <- replicateM nbLarge (A.take 4)
+                packfileChecksum <- fromBinary <$> A.take 20
+                idxfileChecksum  <- fromBinary <$> A.take 20
+                -- large packfile offsets
+                -- trailer
+                return (sha1s, crcs, packoffs, largeoffs, packfileChecksum, idxfileChecksum)
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,108 @@
+-- |
+-- Module      : Data.Git.Object
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Data.Git.Types
+    (
+    -- * Type of types
+      ObjectType(..)
+    -- * Main git types
+    , Tree(..)
+    , Commit(..)
+    , Blob(..)
+    , Tag(..)
+    -- * Pack delta types
+    , DeltaOfs(..)
+    , DeltaRef(..)
+    -- * Basic types part of other bigger types
+    , TreeEnt
+    , Name
+    ) where
+
+import Data.Word
+import Data.Monoid
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Lazy as L
+
+import Data.Git.Ref
+import Data.Git.Delta
+import Data.Time.Clock
+import Data.Time.LocalTime
+
+-- | type of a git object.
+data ObjectType =
+          TypeTree
+        | TypeBlob
+        | TypeCommit
+        | TypeTag
+        | TypeDeltaOff
+        | TypeDeltaRef
+        deriving (Show,Eq)
+
+-- | the enum instance is useful when marshalling to pack file.
+instance Enum ObjectType where
+        fromEnum TypeCommit   = 0x1
+        fromEnum TypeTree     = 0x2
+        fromEnum TypeBlob     = 0x3
+        fromEnum TypeTag      = 0x4
+        fromEnum TypeDeltaOff = 0x6
+        fromEnum TypeDeltaRef = 0x7
+
+        toEnum 0x1 = TypeCommit
+        toEnum 0x2 = TypeTree
+        toEnum 0x3 = TypeBlob
+        toEnum 0x4 = TypeTag
+        toEnum 0x6 = TypeDeltaOff
+        toEnum 0x7 = TypeDeltaRef
+        toEnum n   = error ("not a valid object: " ++ show n)
+
+-- | represent one entry in the tree
+-- (permission,file or directory name,blob or tree ref)
+-- name should maybe a filepath, but not sure about the encoding.
+type TreeEnt = (Int,ByteString,Ref)
+
+-- | an author or committer line
+-- has the format: name <email> time timezone
+-- FIXME: should be a string, but I don't know if the data is stored
+-- consistantly in one encoding (UTF8)
+type Name = (ByteString,ByteString,UTCTime,TimeZone)
+
+-- | Represent a root tree with zero to many tree entries.
+data Tree = Tree { treeGetEnts :: [TreeEnt] } deriving (Show,Eq)
+
+instance Monoid Tree where
+    mempty                      = Tree []
+    mappend (Tree e1) (Tree e2) = Tree (e1 ++ e2)
+    mconcat trees               = Tree $ concatMap treeGetEnts trees
+
+-- | Represent a binary blob.
+data Blob = Blob { blobGetContent :: L.ByteString } deriving (Show,Eq)
+
+-- | Represent a commit object.
+data Commit = Commit
+        { commitTreeish   :: Ref
+        , commitParents   :: [Ref]
+        , commitAuthor    :: Name
+        , commitCommitter :: Name
+        , commitMessage   :: ByteString
+        } deriving (Show,Eq)
+
+-- | Represent a signed tag.
+data Tag = Tag
+        { tagRef        :: Ref
+        , tagObjectType :: ObjectType
+        , tagBlob       :: ByteString
+        , tagName       :: Name
+        , tagS          :: ByteString
+        } deriving (Show,Eq)
+
+-- | Delta pointing to an offset.
+data DeltaOfs = DeltaOfs Word64 Delta
+        deriving (Show,Eq)
+
+-- | Delta pointing to a ref.
+data DeltaRef = DeltaRef Ref Delta
+        deriving (Show,Eq)
diff --git a/Hit.hs b/Hit.hs
deleted file mode 100644
--- a/Hit.hs
+++ /dev/null
@@ -1,160 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
--- |
--- Module      : Hit
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unix
---
-module Main where
-
-import System.Environment
-import Control.Applicative ((<$>))
-import Control.Monad
-import Data.IORef
-import Data.Maybe
-import Data.Git.Pack
-import Data.Git.Object
-import Data.Git.Ref
-import Data.Git.Repository
-import Data.Git.Revision
-import Data.Word
-import qualified Data.ByteString.Lazy as L
-import qualified Data.ByteString.Char8 as BC
-import Text.Printf
-import qualified Data.Map as M
-import qualified Data.HashTable.IO as H
-import qualified Data.Hashable as Hashable
-
-type HashTable k v = H.CuckooHashTable k v
-
-instance Hashable.Hashable Ref where
-	hash = Hashable.hash . toBinary
-
-verifyPack pref git = do
-	offsets     <- H.new
-	tree        <- H.new
-	refs        <- newIORef M.empty
-	entries     <- fromIntegral <$> packReadHeader (gitRepoPath git) pref
-	leftParsed  <- newIORef entries
-	-- enumerate all objects either directly in tree for fully formed objects
-	-- 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
-		readIORefAndReplace ioref emptyVal = do
-			v <- readIORef ioref
-			writeIORef ioref emptyVal
-			return v
-
-		setObj_ refs offsets tree (!info, objData)
-			| objectTypeIsDelta (poiType info) = do
-				(!ty, !ref, !ptr, !lenChain) <- do
-					let loc = Packed pref (poiOffset info)
-					objInfo <- maybe (error "cannot find delta chain") id <$> findObjectRawAt git loc True
-					let (ty, sz, _) = oiHeader objInfo
-					let !ref = objectHash ty sz (oiData objInfo)
-					let ptr = head $ oiChains objInfo -- it's safe since deltas always have a non empty valid chain
-					return (ty, ref, ptr, (length $ oiChains objInfo))
-				H.insert tree ref (info { poiType = ty }, Just (ptr, lenChain))
-			| otherwise = do
-				let !ref = objectHash (poiType info) (poiActualSize info) objData
-				modifyIORef refs (M.insert ref ())
-				H.insert offsets (poiOffset info) ref
-				H.insert tree ref (info,Nothing)
-
-		setObj leftParsed refs offsets tree x = do
-			parsed <- readIORef leftParsed
-			when ((parsed `mod` 256) == 0) $ putStrLn (show parsed ++ " left to parse")
-			modifyIORef leftParsed (\i -> i-1)
-			setObj_ refs offsets tree x
-
-		dumpTree :: HashTable Word64 Ref -> HashTable Ref (PackedObjectInfo, Maybe (ObjectPtr, Int)) -> M.Map Ref () -> IO ()
-		dumpTree offsets tree refs = do
-			forM_ (M.toAscList refs) $ \(ref, ()) -> do
-				ent <- fromJust <$> H.lookup tree ref
-				printEnt offsets ref ent
-
-		-- print one line about the entry
-		-- format is <sha1> <type> <real size> <size> <offset> [<number of chain element> <parent element>]
-		printEnt _ ref (info,Nothing) = do
-			printf "%s %-6s %d %d %d\n" (show ref)
-			       (objectTypeMarshall $ poiType info)
-			       (poiActualSize info)
-			       (poiSize info)
-			       (poiOffset info)
-
-		printEnt offsets ref (info,Just (parentOffset, lenChain)) = do
-			parentRef <- case parentOffset of
-				PtrRef r -> return r
-				PtrOfs off -> do
-					let poff = poiOffset info - off
-					maybe (error "cannot find delta's parent in pack ?") id <$> H.lookup offsets poff
-			printf "%s %-6s %d %d %d %d %s\n" (show ref)
-			       (objectTypeMarshall $ poiType info)
-			       (poiActualSize info)
-			       (poiSize info)
-			       (poiOffset info)
-			       (lenChain)
-			       (show parentRef)
-
-
-catFile ty ref git = do
-	let expectedType = case ty of
-		"commit" -> Just TypeCommit
-		"blob"   -> Just TypeBlob
-		"tag"    -> Just TypeTag
-		"tree"   -> Just TypeTree
-		"-t"     -> Nothing
-		_        -> error "unknown type request"
-	mobj <- findObjectRaw git ref True
-	case mobj of
-		Nothing  -> error "not a valid object"
-		Just obj ->
-			let (objty, _, _) = oiHeader obj in
-			case expectedType of
-				Nothing  -> putStrLn $ objectTypeMarshall objty
-				Just ety -> do
-					when (ety /= objty) $ error "not expected type"
-					L.putStrLn (oiData obj)
-
-lsTree revision _ git = do
-	ref <- maybe (error "revision cannot be found") id <$> resolveRevision git revision
-	tree <- resolveTreeish git ref
-	case tree of
-		Just t -> do
-			htree <- buildHTree git t
-			mapM_ (showTreeEnt) htree 
-		_      -> error "cannot build a tree from this reference"
-	where
-		showTreeEnt (p,n,TreeDir r _) = printf "%06o tree %s    %s\n" p (show r) (BC.unpack n)
-		showTreeEnt (p,n,TreeFile r)  = printf "%06o blob %s    %s\n" p (show r) (BC.unpack n)
-
-revList revision git = do
-	ref <- maybe (error "revision cannot be found") id <$> resolveRevision git revision
-	loopTillEmpty ref
-	where loopTillEmpty ref = do
-		obj <- findCommit git ref
-		case obj of
-			Just (Commit _ parents _ _ _) -> do
-				putStrLn $ show ref
-				-- this behave like rev-list --first-parent.
-				-- otherwise the parents need to be organized and printed
-				-- in a reverse chronological fashion.
-				case parents of
-					[]    -> return ()
-					(p:_) -> loopTillEmpty p
-			Nothing -> error "reference in commit chain doesn't exists"
-
-main = do
-	p    <- findRepo
-	args <- getArgs
-	case args of
-		["verify-pack",ref]  -> withRepo p $ verifyPack (fromHexString ref)
-		["cat-file",ty,ref]  -> withRepo p $ catFile ty (fromHexString ref)
-		["ls-tree",rev]      -> withRepo p $ lsTree (revFromString rev) ""
-		["ls-tree",rev,path] -> withRepo p $ lsTree (revFromString rev) path
-		["rev-list",rev]     -> withRepo p $ revList (revFromString rev)
-		cmd : [] -> error ("unknown command: " ++ cmd)
-		[]       -> error "no args"
-		_        -> error "unknown command line arguments"
diff --git a/Hit/Hit.hs b/Hit/Hit.hs
new file mode 100644
--- /dev/null
+++ b/Hit/Hit.hs
@@ -0,0 +1,161 @@
+{-# LANGUAGE BangPatterns #-}
+-- |
+-- Module      : Hit
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unix
+--
+module Main where
+
+import System.Environment
+import Control.Applicative ((<$>))
+import Control.Monad
+import Data.IORef
+import Data.Maybe
+import Data.Git.Storage.Pack
+import Data.Git.Storage.Object
+import Data.Git.Storage
+import Data.Git.Types
+import Data.Git.Ref
+import Data.Git.Repository
+import Data.Git.Revision
+import Data.Word
+import qualified Data.ByteString.Lazy.Char8 as LC
+import qualified Data.ByteString.Char8 as BC
+import Text.Printf
+import qualified Data.Map as M
+import qualified Data.HashTable.IO as H
+import qualified Data.Hashable as Hashable
+
+type HashTable k v = H.CuckooHashTable k v
+
+instance Hashable.Hashable Ref where
+	hash = Hashable.hash . toBinary
+
+verifyPack pref git = do
+	offsets     <- H.new
+	tree        <- H.new
+	refs        <- newIORef M.empty
+	entries     <- fromIntegral <$> packReadHeader (gitRepoPath git) pref
+	leftParsed  <- newIORef entries
+	-- enumerate all objects either directly in tree for fully formed objects
+	-- 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
+		readIORefAndReplace ioref emptyVal = do
+			v <- readIORef ioref
+			writeIORef ioref emptyVal
+			return v
+
+		setObj_ refs offsets tree (!info, objData)
+			| objectTypeIsDelta (poiType info) = do
+				(!ty, !ref, !ptr, !lenChain) <- do
+					let loc = Packed pref (poiOffset info)
+					objInfo <- maybe (error "cannot find delta chain") id <$> getObjectRawAt git loc True
+					let (ty, sz, _) = oiHeader objInfo
+					let !ref = objectHash ty sz (oiData objInfo)
+					let ptr = head $ oiChains objInfo -- it's safe since deltas always have a non empty valid chain
+					return (ty, ref, ptr, (length $ oiChains objInfo))
+				H.insert tree ref (info { poiType = ty }, Just (ptr, lenChain))
+			| otherwise = do
+				let !ref = objectHash (poiType info) (poiActualSize info) objData
+				modifyIORef refs (M.insert ref ())
+				H.insert offsets (poiOffset info) ref
+				H.insert tree ref (info,Nothing)
+
+		setObj leftParsed refs offsets tree x = do
+			parsed <- readIORef leftParsed
+			when ((parsed `mod` 256) == 0) $ putStrLn (show parsed ++ " left to parse")
+			modifyIORef leftParsed (\i -> i-1)
+			setObj_ refs offsets tree x
+
+		dumpTree :: HashTable Word64 Ref -> HashTable Ref (PackedObjectInfo, Maybe (ObjectPtr, Int)) -> M.Map Ref () -> IO ()
+		dumpTree offsets tree refs = do
+			forM_ (M.toAscList refs) $ \(ref, ()) -> do
+				ent <- fromJust <$> H.lookup tree ref
+				printEnt offsets ref ent
+
+		-- print one line about the entry
+		-- format is <sha1> <type> <real size> <size> <offset> [<number of chain element> <parent element>]
+		printEnt _ ref (info,Nothing) = do
+			printf "%s %-6s %d %d %d\n" (show ref)
+			       (objectTypeMarshall $ poiType info)
+			       (poiActualSize info)
+			       (poiSize info)
+			       (poiOffset info)
+
+		printEnt offsets ref (info,Just (parentOffset, lenChain)) = do
+			parentRef <- case parentOffset of
+				PtrRef r -> return r
+				PtrOfs off -> do
+					let poff = poiOffset info - off
+					maybe (error "cannot find delta's parent in pack ?") id <$> H.lookup offsets poff
+			printf "%s %-6s %d %d %d %d %s\n" (show ref)
+			       (objectTypeMarshall $ poiType info)
+			       (poiActualSize info)
+			       (poiSize info)
+			       (poiOffset info)
+			       (lenChain)
+			       (show parentRef)
+
+
+catFile ty ref git = do
+	let expectedType = case ty of
+		"commit" -> Just TypeCommit
+		"blob"   -> Just TypeBlob
+		"tag"    -> Just TypeTag
+		"tree"   -> Just TypeTree
+		"-t"     -> Nothing
+		_        -> error "unknown type request"
+	mobj <- getObjectRaw git ref True
+	case mobj of
+		Nothing  -> error "not a valid object"
+		Just obj ->
+			let (objty, _, _) = oiHeader obj in
+			case expectedType of
+				Nothing  -> putStrLn $ objectTypeMarshall objty
+				Just ety -> do
+					when (ety /= objty) $ error "not expected type"
+					LC.putStrLn (oiData obj)
+
+lsTree revision _ git = do
+	ref <- maybe (error "revision cannot be found") id <$> resolveRevision git revision
+	tree <- resolveTreeish git ref
+	case tree of
+		Just t -> do
+			htree <- buildHTree git t
+			mapM_ (showTreeEnt) htree 
+		_      -> error "cannot build a tree from this reference"
+	where
+		showTreeEnt (p,n,TreeDir r _) = printf "%06o tree %s    %s\n" p (show r) (BC.unpack n)
+		showTreeEnt (p,n,TreeFile r)  = printf "%06o blob %s    %s\n" p (show r) (BC.unpack n)
+
+revList revision git = do
+	ref <- maybe (error "revision cannot be found") id <$> resolveRevision git revision
+	loopTillEmpty ref
+	where loopTillEmpty ref = do
+		obj <- getCommit git ref
+		case obj of
+			Just (Commit _ parents _ _ _) -> do
+				putStrLn $ show ref
+				-- this behave like rev-list --first-parent.
+				-- otherwise the parents need to be organized and printed
+				-- in a reverse chronological fashion.
+				case parents of
+					[]    -> return ()
+					(p:_) -> loopTillEmpty p
+			Nothing -> error "reference in commit chain doesn't exists"
+
+main = do
+	args <- getArgs
+	case args of
+		["verify-pack",ref]  -> withCurrentRepo $ verifyPack (fromHexString ref)
+		["cat-file",ty,ref]  -> withCurrentRepo $ catFile ty (fromHexString ref)
+		["ls-tree",rev]      -> withCurrentRepo $ lsTree (fromString rev) ""
+		["ls-tree",rev,path] -> withCurrentRepo $ lsTree (fromString rev) path
+		["rev-list",rev]     -> withCurrentRepo $ revList (fromString rev)
+		cmd : [] -> error ("unknown command: " ++ cmd)
+		[]       -> error "no args"
+		_        -> error "unknown command line arguments"
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,10 +1,11 @@
-Copyright (c) 2010-2011 Vincent Hanquez <vincent@snarc.org>
+Copyright (c) 2010-2012 Vincent Hanquez <vincent@snarc.org>
 
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
+
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -15,15 +15,18 @@
 * reimplement the whole of git.
 * checkout's index reading/writing, fetching, merging, diffing.
 
-The main functions for users are available from the Data.Git.Repository module.
+The main functions for users are available from the Data.Git module.
 
 The essential functions are:
 
 * withRepo: create a new git context and execute a function with the context. functional equivalent of withFile but for git repository.
+* withCurrentRepo: similar to withRepo but found the repository from the user current directory.
 * resolveRevision: turns a git revision (e.g. HEAD, 0a24^^^~3) into a SHA1 reference.
 * resolvePath: from a commit ref and a path, it will gives the tree or blob reference of the object at the specific path (see example).
-* findObject: from a SHA1 reference, gives a high level object (Commit, Blob, Tree, Tag, Delta) from the git repository. if called with resolveDelta set, it will resolves deltas to be simple objects with the deltas applied.
-* findObjectRaw: similar to findObject but gives a raw representation (lazy bytestring) of the object.
+* getObject: from a SHA1 reference, gives a high level object (Commit, Blob, Tree, Tag, Delta) from the git repository. if called with resolveDelta set, it will resolves deltas to be simple objects with the deltas applied.
+* getObjectRaw: similar to getObject but gives a raw representation (lazy bytestring) of the object.
+* getCommit: similar to getObject but gives back a commit.
+* getTree: similar to getObject but gives back a tree.
 
 API Example
 -----------
@@ -34,7 +37,7 @@
     import Data.Git.Repository
 
     showPathRef commitRef = withRepo ".git" $ \git -> do
-	ref <- maybe (error "inexistent object at this path") id `fmap` resolvePath git commitRef ["README"]
+        ref <- maybe (error "inexistent object at this path") id `fmap` resolvePath git commitRef ["README"]
         putStrLn ("README has the reference: " ++ show ref)
 
 
@@ -43,7 +46,7 @@
     import Data.Git.Repository
 
     catFile ref = withRepo ".git" $ \git -> do
-	obj <- maybe (error "not a valid object") id `fmap` findObjectRaw git ref True
+        obj <- maybe (error "not a valid object") id `fmap` getObjectRaw git ref True
         L.putStrLn (oiData obj)
 
 
diff --git a/Tests.hs b/Tests.hs
deleted file mode 100644
--- a/Tests.hs
+++ /dev/null
@@ -1,88 +0,0 @@
-import Test.QuickCheck
-import Test.Framework(defaultMain, testGroup)
-import Test.Framework.Providers.QuickCheck2(testProperty)
-
-import qualified Data.ByteString.Lazy as L
-import qualified Data.ByteString as B
-
-import Control.Applicative
-import Control.Monad
-
-import Data.Git.Object
-import Data.Git.Loose
-import Data.Git.Ref
-
--- for arbitrary instance to generate only data that are writable
--- to disk. i.e. no deltas.
---data ObjNoDelta = ObjNoDelta Object
-
---instance Show ObjNoDelta where
---	show (ObjNoDelta o) = show o
-
-arbitraryBS size = B.pack . map fromIntegral <$> replicateM size (choose (0,255) :: Gen Int)
-arbitraryBSno0 size = B.pack . map fromIntegral <$> replicateM size (choose (1,255) :: Gen Int)
-
-arbitraryBSascii size = B.pack . map fromIntegral <$> replicateM size (choose (0x20,0x7f) :: Gen Int)
-arbitraryBSnoangle size = B.pack . map fromIntegral <$> replicateM size (choose (0x40,0x7f) :: Gen Int)
-
-instance Arbitrary Ref where
-	arbitrary = fromBinary <$> arbitraryBS 20
-
-arbitraryMsg = arbitraryBSno0 128
-arbitraryLazy = L.fromChunks . (:[]) <$> arbitraryBS 4096
-
-arbitraryRefList :: Gen [Ref]
-arbitraryRefList = replicateM 2 arbitrary
-
-arbitraryEnt = liftM3 (,,) arbitrary (arbitraryBSno0 48) arbitrary
-arbitraryEnts = choose (1,100) >>= \i -> replicateM i arbitraryEnt
-
-arbitraryName = liftM4 (,,,) (arbitraryBSnoangle 16)
-                             (arbitraryBSnoangle 16)
-                             (arbitrary `suchThat` (\i -> i > 0))
-                              arbitrary
-
-arbitraryObjTypeNoDelta = oneof [return TypeTree,return TypeBlob,return TypeCommit,return TypeTag]
-
-instance Arbitrary Commit where
-	arbitrary = Commit <$> arbitrary <*> arbitraryRefList <*> arbitraryName <*> arbitraryName <*> arbitraryMsg
-
-instance Arbitrary Tree where
-	arbitrary = Tree <$> arbitraryEnts
-
-instance Arbitrary Blob where
-	arbitrary = Blob <$> arbitraryLazy
-
-instance Arbitrary Tag where
-	arbitrary = Tag <$> arbitrary <*> arbitraryObjTypeNoDelta <*> arbitraryBSascii 20 <*> arbitraryName <*> arbitraryMsg
-
-{-
-instance Arbitrary Object where
-	arbitrary = undefined
-
-instance Arbitrary ObjNoDelta where
-	arbitrary = ObjNoDelta <$> oneof
-		[ liftM5 Commit arbitrary arbitraryRefList arbitraryName arbitraryName arbitraryMsg
-		, liftM Tree arbitraryEnts
-		, liftM Blob arbitraryLazy
-		, liftM5 Tag
-		]
--}
-
---prop_object_marshalling_id (ObjNoDelta obj) = obj == (looseUnmarshall $ looseMarshall obj)
-
-refTests =
-	[ testProperty "hexadecimal" (marshEqual (fromHex . toHex))
-	, testProperty "binary" (marshEqual (fromBinary . toBinary))
-	]
-	where
-		marshEqual t ref = ref == t ref
-
-objTests =
-	[ -- testProperty "unmarshall.marshall==id" prop_object_marshalling_id
-	]
-
-main = defaultMain
-	[ testGroup "ref marshalling" refTests
-	, testGroup "object marshalling" objTests
-	]
diff --git a/Tests/Repo.hs b/Tests/Repo.hs
new file mode 100644
--- /dev/null
+++ b/Tests/Repo.hs
@@ -0,0 +1,59 @@
+import Test.QuickCheck
+import Test.Framework(defaultMain, testGroup, buildTest)
+import Test.Framework.Providers.QuickCheck2(testProperty)
+
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString as B
+
+import Control.Applicative
+import Control.Monad
+
+import Data.Git.Storage.Object
+import Data.Git.Storage.Loose
+import Data.Git.Storage
+import Data.Git.Ref
+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
+import System.Exit
+
+onLocalRepo f = withCurrentRepo f
+
+doLocalMarshallEq git = do
+     prefixes <- looseEnumeratePrefixes (gitRepoPath git)
+     forM prefixes $ \prefix -> do
+         refs <- looseEnumerateWithPrefix (gitRepoPath git) prefix
+         forM refs $ \ref -> do
+             raw <- looseReadRaw (gitRepoPath git) ref
+             obj <- looseRead (gitRepoPath git) ref
+             let content = looseMarshall obj
+             let raw2 = looseUnmarshallRaw content
+             let hashed = hashLBS content
+             if ref /= hashed
+                  then return $ Just (ref, hashed, raw, raw2)
+                  else return Nothing
+
+printDiff (actualRef, gotRef, (actualHeader, actualRaw), (gotHeader, gotRaw)) = do
+    putStrLn "=========== difference found"
+    putStrLn ("ref expected: " ++ show actualRef)
+    putStrLn ("ref got     : " ++ show gotRef)
+    putStrLn ("header expected: " ++ show actualHeader)
+    putStrLn ("header got     : " ++ show gotHeader)
+    putStrLn "raw diff:"
+    putStrLn $ dumpDiffLBS actualRaw gotRaw
+
+printLocalMarshallError l
+    | null l    = putStrLn "local marshall:   [OK]"
+    | otherwise = putStrLn ("local marshall: [" ++ show (length l) ++ " errors]")
+               >> mapM_ printDiff l
+               >> exitFailure
+
+main = onLocalRepo $ \git -> do
+    doLocalMarshallEq git >>= printLocalMarshallError . catMaybes . concat
+    return ()
diff --git a/Tests/Tests.hs b/Tests/Tests.hs
new file mode 100644
--- /dev/null
+++ b/Tests/Tests.hs
@@ -0,0 +1,99 @@
+import Test.QuickCheck
+import Test.Framework(defaultMain, testGroup)
+import Test.Framework.Providers.QuickCheck2(testProperty)
+
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString as B
+
+import Control.Applicative
+import Control.Monad
+
+import Data.Git.Storage.Object
+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
+
+-- for arbitrary instance to generate only data that are writable
+-- to disk. i.e. no deltas.
+--data ObjNoDelta = ObjNoDelta Object
+
+--instance Show ObjNoDelta where
+--	show (ObjNoDelta o) = show o
+
+arbitraryBS size = B.pack . map fromIntegral <$> replicateM size (choose (0,255) :: Gen Int)
+arbitraryBSno0 size = B.pack . map fromIntegral <$> replicateM size (choose (1,255) :: Gen Int)
+
+arbitraryBSascii size = B.pack . map fromIntegral <$> replicateM size (choose (0x20,0x7f) :: Gen Int)
+arbitraryBSnoangle size = B.pack . map fromIntegral <$> replicateM size (choose (0x40,0x7f) :: Gen Int)
+
+instance Arbitrary Ref where
+	arbitrary = fromBinary <$> arbitraryBS 20
+
+arbitraryMsg = arbitraryBSno0 128
+arbitraryLazy = L.fromChunks . (:[]) <$> arbitraryBS 4096
+
+arbitraryRefList :: Gen [Ref]
+arbitraryRefList = replicateM 2 arbitrary
+
+arbitraryEnt = liftM3 (,,) arbitrary (arbitraryBSno0 48) arbitrary
+arbitraryEnts = choose (1,100) >>= \i -> replicateM i arbitraryEnt
+
+instance Arbitrary TimeZone where
+    arbitrary = hoursToTimeZone <$> arbitrary 
+
+instance Arbitrary UTCTime where
+    arbitrary = UTCTime <$> (flip addDays b <$> choose (0, 365 * 40))
+                        <*> (secondsToDiffTime <$> arbitrary)
+        where b = fromGregorian 1970 1 1
+arbitraryName = liftM4 (,,,) (arbitraryBSnoangle 16)
+                             (arbitraryBSnoangle 16)
+                             arbitrary
+                             arbitrary
+
+arbitraryObjTypeNoDelta = oneof [return TypeTree,return TypeBlob,return TypeCommit,return TypeTag]
+
+instance Arbitrary Commit where
+	arbitrary = Commit <$> arbitrary <*> arbitraryRefList <*> arbitraryName <*> arbitraryName <*> arbitraryMsg
+
+instance Arbitrary Tree where
+	arbitrary = Tree <$> arbitraryEnts
+
+instance Arbitrary Blob where
+	arbitrary = Blob <$> arbitraryLazy
+
+instance Arbitrary Tag where
+	arbitrary = Tag <$> arbitrary <*> arbitraryObjTypeNoDelta <*> arbitraryBSascii 20 <*> arbitraryName <*> arbitraryMsg
+
+{-
+instance Arbitrary Object where
+	arbitrary = undefined
+
+instance Arbitrary ObjNoDelta where
+	arbitrary = ObjNoDelta <$> oneof
+		[ liftM5 Commit arbitrary arbitraryRefList arbitraryName arbitraryName arbitraryMsg
+		, liftM Tree arbitraryEnts
+		, liftM Blob arbitraryLazy
+		, liftM5 Tag
+		]
+-}
+
+--prop_object_marshalling_id (ObjNoDelta obj) = obj == (looseUnmarshall $ looseMarshall obj)
+
+refTests =
+	[ testProperty "hexadecimal" (marshEqual (fromHex . toHex))
+	, testProperty "binary" (marshEqual (fromBinary . toBinary))
+	]
+	where
+		marshEqual t ref = ref == t ref
+
+objTests =
+	[ -- testProperty "unmarshall.marshall==id" prop_object_marshalling_id
+	]
+
+main = defaultMain
+	[ testGroup "ref marshalling" refTests
+	, testGroup "object marshalling" objTests
+	]
diff --git a/hit.cabal b/hit.cabal
--- a/hit.cabal
+++ b/hit.cabal
@@ -1,5 +1,5 @@
 Name:                hit
-Version:             0.2.2
+Version:             0.3.0
 Synopsis:            Git operations
 Description:         Provides low level git operations
 License:             BSD3
@@ -11,12 +11,9 @@
 Stability:           experimental
 Build-Type:          Simple
 Homepage:            http://github.com/vincenthz/hit
-Cabal-Version:       >=1.6
+Cabal-Version:       >=1.8
 data-files:          README.md
-
-Flag test
-  Description:       Build unit test
-  Default:           False
+extra-source-files:  Tests/*.hs
 
 Flag executable
   Description:       Build the executable
@@ -38,26 +35,31 @@
                    , vector
                    , random
                    , zlib
-                   , zlib-bindings >= 0.0.1
+                   , zlib-bindings >= 0.1 && < 0.2
                    , bytedump
-  Exposed-modules:   Data.Git.Index
-                     Data.Git.Pack
-                     Data.Git.Object
-                     Data.Git.Loose
+                   , time
+  Exposed-modules:   Data.Git
+                     Data.Git.Types
+                     Data.Git.Storage
+                     Data.Git.Storage.PackIndex
+                     Data.Git.Storage.Pack
+                     Data.Git.Storage.Object
+                     Data.Git.Storage.Loose
                      Data.Git.Named
                      Data.Git.Delta
                      Data.Git.Ref
                      Data.Git.Revision
                      Data.Git.Repository
   Other-modules:     Data.Git.Internal
-                     Data.Git.FileReader
-                     Data.Git.FileWriter
+                     Data.Git.Storage.FileReader
+                     Data.Git.Storage.FileWriter
                      Data.Git.Path
   ghc-options:       -Wall -fno-warn-missing-signatures
 
 Executable           Hit
   Main-Is:           Hit.hs
-  ghc-options:       -Wall -fno-warn-missing-signatures
+  hs-source-dirs:    Hit
+  ghc-options:       -Wall -fno-warn-orphans -fno-warn-missing-signatures
   if flag(debug)
     ghc-options:     -rtsops -auto-all -caf-all
   if flag(executable)
@@ -68,27 +70,41 @@
                    , hashtables
                    , bytestring
                    , attoparsec >= 0.10.1
+                   , parsec     >= 3
                    , filepath
                    , directory
-                   , zlib
                    , bytedump
+                   , hit
     Buildable: True
   else
     Buildable: False
 
-Executable           Tests
+Test-Suite test-unit
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    Tests
   Main-Is:           Tests.hs
-  if flag(test)
-    Buildable:       True
-    Build-depends:   base >= 3 && < 7
+  Build-depends:     base >= 3 && < 7
                    , HUnit
                    , QuickCheck >= 2
                    , bytestring
                    , test-framework >= 0.3
                    , test-framework-quickcheck2 >= 0.2
-  else
-    Buildable:       False
+                   , time
+                   , hit
 
+Test-Suite test-repository
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    Tests
+  Main-Is:           Repo.hs
+  Build-depends:     base >= 3 && < 7
+                   , HUnit
+                   , QuickCheck >= 2
+                   , bytestring
+                   , test-framework >= 0.3
+                   , test-framework-quickcheck2 >= 0.2
+                   , time
+                   , bytedump
+                   , hit
 
 source-repository head
   type: git
