{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} module VC.Tree where import Filesystem.Path (basename) import Shelly import Control.Monad.Extra import Prelude hiding (FilePath) #if __GLASGOW_HASKELL__ < 710 import Data.Traversable #endif check :: FilePath -> Sh Managed check dir = do names <- ls dir if checkVCDir names then return Managed else do -- TODO exclude symlinks from files (subdirs, files) <- partitionDirs =<< ls dir managed <- traverse check subdirs return $ summary dir (files == []) managed checkVCDir :: [FilePath] -> Bool checkVCDir xs = let names = map basename xs in any (`elem` names) [ ".git", "_darcs", ".hg", ".svn", "CVS", ".fslckout"] data Managed = UnManaged FilePath | Managed | Empty | SomeManaged [FilePath] summary :: FilePath -> Bool -> [Managed] -> Managed summary dir nofiles xs | all isEmpty xs = if nofiles then Empty else UnManaged dir | all isManaged xs = if nofiles then Managed else UnManaged dir | all isUnmanaged xs = UnManaged dir | otherwise = SomeManaged $ if nofiles then concatMap directories xs else dir : concatMap directories xs partitionDirs :: [FilePath] -> Sh ([FilePath], [FilePath]) partitionDirs paths = do (dirs, nondirs) <- partitionM test_d paths files <- filterM (fmap not . test_s) nondirs -- exclude symlinks return (dirs, files) isEmpty :: Managed -> Bool isEmpty Empty = True isEmpty _ = False isManaged :: Managed-> Bool isManaged Managed = True isManaged Empty = True isManaged _ = False isUnmanaged :: Managed -> Bool isUnmanaged (UnManaged _) = True isUnmanaged Empty = True isUnmanaged _ = False directories :: Managed -> [FilePath] directories (SomeManaged ds) = ds directories (UnManaged d) = [d] directories _ = []