add git queue to Annex monad

not used anywhere just yet..
This commit is contained in:
Joey Hess 2010-10-26 15:59:50 -04:00
parent 4cda7b6e7c
commit ef26076a5a
4 changed files with 112 additions and 7 deletions

View file

@ -11,25 +11,28 @@ module Annex (
flagIsSet, flagIsSet,
flagChange, flagChange,
flagGet, flagGet,
Flag(..) Flag(..),
queue,
queueGet
) where ) where
import Control.Monad.State import Control.Monad.State
import qualified Data.Map as M import qualified Data.Map as M
import qualified GitRepo as Git import qualified GitRepo as Git
import qualified GitQueue
import Types import Types
import qualified TypeInternals as Internals import qualified TypeInternals as Internals
{- Create and returns an Annex state object for the specified git repo. {- Create and returns an Annex state object for the specified git repo. -}
-}
new :: Git.Repo -> [Backend] -> IO AnnexState new :: Git.Repo -> [Backend] -> IO AnnexState
new gitrepo allbackends = do new gitrepo allbackends = do
let s = Internals.AnnexState { let s = Internals.AnnexState {
Internals.repo = gitrepo, Internals.repo = gitrepo,
Internals.backends = [], Internals.backends = [],
Internals.supportedBackends = allbackends, Internals.supportedBackends = allbackends,
Internals.flags = M.empty Internals.flags = M.empty,
Internals.repoqueue = GitQueue.empty
} }
(_,s') <- Annex.run s (prep gitrepo) (_,s') <- Annex.run s (prep gitrepo)
return s' return s'
@ -39,46 +42,73 @@ new gitrepo allbackends = do
gitrepo' <- liftIO $ Git.configRead gitrepo gitrepo' <- liftIO $ Git.configRead gitrepo
Annex.gitRepoChange gitrepo' Annex.gitRepoChange gitrepo'
-- performs an action in the Annex monad {- performs an action in the Annex monad -}
run state action = runStateT (action) state run state action = runStateT (action) state
-- Annex monad state accessors {- Returns the git repository being acted on -}
gitRepo :: Annex Git.Repo gitRepo :: Annex Git.Repo
gitRepo = do gitRepo = do
state <- get state <- get
return (Internals.repo state) return (Internals.repo state)
{- Changes the git repository being acted on. -}
gitRepoChange :: Git.Repo -> Annex () gitRepoChange :: Git.Repo -> Annex ()
gitRepoChange r = do gitRepoChange r = do
state <- get state <- get
put state { Internals.repo = r } put state { Internals.repo = r }
return () return ()
{- Returns the backends being used. -}
backends :: Annex [Backend] backends :: Annex [Backend]
backends = do backends = do
state <- get state <- get
return (Internals.backends state) return (Internals.backends state)
{- Sets the backends to use. -}
backendsChange :: [Backend] -> Annex () backendsChange :: [Backend] -> Annex ()
backendsChange b = do backendsChange b = do
state <- get state <- get
put state { Internals.backends = b } put state { Internals.backends = b }
return () return ()
{- Returns the full list of supported backends. -}
supportedBackends :: Annex [Backend] supportedBackends :: Annex [Backend]
supportedBackends = do supportedBackends = do
state <- get state <- get
return (Internals.supportedBackends state) return (Internals.supportedBackends state)
{- Return True if a Bool flag is set. -}
flagIsSet :: FlagName -> Annex Bool flagIsSet :: FlagName -> Annex Bool
flagIsSet name = do flagIsSet name = do
state <- get state <- get
case (M.lookup name $ Internals.flags state) of case (M.lookup name $ Internals.flags state) of
Just (FlagBool True) -> return True Just (FlagBool True) -> return True
_ -> return False _ -> return False
{- Sets the value of a flag. -}
flagChange :: FlagName -> Flag -> Annex () flagChange :: FlagName -> Flag -> Annex ()
flagChange name val = do flagChange name val = do
state <- get state <- get
put state { Internals.flags = M.insert name val $ Internals.flags state } put state { Internals.flags = M.insert name val $ Internals.flags state }
return () return ()
{- Gets the value of a String flag (or "" if there is no such String flag) -}
flagGet :: FlagName -> Annex String flagGet :: FlagName -> Annex String
flagGet name = do flagGet name = do
state <- get state <- get
case (M.lookup name $ Internals.flags state) of case (M.lookup name $ Internals.flags state) of
Just (FlagString s) -> return s Just (FlagString s) -> return s
_ -> return "" _ -> return ""
{- Adds a git command to the queue. -}
queue :: String -> [String] -> FilePath -> Annex ()
queue subcommand params file = do
state <- get
let q = Internals.repoqueue state
put state { Internals.repoqueue = GitQueue.add q subcommand params file }
{- Returns the queue. -}
queueGet :: Annex GitQueue.Queue
queueGet = do
state <- get
return (Internals.repoqueue state)

