Merge branch 'optparse-applicative'

This commit is contained in:
Joey Hess 2015-07-13 13:25:57 -04:00
commit 36b37311e4
122 changed files with 2095 additions and 1549 deletions

View file

@ -57,7 +57,6 @@ import Types.UUID
import Types.FileMatcher import Types.FileMatcher
import Types.NumCopies import Types.NumCopies
import Types.LockCache import Types.LockCache
import Types.MetaData
import Types.DesktopNotify import Types.DesktopNotify
import Types.CleanupActions import Types.CleanupActions
#ifdef WITH_QUVI #ifdef WITH_QUVI
@ -121,7 +120,6 @@ data AnnexState = AnnexState
, lockcache :: LockCache , lockcache :: LockCache
, flags :: M.Map String Bool , flags :: M.Map String Bool
, fields :: M.Map String String , fields :: M.Map String String
, modmeta :: [ModMeta]
, cleanup :: M.Map CleanupAction (Annex ()) , cleanup :: M.Map CleanupAction (Annex ())
, sentinalstatus :: Maybe SentinalStatus , sentinalstatus :: Maybe SentinalStatus
, useragent :: Maybe String , useragent :: Maybe String
@ -166,7 +164,6 @@ newState c r = AnnexState
, lockcache = M.empty , lockcache = M.empty
, flags = M.empty , flags = M.empty
, fields = M.empty , fields = M.empty
, modmeta = []
, cleanup = M.empty , cleanup = M.empty
, sentinalstatus = Nothing , sentinalstatus = Nothing
, useragent = Nothing , useragent = Nothing

View file

