module Darcs.Diff( treeDiff ) where
import Darcs.Witnesses.Ordered ( FL(..), (+>+) )
import Darcs.Repository.Prefs ( FileType(..) )
import Darcs.Patch ( Prim, hunk, canonize, binary
, addfile, rmfile, adddir, rmdir, invert)
import Storage.Hashed.Tree( diffTrees, zipTrees, TreeItem(..), Tree
, readBlob, emptyBlob )
import Storage.Hashed.AnchoredPath( AnchoredPath, anchorPath )
import qualified Data.ByteString.Lazy.Char8 as BLC
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import ByteStringUtils( is_funky )
#include "gadts.h"
treeDiff :: (FilePath -> FileType) -> Tree IO -> Tree IO -> IO (FL Prim C(x y))
#ifdef GADT_WITNESSES
treeDiff = undefined
#else
treeDiff ft t1 t2 = do
(from, to) <- diffTrees t1 t2
diffs <- sequence $ zipTrees diff from to
return $ foldr (+>+) NilFL diffs
where diff :: AnchoredPath -> Maybe (TreeItem IO) -> Maybe (TreeItem IO)
-> IO (FL Prim)
diff _ (Just (SubTree _)) (Just (SubTree _)) = return NilFL
diff p (Just (SubTree _)) Nothing =
return $ rmdir (anchorPath "" p) :>: NilFL
diff p Nothing (Just (SubTree _)) =
return $ adddir (anchorPath "" p) :>: NilFL
diff p Nothing b'@(Just (File _)) =
do diff' <- diff p (Just (File emptyBlob)) b'
return $ addfile (anchorPath "" p) :>: diff'
diff p a'@(Just (File _)) Nothing =
do diff' <- diff p a' (Just (File emptyBlob))
return $ diff' +>+ (rmfile (anchorPath "" p) :>: NilFL)
diff p (Just (File a')) (Just (File b')) =
do a <- readBlob a'
b <- readBlob b'
let path = anchorPath "" p
case ft path of
TextFile | no_bin a && no_bin b ->
return $ text_diff path a b
_ -> return $ if a /= b
then binary path (strict a) (strict b) :>: NilFL
else NilFL
diff p _ _ = fail $ "Missing case at path " ++ show p
text_diff p a b
| BL.null a && BL.null b = NilFL
| BL.null a = diff_from_empty p b
| BL.null b = diff_to_empty p a
| otherwise = line_diff p (linesB a) (linesB b)
line_diff p a b = canonize (hunk p 1 a b)
diff_to_empty p x | BLC.last x == '\n' = line_diff p (init $ linesB x) []
| otherwise = line_diff p (linesB x) [BS.empty]
diff_from_empty p x = invert (diff_to_empty p x)
no_bin = not . is_funky . strict . BL.take 4096
linesB = map strict . BLC.split '\n'
strict = BS.concat . BL.toChunks
#endif