fix inversion of control in CommandSeek (no behavior changes)

I've been disliking how the command seek actions were written for some
time, with their inversion of control and ugly workarounds.

The last straw to fix it was sync --content, which didn't fit the
Annex [CommandStart] interface well at all. I have not yet made it take
advantage of the changed interface though.

The crucial change, and probably why I didn't do it this way from the
beginning, is to make each CommandStart action be run with exceptions
caught, and if it fails, increment a failure counter in annex state.
So I finally remove the very first code I wrote for git-annex, which
was before I had exception handling in the Annex monad, and so ran outside
that monad, passing state explicitly as it ran each CommandStart action.

This was a real slog from 1 to 5 am.

Test suite passes.

Memory usage is lower than before, sometimes by a couple of megabytes, and
remains constant, even when running in a large repo, and even when
repeatedly failing and incrementing the error counter. So no accidental
laziness space leaks.

Wall clock speed is identical, even in large repos.

This commit was sponsored by an anonymous bitcoiner.
This commit is contained in:
Joey Hess 2014-01-20 04:11:42 -04:00
parent df5e2e3d65
commit 34c8af74ba
79 changed files with 389 additions and 355 deletions

View file

@ -109,6 +109,7 @@ data AnnexState = AnnexState
, cleanup :: M.Map String (Annex ()) , cleanup :: M.Map String (Annex ())
, inodeschanged :: Maybe Bool , inodeschanged :: Maybe Bool
, useragent :: Maybe String , useragent :: Maybe String
, errcounter :: Integer
} }
newState :: GitConfig -> Git.Repo -> AnnexState newState :: GitConfig -> Git.Repo -> AnnexState
@ -143,6 +144,7 @@ newState c r = AnnexState
, cleanup = M.empty , cleanup = M.empty
, inodeschanged = Nothing , inodeschanged = Nothing
, useragent = Nothing , useragent = Nothing
, errcounter = 0
} }
{- Makes an Annex state object for the specified git repo. {- Makes an Annex state object for the specified git repo.

View file

@ -92,7 +92,7 @@ handleDropsFrom locs rs reason fromhere key (Just afile) knownpresentremote = do
checkdrop fs n@(have, numcopies, _untrusted) u a = checkdrop fs n@(have, numcopies, _untrusted) u a =
ifM (allM (wantDrop True u . Just) fs) ifM (allM (wantDrop True u . Just) fs)
( ifM (safely $ doCommand $ a (Just numcopies)) ( ifM (safely $ callCommand $ a (Just numcopies))
( do ( do
liftIO $ debugM "drop" $ unwords liftIO $ debugM "drop" $ unwords
[ "dropped" [ "dropped"

View file

@ -23,7 +23,6 @@ import System.Posix.Signals
import Common.Annex import Common.Annex
import qualified Annex import qualified Annex
import qualified Annex.Queue
import qualified Git import qualified Git
import qualified Git.AutoCorrect import qualified Git.AutoCorrect
import Annex.Content import Annex.Content
@ -41,7 +40,7 @@ dispatch fuzzyok allargs allcmds commonoptions fields header getgitrepo = do
Left e -> maybe (throw e) (\a -> a params) (cmdnorepo cmd) Left e -> maybe (throw e) (\a -> a params) (cmdnorepo cmd)
Right g -> do Right g -> do
state <- Annex.new g state <- Annex.new g
(actions, state') <- Annex.run state $ do Annex.eval state $ do
checkEnvironment checkEnvironment
checkfuzzy checkfuzzy
forM_ fields $ uncurry Annex.setField forM_ fields $ uncurry Annex.setField
@ -50,8 +49,9 @@ dispatch fuzzyok allargs allcmds commonoptions fields header getgitrepo = do
sequence_ flags sequence_ flags
whenM (annexDebug <$> Annex.getGitConfig) $ whenM (annexDebug <$> Annex.getGitConfig) $
liftIO enableDebugOutput liftIO enableDebugOutput
prepCommand cmd params startup
tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdnocommit cmd] performCommand cmd params
shutdown $ cmdnocommit cmd
where where
err msg = msg ++ "\n\n" ++ usage header allcmds err msg = msg ++ "\n\n" ++ usage header allcmds
cmd = Prelude.head cmds cmd = Prelude.head cmds
@ -92,44 +92,19 @@ getOptCmd argv cmd commonoptions = check $
, commandUsage cmd , commandUsage cmd
] ]
{- Runs a list of Annex actions. Catches IO errors and continues
- (but explicitly thrown errors terminate the whole command).
-}
tryRun :: Annex.AnnexState -> Command -> [CommandCleanup] -> IO ()
tryRun = tryRun' 0
tryRun' :: Integer -> Annex.AnnexState -> Command -> [CommandCleanup] -> IO ()
tryRun' errnum _ cmd []
| errnum > 0 = error $ cmdname cmd ++ ": " ++ show errnum ++ " failed"
| otherwise = noop
tryRun' errnum state cmd (a:as) = do
r <- run
handle $! r
where
run = tryIO $ Annex.run state $ do
Annex.Queue.flushWhenFull
a
handle (Left err) = showerr err >> cont False state
handle (Right (success, state')) = cont success state'
cont success s = do
let errnum' = if success then errnum else errnum + 1
(tryRun' $! errnum') s cmd as
showerr err = Annex.eval state $ do
showErr err
showEndFail
{- Actions to perform each time ran. -} {- Actions to perform each time ran. -}
startup :: Annex Bool startup :: Annex ()
startup = liftIO $ do startup =
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
void $ installHandler sigINT Default Nothing liftIO $ void $ installHandler sigINT Default Nothing
#else
return ()
#endif #endif
return True
{- Cleanup actions. -} {- Cleanup actions. -}
shutdown :: Bool -> Annex Bool shutdown :: Bool -> Annex ()
shutdown nocommit = do shutdown nocommit = do
saveState nocommit saveState nocommit
sequence_ =<< M.elems <$> Annex.getState Annex.cleanup sequence_ =<< M.elems <$> Annex.getState Annex.cleanup
liftIO reapZombies -- zombies from long-running git processes liftIO reapZombies -- zombies from long-running git processes
sshCleanup -- ssh connection caching sshCleanup -- ssh connection caching
return True

View file

@ -1,10 +1,12 @@
{- git-annex command infrastructure {- git-annex command infrastructure
- -
- Copyright 2010-2011 Joey Hess <joey@kitenet.net> - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE BangPatterns #-}
module Command ( module Command (
command, command,
noRepo, noRepo,
@ -14,8 +16,7 @@ module Command (
next, next,
stop, stop,
stopUnless, stopUnless,
prepCommand, runCommand,
doCommand,
whenAnnexed, whenAnnexed,
ifAnnexed, ifAnnexed,
isBareRepo, isBareRepo,
@ -35,12 +36,13 @@ import Types.Option as ReExported
import Seek as ReExported import Seek as ReExported
import Checks as ReExported import Checks as ReExported
import Usage as ReExported import Usage as ReExported
import RunCommand as ReExported
import Logs.Trust import Logs.Trust
import Config import Config
import Annex.CheckAttr import Annex.CheckAttr
{- Generates a normal command -} {- Generates a normal command -}
command :: String -> String -> [CommandSeek] -> CommandSection -> String -> Command command :: String -> String -> CommandSeek -> CommandSection -> String -> Command
command = Command [] Nothing commonChecks False False command = Command [] Nothing commonChecks False False
{- Indicates that a command doesn't need to commit any changes to {- Indicates that a command doesn't need to commit any changes to
@ -74,25 +76,6 @@ stop = return Nothing
stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a) stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a)
stopUnless c a = ifM c ( a , stop ) stopUnless c a = ifM c ( a , stop )
{- Prepares to run a command via the check and seek stages, returning a
- list of actions to perform to run the command. -}
prepCommand :: Command -> [String] -> Annex [CommandCleanup]
prepCommand Command { cmdseek = seek, cmdcheck = c } params = do
mapM_ runCheck c
map doCommand . concat <$> mapM (\s -> s params) seek
{- Runs a command through the start, perform and cleanup stages -}
doCommand :: CommandStart -> CommandCleanup
doCommand = start
where
start = stage $ maybe skip perform
perform = stage $ maybe failure cleanup
cleanup = stage $ status
stage = (=<<)
skip = return True
failure = showEndFail >> return False
status r = showEndResult r >> return r
{- Modifies an action to only act on files that are already annexed, {- Modifies an action to only act on files that are already annexed,
- and passes the key and backend on to it. -} - and passes the key and backend on to it. -}
whenAnnexed :: (FilePath -> (Key, Backend) -> Annex (Maybe a)) -> FilePath -> Annex (Maybe a) whenAnnexed :: (FilePath -> (Key, Backend) -> Annex (Maybe a)) -> FilePath -> Annex (Maybe a)

View file

@ -41,18 +41,18 @@ def = [notBareRepo $ command "add" paramPaths seek SectionCommon
{- Add acts on both files not checked into git yet, and unlocked files. {- Add acts on both files not checked into git yet, and unlocked files.
- -
- In direct mode, it acts on any files that have changed. -} - In direct mode, it acts on any files that have changed. -}
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ go withFilesNotInGit matcher <- largeFilesMatcher
, whenNotDirect $ go withFilesUnlocked let go a = flip a ps $ \file -> ifM (checkFileMatcher matcher file <||> Annex.getState Annex.force)
, whenDirect $ go withFilesMaybeModified ( start file
] , stop
where )
go a = withValue largeFilesMatcher $ \matcher -> go withFilesNotInGit
a $ \file -> ifM (checkFileMatcher matcher file <||> Annex.getState Annex.force) ifM isDirect
( start file ( go withFilesMaybeModified
, stop , go withFilesUnlocked
) )
{- The add subcommand annexes a file, generating a key for it using a {- The add subcommand annexes a file, generating a key for it using a
- backend, and then moving it into the annex directory and setting up - backend, and then moving it into the annex directory and setting up

View file

@ -18,8 +18,8 @@ def :: [Command]
def = [notDirect $ command "addunused" (paramRepeating paramNumRange) def = [notDirect $ command "addunused" (paramRepeating paramNumRange)
seek SectionMaintenance "add back unused files"] seek SectionMaintenance "add back unused files"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withUnusedMaps start] seek = withUnusedMaps start
start :: UnusedMaps -> Int -> CommandStart start :: UnusedMaps -> Int -> CommandStart
start = startUnused "addunused" perform start = startUnused "addunused" perform

View file

@ -47,11 +47,12 @@ pathdepthOption = Option.field [] "pathdepth" paramNumber "path components to us
relaxedOption :: Option relaxedOption :: Option
relaxedOption = Option.flag [] "relaxed" "skip size check" relaxedOption = Option.flag [] "relaxed" "skip size check"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField fileOption return $ \f -> seek ps = do
withFlag relaxedOption $ \relaxed -> f <- getOptionField fileOption return
withField pathdepthOption (return . maybe Nothing readish) $ \d -> relaxed <- getOptionFlag relaxedOption
withStrings $ start relaxed f d] d <- getOptionField pathdepthOption (return . maybe Nothing readish)
withStrings (start relaxed f d) ps
start :: Bool -> Maybe FilePath -> Maybe Int -> String -> CommandStart start :: Bool -> Maybe FilePath -> Maybe Int -> String -> CommandStart
start relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI s start relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI s

View file

@ -37,12 +37,13 @@ autoStartOption = Option.flag [] "autostart" "start in known repositories"
startDelayOption :: Option startDelayOption :: Option
startDelayOption = Option.field [] "startdelay" paramNumber "delay before running startup scan" startDelayOption = Option.field [] "startdelay" paramNumber "delay before running startup scan"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFlag Command.Watch.stopOption $ \stopdaemon -> seek ps = do
withFlag Command.Watch.foregroundOption $ \foreground -> stopdaemon <- getOptionFlag Command.Watch.stopOption
withFlag autoStartOption $ \autostart -> foreground <- getOptionFlag Command.Watch.foregroundOption
withField startDelayOption (pure . maybe Nothing parseDuration) $ \startdelay -> autostart <- getOptionFlag autoStartOption
withNothing $ start foreground stopdaemon autostart startdelay] startdelay <- getOptionField startDelayOption (pure . maybe Nothing parseDuration)
withNothing (start foreground stopdaemon autostart startdelay) ps
start :: Bool -> Bool -> Bool -> Maybe Duration -> CommandStart start :: Bool -> Bool -> Bool -> Maybe Duration -> CommandStart
start foreground stopdaemon autostart startdelay start foreground stopdaemon autostart startdelay

View file

@ -16,8 +16,8 @@ def :: [Command]
def = [command "commit" paramNothing seek def = [command "commit" paramNothing seek
SectionPlumbing "commits any staged changes to the git-annex branch"] SectionPlumbing "commits any staged changes to the git-annex branch"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = next $ next $ do start = next $ next $ do

View file

@ -17,8 +17,8 @@ def :: [Command]
def = [noCommit $ command "configlist" paramNothing seek def = [noCommit $ command "configlist" paramNothing seek
SectionPlumbing "outputs relevant git configuration"] SectionPlumbing "outputs relevant git configuration"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do

View file

@ -18,13 +18,14 @@ def :: [Command]
def = [withOptions Command.Move.moveOptions $ command "copy" paramPaths seek def = [withOptions Command.Move.moveOptions $ command "copy" paramPaths seek
SectionCommon "copy content of files to/from another repository"] SectionCommon "copy content of files to/from another repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField toOption Remote.byNameWithUUID $ \to -> to <- getOptionField toOption Remote.byNameWithUUID
withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (Command.Move.startKey to from False) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start to from (Command.Move.startKey to from False)
] (withFilesInGit $ whenAnnexed $ start to from)
ps
{- A copy is just a move that does not delete the source file. {- A copy is just a move that does not delete the source file.
- However, --auto mode avoids unnecessary copies, and avoids getting or - However, --auto mode avoids unnecessary copies, and avoids getting or

View file

@ -19,8 +19,8 @@ def :: [Command]
def = [command "dead" (paramRepeating paramRemote) seek def = [command "dead" (paramRepeating paramRemote) seek
SectionSetup "hide a lost repository"] SectionSetup "hide a lost repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start ws = do start ws = do

View file

@ -16,8 +16,8 @@ def :: [Command]
def = [command "describe" (paramPair paramRemote paramDesc) seek def = [command "describe" (paramPair paramRemote paramDesc) seek
SectionSetup "change description of a repository"] SectionSetup "change description of a repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start (name:description) = do start (name:description) = do

View file

@ -23,8 +23,8 @@ def = [notBareRepo $ noDaemonRunning $
command "direct" paramNothing seek command "direct" paramNothing seek
SectionSetup "switch repository to direct mode"] SectionSetup "switch repository to direct mode"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = ifM isDirect ( stop , next perform ) start = ifM isDirect ( stop , next perform )

View file

@ -27,9 +27,10 @@ def = [withOptions [fromOption] $ command "drop" paramPaths seek
fromOption :: Option fromOption :: Option
fromOption = Option.field ['f'] "from" paramRemote "drop content from a remote" fromOption = Option.field ['f'] "from" paramRemote "drop content from a remote"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField fromOption Remote.byNameWithUUID $ \from -> seek ps = do
withFilesInGit $ whenAnnexed $ start from] from <- getOptionField fromOption Remote.byNameWithUUID
withFilesInGit (whenAnnexed $ start from) ps
start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
start from file (key, _) = checkDropAuto from file key $ \numcopies -> start from file (key, _) = checkDropAuto from file key $ \numcopies ->

View file

@ -18,8 +18,8 @@ def :: [Command]
def = [noCommit $ command "dropkey" (paramRepeating paramKey) seek def = [noCommit $ command "dropkey" (paramRepeating paramKey) seek
SectionPlumbing "drops annexed content for specified keys"] SectionPlumbing "drops annexed content for specified keys"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withKeys start] seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart
start key = stopUnless (inAnnex key) $ do start key = stopUnless (inAnnex key) $ do

View file

@ -21,8 +21,8 @@ def = [withOptions [Command.Drop.fromOption] $
command "dropunused" (paramRepeating paramNumRange) command "dropunused" (paramRepeating paramNumRange)
seek SectionMaintenance "drop unused file content"] seek SectionMaintenance "drop unused file content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withUnusedMaps start] seek = withUnusedMaps start
start :: UnusedMaps -> Int -> CommandStart start :: UnusedMaps -> Int -> CommandStart
start = startUnused "dropunused" perform (performOther gitAnnexBadLocation) (performOther gitAnnexTmpLocation) start = startUnused "dropunused" perform (performOther gitAnnexBadLocation) (performOther gitAnnexTmpLocation)

View file

@ -20,8 +20,8 @@ def = [command "enableremote"
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue) (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
seek SectionSetup "enables use of an existing special remote"] seek SectionSetup "enables use of an existing special remote"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start [] = unknownNameError "Specify the name of the special remote to enable." start [] = unknownNameError "Specify the name of the special remote to enable."

View file

@ -10,7 +10,7 @@ module Command.ExamineKey where
import Common.Annex import Common.Annex
import Command import Command
import qualified Utility.Format import qualified Utility.Format
import Command.Find (formatOption, withFormat, showFormatted, keyVars) import Command.Find (formatOption, getFormat, showFormatted, keyVars)
import Types.Key import Types.Key
import GitAnnex.Options import GitAnnex.Options
@ -19,8 +19,10 @@ def = [noCommit $ noMessages $ withOptions [formatOption, jsonOption] $
command "examinekey" (paramRepeating paramKey) seek command "examinekey" (paramRepeating paramKey) seek
SectionPlumbing "prints information from a key"] SectionPlumbing "prints information from a key"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFormat $ \f -> withKeys $ start f] seek ps = do
format <- getFormat
withKeys (start format) ps
start :: Maybe Utility.Format.Format -> Key -> CommandStart start :: Maybe Utility.Format.Format -> Key -> CommandStart
start format key = do start format key = do

View file

@ -27,8 +27,8 @@ def = [noCommit $ noMessages $ withOptions [formatOption, print0Option, jsonOpti
formatOption :: Option formatOption :: Option
formatOption = Option.field [] "format" paramFormat "control format of output" formatOption = Option.field [] "format" paramFormat "control format of output"
withFormat :: (Maybe Utility.Format.Format -> CommandSeek) -> CommandSeek getFormat :: Annex (Maybe Utility.Format.Format)
withFormat = withField formatOption $ return . fmap Utility.Format.gen getFormat = getOptionField formatOption $ return . fmap Utility.Format.gen
print0Option :: Option print0Option :: Option
print0Option = Option.Option [] ["print0"] (Option.NoArg set) print0Option = Option.Option [] ["print0"] (Option.NoArg set)
@ -36,8 +36,10 @@ print0Option = Option.Option [] ["print0"] (Option.NoArg set)
where where
set = Annex.setField (Option.name formatOption) "${file}\0" set = Annex.setField (Option.name formatOption) "${file}\0"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFormat $ \f -> withFilesInGit $ whenAnnexed $ start f] seek ps = do
format <- getFormat
withFilesInGit (whenAnnexed $ start format) ps
start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart
start format file (key, _) = do start format file (key, _) = do

View file

@ -24,8 +24,8 @@ def :: [Command]
def = [notDirect $ noCommit $ command "fix" paramPaths seek def = [notDirect $ noCommit $ command "fix" paramPaths seek
SectionMaintenance "fix up symlinks to point to annexed content"] SectionMaintenance "fix up symlinks to point to annexed content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFilesInGit $ whenAnnexed start] seek = withFilesInGit $ whenAnnexed start
{- Fixes the symlink to an annexed file. -} {- Fixes the symlink to an annexed file. -}
start :: FilePath -> (Key, Backend) -> CommandStart start :: FilePath -> (Key, Backend) -> CommandStart

View file

@ -26,9 +26,10 @@ forgetOptions = [dropDeadOption]
dropDeadOption :: Option dropDeadOption :: Option
dropDeadOption = Option.flag [] "drop-dead" "drop references to dead repositories" dropDeadOption = Option.flag [] "drop-dead" "drop references to dead repositories"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFlag dropDeadOption $ \dropdead -> seek ps = do
withNothing $ start dropdead] dropdead <- getOptionFlag dropDeadOption
withNothing (start dropdead) ps
start :: Bool -> CommandStart start :: Bool -> CommandStart
start dropdead = do start dropdead = do

View file

@ -20,8 +20,8 @@ def = [notDirect $ notBareRepo $
command "fromkey" (paramPair paramKey paramPath) seek command "fromkey" (paramPair paramKey paramPath) seek
SectionPlumbing "adds a file using a specific key"] SectionPlumbing "adds a file using a specific key"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start (keyname:file:[]) = do start (keyname:file:[]) = do

View file

@ -70,16 +70,17 @@ fsckOptions =
, incrementalScheduleOption , incrementalScheduleOption
] ++ keyOptions ] ++ keyOptions
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withIncremental $ \i -> i <- getIncremental
withKeyOptions (startKey i) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start from i (startKey i)
] (withFilesInGit $ whenAnnexed $ start from i)
ps
withIncremental :: (Incremental -> CommandSeek) -> CommandSeek getIncremental :: Annex Incremental
withIncremental = withValue $ do getIncremental = do
i <- maybe (return False) (checkschedule . parseDuration) i <- maybe (return False) (checkschedule . parseDuration)
=<< Annex.getField (Option.name incrementalScheduleOption) =<< Annex.getField (Option.name incrementalScheduleOption)
starti <- Annex.getFlag (Option.name startIncrementalOption) starti <- Annex.getFlag (Option.name startIncrementalOption)

View file

@ -25,8 +25,8 @@ def :: [Command]
def = [ notBareRepo $ command "fuzztest" paramNothing seek SectionPlumbing def = [ notBareRepo $ command "fuzztest" paramNothing seek SectionPlumbing
"generates fuzz test files"] "generates fuzz test files"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do

View file

@ -18,8 +18,8 @@ def = [dontCheck repoExists $ noCommit $
command "gcryptsetup" paramValue seek command "gcryptsetup" paramValue seek
SectionPlumbing "sets up gcrypt repository"] SectionPlumbing "sets up gcrypt repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withStrings start] seek = withStrings start
start :: String -> CommandStart start :: String -> CommandStart
start gcryptid = next $ next $ do start gcryptid = next $ next $ do

View file

@ -24,12 +24,13 @@ def = [withOptions getOptions $ command "get" paramPaths seek
getOptions :: [Option] getOptions :: [Option]
getOptions = fromOption : keyOptions getOptions = fromOption : keyOptions
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (startKeys from) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start from (startKeys from)
] (withFilesInGit $ whenAnnexed $ start from)
ps
start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
start from file (key, _) = start' expensivecheck from key (Just file) start from file (key, _) = start' expensivecheck from key (Just file)

View file

@ -19,8 +19,8 @@ def :: [Command]
def = [command "group" (paramPair paramRemote paramDesc) seek def = [command "group" (paramPair paramRemote paramDesc) seek
SectionSetup "add a repository to a group"] SectionSetup "add a repository to a group"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start (name:g:[]) = do start (name:g:[]) = do

View file

@ -26,8 +26,8 @@ def :: [Command]
def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $ def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $
command "help" paramNothing seek SectionQuery "display help"] command "help" paramNothing seek SectionQuery "display help"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start params = do start params = do

View file

@ -61,8 +61,10 @@ getDuplicateMode = gen
gen False False False True = SkipDuplicates gen False False False True = SkipDuplicates
gen _ _ _ _ = error "bad combination of --duplicate, --deduplicate, --clean-duplicates, --skip-duplicates" gen _ _ _ _ = error "bad combination of --duplicate, --deduplicate, --clean-duplicates, --skip-duplicates"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withValue getDuplicateMode $ \mode -> withPathContents $ start mode] seek ps = do
mode <- getDuplicateMode
withPathContents (start mode) ps
start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart
start mode (srcfile, destfile) = start mode (srcfile, destfile) =

View file

@ -41,11 +41,12 @@ def = [notBareRepo $ withOptions [templateOption, relaxedOption] $
templateOption :: Option templateOption :: Option
templateOption = Option.field [] "template" paramFormat "template for filenames" templateOption = Option.field [] "template" paramFormat "template for filenames"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField templateOption return $ \tmpl -> seek ps = do
withFlag relaxedOption $ \relaxed -> tmpl <- getOptionField templateOption return
withValue (getCache tmpl) $ \cache -> relaxed <- getOptionFlag relaxedOption
withStrings $ start relaxed cache] cache <- getCache tmpl
withStrings (start relaxed cache) ps
start :: Bool -> Cache -> URLString -> CommandStart start :: Bool -> Cache -> URLString -> CommandStart
start relaxed cache url = do start relaxed cache url = do

View file

@ -15,8 +15,8 @@ def :: [Command]
def = [noCommit $ command "inannex" (paramRepeating paramKey) seek def = [noCommit $ command "inannex" (paramRepeating paramKey) seek
SectionPlumbing "checks if keys are present in the annex"] SectionPlumbing "checks if keys are present in the annex"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withKeys start] seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart
start key = inAnnexSafe key >>= dispatch start key = inAnnexSafe key >>= dispatch

View file

@ -31,8 +31,8 @@ def = [notBareRepo $ noDaemonRunning $
command "indirect" paramNothing seek command "indirect" paramNothing seek
SectionSetup "switch repository to indirect mode"] SectionSetup "switch repository to indirect mode"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = ifM isDirect start = ifM isDirect

View file

@ -75,8 +75,8 @@ def = [noCommit $ withOptions [jsonOption] $
command "info" paramPaths seek SectionQuery command "info" paramPaths seek SectionQuery
"shows general information about the annex"] "shows general information about the annex"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [FilePath] -> CommandStart start :: [FilePath] -> CommandStart
start [] = do start [] = do

View file

@ -15,8 +15,8 @@ def :: [Command]
def = [dontCheck repoExists $ def = [dontCheck repoExists $
command "init" paramDesc seek SectionSetup "initialize git-annex"] command "init" paramDesc seek SectionSetup "initialize git-annex"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start ws = do start ws = do

View file

@ -24,8 +24,8 @@ def = [command "initremote"
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue) (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
seek SectionSetup "creates a special (non-git) remote"] seek SectionSetup "creates a special (non-git) remote"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start [] = error "Specify a name for the remote." start [] = error "Specify a name for the remote."

View file

@ -31,11 +31,11 @@ def = [noCommit $ withOptions [allrepos] $ command "list" paramPaths seek
allrepos :: Option allrepos :: Option
allrepos = Option.flag [] "allrepos" "show all repositories, not only remotes" allrepos = Option.flag [] "allrepos" "show all repositories, not only remotes"
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withValue getList $ withWords . startHeader list <- getList
, withValue getList $ withFilesInGit . whenAnnexed . start printHeader list
] withFilesInGit (whenAnnexed $ start list) ps
getList :: Annex [(UUID, RemoteName, TrustLevel)] getList :: Annex [(UUID, RemoteName, TrustLevel)]
getList = ifM (Annex.getFlag $ Option.name allrepos) getList = ifM (Annex.getFlag $ Option.name allrepos)
@ -58,10 +58,8 @@ getList = ifM (Annex.getFlag $ Option.name allrepos)
return $ sortBy (comparing snd3) $ return $ sortBy (comparing snd3) $
filter (\t -> thd3 t /= DeadTrusted) rs3 filter (\t -> thd3 t /= DeadTrusted) rs3
startHeader :: [(UUID, RemoteName, TrustLevel)] -> [String] -> CommandStart printHeader :: [(UUID, RemoteName, TrustLevel)] -> Annex ()
startHeader l _ = do printHeader l = liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l
liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l
stop
start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> (Key, Backend) -> CommandStart start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> (Key, Backend) -> CommandStart
start l file (key, _) = do start l file (key, _) = do

View file

@ -16,8 +16,10 @@ def :: [Command]
def = [notDirect $ command "lock" paramPaths seek SectionCommon def = [notDirect $ command "lock" paramPaths seek SectionCommon
"undo unlock command"] "undo unlock command"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFilesUnlocked start, withFilesUnlockedToBeCommitted start] seek ps = do
withFilesUnlocked start ps
withFilesUnlockedToBeCommitted start ps
start :: FilePath -> CommandStart start :: FilePath -> CommandStart
start file = do start file = do

View file

@ -53,12 +53,13 @@ passthruOptions = map odate ["since", "after", "until", "before"] ++
gourceOption :: Option gourceOption :: Option
gourceOption = Option.flag [] "gource" "format output for gource" gourceOption = Option.flag [] "gource" "format output for gource"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withValue Remote.uuidDescriptions $ \m -> seek ps = do
withValue (liftIO getCurrentTimeZone) $ \zone -> m <- Remote.uuidDescriptions
withValue (concat <$> mapM getoption passthruOptions) $ \os -> zone <- liftIO getCurrentTimeZone
withFlag gourceOption $ \gource -> os <- concat <$> mapM getoption passthruOptions
withFilesInGit $ whenAnnexed $ start m zone os gource] gource <- getOptionFlag gourceOption
withFilesInGit (whenAnnexed $ start m zone os gource) ps
where where
getoption o = maybe [] (use o) <$> getoption o = maybe [] (use o) <$>
Annex.getField (Option.name o) Annex.getField (Option.name o)

View file

@ -17,8 +17,8 @@ def = [notBareRepo $ noCommit $ noMessages $
command "lookupkey" (paramRepeating paramFile) seek command "lookupkey" (paramRepeating paramFile) seek
SectionPlumbing "looks up key used for file"] SectionPlumbing "looks up key used for file"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withStrings start] seek = withStrings start
start :: String -> CommandStart start :: String -> CommandStart
start file = do start file = do

View file

@ -31,8 +31,8 @@ def = [dontCheck repoExists $
command "map" paramNothing seek SectionQuery command "map" paramNothing seek SectionQuery
"generate map of repositories"] "generate map of repositories"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do

View file

@ -17,11 +17,10 @@ def :: [Command]
def = [command "merge" paramNothing seek SectionMaintenance def = [command "merge" paramNothing seek SectionMaintenance
"automatically merge changes from remotes"] "automatically merge changes from remotes"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withNothing mergeBranch withNothing mergeBranch ps
, withNothing mergeSynced withNothing mergeSynced ps
]
mergeBranch :: CommandStart mergeBranch :: CommandStart
mergeBranch = do mergeBranch = do

View file

@ -22,8 +22,8 @@ def = [notDirect $
command "migrate" paramPaths seek command "migrate" paramPaths seek
SectionUtility "switch data to different backend"] SectionUtility "switch data to different backend"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFilesInGit $ whenAnnexed start] seek = withFilesInGit $ whenAnnexed start
start :: FilePath -> (Key, Backend) -> CommandStart start :: FilePath -> (Key, Backend) -> CommandStart
start file (key, oldbackend) = do start file (key, oldbackend) = do

View file

@ -22,13 +22,14 @@ def = [withOptions (fromToOptions ++ keyOptions) $
command "mirror" paramPaths seek command "mirror" paramPaths seek
SectionCommon "mirror content of files to/from another repository"] SectionCommon "mirror content of files to/from another repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField toOption Remote.byNameWithUUID $ \to -> to <- getOptionField toOption Remote.byNameWithUUID
withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (startKey Nothing to from Nothing) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start to from (startKey Nothing to from Nothing)
] (withFilesInGit $ whenAnnexed $ start to from)
ps
start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
start to from file (key, _backend) = do start to from file (key, _backend) = do

View file

@ -26,13 +26,14 @@ def = [withOptions moveOptions $ command "move" paramPaths seek
moveOptions :: [Option] moveOptions :: [Option]
moveOptions = fromToOptions ++ keyOptions moveOptions = fromToOptions ++ keyOptions
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField toOption Remote.byNameWithUUID $ \to -> to <- getOptionField toOption Remote.byNameWithUUID
withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (startKey to from True) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start to from True (startKey to from True)
] (withFilesInGit $ whenAnnexed $ start to from True)
ps
start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> (Key, Backend) -> CommandStart
start to from move file (key, _) = start' to from move (Just file) key start to from move file (key, _) = start' to from move (Just file) key

View file

@ -9,6 +9,7 @@ module Command.PreCommit where
import Common.Annex import Common.Annex
import Command import Command
import Config
import qualified Command.Add import qualified Command.Add
import qualified Command.Fix import qualified Command.Fix
import Annex.Direct import Annex.Direct
@ -17,19 +18,20 @@ def :: [Command]
def = [command "pre-commit" paramPaths seek SectionPlumbing def = [command "pre-commit" paramPaths seek SectionPlumbing
"run by git pre-commit hook"] "run by git pre-commit hook"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = ifM isDirect
-- fix symlinks to files being committed
[ whenNotDirect $ withFilesToBeCommitted $ whenAnnexed Command.Fix.start
-- inject unlocked files into the annex
, whenNotDirect $ withFilesUnlockedToBeCommitted startIndirect
-- update direct mode mappings for committed files -- update direct mode mappings for committed files
, whenDirect $ withWords startDirect ( withWords startDirect ps
] , do
-- fix symlinks to files being committed
withFilesToBeCommitted (whenAnnexed Command.Fix.start) ps
-- inject unlocked files into the annex
withFilesUnlockedToBeCommitted startIndirect ps
)
startIndirect :: FilePath -> CommandStart startIndirect :: FilePath -> CommandStart
startIndirect file = next $ do startIndirect file = next $ do
unlessM (doCommand $ Command.Add.start file) $ unlessM (callCommand $ Command.Add.start file) $
error $ "failed to add " ++ file ++ "; canceling commit" error $ "failed to add " ++ file ++ "; canceling commit"
next $ return True next $ return True

View file

@ -22,8 +22,8 @@ def = [notDirect $ command "rekey"
(paramOptional $ paramRepeating $ paramPair paramPath paramKey) (paramOptional $ paramRepeating $ paramPair paramPath paramKey)
seek SectionPlumbing "change keys used for files"] seek SectionPlumbing "change keys used for files"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withPairs start] seek = withPairs start
start :: (FilePath, String) -> CommandStart start :: (FilePath, String) -> CommandStart
start (file, keyname) = ifAnnexed file go stop start (file, keyname) = ifAnnexed file go stop

View file

@ -26,8 +26,8 @@ def :: [Command]
def = [noCommit $ command "recvkey" paramKey seek def = [noCommit $ command "recvkey" paramKey seek
SectionPlumbing "runs rsync in server mode to receive content"] SectionPlumbing "runs rsync in server mode to receive content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withKeys start] seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart
start key = ifM (inAnnex key) start key = ifM (inAnnex key)

View file

@ -17,8 +17,8 @@ def :: [Command]
def = [command "reinject" (paramPair "SRC" "DEST") seek def = [command "reinject" (paramPair "SRC" "DEST") seek
SectionUtility "sets content of annexed file"] SectionUtility "sets content of annexed file"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [FilePath] -> CommandStart start :: [FilePath] -> CommandStart
start (src:dest:[]) start (src:dest:[])

View file

@ -20,8 +20,8 @@ def :: [Command]
def = [noCommit $ dontCheck repoExists $ def = [noCommit $ dontCheck repoExists $
command "repair" paramNothing seek SectionMaintenance "recover broken git repository"] command "repair" paramNothing seek SectionMaintenance "recover broken git repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = next $ next $ runRepair =<< Annex.getState Annex.force start = next $ next $ runRepair =<< Annex.getState Annex.force

View file

@ -16,8 +16,8 @@ def = [notBareRepo $
command "rmurl" (paramPair paramFile paramUrl) seek command "rmurl" (paramPair paramFile paramUrl) seek
SectionCommon "record file is not available at url"] SectionCommon "record file is not available at url"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withPairs start] seek = withPairs start
start :: (FilePath, String) -> CommandStart start :: (FilePath, String) -> CommandStart
start (file, url) = flip whenAnnexed file $ \_ (key, _) -> do start (file, url) = flip whenAnnexed file $ \_ (key, _) -> do

View file

@ -21,8 +21,8 @@ def :: [Command]
def = [command "schedule" (paramPair paramRemote (paramOptional paramExpression)) seek def = [command "schedule" (paramPair paramRemote (paramOptional paramExpression)) seek
SectionSetup "get or set scheduled jobs"] SectionSetup "get or set scheduled jobs"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start = parse start = parse

View file

@ -16,8 +16,8 @@ def :: [Command]
def = [command "semitrust" (paramRepeating paramRemote) seek def = [command "semitrust" (paramRepeating paramRemote) seek
SectionSetup "return repository to default trust level"] SectionSetup "return repository to default trust level"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start ws = do start ws = do

View file

@ -20,8 +20,8 @@ def :: [Command]
def = [noCommit $ command "sendkey" paramKey seek def = [noCommit $ command "sendkey" paramKey seek
SectionPlumbing "runs rsync in server mode to send content"] SectionPlumbing "runs rsync in server mode to send content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withKeys start] seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart
start key = do start key = do

View file

@ -22,10 +22,8 @@ def = [notBareRepo $ noCommit $ noMessages $ withOptions [jsonOption] $
command "status" paramPaths seek SectionCommon command "status" paramPaths seek SectionCommon
"show the working tree status"] "show the working tree status"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek = withWords start
[ withWords start
]
start :: [FilePath] -> CommandStart start :: [FilePath] -> CommandStart
start [] = do start [] = do

View file

@ -47,7 +47,7 @@ import Control.Concurrent.MVar
def :: [Command] def :: [Command]
def = [withOptions syncOptions $ def = [withOptions syncOptions $
command "sync" (paramOptional (paramRepeating paramRemote)) command "sync" (paramOptional (paramRepeating paramRemote))
[seek] SectionCommon "synchronize local repository with remotes"] seek SectionCommon "synchronize local repository with remotes"]
syncOptions :: [Option] syncOptions :: [Option]
syncOptions = [ contentOption ] syncOptions = [ contentOption ]
@ -55,7 +55,6 @@ syncOptions = [ contentOption ]
contentOption :: Option contentOption :: Option
contentOption = Option.flag [] "content" "also transfer file contents" contentOption = Option.flag [] "content" "also transfer file contents"
-- syncing involves several operations, any of which can independently fail
seek :: CommandSeek seek :: CommandSeek
seek rs = do seek rs = do
prepMerge prepMerge
@ -78,20 +77,16 @@ seek rs = do
remotes <- syncRemotes rs remotes <- syncRemotes rs
let gitremotes = filter Remote.gitSyncableRemote remotes let gitremotes = filter Remote.gitSyncableRemote remotes
synccontent <- ifM (Annex.getFlag $ Option.name contentOption) -- Syncing involves many actions, any of which can independently
( withFilesInGit (whenAnnexed $ syncContent remotes) [] -- fail, without preventing the others from running.
, return [] seekActions $ return [ commit ]
) seekActions $ return [ withbranch mergeLocal ]
seekActions $ return $ map (withbranch . pullRemote) gitremotes
return $ concat seekActions $ return [ mergeAnnex ]
[ [ commit ] whenM (Annex.getFlag $ Option.name contentOption) $
, [ withbranch mergeLocal ] withFilesInGit (whenAnnexed $ syncContent remotes) []
, map (withbranch . pullRemote) gitremotes seekActions $ return $ [ withbranch pushLocal ]
, [ mergeAnnex ] seekActions $ return $ map (withbranch . pushRemote) gitremotes
, synccontent
, [ withbranch pushLocal ]
, map (withbranch . pushRemote) gitremotes
]
{- Merging may delete the current directory, so go to the top {- Merging may delete the current directory, so go to the top
- of the repo. This also means that sync always acts on all files in the - of the repo. This also means that sync always acts on all files in the

View file

@ -16,8 +16,8 @@ def = [ noRepo startIO $ dontCheck repoExists $
command "test" paramNothing seek SectionPlumbing command "test" paramNothing seek SectionPlumbing
"run built-in test suite"] "run built-in test suite"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
{- We don't actually run the test suite here because of a dependency loop. {- We don't actually run the test suite here because of a dependency loop.
- The main program notices when the command is test and runs it; this - The main program notices when the command is test and runs it; this

View file

@ -19,8 +19,8 @@ def :: [Command]
def = [noCommit $ command "transferinfo" paramKey seek SectionPlumbing def = [noCommit $ command "transferinfo" paramKey seek SectionPlumbing
"updates sender on number of bytes of content received"] "updates sender on number of bytes of content received"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
{- Security: {- Security:
- -

View file

@ -28,11 +28,12 @@ transferKeyOptions = fileOption : fromToOptions
fileOption :: Option fileOption :: Option
fileOption = Option.field [] "file" paramFile "the associated file" fileOption = Option.field [] "file" paramFile "the associated file"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField toOption Remote.byNameWithUUID $ \to -> seek ps = do
withField fromOption Remote.byNameWithUUID $ \from -> to <- getOptionField toOption Remote.byNameWithUUID
withField fileOption return $ \file -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeys $ start to from file] file <- getOptionField fileOption return
withKeys (start to from file) ps
start :: Maybe Remote -> Maybe Remote -> AssociatedFile -> Key -> CommandStart start :: Maybe Remote -> Maybe Remote -> AssociatedFile -> Key -> CommandStart
start to from file key = start to from file key =

View file

@ -25,8 +25,8 @@ def :: [Command]
def = [command "transferkeys" paramNothing seek def = [command "transferkeys" paramNothing seek
SectionPlumbing "transfers keys"] SectionPlumbing "transfers keys"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = withHandles $ \(readh, writeh) -> do start = withHandles $ \(readh, writeh) -> do

View file

@ -16,8 +16,8 @@ def :: [Command]
def = [command "trust" (paramRepeating paramRemote) seek def = [command "trust" (paramRepeating paramRemote) seek
SectionSetup "trust a repository"] SectionSetup "trust a repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start ws = do start ws = do

View file

@ -23,8 +23,8 @@ def :: [Command]
def = [command "unannex" paramPaths seek SectionUtility def = [command "unannex" paramPaths seek SectionUtility
"undo accidential add command"] "undo accidential add command"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFilesInGit $ whenAnnexed start] seek = withFilesInGit $ whenAnnexed start
start :: FilePath -> (Key, Backend) -> CommandStart start :: FilePath -> (Key, Backend) -> CommandStart
start file (key, _) = stopUnless (inAnnex key) $ do start file (key, _) = stopUnless (inAnnex key) $ do

View file

@ -19,8 +19,8 @@ def :: [Command]
def = [command "ungroup" (paramPair paramRemote paramDesc) seek def = [command "ungroup" (paramPair paramRemote paramDesc) seek
SectionSetup "remove a repository from a group"] SectionSetup "remove a repository from a group"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start (name:g:[]) = do start (name:g:[]) = do

View file

@ -34,12 +34,11 @@ check = do
revhead = inRepo $ Git.Command.pipeReadStrict revhead = inRepo $ Git.Command.pipeReadStrict
[Params "rev-parse --abbrev-ref HEAD"] [Params "rev-parse --abbrev-ref HEAD"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withFilesNotInGit $ whenAnnexed startCheckIncomplete withFilesNotInGit (whenAnnexed startCheckIncomplete) ps
, withFilesInGit $ whenAnnexed Command.Unannex.start withFilesInGit (whenAnnexed Command.Unannex.start) ps
, withNothing start finish
]
{- git annex symlinks that are not checked into git could be left by an {- git annex symlinks that are not checked into git could be left by an
- interrupted add. -} - interrupted add. -}
@ -50,8 +49,8 @@ startCheckIncomplete file _ = error $ unlines
, "Not continuing with uninit; either delete or git annex add the file and retry." , "Not continuing with uninit; either delete or git annex add the file and retry."
] ]
start :: CommandStart finish :: Annex ()
start = next $ next $ do finish = do
annexdir <- fromRepo gitAnnexDir annexdir <- fromRepo gitAnnexDir
annexobjectdir <- fromRepo gitAnnexObjectDir annexobjectdir <- fromRepo gitAnnexObjectDir
leftovers <- removeUnannexed =<< getKeysPresent leftovers <- removeUnannexed =<< getKeysPresent

View file

@ -20,8 +20,8 @@ def =
where where
c n = notDirect . command n paramPaths seek SectionCommon c n = notDirect . command n paramPaths seek SectionCommon
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFilesInGit $ whenAnnexed start] seek = withFilesInGit $ whenAnnexed start
{- The unlock subcommand replaces the symlink with a copy of the file's {- The unlock subcommand replaces the symlink with a copy of the file's
- content. -} - content. -}

View file

@ -16,8 +16,8 @@ def :: [Command]
def = [command "untrust" (paramRepeating paramRemote) seek def = [command "untrust" (paramRepeating paramRemote) seek
SectionSetup "do not trust a repository"] SectionSetup "do not trust a repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start ws = do start ws = do

View file

@ -45,8 +45,8 @@ def = [withOptions [fromOption] $ command "unused" paramNothing seek
fromOption :: Option fromOption :: Option
fromOption = Option.field ['f'] "from" paramRemote "remote to check for unused content" fromOption = Option.field ['f'] "from" paramRemote "remote to check for unused content"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
{- Finds unused content in the annex. -} {- Finds unused content in the annex. -}
start :: CommandStart start :: CommandStart
@ -326,14 +326,14 @@ data UnusedMaps = UnusedMaps
, unusedTmpMap :: UnusedMap , unusedTmpMap :: UnusedMap
} }
{- Read unused logs once, and pass the maps to each start action. -}
withUnusedMaps :: (UnusedMaps -> Int -> CommandStart) -> CommandSeek withUnusedMaps :: (UnusedMaps -> Int -> CommandStart) -> CommandSeek
withUnusedMaps a params = do withUnusedMaps a params = do
unused <- readUnusedLog "" unused <- readUnusedLog ""
unusedbad <- readUnusedLog "bad" unusedbad <- readUnusedLog "bad"
unusedtmp <- readUnusedLog "tmp" unusedtmp <- readUnusedLog "tmp"
let m = unused `M.union` unusedbad `M.union` unusedtmp let m = unused `M.union` unusedbad `M.union` unusedtmp
return $ map (a $ UnusedMaps unused unusedbad unusedtmp) $ let unusedmaps = UnusedMaps unused unusedbad unusedtmp
seekActions $ return $ map (a unusedmaps) $
concatMap (unusedSpec m) params concatMap (unusedSpec m) params
unusedSpec :: UnusedMap -> String -> [Int] unusedSpec :: UnusedMap -> String -> [Int]
@ -349,8 +349,8 @@ unusedSpec m spec
_ -> badspec _ -> badspec
badspec = error $ "Expected number or range, not \"" ++ spec ++ "\"" badspec = error $ "Expected number or range, not \"" ++ spec ++ "\""
{- Start action for unused content. Finds the number in the maps, and {- Seek action for unused content. Finds the number in the maps, and
- calls either of 3 actions, depending on the type of unused file. -} - calls one of 3 actions, depending on the type of unused file. -}
startUnused :: String startUnused :: String
-> (Key -> CommandPerform) -> (Key -> CommandPerform)
-> (Key -> CommandPerform) -> (Key -> CommandPerform)

View file

@ -16,8 +16,8 @@ def = [dontCheck repoExists $ -- because an old version may not seem to exist
command "upgrade" paramNothing seek command "upgrade" paramNothing seek
SectionMaintenance "upgrade repository layout"] SectionMaintenance "upgrade repository layout"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do

View file

@ -21,8 +21,8 @@ def :: [Command]
def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $ def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $
command "version" paramNothing seek SectionQuery "show version info"] command "version" paramNothing seek SectionQuery "show version info"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do

View file

@ -30,8 +30,8 @@ def :: [Command]
def = [command "vicfg" paramNothing seek def = [command "vicfg" paramNothing seek
SectionSetup "edit git-annex's configuration"] SectionSetup "edit git-annex's configuration"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do

View file

@ -20,8 +20,8 @@ def :: [Command]
def = [command "wanted" (paramPair paramRemote (paramOptional paramExpression)) seek def = [command "wanted" (paramPair paramRemote (paramOptional paramExpression)) seek
SectionSetup "get or set preferred content expression"] SectionSetup "get or set preferred content expression"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start = parse start = parse

View file

@ -17,10 +17,11 @@ def :: [Command]
def = [notBareRepo $ withOptions [foregroundOption, stopOption] $ def = [notBareRepo $ withOptions [foregroundOption, stopOption] $
command "watch" paramNothing seek SectionCommon "watch for changes"] command "watch" paramNothing seek SectionCommon "watch for changes"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFlag stopOption $ \stopdaemon -> seek ps = do
withFlag foregroundOption $ \foreground -> stopdaemon <- getOptionFlag stopOption
withNothing $ start False foreground stopdaemon Nothing] foreground <- getOptionFlag foregroundOption
withNothing (start False foreground stopdaemon Nothing) ps
foregroundOption :: Option foregroundOption :: Option
foregroundOption = Option.flag [] "foreground" "do not daemonize" foregroundOption = Option.flag [] "foreground" "do not daemonize"

View file

@ -48,9 +48,10 @@ listenOption :: Option
listenOption = Option.field [] "listen" paramAddress listenOption = Option.field [] "listen" paramAddress
"accept connections to this address" "accept connections to this address"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField listenOption return $ \listenhost -> seek ps = do
withNothing $ start listenhost] listenhost <- getOptionField listenOption return
withNothing (start listenhost) ps
start :: Maybe HostName -> CommandStart start :: Maybe HostName -> CommandStart
start = start' True start = start' True
@ -107,7 +108,7 @@ startNoRepo _ = do
(d:_) -> do (d:_) -> do
setCurrentDirectory d setCurrentDirectory d
state <- Annex.new =<< Git.CurrentRepo.get state <- Annex.new =<< Git.CurrentRepo.get
void $ Annex.eval state $ doCommand $ void $ Annex.eval state $ callCommand $
start' False listenhost start' False listenhost
{- Run the webapp without a repository, which prompts the user, makes one, {- Run the webapp without a repository, which prompts the user, makes one,

View file

@ -20,9 +20,10 @@ def = [noCommit $ withOptions [jsonOption] $
command "whereis" paramPaths seek SectionQuery command "whereis" paramPaths seek SectionQuery
"lists repositories that have file content"] "lists repositories that have file content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withValue (remoteMap id) $ \m -> seek ps = do
withFilesInGit $ whenAnnexed $ start m] m <- remoteMap id
withFilesInGit (whenAnnexed $ start m) ps
start :: M.Map UUID Remote -> FilePath -> (Key, Backend) -> CommandStart start :: M.Map UUID Remote -> FilePath -> (Key, Backend) -> CommandStart
start remotemap file (key, _) = do start remotemap file (key, _) = do

View file

@ -16,8 +16,8 @@ def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $
command "xmppgit" paramNothing seek command "xmppgit" paramNothing seek
SectionPlumbing "git to XMPP relay"] SectionPlumbing "git to XMPP relay"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start _ = do start _ = do

View file

@ -104,7 +104,7 @@ builtin cmd dir params = do
Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath
where where
addrsyncopts opts seek k = setField "RsyncOptions" opts >> seek k addrsyncopts opts seek k = setField "RsyncOptions" opts >> seek k
newcmd opts c = c { cmdseek = map (addrsyncopts opts) (cmdseek c) } newcmd opts c = c { cmdseek = addrsyncopts opts (cmdseek c) }
external :: [String] -> IO () external :: [String] -> IO ()
external params = do external params = do

64
RunCommand.hs Normal file
View file

@ -0,0 +1,64 @@
{- git-annex running commands
-
- Copyright 2010-2014 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE BangPatterns #-}
module RunCommand where
import Common.Annex
import qualified Annex
import Types.Command
import qualified Annex.Queue
import Annex.Exception
{- Runs a command, starting with the check stage, and then
- the seek stage. Finishes by printing the number of commandActions that
- failed. -}
performCommand :: Command -> CmdParams -> Annex ()
performCommand Command { cmdseek = seek, cmdcheck = c, cmdname = name } params = do
mapM_ runCheck c
Annex.changeState $ \s -> s { Annex.errcounter = 0 }
seek params
showerrcount =<< Annex.getState Annex.errcounter
where
showerrcount 0 = noop
showerrcount cnt = error $ name ++ ": " ++ show cnt ++ " failed"
{- Runs one of the actions needed to perform a command.
- Individual actions can fail without stopping the whole command,
- including by throwing IO errors (but other errors terminate the whole
- command).
-
- This should only be run in the seek stage. -}
commandAction :: CommandStart -> Annex ()
commandAction a = handle =<< tryAnnexIO go
where
go = do
Annex.Queue.flushWhenFull
callCommand a
handle (Right True) = noop
handle (Right False) = incerr
handle (Left err) = do
showErr err
showEndFail
incerr
incerr = Annex.changeState $ \s ->
let ! c = Annex.errcounter s + 1
! s' = s { Annex.errcounter = c }
in s'
{- Runs a single command action through the start, perform and cleanup stages -}
callCommand :: CommandStart -> CommandCleanup
callCommand = start
where
start = stage $ maybe skip perform
perform = stage $ maybe failure cleanup
cleanup = stage $ status
stage = (=<<)
skip = return True
failure = showEndFail >> return False
status r = showEndResult r >> return r

82
Seek.hs
View file

@ -4,7 +4,7 @@
- the values a user passes to a command, and prepare actions operating - the values a user passes to a command, and prepare actions operating
- on them. - on them.
- -
- Copyright 2010-2013 Joey Hess <joey@kitenet.net> - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -23,23 +23,14 @@ import qualified Git.Command
import qualified Git.LsFiles as LsFiles import qualified Git.LsFiles as LsFiles
import qualified Limit import qualified Limit
import qualified Option import qualified Option
import Config
import Logs.Location import Logs.Location
import Logs.Unused import Logs.Unused
import Annex.CatFile import Annex.CatFile
import RunCommand
seekHelper :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> [FilePath] -> Annex [FilePath]
seekHelper a params = do
ll <- inRepo $ \g ->
runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) params
{- Show warnings only for files/directories that do not exist. -}
forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p ->
unlessM (isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)) $
fileNotFound p
return $ concat ll
withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek
withFilesInGit a params = prepFiltered a $ seekHelper LsFiles.inRepo params withFilesInGit a params = seekActions $ prepFiltered a $
seekHelper LsFiles.inRepo params
withFilesNotInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesNotInGit :: (FilePath -> CommandStart) -> CommandSeek
withFilesNotInGit a params = do withFilesNotInGit a params = do
@ -47,7 +38,8 @@ withFilesNotInGit a params = do
files <- filter (not . dotfile) <$> files <- filter (not . dotfile) <$>
seekunless (null ps && not (null params)) ps seekunless (null ps && not (null params)) ps
dotfiles <- seekunless (null dotps) dotps dotfiles <- seekunless (null dotps) dotps
prepFiltered a $ return $ concat $ segmentPaths params (files++dotfiles) seekActions $ prepFiltered a $
return $ concat $ segmentPaths params (files++dotfiles)
where where
(dotps, ps) = partition dotfile params (dotps, ps) = partition dotfile params
seekunless True _ = return [] seekunless True _ = return []
@ -57,7 +49,8 @@ withFilesNotInGit a params = do
liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g
withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek
withPathContents a params = map a . concat <$> liftIO (mapM get params) withPathContents a params = seekActions $
map a . concat <$> liftIO (mapM get params)
where where
get p = ifM (isDirectory <$> getFileStatus p) get p = ifM (isDirectory <$> getFileStatus p)
( map (\f -> (f, makeRelative (parentDir p) f)) ( map (\f -> (f, makeRelative (parentDir p) f))
@ -66,20 +59,20 @@ withPathContents a params = map a . concat <$> liftIO (mapM get params)
) )
withWords :: ([String] -> CommandStart) -> CommandSeek withWords :: ([String] -> CommandStart) -> CommandSeek
withWords a params = return [a params] withWords a params = seekActions $ return [a params]
withStrings :: (String -> CommandStart) -> CommandSeek withStrings :: (String -> CommandStart) -> CommandSeek
withStrings a params = return $ map a params withStrings a params = seekActions $ return $ map a params
withPairs :: ((String, String) -> CommandStart) -> CommandSeek withPairs :: ((String, String) -> CommandStart) -> CommandSeek
withPairs a params = return $ map a $ pairs [] params withPairs a params = seekActions $ return $ map a $ pairs [] params
where where
pairs c [] = reverse c pairs c [] = reverse c
pairs c (x:y:xs) = pairs ((x,y):c) xs pairs c (x:y:xs) = pairs ((x,y):c) xs
pairs _ _ = error "expected pairs" pairs _ _ = error "expected pairs"
withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek
withFilesToBeCommitted a params = prepFiltered a $ withFilesToBeCommitted a params = seekActions $ prepFiltered a $
seekHelper LsFiles.stagedNotDeleted params seekHelper LsFiles.stagedNotDeleted params
withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek
@ -94,7 +87,8 @@ withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged
- not some other sort of symlink. - not some other sort of symlink.
-} -}
withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> (FilePath -> CommandStart) -> CommandSeek
withFilesUnlocked' typechanged a params = prepFiltered a unlockedfiles withFilesUnlocked' typechanged a params = seekActions $
prepFiltered a unlockedfiles
where where
check f = liftIO (notSymlink f) <&&> check f = liftIO (notSymlink f) <&&>
(isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f) (isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f)
@ -102,32 +96,25 @@ withFilesUnlocked' typechanged a params = prepFiltered a unlockedfiles
{- Finds files that may be modified. -} {- Finds files that may be modified. -}
withFilesMaybeModified :: (FilePath -> CommandStart) -> CommandSeek withFilesMaybeModified :: (FilePath -> CommandStart) -> CommandSeek
withFilesMaybeModified a params = withFilesMaybeModified a params = seekActions $
prepFiltered a $ seekHelper LsFiles.modified params prepFiltered a $ seekHelper LsFiles.modified params
withKeys :: (Key -> CommandStart) -> CommandSeek withKeys :: (Key -> CommandStart) -> CommandSeek
withKeys a params = return $ map (a . parse) params withKeys a params = seekActions $ return $ map (a . parse) params
where where
parse p = fromMaybe (error "bad key") $ file2key p parse p = fromMaybe (error "bad key") $ file2key p
withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek {- Gets the value of a field options, which is fed into
withValue v a params = do - a conversion function.
r <- v
a r params
{- Modifies a seek action using the value of a field option, which is fed into
- a conversion function, and then is passed into the seek action.
- This ensures that the conversion function only runs once.
-} -}
withField :: Option -> (Maybe String -> Annex a) -> (a -> CommandSeek) -> CommandSeek getOptionField :: Option -> (Maybe String -> Annex a) -> Annex a
withField option converter = withValue $ getOptionField option converter = converter <=< Annex.getField $ Option.name option
converter <=< Annex.getField $ Option.name option
withFlag :: Option -> (Bool -> CommandSeek) -> CommandSeek getOptionFlag :: Option -> Annex Bool
withFlag option = withValue $ Annex.getFlag (Option.name option) getOptionFlag option = Annex.getFlag (Option.name option)
withNothing :: CommandStart -> CommandSeek withNothing :: CommandStart -> CommandSeek
withNothing a [] = return [a] withNothing a [] = seekActions $ return [a]
withNothing _ _ = error "This command takes no parameters." withNothing _ _ = error "This command takes no parameters."
{- If --all is specified, or in a bare repo, runs an action on all {- If --all is specified, or in a bare repo, runs an action on all
@ -159,7 +146,7 @@ withKeyOptions keyop fallbackop params = do
unless (null params) $ unless (null params) $
error "Cannot mix --all or --unused with file names." error "Cannot mix --all or --unused with file names."
matcher <- Limit.getMatcher matcher <- Limit.getMatcher
map (process matcher) <$> a seekActions $ map (process matcher) <$> a
process matcher k = ifM (matcher $ MatchingKey k) process matcher k = ifM (matcher $ MatchingKey k)
( keyop k , return Nothing) ( keyop k , return Nothing)
@ -171,11 +158,20 @@ prepFiltered a fs = do
process matcher f = ifM (matcher $ MatchingFile $ FileInfo f f) process matcher f = ifM (matcher $ MatchingFile $ FileInfo f f)
( a f , return Nothing ) ( a f , return Nothing )
seekActions :: Annex [CommandStart] -> Annex ()
seekActions gen = do
as <- gen
mapM_ commandAction as
seekHelper :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> [FilePath] -> Annex [FilePath]
seekHelper a params = do
ll <- inRepo $ \g ->
runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) params
{- Show warnings only for files/directories that do not exist. -}
forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p ->
unlessM (isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)) $
fileNotFound p
return $ concat ll
notSymlink :: FilePath -> IO Bool notSymlink :: FilePath -> IO Bool
notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
whenNotDirect :: CommandSeek -> CommandSeek
whenNotDirect a params = ifM isDirect ( return [] , a params )
whenDirect :: CommandSeek -> CommandSeek
whenDirect a params = ifM isDirect ( a params, return [] )

View file

@ -18,9 +18,9 @@ import Types
data CommandCheck = CommandCheck { idCheck :: Int, runCheck :: Annex () } data CommandCheck = CommandCheck { idCheck :: Int, runCheck :: Annex () }
{- b. The seek stage takes the parameters passed to the command, {- b. The seek stage takes the parameters passed to the command,
- looks through the repo to find the ones that are relevant - looks through the repo to find the ones that are relevant
- to that command (ie, new files to add), and generates - to that command (ie, new files to add), and runs commandAction
- a list of start stage actions. -} - to handle all necessary actions. -}
type CommandSeek = [String] -> Annex [CommandStart] type CommandSeek = [String] -> Annex ()
{- c. The start stage is run before anything is printed about the {- c. The start stage is run before anything is printed about the
- command, is passed some input, and can early abort it - command, is passed some input, and can early abort it
- if the input does not make sense. It should run quickly and - if the input does not make sense. It should run quickly and
@ -42,7 +42,7 @@ data Command = Command
, cmdnomessages :: Bool -- don't output normal messages , cmdnomessages :: Bool -- don't output normal messages
, cmdname :: String , cmdname :: String
, cmdparamdesc :: String -- description of params for usage , cmdparamdesc :: String -- description of params for usage
, cmdseek :: [CommandSeek] -- seek stage , cmdseek :: CommandSeek
, cmdsection :: CommandSection , cmdsection :: CommandSection
, cmddesc :: String -- description of command for usage , cmddesc :: String -- description of command for usage
} }