View file

@ -14,6 +14,7 @@ import Locations
import LocationLog import LocationLog
import UUID import UUID
import qualified GitRepo as Git import qualified GitRepo as Git
import qualified GitQueue
import qualified Annex import qualified Annex
import Utility import Utility
@ -30,6 +31,14 @@ shutdown :: Annex Bool
shutdown = do shutdown = do
g <- Annex.gitRepo g <- Annex.gitRepo
-- Runs all queued git commands.
q <- Annex.queueGet
if (q == GitQueue.empty)
then return ()
else do
liftIO $ putStrLn "Recording state in git..."
liftIO $ GitQueue.run g q
liftIO $ Git.run g ["add", gitStateDir g] liftIO $ Git.run g ["add", gitStateDir g]
-- clean up any files left in the temp directory, but leave -- clean up any files left in the temp directory, but leave

64
GitQueue.hs Normal file
View file

@ -0,0 +1,64 @@
{- git repository command queues
-}
module GitQueue (
Queue,
empty,
add,
run
) where
import qualified Data.Map as M
import qualified GitRepo as Git
{- An action to perform in a git repository. The file to act on
- is not included, and must be able to be appended after the params. -}
data Action = Action {
subcommand :: String,
params :: [String]
} deriving (Show, Eq, Ord)
{- A queue of actions to perform (in any order) on a git repository,
- with lists of files to perform them on. This allows coalescing
- similar git commands. -}
type Queue = M.Map Action [FilePath]
{- Constructor for empty queue. -}
empty :: Queue
empty = M.empty
{- Adds an action to a queue. -}
add :: Queue -> String -> [String] -> FilePath -> Queue
add queue subcommand params file = M.insertWith (++) action [file] queue
where
action = Action subcommand params
{- Runs a queue on a git repository. -}
run :: Git.Repo -> Queue -> IO ()
run repo queue = do
mapM (\(k, v) -> runAction repo k v) $ M.toList queue
return ()
{- Runs an Action on a list of files in a git repository.
-
- Complicated by commandline length limits. -}
runAction :: Git.Repo -> Action -> [FilePath] -> IO ()
runAction repo action files = do
xargs [] 0 files
where
arg_max = 2048 -- TODO get better ARG_MAX
maxlen = arg_max - cmdlen
c = (subcommand action):(params action)
cmdlen = (length "git") +
(foldl (\a b -> a + b + 1) 1 $ map length c)
xargs collect _ [] = exec collect
xargs collect len (f:fs) = do
let len' = len + 1 + length f
if (len' >= maxlen)
then do
exec collect
xargs [f] (length f) fs
else xargs (f:collect) len' fs
exec [] = return ()
exec fs = Git.run repo $ c ++ fs

View file

@ -10,6 +10,7 @@ import Data.String.Utils
import qualified Data.Map as M import qualified Data.Map as M
import qualified GitRepo as Git import qualified GitRepo as Git
import qualified GitQueue
-- command-line flags -- command-line flags
type FlagName = String type FlagName = String
@ -24,7 +25,8 @@ data AnnexState = AnnexState {
repo :: Git.Repo, repo :: Git.Repo,
backends :: [Backend], backends :: [Backend],
supportedBackends :: [Backend], supportedBackends :: [Backend],
flags :: M.Map FlagName Flag flags :: M.Map FlagName Flag,
repoqueue :: GitQueue.Queue
} deriving (Show) } deriving (Show)
-- git-annex's monad -- git-annex's monad