@ -45,7 +45,7 @@ while (<>) {
if ($inNAME) { if ($inNAME) {
# make lexgrog happy # make lexgrog happy
s/^git-annex /git-annex-/; s/^git-annex (\w)/git-annex-$1/;
} }
if ($_ eq ".SH NAME\n") { if ($_ eq ".SH NAME\n") {
$inNAME=1; $inNAME=1;

View file

@ -1,6 +1,6 @@
{- git-annex command line parsing and dispatch {- git-annex command line parsing and dispatch
- -
- Copyright 2010-2012 Joey Hess <id@joeyh.name> - Copyright 2010-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -13,10 +13,11 @@ module CmdLine (
shutdown shutdown
) where ) where
import qualified Options.Applicative as O
import qualified Options.Applicative.Help as H
import qualified Control.Exception as E import qualified Control.Exception as E
import qualified Data.Map as M import qualified Data.Map as M
import Control.Exception (throw) import Control.Exception (throw)
import System.Console.GetOpt
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
import System.Posix.Signals import System.Posix.Signals
#endif #endif
@ -32,48 +33,81 @@ import Command
import Types.Messages import Types.Messages
{- Runs the passed command line. -} {- Runs the passed command line. -}
dispatch :: Bool -> CmdParams -> [Command] -> [Option] -> [(String, String)] -> String -> IO Git.Repo -> IO () dispatch :: Bool -> CmdParams -> [Command] -> [GlobalOption] -> [(String, String)] -> IO Git.Repo -> String -> String -> IO ()
dispatch fuzzyok allargs allcmds commonoptions fields header getgitrepo = do dispatch fuzzyok allargs allcmds globaloptions fields getgitrepo progname progdesc = do
setupConsole setupConsole
case getOptCmd args cmd commonoptions of go =<< (E.try getgitrepo :: IO (Either E.SomeException Git.Repo))
Right (flags, params) -> go flags params
=<< (E.try getgitrepo :: IO (Either E.SomeException Git.Repo))
Left parseerr -> error parseerr
where where
go flags params (Right g) = do go (Right g) = do
state <- Annex.new g state <- Annex.new g
Annex.eval state $ do Annex.eval state $ do
checkEnvironment checkEnvironment
when fuzzy $
inRepo $ autocorrect . Just
forM_ fields $ uncurry Annex.setField forM_ fields $ uncurry Annex.setField
(cmd, seek, globalconfig) <- parsewith cmdparser
(\a -> inRepo $ a . Just)
when (cmdnomessages cmd) $ when (cmdnomessages cmd) $
Annex.setOutput QuietOutput Annex.setOutput QuietOutput
sequence_ flags getParsed globalconfig
whenM (annexDebug <$> Annex.getGitConfig) $ whenM (annexDebug <$> Annex.getGitConfig) $
liftIO enableDebugOutput liftIO enableDebugOutput
startup startup
performCommandAction cmd params $ performCommandAction cmd seek $
shutdown $ cmdnocommit cmd shutdown $ cmdnocommit cmd
go _flags params (Left e) = do go (Left norepo) = do
when fuzzy $ (_, a, _globalconfig) <- parsewith
autocorrect =<< Git.Config.global (fromMaybe (throw norepo) . cmdnorepo)
maybe (throw e) (\a -> a params) (cmdnorepo cmd) (\a -> a =<< Git.Config.global)
err msg = msg ++ "\n\n" ++ usage header allcmds a
cmd = Prelude.head cmds
(fuzzy, cmds, name, args) = findCmd fuzzyok allargs allcmds err parsewith getparser ingitrepo =
autocorrect = Git.AutoCorrect.prepare name cmdname cmds case parseCmd progname progdesc globaloptions allargs allcmds getparser of
O.Failure _ -> do
-- parse failed, so fall back to
-- fuzzy matching, or to showing usage
when fuzzy $
ingitrepo autocorrect
liftIO (O.handleParseResult (parseCmd progname progdesc globaloptions correctedargs allcmds getparser))
res -> liftIO (O.handleParseResult res)
where
autocorrect = Git.AutoCorrect.prepare (fromJust inputcmdname) cmdname cmds
(fuzzy, cmds, inputcmdname, args) = findCmd fuzzyok allargs allcmds
name
| fuzzy = case cmds of
(c:_) -> Just (cmdname c)
_ -> inputcmdname
| otherwise = inputcmdname
correctedargs = case name of
Nothing -> allargs
Just n -> n:args
{- Parses command line, selecting one of the commands from the list. -}
parseCmd :: String -> String -> [GlobalOption] -> CmdParams -> [Command] -> (Command -> O.Parser v) -> O.ParserResult (Command, v, GlobalSetter)
parseCmd progname progdesc globaloptions allargs allcmds getparser =
O.execParserPure (O.prefs O.idm) pinfo allargs
where
pinfo = O.info (O.helper <*> subcmds) (O.progDescDoc (Just intro))
subcmds = O.hsubparser $ mconcat $ map mkcommand allcmds
mkcommand c = O.command (cmdname c) $ O.info (mkparser c) $ O.fullDesc
<> O.header (synopsis (progname ++ " " ++ cmdname c) (cmddesc c))
<> O.footer ("For details, run: " ++ progname ++ " help " ++ cmdname c)
mkparser c = (,,)
<$> pure c
<*> getparser c
<*> combineGlobalOptions globaloptions
synopsis n d = n ++ " - " ++ d
intro = mconcat $ concatMap (\l -> [H.text l, H.line])
(synopsis progname progdesc : commandList allcmds)
{- Parses command line params far enough to find the Command to run, and {- Parses command line params far enough to find the Command to run, and
- returns the remaining params. - returns the remaining params.
- Does fuzzy matching if necessary, which may result in multiple Commands. -} - Does fuzzy matching if necessary, which may result in multiple Commands. -}
findCmd :: Bool -> CmdParams -> [Command] -> (String -> String) -> (Bool, [Command], String, CmdParams) findCmd :: Bool -> CmdParams -> [Command] -> (Bool, [Command], Maybe String, CmdParams)
findCmd fuzzyok argv cmds err findCmd fuzzyok argv cmds
| isNothing name = error $ err "missing command" | not (null exactcmds) = ret (False, exactcmds)
| not (null exactcmds) = (False, exactcmds, fromJust name, args) | fuzzyok && not (null inexactcmds) = ret (True, inexactcmds)
| fuzzyok && not (null inexactcmds) = (True, inexactcmds, fromJust name, args) | otherwise = ret (False, [])
| otherwise = error $ err $ "unknown command " ++ fromJust name
where where
ret (fuzzy, matches) = (fuzzy, matches, name, args)
(name, args) = findname argv [] (name, args) = findname argv []
findname [] c = (Nothing, reverse c) findname [] c = (Nothing, reverse c)
findname (a:as) c findname (a:as) c
@ -84,18 +118,6 @@ findCmd fuzzyok argv cmds err
Nothing -> [] Nothing -> []
Just n -> Git.AutoCorrect.fuzzymatches n cmdname cmds Just n -> Git.AutoCorrect.fuzzymatches n cmdname cmds
{- Parses command line options, and returns actions to run to configure flags
- and the remaining parameters for the command. -}
getOptCmd :: CmdParams -> Command -> [Option] -> Either String ([Annex ()], CmdParams)
getOptCmd argv cmd commonoptions = check $
getOpt Permute (commonoptions ++ cmdoptions cmd) argv
where
check (flags, rest, []) = Right (flags, rest)
check (_, _, errs) = Left $ unlines
[ concat errs
, commandUsage cmd
]
{- Actions to perform each time ran. -} {- Actions to perform each time ran. -}
startup :: Annex () startup :: Annex ()
startup = startup =

View file

@ -22,11 +22,11 @@ import Data.Either
{- Runs a command, starting with the check stage, and then {- Runs a command, starting with the check stage, and then
- the seek stage. Finishes by running the continutation, and - the seek stage. Finishes by running the continutation, and
- then showing a count of any failures. -} - then showing a count of any failures. -}
performCommandAction :: Command -> CmdParams -> Annex () -> Annex () performCommandAction :: Command -> CommandSeek -> Annex () -> Annex ()
performCommandAction Command { cmdseek = seek, cmdcheck = c, cmdname = name } params cont = do performCommandAction Command { cmdcheck = c, cmdname = name } seek cont = do
mapM_ runCheck c mapM_ runCheck c
Annex.changeState $ \s -> s { Annex.errcounter = 0 } Annex.changeState $ \s -> s { Annex.errcounter = 0 }
seek params seek
finishCommandActions finishCommandActions
cont cont
showerrcount =<< Annex.getState Annex.errcounter showerrcount =<< Annex.getState Annex.errcounter

View file

@ -10,29 +10,42 @@ module CmdLine.Batch where
import Common.Annex import Common.Annex
import Command import Command
batchOption :: Option
batchOption = flagOption [] "batch" "enable batch mode"
data BatchMode = Batch | NoBatch data BatchMode = Batch | NoBatch
batchOption :: Parser BatchMode
batchOption = flag NoBatch Batch
( long "batch"
<> help "enable batch mode"
)
type Batchable t = BatchMode -> t -> CommandStart type Batchable t = BatchMode -> t -> CommandStart
-- A Batchable command can run in batch mode, or not. -- A Batchable command can run in batch mode, or not.
-- In batch mode, one line at a time is read, parsed, and a reply output to -- In batch mode, one line at a time is read, parsed, and a reply output to
-- stdout. In non batch mode, the command's parameters are parsed and -- stdout. In non batch mode, the command's parameters are parsed and
-- a reply output for each. -- a reply output for each.
batchable :: ((t -> CommandStart) -> CommandSeek) -> Batchable t -> CommandSeek batchable :: (opts -> String -> Annex Bool) -> Parser opts -> CmdParamsDesc -> CommandParser
batchable seeker starter params = ifM (getOptionFlag batchOption) batchable handler parser paramdesc = batchseeker <$> batchparser
( batchloop
, seeker (starter NoBatch) params
)
where where
batchloop = do batchparser = (,,)
<$> parser
<*> batchOption
<*> cmdParams paramdesc
batchseeker (opts, NoBatch, params) = mapM_ (go NoBatch opts) params
batchseeker (opts, Batch, _) = batchloop opts
batchloop opts = do
mp <- liftIO $ catchMaybeIO getLine mp <- liftIO $ catchMaybeIO getLine
case mp of case mp of
Nothing -> return () Nothing -> return ()
Just p -> do Just p -> do
seeker (starter Batch) [p] go Batch opts p
batchloop batchloop opts
go batchmode opts p =
unlessM (handler opts p) $
batchBadInput batchmode
-- bad input is indicated by an empty line in batch mode. In non batch -- bad input is indicated by an empty line in batch mode. In non batch
-- mode, exit on bad input. -- mode, exit on bad input.

View file

@ -1,6 +1,6 @@
{- git-annex main program {- git-annex main program
- -
- Copyright 2010-2014 Joey Hess <id@joeyh.name> - Copyright 2010-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -14,13 +14,16 @@ import CmdLine
import Command import Command
import Utility.Env import Utility.Env
import Annex.Ssh import Annex.Ssh
import Types.Test
import qualified Command.Help
import qualified Command.Add import qualified Command.Add
import qualified Command.Unannex import qualified Command.Unannex
import qualified Command.Drop import qualified Command.Drop
import qualified Command.Move import qualified Command.Move
import qualified Command.Copy import qualified Command.Copy
import qualified Command.Get import qualified Command.Get
import qualified Command.Fsck
import qualified Command.LookupKey import qualified Command.LookupKey
import qualified Command.ContentLocation import qualified Command.ContentLocation
import qualified Command.ExamineKey import qualified Command.ExamineKey
@ -46,7 +49,6 @@ import qualified Command.Init
import qualified Command.Describe import qualified Command.Describe
import qualified Command.InitRemote import qualified Command.InitRemote
import qualified Command.EnableRemote import qualified Command.EnableRemote
import qualified Command.Fsck
import qualified Command.Expire import qualified Command.Expire
import qualified Command.Repair import qualified Command.Repair
import qualified Command.Unused import qualified Command.Unused
@ -96,7 +98,6 @@ import qualified Command.Proxy
import qualified Command.DiffDriver import qualified Command.DiffDriver
import qualified Command.Undo import qualified Command.Undo
import qualified Command.Version import qualified Command.Version
import qualified Command.Help
#ifdef WITH_ASSISTANT #ifdef WITH_ASSISTANT
import qualified Command.Watch import qualified Command.Watch
import qualified Command.Assistant import qualified Command.Assistant
@ -117,14 +118,17 @@ import qualified Command.TestRemote
import System.Remote.Monitoring import System.Remote.Monitoring
#endif #endif
cmds :: [Command] cmds :: Parser TestOptions -> Maybe TestRunner -> [Command]
cmds = concat cmds testoptparser testrunner =
[ Command.Add.cmd [ Command.Help.cmd
, Command.Add.cmd
, Command.Get.cmd , Command.Get.cmd
, Command.Drop.cmd , Command.Drop.cmd
, Command.Move.cmd , Command.Move.cmd
, Command.Copy.cmd , Command.Copy.cmd
, Command.Fsck.cmd
, Command.Unlock.cmd , Command.Unlock.cmd
, Command.Unlock.editcmd
, Command.Lock.cmd , Command.Lock.cmd
, Command.Sync.cmd , Command.Sync.cmd
, Command.Mirror.cmd , Command.Mirror.cmd
@ -175,7 +179,6 @@ cmds = concat
, Command.VPop.cmd , Command.VPop.cmd
, Command.VCycle.cmd , Command.VCycle.cmd
, Command.Fix.cmd , Command.Fix.cmd
, Command.Fsck.cmd
, Command.Expire.cmd , Command.Expire.cmd
, Command.Repair.cmd , Command.Repair.cmd
, Command.Unused.cmd , Command.Unused.cmd
@ -200,7 +203,6 @@ cmds = concat
, Command.DiffDriver.cmd , Command.DiffDriver.cmd
, Command.Undo.cmd , Command.Undo.cmd
, Command.Version.cmd , Command.Version.cmd
, Command.Help.cmd
#ifdef WITH_ASSISTANT #ifdef WITH_ASSISTANT
, Command.Watch.cmd , Command.Watch.cmd
, Command.Assistant.cmd , Command.Assistant.cmd
@ -212,24 +214,25 @@ cmds = concat
#endif #endif
, Command.RemoteDaemon.cmd , Command.RemoteDaemon.cmd
#endif #endif
, Command.Test.cmd , Command.Test.cmd testoptparser testrunner
#ifdef WITH_TESTSUITE #ifdef WITH_TESTSUITE
, Command.FuzzTest.cmd , Command.FuzzTest.cmd
, Command.TestRemote.cmd , Command.TestRemote.cmd
#endif #endif
] ]
header :: String run :: Parser TestOptions -> Maybe TestRunner -> [String] -> IO ()
header = "git-annex command [option ...]" run testoptparser testrunner args = do
run :: [String] -> IO ()
run args = do
#ifdef WITH_EKG #ifdef WITH_EKG
_ <- forkServer "localhost" 4242 _ <- forkServer "localhost" 4242
#endif #endif
go envmodes go envmodes
where where
go [] = dispatch True args cmds gitAnnexOptions [] header Git.CurrentRepo.get go [] = dispatch True args
(cmds testoptparser testrunner)
gitAnnexGlobalOptions [] Git.CurrentRepo.get
"git-annex"
"manage files with git, without checking their contents in"
go ((v, a):rest) = maybe (go rest) a =<< getEnv v go ((v, a):rest) = maybe (go rest) a =<< getEnv v
envmodes = envmodes =
[ (sshOptionsEnv, runSshOptions args) [ (sshOptionsEnv, runSshOptions args)

View file

@ -1,4 +1,4 @@
{- git-annex options {- git-annex command-line option parsing
- -
- Copyright 2010-2015 Joey Hess <id@joeyh.name> - Copyright 2010-2015 Joey Hess <id@joeyh.name>
- -
@ -7,7 +7,7 @@
module CmdLine.GitAnnex.Options where module CmdLine.GitAnnex.Options where
import System.Console.GetOpt import Options.Applicative
import Common.Annex import Common.Annex
import qualified Git.Config import qualified Git.Config
@ -15,63 +15,155 @@ import Git.Types
import Types.TrustLevel import Types.TrustLevel
import Types.NumCopies import Types.NumCopies
import Types.Messages import Types.Messages
import Types.Key
import Types.Command
import Types.DeferredParse
import Types.DesktopNotify
import qualified Annex import qualified Annex
import qualified Remote import qualified Remote
import qualified Limit import qualified Limit
import qualified Limit.Wanted import qualified Limit.Wanted
import CmdLine.Option import CmdLine.Option
import CmdLine.Usage import CmdLine.Usage
import CmdLine.GlobalSetter
-- Options that are accepted by all git-annex sub-commands, -- Global options that are accepted by all git-annex sub-commands,
-- although not always used. -- although not always used.
gitAnnexOptions :: [Option] gitAnnexGlobalOptions :: [GlobalOption]
gitAnnexOptions = commonOptions ++ gitAnnexGlobalOptions = commonGlobalOptions ++
[ Option ['N'] ["numcopies"] (ReqArg setnumcopies paramNumber) [ globalSetter setnumcopies $ option auto
"override default number of copies" ( long "numcopies" <> short 'N' <> metavar paramNumber
, Option [] ["trust"] (trustArg Trusted) <> help "override default number of copies"
"override trust setting" <> hidden
, Option [] ["semitrust"] (trustArg SemiTrusted) )
"override trust setting back to default" , globalSetter (Remote.forceTrust Trusted) $ strOption
, Option [] ["untrust"] (trustArg UnTrusted) ( long "trust" <> metavar paramRemote
"override trust setting to untrusted" <> help "override trust setting"
, Option ['c'] ["config"] (ReqArg setgitconfig "NAME=VALUE") <> hidden
"override git configuration setting" )
, Option [] ["user-agent"] (ReqArg setuseragent paramName) , globalSetter (Remote.forceTrust SemiTrusted) $ strOption
"override default User-Agent" ( long "semitrust" <> metavar paramRemote
, Option [] ["trust-glacier"] (NoArg (Annex.setFlag "trustglacier")) <> help "override trust setting back to default"
"Trust Amazon Glacier inventory" <> hidden
)
, globalSetter (Remote.forceTrust UnTrusted) $ strOption
( long "untrust" <> metavar paramRemote
<> help "override trust setting to untrusted"
<> hidden
)
, globalSetter setgitconfig $ strOption
( long "config" <> short 'c' <> metavar "NAME=VALUE"
<> help "override git configuration setting"
<> hidden
)
, globalSetter setuseragent $ strOption
( long "user-agent" <> metavar paramName
<> help "override default User-Agent"
<> hidden
)
, globalFlag (Annex.setFlag "trustglacier")
( long "trust-glacier"
<> help "Trust Amazon Glacier inventory"
<> hidden
)
, globalFlag (setdesktopnotify mkNotifyFinish)
( long "notify-finish"
<> help "show desktop notification after transfer finishes"
<> hidden
)
, globalFlag (setdesktopnotify mkNotifyStart)
( long "notify-start"
<> help "show desktop notification after transfer completes"
<> hidden
)
] ]
where where
trustArg t = ReqArg (Remote.forceTrust t) paramRemote setnumcopies n = Annex.changeState $ \s -> s { Annex.forcenumcopies = Just $ NumCopies n }
setnumcopies v = maybe noop
(\n -> Annex.changeState $ \s -> s { Annex.forcenumcopies = Just $ NumCopies n })
(readish v)
setuseragent v = Annex.changeState $ \s -> s { Annex.useragent = Just v } setuseragent v = Annex.changeState $ \s -> s { Annex.useragent = Just v }
setgitconfig v = inRepo (Git.Config.store v) setgitconfig v = inRepo (Git.Config.store v)
>>= pure . (\r -> r { gitGlobalOpts = gitGlobalOpts r ++ [Param "-c", Param v] }) >>= pure . (\r -> r { gitGlobalOpts = gitGlobalOpts r ++ [Param "-c", Param v] })
>>= Annex.changeGitRepo >>= Annex.changeGitRepo
setdesktopnotify v = Annex.changeState $ \s -> s { Annex.desktopnotify = Annex.desktopnotify s <> v }
-- Options for matching on annexed keys, rather than work tree files. {- Parser that accepts all non-option params. -}
keyOptions :: [Option] cmdParams :: CmdParamsDesc -> Parser CmdParams
keyOptions = [ allOption, unusedOption, keyOption] cmdParams paramdesc = many $ argument str
( metavar paramdesc
-- Let bash completion complete files
<> action "file"
)
allOption :: Option parseAutoOption :: Parser Bool
allOption = Option ['A'] ["all"] (NoArg (Annex.setFlag "all")) parseAutoOption = switch
"operate on all versions of all files" ( long "auto" <> short 'a'
<> help "automatic mode"
)
unusedOption :: Option parseRemoteOption :: Parser RemoteName -> Parser (DeferredParse Remote)
unusedOption = Option ['U'] ["unused"] (NoArg (Annex.setFlag "unused")) parseRemoteOption p = DeferredParse . (fromJust <$$> Remote.byNameWithUUID) . Just <$> p
"operate on files found by last run of git-annex unused"
keyOption :: Option data FromToOptions
keyOption = Option [] ["key"] (ReqArg (Annex.setField "key") paramKey) = FromRemote (DeferredParse Remote)
"operate on specified key" | ToRemote (DeferredParse Remote)
incompleteOption :: Option instance DeferredParseClass FromToOptions where
incompleteOption = flagOption [] "incomplete" "resume previous downloads" finishParse (FromRemote v) = FromRemote <$> finishParse v
finishParse (ToRemote v) = ToRemote <$> finishParse v
parseFromToOptions :: Parser FromToOptions
parseFromToOptions =
(FromRemote <$> parseFromOption)
<|> (ToRemote <$> parseToOption)
parseFromOption :: Parser (DeferredParse Remote)
parseFromOption = parseRemoteOption $ strOption
( long "from" <> short 'f' <> metavar paramRemote
<> help "source remote"
)
parseToOption :: Parser (DeferredParse Remote)
parseToOption = parseRemoteOption $ strOption
( long "to" <> short 't' <> metavar paramRemote
<> help "destination remote"
)
-- Options for acting on keys, rather than work tree files.
data KeyOptions
= WantAllKeys
| WantUnusedKeys
| WantSpecificKey Key
| WantIncompleteKeys
parseKeyOptions :: Bool -> Parser KeyOptions
parseKeyOptions allowincomplete = if allowincomplete
then base
<|> flag' WantIncompleteKeys
( long "incomplete"
<> help "resume previous downloads"
)
else base
where
base = parseAllOption
<|> flag' WantUnusedKeys
( long "unused" <> short 'U'
<> help "operate on files found by last run of git-annex unused"
)
<|> (WantSpecificKey <$> option (str >>= parseKey)
( long "key" <> metavar paramKey
<> help "operate on specified key"
))
parseAllOption :: Parser KeyOptions
parseAllOption = flag' WantAllKeys
( long "all" <> short 'A'
<> help "operate on all versions of all files"
)
parseKey :: Monad m => String -> m Key
parseKey = maybe (fail "invalid key") return . file2key
-- Options to match properties of annexed files. -- Options to match properties of annexed files.
annexedMatchingOptions :: [Option] annexedMatchingOptions :: [GlobalOption]
annexedMatchingOptions = concat annexedMatchingOptions = concat
[ nonWorkTreeMatchingOptions' [ nonWorkTreeMatchingOptions'
, fileMatchingOptions' , fileMatchingOptions'
@ -80,84 +172,132 @@ annexedMatchingOptions = concat
] ]
-- Matching options that don't need to examine work tree files. -- Matching options that don't need to examine work tree files.
nonWorkTreeMatchingOptions :: [Option] nonWorkTreeMatchingOptions :: [GlobalOption]
nonWorkTreeMatchingOptions = nonWorkTreeMatchingOptions' ++ combiningOptions nonWorkTreeMatchingOptions = nonWorkTreeMatchingOptions' ++ combiningOptions
nonWorkTreeMatchingOptions' :: [Option] nonWorkTreeMatchingOptions' :: [GlobalOption]
nonWorkTreeMatchingOptions' = nonWorkTreeMatchingOptions' =
[ Option ['i'] ["in"] (ReqArg Limit.addIn paramRemote) [ globalSetter Limit.addIn $ strOption
"match files present in a remote" ( long "in" <> short 'i' <> metavar paramRemote
, Option ['C'] ["copies"] (ReqArg Limit.addCopies paramNumber) <> help "match files present in a remote"
"skip files with fewer copies" <> hidden
, Option [] ["lackingcopies"] (ReqArg (Limit.addLackingCopies False) paramNumber) )
"match files that need more copies" , globalSetter Limit.addCopies $ strOption
, Option [] ["approxlackingcopies"] (ReqArg (Limit.addLackingCopies True) paramNumber) ( long "copies" <> short 'C' <> metavar paramRemote
"match files that need more copies (faster)" <> help "skip files with fewer copies"
, Option ['B'] ["inbackend"] (ReqArg Limit.addInBackend paramName) <> hidden
"match files using a key-value backend" )
, Option [] ["inallgroup"] (ReqArg Limit.addInAllGroup paramGroup) , globalSetter (Limit.addLackingCopies False) $ strOption
"match files present in all remotes in a group" ( long "lackingcopies" <> metavar paramNumber
, Option [] ["metadata"] (ReqArg Limit.addMetaData "FIELD=VALUE") <> help "match files that need more copies"
"match files with attached metadata" <> hidden
, Option [] ["want-get"] (NoArg Limit.Wanted.addWantGet) )
"match files the repository wants to get" , globalSetter (Limit.addLackingCopies True) $ strOption
, Option [] ["want-drop"] (NoArg Limit.Wanted.addWantDrop) ( long "approxlackingcopies" <> metavar paramNumber
"match files the repository wants to drop" <> help "match files that need more copies (faster)"
<> hidden
)
, globalSetter Limit.addInBackend $ strOption
( long "inbackend" <> short 'B' <> metavar paramName
<> help "match files using a key-value backend"
<> hidden
)
, globalSetter Limit.addInAllGroup $ strOption
( long "inallgroup" <> metavar paramGroup
<> help "match files present in all remotes in a group"
<> hidden
)
, globalSetter Limit.addMetaData $ strOption
( long "metadata" <> metavar "FIELD=VALUE"
<> help "match files with attached metadata"
<> hidden
)
, globalFlag Limit.Wanted.addWantGet
( long "want-get"
<> help "match files the repository wants to get"
<> hidden
)
, globalFlag Limit.Wanted.addWantDrop
( long "want-drop"
<> help "match files the repository wants to drop"
<> hidden
)
] ]
-- Options to match files which may not yet be annexed. -- Options to match files which may not yet be annexed.
fileMatchingOptions :: [Option] fileMatchingOptions :: [GlobalOption]
fileMatchingOptions = fileMatchingOptions' ++ combiningOptions fileMatchingOptions = fileMatchingOptions' ++ combiningOptions
fileMatchingOptions' :: [Option] fileMatchingOptions' :: [GlobalOption]
fileMatchingOptions' = fileMatchingOptions' =
[ Option ['x'] ["exclude"] (ReqArg Limit.addExclude paramGlob) [ globalSetter Limit.addExclude $ strOption
"skip files matching the glob pattern" ( long "exclude" <> short 'x' <> metavar paramGlob
, Option ['I'] ["include"] (ReqArg Limit.addInclude paramGlob) <> help "skip files matching the glob pattern"
"limit to files matching the glob pattern" <> hidden
, Option [] ["largerthan"] (ReqArg Limit.addLargerThan paramSize) )
"match files larger than a size" , globalSetter Limit.addInclude $ strOption
, Option [] ["smallerthan"] (ReqArg Limit.addSmallerThan paramSize) ( long "include" <> short 'I' <> metavar paramGlob
"match files smaller than a size" <> help "limit to files matching the glob pattern"
<> hidden
)
, globalSetter Limit.addLargerThan $ strOption
( long "largerthan" <> metavar paramSize
<> help "match files larger than a size"
<> hidden
)
, globalSetter Limit.addSmallerThan $ strOption
( long "smallerthan" <> metavar paramSize
<> help "match files smaller than a size"
<> hidden
)
] ]
combiningOptions :: [Option] combiningOptions :: [GlobalOption]
combiningOptions = combiningOptions =
[ longopt "not" "negate next option" [ longopt "not" "negate next option"
, longopt "and" "both previous and next option must match" , longopt "and" "both previous and next option must match"
, longopt "or" "either previous or next option must match" , longopt "or" "either previous or next option must match"
, shortopt "(" "open group of options" , shortopt '(' "open group of options"
, shortopt ")" "close group of options" , shortopt ')' "close group of options"
] ]
where where
longopt o = Option [] [o] $ NoArg $ Limit.addToken o longopt o h = globalFlag (Limit.addToken o) ( long o <> help h <> hidden )
shortopt o = Option o [] $ NoArg $ Limit.addToken o shortopt o h = globalFlag (Limit.addToken [o]) ( short o <> help h <> hidden )
fromOption :: Option jsonOption :: GlobalOption
fromOption = fieldOption ['f'] "from" paramRemote "source remote" jsonOption = globalFlag (Annex.setOutput JSONOutput)
( long "json" <> short 'j'
<> help "enable JSON output"
<> hidden
)
toOption :: Option jobsOption :: GlobalOption
toOption = fieldOption ['t'] "to" paramRemote "destination remote" jobsOption = globalSetter (Annex.setOutput . ParallelOutput) $
option auto
( long "jobs" <> short 'J' <> metavar paramNumber
<> help "enable concurrent jobs"
<> hidden
)
fromToOptions :: [Option] timeLimitOption :: GlobalOption
fromToOptions = [fromOption, toOption] timeLimitOption = globalSetter Limit.addTimeLimit $ strOption
( long "time-limit" <> short 'T' <> metavar paramTime
<> help "stop after the specified amount of time"
<> hidden
)
jsonOption :: Option data DaemonOptions = DaemonOptions
jsonOption = Option ['j'] ["json"] (NoArg (Annex.setOutput JSONOutput)) { foregroundDaemonOption :: Bool
"enable JSON output" , stopDaemonOption :: Bool
}
jobsOption :: Option parseDaemonOptions :: Parser DaemonOptions
jobsOption = Option ['J'] ["jobs"] (ReqArg set paramNumber) parseDaemonOptions = DaemonOptions
"enable concurrent jobs" <$> switch
where ( long "foreground"
set s = case readish s of <> help "do not daemonize"
Nothing -> error "Bad --jobs number" )
Just n -> Annex.setOutput (ParallelOutput n) <*> switch
( long "stop"
timeLimitOption :: Option <> help "stop daemon"
timeLimitOption = Option ['T'] ["time-limit"] )
(ReqArg Limit.addTimeLimit paramTime)
"stop after the specified amount of time"
autoOption :: Option
autoOption = flagOption ['a'] "auto" "automatic mode"

View file

@ -8,15 +8,14 @@
module CmdLine.GitAnnexShell where module CmdLine.GitAnnexShell where
import System.Environment import System.Environment
import System.Console.GetOpt
import Common.Annex import Common.Annex
import qualified Git.Construct import qualified Git.Construct
import qualified Git.Config import qualified Git.Config
import CmdLine import CmdLine
import CmdLine.GlobalSetter
import Command import Command
import Annex.UUID import Annex.UUID
import Annex (setField)
import CmdLine.GitAnnexShell.Fields import CmdLine.GitAnnexShell.Fields
import Utility.UserInfo import Utility.UserInfo
import Remote.GCrypt (getGCryptUUID) import Remote.GCrypt (getGCryptUUID)
@ -34,7 +33,7 @@ import qualified Command.NotifyChanges
import qualified Command.GCryptSetup import qualified Command.GCryptSetup
cmds_readonly :: [Command] cmds_readonly :: [Command]
cmds_readonly = concat cmds_readonly =
[ gitAnnexShellCheck Command.ConfigList.cmd [ gitAnnexShellCheck Command.ConfigList.cmd
, gitAnnexShellCheck Command.InAnnex.cmd , gitAnnexShellCheck Command.InAnnex.cmd
, gitAnnexShellCheck Command.SendKey.cmd , gitAnnexShellCheck Command.SendKey.cmd
@ -43,7 +42,7 @@ cmds_readonly = concat
] ]
cmds_notreadonly :: [Command] cmds_notreadonly :: [Command]
cmds_notreadonly = concat cmds_notreadonly =
[ gitAnnexShellCheck Command.RecvKey.cmd [ gitAnnexShellCheck Command.RecvKey.cmd
, gitAnnexShellCheck Command.DropKey.cmd , gitAnnexShellCheck Command.DropKey.cmd
, gitAnnexShellCheck Command.Commit.cmd , gitAnnexShellCheck Command.Commit.cmd
@ -55,10 +54,13 @@ cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
where where
adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c } adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c }
options :: [OptDescr (Annex ())] globalOptions :: [GlobalOption]
options = commonOptions ++ globalOptions =
[ Option [] ["uuid"] (ReqArg checkUUID paramUUID) "local repository uuid" globalSetter checkUUID (strOption
] ( long "uuid" <> metavar paramUUID
<> help "local repository uuid"
))
: commonGlobalOptions
where where
checkUUID expected = getUUID >>= check checkUUID expected = getUUID >>= check
where where
@ -74,9 +76,6 @@ options = commonOptions ++
unexpected expected s = error $ unexpected expected s = error $
"expected repository UUID " ++ expected ++ " but found " ++ s "expected repository UUID " ++ expected ++ " but found " ++ s
header :: String
header = "git-annex-shell [-c] command [parameters ...] [option ...]"
run :: [String] -> IO () run :: [String] -> IO ()
run [] = failure run [] = failure
-- skip leading -c options, passed by eg, ssh -- skip leading -c options, passed by eg, ssh
@ -100,12 +99,12 @@ builtin cmd dir params = do
checkNotReadOnly cmd checkNotReadOnly cmd
checkDirectory $ Just dir checkDirectory $ Just dir
let (params', fieldparams, opts) = partitionParams params let (params', fieldparams, opts) = partitionParams params
fields = filter checkField $ parseFields fieldparams rsyncopts = ("RsyncOptions", unwords opts)
cmds' = map (newcmd $ unwords opts) cmds fields = rsyncopts : filter checkField (parseFields fieldparams)
dispatch False (cmd : params') cmds' options fields header mkrepo dispatch False (cmd : params') cmds globalOptions fields mkrepo
"git-annex-shell"
"Restricted login shell for git-annex only SSH access"
where where
addrsyncopts opts seek k = setField "RsyncOptions" opts >> seek k
newcmd opts c = c { cmdseek = addrsyncopts opts (cmdseek c) }
mkrepo = do mkrepo = do
r <- Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath r <- Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath
Git.Config.read r Git.Config.read r
@ -143,14 +142,16 @@ parseFields = map (separate (== '='))
{- Only allow known fields to be set, ignore others. {- Only allow known fields to be set, ignore others.
- Make sure that field values make sense. -} - Make sure that field values make sense. -}
checkField :: (String, String) -> Bool checkField :: (String, String) -> Bool
checkField (field, value) checkField (field, val)
| field == fieldName remoteUUID = fieldCheck remoteUUID value | field == fieldName remoteUUID = fieldCheck remoteUUID val
| field == fieldName associatedFile = fieldCheck associatedFile value | field == fieldName associatedFile = fieldCheck associatedFile val
| field == fieldName direct = fieldCheck direct value | field == fieldName direct = fieldCheck direct val
| otherwise = False | otherwise = False
failure :: IO () failure :: IO ()
failure = error $ "bad parameters\n\n" ++ usage header cmds failure = error $ "bad parameters\n\n" ++ usage h cmds
where
h = "git-annex-shell [-c] command [parameters ...] [option ...]"
checkNotLimited :: IO () checkNotLimited :: IO ()
checkNotLimited = checkEnv "GIT_ANNEX_SHELL_LIMITED" checkNotLimited = checkEnv "GIT_ANNEX_SHELL_LIMITED"
@ -200,8 +201,8 @@ checkEnv var = do
{- Modifies a Command to check that it is run in either a git-annex {- Modifies a Command to check that it is run in either a git-annex
- repository, or a repository with a gcrypt-id set. -} - repository, or a repository with a gcrypt-id set. -}
gitAnnexShellCheck :: [Command] -> [Command] gitAnnexShellCheck :: Command -> Command
gitAnnexShellCheck = map $ addCheck okforshell . dontCheck repoExists gitAnnexShellCheck = addCheck okforshell . dontCheck repoExists
where where
okforshell = unlessM (isInitialized <||> isJust . gcryptId <$> Annex.getGitConfig) $ okforshell = unlessM (isInitialized <||> isJust . gcryptId <$> Annex.getGitConfig) $
error "Not a git-annex or gcrypt repository." error "Not a git-annex or gcrypt repository."

24
CmdLine/GlobalSetter.hs Normal file
View file

@ -0,0 +1,24 @@
{- git-annex global options
-
- Copyright 2015 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module CmdLine.GlobalSetter where
import Types.DeferredParse
import Common
import Annex
import Options.Applicative
globalFlag :: Annex () -> Mod FlagFields GlobalSetter -> GlobalOption
globalFlag setter = flag' (DeferredParse setter)
globalSetter :: (v -> Annex ()) -> Parser v -> GlobalOption
globalSetter setter parser = DeferredParse . setter <$> parser
combineGlobalOptions :: [GlobalOption] -> Parser GlobalSetter
combineGlobalOptions l = DeferredParse . sequence_ . map getParsed
<$> many (foldl1 (<|>) l)

View file

@ -5,45 +5,55 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module CmdLine.Option ( module CmdLine.Option where
commonOptions,
flagOption,
fieldOption,
optionName,
optionParam,
ArgDescr(..),
OptDescr(..),
) where
import System.Console.GetOpt import Options.Applicative
import Common.Annex import Common.Annex
import CmdLine.Usage
import CmdLine.GlobalSetter
import qualified Annex import qualified Annex
import Types.Messages import Types.Messages
import Types.DesktopNotify import Types.DeferredParse
import CmdLine.Usage
-- Options accepted by both git-annex and git-annex-shell sub-commands. -- Global options accepted by both git-annex and git-annex-shell sub-commands.
commonOptions :: [Option] commonGlobalOptions :: [GlobalOption]
commonOptions = commonGlobalOptions =
[ Option [] ["force"] (NoArg (setforce True)) [ globalFlag (setforce True)
"allow actions that may lose annexed data" ( long "force"
, Option ['F'] ["fast"] (NoArg (setfast True)) <> help "allow actions that may lose annexed data"
"avoid slow operations" <> hidden
, Option ['q'] ["quiet"] (NoArg (Annex.setOutput QuietOutput)) )
"avoid verbose output" , globalFlag (setfast True)
, Option ['v'] ["verbose"] (NoArg (Annex.setOutput NormalOutput)) ( long "fast" <> short 'F'
"allow verbose output (default)" <> help "avoid slow operations"
, Option ['d'] ["debug"] (NoArg setdebug) <> hidden
"show debug messages" )
, Option [] ["no-debug"] (NoArg unsetdebug) , globalFlag (Annex.setOutput QuietOutput)
"don't show debug messages" ( long "quiet" <> short 'q'
, Option ['b'] ["backend"] (ReqArg setforcebackend paramName) <> help "avoid verbose output"
"specify key-value backend to use" <> hidden
, Option [] ["notify-finish"] (NoArg (setdesktopnotify mkNotifyFinish)) )
"show desktop notification after transfer finishes" , globalFlag (Annex.setOutput NormalOutput)
, Option [] ["notify-start"] (NoArg (setdesktopnotify mkNotifyStart)) ( long "verbose" <> short 'v'
"show desktop notification after transfer completes" <> help "allow verbose output (default)"
<> hidden
)
, globalFlag setdebug
( long "debug" <> short 'd'
<> help "show debug messages"
<> hidden
)
, globalFlag unsetdebug
( long "no-debug"
<> help "don't show debug messages"
<> hidden
)
, globalSetter setforcebackend $ strOption
( long "backend" <> short 'b' <> metavar paramName
<> help "specify key-value backend to use"
<> hidden
)
] ]
where where
setforce v = Annex.changeState $ \s -> s { Annex.force = v } setforce v = Annex.changeState $ \s -> s { Annex.force = v }
@ -51,21 +61,3 @@ commonOptions =
setforcebackend v = Annex.changeState $ \s -> s { Annex.forcebackend = Just v } setforcebackend v = Annex.changeState $ \s -> s { Annex.forcebackend = Just v }
setdebug = Annex.changeGitConfig $ \c -> c { annexDebug = True } setdebug = Annex.changeGitConfig $ \c -> c { annexDebug = True }
unsetdebug = Annex.changeGitConfig $ \c -> c { annexDebug = False } unsetdebug = Annex.changeGitConfig $ \c -> c { annexDebug = False }
setdesktopnotify v = Annex.changeState $ \s -> s { Annex.desktopnotify = Annex.desktopnotify s <> v }
{- An option that sets a flag. -}
flagOption :: String -> String -> String -> Option
flagOption short opt description =
Option short [opt] (NoArg (Annex.setFlag opt)) description
{- An option that sets a field. -}
fieldOption :: String -> String -> String -> String -> Option
fieldOption short opt paramdesc description =
Option short [opt] (ReqArg (Annex.setField opt) paramdesc) description
{- The flag or field name used for an option. -}
optionName :: Option -> String
optionName (Option _ o _ _) = Prelude.head o
optionParam :: Option -> String
optionParam o = "--" ++ optionName o

View file

@ -22,18 +22,18 @@ import qualified Git.LsFiles as LsFiles
import qualified Git.LsTree as LsTree import qualified Git.LsTree as LsTree
import Git.FilePath import Git.FilePath
import qualified Limit import qualified Limit
import CmdLine.Option import CmdLine.GitAnnex.Options
import CmdLine.Action import CmdLine.Action
import Logs.Location import Logs.Location
import Logs.Unused import Logs.Unused
import Annex.CatFile import Annex.CatFile
import Annex.Content import Annex.Content
withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesInGit :: (FilePath -> CommandStart) -> CmdParams -> CommandSeek
withFilesInGit a params = seekActions $ prepFiltered a $ withFilesInGit a params = seekActions $ prepFiltered a $
seekHelper LsFiles.inRepo params seekHelper LsFiles.inRepo params
withFilesInGitNonRecursive :: (FilePath -> CommandStart) -> CommandSeek withFilesInGitNonRecursive :: (FilePath -> CommandStart) -> CmdParams -> CommandSeek
withFilesInGitNonRecursive a params = ifM (Annex.getState Annex.force) withFilesInGitNonRecursive a params = ifM (Annex.getState Annex.force)
( withFilesInGit a params ( withFilesInGit a params
, if null params , if null params
@ -54,7 +54,7 @@ withFilesInGitNonRecursive a params = ifM (Annex.getState Annex.force)
_ -> needforce _ -> needforce
needforce = error "Not recursively setting metadata. Use --force to do that." needforce = error "Not recursively setting metadata. Use --force to do that."
withFilesNotInGit :: Bool -> (FilePath -> CommandStart) -> CommandSeek withFilesNotInGit :: Bool -> (FilePath -> CommandStart) -> CmdParams -> CommandSeek
withFilesNotInGit skipdotfiles a params withFilesNotInGit skipdotfiles a params
| skipdotfiles = do | skipdotfiles = do
{- dotfiles are not acted on unless explicitly listed -} {- dotfiles are not acted on unless explicitly listed -}
@ -73,7 +73,7 @@ withFilesNotInGit skipdotfiles a params
go l = seekActions $ prepFiltered a $ go l = seekActions $ prepFiltered a $
return $ concat $ segmentPaths params l return $ concat $ segmentPaths params l
withFilesInRefs :: (FilePath -> Key -> CommandStart) -> CommandSeek withFilesInRefs :: (FilePath -> Key -> CommandStart) -> CmdParams -> CommandSeek
withFilesInRefs a = mapM_ go withFilesInRefs a = mapM_ go
where where
go r = do go r = do
@ -87,7 +87,7 @@ withFilesInRefs a = mapM_ go
Just k -> whenM (matcher $ MatchingKey k) $ Just k -> whenM (matcher $ MatchingKey k) $
commandAction $ a f k commandAction $ a f k
withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CmdParams -> CommandSeek
withPathContents a params = do withPathContents a params = do
matcher <- Limit.getMatcher matcher <- Limit.getMatcher
seekActions $ map a <$> (filterM (checkmatch matcher) =<< ps) seekActions $ map a <$> (filterM (checkmatch matcher) =<< ps)
@ -103,27 +103,27 @@ withPathContents a params = do
, matchFile = relf , matchFile = relf
} }
withWords :: ([String] -> CommandStart) -> CommandSeek withWords :: ([String] -> CommandStart) -> CmdParams -> CommandSeek
withWords a params = seekActions $ return [a params] withWords a params = seekActions $ return [a params]
withStrings :: (String -> CommandStart) -> CommandSeek withStrings :: (String -> CommandStart) -> CmdParams -> CommandSeek
withStrings a params = seekActions $ return $ map a params withStrings a params = seekActions $ return $ map a params
withPairs :: ((String, String) -> CommandStart) -> CommandSeek withPairs :: ((String, String) -> CommandStart) -> CmdParams -> CommandSeek
withPairs a params = seekActions $ 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) -> CmdParams -> CommandSeek
withFilesToBeCommitted a params = seekActions $ prepFiltered a $ withFilesToBeCommitted a params = seekActions $ prepFiltered a $
seekHelper LsFiles.stagedNotDeleted params seekHelper LsFiles.stagedNotDeleted params
withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked :: (FilePath -> CommandStart) -> CmdParams -> CommandSeek
withFilesUnlocked = withFilesUnlocked' LsFiles.typeChanged withFilesUnlocked = withFilesUnlocked' LsFiles.typeChanged
withFilesUnlockedToBeCommitted :: (FilePath -> CommandStart) -> CommandSeek withFilesUnlockedToBeCommitted :: (FilePath -> CommandStart) -> CmdParams -> CommandSeek
withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged
{- Unlocked files have changed type from a symlink to a regular file. {- Unlocked files have changed type from a symlink to a regular file.
@ -131,7 +131,7 @@ withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged
- Furthermore, unlocked files used to be a git-annex symlink, - Furthermore, unlocked files used to be a git-annex symlink,
- 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) -> CmdParams -> CommandSeek
withFilesUnlocked' typechanged a params = seekActions $ withFilesUnlocked' typechanged a params = seekActions $
prepFiltered a unlockedfiles prepFiltered a unlockedfiles
where where
@ -142,25 +142,16 @@ isUnlocked f = liftIO (notSymlink f) <&&>
(isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f) (isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f)
{- Finds files that may be modified. -} {- Finds files that may be modified. -}
withFilesMaybeModified :: (FilePath -> CommandStart) -> CommandSeek withFilesMaybeModified :: (FilePath -> CommandStart) -> CmdParams -> CommandSeek
withFilesMaybeModified a params = seekActions $ withFilesMaybeModified a params = seekActions $
prepFiltered a $ seekHelper LsFiles.modified params prepFiltered a $ seekHelper LsFiles.modified params
withKeys :: (Key -> CommandStart) -> CommandSeek withKeys :: (Key -> CommandStart) -> CmdParams -> CommandSeek
withKeys a params = seekActions $ 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
{- Gets the value of a field options, which is fed into withNothing :: CommandStart -> CmdParams -> CommandSeek
- a conversion function.
-}
getOptionField :: Option -> (Maybe String -> Annex a) -> Annex a
getOptionField option converter = converter <=< Annex.getField $ optionName option
getOptionFlag :: Option -> Annex Bool
getOptionFlag option = Annex.getFlag (optionName option)
withNothing :: CommandStart -> CommandSeek
withNothing a [] = seekActions $ return [a] withNothing a [] = seekActions $ return [a]
withNothing _ _ = error "This command takes no parameters." withNothing _ _ = error "This command takes no parameters."
@ -171,40 +162,34 @@ withNothing _ _ = error "This command takes no parameters."
- -
- Otherwise falls back to a regular CommandSeek action on - Otherwise falls back to a regular CommandSeek action on
- whatever params were passed. -} - whatever params were passed. -}
withKeyOptions :: Bool -> (Key -> CommandStart) -> CommandSeek -> CommandSeek withKeyOptions :: Maybe KeyOptions -> Bool -> (Key -> CommandStart) -> (CmdParams -> CommandSeek) -> CmdParams -> CommandSeek
withKeyOptions auto keyop = withKeyOptions' auto $ \getkeys -> do withKeyOptions ko auto keyaction = withKeyOptions' ko auto $ \getkeys -> do
matcher <- Limit.getMatcher matcher <- Limit.getMatcher
seekActions $ map (process matcher) <$> getkeys seekActions $ map (process matcher) <$> getkeys
where where
process matcher k = ifM (matcher $ MatchingKey k) process matcher k = ifM (matcher $ MatchingKey k)
( keyop k ( keyaction k
, return Nothing , return Nothing
) )
withKeyOptions' :: Bool -> (Annex [Key] -> Annex ()) -> CommandSeek -> CommandSeek withKeyOptions' :: Maybe KeyOptions -> Bool -> (Annex [Key] -> Annex ()) -> (CmdParams -> CommandSeek) -> CmdParams -> CommandSeek
withKeyOptions' auto keyop fallbackop params = do withKeyOptions' ko auto keyaction fallbackaction params = do
bare <- fromRepo Git.repoIsLocalBare bare <- fromRepo Git.repoIsLocalBare
allkeys <- Annex.getFlag "all"
unused <- Annex.getFlag "unused"
incomplete <- Annex.getFlag "incomplete"
specifickey <- Annex.getField "key"
when (auto && bare) $ when (auto && bare) $
error "Cannot use --auto in a bare repository" error "Cannot use --auto in a bare repository"
case (allkeys, unused, incomplete, null params, specifickey) of case (null params, ko) of
(False , False , False , True , Nothing) (True, Nothing)
| bare -> go auto loggedKeys | bare -> go auto loggedKeys
| otherwise -> fallbackop params | otherwise -> fallbackaction params
(False , False , False , _ , Nothing) -> fallbackop params (False, Nothing) -> fallbackaction params
(True , False , False , True , Nothing) -> go auto loggedKeys (True, Just WantAllKeys) -> go auto loggedKeys
(False , True , False , True , Nothing) -> go auto unusedKeys' (True, Just WantUnusedKeys) -> go auto unusedKeys'
(False , False , True , True , Nothing) -> go auto incompletekeys (True, Just (WantSpecificKey k)) -> go auto $ return [k]
(False , False , False , True , Just ks) -> case file2key ks of (True, Just WantIncompleteKeys) -> go auto incompletekeys
Nothing -> error "Invalid key" (False, Just _) -> error "Can only specify one of file names, --all, --unused, --key, or --incomplete"
Just k -> go auto $ return [k]
_ -> error "Can only specify one of file names, --all, --unused, --key, or --incomplete"
where where
go True _ = error "Cannot use --auto with --all or --unused or --key or --incomplete" go True _ = error "Cannot use --auto with --all or --unused or --key or --incomplete"
go False getkeys = keyop getkeys go False getkeys = keyaction getkeys
incompletekeys = staleKeysPrune gitAnnexTmpObjectDir True incompletekeys = staleKeysPrune gitAnnexTmpObjectDir True
prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart] prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart]

View file

@ -1,6 +1,6 @@
{- git-annex usage messages {- git-annex usage messages
- -
- Copyright 2010-2011 Joey Hess <id@joeyh.name> - Copyright 2010-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -8,17 +8,17 @@
module CmdLine.Usage where module CmdLine.Usage where
import Common.Annex import Common.Annex
import Types.Command import Types.Command
import System.Console.GetOpt
usageMessage :: String -> String usageMessage :: String -> String
usageMessage s = "Usage: " ++ s usageMessage s = "Usage: " ++ s
{- Usage message with lists of commands by section. -}
usage :: String -> [Command] -> String usage :: String -> [Command] -> String
usage header cmds = unlines $ usageMessage header : concatMap go [minBound..] usage header cmds = unlines $ usageMessage header : commandList cmds
{- Commands listed by section, with breif usage and description. -}
commandList :: [Command] -> [String]
commandList cmds = concatMap go [minBound..]
where where
go section go section
| null cs = [] | null cs = []
@ -42,23 +42,10 @@ usage header cmds = unlines $ usageMessage header : concatMap go [minBound..]
longest f = foldl max 0 $ map (length . f) cmds longest f = foldl max 0 $ map (length . f) cmds
scmds = sort cmds scmds = sort cmds
{- Usage message for a single command. -}
commandUsage :: Command -> String
commandUsage cmd = unlines
[ usageInfo header (cmdoptions cmd)
, "To see additional options common to all commands, run: git annex help options"
]
where
header = usageMessage $ unwords
[ "git-annex"
, cmdname cmd
, cmdparamdesc cmd
, "[option ...]"
]
{- Descriptions of params used in usage messages. -} {- Descriptions of params used in usage messages. -}
paramPaths :: String paramPaths :: String
paramPaths = paramOptional $ paramRepeating paramPath -- most often used paramPaths = paramRepeating paramPath -- most often used
paramPath :: String paramPath :: String
paramPath = "PATH" paramPath = "PATH"
paramKey :: String paramKey :: String
@ -114,6 +101,6 @@ paramNothing = ""
paramRepeating :: String -> String paramRepeating :: String -> String
paramRepeating s = s ++ " ..." paramRepeating s = s ++ " ..."
paramOptional :: String -> String paramOptional :: String -> String
paramOptional s = "[" ++ s ++ "]" paramOptional s = s
paramPair :: String -> String -> String paramPair :: String -> String -> String
paramPair a b = a ++ " " ++ b paramPair a b = a ++ " " ++ b

View file

@ -1,16 +1,18 @@
{- git-annex command infrastructure {- git-annex command infrastructure
- -
- Copyright 2010-2014 Joey Hess <id@joeyh.name> - Copyright 2010-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Command ( module Command (
command, command,
withParams,
(<--<),
noRepo, noRepo,
noCommit, noCommit,
noMessages, noMessages,
withOptions, withGlobalOptions,
next, next,
stop, stop,
stopUnless, stopUnless,
@ -25,16 +27,38 @@ import qualified Backend
import qualified Git import qualified Git
import Types.Command as ReExported import Types.Command as ReExported
import Types.Option as ReExported import Types.Option as ReExported
import Types.DeferredParse as ReExported
import CmdLine.Seek as ReExported import CmdLine.Seek as ReExported
import Checks as ReExported import Checks as ReExported
import CmdLine.Usage as ReExported import CmdLine.Usage as ReExported
import CmdLine.Action as ReExported import CmdLine.Action as ReExported
import CmdLine.Option as ReExported import CmdLine.Option as ReExported
import CmdLine.GlobalSetter as ReExported
import CmdLine.GitAnnex.Options as ReExported import CmdLine.GitAnnex.Options as ReExported
import Options.Applicative as ReExported hiding (command)
{- Generates a normal command -} import qualified Options.Applicative as O
command :: String -> String -> CommandSeek -> CommandSection -> String -> Command
command = Command [] Nothing commonChecks False False {- Generates a normal Command -}
command :: String -> CommandSection -> String -> CmdParamsDesc -> (CmdParamsDesc -> CommandParser) -> Command
command name section desc paramdesc mkparser =
Command commonChecks False False name paramdesc
section desc (mkparser paramdesc) Nothing
{- Simple option parser that takes all non-option params as-is. -}
withParams :: (CmdParams -> v) -> CmdParamsDesc -> O.Parser v
withParams mkseek paramdesc = mkseek <$> cmdParams paramdesc
{- Uses the supplied option parser, which yields a deferred parse,
- and calls finishParse on the result before passing it to the
- CommandSeek constructor. -}
(<--<) :: DeferredParseClass a
=> (a -> CommandSeek)
-> (CmdParamsDesc -> Parser a)
-> CmdParamsDesc
-> Parser CommandSeek
(<--<) mkseek optparser paramsdesc =
(mkseek <=< finishParse) <$> optparser paramsdesc
{- Indicates that a command doesn't need to commit any changes to {- Indicates that a command doesn't need to commit any changes to
- the git-annex branch. -} - the git-annex branch. -}
@ -48,12 +72,21 @@ noMessages c = c { cmdnomessages = True }
{- Adds a fallback action to a command, that will be run if it's used {- Adds a fallback action to a command, that will be run if it's used
- outside a git repository. -} - outside a git repository. -}
noRepo :: (CmdParams -> IO ()) -> Command -> Command noRepo :: (String -> O.Parser (IO ())) -> Command -> Command
noRepo a c = c { cmdnorepo = Just a } noRepo a c = c { cmdnorepo = Just (a (cmdparamdesc c)) }
{- Adds options to a command. -} {- Adds global options to a command's option parser, and modifies its seek
withOptions :: [Option] -> Command -> Command - option to first run actions for them.
withOptions o c = c { cmdoptions = cmdoptions c ++ o } -}
withGlobalOptions :: [GlobalOption] -> Command -> Command
withGlobalOptions os c = c { cmdparser = apply <$> mixin (cmdparser c) }
where
mixin p = (,)
<$> p
<*> combineGlobalOptions os
apply (seek, globalsetters) = do
void $ getParsed globalsetters
seek
{- For start and perform stages to indicate what step to run next. -} {- For start and perform stages to indicate what step to run next. -}
next :: a -> Annex (Maybe a) next :: a -> Annex (Maybe a)

View file

@ -34,28 +34,35 @@ import Utility.Tmp
import Control.Exception (IOException) import Control.Exception (IOException)
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ withOptions addOptions $ cmd = notBareRepo $ withGlobalOptions fileMatchingOptions $
command "add" paramPaths seek SectionCommon "add files to annex"] command "add" SectionCommon "add files to annex"
paramPaths (seek <$$> optParser)
addOptions :: [Option] data AddOptions = AddOptions
addOptions = includeDotFilesOption : fileMatchingOptions { addThese :: CmdParams
, includeDotFiles :: Bool
}
includeDotFilesOption :: Option optParser :: CmdParamsDesc -> Parser AddOptions
includeDotFilesOption = flagOption [] "include-dotfiles" "don't skip dotfiles" optParser desc = AddOptions
<$> cmdParams desc
<*> switch
( long "include-dotfiles"
<> help "don't skip dotfiles"
)
{- 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 :: AddOptions -> CommandSeek
seek ps = do seek o = do
matcher <- largeFilesMatcher matcher <- largeFilesMatcher
let go a = flip a ps $ \file -> ifM (checkFileMatcher matcher file <||> Annex.getState Annex.force) let go a = flip a (addThese o) $ \file -> ifM (checkFileMatcher matcher file <||> Annex.getState Annex.force)
( start file ( start file
, startSmall file , startSmall file
) )
skipdotfiles <- not <$> Annex.getFlag (optionName includeDotFilesOption) go $ withFilesNotInGit (not $ includeDotFiles o)
go $ withFilesNotInGit skipdotfiles
ifM isDirect ifM isDirect
( go withFilesMaybeModified ( go withFilesMaybeModified
, go withFilesUnlocked , go withFilesUnlocked
@ -70,8 +77,8 @@ startSmall file = do
performAdd :: FilePath -> CommandPerform performAdd :: FilePath -> CommandPerform
performAdd file = do performAdd file = do
params <- forceParams ps <- forceParams
Annex.Queue.addCommand "add" (params++[Param "--"]) [file] Annex.Queue.addCommand "add" (ps++[Param "--"]) [file]
next $ return True next $ return True
{- 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
@ -278,8 +285,8 @@ addLink :: FilePath -> Key -> Maybe InodeCache -> Annex ()
addLink file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig) addLink file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig)
( do ( do
_ <- link file key mcache _ <- link file key mcache
params <- forceParams ps <- forceParams
Annex.Queue.addCommand "add" (params++[Param "--"]) [file] Annex.Queue.addCommand "add" (ps++[Param "--"]) [file]
, do , do
l <- link file key mcache l <- link file key mcache
addAnnexLink l file addAnnexLink l file

View file

@ -14,11 +14,13 @@ import qualified Command.Add
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused) import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [notDirect $ command "addunused" (paramRepeating paramNumRange) cmd = notDirect $
seek SectionMaintenance "add back unused files"] command "addunused" SectionMaintenance
"add back unused files"
(paramRepeating paramNumRange) (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withUnusedMaps start seek = withUnusedMaps start
start :: UnusedMaps -> Int -> CommandStart start :: UnusedMaps -> Int -> CommandStart

View file

@ -37,34 +37,51 @@ import Annex.Quvi
import qualified Utility.Quvi as Quvi import qualified Utility.Quvi as Quvi
#endif #endif
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ withOptions [fileOption, pathdepthOption, relaxedOption, rawOption] $ cmd = notBareRepo $
command "addurl" (paramRepeating paramUrl) seek command "addurl" SectionCommon "add urls to annex"
SectionCommon "add urls to annex"] (paramRepeating paramUrl) (seek <$$> optParser)
fileOption :: Option data AddUrlOptions = AddUrlOptions
fileOption = fieldOption [] "file" paramFile "specify what file the url is added to" { addUrls :: CmdParams
, fileOption :: Maybe FilePath
, pathdepthOption :: Maybe Int
, relaxedOption :: Bool
, rawOption :: Bool
}
pathdepthOption :: Option optParser :: CmdParamsDesc -> Parser AddUrlOptions
pathdepthOption = fieldOption [] "pathdepth" paramNumber "path components to use in filename" optParser desc = AddUrlOptions
<$> cmdParams desc
<*> optional (strOption
( long "file" <> metavar paramFile
<> help "specify what file the url is added to"
))
<*> optional (option auto
( long "pathdepth" <> metavar paramNumber
<> help "path components to use in filename"
))
<*> parseRelaxedOption
<*> parseRawOption
relaxedOption :: Option parseRelaxedOption :: Parser Bool
relaxedOption = flagOption [] "relaxed" "skip size check" parseRelaxedOption = switch
( long "relaxed"
<> help "skip size check"
)
rawOption :: Option parseRawOption :: Parser Bool
rawOption = flagOption [] "raw" "disable special handling for torrents, quvi, etc" parseRawOption = switch
( long "raw"
<> help "disable special handling for torrents, quvi, etc"
)
seek :: CommandSeek seek :: AddUrlOptions -> CommandSeek
seek us = do seek o = forM_ (addUrls o) $ \u -> do
optfile <- getOptionField fileOption return r <- Remote.claimingUrl u
relaxed <- getOptionFlag relaxedOption if Remote.uuid r == webUUID || rawOption o
raw <- getOptionFlag rawOption then void $ commandAction $ startWeb (relaxedOption o) (fileOption o) (pathdepthOption o) u
pathdepth <- getOptionField pathdepthOption (return . maybe Nothing readish) else checkUrl r u (fileOption o) (relaxedOption o) (pathdepthOption o)
forM_ us $ \u -> do
r <- Remote.claimingUrl u
if Remote.uuid r == webUUID || raw
then void $ commandAction $ startWeb relaxed optfile pathdepth u
else checkUrl r u optfile relaxed pathdepth
checkUrl :: Remote -> URLString -> Maybe FilePath -> Bool -> Maybe Int -> Annex () checkUrl :: Remote -> URLString -> Maybe FilePath -> Bool -> Maybe Int -> Annex ()
checkUrl r u optfile relaxed pathdepth = do checkUrl r u optfile relaxed pathdepth = do

View file

@ -1,6 +1,6 @@
{- git-annex assistant {- git-annex assistant
- -
- Copyright 2012 Joey Hess <id@joeyh.name> - Copyright 2012-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -17,65 +17,60 @@ import qualified Build.SysConfig
import Utility.HumanTime import Utility.HumanTime
import Assistant.Install import Assistant.Install
import System.Environment cmd :: Command
cmd = dontCheck repoExists $ notBareRepo $
noRepo (startNoRepo <$$> optParser) $
command "assistant" SectionCommon
"automatically sync changes"
paramNothing (seek <$$> optParser)
cmd :: [Command] data AssistantOptions = AssistantOptions
cmd = [noRepo checkNoRepoOpts $ dontCheck repoExists $ withOptions options $ { daemonOptions :: DaemonOptions
notBareRepo $ command "assistant" paramNothing seek SectionCommon , autoStartOption :: Bool
"automatically sync changes"] , startDelayOption :: Maybe Duration
, autoStopOption :: Bool
}
options :: [Option] optParser :: CmdParamsDesc -> Parser AssistantOptions
options = optParser _ = AssistantOptions
[ Command.Watch.foregroundOption <$> parseDaemonOptions
, Command.Watch.stopOption <*> switch
, autoStartOption ( long "autostart"
, startDelayOption <> help "start in known repositories"
, autoStopOption )
] <*> optional (option (str >>= parseDuration)
( long "startdelay" <> metavar paramNumber
<> help "delay before running startup scan"
))
<*> switch
( long "autostop"
<> help "stop in known repositories"
)
autoStartOption :: Option seek :: AssistantOptions -> CommandSeek
autoStartOption = flagOption [] "autostart" "start in known repositories" seek = commandAction . start
autoStopOption :: Option start :: AssistantOptions -> CommandStart
autoStopOption = flagOption [] "autostop" "stop in known repositories" start o
| autoStartOption o = do
startDelayOption :: Option liftIO $ autoStart o
startDelayOption = fieldOption [] "startdelay" paramNumber "delay before running startup scan"
seek :: CommandSeek
seek ps = do
stopdaemon <- getOptionFlag Command.Watch.stopOption
foreground <- getOptionFlag Command.Watch.foregroundOption
autostart <- getOptionFlag autoStartOption
autostop <- getOptionFlag autoStopOption
startdelay <- getOptionField startDelayOption (pure . maybe Nothing parseDuration)
withNothing (start foreground stopdaemon autostart autostop startdelay) ps
start :: Bool -> Bool -> Bool -> Bool -> Maybe Duration -> CommandStart
start foreground stopdaemon autostart autostop startdelay
| autostart = do
liftIO $ autoStart startdelay
stop stop
| autostop = do | autoStopOption o = do
liftIO autoStop liftIO autoStop
stop stop
| otherwise = do | otherwise = do
liftIO ensureInstalled liftIO ensureInstalled
ensureInitialized ensureInitialized
Command.Watch.start True foreground stopdaemon startdelay Command.Watch.start True (daemonOptions o) (startDelayOption o)
{- Run outside a git repository; support autostart and autostop mode. -} startNoRepo :: AssistantOptions -> IO ()
checkNoRepoOpts :: CmdParams -> IO () startNoRepo o
checkNoRepoOpts _ = ifM (elem "--autostart" <$> getArgs) | autoStartOption o = autoStart o
( autoStart Nothing | autoStopOption o = autoStop
, ifM (elem "--autostop" <$> getArgs) | otherwise = error "Not in a git repository."
( autoStop
, error "Not in a git repository."
)
)
autoStart :: Maybe Duration -> IO () autoStart :: AssistantOptions -> IO ()
autoStart startdelay = do autoStart o = do
dirs <- liftIO readAutoStartFile dirs <- liftIO readAutoStartFile
when (null dirs) $ do when (null dirs) $ do
f <- autoStartFile f <- autoStartFile
@ -103,7 +98,7 @@ autoStart startdelay = do
where where
baseparams = baseparams =
[ Param "assistant" [ Param "assistant"
, Param $ "--startdelay=" ++ fromDuration (fromMaybe (Duration 5) startdelay) , Param $ "--startdelay=" ++ fromDuration (fromMaybe (Duration 5) (startDelayOption o))
] ]
autoStop :: IO () autoStop :: IO ()

View file

@ -14,11 +14,14 @@ import qualified Remote
import Annex import Annex
import Types.Messages import Types.Messages
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "checkpresentkey" (paramPair paramKey paramRemote) seek cmd = noCommit $
SectionPlumbing "check if key is present in remote"] command "checkpresentkey" SectionPlumbing
"check if key is present in remote"
(paramPair paramKey paramRemote)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -12,11 +12,12 @@ import Command
import qualified Annex.Branch import qualified Annex.Branch
import qualified Git import qualified Git
cmd :: [Command] cmd :: Command
cmd = [command "commit" paramNothing seek cmd = command "commit" SectionPlumbing
SectionPlumbing "commits any staged changes to the git-annex branch"] "commits any staged changes to the git-annex branch"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -15,11 +15,13 @@ import qualified Annex.Branch
import qualified Git.Config import qualified Git.Config
import Remote.GCrypt (coreGCryptId) import Remote.GCrypt (coreGCryptId)
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "configlist" paramNothing seek cmd = noCommit $
SectionPlumbing "outputs relevant git configuration"] command "configlist" SectionPlumbing
"outputs relevant git configuration"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -11,20 +11,20 @@ import Common.Annex
import Command import Command
import CmdLine.Batch import CmdLine.Batch
import Annex.Content import Annex.Content
import Types.Key
cmd :: [Command] cmd :: Command
cmd = [withOptions [batchOption] $ noCommit $ noMessages $ cmd = noCommit $ noMessages $
command "contentlocation" (paramRepeating paramKey) seek command "contentlocation" SectionPlumbing
SectionPlumbing "looks up content for a key"] "looks up content for a key"
(paramRepeating paramKey)
(batchable run (pure ()))
seek :: CommandSeek run :: () -> String -> Annex Bool
seek = batchable withKeys start run _ p = do
let k = fromMaybe (error "bad key") $ file2key p
start :: Batchable Key maybe (return False) (\f -> liftIO (putStrLn f) >> return True)
start batchmode k = do
maybe (batchBadInput batchmode) (liftIO . putStrLn)
=<< inAnnex' (pure True) Nothing check k =<< inAnnex' (pure True) Nothing check k
stop
where where
check f = ifM (liftIO (doesFileExist f)) check f = ifM (liftIO (doesFileExist f))
( return (Just f) ( return (Just f)

View file

@ -14,33 +14,44 @@ import qualified Remote
import Annex.Wanted import Annex.Wanted
import Annex.NumCopies import Annex.NumCopies
cmd :: [Command] cmd :: Command
cmd = [withOptions copyOptions $ command "copy" paramPaths seek cmd = command "copy" SectionCommon
SectionCommon "copy content of files to/from another repository"] "copy content of files to/from another repository"
paramPaths (seek <--< optParser)
copyOptions :: [Option] data CopyOptions = CopyOptions
copyOptions = Command.Move.moveOptions ++ [autoOption] { moveOptions :: Command.Move.MoveOptions
, autoMode :: Bool
}
seek :: CommandSeek optParser :: CmdParamsDesc -> Parser CopyOptions
seek ps = do optParser desc = CopyOptions
to <- getOptionField toOption Remote.byNameWithUUID <$> Command.Move.optParser desc
from <- getOptionField fromOption Remote.byNameWithUUID <*> parseAutoOption
auto <- getOptionFlag autoOption
withKeyOptions auto instance DeferredParseClass CopyOptions where
(Command.Move.startKey to from False) finishParse v = CopyOptions
(withFilesInGit $ whenAnnexed $ start auto to from) <$> finishParse (moveOptions v)
ps <*> pure (autoMode v)
seek :: CopyOptions -> CommandSeek
seek o = withKeyOptions (Command.Move.keyOptions $ moveOptions o) (autoMode o)
(Command.Move.startKey (moveOptions o) False)
(withFilesInGit $ whenAnnexed $ start o)
(Command.Move.moveFiles $ moveOptions o)
{- 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
- sending non-preferred content. -} - sending non-preferred content. -}
start :: Bool -> Maybe Remote -> Maybe Remote -> FilePath -> Key -> CommandStart start :: CopyOptions -> FilePath -> Key -> CommandStart
start auto to from file key = stopUnless shouldCopy $ start o file key = stopUnless shouldCopy $
Command.Move.start to from False file key Command.Move.start (moveOptions o) False file key
where where
shouldCopy shouldCopy
| auto = want <||> numCopiesCheck file key (<) | autoMode o = want <||> numCopiesCheck file key (<)
| otherwise = return True | otherwise = return True
want = case to of want = case Command.Move.fromToOptions (moveOptions o) of
Nothing -> wantGet False (Just key) (Just file) ToRemote _ ->
Just r -> wantSend False (Just key) (Just file) (Remote.uuid r) wantGet False (Just key) (Just file)
FromRemote dest -> (Remote.uuid <$> getParsed dest) >>=
wantSend False (Just key) (Just file)

View file

@ -9,26 +9,29 @@ module Command.Dead where
import Command import Command
import Common.Annex import Common.Annex
import qualified Annex
import Types.TrustLevel import Types.TrustLevel
import Types.Key import Types.Key
import Command.Trust (trustCommand) import Command.Trust (trustCommand)
import Logs.Location import Logs.Location
import Remote (keyLocations) import Remote (keyLocations)
import Git.Types
cmd :: [Command] cmd :: Command
cmd = [withOptions [keyOption] $ cmd = command "dead" SectionSetup "hide a lost repository or key"
command "dead" (paramRepeating paramRemote) seek (paramRepeating paramRemote) (seek <$$> optParser)
SectionSetup "hide a lost repository or key"]
seek :: CommandSeek data DeadOptions = DeadRemotes [RemoteName] | DeadKeys [Key]
seek ps = maybe (trustCommand "dead" DeadTrusted ps) (flip seekKey ps)
=<< Annex.getField "key"
seekKey :: String -> CommandSeek optParser :: CmdParamsDesc -> Parser DeadOptions
seekKey ks = case file2key ks of optParser desc = (DeadRemotes <$> cmdParams desc)
Nothing -> error "Invalid key" <|> (DeadKeys <$> many (option (str >>= parseKey)
Just key -> withNothing (startKey key) ( long "key" <> metavar paramKey
<> help "keys whose content has been irretrievably lost"
)))
seek :: DeadOptions -> CommandSeek
seek (DeadRemotes rs) = trustCommand "dead" DeadTrusted rs
seek (DeadKeys ks) = seekActions $ pure $ map startKey ks
startKey :: Key -> CommandStart startKey :: Key -> CommandStart
startKey key = do startKey key = do

View file

@ -12,11 +12,13 @@ import Command
import qualified Remote import qualified Remote
import Logs.UUID import Logs.UUID
cmd :: [Command] cmd :: Command
cmd = [command "describe" (paramPair paramRemote paramDesc) seek cmd = command "describe" SectionSetup
SectionSetup "change description of a repository"] "change description of a repository"
(paramPair paramRemote paramDesc)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -13,12 +13,13 @@ import Annex.Content
import Annex.Link import Annex.Link
import Git.Types import Git.Types
cmd :: [Command] cmd :: Command
cmd = [dontCheck repoExists $ cmd = dontCheck repoExists $
command "diffdriver" ("[-- cmd --]") seek command "diffdriver" SectionPlumbing
SectionPlumbing "external git diff driver shim"] "external git diff driver shim"
("-- cmd --") (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -15,12 +15,12 @@ import qualified Git.Branch
import Config import Config
import Annex.Direct import Annex.Direct
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ noDaemonRunning $ cmd = notBareRepo $ noDaemonRunning $
command "direct" paramNothing seek command "direct" SectionSetup "switch repository to direct mode"
SectionSetup "switch repository to direct mode"] paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -22,45 +22,60 @@ import Annex.Notification
import qualified Data.Set as S import qualified Data.Set as S
cmd :: [Command] cmd :: Command
cmd = [withOptions (dropOptions) $ command "drop" paramPaths seek cmd = withGlobalOptions annexedMatchingOptions $
SectionCommon "indicate content of files not currently wanted"] command "drop" SectionCommon
"remove content of files from repository"
paramPaths (seek <$$> optParser)
dropOptions :: [Option] data DropOptions = DropOptions
dropOptions = dropFromOption : annexedMatchingOptions ++ [autoOption] ++ keyOptions { dropFiles :: CmdParams
, dropFrom :: Maybe (DeferredParse Remote)
, autoMode :: Bool
, keyOptions :: Maybe KeyOptions
}
dropFromOption :: Option optParser :: CmdParamsDesc -> Parser DropOptions
dropFromOption = fieldOption ['f'] "from" paramRemote "drop content from a remote" optParser desc = DropOptions
<$> cmdParams desc
<*> optional parseDropFromOption
<*> parseAutoOption
<*> optional (parseKeyOptions False)
seek :: CommandSeek parseDropFromOption :: Parser (DeferredParse Remote)
seek ps = do parseDropFromOption = parseRemoteOption $ strOption
from <- getOptionField dropFromOption Remote.byNameWithUUID ( long "from" <> short 'f' <> metavar paramRemote
auto <- getOptionFlag autoOption <> help "drop content from a remote"
withKeyOptions auto )
(startKeys auto from)
(withFilesInGit $ whenAnnexed $ start auto from)
ps
start :: Bool -> Maybe Remote -> FilePath -> Key -> CommandStart seek :: DropOptions -> CommandSeek
start auto from file key = start' auto from key (Just file) seek o = withKeyOptions (keyOptions o) (autoMode o)
(startKeys o)
(withFilesInGit $ whenAnnexed $ start o)
(dropFiles o)
start' :: Bool -> Maybe Remote -> Key -> AssociatedFile -> CommandStart start :: DropOptions -> FilePath -> Key -> CommandStart
start' auto from key afile = checkDropAuto auto from afile key $ \numcopies -> start o file key = start' o key (Just file)
stopUnless want $
case from of
Nothing -> startLocal afile numcopies key Nothing
Just remote -> do
u <- getUUID
if Remote.uuid remote == u
then startLocal afile numcopies key Nothing
else startRemote afile numcopies key remote
where
want
| auto = wantDrop False (Remote.uuid <$> from) (Just key) afile
| otherwise = return True
startKeys :: Bool -> Maybe Remote -> Key -> CommandStart start' :: DropOptions -> Key -> AssociatedFile -> CommandStart
startKeys auto from key = start' auto from key Nothing start' o key afile = do
from <- maybe (pure Nothing) (Just <$$> getParsed) (dropFrom o)
checkDropAuto (autoMode o) from afile key $ \numcopies ->
stopUnless (want from) $
case from of
Nothing -> startLocal afile numcopies key Nothing
Just remote -> do
u <- getUUID
if Remote.uuid remote == u
then startLocal afile numcopies key Nothing
else startRemote afile numcopies key remote
where
want from
| autoMode o = wantDrop False (Remote.uuid <$> from) (Just key) afile
| otherwise = return True
startKeys :: DropOptions -> Key -> CommandStart
startKeys o key = start' o key Nothing
startLocal :: AssociatedFile -> NumCopies -> Key -> Maybe Remote -> CommandStart startLocal :: AssociatedFile -> NumCopies -> Key -> Maybe Remote -> CommandStart
startLocal afile numcopies key knownpresentremote = stopUnless (inAnnex key) $ do startLocal afile numcopies key knownpresentremote = stopUnless (inAnnex key) $ do
@ -164,10 +179,10 @@ requiredContent = do
{- In auto mode, only runs the action if there are enough {- In auto mode, only runs the action if there are enough
- copies on other semitrusted repositories. -} - copies on other semitrusted repositories. -}
checkDropAuto :: Bool -> Maybe Remote -> AssociatedFile -> Key -> (NumCopies -> CommandStart) -> CommandStart checkDropAuto :: Bool -> Maybe Remote -> AssociatedFile -> Key -> (NumCopies -> CommandStart) -> CommandStart
checkDropAuto auto mremote afile key a = go =<< maybe getNumCopies getFileNumCopies afile checkDropAuto automode mremote afile key a = go =<< maybe getNumCopies getFileNumCopies afile
where where
go numcopies go numcopies
| auto = do | automode = do
locs <- Remote.keyLocations key locs <- Remote.keyLocations key
uuid <- getUUID uuid <- getUUID
let remoteuuid = fromMaybe uuid $ Remote.uuid <$> mremote let remoteuuid = fromMaybe uuid $ Remote.uuid <$> mremote

View file

@ -13,11 +13,14 @@ import qualified Annex
import Logs.Location import Logs.Location
import Annex.Content import Annex.Content
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "dropkey" (paramRepeating paramKey) seek cmd = noCommit $
SectionPlumbing "drops annexed content for specified keys"] command "dropkey" SectionPlumbing
"drops annexed content for specified keys"
(paramRepeating paramKey)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withKeys start seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart

View file

@ -9,34 +9,42 @@ module Command.DropUnused where
import Common.Annex import Common.Annex
import Command import Command
import qualified Annex
import qualified Command.Drop import qualified Command.Drop
import qualified Remote import qualified Remote
import qualified Git import qualified Git
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused) import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
import Annex.NumCopies import Annex.NumCopies
cmd :: [Command] cmd :: Command
cmd = [withOptions [Command.Drop.dropFromOption] $ cmd = command "dropunused" SectionMaintenance
command "dropunused" (paramRepeating paramNumRange) "drop unused file content"
seek SectionMaintenance "drop unused file content"] (paramRepeating paramNumRange) (seek <$$> optParser)
seek :: CommandSeek data DropUnusedOptions = DropUnusedOptions
seek ps = do { rangesToDrop :: CmdParams
, dropFrom :: Maybe (DeferredParse Remote)
}
optParser :: CmdParamsDesc -> Parser DropUnusedOptions
optParser desc = DropUnusedOptions
<$> cmdParams desc
<*> optional (Command.Drop.parseDropFromOption)
seek :: DropUnusedOptions -> CommandSeek
seek o = do
numcopies <- getNumCopies numcopies <- getNumCopies
withUnusedMaps (start numcopies) ps from <- maybe (pure Nothing) (Just <$$> getParsed) (dropFrom o)
withUnusedMaps (start from numcopies) (rangesToDrop o)
start :: NumCopies -> UnusedMaps -> Int -> CommandStart start :: Maybe Remote -> NumCopies -> UnusedMaps -> Int -> CommandStart
start numcopies = startUnused "dropunused" (perform numcopies) (performOther gitAnnexBadLocation) (performOther gitAnnexTmpObjectLocation) start from numcopies = startUnused "dropunused" (perform from numcopies) (performOther gitAnnexBadLocation) (performOther gitAnnexTmpObjectLocation)
perform :: NumCopies -> Key -> CommandPerform perform :: Maybe Remote -> NumCopies -> Key -> CommandPerform
perform numcopies key = maybe droplocal dropremote =<< Remote.byNameWithUUID =<< from perform from numcopies key = case from of
where Just r -> do
dropremote r = do
showAction $ "from " ++ Remote.name r showAction $ "from " ++ Remote.name r
Command.Drop.performRemote key Nothing numcopies r Command.Drop.performRemote key Nothing numcopies r
droplocal = Command.Drop.performLocal key Nothing numcopies Nothing Nothing -> Command.Drop.performLocal key Nothing numcopies Nothing
from = Annex.getField $ optionName Command.Drop.dropFromOption
performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform
performOther filespec key = do performOther filespec key = do

View file

@ -15,12 +15,13 @@ import qualified Command.InitRemote as InitRemote
import qualified Data.Map as M import qualified Data.Map as M
cmd :: [Command] cmd :: Command
cmd = [command "enableremote" cmd = command "enableremote" SectionSetup
"enables use of an existing special remote"
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue) (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
seek SectionSetup "enables use of an existing special remote"] (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -11,20 +11,18 @@ import Common.Annex
import Command import Command
import CmdLine.Batch import CmdLine.Batch
import qualified Utility.Format import qualified Utility.Format
import Command.Find (formatOption, getFormat, showFormatted, keyVars) import Command.Find (parseFormatOption, showFormatted, keyVars)
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [noCommit $ noMessages $ withOptions [formatOption, jsonOption, batchOption] $ cmd = noCommit $ noMessages $ withGlobalOptions [jsonOption] $
command "examinekey" (paramRepeating paramKey) seek command "examinekey" SectionPlumbing
SectionPlumbing "prints information from a key"] "prints information from a key"
(paramRepeating paramKey)
(batchable run (optional parseFormatOption))
seek :: CommandSeek run :: Maybe Utility.Format.Format -> String -> Annex Bool
seek ps = do run format p = do
format <- getFormat let k = fromMaybe (error "bad key") $ file2key p
batchable withKeys (start format) ps showFormatted format (key2file k) (keyVars k)
return True
start :: Maybe Utility.Format.Format -> Batchable Key
start format _ key = do
showFormatted format (key2file key) (keyVars key)
stop

View file

@ -20,29 +20,40 @@ import Utility.HumanTime
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import qualified Data.Map as M import qualified Data.Map as M
cmd :: [Command] cmd :: Command
cmd = [withOptions [activityOption, noActOption] $ command "expire" paramExpire seek cmd = command "expire" SectionMaintenance
SectionMaintenance "expire inactive repositories"] "expire inactive repositories"
paramExpire (seek <$$> optParser)
paramExpire :: String paramExpire :: String
paramExpire = (paramRepeating $ paramOptional paramRemote ++ ":" ++ paramTime) paramExpire = (paramRepeating $ paramOptional paramRemote ++ ":" ++ paramTime)
activityOption :: Option data ExpireOptions = ExpireOptions
activityOption = fieldOption [] "activity" "Name" "specify activity" { expireParams :: CmdParams
, activityOption :: Maybe Activity
, noActOption :: Bool
}
noActOption :: Option optParser :: CmdParamsDesc -> Parser ExpireOptions
noActOption = flagOption [] "no-act" "don't really do anything" optParser desc = ExpireOptions
<$> cmdParams desc
<*> optional (option (str >>= parseActivity)
( long "activity" <> metavar paramName
<> help "specify activity that prevents expiry"
))
<*> switch
( long "no-act"
<> help "don't really do anything"
)
seek :: CommandSeek seek :: ExpireOptions -> CommandSeek
seek ps = do seek o = do
expire <- parseExpire ps expire <- parseExpire (expireParams o)
wantact <- getOptionField activityOption (pure . parseActivity) actlog <- lastActivities (activityOption o)
noact <- getOptionFlag noActOption
actlog <- lastActivities wantact
u <- getUUID u <- getUUID
us <- filter (/= u) . M.keys <$> uuidMap us <- filter (/= u) . M.keys <$> uuidMap
descs <- uuidMap descs <- uuidMap
seekActions $ pure $ map (start expire noact actlog descs) us seekActions $ pure $ map (start expire (noActOption o) actlog descs) us
start :: Expire -> Bool -> Log Activity -> M.Map UUID String -> UUID -> CommandStart start :: Expire -> Bool -> Log Activity -> M.Map UUID String -> UUID -> CommandStart
start (Expire expire) noact actlog descs u = start (Expire expire) noact actlog descs u =
@ -97,10 +108,9 @@ parseExpire ps = do
Nothing -> error $ "bad expire time: " ++ s Nothing -> error $ "bad expire time: " ++ s
Just d -> Just (now - durationToPOSIXTime d) Just d -> Just (now - durationToPOSIXTime d)
parseActivity :: Maybe String -> Maybe Activity parseActivity :: Monad m => String -> m Activity
parseActivity Nothing = Nothing parseActivity s = case readish s of
parseActivity (Just s) = case readish s of Nothing -> fail $ "Unknown activity. Choose from: " ++
Nothing -> error $ "Unknown activity. Choose from: " ++
unwords (map show [minBound..maxBound :: Activity]) unwords (map show [minBound..maxBound :: Activity])
Just v -> Just v Just v -> return v

View file

@ -14,41 +14,48 @@ import Common.Annex
import Command import Command
import Annex.Content import Annex.Content
import Limit import Limit
import qualified Annex
import qualified Utility.Format import qualified Utility.Format
import Utility.DataUnits import Utility.DataUnits
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [withOptions annexedMatchingOptions $ mkCommand $ cmd = withGlobalOptions annexedMatchingOptions $ mkCommand $
command "find" paramPaths seek SectionQuery "lists available files"] command "find" SectionQuery "lists available files"
paramPaths (seek <$$> optParser)
mkCommand :: Command -> Command mkCommand :: Command -> Command
mkCommand = noCommit . noMessages . withOptions [formatOption, print0Option, jsonOption] mkCommand = noCommit . noMessages . withGlobalOptions [jsonOption]
formatOption :: Option data FindOptions = FindOptions
formatOption = fieldOption [] "format" paramFormat "control format of output" { findThese :: CmdParams
, formatOption :: Maybe Utility.Format.Format
}
getFormat :: Annex (Maybe Utility.Format.Format) optParser :: CmdParamsDesc -> Parser FindOptions
getFormat = getOptionField formatOption $ return . fmap Utility.Format.gen optParser desc = FindOptions
<$> cmdParams desc
<*> optional parseFormatOption
print0Option :: Option parseFormatOption :: Parser Utility.Format.Format
print0Option = Option [] ["print0"] (NoArg set) parseFormatOption =
"terminate output with null" option (Utility.Format.gen <$> str)
where ( long "format" <> metavar paramFormat
set = Annex.setField (optionName formatOption) "${file}\0" <> help "control format of output"
)
<|> flag' (Utility.Format.gen "${file}\0")
( long "print0"
<> help "output filenames terminated with nulls"
)
seek :: CommandSeek seek :: FindOptions -> CommandSeek
seek ps = do seek o = withFilesInGit (whenAnnexed $ start o) (findThese o)
format <- getFormat
withFilesInGit (whenAnnexed $ start format) ps
start :: Maybe Utility.Format.Format -> FilePath -> Key -> CommandStart start :: FindOptions -> FilePath -> Key -> CommandStart
start format file key = do start o file key = do
-- only files inAnnex are shown, unless the user has requested -- only files inAnnex are shown, unless the user has requested
-- others via a limit -- others via a limit
whenM (limited <||> inAnnex key) $ whenM (limited <||> inAnnex key) $
showFormatted format file $ ("file", file) : keyVars key showFormatted (formatOption o) file $ ("file", file) : keyVars key
stop stop
showFormatted :: Maybe Utility.Format.Format -> String -> [(String, String)] -> Annex () showFormatted :: Maybe Utility.Format.Format -> String -> [(String, String)] -> Annex ()

View file

@ -7,15 +7,15 @@
module Command.FindRef where module Command.FindRef where
import Common.Annex
import Command import Command
import qualified Command.Find as Find import qualified Command.Find as Find
cmd :: [Command] cmd :: Command
cmd = [withOptions nonWorkTreeMatchingOptions $ Find.mkCommand $ cmd = withGlobalOptions nonWorkTreeMatchingOptions $ Find.mkCommand $
command "findref" paramRef seek SectionPlumbing command "findref" SectionPlumbing
"lists files in a git ref"] "lists files in a git ref"
paramRef (seek <$$> Find.optParser)
seek :: CommandSeek seek :: Find.FindOptions -> CommandSeek
seek refs = do seek o = Find.start o `withFilesInRefs` Find.findThese o
format <- Find.getFormat
Find.start format `withFilesInRefs` refs

View file

@ -18,12 +18,13 @@ import Utility.Touch
#endif #endif
#endif #endif
cmd :: [Command] cmd :: Command
cmd = [notDirect $ noCommit $ withOptions annexedMatchingOptions $ cmd = notDirect $ noCommit $ withGlobalOptions annexedMatchingOptions $
command "fix" paramPaths seek command "fix" SectionMaintenance
SectionMaintenance "fix up symlinks to point to annexed content"] "fix up symlinks to point to annexed content"
paramPaths (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withFilesInGit $ whenAnnexed start seek = withFilesInGit $ whenAnnexed start
{- Fixes the symlink to an annexed file. -} {- Fixes the symlink to an annexed file. -}

View file

@ -15,27 +15,31 @@ import qualified Annex
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
cmd :: [Command] cmd :: Command
cmd = [withOptions forgetOptions $ command "forget" paramNothing seek cmd = command "forget" SectionMaintenance
SectionMaintenance "prune git-annex branch history"] "prune git-annex branch history"
paramNothing (seek <$$> optParser)
forgetOptions :: [Option] data ForgetOptions = ForgetOptions
forgetOptions = [dropDeadOption] { dropDead :: Bool
}
dropDeadOption :: Option optParser :: CmdParamsDesc -> Parser ForgetOptions
dropDeadOption = flagOption [] "drop-dead" "drop references to dead repositories" optParser _ = ForgetOptions
<$> switch
( long "drop-dead"
<> help "drop references to dead repositories"
)
seek :: CommandSeek seek :: ForgetOptions -> CommandSeek
seek ps = do seek = commandAction . start
dropdead <- getOptionFlag dropDeadOption
withNothing (start dropdead) ps
start :: Bool -> CommandStart start :: ForgetOptions -> CommandStart
start dropdead = do start o = do
showStart "forget" "git-annex" showStart "forget" "git-annex"
now <- liftIO getPOSIXTime now <- liftIO getPOSIXTime
let basets = addTransition now ForgetGitHistory noTransitions let basets = addTransition now ForgetGitHistory noTransitions
let ts = if dropdead let ts = if dropDead o
then addTransition now ForgetDeadRemotes basets then addTransition now ForgetDeadRemotes basets
else basets else basets
next $ perform ts =<< Annex.getState Annex.force next $ perform ts =<< Annex.getState Annex.force

View file

@ -19,12 +19,13 @@ import qualified Backend.URL
import Network.URI import Network.URI
cmd :: [Command] cmd :: Command
cmd = [notDirect $ notBareRepo $ cmd = notDirect $ notBareRepo $
command "fromkey" (paramPair paramKey paramPath) seek command "fromkey" SectionPlumbing "adds a file using a specific key"
SectionPlumbing "adds a file using a specific key"] (paramPair paramKey paramPath)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek ps = do seek ps = do
force <- Annex.getState Annex.force force <- Annex.getState Annex.force
withWords (start force) ps withWords (start force) ps

View file

@ -40,40 +40,57 @@ import qualified Database.Fsck as FsckDb
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import System.Posix.Types (EpochTime) import System.Posix.Types (EpochTime)
cmd :: [Command] cmd :: Command
cmd = [withOptions fsckOptions $ command "fsck" paramPaths seek cmd = withGlobalOptions annexedMatchingOptions $
SectionMaintenance "check for problems"] command "fsck" SectionMaintenance
"find and fix problems"
paramPaths (seek <$$> optParser)
fsckFromOption :: Option data FsckOptions = FsckOptions
fsckFromOption = fieldOption ['f'] "from" paramRemote "check remote" { fsckFiles :: CmdParams
, fsckFromOption :: Maybe (DeferredParse Remote)
, incrementalOpt :: Maybe IncrementalOpt
, keyOptions :: Maybe KeyOptions
}
startIncrementalOption :: Option data IncrementalOpt
startIncrementalOption = flagOption ['S'] "incremental" "start an incremental fsck" = StartIncrementalO
| MoreIncrementalO
| ScheduleIncrementalO Duration
moreIncrementalOption :: Option optParser :: CmdParamsDesc -> Parser FsckOptions
moreIncrementalOption = flagOption ['m'] "more" "continue an incremental fsck" optParser desc = FsckOptions
<$> cmdParams desc
<*> optional (parseRemoteOption $ strOption
( long "from" <> short 'f' <> metavar paramRemote
<> help "check remote"
))
<*> optional parseincremental
<*> optional (parseKeyOptions False)
where
parseincremental =
flag' StartIncrementalO
( long "incremental" <> short 'S'
<> help "start an incremental fsck"
)
<|> flag' MoreIncrementalO
( long "more" <> short 'm'
<> help "continue an incremental fsck"
)
<|> (ScheduleIncrementalO <$> option (str >>= parseDuration)
( long "incremental-schedule" <> metavar paramTime
<> help "schedule incremental fscking"
))
incrementalScheduleOption :: Option seek :: FsckOptions -> CommandSeek
incrementalScheduleOption = fieldOption [] "incremental-schedule" paramTime seek o = do
"schedule incremental fscking" from <- maybe (pure Nothing) (Just <$$> getParsed) (fsckFromOption o)
fsckOptions :: [Option]
fsckOptions =
[ fsckFromOption
, startIncrementalOption
, moreIncrementalOption
, incrementalScheduleOption
] ++ keyOptions ++ annexedMatchingOptions
seek :: CommandSeek
seek ps = do
from <- getOptionField fsckFromOption Remote.byNameWithUUID
u <- maybe getUUID (pure . Remote.uuid) from u <- maybe getUUID (pure . Remote.uuid) from
i <- getIncremental u i <- prepIncremental u (incrementalOpt o)
withKeyOptions False withKeyOptions (keyOptions o) False
(\k -> startKey i k =<< getNumCopies) (\k -> startKey i k =<< getNumCopies)
(withFilesInGit $ whenAnnexed $ start from i) (withFilesInGit $ whenAnnexed $ start from i)
ps (fsckFiles o)
withFsckDb i FsckDb.closeDb withFsckDb i FsckDb.closeDb
void $ tryIO $ recordActivity Fsck u void $ tryIO $ recordActivity Fsck u
@ -497,37 +514,26 @@ getStartTime u = do
data Incremental = StartIncremental FsckDb.FsckHandle | ContIncremental FsckDb.FsckHandle | NonIncremental data Incremental = StartIncremental FsckDb.FsckHandle | ContIncremental FsckDb.FsckHandle | NonIncremental
getIncremental :: UUID -> Annex Incremental prepIncremental :: UUID -> Maybe IncrementalOpt -> Annex Incremental
getIncremental u = do prepIncremental _ Nothing = pure NonIncremental
i <- maybe (return False) (checkschedule . parseDuration) prepIncremental u (Just StartIncrementalO) = do
=<< Annex.getField (optionName incrementalScheduleOption) recordStartTime u
starti <- getOptionFlag startIncrementalOption ifM (FsckDb.newPass u)
morei <- getOptionFlag moreIncrementalOption ( StartIncremental <$> FsckDb.openDb u
case (i, starti, morei) of , error "Cannot start a new --incremental fsck pass; another fsck process is already running."
(False, False, False) -> return NonIncremental )
(False, True, False) -> startIncremental prepIncremental u (Just MoreIncrementalO) =
(False ,False, True) -> contIncremental ContIncremental <$> FsckDb.openDb u
(True, False, False) -> prepIncremental u (Just (ScheduleIncrementalO delta)) = do
maybe startIncremental (const contIncremental) Annex.addCleanup FsckCleanup $ do
=<< getStartTime u v <- getStartTime u
_ -> error "Specify only one of --incremental, --more, or --incremental-schedule" case v of
where Nothing -> noop
startIncremental = do Just started -> do
recordStartTime u now <- liftIO getPOSIXTime
ifM (FsckDb.newPass u) when (now - realToFrac started >= durationToPOSIXTime delta) $
( StartIncremental <$> FsckDb.openDb u resetStartTime u
, error "Cannot start a new --incremental fsck pass; another fsck process is already running." started <- getStartTime u
) prepIncremental u $ Just $ case started of
contIncremental = ContIncremental <$> FsckDb.openDb u Nothing -> StartIncrementalO
Just _ -> MoreIncrementalO
checkschedule Nothing = error "bad --incremental-schedule value"
checkschedule (Just delta) = do
Annex.addCleanup FsckCleanup $ do
v <- getStartTime u
case v of
Nothing -> noop
Just started -> do
now <- liftIO getPOSIXTime
when (now - realToFrac started >= durationToPOSIXTime delta) $
resetStartTime u
return True

View file

@ -20,11 +20,13 @@ import System.Random (getStdRandom, random, randomR)
import Test.QuickCheck import Test.QuickCheck
import Control.Concurrent import Control.Concurrent
cmd :: [Command] cmd :: Command
cmd = [ notBareRepo $ command "fuzztest" paramNothing seek SectionTesting cmd = notBareRepo $
"generates fuzz test files"] command "fuzztest" SectionTesting
"generates fuzz test files"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart
@ -53,9 +55,9 @@ guardTest = unlessM (fromMaybe False . Git.Config.isTrue <$> getConfig key "") $
fuzz :: Handle -> Annex () fuzz :: Handle -> Annex ()
fuzz logh = do fuzz logh = do
action <- genFuzzAction fuzzer <- genFuzzAction
record logh $ flip Started action record logh $ flip Started fuzzer
result <- tryNonAsync $ runFuzzAction action result <- tryNonAsync $ runFuzzAction fuzzer
record logh $ flip Finished $ record logh $ flip Finished $
either (const False) (const True) result either (const False) (const True) result

View file

@ -13,12 +13,13 @@ import Annex.UUID
import qualified Remote.GCrypt import qualified Remote.GCrypt
import qualified Git import qualified Git
cmd :: [Command] cmd :: Command
cmd = [dontCheck repoExists $ noCommit $ cmd = dontCheck repoExists $ noCommit $
command "gcryptsetup" paramValue seek command "gcryptsetup" SectionPlumbing
SectionPlumbing "sets up gcrypt repository"] "sets up gcrypt repository"
paramValue (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withStrings start seek = withStrings start
start :: String -> CommandStart start :: String -> CommandStart

View file

@ -16,28 +16,39 @@ import Annex.NumCopies
import Annex.Wanted import Annex.Wanted
import qualified Command.Move import qualified Command.Move
cmd :: [Command] cmd :: Command
cmd = [withOptions getOptions $ command "get" paramPaths seek cmd = withGlobalOptions (jobsOption : annexedMatchingOptions) $
SectionCommon "make content of annexed files available"] command "get" SectionCommon
"make content of annexed files available"
paramPaths (seek <$$> optParser)
getOptions :: [Option] data GetOptions = GetOptions
getOptions = fromOption : autoOption : jobsOption : annexedMatchingOptions { getFiles :: CmdParams
++ incompleteOption : keyOptions , getFrom :: Maybe (DeferredParse Remote)
, autoMode :: Bool
, keyOptions :: Maybe KeyOptions
}
seek :: CommandSeek optParser :: CmdParamsDesc -> Parser GetOptions
seek ps = do optParser desc = GetOptions
from <- getOptionField fromOption Remote.byNameWithUUID <$> cmdParams desc
auto <- getOptionFlag autoOption <*> optional parseFromOption
withKeyOptions auto <*> parseAutoOption
<*> optional (parseKeyOptions True)
seek :: GetOptions -> CommandSeek
seek o = do
from <- maybe (pure Nothing) (Just <$$> getParsed) (getFrom o)
withKeyOptions (keyOptions o) (autoMode o)
(startKeys from) (startKeys from)
(withFilesInGit $ whenAnnexed $ start auto from) (withFilesInGit $ whenAnnexed $ start o from)
ps (getFiles o)
start :: Bool -> Maybe Remote -> FilePath -> Key -> CommandStart start :: GetOptions -> Maybe Remote -> FilePath -> Key -> CommandStart
start auto from file key = start' expensivecheck from key (Just file) start o from file key = start' expensivecheck from key (Just file)
where where
expensivecheck expensivecheck
| auto = numCopiesCheck file key (<) <||> wantGet False (Just key) (Just file) | autoMode o = numCopiesCheck file key (<) <||> wantGet False (Just key) (Just file)
| otherwise = return True | otherwise = return True
startKeys :: Maybe Remote -> Key -> CommandStart startKeys :: Maybe Remote -> Key -> CommandStart

View file

@ -15,11 +15,11 @@ import Types.Group
import qualified Data.Set as S import qualified Data.Set as S
cmd :: [Command] cmd :: Command
cmd = [command "group" (paramPair paramRemote paramDesc) seek cmd = command "group" SectionSetup "add a repository to a group"
SectionSetup "add a repository to a group"] (paramPair paramRemote paramDesc) (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -12,11 +12,13 @@ import Command
import Logs.PreferredContent import Logs.PreferredContent
import Command.Wanted (performGet, performSet) import Command.Wanted (performGet, performSet)
cmd :: [Command] cmd :: Command
cmd = [command "groupwanted" (paramPair paramGroup (paramOptional paramExpression)) seek cmd = command "groupwanted" SectionSetup
SectionSetup "get or set groupwanted expression"] "get or set groupwanted expression"
(paramPair paramGroup (paramOptional paramExpression))
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -19,13 +19,15 @@ import qualified Command.Sync
import qualified Command.Whereis import qualified Command.Whereis
import qualified Command.Fsck import qualified Command.Fsck
import System.Console.GetOpt cmd :: Command
cmd = noCommit $ dontCheck repoExists $
noRepo (parseparams startNoRepo) $
command "help" SectionCommon "display help"
"COMMAND" (parseparams seek)
where
parseparams = withParams
cmd :: [Command] seek :: CmdParams -> CommandSeek
cmd = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $
command "help" (paramOptional "COMMAND") seek SectionCommon "display help"]
seek :: CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
@ -37,17 +39,13 @@ startNoRepo :: CmdParams -> IO ()
startNoRepo = start' startNoRepo = start'
start' :: [String] -> IO () start' :: [String] -> IO ()
start' ["options"] = showCommonOptions
start' [c] = showGitHelp c start' [c] = showGitHelp c
start' _ = showGeneralHelp start' _ = showGeneralHelp
showCommonOptions :: IO ()
showCommonOptions = putStrLn $ usageInfo "Common options:" gitAnnexOptions
showGeneralHelp :: IO () showGeneralHelp :: IO ()
showGeneralHelp = putStrLn $ unlines showGeneralHelp = putStrLn $ unlines
[ "The most frequently used git-annex commands are:" [ "The most frequently used git-annex commands are:"
, unlines $ map cmdline $ concat , unlines $ map cmdline $
[ Command.Init.cmd [ Command.Init.cmd
, Command.Add.cmd , Command.Add.cmd
, Command.Drop.cmd , Command.Drop.cmd
@ -58,9 +56,8 @@ showGeneralHelp = putStrLn $ unlines
, Command.Whereis.cmd , Command.Whereis.cmd
, Command.Fsck.cmd , Command.Fsck.cmd
] ]
, "Run 'git-annex' for a complete command list." , "For a complete command list, run: git-annex"
, "Run 'git-annex help command' for help on a specific command." , "For help on a specific command, run: git-annex help COMMAND"
, "Run `git annex help options' for a list of common options."
] ]
where where
cmdline c = "\t" ++ cmdname c ++ "\t" ++ cmddesc c cmdline c = "\t" ++ cmdname c ++ "\t" ++ cmddesc c

View file

@ -22,52 +22,51 @@ import Annex.NumCopies
import Types.TrustLevel import Types.TrustLevel
import Logs.Trust import Logs.Trust
cmd :: [Command] cmd :: Command
cmd = [withOptions opts $ notBareRepo $ command "import" paramPaths seek cmd = withGlobalOptions fileMatchingOptions $ notBareRepo $
SectionCommon "move and add files from outside git working copy"] command "import" SectionCommon
"move and add files from outside git working copy"
opts :: [Option] paramPaths (seek <$$> optParser)
opts = duplicateModeOptions ++ fileMatchingOptions
data DuplicateMode = Default | Duplicate | DeDuplicate | CleanDuplicates | SkipDuplicates data DuplicateMode = Default | Duplicate | DeDuplicate | CleanDuplicates | SkipDuplicates
deriving (Eq, Enum, Bounded) deriving (Eq)
associatedOption :: DuplicateMode -> Maybe Option data ImportOptions = ImportOptions
associatedOption Default = Nothing { importFiles :: CmdParams
associatedOption Duplicate = Just $ , duplicateMode :: DuplicateMode
flagOption [] "duplicate" "do not delete source files" }
associatedOption DeDuplicate = Just $
flagOption [] "deduplicate" "delete source files whose content was imported before"
associatedOption CleanDuplicates = Just $
flagOption [] "clean-duplicates" "delete duplicate source files (import nothing)"
associatedOption SkipDuplicates = Just $
flagOption [] "skip-duplicates" "import only new files"
duplicateModeOptions :: [Option] optParser :: CmdParamsDesc -> Parser ImportOptions
duplicateModeOptions = mapMaybe associatedOption [minBound..maxBound] optParser desc = ImportOptions
<$> cmdParams desc
<*> (fromMaybe Default <$> optional duplicateModeParser)
getDuplicateMode :: Annex DuplicateMode duplicateModeParser :: Parser DuplicateMode
getDuplicateMode = go . catMaybes <$> mapM getflag [minBound..maxBound] duplicateModeParser =
where flag' Duplicate
getflag m = case associatedOption m of ( long "duplicate"
Nothing -> return Nothing <> help "do not delete source files"
Just o -> ifM (Annex.getFlag (optionName o)) )
( return (Just m) <|> flag' DeDuplicate
, return Nothing ( long "deduplicate"
) <> help "delete source files whose content was imported before"
go [] = Default )
go [m] = m <|> flag' CleanDuplicates
go ms = error $ "cannot combine " ++ ( long "clean-duplicates"
unwords (map (optionParam . fromJust . associatedOption) ms) <> help "delete duplicate source files (import nothing)"
)
<|> flag' SkipDuplicates
( long "skip-duplicates"
<> help "import only new files"
)
seek :: CommandSeek seek :: ImportOptions -> CommandSeek
seek ps = do seek o = do
mode <- getDuplicateMode
repopath <- liftIO . absPath =<< fromRepo Git.repoPath repopath <- liftIO . absPath =<< fromRepo Git.repoPath
inrepops <- liftIO $ filter (dirContains repopath) <$> mapM absPath ps inrepops <- liftIO $ filter (dirContains repopath) <$> mapM absPath (importFiles o)
unless (null inrepops) $ do unless (null inrepops) $ do
error $ "cannot import files from inside the working tree (use git annex add instead): " ++ unwords inrepops error $ "cannot import files from inside the working tree (use git annex add instead): " ++ unwords inrepops
withPathContents (start mode) ps withPathContents (start (duplicateMode o)) (importFiles o)
start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart
start mode (srcfile, destfile) = start mode (srcfile, destfile) =

View file

@ -30,7 +30,7 @@ import Types.UrlContents
import Logs.Web import Logs.Web
import qualified Utility.Format import qualified Utility.Format
import Utility.Tmp import Utility.Tmp
import Command.AddUrl (addUrlFile, downloadRemoteFile, relaxedOption, rawOption) import Command.AddUrl (addUrlFile, downloadRemoteFile, parseRelaxedOption, parseRawOption)
import Annex.Perms import Annex.Perms
import Annex.UUID import Annex.UUID
import Backend.URL (fromUrl) import Backend.URL (fromUrl)
@ -43,34 +43,39 @@ import Types.MetaData
import Logs.MetaData import Logs.MetaData
import Annex.MetaData import Annex.MetaData
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ withOptions [templateOption, relaxedOption, rawOption] $ cmd = notBareRepo $
command "importfeed" (paramRepeating paramUrl) seek command "importfeed" SectionCommon "import files from podcast feeds"
SectionCommon "import files from podcast feeds"] (paramRepeating paramUrl) (seek <$$> optParser)
templateOption :: Option data ImportFeedOptions = ImportFeedOptions
templateOption = fieldOption [] "template" paramFormat "template for filenames" { feedUrls :: CmdParams
, templateOption :: Maybe String
seek :: CommandSeek , relaxedOption :: Bool
seek ps = do , rawOption :: Bool
tmpl <- getOptionField templateOption return
relaxed <- getOptionFlag relaxedOption
raw <- getOptionFlag rawOption
let opts = Opts { relaxedOpt = relaxed, rawOpt = raw }
cache <- getCache tmpl
withStrings (start opts cache) ps
data Opts = Opts
{ relaxedOpt :: Bool
, rawOpt :: Bool
} }
start :: Opts -> Cache -> URLString -> CommandStart optParser :: CmdParamsDesc -> Parser ImportFeedOptions
optParser desc = ImportFeedOptions
<$> cmdParams desc
<*> optional (strOption
( long "template" <> metavar paramFormat
<> help "template for filenames"
))
<*> parseRelaxedOption
<*> parseRawOption
seek :: ImportFeedOptions -> CommandSeek
seek o = do
cache <- getCache (templateOption o)
withStrings (start o cache) (feedUrls o)
start :: ImportFeedOptions -> Cache -> URLString -> CommandStart
start opts cache url = do start opts cache url = do
showStart "importfeed" url showStart "importfeed" url
next $ perform opts cache url next $ perform opts cache url
perform :: Opts -> Cache -> URLString -> CommandPerform perform :: ImportFeedOptions -> Cache -> URLString -> CommandPerform
perform opts cache url = do perform opts cache url = do
v <- findDownloads url v <- findDownloads url
case v of case v of
@ -160,15 +165,15 @@ downloadFeed url
, return Nothing , return Nothing
) )
performDownload :: Opts -> Cache -> ToDownload -> Annex Bool performDownload :: ImportFeedOptions -> Cache -> ToDownload -> Annex Bool
performDownload opts cache todownload = case location todownload of performDownload opts cache todownload = case location todownload of
Enclosure url -> checkknown url $ Enclosure url -> checkknown url $
rundownload url (takeExtension url) $ \f -> do rundownload url (takeExtension url) $ \f -> do
r <- Remote.claimingUrl url r <- Remote.claimingUrl url
if Remote.uuid r == webUUID || rawOpt opts if Remote.uuid r == webUUID || rawOption opts
then do then do
urlinfo <- Url.withUrlOptions (Url.getUrlInfo url) urlinfo <- Url.withUrlOptions (Url.getUrlInfo url)
maybeToList <$> addUrlFile (relaxedOpt opts) url urlinfo f maybeToList <$> addUrlFile (relaxedOption opts) url urlinfo f
else do else do
res <- tryNonAsync $ maybe res <- tryNonAsync $ maybe
(error $ "unable to checkUrl of " ++ Remote.name r) (error $ "unable to checkUrl of " ++ Remote.name r)
@ -178,10 +183,10 @@ performDownload opts cache todownload = case location todownload of
Left _ -> return [] Left _ -> return []
Right (UrlContents sz _) -> Right (UrlContents sz _) ->
maybeToList <$> maybeToList <$>
downloadRemoteFile r (relaxedOpt opts) url f sz downloadRemoteFile r (relaxedOption opts) url f sz
Right (UrlMulti l) -> do Right (UrlMulti l) -> do
kl <- forM l $ \(url', sz, subf) -> kl <- forM l $ \(url', sz, subf) ->
downloadRemoteFile r (relaxedOpt opts) url' (f </> fromSafeFilePath subf) sz downloadRemoteFile r (relaxedOption opts) url' (f </> fromSafeFilePath subf) sz
return $ if all isJust kl return $ if all isJust kl
then catMaybes kl then catMaybes kl
else [] else []
@ -199,7 +204,7 @@ performDownload opts cache todownload = case location todownload of
let videourl = Quvi.linkUrl link let videourl = Quvi.linkUrl link
checkknown videourl $ checkknown videourl $
rundownload videourl ("." ++ fromMaybe "m" (Quvi.linkSuffix link)) $ \f -> rundownload videourl ("." ++ fromMaybe "m" (Quvi.linkSuffix link)) $ \f ->
maybeToList <$> addUrlFileQuvi (relaxedOpt opts) quviurl videourl f maybeToList <$> addUrlFileQuvi (relaxedOption opts) quviurl videourl f
#else #else
return False return False
#endif #endif

View file

@ -11,11 +11,14 @@ import Common.Annex
import Command import Command
import Annex.Content import Annex.Content
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "inannex" (paramRepeating paramKey) seek cmd = noCommit $
SectionPlumbing "checks if keys are present in the annex"] command "inannex" SectionPlumbing
"checks if keys are present in the annex"
(paramRepeating paramKey)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withKeys start seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart

View file

@ -22,12 +22,12 @@ import Annex.CatFile
import Annex.Init import Annex.Init
import qualified Command.Add import qualified Command.Add
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ noDaemonRunning $ cmd = notBareRepo $ noDaemonRunning $
command "indirect" paramNothing seek command "indirect" SectionSetup "switch repository to indirect mode"
SectionSetup "switch repository to indirect mode"] paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -70,79 +70,94 @@ data StatInfo = StatInfo
, referencedData :: Maybe KeyData , referencedData :: Maybe KeyData
, repoData :: M.Map UUID KeyData , repoData :: M.Map UUID KeyData
, numCopiesStats :: Maybe NumCopiesStats , numCopiesStats :: Maybe NumCopiesStats
, infoOptions :: InfoOptions
} }
emptyStatInfo :: StatInfo emptyStatInfo :: InfoOptions -> StatInfo
emptyStatInfo = StatInfo Nothing Nothing M.empty Nothing emptyStatInfo = StatInfo Nothing Nothing M.empty Nothing
-- a state monad for running Stats in -- a state monad for running Stats in
type StatState = StateT StatInfo Annex type StatState = StateT StatInfo Annex
cmd :: [Command] cmd :: Command
cmd = [noCommit $ dontCheck repoExists $ withOptions (jsonOption : bytesOption : annexedMatchingOptions) $ cmd = noCommit $ dontCheck repoExists $ withGlobalOptions (jsonOption : annexedMatchingOptions) $
command "info" (paramOptional $ paramRepeating paramItem) seek SectionQuery command "info" SectionQuery
"shows information about the specified item or the repository as a whole"] "shows information about the specified item or the repository as a whole"
(paramRepeating paramItem) (seek <$$> optParser)
seek :: CommandSeek data InfoOptions = InfoOptions
seek = withWords start { infoFor :: CmdParams
, bytesOption :: Bool
}
start :: [String] -> CommandStart optParser :: CmdParamsDesc -> Parser InfoOptions
start [] = do optParser desc = InfoOptions
globalInfo <$> cmdParams desc
<*> switch
( long "bytes"
<> help "display file sizes in bytes"
)
seek :: InfoOptions -> CommandSeek
seek o = withWords (start o) (infoFor o)
start :: InfoOptions -> [String] -> CommandStart
start o [] = do
globalInfo o
stop stop
start ps = do start o ps = do
mapM_ itemInfo ps mapM_ (itemInfo o) ps
stop stop
globalInfo :: Annex () globalInfo :: InfoOptions -> Annex ()
globalInfo = do globalInfo o = do
stats <- selStats global_fast_stats global_slow_stats stats <- selStats global_fast_stats global_slow_stats
showCustom "info" $ do showCustom "info" $ do
evalStateT (mapM_ showStat stats) emptyStatInfo evalStateT (mapM_ showStat stats) (emptyStatInfo o)
return True return True
itemInfo :: String -> Annex () itemInfo :: InfoOptions -> String -> Annex ()
itemInfo p = ifM (isdir p) itemInfo o p = ifM (isdir p)
( dirInfo p ( dirInfo o p
, do , do
v <- Remote.byName' p v <- Remote.byName' p
case v of case v of
Right r -> remoteInfo r Right r -> remoteInfo o r
Left _ -> do Left _ -> do
v' <- Remote.nameToUUID' p v' <- Remote.nameToUUID' p
case v' of case v' of
Right u -> uuidInfo u Right u -> uuidInfo o u
Left _ -> maybe noinfo (fileInfo p) Left _ -> maybe noinfo (fileInfo o p)
=<< isAnnexLink p =<< isAnnexLink p
) )
where where
isdir = liftIO . catchBoolIO . (isDirectory <$$> getFileStatus) isdir = liftIO . catchBoolIO . (isDirectory <$$> getFileStatus)
noinfo = error $ p ++ " is not a directory or an annexed file or a remote or a uuid" noinfo = error $ p ++ " is not a directory or an annexed file or a remote or a uuid"
dirInfo :: FilePath -> Annex () dirInfo :: InfoOptions -> FilePath -> Annex ()
dirInfo dir = showCustom (unwords ["info", dir]) $ do dirInfo o dir = showCustom (unwords ["info", dir]) $ do
stats <- selStats (tostats dir_fast_stats) (tostats dir_slow_stats) stats <- selStats (tostats dir_fast_stats) (tostats dir_slow_stats)
evalStateT (mapM_ showStat stats) =<< getDirStatInfo dir evalStateT (mapM_ showStat stats) =<< getDirStatInfo o dir
return True return True
where where
tostats = map (\s -> s dir) tostats = map (\s -> s dir)
fileInfo :: FilePath -> Key -> Annex () fileInfo :: InfoOptions -> FilePath -> Key -> Annex ()
fileInfo file k = showCustom (unwords ["info", file]) $ do fileInfo o file k = showCustom (unwords ["info", file]) $ do
evalStateT (mapM_ showStat (file_stats file k)) emptyStatInfo evalStateT (mapM_ showStat (file_stats file k)) (emptyStatInfo o)
return True return True
remoteInfo :: Remote -> Annex () remoteInfo :: InfoOptions -> Remote -> Annex ()
remoteInfo r = showCustom (unwords ["info", Remote.name r]) $ do remoteInfo o r = showCustom (unwords ["info", Remote.name r]) $ do
info <- map (\(k, v) -> simpleStat k (pure v)) <$> Remote.getInfo r i <- map (\(k, v) -> simpleStat k (pure v)) <$> Remote.getInfo r
l <- selStats (remote_fast_stats r ++ info) (uuid_slow_stats (Remote.uuid r)) l <- selStats (remote_fast_stats r ++ i) (uuid_slow_stats (Remote.uuid r))
evalStateT (mapM_ showStat l) emptyStatInfo evalStateT (mapM_ showStat l) (emptyStatInfo o)
return True return True
uuidInfo :: UUID -> Annex () uuidInfo :: InfoOptions -> UUID -> Annex ()
uuidInfo u = showCustom (unwords ["info", fromUUID u]) $ do uuidInfo o u = showCustom (unwords ["info", fromUUID u]) $ do
l <- selStats [] ((uuid_slow_stats u)) l <- selStats [] ((uuid_slow_stats u))
evalStateT (mapM_ showStat l) emptyStatInfo evalStateT (mapM_ showStat l) (emptyStatInfo o)
return True return True
selStats :: [Stat] -> [Stat] -> Annex [Stat] selStats :: [Stat] -> [Stat] -> Annex [Stat]
@ -298,7 +313,7 @@ local_annex_keys = stat "local annex keys" $ json show $
local_annex_size :: Stat local_annex_size :: Stat
local_annex_size = simpleStat "local annex size" $ local_annex_size = simpleStat "local annex size" $
lift . showSizeKeys =<< cachedPresentData showSizeKeys =<< cachedPresentData
remote_annex_keys :: UUID -> Stat remote_annex_keys :: UUID -> Stat
remote_annex_keys u = stat "remote annex keys" $ json show $ remote_annex_keys u = stat "remote annex keys" $ json show $
@ -306,7 +321,7 @@ remote_annex_keys u = stat "remote annex keys" $ json show $
remote_annex_size :: UUID -> Stat remote_annex_size :: UUID -> Stat
remote_annex_size u = simpleStat "remote annex size" $ remote_annex_size u = simpleStat "remote annex size" $
lift . showSizeKeys =<< cachedRemoteData u showSizeKeys =<< cachedRemoteData u
known_annex_files :: Stat known_annex_files :: Stat
known_annex_files = stat "annexed files in working tree" $ json show $ known_annex_files = stat "annexed files in working tree" $ json show $
@ -314,7 +329,7 @@ known_annex_files = stat "annexed files in working tree" $ json show $
known_annex_size :: Stat known_annex_size :: Stat
known_annex_size = simpleStat "size of annexed files in working tree" $ known_annex_size = simpleStat "size of annexed files in working tree" $
lift . showSizeKeys =<< cachedReferencedData showSizeKeys =<< cachedReferencedData
tmp_size :: Stat tmp_size :: Stat
tmp_size = staleSize "temporary object directory size" gitAnnexTmpObjectDir tmp_size = staleSize "temporary object directory size" gitAnnexTmpObjectDir
@ -323,7 +338,7 @@ bad_data_size :: Stat
bad_data_size = staleSize "bad keys size" gitAnnexBadDir bad_data_size = staleSize "bad keys size" gitAnnexBadDir
key_size :: Key -> Stat key_size :: Key -> Stat
key_size k = simpleStat "size" $ lift $ showSizeKeys $ foldKeys [k] key_size k = simpleStat "size" $ showSizeKeys $ foldKeys [k]
key_name :: Key -> Stat key_name :: Key -> Stat
key_name k = simpleStat "key" $ pure $ key2file k key_name k = simpleStat "key" $ pure $ key2file k
@ -339,7 +354,7 @@ bloom_info = simpleStat "bloom filter size" $ do
-- Two bloom filters are used at the same time when running -- Two bloom filters are used at the same time when running
-- git-annex unused, so double the size of one. -- git-annex unused, so double the size of one.
sizer <- lift mkSizer sizer <- mkSizer
size <- sizer memoryUnits False . (* 2) . fromIntegral . fst <$> size <- sizer memoryUnits False . (* 2) . fromIntegral . fst <$>
lift bloomBitsHashes lift bloomBitsHashes
@ -371,10 +386,10 @@ transfer_list = stat desc $ nojson $ lift $ do
] ]
disk_size :: Stat disk_size :: Stat
disk_size = simpleStat "available local disk space" $ lift $ disk_size = simpleStat "available local disk space" $
calcfree calcfree
<$> (annexDiskReserve <$> Annex.getGitConfig) <$> (lift $ annexDiskReserve <$> Annex.getGitConfig)
<*> inRepo (getDiskFree . gitAnnexDir) <*> (lift $ inRepo $ getDiskFree . gitAnnexDir)
<*> mkSizer <*> mkSizer
where where
calcfree reserve (Just have) sizer = unwords calcfree reserve (Just have) sizer = unwords
@ -408,7 +423,7 @@ numcopies_stats = stat "numcopies stats" $ json fmt $
reposizes_stats :: Stat reposizes_stats :: Stat
reposizes_stats = stat desc $ nojson $ do reposizes_stats = stat desc $ nojson $ do
sizer <- lift mkSizer sizer <- mkSizer
l <- map (\(u, kd) -> (u, sizer storageUnits True (sizeKeys kd))) l <- map (\(u, kd) -> (u, sizer storageUnits True (sizeKeys kd)))
. sortBy (flip (comparing (sizeKeys . snd))) . sortBy (flip (comparing (sizeKeys . snd)))
. M.toList . M.toList
@ -465,14 +480,14 @@ cachedNumCopiesStats = numCopiesStats <$> get
cachedRepoData :: StatState (M.Map UUID KeyData) cachedRepoData :: StatState (M.Map UUID KeyData)
cachedRepoData = repoData <$> get cachedRepoData = repoData <$> get
getDirStatInfo :: FilePath -> Annex StatInfo getDirStatInfo :: InfoOptions -> FilePath -> Annex StatInfo
getDirStatInfo dir = do getDirStatInfo o dir = do
fast <- Annex.getState Annex.fast fast <- Annex.getState Annex.fast
matcher <- Limit.getMatcher matcher <- Limit.getMatcher
(presentdata, referenceddata, numcopiesstats, repodata) <- (presentdata, referenceddata, numcopiesstats, repodata) <-
Command.Unused.withKeysFilesReferencedIn dir initial Command.Unused.withKeysFilesReferencedIn dir initial
(update matcher fast) (update matcher fast)
return $ StatInfo (Just presentdata) (Just referenceddata) repodata (Just numcopiesstats) return $ StatInfo (Just presentdata) (Just referenceddata) repodata (Just numcopiesstats) o
where where
initial = (emptyKeyData, emptyKeyData, emptyNumCopiesStats, M.empty) initial = (emptyKeyData, emptyKeyData, emptyNumCopiesStats, M.empty)
update matcher fast key file vs@(presentdata, referenceddata, numcopiesstats, repodata) = update matcher fast key file vs@(presentdata, referenceddata, numcopiesstats, repodata) =
@ -529,7 +544,7 @@ updateNumCopiesStats file (NumCopiesStats m) locs = do
let !ret = NumCopiesStats m' let !ret = NumCopiesStats m'
return ret return ret
showSizeKeys :: KeyData -> Annex String showSizeKeys :: KeyData -> StatState String
showSizeKeys d = do showSizeKeys d = do
sizer <- mkSizer sizer <- mkSizer
return $ total sizer ++ missingnote return $ total sizer ++ missingnote
@ -549,7 +564,7 @@ staleSize label dirspec = go =<< lift (dirKeys dirspec)
onsize 0 = nostat onsize 0 = nostat
onsize size = stat label $ onsize size = stat label $
json (++ aside "clean up with git-annex unused") $ do json (++ aside "clean up with git-annex unused") $ do
sizer <- lift mkSizer sizer <- mkSizer
return $ sizer storageUnits False size return $ sizer storageUnits False size
keysizes keys = do keysizes keys = do
dir <- lift $ fromRepo dirspec dir <- lift $ fromRepo dirspec
@ -562,11 +577,8 @@ aside s = " (" ++ s ++ ")"
multiLine :: [String] -> String multiLine :: [String] -> String
multiLine = concatMap (\l -> "\n\t" ++ l) multiLine = concatMap (\l -> "\n\t" ++ l)
mkSizer :: Annex ([Unit] -> Bool -> ByteSize -> String) mkSizer :: StatState ([Unit] -> Bool -> ByteSize -> String)
mkSizer = ifM (getOptionFlag bytesOption) mkSizer = ifM (bytesOption . infoOptions <$> get)
( return (const $ const show) ( return (const $ const show)
, return roughSize , return roughSize
) )
bytesOption :: Option
bytesOption = flagOption [] "bytes" "display file sizes in bytes"

View file

@ -11,11 +11,12 @@ import Common.Annex
import Command import Command
import Annex.Init import Annex.Init
cmd :: [Command] cmd :: Command
cmd = [dontCheck repoExists $ cmd = dontCheck repoExists $
command "init" paramDesc seek SectionSetup "initialize git-annex"] command "init" SectionSetup "initialize git-annex"
paramDesc (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -19,12 +19,13 @@ import Logs.Trust
import Data.Ord import Data.Ord
cmd :: [Command] cmd :: Command
cmd = [command "initremote" cmd = command "initremote" SectionSetup
"creates a special (non-git) remote"
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue) (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
seek SectionSetup "creates a special (non-git) remote"] (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -20,28 +20,37 @@ import Remote
import Logs.Trust import Logs.Trust
import Logs.UUID import Logs.UUID
import Annex.UUID import Annex.UUID
import qualified Annex
import Git.Types (RemoteName) import Git.Types (RemoteName)
cmd :: [Command] cmd :: Command
cmd = [noCommit $ withOptions (allrepos : annexedMatchingOptions) $ cmd = noCommit $ withGlobalOptions annexedMatchingOptions $
command "list" paramPaths seek command "list" SectionQuery
SectionQuery "show which remotes contain files"] "show which remotes contain files"
paramPaths (seek <$$> optParser)
allrepos :: Option data ListOptions = ListOptions
allrepos = flagOption [] "allrepos" "show all repositories, not only remotes" { listThese :: CmdParams
, allRepos :: Bool
}
seek :: CommandSeek optParser :: CmdParamsDesc -> Parser ListOptions
seek ps = do optParser desc = ListOptions
list <- getList <$> cmdParams desc
<*> switch
( long "allrepos"
<> help "show all repositories, not only remotes"
)
seek :: ListOptions -> CommandSeek
seek o = do
list <- getList o
printHeader list printHeader list
withFilesInGit (whenAnnexed $ start list) ps withFilesInGit (whenAnnexed $ start list) (listThese o)
getList :: Annex [(UUID, RemoteName, TrustLevel)] getList :: ListOptions -> Annex [(UUID, RemoteName, TrustLevel)]
getList = ifM (Annex.getFlag $ optionName allrepos) getList o
( nubBy ((==) `on` fst3) <$> ((++) <$> getRemotes <*> getAllUUIDs) | allRepos o = nubBy ((==) `on` fst3) <$> ((++) <$> getRemotes <*> getAllUUIDs)
, getRemotes | otherwise = getRemotes
)
where where
getRemotes = do getRemotes = do
rs <- remoteList rs <- remoteList
@ -59,7 +68,7 @@ getList = ifM (Annex.getFlag $ optionName allrepos)
filter (\t -> thd3 t /= DeadTrusted) rs3 filter (\t -> thd3 t /= DeadTrusted) rs3
printHeader :: [(UUID, RemoteName, TrustLevel)] -> Annex () printHeader :: [(UUID, RemoteName, TrustLevel)] -> Annex ()
printHeader l = liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l printHeader l = liftIO $ putStrLn $ lheader $ map (\(_, n, t) -> (n, t)) l
start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> Key -> CommandStart start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> Key -> CommandStart
start l file key = do start l file key = do
@ -69,8 +78,8 @@ start l file key = do
type Present = Bool type Present = Bool
header :: [(RemoteName, TrustLevel)] -> String lheader :: [(RemoteName, TrustLevel)] -> String
header remotes = unlines (zipWith formatheader [0..] remotes) ++ pipes (length remotes) lheader remotes = unlines (zipWith formatheader [0..] remotes) ++ pipes (length remotes)
where where
formatheader n (remotename, trustlevel) = pipes n ++ remotename ++ trust trustlevel formatheader n (remotename, trustlevel) = pipes n ++ remotename ++ trust trustlevel
pipes = flip replicate '|' pipes = flip replicate '|'

View file

@ -12,12 +12,13 @@ import Command
import qualified Annex.Queue import qualified Annex.Queue
import qualified Annex import qualified Annex
cmd :: [Command] cmd :: Command
cmd = [notDirect $ withOptions annexedMatchingOptions $ cmd = notDirect $ withGlobalOptions annexedMatchingOptions $
command "lock" paramPaths seek SectionCommon command "lock" SectionCommon
"undo unlock command"] "undo unlock command"
paramPaths (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek ps = do seek ps = do
withFilesUnlocked start ps withFilesUnlocked start ps
withFilesUnlockedToBeCommitted start ps withFilesUnlockedToBeCommitted start ps

View file

@ -38,52 +38,62 @@ data RefChange = RefChange
type Outputter = Bool -> POSIXTime -> [UUID] -> Annex () type Outputter = Bool -> POSIXTime -> [UUID] -> Annex ()
cmd :: [Command] cmd :: Command
cmd = [withOptions options $ cmd = withGlobalOptions annexedMatchingOptions $
command "log" paramPaths seek SectionQuery "shows location log"] command "log" SectionQuery "shows location log"
paramPaths (seek <$$> optParser)
options :: [Option] data LogOptions = LogOptions
options = passthruOptions ++ [gourceOption] ++ annexedMatchingOptions { logFiles :: CmdParams
, gourceOption :: Bool
, passthruOptions :: [CommandParam]
}
passthruOptions :: [Option] optParser :: CmdParamsDesc -> Parser LogOptions
passthruOptions = map odate ["since", "after", "until", "before"] ++ optParser desc = LogOptions
[ fieldOption ['n'] "max-count" paramNumber <$> cmdParams desc
"limit number of logs displayed" <*> switch
] ( long "gource"
<> help "format output for gource"
)
<*> (concat <$> many passthru)
where where
odate n = fieldOption [] n paramDate $ "show log " ++ n ++ " date" passthru :: Parser [CommandParam]
passthru = datepassthru "since"
<|> datepassthru "after"
<|> datepassthru "until"
<|> datepassthru "before"
<|> (mkpassthru "max-count" <$> strOption
( long "max-count" <> metavar paramNumber
<> help "limit number of logs displayed"
))
datepassthru n = mkpassthru n <$> strOption
( long n <> metavar paramDate
<> help ("show log " ++ n ++ " date")
)
mkpassthru n v = [Param ("--" ++ n), Param v]
gourceOption :: Option seek :: LogOptions -> CommandSeek
gourceOption = flagOption [] "gource" "format output for gource" seek o = do
seek :: CommandSeek
seek ps = do
m <- Remote.uuidDescriptions m <- Remote.uuidDescriptions
zone <- liftIO getCurrentTimeZone zone <- liftIO getCurrentTimeZone
os <- concat <$> mapM getoption passthruOptions withFilesInGit (whenAnnexed $ start m zone o) (logFiles o)
gource <- getOptionFlag gourceOption
withFilesInGit (whenAnnexed $ start m zone os gource) ps
where
getoption o = maybe [] (use o) <$>
Annex.getField (optionName o)
use o v = [Param ("--" ++ optionName o), Param v]
start start
:: M.Map UUID String :: M.Map UUID String
-> TimeZone -> TimeZone
-> [CommandParam] -> LogOptions
-> Bool
-> FilePath -> FilePath
-> Key -> Key
-> CommandStart -> CommandStart
start m zone os gource file key = do start m zone o file key = do
showLog output =<< readLog <$> getLog key os showLog output =<< readLog <$> getLog key (passthruOptions o)
-- getLog produces a zombie; reap it -- getLog produces a zombie; reap it
liftIO reapZombies liftIO reapZombies
stop stop
where where
output output
| gource = gourceOutput lookupdescription file | (gourceOption o) = gourceOutput lookupdescription file
| otherwise = normalOutput lookupdescription file zone | otherwise = normalOutput lookupdescription file zone
lookupdescription u = fromMaybe (fromUUID u) $ M.lookup u m lookupdescription u = fromMaybe (fromUUID u) $ M.lookup u m

View file

@ -13,16 +13,18 @@ import CmdLine.Batch
import Annex.CatFile import Annex.CatFile
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [withOptions [batchOption] $ notBareRepo $ noCommit $ noMessages $ cmd = notBareRepo $ noCommit $ noMessages $
command "lookupkey" (paramRepeating paramFile) seek command "lookupkey" SectionPlumbing
SectionPlumbing "looks up key used for file"] "looks up key used for file"
(paramRepeating paramFile)
(batchable run (pure ()))
seek :: CommandSeek run :: () -> String -> Annex Bool
seek = batchable withStrings start run _ file = do
mk <- catKeyFile file
start :: Batchable String case mk of
start batchmode file = do Just k -> do
maybe (batchBadInput batchmode) (liftIO . putStrLn . key2file) liftIO $ putStrLn $ key2file k
=<< catKeyFile file return True
stop Nothing -> return False

View file

@ -25,12 +25,13 @@ import qualified Utility.Dot as Dot
-- a link from the first repository to the second (its remote) -- a link from the first repository to the second (its remote)
data Link = Link Git.Repo Git.Repo data Link = Link Git.Repo Git.Repo
cmd :: [Command] cmd :: Command
cmd = [dontCheck repoExists $ cmd = dontCheck repoExists $
command "map" paramNothing seek SectionQuery command "map" SectionQuery
"generate map of repositories"] "generate map of repositories"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -13,11 +13,12 @@ import qualified Annex.Branch
import qualified Git.Branch import qualified Git.Branch
import Command.Sync (prepMerge, mergeLocal) import Command.Sync (prepMerge, mergeLocal)
cmd :: [Command] cmd :: Command
cmd = [command "merge" paramNothing seek SectionMaintenance cmd = command "merge" SectionMaintenance
"automatically merge changes from remotes"] "automatically merge changes from remotes"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek ps = do seek ps = do
withNothing mergeBranch ps withNothing mergeBranch ps
withNothing mergeSynced ps withNothing mergeSynced ps

View file

@ -8,7 +8,6 @@
module Command.MetaData where module Command.MetaData where
import Common.Annex import Common.Annex
import qualified Annex
import Command import Command
import Annex.MetaData import Annex.MetaData
import Logs.MetaData import Logs.MetaData
@ -16,71 +15,70 @@ import Logs.MetaData
import qualified Data.Set as S import qualified Data.Set as S
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
cmd :: [Command] cmd :: Command
cmd = [withOptions metaDataOptions $ cmd = withGlobalOptions ([jsonOption] ++ annexedMatchingOptions) $
command "metadata" paramPaths seek command "metadata" SectionMetaData
SectionMetaData "sets or gets metadata of a file"] "sets or gets metadata of a file"
paramPaths (seek <$$> optParser)
metaDataOptions :: [Option] data MetaDataOptions = MetaDataOptions
metaDataOptions = { forFiles :: CmdParams
[ setOption , getSet :: GetSet
, tagOption , keyOptions :: Maybe KeyOptions
, untagOption }
, getOption
, jsonOption
] ++ keyOptions ++ annexedMatchingOptions
storeModMeta :: ModMeta -> Annex () data GetSet = Get MetaField | Set [ModMeta]
storeModMeta modmeta = Annex.changeState $
\s -> s { Annex.modmeta = modmeta:Annex.modmeta s }
setOption :: Option optParser :: CmdParamsDesc -> Parser MetaDataOptions
setOption = Option ['s'] ["set"] (ReqArg mkmod "FIELD[+-]=VALUE") "set metadata" optParser desc = MetaDataOptions
<$> cmdParams desc
<*> ((Get <$> getopt) <|> (Set <$> many modopts))
<*> optional (parseKeyOptions False)
where where
mkmod = either error storeModMeta . parseModMeta getopt = option (eitherReader mkMetaField)
( long "get" <> short 'g' <> metavar paramField
<> help "get single metadata field"
)
modopts = option (eitherReader parseModMeta)
( long "set" <> short 's' <> metavar "FIELD[+-]=VALUE"
<> help "set or unset metadata value"
)
<|> (AddMeta tagMetaField . toMetaValue <$> strOption
( long "tag" <> short 't' <> metavar "TAG"
<> help "set a tag"
))
<|> (AddMeta tagMetaField . mkMetaValue (CurrentlySet False) <$> strOption
( long "untag" <> short 'u' <> metavar "TAG"
<> help "remove a tag"
))
getOption :: Option seek :: MetaDataOptions -> CommandSeek
getOption = fieldOption ['g'] "get" paramField "get single metadata field" seek o = do
tagOption :: Option
tagOption = Option ['t'] ["tag"] (ReqArg mkmod "TAG") "set a tag"
where
mkmod = storeModMeta . AddMeta tagMetaField . toMetaValue
untagOption :: Option
untagOption = Option ['u'] ["untag"] (ReqArg mkmod "TAG") "remove a tag"
where
mkmod = storeModMeta . AddMeta tagMetaField . mkMetaValue (CurrentlySet False)
seek :: CommandSeek
seek ps = do
modmeta <- Annex.getState Annex.modmeta
getfield <- getOptionField getOption $ \ms ->
return $ either error id . mkMetaField <$> ms
now <- liftIO getPOSIXTime now <- liftIO getPOSIXTime
let seeker = if null modmeta let seeker = case getSet o of
then withFilesInGit Get _ -> withFilesInGit
else withFilesInGitNonRecursive Set _ -> withFilesInGitNonRecursive
withKeyOptions False withKeyOptions (keyOptions o) False
(startKeys now getfield modmeta) (startKeys now o)
(seeker $ whenAnnexed $ start now getfield modmeta) (seeker $ whenAnnexed $ start now o)
ps (forFiles o)
start :: POSIXTime -> Maybe MetaField -> [ModMeta] -> FilePath -> Key -> CommandStart start :: POSIXTime -> MetaDataOptions -> FilePath -> Key -> CommandStart
start now f ms file = start' (Just file) now f ms start now o file = start' (Just file) now o
startKeys :: POSIXTime -> Maybe MetaField -> [ModMeta] -> Key -> CommandStart startKeys :: POSIXTime -> MetaDataOptions -> Key -> CommandStart
startKeys = start' Nothing startKeys = start' Nothing
start' :: AssociatedFile -> POSIXTime -> Maybe MetaField -> [ModMeta] -> Key -> CommandStart start' :: AssociatedFile -> POSIXTime -> MetaDataOptions -> Key -> CommandStart
start' afile now Nothing ms k = do start' afile now o k = case getSet o of
showStart' "metadata" k afile Set ms -> do
next $ perform now ms k showStart' "metadata" k afile
start' _ _ (Just f) _ k = do next $ perform now ms k
l <- S.toList . currentMetaDataValues f <$> getCurrentMetaData k Get f -> do
liftIO $ forM_ l $ l <- S.toList . currentMetaDataValues f <$> getCurrentMetaData k
putStrLn . fromMetaValue liftIO $ forM_ l $
stop putStrLn . fromMetaValue
stop
perform :: POSIXTime -> [ModMeta] -> Key -> CommandPerform perform :: POSIXTime -> [ModMeta] -> Key -> CommandPerform
perform _ [] k = next $ cleanup k perform _ [] k = next $ cleanup k

View file

@ -18,12 +18,13 @@ import qualified Command.ReKey
import qualified Command.Fsck import qualified Command.Fsck
import qualified Annex import qualified Annex
cmd :: [Command] cmd :: Command
cmd = [notDirect $ withOptions annexedMatchingOptions $ cmd = notDirect $ withGlobalOptions annexedMatchingOptions $
command "migrate" paramPaths seek command "migrate" SectionUtility
SectionUtility "switch data to different backend"] "switch data to different backend"
paramPaths (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withFilesInGit $ whenAnnexed start seek = withFilesInGit $ whenAnnexed start
start :: FilePath -> Key -> CommandStart start :: FilePath -> Key -> CommandStart

View file

@ -16,41 +16,49 @@ import qualified Remote
import Annex.Content import Annex.Content
import Annex.NumCopies import Annex.NumCopies
cmd :: [Command] cmd :: Command
cmd = [withOptions mirrorOptions $ command "mirror" paramPaths seek cmd = withGlobalOptions ([jobsOption] ++ annexedMatchingOptions) $
SectionCommon "mirror content of files to/from another repository"] command "mirror" SectionCommon
"mirror content of files to/from another repository"
paramPaths (seek <--< optParser)
mirrorOptions :: [Option] data MirrorOptions = MirrorOptions
mirrorOptions = fromToOptions ++ [jobsOption] ++ annexedMatchingOptions ++ keyOptions { mirrorFiles :: CmdParams
, fromToOptions :: FromToOptions
, keyOptions :: Maybe KeyOptions
}
seek :: CommandSeek optParser :: CmdParamsDesc -> Parser MirrorOptions
seek ps = do optParser desc = MirrorOptions
to <- getOptionField toOption Remote.byNameWithUUID <$> cmdParams desc
from <- getOptionField fromOption Remote.byNameWithUUID <*> parseFromToOptions
withKeyOptions False <*> optional (parseKeyOptions False)
(startKey to from Nothing)
(withFilesInGit $ whenAnnexed $ start to from)
ps
start :: Maybe Remote -> Maybe Remote -> FilePath -> Key -> CommandStart instance DeferredParseClass MirrorOptions where
start to from file = startKey to from (Just file) finishParse v = MirrorOptions
<$> pure (mirrorFiles v)
<*> finishParse (fromToOptions v)
<*> pure (keyOptions v)
startKey :: Maybe Remote -> Maybe Remote -> Maybe FilePath -> Key -> CommandStart seek :: MirrorOptions -> CommandSeek
startKey to from afile key = seek o = withKeyOptions (keyOptions o) False
case (from, to) of (startKey o Nothing)
(Nothing, Nothing) -> error "specify either --from or --to" (withFilesInGit $ whenAnnexed $ start o)
(Nothing, Just r) -> mirrorto r (mirrorFiles o)
(Just r, Nothing) -> mirrorfrom r
_ -> error "only one of --from or --to can be specified" start :: MirrorOptions -> FilePath -> Key -> CommandStart
where start o file = startKey o (Just file)
mirrorto r = ifM (inAnnex key)
( Command.Move.toStart r False afile key startKey :: MirrorOptions -> Maybe FilePath -> Key -> CommandStart
startKey o afile key = case fromToOptions o of
ToRemote r -> ifM (inAnnex key)
( Command.Move.toStart False afile key =<< getParsed r
, do , do
numcopies <- getnumcopies numcopies <- getnumcopies
Command.Drop.startRemote afile numcopies key r Command.Drop.startRemote afile numcopies key =<< getParsed r
) )
mirrorfrom r = do FromRemote r -> do
haskey <- Remote.hasKey r key haskey <- flip Remote.hasKey key =<< getParsed r
case haskey of case haskey of
Left _ -> stop Left _ -> stop
Right True -> Command.Get.start' (return True) Nothing key afile Right True -> Command.Get.start' (return True) Nothing key afile
@ -60,4 +68,5 @@ startKey to from afile key =
Command.Drop.startLocal afile numcopies key Nothing Command.Drop.startLocal afile numcopies key Nothing
, stop , stop
) )
where
getnumcopies = maybe getNumCopies getFileNumCopies afile getnumcopies = maybe getNumCopies getFileNumCopies afile

View file

@ -17,35 +17,47 @@ import Annex.UUID
import Annex.Transfer import Annex.Transfer
import Logs.Presence import Logs.Presence
cmd :: [Command] cmd :: Command
cmd = [withOptions moveOptions $ command "move" paramPaths seek cmd = withGlobalOptions (jobsOption : annexedMatchingOptions) $
SectionCommon "move content of files to/from another repository"] command "move" SectionCommon
"move content of files to/from another repository"
paramPaths (seek <--< optParser)
moveOptions :: [Option] data MoveOptions = MoveOptions
moveOptions = fromToOptions ++ [jobsOption] ++ keyOptions ++ annexedMatchingOptions { moveFiles :: CmdParams
, fromToOptions :: FromToOptions
, keyOptions :: Maybe KeyOptions
}
seek :: CommandSeek optParser :: CmdParamsDesc -> Parser MoveOptions
seek ps = do optParser desc = MoveOptions
to <- getOptionField toOption Remote.byNameWithUUID <$> cmdParams desc
from <- getOptionField fromOption Remote.byNameWithUUID <*> parseFromToOptions
withKeyOptions False <*> optional (parseKeyOptions False)
(startKey to from True)
(withFilesInGit $ whenAnnexed $ start to from True)
ps
start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> Key -> CommandStart instance DeferredParseClass MoveOptions where
start to from move = start' to from move . Just finishParse v = MoveOptions
<$> pure (moveFiles v)
<*> finishParse (fromToOptions v)
<*> pure (keyOptions v)
startKey :: Maybe Remote -> Maybe Remote -> Bool -> Key -> CommandStart seek :: MoveOptions -> CommandSeek
startKey to from move = start' to from move Nothing seek o = withKeyOptions (keyOptions o) False
(startKey o True)
(withFilesInGit $ whenAnnexed $ start o True)
(moveFiles o)
start' :: Maybe Remote -> Maybe Remote -> Bool -> AssociatedFile -> Key -> CommandStart start :: MoveOptions -> Bool -> FilePath -> Key -> CommandStart
start' to from move afile key = do start o move = start' o move . Just
case (from, to) of
(Nothing, Nothing) -> error "specify either --from or --to" startKey :: MoveOptions -> Bool -> Key -> CommandStart
(Nothing, Just dest) -> toStart dest move afile key startKey o move = start' o move Nothing
(Just src, Nothing) -> fromStart src move afile key
_ -> error "only one of --from or --to can be specified" start' :: MoveOptions -> Bool -> AssociatedFile -> Key -> CommandStart
start' o move afile key =
case fromToOptions o of
FromRemote src -> fromStart move afile key =<< getParsed src
ToRemote dest -> toStart move afile key =<< getParsed dest
showMoveAction :: Bool -> Key -> AssociatedFile -> Annex () showMoveAction :: Bool -> Key -> AssociatedFile -> Annex ()
showMoveAction move = showStart' (if move then "move" else "copy") showMoveAction move = showStart' (if move then "move" else "copy")
@ -59,8 +71,8 @@ showMoveAction move = showStart' (if move then "move" else "copy")
- A file's content can be moved even if there are insufficient copies to - A file's content can be moved even if there are insufficient copies to
- allow it to be dropped. - allow it to be dropped.
-} -}
toStart :: Remote -> Bool -> AssociatedFile -> Key -> CommandStart toStart :: Bool -> AssociatedFile -> Key -> Remote -> CommandStart
toStart dest move afile key = do toStart move afile key dest = do
u <- getUUID u <- getUUID
ishere <- inAnnex key ishere <- inAnnex key
if not ishere || u == Remote.uuid dest if not ishere || u == Remote.uuid dest
@ -122,8 +134,8 @@ toPerform dest move key afile fastcheck isthere =
- If the current repository already has the content, it is still removed - If the current repository already has the content, it is still removed
- from the remote. - from the remote.
-} -}
fromStart :: Remote -> Bool -> AssociatedFile -> Key -> CommandStart fromStart :: Bool -> AssociatedFile -> Key -> Remote -> CommandStart
fromStart src move afile key fromStart move afile key src
| move = go | move = go
| otherwise = stopUnless (not <$> inAnnex key) go | otherwise = stopUnless (not <$> inAnnex key) go
where where

View file

@ -19,11 +19,13 @@ import Control.Concurrent
import Control.Concurrent.Async import Control.Concurrent.Async
import Control.Concurrent.STM import Control.Concurrent.STM
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "notifychanges" paramNothing seek SectionPlumbing cmd = noCommit $
"sends notification when git refs are changed"] command "notifychanges" SectionPlumbing
"sends notification when git refs are changed"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -13,11 +13,12 @@ import Command
import Annex.NumCopies import Annex.NumCopies
import Types.Messages import Types.Messages
cmd :: [Command] cmd :: Command
cmd = [command "numcopies" paramNumber seek cmd = command "numcopies" SectionSetup
SectionSetup "configure desired number of copies"] "configure desired number of copies"
paramNumber (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -28,11 +28,13 @@ import qualified Git.LsFiles as Git
import qualified Data.Set as S import qualified Data.Set as S
cmd :: [Command] cmd :: Command
cmd = [command "pre-commit" paramPaths seek SectionPlumbing cmd = command "pre-commit" SectionPlumbing
"run by git pre-commit hook"] "run by git pre-commit hook"
paramPaths
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek ps = lockPreCommitHook $ ifM isDirect seek ps = lockPreCommitHook $ ifM isDirect
( do ( do
-- update direct mode mappings for committed files -- update direct mode mappings for committed files

View file

@ -17,12 +17,13 @@ import qualified Git.Sha
import qualified Git.Ref import qualified Git.Ref
import qualified Git.Branch import qualified Git.Branch
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ cmd = notBareRepo $
command "proxy" ("-- git command") seek command "proxy" SectionPlumbing
SectionPlumbing "safely bypass direct mode guard"] "safely bypass direct mode guard"
("-- git command") (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -18,12 +18,14 @@ import Logs.Location
import Utility.CopyFile import Utility.CopyFile
import qualified Remote import qualified Remote
cmd :: [Command] cmd :: Command
cmd = [notDirect $ command "rekey" cmd = notDirect $
(paramOptional $ paramRepeating $ paramPair paramPath paramKey) command "rekey" SectionPlumbing
seek SectionPlumbing "change keys used for files"] "change keys used for files"
(paramRepeating $ paramPair paramPath paramKey)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withPairs start seek = withPairs start
start :: (FilePath, String) -> CommandStart start :: (FilePath, String) -> CommandStart

View file

@ -12,11 +12,14 @@ import Command
import Logs.Location import Logs.Location
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "readpresentkey" (paramPair paramKey paramUUID) seek cmd = noCommit $
SectionPlumbing "read records of where key is present"] command "readpresentkey" SectionPlumbing
"read records of where key is present"
(paramPair paramKey paramUUID)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -20,11 +20,12 @@ import qualified Types.Key
import qualified Types.Backend import qualified Types.Backend
import qualified Backend import qualified Backend
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "recvkey" paramKey seek cmd = noCommit $ command "recvkey" SectionPlumbing
SectionPlumbing "runs rsync in server mode to receive content"] "runs rsync in server mode to receive content"
paramKey (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withKeys start seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart

View file

@ -15,12 +15,14 @@ import Logs.Web
import Annex.UUID import Annex.UUID
import Command.FromKey (mkKey) import Command.FromKey (mkKey)
cmd :: [Command] cmd :: Command
cmd = [notDirect $ notBareRepo $ cmd = notDirect $ notBareRepo $
command "registerurl" (paramPair paramKey paramUrl) seek command "registerurl"
SectionPlumbing "registers an url for a key"] SectionPlumbing "registers an url for a key"
(paramPair paramKey paramUrl)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -14,11 +14,14 @@ import Annex.UUID
import Types.UUID import Types.UUID
import qualified Remote import qualified Remote
cmd :: [Command] cmd :: Command
cmd = [dontCheck repoExists $ cmd = dontCheck repoExists $
command "reinit" (paramUUID ++ "|" ++ paramDesc) seek SectionUtility "initialize repository, reusing old UUID"] command "reinit" SectionUtility
"initialize repository, reusing old UUID"
(paramUUID ++ "|" ++ paramDesc)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -14,11 +14,12 @@ import Annex.Content
import qualified Command.Fsck import qualified Command.Fsck
import qualified Backend import qualified Backend
cmd :: [Command] cmd :: Command
cmd = [command "reinject" (paramPair "SRC" "DEST") seek cmd = command "reinject" SectionUtility
SectionUtility "sets content of annexed file"] "sets content of annexed file"
(paramPair "SRC" "DEST") (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [FilePath] -> CommandStart start :: [FilePath] -> CommandStart

View file

@ -11,11 +11,13 @@ import Common.Annex
import Command import Command
import RemoteDaemon.Core import RemoteDaemon.Core
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "remotedaemon" paramNothing seek SectionPlumbing cmd = noCommit $
"detects when remotes have changed, and fetches from them"] command "remotedaemon" SectionPlumbing
"detects when remotes have changed, and fetches from them"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -16,11 +16,13 @@ import qualified Git.Ref
import Git.Types import Git.Types
import Annex.Version import Annex.Version
cmd :: [Command] cmd :: Command
cmd = [noCommit $ dontCheck repoExists $ cmd = noCommit $ dontCheck repoExists $
command "repair" paramNothing seek SectionMaintenance "recover broken git repository"] command "repair" SectionMaintenance
"recover broken git repository"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -11,7 +11,7 @@ import Command
import Logs.PreferredContent import Logs.PreferredContent
import qualified Command.Wanted import qualified Command.Wanted
cmd :: [Command] cmd :: Command
cmd = Command.Wanted.cmd' "required" "get or set required content expression" cmd = Command.Wanted.cmd' "required" "get or set required content expression"
requiredContentMapRaw requiredContentMapRaw
requiredContentSet requiredContentSet

View file

@ -14,11 +14,12 @@ import Git.Sha
import qualified Git.Branch import qualified Git.Branch
import Annex.AutoMerge import Annex.AutoMerge
cmd :: [Command] cmd :: Command
cmd = [command "resolvemerge" paramNothing seek SectionPlumbing cmd = command "resolvemerge" SectionPlumbing
"resolve merge conflicts"] "resolve merge conflicts"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -13,12 +13,14 @@ import Logs.Web
import Annex.UUID import Annex.UUID
import qualified Remote import qualified Remote
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ cmd = notBareRepo $
command "rmurl" (paramPair paramFile paramUrl) seek command "rmurl" SectionCommon
SectionCommon "record file is not available at url"] "record file is not available at url"
(paramPair paramFile paramUrl)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withPairs start seek = withPairs start
start :: (FilePath, String) -> CommandStart start :: (FilePath, String) -> CommandStart

View file

@ -17,11 +17,12 @@ import Types.Messages
import qualified Data.Set as S import qualified Data.Set as S
cmd :: [Command] cmd :: Command
cmd = [command "schedule" (paramPair paramRemote (paramOptional paramExpression)) seek cmd = command "schedule" SectionSetup "get or set scheduled jobs"
SectionSetup "get or set scheduled jobs"] (paramPair paramRemote (paramOptional paramExpression))
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -11,9 +11,10 @@ import Command
import Types.TrustLevel import Types.TrustLevel
import Command.Trust (trustCommand) import Command.Trust (trustCommand)
cmd :: [Command] cmd :: Command
cmd = [command "semitrust" (paramRepeating paramRemote) seek cmd = command "semitrust" SectionSetup
SectionSetup "return repository to default trust level"] "return repository to default trust level"
(paramRepeating paramRemote) (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = trustCommand "semitrust" SemiTrusted seek = trustCommand "semitrust" SemiTrusted

View file

@ -16,11 +16,13 @@ import Annex.Transfer
import qualified CmdLine.GitAnnexShell.Fields as Fields import qualified CmdLine.GitAnnexShell.Fields as Fields
import Utility.Metered import Utility.Metered
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "sendkey" paramKey seek cmd = noCommit $
SectionPlumbing "runs rsync in server mode to send content"] command "sendkey" SectionPlumbing
"runs rsync in server mode to send content"
paramKey (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withKeys start seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart

View file

@ -13,11 +13,12 @@ import Logs.Location
import Annex.Content import Annex.Content
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [command "setkey" (paramPair paramKey paramPath) seek cmd = command "setkey" SectionPlumbing "sets annexed content for a key"
SectionPlumbing "sets annexed content for a key"] (paramPair paramKey paramPath)
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -13,11 +13,14 @@ import Logs.Location
import Logs.Presence.Pure import Logs.Presence.Pure
import Types.Key import Types.Key
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "setpresentkey" (paramPair paramKey (paramPair paramUUID "[1|0]")) seek cmd = noCommit $
SectionPlumbing "change records of where key is present"] command "setpresentkey" SectionPlumbing
"change records of where key is present"
(paramPair paramKey (paramPair paramUUID "[1|0]"))
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -16,12 +16,13 @@ import qualified Git.LsFiles as LsFiles
import qualified Git.Ref import qualified Git.Ref
import qualified Git import qualified Git
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ noCommit $ noMessages $ withOptions [jsonOption] $ cmd = notBareRepo $ noCommit $ noMessages $ withGlobalOptions [jsonOption] $
command "status" paramPaths seek SectionCommon command "status" SectionCommon
"show the working tree status"] "show the working tree status"
paramPaths (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [FilePath] -> CommandStart start :: [FilePath] -> CommandStart

View file

@ -51,26 +51,33 @@ import Utility.Bloom
import Control.Concurrent.MVar import Control.Concurrent.MVar
import qualified Data.Map as M import qualified Data.Map as M
cmd :: [Command] cmd :: Command
cmd = [withOptions syncOptions $ cmd = command "sync" SectionCommon
command "sync" (paramOptional (paramRepeating paramRemote)) "synchronize local repository with remotes"
seek SectionCommon "synchronize local repository with remotes"] (paramRepeating paramRemote) (seek <$$> optParser)
syncOptions :: [Option] data SyncOptions = SyncOptions
syncOptions = { syncWith :: CmdParams
[ contentOption , contentOption :: Bool
, messageOption , messageOption :: Maybe String
, allOption , keyOptions :: Maybe KeyOptions
] }
contentOption :: Option optParser :: CmdParamsDesc -> Parser SyncOptions
contentOption = flagOption [] "content" "also transfer file contents" optParser desc = SyncOptions
<$> cmdParams desc
<*> switch
( long "content"
<> help "also transfer file contents"
)
<*> optional (strOption
( long "message" <> short 'm' <> metavar "MSG"
<> help "commit message"
))
<*> optional parseAllOption
messageOption :: Option seek :: SyncOptions -> CommandSeek
messageOption = fieldOption ['m'] "message" "MSG" "specify commit message" seek o = do
seek :: CommandSeek
seek rs = do
prepMerge prepMerge
-- There may not be a branch checked out until after the commit, -- There may not be a branch checked out until after the commit,
@ -89,20 +96,20 @@ seek rs = do
) )
let withbranch a = a =<< getbranch let withbranch a = a =<< getbranch
remotes <- syncRemotes rs remotes <- syncRemotes (syncWith o)
let gitremotes = filter Remote.gitSyncableRemote remotes let gitremotes = filter Remote.gitSyncableRemote remotes
let dataremotes = filter (not . remoteAnnexIgnore . Remote.gitconfig) remotes let dataremotes = filter (not . remoteAnnexIgnore . Remote.gitconfig) remotes
-- Syncing involves many actions, any of which can independently -- Syncing involves many actions, any of which can independently
-- fail, without preventing the others from running. -- fail, without preventing the others from running.
seekActions $ return $ concat seekActions $ return $ concat
[ [ commit ] [ [ commit o ]
, [ withbranch mergeLocal ] , [ withbranch mergeLocal ]
, map (withbranch . pullRemote) gitremotes , map (withbranch . pullRemote) gitremotes
, [ mergeAnnex ] , [ mergeAnnex ]
] ]
whenM (Annex.getFlag $ optionName contentOption) $ when (contentOption o) $
whenM (seekSyncContent dataremotes) $ whenM (seekSyncContent o dataremotes) $
-- Transferring content can take a while, -- Transferring content can take a while,
-- and other changes can be pushed to the git-annex -- and other changes can be pushed to the git-annex
-- branch on the remotes in the meantime, so pull -- branch on the remotes in the meantime, so pull
@ -150,15 +157,14 @@ syncRemotes rs = ifM (Annex.getState Annex.fast) ( nub <$> pickfast , wanted )
fastest = fromMaybe [] . headMaybe . Remote.byCost fastest = fromMaybe [] . headMaybe . Remote.byCost
commit :: CommandStart commit :: SyncOptions -> CommandStart
commit = ifM (annexAutoCommit <$> Annex.getGitConfig) commit o = ifM (annexAutoCommit <$> Annex.getGitConfig)
( go ( go
, stop , stop
) )
where where
go = next $ next $ do go = next $ next $ do
commitmessage <- maybe commitMsg return commitmessage <- maybe commitMsg return (messageOption o)
=<< Annex.getField (optionName messageOption)
showStart "commit" "" showStart "commit" ""
Annex.Branch.commit "update" Annex.Branch.commit "update"
ifM isDirect ifM isDirect
@ -371,14 +377,16 @@ newer remote b = do
- -
- If any file movements were generated, returns true. - If any file movements were generated, returns true.
-} -}
seekSyncContent :: [Remote] -> Annex Bool seekSyncContent :: SyncOptions -> [Remote] -> Annex Bool
seekSyncContent rs = do seekSyncContent o rs = do
mvar <- liftIO newEmptyMVar mvar <- liftIO newEmptyMVar
bloom <- ifM (Annex.getFlag "all") bloom <- case keyOptions o of
( Just <$> genBloomFilter (seekworktree mvar []) Just WantAllKeys -> Just <$> genBloomFilter (seekworktree mvar [])
, seekworktree mvar [] (const noop) >> pure Nothing _ -> seekworktree mvar [] (const noop) >> pure Nothing
) withKeyOptions' (keyOptions o) False
withKeyOptions' False (seekkeys mvar bloom) (const noop) [] (seekkeys mvar bloom)
(const noop)
[]
liftIO $ not <$> isEmptyMVar mvar liftIO $ not <$> isEmptyMVar mvar
where where
seekworktree mvar l bloomfeeder = seekHelper LsFiles.inRepo l >>= seekworktree mvar l bloomfeeder = seekHelper LsFiles.inRepo l >>=

View file

@ -1,6 +1,6 @@
{- git-annex command {- git-annex command
- -
- Copyright 2013 Joey Hess <id@joeyh.name> - Copyright 2013-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -10,28 +10,23 @@ module Command.Test where
import Common import Common
import Command import Command
import Messages import Messages
import Types.Test
cmd :: [Command] cmd :: Parser TestOptions -> Maybe TestRunner -> Command
cmd = [ noRepo startIO $ dontCheck repoExists $ cmd optparser runner = noRepo (startIO runner <$$> const optparser) $
command "test" paramNothing seek SectionTesting dontCheck repoExists $
"run built-in test suite"] command "test" SectionTesting
"run built-in test suite"
paramNothing (seek runner <$$> const optparser)
seek :: CommandSeek seek :: Maybe TestRunner -> TestOptions -> CommandSeek
seek = withWords start seek runner o = commandAction $ start runner o
{- We don't actually run the test suite here because of a dependency loop. start :: Maybe TestRunner -> TestOptions -> CommandStart
- The main program notices when the command is test and runs it; this start runner o = do
- function is never run if that works. liftIO $ startIO runner o
-
- However, if git-annex is built without the test suite, just print a
- warning, and do not exit nonzero. This is so git-annex test can be run
- in debian/rules despite some architectures not being able to build the
- test suite.
-}
start :: [String] -> CommandStart
start ps = do
liftIO $ startIO ps
stop stop
startIO :: CmdParams -> IO () startIO :: Maybe TestRunner -> TestOptions -> IO ()
startIO _ = warningIO "git-annex was built without its test suite; not testing" startIO Nothing _ = warningIO "git-annex was built without its test suite; not testing"
startIO (Just runner) o = runner o

View file

@ -27,6 +27,7 @@ import Messages
import Types.Messages import Types.Messages
import Remote.Helper.Chunked import Remote.Helper.Chunked
import Locations import Locations
import Git.Types
import Test.Tasty import Test.Tasty
import Test.Tasty.Runners import Test.Tasty.Runners
@ -36,25 +37,30 @@ import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import qualified Data.Map as M import qualified Data.Map as M
cmd :: [Command] cmd :: Command
cmd = [ withOptions [sizeOption] $ cmd = command "testremote" SectionTesting
command "testremote" paramRemote seek SectionTesting "test transfers to/from a remote"
"test transfers to/from a remote"] paramRemote (seek <$$> optParser)
sizeOption :: Option data TestRemoteOptions = TestRemoteOptions
sizeOption = fieldOption [] "size" paramSize "base key size (default 1MiB)" { testRemote :: RemoteName
, sizeOption :: ByteSize
}
seek :: CommandSeek optParser :: CmdParamsDesc -> Parser TestRemoteOptions
seek ps = do optParser desc = TestRemoteOptions
basesz <- fromInteger . fromMaybe (1024 * 1024) <$> argument str ( metavar desc )
<$> getOptionField sizeOption (pure . getsize) <*> option (str >>= maybe (fail "parse error") return . readSize dataUnits)
withWords (start basesz) ps ( long "size" <> metavar paramSize
where <> value (1024 * 1024)
getsize v = v >>= readSize dataUnits <> help "base key size (default 1MiB)"
)
start :: Int -> [String] -> CommandStart seek :: TestRemoteOptions -> CommandSeek
start basesz ws = do seek o = commandAction $ start (fromInteger $ sizeOption o) (testRemote o)
let name = unwords ws
start :: Int -> RemoteName -> CommandStart
start basesz name = do
showStart "testremote" name showStart "testremote" name
r <- either error id <$> Remote.byName' name r <- either error id <$> Remote.byName' name
showSideAction "generating test keys" showSideAction "generating test keys"

View file

@ -15,11 +15,13 @@ import Types.Key
import qualified CmdLine.GitAnnexShell.Fields as Fields import qualified CmdLine.GitAnnexShell.Fields as Fields
import Utility.Metered import Utility.Metered
cmd :: [Command] cmd :: Command
cmd = [noCommit $ command "transferinfo" paramKey seek SectionPlumbing cmd = noCommit $
"updates sender on number of bytes of content received"] command "transferinfo" SectionPlumbing
"updates sender on number of bytes of content received"
paramKey (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
{- Security: {- Security:
@ -47,8 +49,8 @@ start (k:[]) = do
, transferUUID = u , transferUUID = u
, transferKey = key , transferKey = key
} }
info <- liftIO $ startTransferInfo file tinfo <- liftIO $ startTransferInfo file
(update, tfile, _) <- mkProgressUpdater t info (update, tfile, _) <- mkProgressUpdater t tinfo
liftIO $ mapM_ void liftIO $ mapM_ void
[ tryIO $ forever $ do [ tryIO $ forever $ do
bytes <- readUpdate bytes <- readUpdate

View file

@ -15,41 +15,51 @@ import Annex.Transfer
import qualified Remote import qualified Remote
import Types.Remote import Types.Remote
cmd :: [Command] cmd :: Command
cmd = [withOptions transferKeyOptions $ cmd = noCommit $
noCommit $ command "transferkey" paramKey seek SectionPlumbing command "transferkey" SectionPlumbing
"transfers a key from or to a remote"] "transfers a key from or to a remote"
paramKey (seek <--< optParser)
transferKeyOptions :: [Option] data TransferKeyOptions = TransferKeyOptions
transferKeyOptions = fileOption : fromToOptions { keyOptions :: CmdParams
, fromToOptions :: FromToOptions
, fileOption :: AssociatedFile
}
fileOption :: Option optParser :: CmdParamsDesc -> Parser TransferKeyOptions
fileOption = fieldOption [] "file" paramFile "the associated file" optParser desc = TransferKeyOptions
<$> cmdParams desc
<*> parseFromToOptions
<*> optional (strOption
( long "file" <> metavar paramFile
<> help "the associated file"
))
seek :: CommandSeek instance DeferredParseClass TransferKeyOptions where
seek ps = do finishParse v = TransferKeyOptions
to <- getOptionField toOption Remote.byNameWithUUID <$> pure (keyOptions v)
from <- getOptionField fromOption Remote.byNameWithUUID <*> finishParse (fromToOptions v)
file <- getOptionField fileOption return <*> pure (fileOption v)
withKeys (start to from file) ps
start :: Maybe Remote -> Maybe Remote -> AssociatedFile -> Key -> CommandStart seek :: TransferKeyOptions -> CommandSeek
start to from file key = seek o = withKeys (start o) (keyOptions o)
case (from, to) of
(Nothing, Just dest) -> next $ toPerform dest key file
(Just src, Nothing) -> next $ fromPerform src key file
_ -> error "specify either --from or --to"
toPerform :: Remote -> Key -> AssociatedFile -> CommandPerform start :: TransferKeyOptions -> Key -> CommandStart
toPerform remote key file = go Upload file $ start o key = case fromToOptions o of
ToRemote dest -> next $ toPerform key (fileOption o) =<< getParsed dest
FromRemote src -> next $ fromPerform key (fileOption o) =<< getParsed src
toPerform :: Key -> AssociatedFile -> Remote -> CommandPerform
toPerform key file remote = go Upload file $
upload (uuid remote) key file forwardRetry noObserver $ \p -> do upload (uuid remote) key file forwardRetry noObserver $ \p -> do
ok <- Remote.storeKey remote key file p ok <- Remote.storeKey remote key file p
when ok $ when ok $
Remote.logStatus remote key InfoPresent Remote.logStatus remote key InfoPresent
return ok return ok
fromPerform :: Remote -> Key -> AssociatedFile -> CommandPerform fromPerform :: Key -> AssociatedFile -> Remote -> CommandPerform
fromPerform remote key file = go Upload file $ fromPerform key file remote = go Upload file $
download (uuid remote) key file forwardRetry noObserver $ \p -> download (uuid remote) key file forwardRetry noObserver $ \p ->
getViaTmp key $ \t -> Remote.retrieveKeyFile remote key file t p getViaTmp key $ \t -> Remote.retrieveKeyFile remote key file t p

View file

@ -21,11 +21,11 @@ import Git.Types (RemoteName)
data TransferRequest = TransferRequest Direction Remote Key AssociatedFile data TransferRequest = TransferRequest Direction Remote Key AssociatedFile
cmd :: [Command] cmd :: Command
cmd = [command "transferkeys" paramNothing seek cmd = command "transferkeys" SectionPlumbing "transfers keys"
SectionPlumbing "transfers keys"] paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart
@ -45,7 +45,7 @@ start = do
download (Remote.uuid remote) key file forwardRetry observer $ \p -> download (Remote.uuid remote) key file forwardRetry observer $ \p ->
getViaTmp key $ \t -> Remote.retrieveKeyFile remote key file t p getViaTmp key $ \t -> Remote.retrieveKeyFile remote key file t p
observer False t info = recordFailedTransfer t info observer False t tinfo = recordFailedTransfer t tinfo
observer True _ _ = noop observer True _ _ = noop
runRequests runRequests
@ -80,14 +80,14 @@ runRequests readh writeh a = do
hFlush writeh hFlush writeh
sendRequest :: Transfer -> TransferInfo -> Handle -> IO () sendRequest :: Transfer -> TransferInfo -> Handle -> IO ()
sendRequest t info h = do sendRequest t tinfo h = do
hPutStr h $ intercalate fieldSep hPutStr h $ intercalate fieldSep
[ serialize (transferDirection t) [ serialize (transferDirection t)
, maybe (serialize (fromUUID (transferUUID t))) , maybe (serialize (fromUUID (transferUUID t)))
(serialize . Remote.name) (serialize . Remote.name)
(transferRemote info) (transferRemote tinfo)
, serialize (transferKey t) , serialize (transferKey t)
, serialize (associatedFile info) , serialize (associatedFile tinfo)
, "" -- adds a trailing null , "" -- adds a trailing null
] ]
hFlush h hFlush h

View file

@ -16,14 +16,14 @@ import Logs.Group
import qualified Data.Set as S import qualified Data.Set as S
cmd :: [Command] cmd :: Command
cmd = [command "trust" (paramRepeating paramRemote) seek cmd = command "trust" SectionSetup "trust a repository"
SectionSetup "trust a repository"] (paramRepeating paramRemote) (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = trustCommand "trust" Trusted seek = trustCommand "trust" Trusted
trustCommand :: String -> TrustLevel -> CommandSeek trustCommand :: String -> TrustLevel -> CmdParams -> CommandSeek
trustCommand c level = withWords start trustCommand c level = withWords start
where where
start ws = do start ws = do

View file

@ -22,12 +22,13 @@ import qualified Git.DiffTree as DiffTree
import Utility.CopyFile import Utility.CopyFile
import Command.PreCommit (lockPreCommitHook) import Command.PreCommit (lockPreCommitHook)
cmd :: [Command] cmd :: Command
cmd = [withOptions annexedMatchingOptions $ cmd = withGlobalOptions annexedMatchingOptions $
command "unannex" paramPaths seek SectionUtility command "unannex" SectionUtility
"undo accidential add command"] "undo accidential add command"
paramPaths (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = wrapUnannex . (withFilesInGit $ whenAnnexed start) seek = wrapUnannex . (withFilesInGit $ whenAnnexed start)
wrapUnannex :: Annex a -> Annex a wrapUnannex :: Annex a -> Annex a

View file

@ -21,12 +21,13 @@ import qualified Git.Command as Git
import qualified Git.Branch import qualified Git.Branch
import qualified Command.Sync import qualified Command.Sync
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ cmd = notBareRepo $
command "undo" paramPaths seek command "undo" SectionCommon
SectionCommon "undo last change to a file or directory"] "undo last change to a file or directory"
paramPaths (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek ps = do seek ps = do
-- Safety first; avoid any undo that would touch files that are not -- Safety first; avoid any undo that would touch files that are not
-- in the index. -- in the index.

View file

@ -15,11 +15,11 @@ import Types.Group
import qualified Data.Set as S import qualified Data.Set as S
cmd :: [Command] cmd :: Command
cmd = [command "ungroup" (paramPair paramRemote paramDesc) seek cmd = command "ungroup" SectionSetup "remove a repository from a group"
SectionSetup "remove a repository from a group"] (paramPair paramRemote paramDesc) (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -21,9 +21,11 @@ import Utility.FileMode
import System.IO.HVFS import System.IO.HVFS
import System.IO.HVFS.Utils import System.IO.HVFS.Utils
cmd :: [Command] cmd :: Command
cmd = [addCheck check $ command "uninit" paramPaths seek cmd = addCheck check $
SectionUtility "de-initialize git-annex and clean out repository"] command "uninit" SectionUtility
"de-initialize git-annex and clean out repository"
paramPaths (withParams seek)
check :: Annex () check :: Annex ()
check = do check = do
@ -39,7 +41,7 @@ check = do
revhead = inRepo $ Git.Command.pipeReadStrict revhead = inRepo $ Git.Command.pipeReadStrict
[Param "rev-parse", Param "--abbrev-ref", Param "HEAD"] [Param "rev-parse", Param "--abbrev-ref", Param "HEAD"]
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek ps = do seek ps = do
withFilesNotInGit False (whenAnnexed startCheckIncomplete) ps withFilesNotInGit False (whenAnnexed startCheckIncomplete) ps
Annex.changeState $ \s -> s { Annex.fast = True } Annex.changeState $ \s -> s { Annex.fast = True }

View file

@ -13,16 +13,17 @@ import Annex.Content
import Annex.CatFile import Annex.CatFile
import Utility.CopyFile import Utility.CopyFile
cmd :: [Command] cmd :: Command
cmd = cmd = mkcmd "unlock" "unlock files for modification"
[ c "unlock" "unlock files for modification"
, c "edit" "same as unlock"
]
where
c n = notDirect . withOptions annexedMatchingOptions
. command n paramPaths seek SectionCommon
seek :: CommandSeek editcmd :: Command
editcmd = mkcmd "edit" "same as unlock"
mkcmd :: String -> String -> Command
mkcmd n d = notDirect $ withGlobalOptions annexedMatchingOptions $
command n SectionCommon d paramPaths (withParams seek)
seek :: CmdParams -> 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

View file

@ -11,9 +11,9 @@ import Command
import Types.TrustLevel import Types.TrustLevel
import Command.Trust (trustCommand) import Command.Trust (trustCommand)
cmd :: [Command] cmd :: Command
cmd = [command "untrust" (paramRepeating paramRemote) seek cmd = command "untrust" SectionSetup "do not trust a repository"
SectionSetup "do not trust a repository"] (paramRepeating paramRemote) (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = trustCommand "untrust" UnTrusted seek = trustCommand "untrust" UnTrusted

View file

@ -1,6 +1,6 @@
{- git-annex command {- git-annex command
- -
- Copyright 2010-2012 Joey Hess <id@joeyh.name> - Copyright 2010-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -31,38 +31,47 @@ import Annex.CatFile
import Types.Key import Types.Key
import Types.RefSpec import Types.RefSpec
import Git.FilePath import Git.FilePath
import Git.Types
import Logs.View (is_branchView) import Logs.View (is_branchView)
import Annex.BloomFilter import Annex.BloomFilter
cmd :: [Command] cmd :: Command
cmd = [withOptions [unusedFromOption, refSpecOption] $ cmd = -- withGlobalOptions [unusedFromOption, refSpecOption] $
command "unused" paramNothing seek command "unused" SectionMaintenance
SectionMaintenance "look for unused file content"] "look for unused file content"
paramNothing (seek <$$> optParser)
unusedFromOption :: Option data UnusedOptions = UnusedOptions
unusedFromOption = fieldOption ['f'] "from" paramRemote "remote to check for unused content" { fromRemote :: Maybe RemoteName
, refSpecOption :: Maybe RefSpec
}
refSpecOption :: Option optParser :: CmdParamsDesc -> Parser UnusedOptions
refSpecOption = fieldOption [] "used-refspec" paramRefSpec "refs to consider used (default: all refs)" optParser _ = UnusedOptions
<$> optional (strOption
( long "from" <> short 'f' <> metavar paramRemote
<> help "remote to check for unused content"
))
<*> optional (option (eitherReader parseRefSpec)
( long "unused-refspec" <> metavar paramRefSpec
<> help "refs to consider used (default: all branches)"
))
seek :: CommandSeek seek :: UnusedOptions -> CommandSeek
seek = withNothing start seek = commandAction . start
{- Finds unused content in the annex. -} start :: UnusedOptions -> CommandStart
start :: CommandStart start o = do
start = do
cfgrefspec <- fromMaybe allRefSpec . annexUsedRefSpec cfgrefspec <- fromMaybe allRefSpec . annexUsedRefSpec
<$> Annex.getGitConfig <$> Annex.getGitConfig
!refspec <- maybe cfgrefspec (either error id . parseRefSpec) let refspec = fromMaybe cfgrefspec (refSpecOption o)
<$> Annex.getField (optionName refSpecOption) let (name, perform) = case fromRemote o of
from <- Annex.getField (optionName unusedFromOption)
let (name, action) = case from of
Nothing -> (".", checkUnused refspec) Nothing -> (".", checkUnused refspec)
Just "." -> (".", checkUnused refspec) Just "." -> (".", checkUnused refspec)
Just "here" -> (".", checkUnused refspec) Just "here" -> (".", checkUnused refspec)
Just n -> (n, checkRemoteUnused n refspec) Just n -> (n, checkRemoteUnused n refspec)
showStart "unused" name showStart "unused" name
next action next perform
checkUnused :: RefSpec -> CommandPerform checkUnused :: RefSpec -> CommandPerform
checkUnused refspec = chain 0 checkUnused refspec = chain 0
@ -126,11 +135,11 @@ unusedMsg u = unusedMsg' u
["Some annexed data is no longer used by any files:"] ["Some annexed data is no longer used by any files:"]
[dropMsg Nothing] [dropMsg Nothing]
unusedMsg' :: [(Int, Key)] -> [String] -> [String] -> String unusedMsg' :: [(Int, Key)] -> [String] -> [String] -> String
unusedMsg' u header trailer = unlines $ unusedMsg' u mheader mtrailer = unlines $
header ++ mheader ++
table u ++ table u ++
["(To see where data was previously used, try: git log --stat -S'KEY')"] ++ ["(To see where data was previously used, try: git log --stat -S'KEY')"] ++
trailer mtrailer
remoteUnusedMsg :: Remote -> [(Int, Key)] -> String remoteUnusedMsg :: Remote -> [(Int, Key)] -> String
remoteUnusedMsg r u = unusedMsg' u remoteUnusedMsg r u = unusedMsg' u
@ -267,7 +276,7 @@ data UnusedMaps = UnusedMaps
, unusedTmpMap :: UnusedMap , unusedTmpMap :: UnusedMap
} }
withUnusedMaps :: (UnusedMaps -> Int -> CommandStart) -> CommandSeek withUnusedMaps :: (UnusedMaps -> Int -> CommandStart) -> CmdParams -> CommandSeek
withUnusedMaps a params = do withUnusedMaps a params = do
unused <- readUnusedMap "" unused <- readUnusedMap ""
unusedbad <- readUnusedMap "bad" unusedbad <- readUnusedMap "bad"

View file

@ -11,12 +11,12 @@ import Common.Annex
import Command import Command
import Upgrade import Upgrade
cmd :: [Command] cmd :: Command
cmd = [dontCheck repoExists $ -- because an old version may not seem to exist cmd = dontCheck repoExists $ -- because an old version may not seem to exist
command "upgrade" paramNothing seek command "upgrade" SectionMaintenance "upgrade repository layout"
SectionMaintenance "upgrade repository layout"] paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start :: CommandStart start :: CommandStart

View file

@ -12,11 +12,14 @@ import Command
import Annex.View import Annex.View
import Command.View (checkoutViewBranch) import Command.View (checkoutViewBranch)
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ notDirect $ command "vadd" (paramRepeating "FIELD=GLOB") cmd = notBareRepo $ notDirect $
seek SectionMetaData "add subdirs to current view"] command "vadd" SectionMetaData
"add subdirs to current view"
(paramRepeating "FIELD=GLOB")
(withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

View file

@ -14,12 +14,13 @@ import Types.View
import Logs.View import Logs.View
import Command.View (checkoutViewBranch) import Command.View (checkoutViewBranch)
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ notDirect $ cmd = notBareRepo $ notDirect $
command "vcycle" paramNothing seek SectionMetaData command "vcycle" SectionMetaData
"switch view to next layout"] "switch view to next layout"
paramNothing (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withNothing start seek = withNothing start
start ::CommandStart start ::CommandStart

View file

@ -12,11 +12,12 @@ import Command
import Annex.View import Annex.View
import Command.View (paramView, checkoutViewBranch) import Command.View (paramView, checkoutViewBranch)
cmd :: [Command] cmd :: Command
cmd = [notBareRepo $ notDirect $ cmd = notBareRepo $ notDirect $
command "vfilter" paramView seek SectionMetaData "filter current view"] command "vfilter" SectionMetaData "filter current view"
paramView (withParams seek)
seek :: CommandSeek seek :: CmdParams -> CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart

Some files were not shown because too many files have changed in this diff Show more