tagging package git-annex version 5.20140210

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIVAwUAUvkVDskQ2SIlEuPHAQho0Q//Y1+hSKqSUcbfuaV9nlU1puUi0VdYWouA
 SmSCpp1Kp44lPNERTrPPA66d8cQo12iCixAxnGt50Wl7n8VD8Py1kyqoNWgMe54W
 H3A7rC58mzXYXWEox1xgX6Sz/RbYMekx9NbALpgQZkVtnOsKO7lNClx1LUNpvXwh
 U52/D4ABpOLAjcb4HS2rs1Z4w0u6fFvFas313RXhIEIecuS2Iz1V/h4Wv6fPkJGR
 S38rG3OQfFXL0v6cU1l+jM2at7sEs+f7q2sujIam3/kgcdysgnyU7rV+p2xqv2+e
 LeOiHszGhjdlK6wnKOoXDZ0rvdNfUzUXbFGHIZfg5ACYR9Ps5hVqfDH6J9riCH8J
 F4WzuDBtKcdVJyBehB/yWw4/ABh5D4YoC2+rsWm6buu/pX5qU+Yshu8X5LRX6h4h
 Ex1jSE2QbTaXNkEa7+14Eb/NscRJqULzUhjBjCrd8JzOVe+jrK/2C2FLETv1ImfN
 G+8iOD/mQL71XwLhdMHA8jwqdSnJOtGXCgaVOcgBKe7E2qXqZAefVrjGvENHQUCb
 AxP2KjGDybYtrCSfHOdYiM8+IJvV/pgmDe9KFjNsUemG+20+a+D64+4b890HwpTT
 gLQpw8U5EwFXgLsYFqu4YBcuVck8T9bEzQhG1FKu2ZP9QJPqR6Z3W2T8jGRyYuC7
 pFh2EtFUKZQ=
 =ojkV
 -----END PGP SIGNATURE-----

Merge tag '5.20140210' into debian-wheezy-backport

tagging package git-annex version 5.20140210

Conflicts:
	debian/control
	git-annex.cabal
This commit is contained in:
Joey Hess 2014-02-20 22:55:13 +00:00
commit f78b81f463
416 changed files with 7498 additions and 1684 deletions

1
.gitignore vendored
View file

@ -28,3 +28,4 @@ cabal-dev
# OSX related # OSX related
.DS_Store .DS_Store
.virthualenv .virthualenv
.tasty-rerun-log

6
.mailmap Normal file
View file

@ -0,0 +1,6 @@
Joey Hess <joey@kitenet.net> http://joey.kitenet.net/ <joey@web>
Joey Hess <joey@kitenet.net> http://joeyh.name/ <joey@web>
Joey Hess <joey@kitenet.net> http://joeyh.name/ <http://joeyh.name/@web>
Yaroslav Halchenko <debian@onerussian.com>
Yaroslav Halchenko <debian@onerussian.com> http://yarikoptic.myopenid.com/ <site-myopenid@web>
Yaroslav Halchenko <debian@onerussian.com> https://www.google.com/accounts/o8/id?id=AItOawnx8kHW66N3BqmkVpgtXDlYMvr8TJ5VvfY <Yaroslav@web>

View file

@ -34,7 +34,6 @@ module Annex (
import "mtl" Control.Monad.Reader import "mtl" Control.Monad.Reader
import "MonadCatchIO-transformers" Control.Monad.CatchIO import "MonadCatchIO-transformers" Control.Monad.CatchIO
import System.Posix.Types (Fd)
import Control.Concurrent import Control.Concurrent
import Common import Common
@ -46,6 +45,7 @@ import Git.CheckAttr
import Git.CheckIgnore import Git.CheckIgnore
import Git.SharedRepository import Git.SharedRepository
import qualified Git.Queue import qualified Git.Queue
import Types.Key
import Types.Backend import Types.Backend
import Types.GitConfig import Types.GitConfig
import qualified Types.Remote import qualified Types.Remote
@ -56,6 +56,8 @@ import Types.Group
import Types.Messages import Types.Messages
import Types.UUID import Types.UUID
import Types.FileMatcher import Types.FileMatcher
import Types.NumCopies
import Types.LockPool
import qualified Utility.Matcher import qualified Utility.Matcher
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
@ -75,7 +77,7 @@ newtype Annex a = Annex { runAnnex :: ReaderT (MVar AnnexState) IO a }
) )
type Matcher a = Either [Utility.Matcher.Token a] (Utility.Matcher.Matcher a) type Matcher a = Either [Utility.Matcher.Token a] (Utility.Matcher.Matcher a)
type PreferredContentMap = M.Map UUID (Utility.Matcher.Matcher (S.Set UUID -> FileInfo -> Annex Bool)) type PreferredContentMap = M.Map UUID (Utility.Matcher.Matcher (S.Set UUID -> MatchInfo -> Annex Bool))
-- internal state storage -- internal state storage
data AnnexState = AnnexState data AnnexState = AnnexState
@ -94,8 +96,9 @@ data AnnexState = AnnexState
, checkattrhandle :: Maybe CheckAttrHandle , checkattrhandle :: Maybe CheckAttrHandle
, checkignorehandle :: Maybe (Maybe CheckIgnoreHandle) , checkignorehandle :: Maybe (Maybe CheckIgnoreHandle)
, forcebackend :: Maybe String , forcebackend :: Maybe String
, forcenumcopies :: Maybe Int , globalnumcopies :: Maybe NumCopies
, limit :: Matcher (FileInfo -> Annex Bool) , forcenumcopies :: Maybe NumCopies
, limit :: Matcher (MatchInfo -> Annex Bool)
, uuidmap :: Maybe UUIDMap , uuidmap :: Maybe UUIDMap
, preferredcontentmap :: Maybe PreferredContentMap , preferredcontentmap :: Maybe PreferredContentMap
, shared :: Maybe SharedRepository , shared :: Maybe SharedRepository
@ -103,12 +106,14 @@ data AnnexState = AnnexState
, trustmap :: Maybe TrustMap , trustmap :: Maybe TrustMap
, groupmap :: Maybe GroupMap , groupmap :: Maybe GroupMap
, ciphers :: M.Map StorableCipher Cipher , ciphers :: M.Map StorableCipher Cipher
, lockpool :: M.Map FilePath Fd , lockpool :: LockPool
, flags :: M.Map String Bool , flags :: M.Map String Bool
, fields :: M.Map String String , fields :: M.Map String String
, cleanup :: M.Map String (Annex ()) , cleanup :: M.Map String (Annex ())
, inodeschanged :: Maybe Bool , inodeschanged :: Maybe Bool
, useragent :: Maybe String , useragent :: Maybe String
, errcounter :: Integer
, unusedkeys :: Maybe (S.Set Key)
} }
newState :: GitConfig -> Git.Repo -> AnnexState newState :: GitConfig -> Git.Repo -> AnnexState
@ -128,6 +133,7 @@ newState c r = AnnexState
, checkattrhandle = Nothing , checkattrhandle = Nothing
, checkignorehandle = Nothing , checkignorehandle = Nothing
, forcebackend = Nothing , forcebackend = Nothing
, globalnumcopies = Nothing
, forcenumcopies = Nothing , forcenumcopies = Nothing
, limit = Left [] , limit = Left []
, uuidmap = Nothing , uuidmap = Nothing
@ -143,6 +149,8 @@ newState c r = AnnexState
, cleanup = M.empty , cleanup = M.empty
, inodeschanged = Nothing , inodeschanged = Nothing
, useragent = Nothing , useragent = Nothing
, errcounter = 0
, unusedkeys = Nothing
} }
{- Makes an Annex state object for the specified git repo. {- Makes an Annex state object for the specified git repo.

View file

@ -18,6 +18,7 @@ module Annex.Branch (
forceUpdate, forceUpdate,
updateTo, updateTo,
get, get,
getHistorical,
change, change,
commit, commit,
forceCommit, forceCommit,
@ -197,7 +198,13 @@ getLocal file = go =<< getJournalFileStale file
go Nothing = getRaw file go Nothing = getRaw file
getRaw :: FilePath -> Annex String getRaw :: FilePath -> Annex String
getRaw file = withIndex $ L.unpack <$> catFile fullname file getRaw = getRef fullname
getHistorical :: RefDate -> FilePath -> Annex String
getHistorical date = getRef (Git.Ref.dateRef fullname date)
getRef :: Ref -> FilePath -> Annex String
getRef ref file = withIndex $ L.unpack <$> catFile ref file
{- Applies a function to modifiy the content of a file. {- Applies a function to modifiy the content of a file.
- -
@ -252,8 +259,7 @@ commitIndex' jl branchref message parents = do
committedref <- inRepo $ Git.Branch.commitAlways message fullname parents committedref <- inRepo $ Git.Branch.commitAlways message fullname parents
setIndexSha committedref setIndexSha committedref
parentrefs <- commitparents <$> catObject committedref parentrefs <- commitparents <$> catObject committedref
when (racedetected branchref parentrefs) $ do when (racedetected branchref parentrefs) $
liftIO $ print ("race detected", branchref, parentrefs, "committing", (branchref, parents))
fixrace committedref parentrefs fixrace committedref parentrefs
where where
-- look for "parent ref" lines and return the refs -- look for "parent ref" lines and return the refs

View file

@ -41,6 +41,7 @@ dropDead f content trustmap = case getLogVariety f of
in if null newlog in if null newlog
then RemoveFile then RemoveFile
else ChangeFile $ Presence.showLog newlog else ChangeFile $ Presence.showLog newlog
Just SingleValueLog -> PreserveFile
Nothing -> PreserveFile Nothing -> PreserveFile
dropDeadFromUUIDBasedLog :: TrustMap -> UUIDBased.Log String -> UUIDBased.Log String dropDeadFromUUIDBasedLog :: TrustMap -> UUIDBased.Log String -> UUIDBased.Log String

View file

@ -1,6 +1,6 @@
{- git-annex file content managing {- git-annex file content managing
- -
- Copyright 2010-2013 Joey Hess <joey@kitenet.net> - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -35,7 +35,6 @@ module Annex.Content (
) where ) where
import System.IO.Unsafe (unsafeInterleaveIO) import System.IO.Unsafe (unsafeInterleaveIO)
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Logs.Location import Logs.Location
@ -57,6 +56,10 @@ import Annex.Content.Direct
import Annex.ReplaceFile import Annex.ReplaceFile
import Annex.Exception import Annex.Exception
#ifdef mingw32_HOST_OS
import Utility.WinLock
#endif
{- Checks if a given key's content is currently present. -} {- Checks if a given key's content is currently present. -}
inAnnex :: Key -> Annex Bool inAnnex :: Key -> Annex Bool
inAnnex key = inAnnexCheck key $ liftIO . doesFileExist inAnnex key = inAnnexCheck key $ liftIO . doesFileExist
@ -90,60 +93,105 @@ inAnnex' isgood bad check key = withObjectLoc key checkindirect checkdirect
{- A safer check; the key's content must not only be present, but {- A safer check; the key's content must not only be present, but
- is not in the process of being removed. -} - is not in the process of being removed. -}
inAnnexSafe :: Key -> Annex (Maybe Bool) inAnnexSafe :: Key -> Annex (Maybe Bool)
inAnnexSafe = inAnnex' (fromMaybe False) (Just False) go inAnnexSafe key = inAnnex' (fromMaybe False) (Just False) go key
where where
go f = liftIO $ openforlock f >>= check is_locked = Nothing
is_unlocked = Just True
is_missing = Just False
go contentfile = maybe (checkindirect contentfile) (checkdirect contentfile)
=<< contentLockFile key
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
checkindirect f = liftIO $ openforlock f >>= check is_missing
{- In direct mode, the content file must exist, but
- the lock file often generally won't exist unless a removal is in
- process. This does not create the lock file, it only checks for
- it. -}
checkdirect contentfile lockfile = liftIO $
ifM (doesFileExist contentfile)
( openforlock lockfile >>= check is_unlocked
, return is_missing
)
openforlock f = catchMaybeIO $ openforlock f = catchMaybeIO $
openFd f ReadOnly Nothing defaultFileFlags openFd f ReadOnly Nothing defaultFileFlags
#else check _ (Just h) = do
openforlock _ = return $ Just ()
#endif
check Nothing = return is_missing
#ifndef mingw32_HOST_OS
check (Just h) = do
v <- getLock h (ReadLock, AbsoluteSeek, 0, 0) v <- getLock h (ReadLock, AbsoluteSeek, 0, 0)
closeFd h closeFd h
return $ case v of return $ case v of
Just _ -> is_locked Just _ -> is_locked
Nothing -> is_unlocked Nothing -> is_unlocked
check def Nothing = return def
#else #else
check (Just _) = return is_unlocked checkindirect _ = return is_missing
{- In Windows, see if we can take a shared lock. If so,
- remove the lock file to clean up after ourselves. -}
checkdirect contentfile lockfile =
ifM (liftIO $ doesFileExist contentfile)
( modifyContent lockfile $ liftIO $ do
v <- lockShared lockfile
case v of
Nothing -> return is_locked
Just lockhandle -> do
dropLock lockhandle
void $ tryIO $ nukeFile lockfile
return is_unlocked
, return is_missing
)
#endif #endif
#ifndef mingw32_HOST_OS
is_locked = Nothing {- Direct mode and especially Windows has to use a separate lock
#endif - file from the content, since locking the actual content file
is_unlocked = Just True - would interfere with the user's use of it. -}
is_missing = Just False contentLockFile :: Key -> Annex (Maybe FilePath)
contentLockFile key = ifM isDirect
( Just <$> calcRepo (gitAnnexContentLock key)
, return Nothing
)
{- Content is exclusively locked while running an action that might remove {- Content is exclusively locked while running an action that might remove
- it. (If the content is not present, no locking is done.) -} - it. (If the content is not present, no locking is done.) -}
lockContent :: Key -> Annex a -> Annex a lockContent :: Key -> Annex a -> Annex a
#ifndef mingw32_HOST_OS
lockContent key a = do lockContent key a = do
file <- calcRepo $ gitAnnexLocation key contentfile <- calcRepo $ gitAnnexLocation key
bracketIO (openforlock file >>= lock) unlock (const a) lockfile <- contentLockFile key
maybe noop setuplockfile lockfile
bracketAnnex (liftIO $ lock contentfile lockfile) (unlock lockfile) (const a)
where where
{- Since files are stored with the write bit disabled, have alreadylocked = error "content is locked"
setuplockfile lockfile = modifyContent lockfile $
void $ liftIO $ tryIO $
writeFile lockfile ""
cleanuplockfile lockfile = modifyContent lockfile $
void $ liftIO $ tryIO $
nukeFile lockfile
#ifndef mingw32_HOST_OS
lock contentfile Nothing = opencontentforlock contentfile >>= dolock
lock _ (Just lockfile) = openforlock lockfile >>= dolock . Just
{- Since content files are stored with the write bit disabled, have
- to fiddle with permissions to open for an exclusive lock. -} - to fiddle with permissions to open for an exclusive lock. -}
openforlock f = catchMaybeIO $ ifM (doesFileExist f) opencontentforlock f = catchMaybeIO $ ifM (doesFileExist f)
( withModifiedFileMode f ( withModifiedFileMode f
(`unionFileModes` ownerWriteMode) (`unionFileModes` ownerWriteMode)
open (openforlock f)
, open , openforlock f
) )
where openforlock f = openFd f ReadWrite Nothing defaultFileFlags
open = openFd f ReadWrite Nothing defaultFileFlags dolock Nothing = return Nothing
lock Nothing = return Nothing dolock (Just fd) = do
lock (Just fd) = do
v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0) v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
case v of case v of
Left _ -> error "content is locked" Left _ -> alreadylocked
Right _ -> return $ Just fd Right _ -> return $ Just fd
unlock Nothing = noop unlock mlockfile mfd = do
unlock (Just l) = closeFd l maybe noop cleanuplockfile mlockfile
liftIO $ maybe noop closeFd mfd
#else #else
lockContent _key a = a -- no locking for Windows! lock _ (Just lockfile) = maybe alreadylocked (return . Just) =<< lockExclusive lockfile
lock _ Nothing = return Nothing
unlock mlockfile mlockhandle = do
liftIO $ maybe noop dropLock mlockhandle
maybe noop cleanuplockfile mlockfile
#endif #endif
{- Runs an action, passing it a temporary filename to get, {- Runs an action, passing it a temporary filename to get,
@ -377,6 +425,7 @@ removeAnnex :: Key -> Annex ()
removeAnnex key = withObjectLoc key remove removedirect removeAnnex key = withObjectLoc key remove removedirect
where where
remove file = cleanObjectLoc key $ do remove file = cleanObjectLoc key $ do
secureErase file
liftIO $ nukeFile file liftIO $ nukeFile file
removeInodeCache key removeInodeCache key
removedirect fs = do removedirect fs = do
@ -385,11 +434,18 @@ removeAnnex key = withObjectLoc key remove removedirect
mapM_ (resetfile cache) fs mapM_ (resetfile cache) fs
resetfile cache f = whenM (sameInodeCache f cache) $ do resetfile cache f = whenM (sameInodeCache f cache) $ do
l <- inRepo $ gitAnnexLink f key l <- inRepo $ gitAnnexLink f key
top <- fromRepo Git.repoPath secureErase f
cwd <- liftIO getCurrentDirectory replaceFile f $ makeAnnexLink l
let top' = fromMaybe top $ absNormPath cwd top
let l' = relPathDirToFile top' (fromMaybe l $ absNormPath top' l) {- Runs the secure erase command if set, otherwise does nothing.
replaceFile f $ makeAnnexLink l' - File may or may not be deleted at the end; caller is responsible for
- making sure it's deleted. -}
secureErase :: FilePath -> Annex ()
secureErase file = maybe noop go =<< annexSecureEraseCommand <$> Annex.getGitConfig
where
go basecmd = void $ liftIO $
boolSystem "sh" [Param "-c", Param $ gencmd basecmd]
gencmd = massReplace [ ("%file", shellEscape file) ]
{- Moves a key's file out of .git/annex/objects/ -} {- Moves a key's file out of .git/annex/objects/ -}
fromAnnex :: Key -> FilePath -> Annex () fromAnnex :: Key -> FilePath -> Annex ()

View file

@ -52,10 +52,12 @@ associatedFiles key = do
associatedFilesRelative :: Key -> Annex [FilePath] associatedFilesRelative :: Key -> Annex [FilePath]
associatedFilesRelative key = do associatedFilesRelative key = do
mapping <- calcRepo $ gitAnnexMapping key mapping <- calcRepo $ gitAnnexMapping key
liftIO $ catchDefaultIO [] $ do liftIO $ catchDefaultIO [] $ withFile mapping ReadMode $ \h -> do
h <- openFile mapping ReadMode
fileEncoding h fileEncoding h
lines <$> hGetContents h -- Read strictly to ensure the file is closed
-- before changeAssociatedFiles tries to write to it.
-- (Especially needed on Windows.)
lines <$> hGetContentsStrict h
{- Changes the associated files information for a key, applying a {- Changes the associated files information for a key, applying a
- transformation to the list. Returns new associatedFiles value. -} - transformation to the list. Returns new associatedFiles value. -}
@ -66,15 +68,10 @@ changeAssociatedFiles key transform = do
let files' = transform files let files' = transform files
when (files /= files') $ do when (files /= files') $ do
modifyContent mapping $ modifyContent mapping $
liftIO $ viaTmp write mapping $ unlines files' liftIO $ viaTmp writeFileAnyEncoding mapping $
unlines files'
top <- fromRepo Git.repoPath top <- fromRepo Git.repoPath
return $ map (top </>) files' return $ map (top </>) files'
where
write file content = do
h <- openFile file WriteMode
fileEncoding h
hPutStr h content
hClose h
{- Removes the list of associated files. -} {- Removes the list of associated files. -}
removeAssociatedFiles :: Key -> Annex () removeAssociatedFiles :: Key -> Annex ()

124
Annex/Drop.hs Normal file
View file

@ -0,0 +1,124 @@
{- dropping of unwanted content
-
- Copyright 2012-2014 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Annex.Drop where
import Common.Annex
import Logs.Trust
import Config.NumCopies
import Types.Remote (uuid)
import Types.Key (key2file)
import qualified Remote
import qualified Command.Drop
import Command
import Annex.Wanted
import Annex.Exception
import Config
import Annex.Content.Direct
import qualified Data.Set as S
import System.Log.Logger (debugM)
type Reason = String
{- Drop a key from local and/or remote when allowed by the preferred content
- and numcopies settings.
-
- The UUIDs are ones where the content is believed to be present.
- The Remote list can include other remotes that do not have the content;
- only ones that match the UUIDs will be dropped from.
- If allowed to drop fromhere, that drop will be tried first.
-
- A remote can be specified that is known to have the key. This can be
- used an an optimisation when eg, a key has just been uploaded to a
- remote.
-
- In direct mode, all associated files are checked, and only if all
- of them are unwanted are they dropped.
-
- The runner is used to run commands, and so can be either callCommand
- or commandAction.
-}
handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile -> Maybe Remote -> CommandActionRunner -> Annex ()
handleDropsFrom locs rs reason fromhere key afile knownpresentremote runner = do
fs <- ifM isDirect
( do
l <- associatedFilesRelative key
return $ if null l
then maybeToList afile
else l
, return $ maybeToList afile
)
n <- getcopies fs
if fromhere && checkcopies n Nothing
then go fs rs =<< dropl fs n
else go fs rs n
where
getcopies fs = do
(untrusted, have) <- trustPartition UnTrusted locs
numcopies <- if null fs
then getNumCopies
else maximum <$> mapM getFileNumCopies fs
return (NumCopies (length have), numcopies, S.fromList untrusted)
{- Check that we have enough copies still to drop the content.
- When the remote being dropped from is untrusted, it was not
- counted as a copy, so having only numcopies suffices. Otherwise,
- we need more than numcopies to safely drop. -}
checkcopies (have, numcopies, _untrusted) Nothing = have > numcopies
checkcopies (have, numcopies, untrusted) (Just u)
| S.member u untrusted = have >= numcopies
| otherwise = have > numcopies
decrcopies (have, numcopies, untrusted) Nothing =
(NumCopies (fromNumCopies have - 1), numcopies, untrusted)
decrcopies v@(_have, _numcopies, untrusted) (Just u)
| S.member u untrusted = v
| otherwise = decrcopies v Nothing
go _ [] _ = noop
go fs (r:rest) n
| uuid r `S.notMember` slocs = go fs rest n
| checkcopies n (Just $ Remote.uuid r) =
dropr fs r n >>= go fs rest
| otherwise = noop
checkdrop fs n u a
| null fs = check $ -- no associated files; unused content
wantDrop True u (Just key) Nothing
| otherwise = check $
allM (wantDrop True u (Just key) . Just) fs
where
check c = ifM c
( dodrop n u a
, return n
)
dodrop n@(have, numcopies, _untrusted) u a =
ifM (safely $ runner $ a numcopies)
( do
liftIO $ debugM "drop" $ unwords
[ "dropped"
, fromMaybe (key2file key) afile
, "(from " ++ maybe "here" show u ++ ")"
, "(copies now " ++ show (fromNumCopies have - 1) ++ ")"
, ": " ++ reason
]
return $ decrcopies n u
, return n
)
dropl fs n = checkdrop fs n Nothing $ \numcopies ->
Command.Drop.startLocal afile numcopies key knownpresentremote
dropr fs r n = checkdrop fs n (Just $ Remote.uuid r) $ \numcopies ->
Command.Drop.startRemote afile numcopies key r
slocs = S.fromList locs
safely a = either (const False) id <$> tryAnnex a

View file

@ -14,6 +14,7 @@
module Annex.Exception ( module Annex.Exception (
bracketIO, bracketIO,
bracketAnnex,
tryAnnex, tryAnnex,
tryAnnexIO, tryAnnexIO,
throwAnnex, throwAnnex,
@ -29,6 +30,9 @@ import Common.Annex
bracketIO :: IO v -> (v -> IO b) -> (v -> Annex a) -> Annex a bracketIO :: IO v -> (v -> IO b) -> (v -> Annex a) -> Annex a
bracketIO setup cleanup = M.bracket (liftIO setup) (liftIO . cleanup) bracketIO setup cleanup = M.bracket (liftIO setup) (liftIO . cleanup)
bracketAnnex :: Annex v -> (v -> Annex b) -> (v -> Annex a) -> Annex a
bracketAnnex = M.bracket
{- try in the Annex monad -} {- try in the Annex monad -}
tryAnnex :: Annex a -> Annex (Either SomeException a) tryAnnex :: Annex a -> Annex (Either SomeException a)
tryAnnex = M.try tryAnnex = M.try

View file

@ -1,6 +1,6 @@
{- git-annex file matching {- git-annex file matching
- -
- Copyright 2012, 2013 Joey Hess <joey@kitenet.net> - Copyright 2012-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -28,18 +28,25 @@ import qualified Data.Set as S
type FileMatcher = Matcher MatchFiles type FileMatcher = Matcher MatchFiles
checkFileMatcher :: FileMatcher -> FilePath -> Annex Bool checkFileMatcher :: FileMatcher -> FilePath -> Annex Bool
checkFileMatcher matcher file = checkFileMatcher' matcher file S.empty True checkFileMatcher matcher file = checkMatcher matcher Nothing (Just file) S.empty True
checkFileMatcher' :: FileMatcher -> FilePath -> AssumeNotPresent -> Bool -> Annex Bool checkMatcher :: FileMatcher -> Maybe Key -> AssociatedFile -> AssumeNotPresent -> Bool -> Annex Bool
checkFileMatcher' matcher file notpresent def checkMatcher matcher mkey afile notpresent def
| isEmpty matcher = return def | isEmpty matcher = return def
| otherwise = do | otherwise = case (mkey, afile) of
(_, Just file) -> go =<< fileMatchInfo file
(Just key, _) -> go (MatchingKey key)
_ -> return def
where
go mi = matchMrun matcher $ \a -> a notpresent mi
fileMatchInfo :: FilePath -> Annex MatchInfo
fileMatchInfo file = do
matchfile <- getTopFilePath <$> inRepo (toTopFilePath file) matchfile <- getTopFilePath <$> inRepo (toTopFilePath file)
let fi = FileInfo return $ MatchingFile $ FileInfo
{ matchFile = matchfile { matchFile = matchfile
, relFile = file , relFile = file
} }
matchMrun matcher $ \a -> a notpresent fi
matchAll :: FileMatcher matchAll :: FileMatcher
matchAll = generate [] matchAll = generate []
@ -65,11 +72,14 @@ parseToken checkpresent checkpreferreddir groupmap t
| t `elem` tokens = Right $ token t | t `elem` tokens = Right $ token t
| t == "present" = use checkpresent | t == "present" = use checkpresent
| t == "inpreferreddir" = use checkpreferreddir | t == "inpreferreddir" = use checkpreferreddir
| t == "unused" = Right (Operation limitUnused)
| otherwise = maybe (Left $ "near " ++ show t) use $ M.lookup k $ | otherwise = maybe (Left $ "near " ++ show t) use $ M.lookup k $
M.fromList M.fromList
[ ("include", limitInclude) [ ("include", limitInclude)
, ("exclude", limitExclude) , ("exclude", limitExclude)
, ("copies", limitCopies) , ("copies", limitCopies)
, ("lackingcopies", limitLackingCopies False)
, ("approxlackingcopies", limitLackingCopies True)
, ("inbackend", limitInBackend) , ("inbackend", limitInBackend)
, ("largerthan", limitSize (>)) , ("largerthan", limitSize (>))
, ("smallerthan", limitSize (<)) , ("smallerthan", limitSize (<))

View file

@ -7,7 +7,7 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
module Init ( module Annex.Init (
ensureInitialized, ensureInitialized,
isInitialized, isInitialized,
initialize, initialize,

View file

@ -20,6 +20,10 @@ import Annex.Exception
import qualified Git import qualified Git
import Annex.Perms import Annex.Perms
#ifdef mingw32_HOST_OS
import Utility.WinLock
#endif
{- Records content for a file in the branch to the journal. {- Records content for a file in the branch to the journal.
- -
- Using the journal, rather than immediatly staging content to the index - Using the journal, rather than immediatly staging content to the index
@ -116,13 +120,8 @@ lockJournal a = do
l <- noUmask mode $ createFile lockfile mode l <- noUmask mode $ createFile lockfile mode
waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0) waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0)
return l return l
#else
lock lockfile _mode = do
writeFile lockfile ""
return lockfile
#endif
#ifndef mingw32_HOST_OS
unlock = closeFd unlock = closeFd
#else #else
unlock = removeFile lock lockfile _mode = waitToLock $ lockExclusive lockfile
unlock = dropLock
#endif #endif

View file

@ -51,19 +51,15 @@ getAnnexLinkTarget file = ifM (coreSymlinks <$> Annex.getGitConfig)
| otherwise -> return Nothing | otherwise -> return Nothing
Nothing -> fallback Nothing -> fallback
probefilecontent f = do probefilecontent f = withFile f ReadMode $ \h -> do
h <- openFile f ReadMode
fileEncoding h fileEncoding h
-- The first 8k is more than enough to read; link -- The first 8k is more than enough to read; link
-- files are small. -- files are small.
s <- take 8192 <$> hGetContents h s <- take 8192 <$> hGetContents h
-- If we got the full 8k, the file is too large -- If we got the full 8k, the file is too large
if length s == 8192 if length s == 8192
then do then return ""
hClose h else
return ""
else do
hClose h
-- If there are any NUL or newline -- If there are any NUL or newline
-- characters, or whitespace, we -- characters, or whitespace, we
-- certianly don't have a link to a -- certianly don't have a link to a

View file

@ -1,6 +1,6 @@
{- git-annex lock pool {- git-annex lock pool
- -
- Copyright 2012 Joey Hess <joey@kitenet.net> - Copyright 2012, 2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -9,13 +9,16 @@
module Annex.LockPool where module Annex.LockPool where
import qualified Data.Map as M
import System.Posix.Types (Fd)
import Common.Annex import Common.Annex
import Annex import Annex
import Types.LockPool
import qualified Data.Map as M
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
import Annex.Perms import Annex.Perms
#else
import Utility.WinLock
#endif #endif
{- Create a specified lock file, and takes a shared lock. -} {- Create a specified lock file, and takes a shared lock. -}
@ -26,31 +29,32 @@ lockFile file = go =<< fromPool file
go Nothing = do go Nothing = do
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
mode <- annexFileMode mode <- annexFileMode
fd <- liftIO $ noUmask mode $ lockhandle <- liftIO $ noUmask mode $
openFd file ReadOnly (Just mode) defaultFileFlags openFd file ReadOnly (Just mode) defaultFileFlags
liftIO $ waitToSetLock fd (ReadLock, AbsoluteSeek, 0, 0) liftIO $ waitToSetLock lockhandle (ReadLock, AbsoluteSeek, 0, 0)
#else #else
liftIO $ writeFile file "" lockhandle <- liftIO $ waitToLock $ lockShared file
let fd = 0
#endif #endif
changePool $ M.insert file fd changePool $ M.insert file lockhandle
unlockFile :: FilePath -> Annex () unlockFile :: FilePath -> Annex ()
unlockFile file = maybe noop go =<< fromPool file unlockFile file = maybe noop go =<< fromPool file
where where
go fd = do go lockhandle = do
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
liftIO $ closeFd fd liftIO $ closeFd lockhandle
#else
liftIO $ dropLock lockhandle
#endif #endif
changePool $ M.delete file changePool $ M.delete file
getPool :: Annex (M.Map FilePath Fd) getPool :: Annex LockPool
getPool = getState lockpool getPool = getState lockpool
fromPool :: FilePath -> Annex (Maybe Fd) fromPool :: FilePath -> Annex (Maybe LockHandle)
fromPool file = M.lookup file <$> getPool fromPool file = M.lookup file <$> getPool
changePool :: (M.Map FilePath Fd -> M.Map FilePath Fd) -> Annex () changePool :: (LockPool -> LockPool) -> Annex ()
changePool a = do changePool a = do
m <- getPool m <- getPool
changeState $ \s -> s { lockpool = a m } changeState $ \s -> s { lockpool = a m }

View file

@ -14,19 +14,16 @@ import Annex.UUID
import qualified Data.Set as S import qualified Data.Set as S
{- Check if a file is preferred content for the local repository. -} {- Check if a file is preferred content for the local repository. -}
wantGet :: Bool -> AssociatedFile -> Annex Bool wantGet :: Bool -> Maybe Key -> AssociatedFile -> Annex Bool
wantGet def Nothing = return def wantGet def key file = isPreferredContent Nothing S.empty key file def
wantGet def (Just file) = isPreferredContent Nothing S.empty file def
{- Check if a file is preferred content for a remote. -} {- Check if a file is preferred content for a remote. -}
wantSend :: Bool -> AssociatedFile -> UUID -> Annex Bool wantSend :: Bool -> Maybe Key -> AssociatedFile -> UUID -> Annex Bool
wantSend def Nothing _ = return def wantSend def key file to = isPreferredContent (Just to) S.empty key file def
wantSend def (Just file) to = isPreferredContent (Just to) S.empty file def
{- Check if a file can be dropped, maybe from a remote. {- Check if a file can be dropped, maybe from a remote.
- Don't drop files that are preferred content. -} - Don't drop files that are preferred content. -}
wantDrop :: Bool -> Maybe UUID -> AssociatedFile -> Annex Bool wantDrop :: Bool -> Maybe UUID -> Maybe Key -> AssociatedFile -> Annex Bool
wantDrop def _ Nothing = return $ not def wantDrop def from key file = do
wantDrop def from (Just file) = do
u <- maybe getUUID (return . id) from u <- maybe getUUID (return . id) from
not <$> isPreferredContent (Just u) (S.singleton u) file def not <$> isPreferredContent (Just u) (S.singleton u) key file def

View file

@ -145,7 +145,7 @@ startDaemon assistant foreground startdelay cannotrun listenhost startbrowser =
, assist $ transferPollerThread , assist $ transferPollerThread
, assist $ transfererThread , assist $ transfererThread
, assist $ daemonStatusThread , assist $ daemonStatusThread
, assist $ sanityCheckerDailyThread , assist $ sanityCheckerDailyThread urlrenderer
, assist $ sanityCheckerHourlyThread , assist $ sanityCheckerHourlyThread
, assist $ problemFixerThread urlrenderer , assist $ problemFixerThread urlrenderer
#ifdef WITH_CLIBS #ifdef WITH_CLIBS

View file

@ -1,6 +1,6 @@
{- git-annex assistant alerts {- git-annex assistant alerts
- -
- Copyright 2012, 2013 Joey Hess <joey@kitenet.net> - Copyright 2012-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -253,13 +253,32 @@ upgradingAlert = activityAlert Nothing [ fromString "Upgrading git-annex" ]
upgradeFinishedAlert :: Maybe AlertButton -> GitAnnexVersion -> Alert upgradeFinishedAlert :: Maybe AlertButton -> GitAnnexVersion -> Alert
upgradeFinishedAlert button version = upgradeFinishedAlert button version =
baseUpgradeAlert (maybe [] (:[]) button) $ fromString $ baseUpgradeAlert (maybeToList button) $ fromString $
"Finished upgrading git-annex to version " ++ version "Finished upgrading git-annex to version " ++ version
upgradeFailedAlert :: String -> Alert upgradeFailedAlert :: String -> Alert
upgradeFailedAlert msg = (errorAlert msg []) upgradeFailedAlert msg = (errorAlert msg [])
{ alertHeader = Just $ fromString "Upgrade failed." } { alertHeader = Just $ fromString "Upgrade failed." }
unusedFilesAlert :: [AlertButton] -> String -> Alert
unusedFilesAlert buttons message = Alert
{ alertHeader = Just $ fromString $ unwords
[ "Old and deleted files are piling up --"
, message
]
, alertIcon = Just InfoIcon
, alertPriority = High
, alertButtons = buttons
, alertClosable = True
, alertClass = Message
, alertMessageRender = renderData
, alertCounter = 0
, alertBlockDisplay = True
, alertName = Just UnusedFilesAlert
, alertCombiner = Just $ fullCombiner $ \new _old -> new
, alertData = []
}
brokenRepositoryAlert :: [AlertButton] -> Alert brokenRepositoryAlert :: [AlertButton] -> Alert
brokenRepositoryAlert = errorAlert "Serious problems have been detected with your repository. This needs your immediate attention!" brokenRepositoryAlert = errorAlert "Serious problems have been detected with your repository. This needs your immediate attention!"
@ -298,7 +317,7 @@ pairRequestAcknowledgedAlert who button = baseActivityAlert
, alertPriority = High , alertPriority = High
, alertName = Just $ PairAlert who , alertName = Just $ PairAlert who
, alertCombiner = Just $ dataCombiner $ \_old new -> new , alertCombiner = Just $ dataCombiner $ \_old new -> new
, alertButtons = maybe [] (:[]) button , alertButtons = maybeToList button
} }
xmppNeededAlert :: AlertButton -> Alert xmppNeededAlert :: AlertButton -> Alert

View file

@ -55,11 +55,11 @@ calcSyncRemotes = do
let good r = Remote.uuid r `elem` alive let good r = Remote.uuid r `elem` alive
let syncable = filter good rs let syncable = filter good rs
let syncdata = filter (not . remoteAnnexIgnore . Remote.gitconfig) $ let syncdata = filter (not . remoteAnnexIgnore . Remote.gitconfig) $
filter (not . isXMPPRemote) syncable filter (not . Remote.isXMPPRemote) syncable
return $ \dstatus -> dstatus return $ \dstatus -> dstatus
{ syncRemotes = syncable { syncRemotes = syncable
, syncGitRemotes = filter Remote.syncableRemote syncable , syncGitRemotes = filter Remote.gitSyncableRemote syncable
, syncDataRemotes = syncdata , syncDataRemotes = syncdata
, syncingToCloudRemote = any iscloud syncdata , syncingToCloudRemote = any iscloud syncdata
} }
@ -257,11 +257,5 @@ alertDuring alert a = do
i <- addAlert $ alert { alertClass = Activity } i <- addAlert $ alert { alertClass = Activity }
removeAlert i `after` a removeAlert i `after` a
{- Remotes using the XMPP transport have urls like xmpp::user@host -}
isXMPPRemote :: Remote -> Bool
isXMPPRemote remote = Git.repoIsUrl r && "xmpp::" `isPrefixOf` Git.repoLocation r
where
r = Remote.repo remote
getXMPPClientID :: Remote -> ClientID getXMPPClientID :: Remote -> ClientID
getXMPPClientID r = T.pack $ drop (length "xmpp::") (Git.repoLocation (Remote.repo r)) getXMPPClientID r = T.pack $ drop (length "xmpp::") (Git.repoLocation (Remote.repo r))

View file

@ -5,108 +5,21 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Assistant.Drop where module Assistant.Drop (
handleDrops,
handleDropsFrom,
) where
import Assistant.Common import Assistant.Common
import Assistant.DaemonStatus import Assistant.DaemonStatus
import Annex.Drop (handleDropsFrom, Reason)
import Logs.Location import Logs.Location
import Logs.Trust import CmdLine.Action
import Types.Remote (uuid)
import qualified Remote
import qualified Command.Drop
import Command
import Annex.Wanted
import Annex.Exception
import Config
import Annex.Content.Direct
import qualified Data.Set as S
type Reason = String
{- Drop from local and/or remote when allowed by the preferred content and {- Drop from local and/or remote when allowed by the preferred content and
- numcopies settings. -} - numcopies settings. -}
handleDrops :: Reason -> Bool -> Key -> AssociatedFile -> Maybe Remote -> Assistant () handleDrops :: Reason -> Bool -> Key -> AssociatedFile -> Maybe Remote -> Assistant ()
handleDrops _ _ _ Nothing _ = noop
handleDrops reason fromhere key f knownpresentremote = do handleDrops reason fromhere key f knownpresentremote = do
syncrs <- syncDataRemotes <$> getDaemonStatus syncrs <- syncDataRemotes <$> getDaemonStatus
locs <- liftAnnex $ loggedLocations key locs <- liftAnnex $ loggedLocations key
handleDropsFrom locs syncrs reason fromhere key f knownpresentremote liftAnnex $ handleDropsFrom locs syncrs reason fromhere key f knownpresentremote callCommandAction
{- The UUIDs are ones where the content is believed to be present.
- The Remote list can include other remotes that do not have the content;
- only ones that match the UUIDs will be dropped from.
- If allowed to drop fromhere, that drop will be tried first.
-
- In direct mode, all associated files are checked, and only if all
- of them are unwanted are they dropped.
-}
handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile -> Maybe Remote -> Assistant ()
handleDropsFrom _ _ _ _ _ Nothing _ = noop
handleDropsFrom locs rs reason fromhere key (Just afile) knownpresentremote = do
fs <- liftAnnex $ ifM isDirect
( do
l <- associatedFilesRelative key
if null l
then return [afile]
else return l
, return [afile]
)
n <- getcopies fs
if fromhere && checkcopies n Nothing
then go fs rs =<< dropl fs n
else go fs rs n
where
getcopies fs = liftAnnex $ do
(untrusted, have) <- trustPartition UnTrusted locs
numcopies <- maximum <$> mapM (getNumCopies <=< numCopies) fs
return (length have, numcopies, S.fromList untrusted)
{- Check that we have enough copies still to drop the content.
- When the remote being dropped from is untrusted, it was not
- counted as a copy, so having only numcopies suffices. Otherwise,
- we need more than numcopies to safely drop. -}
checkcopies (have, numcopies, _untrusted) Nothing = have > numcopies
checkcopies (have, numcopies, untrusted) (Just u)
| S.member u untrusted = have >= numcopies
| otherwise = have > numcopies
decrcopies (have, numcopies, untrusted) Nothing =
(have - 1, numcopies, untrusted)
decrcopies v@(_have, _numcopies, untrusted) (Just u)
| S.member u untrusted = v
| otherwise = decrcopies v Nothing
go _ [] _ = noop
go fs (r:rest) n
| uuid r `S.notMember` slocs = go fs rest n
| checkcopies n (Just $ Remote.uuid r) =
dropr fs r n >>= go fs rest
| otherwise = noop
checkdrop fs n@(have, numcopies, _untrusted) u a =
ifM (liftAnnex $ allM (wantDrop True u . Just) fs)
( ifM (liftAnnex $ safely $ doCommand $ a (Just numcopies))
( do
debug
[ "dropped"
, afile
, "(from " ++ maybe "here" show u ++ ")"
, "(copies now " ++ show (have - 1) ++ ")"
, ": " ++ reason
]
return $ decrcopies n u
, return n
)
, return n
)
dropl fs n = checkdrop fs n Nothing $ \numcopies ->
Command.Drop.startLocal (Just afile) numcopies key knownpresentremote
dropr fs r n = checkdrop fs n (Just $ Remote.uuid r) $ \numcopies ->
Command.Drop.startRemote (Just afile) numcopies key r
safely a = either (const False) id <$> tryAnnex a
slocs = S.fromList locs

View file

@ -71,7 +71,7 @@ reconnectRemotes notifypushes rs = void $ do
mapM_ signal $ filter (`notElem` failedrs) rs' mapM_ signal $ filter (`notElem` failedrs) rs'
where where
gitremotes = filter (notspecialremote . Remote.repo) rs gitremotes = filter (notspecialremote . Remote.repo) rs
(xmppremotes, nonxmppremotes) = partition isXMPPRemote rs (xmppremotes, nonxmppremotes) = partition Remote.isXMPPRemote rs
notspecialremote r notspecialremote r
| Git.repoIsUrl r = True | Git.repoIsUrl r = True
| Git.repoIsLocal r = True | Git.repoIsLocal r = True
@ -133,7 +133,7 @@ pushToRemotes' now notifypushes remotes = do
<$> gitRepo <$> gitRepo
<*> inRepo Git.Branch.current <*> inRepo Git.Branch.current
<*> getUUID <*> getUUID
let (xmppremotes, normalremotes) = partition isXMPPRemote remotes let (xmppremotes, normalremotes) = partition Remote.isXMPPRemote remotes
ret <- go True branch g u normalremotes ret <- go True branch g u normalremotes
unless (null xmppremotes) $ do unless (null xmppremotes) $ do
shas <- liftAnnex $ map fst <$> shas <- liftAnnex $ map fst <$>
@ -206,7 +206,7 @@ syncAction rs a
return failed return failed
where where
visibleremotes = filter (not . Remote.readonly) $ visibleremotes = filter (not . Remote.readonly) $
filter (not . isXMPPRemote) rs filter (not . Remote.isXMPPRemote) rs
{- Manually pull from remotes and merge their branches. Returns any {- Manually pull from remotes and merge their branches. Returns any
- remotes that it failed to pull from, and a Bool indicating - remotes that it failed to pull from, and a Bool indicating
@ -220,7 +220,7 @@ syncAction rs a
manualPull :: Maybe Git.Ref -> [Remote] -> Assistant ([Remote], Bool) manualPull :: Maybe Git.Ref -> [Remote] -> Assistant ([Remote], Bool)
manualPull currentbranch remotes = do manualPull currentbranch remotes = do
g <- liftAnnex gitRepo g <- liftAnnex gitRepo
let (xmppremotes, normalremotes) = partition isXMPPRemote remotes let (xmppremotes, normalremotes) = partition Remote.isXMPPRemote remotes
failed <- liftIO $ forM normalremotes $ \r -> failed <- liftIO $ forM normalremotes $ \r ->
ifM (Git.Command.runBool [Param "fetch", Param $ Remote.name r] g) ifM (Git.Command.runBool [Param "fetch", Param $ Remote.name r] g)
( return Nothing ( return Nothing

View file

@ -464,7 +464,7 @@ checkChangeContent change@(Change { changeInfo = i }) =
Nothing -> noop Nothing -> noop
Just k -> whenM (scanComplete <$> getDaemonStatus) $ do Just k -> whenM (scanComplete <$> getDaemonStatus) $ do
present <- liftAnnex $ inAnnex k present <- liftAnnex $ inAnnex k
if present void $ if present
then queueTransfers "new file created" Next k (Just f) Upload then queueTransfers "new file created" Next k (Just f) Upload
else queueTransfers "new or renamed file wanted" Next k (Just f) Download else queueTransfers "new or renamed file wanted" Next k (Just f) Download
handleDrops "file renamed" present k (Just f) Nothing handleDrops "file renamed" present k (Just f) Nothing

View file

@ -17,6 +17,7 @@ import Logs.UUID
import Logs.Trust import Logs.Trust
import Logs.PreferredContent import Logs.PreferredContent
import Logs.Group import Logs.Group
import Logs.NumCopies
import Remote.List (remoteListRefresh) import Remote.List (remoteListRefresh)
import qualified Git.LsTree as LsTree import qualified Git.LsTree as LsTree
import Git.FilePath import Git.FilePath
@ -59,6 +60,7 @@ configFilesActions =
, (remoteLog, void $ liftAnnex remoteListRefresh) , (remoteLog, void $ liftAnnex remoteListRefresh)
, (trustLog, void $ liftAnnex trustMapLoad) , (trustLog, void $ liftAnnex trustMapLoad)
, (groupLog, void $ liftAnnex groupMapLoad) , (groupLog, void $ liftAnnex groupMapLoad)
, (numcopiesLog, void $ liftAnnex globalNumCopiesLoad)
, (scheduleLog, void updateScheduleLog) , (scheduleLog, void updateScheduleLog)
-- Preferred content settings depend on most of the other configs, -- Preferred content settings depend on most of the other configs,
-- so will be reloaded whenever any configs change. -- so will be reloaded whenever any configs change.

View file

@ -5,6 +5,8 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE CPP #-}
module Assistant.Threads.SanityChecker ( module Assistant.Threads.SanityChecker (
sanityCheckerStartupThread, sanityCheckerStartupThread,
sanityCheckerDailyThread, sanityCheckerDailyThread,
@ -15,7 +17,10 @@ import Assistant.Common
import Assistant.DaemonStatus import Assistant.DaemonStatus
import Assistant.Alert import Assistant.Alert
import Assistant.Repair import Assistant.Repair
import Assistant.Drop
import Assistant.Ssh import Assistant.Ssh
import Assistant.TransferQueue
import Assistant.Types.UrlRenderer
import qualified Annex.Branch import qualified Annex.Branch
import qualified Git.LsFiles import qualified Git.LsFiles
import qualified Git.Command import qualified Git.Command
@ -27,10 +32,20 @@ import Utility.Batch
import Utility.NotificationBroadcaster import Utility.NotificationBroadcaster
import Config import Config
import Utility.HumanTime import Utility.HumanTime
import Utility.Tense
import Git.Repair import Git.Repair
import Git.Index import Git.Index
import Assistant.Unused
import Logs.Unused
import Logs.Transfer
import Config.Files
import qualified Annex
#ifdef WITH_WEBAPP
import Assistant.WebApp.Types
#endif
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import qualified Data.Text as T
{- This thread runs once at startup, and most other threads wait for it {- This thread runs once at startup, and most other threads wait for it
- to finish. (However, the webapp thread does not, to prevent the UI - to finish. (However, the webapp thread does not, to prevent the UI
@ -78,8 +93,8 @@ sanityCheckerHourlyThread = namedThread "SanityCheckerHourly" $ forever $ do
hourlyCheck hourlyCheck
{- This thread wakes up daily to make sure the tree is in good shape. -} {- This thread wakes up daily to make sure the tree is in good shape. -}
sanityCheckerDailyThread :: NamedThread sanityCheckerDailyThread :: UrlRenderer -> NamedThread
sanityCheckerDailyThread = namedThread "SanityCheckerDaily" $ forever $ do sanityCheckerDailyThread urlrenderer = namedThread "SanityCheckerDaily" $ forever $ do
waitForNextCheck waitForNextCheck
debug ["starting sanity check"] debug ["starting sanity check"]
@ -90,7 +105,8 @@ sanityCheckerDailyThread = namedThread "SanityCheckerDaily" $ forever $ do
modifyDaemonStatus_ $ \s -> s { sanityCheckRunning = True } modifyDaemonStatus_ $ \s -> s { sanityCheckRunning = True }
now <- liftIO getPOSIXTime -- before check started now <- liftIO getPOSIXTime -- before check started
r <- either showerr return =<< (tryIO . batch) <~> dailyCheck r <- either showerr return
=<< (tryIO . batch) <~> dailyCheck urlrenderer
modifyDaemonStatus_ $ \s -> s modifyDaemonStatus_ $ \s -> s
{ sanityCheckRunning = False { sanityCheckRunning = False
@ -119,9 +135,10 @@ waitForNextCheck = do
{- It's important to stay out of the Annex monad as much as possible while {- It's important to stay out of the Annex monad as much as possible while
- running potentially expensive parts of this check, since remaining in it - running potentially expensive parts of this check, since remaining in it
- will block the watcher. -} - will block the watcher. -}
dailyCheck :: Assistant Bool dailyCheck :: UrlRenderer -> Assistant Bool
dailyCheck = do dailyCheck urlrenderer = do
g <- liftAnnex gitRepo g <- liftAnnex gitRepo
batchmaker <- liftIO getBatchCommandMaker
-- Find old unstaged symlinks, and add them to git. -- Find old unstaged symlinks, and add them to git.
(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo False ["."] g (unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo False ["."] g
@ -140,12 +157,29 @@ dailyCheck = do
- to have a lot of small objects and they should not be a - to have a lot of small objects and they should not be a
- significant size. -} - significant size. -}
when (Git.Config.getMaybe "gc.auto" g == Just "0") $ when (Git.Config.getMaybe "gc.auto" g == Just "0") $
liftIO $ void $ Git.Command.runBool liftIO $ void $ Git.Command.runBatch batchmaker
[ Param "-c", Param "gc.auto=670000" [ Param "-c", Param "gc.auto=670000"
, Param "gc" , Param "gc"
, Param "--auto" , Param "--auto"
] g ] g
{- Check if the unused files found last time have been dealt with. -}
checkOldUnused urlrenderer
{- Run git-annex unused once per day. This is run as a separate
- process to stay out of the annex monad and so it can run as a
- batch job. -}
program <- liftIO readProgramFile
let (program', params') = batchmaker (program, [Param "unused"])
void $ liftIO $ boolSystem program' params'
{- Invalidate unused keys cache, and queue transfers of all unused
- keys, or if no transfers are called for, drop them. -}
unused <- liftAnnex unusedKeys'
void $ liftAnnex $ setUnusedKeys unused
forM_ unused $ \k -> do
unlessM (queueTransfers "unused" Later k Nothing Upload) $
handleDrops "unused" True k Nothing Nothing
return True return True
where where
toonew timestamp now = now < (realToFrac (timestamp + slop) :: POSIXTime) toonew timestamp now = now < (realToFrac (timestamp + slop) :: POSIXTime)
@ -159,7 +193,8 @@ dailyCheck = do
insanity $ "found unstaged symlink: " ++ file insanity $ "found unstaged symlink: " ++ file
hourlyCheck :: Assistant () hourlyCheck :: Assistant ()
hourlyCheck = checkLogSize 0 hourlyCheck = do
checkLogSize 0
{- Rotate logs until log file size is < 1 mb. -} {- Rotate logs until log file size is < 1 mb. -}
checkLogSize :: Int -> Assistant () checkLogSize :: Int -> Assistant ()
@ -184,3 +219,23 @@ oneHour = 60 * 60
oneDay :: Int oneDay :: Int
oneDay = 24 * oneHour oneDay = 24 * oneHour
{- If annex.expireunused is set, find any keys that have lingered unused
- for the specified duration, and remove them.
-
- Otherwise, check to see if unused keys are piling up, and let the user
- know. -}
checkOldUnused :: UrlRenderer -> Assistant ()
checkOldUnused urlrenderer = go =<< annexExpireUnused <$> liftAnnex Annex.getGitConfig
where
go (Just Nothing) = noop
go (Just (Just expireunused)) = expireUnused (Just expireunused)
go Nothing = maybe noop prompt =<< describeUnusedWhenBig
prompt msg =
#ifdef WITH_WEBAPP
do
button <- mkAlertButton True (T.pack "Configure") urlrenderer ConfigUnusedR
void $ addAlert $ unusedFilesAlert [button] $ T.unpack $ renderTense Present msg
#else
debug [show $ renderTense Past msg]
#endif

View file

@ -29,6 +29,7 @@ import qualified Git.LsFiles as LsFiles
import qualified Backend import qualified Backend
import Annex.Content import Annex.Content
import Annex.Wanted import Annex.Wanted
import CmdLine.Action
import qualified Data.Set as S import qualified Data.Set as S
@ -156,16 +157,16 @@ expensiveScan urlrenderer rs = unless onlyweb $ batch <~> do
syncrs <- syncDataRemotes <$> getDaemonStatus syncrs <- syncDataRemotes <$> getDaemonStatus
locs <- liftAnnex $ loggedLocations key locs <- liftAnnex $ loggedLocations key
present <- liftAnnex $ inAnnex key present <- liftAnnex $ inAnnex key
handleDropsFrom locs syncrs liftAnnex $ handleDropsFrom locs syncrs
"expensive scan found too many copies of object" "expensive scan found too many copies of object"
present key (Just f) Nothing present key (Just f) Nothing callCommandAction
liftAnnex $ do liftAnnex $ do
let slocs = S.fromList locs let slocs = S.fromList locs
let use a = return $ mapMaybe (a key slocs) syncrs let use a = return $ mapMaybe (a key slocs) syncrs
ts <- if present ts <- if present
then filterM (wantSend True (Just f) . Remote.uuid . fst) then filterM (wantSend True (Just key) (Just f) . Remote.uuid . fst)
=<< use (genTransfer Upload False) =<< use (genTransfer Upload False)
else ifM (wantGet True $ Just f) else ifM (wantGet True (Just key) (Just f))
( use (genTransfer Download True) , return [] ) ( use (genTransfer Download True) , return [] )
let unwanted' = S.difference unwanted slocs let unwanted' = S.difference unwanted slocs
return (unwanted', ts) return (unwanted', ts)

View file

@ -27,6 +27,7 @@ import Assistant.WebApp.Configurators.IA
import Assistant.WebApp.Configurators.WebDAV import Assistant.WebApp.Configurators.WebDAV
import Assistant.WebApp.Configurators.XMPP import Assistant.WebApp.Configurators.XMPP
import Assistant.WebApp.Configurators.Preferences import Assistant.WebApp.Configurators.Preferences
import Assistant.WebApp.Configurators.Unused
import Assistant.WebApp.Configurators.Edit import Assistant.WebApp.Configurators.Edit
import Assistant.WebApp.Configurators.Delete import Assistant.WebApp.Configurators.Delete
import Assistant.WebApp.Configurators.Fsck import Assistant.WebApp.Configurators.Fsck

View file

@ -322,7 +322,7 @@ pairMsgReceived urlrenderer PairReq theiruuid selfjid theirjid
| baseJID selfjid == baseJID theirjid = autoaccept | baseJID selfjid == baseJID theirjid = autoaccept
| otherwise = do | otherwise = do
knownjids <- mapMaybe (parseJID . getXMPPClientID) knownjids <- mapMaybe (parseJID . getXMPPClientID)
. filter isXMPPRemote . syncRemotes <$> getDaemonStatus . filter Remote.isXMPPRemote . syncRemotes <$> getDaemonStatus
um <- liftAnnex uuidMap um <- liftAnnex uuidMap
if elem (baseJID theirjid) knownjids && M.member theiruuid um if elem (baseJID theirjid) knownjids && M.member theiruuid um
then autoaccept then autoaccept

View file

@ -1,6 +1,6 @@
{- git-annex assistant pending transfer queue {- git-annex assistant pending transfer queue
- -
- Copyright 2012 Joey Hess <joey@kitenet.net> - Copyright 2012-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -51,14 +51,17 @@ stubInfo f r = stubTransferInfo
{- Adds transfers to queue for some of the known remotes. {- Adds transfers to queue for some of the known remotes.
- Honors preferred content settings, only transferring wanted files. -} - Honors preferred content settings, only transferring wanted files. -}
queueTransfers :: Reason -> Schedule -> Key -> AssociatedFile -> Direction -> Assistant () queueTransfers :: Reason -> Schedule -> Key -> AssociatedFile -> Direction -> Assistant Bool
queueTransfers = queueTransfersMatching (const True) queueTransfers = queueTransfersMatching (const True)
{- Adds transfers to queue for some of the known remotes, that match a {- Adds transfers to queue for some of the known remotes, that match a
- condition. Honors preferred content settings. -} - condition. Honors preferred content settings. -}
queueTransfersMatching :: (UUID -> Bool) -> Reason -> Schedule -> Key -> AssociatedFile -> Direction -> Assistant () queueTransfersMatching :: (UUID -> Bool) -> Reason -> Schedule -> Key -> AssociatedFile -> Direction -> Assistant Bool
queueTransfersMatching matching reason schedule k f direction queueTransfersMatching matching reason schedule k f direction
| direction == Download = whenM (liftAnnex $ wantGet True f) go | direction == Download = ifM (liftAnnex $ wantGet True (Just k) f)
( go
, return False
)
| otherwise = go | otherwise = go
where where
go = do go = do
@ -67,9 +70,13 @@ queueTransfersMatching matching reason schedule k f direction
=<< syncDataRemotes <$> getDaemonStatus =<< syncDataRemotes <$> getDaemonStatus
let matchingrs = filter (matching . Remote.uuid) rs let matchingrs = filter (matching . Remote.uuid) rs
if null matchingrs if null matchingrs
then defer then do
else forM_ matchingrs $ \r -> defer
return False
else do
forM_ matchingrs $ \r ->
enqueue reason schedule (gentransfer r) (stubInfo f r) enqueue reason schedule (gentransfer r) (stubInfo f r)
return True
selectremotes rs selectremotes rs
{- Queue downloads from all remotes that {- Queue downloads from all remotes that
- have the key. The list of remotes is ordered with - have the key. The list of remotes is ordered with
@ -82,7 +89,7 @@ queueTransfersMatching matching reason schedule k f direction
- already have it. -} - already have it. -}
| otherwise = do | otherwise = do
s <- locs s <- locs
filterM (wantSend True f . Remote.uuid) $ filterM (wantSend True (Just k) f . Remote.uuid) $
filter (\r -> not (inset s r || Remote.readonly r)) rs filter (\r -> not (inset s r || Remote.readonly r)) rs
where where
locs = S.fromList <$> Remote.keyLocations k locs = S.fromList <$> Remote.keyLocations k

View file

@ -103,8 +103,8 @@ runTransferThread' program batchmaker d run = go
{- By the time this is called, the daemonstatus's currentTransfers map should {- By the time this is called, the daemonstatus's currentTransfers map should
- already have been updated to include the transfer. -} - already have been updated to include the transfer. -}
genTransfer :: Transfer -> TransferInfo -> TransferGenerator genTransfer :: Transfer -> TransferInfo -> TransferGenerator
genTransfer t info = case (transferRemote info, associatedFile info) of genTransfer t info = case transferRemote info of
(Just remote, Just file) Just remote
| Git.repoIsLocalUnknown (Remote.repo remote) -> do | Git.repoIsLocalUnknown (Remote.repo remote) -> do
-- optimisation for removable drives not plugged in -- optimisation for removable drives not plugged in
liftAnnex $ recordFailedTransfer t info liftAnnex $ recordFailedTransfer t info
@ -114,7 +114,7 @@ genTransfer t info = case (transferRemote info, associatedFile info) of
( do ( do
debug [ "Transferring:" , describeTransfer t info ] debug [ "Transferring:" , describeTransfer t info ]
notifyTransfer notifyTransfer
return $ Just (t, info, go remote file) return $ Just (t, info, go remote)
, do , do
debug [ "Skipping unnecessary transfer:", debug [ "Skipping unnecessary transfer:",
describeTransfer t info ] describeTransfer t info ]
@ -149,10 +149,12 @@ genTransfer t info = case (transferRemote info, associatedFile info) of
- usual cleanup. However, first check if something else is - usual cleanup. However, first check if something else is
- running the transfer, to avoid removing active transfers. - running the transfer, to avoid removing active transfers.
-} -}
go remote file transferrer = ifM (liftIO $ performTransfer transferrer t $ associatedFile info) go remote transferrer = ifM (liftIO $ performTransfer transferrer t $ associatedFile info)
( do ( do
void $ addAlert $ makeAlertFiller True $ maybe noop
transferFileAlert direction True file (void . addAlert . makeAlertFiller True
. transferFileAlert direction True)
(associatedFile info)
unless isdownload $ unless isdownload $
handleDrops handleDrops
("object uploaded to " ++ show remote) ("object uploaded to " ++ show remote)
@ -188,11 +190,11 @@ genTransfer t info = case (transferRemote info, associatedFile info) of
shouldTransfer :: Transfer -> TransferInfo -> Annex Bool shouldTransfer :: Transfer -> TransferInfo -> Annex Bool
shouldTransfer t info shouldTransfer t info
| transferDirection t == Download = | transferDirection t == Download =
(not <$> inAnnex key) <&&> wantGet True file (not <$> inAnnex key) <&&> wantGet True (Just key) file
| transferDirection t == Upload = case transferRemote info of | transferDirection t == Upload = case transferRemote info of
Nothing -> return False Nothing -> return False
Just r -> notinremote r Just r -> notinremote r
<&&> wantSend True file (Remote.uuid r) <&&> wantSend True (Just key) file (Remote.uuid r)
| otherwise = return False | otherwise = return False
where where
key = transferKey t key = transferKey t
@ -216,7 +218,7 @@ finishedTransfer t (Just info)
| transferDirection t == Download = | transferDirection t == Download =
whenM (liftAnnex $ inAnnex $ transferKey t) $ do whenM (liftAnnex $ inAnnex $ transferKey t) $ do
dodrops False dodrops False
queueTransfersMatching (/= transferUUID t) void $ queueTransfersMatching (/= transferUUID t)
"newly received object" "newly received object"
Later (transferKey t) (associatedFile info) Upload Later (transferKey t) (associatedFile info) Upload
| otherwise = dodrops True | otherwise = dodrops True

View file

@ -32,6 +32,7 @@ data AlertName
| SyncAlert | SyncAlert
| NotFsckedAlert | NotFsckedAlert
| UpgradeAlert | UpgradeAlert
| UnusedFilesAlert
deriving (Eq) deriving (Eq)
{- The first alert is the new alert, the second is an old alert. {- The first alert is the new alert, the second is an old alert.

86
Assistant/Unused.hs Normal file
View file

@ -0,0 +1,86 @@
{- git-annex assistant unused files
-
- Copyright 2014 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE OverloadedStrings #-}
module Assistant.Unused where
import qualified Data.Map as M
import Assistant.Common
import qualified Git
import Types.Key
import Logs.Unused
import Logs.Location
import Annex.Content
import Utility.DataUnits
import Utility.DiskFree
import Utility.HumanTime
import Utility.Tense
import Data.Time.Clock.POSIX
import qualified Data.Text as T
describeUnused :: Assistant (Maybe TenseText)
describeUnused = describeUnused' False
describeUnusedWhenBig :: Assistant (Maybe TenseText)
describeUnusedWhenBig = describeUnused' True
{- This uses heuristics: 1000 unused keys, or more unused keys
- than the remaining free disk space, or more than 1/10th the total
- disk space being unused keys all suggest a problem. -}
describeUnused' :: Bool -> Assistant (Maybe TenseText)
describeUnused' whenbig = liftAnnex $ go =<< readUnusedLog ""
where
go m = do
let num = M.size m
let diskused = foldl' sumkeysize 0 (M.keys m)
df <- forpath getDiskFree
disksize <- forpath getDiskSize
return $ if num == 0
then Nothing
else if not whenbig || moreused df diskused || tenthused disksize diskused
then Just $ tenseWords
[ UnTensed $ T.pack $ roughSize storageUnits False diskused
, Tensed "are" "were"
, "taken up by unused files"
]
else if num > 1000
then Just $ tenseWords
[ UnTensed $ T.pack $ show num ++ " unused files"
, Tensed "exist" "existed"
]
else Nothing
moreused Nothing _ = False
moreused (Just df) used = df <= used
tenthused Nothing _ = False
tenthused (Just disksize) used = used >= disksize `div` 10
sumkeysize s k = s + fromMaybe 0 (keySize k)
forpath a = inRepo $ liftIO . a . Git.repoPath
{- With a duration, expires all unused files that are older.
- With Nothing, expires *all* unused files. -}
expireUnused :: Maybe Duration -> Assistant ()
expireUnused duration = do
m <- liftAnnex $ readUnusedLog ""
now <- liftIO getPOSIXTime
let oldkeys = M.keys $ M.filter (tooold now) m
forM_ oldkeys $ \k -> do
debug ["removing old unused key", key2file k]
liftAnnex $ do
removeAnnex k
logStatus k InfoMissing
where
boundry = durationToPOSIXTime <$> duration
tooold now (_, mt) = case boundry of
Nothing -> True
Just b -> maybe False (\t -> now - t >= b) mt

View file

@ -276,7 +276,6 @@ deleteFromManifest dir = do
removeEmptyRecursive :: FilePath -> IO () removeEmptyRecursive :: FilePath -> IO ()
removeEmptyRecursive dir = do removeEmptyRecursive dir = do
print ("remove", dir)
mapM_ removeEmptyRecursive =<< dirContents dir mapM_ removeEmptyRecursive =<< dirContents dir
void $ tryIO $ removeDirectory dir void $ tryIO $ removeDirectory dir

View file

@ -96,12 +96,11 @@ deleteCurrentRepository = dangerPage $ do
rs <- syncRemotes <$> getDaemonStatus rs <- syncRemotes <$> getDaemonStatus
mapM_ (\r -> changeSyncable (Just r) False) rs mapM_ (\r -> changeSyncable (Just r) False) rs
{- Make all directories writable, so all annexed {- Make all directories writable and files writable
- content can be deleted. -} - so all annexed content can be deleted. -}
liftIO $ do liftIO $ do
recurseDir SystemFS dir >>= recurseDir SystemFS dir
filterM doesDirectoryExist >>= >>= mapM_ (void . tryIO . allowWrite)
mapM_ allowWrite
removeDirectoryRecursive dir removeDirectoryRecursive dir
redirect ShutdownConfirmedR redirect ShutdownConfirmedR

View file

@ -264,6 +264,7 @@ getUpgradeRepositoryR r = go =<< liftAnnex (repoIdRemote r)
liftAnnex $ setConfig liftAnnex $ setConfig
(remoteConfig (Remote.repo rmt) "ignore") (remoteConfig (Remote.repo rmt) "ignore")
(Git.Config.boolConfig False) (Git.Config.boolConfig False)
liftAssistant $ syncRemote rmt
liftAnnex $ void Remote.remoteListRefresh liftAnnex $ void Remote.remoteListRefresh
liftAssistant updateSyncRemotes
liftAssistant $ syncRemote rmt
redirect DashboardR redirect DashboardR

View file

@ -14,7 +14,7 @@ import Assistant.WebApp.Gpg
import Assistant.WebApp.MakeRemote import Assistant.WebApp.MakeRemote
import Assistant.Sync import Assistant.Sync
import Assistant.Restart import Assistant.Restart
import Init import Annex.Init
import qualified Git import qualified Git
import qualified Git.Construct import qualified Git.Construct
import qualified Git.Config import qualified Git.Config

View file

@ -17,6 +17,7 @@ import qualified Annex
import qualified Git import qualified Git
import Config import Config
import Config.Files import Config.Files
import Config.NumCopies
import Utility.DataUnits import Utility.DataUnits
import Git.Config import Git.Config
import Types.Distribution import Types.Distribution
@ -81,7 +82,7 @@ prefsAForm def = PrefsForm
getPrefs :: Annex PrefsForm getPrefs :: Annex PrefsForm
getPrefs = PrefsForm getPrefs = PrefsForm
<$> (T.pack . roughSize storageUnits False . annexDiskReserve <$> Annex.getGitConfig) <$> (T.pack . roughSize storageUnits False . annexDiskReserve <$> Annex.getGitConfig)
<*> (annexNumCopies <$> Annex.getGitConfig) <*> (fromNumCopies <$> getNumCopies)
<*> inAutoStartFile <*> inAutoStartFile
<*> (annexAutoUpgrade <$> Annex.getGitConfig) <*> (annexAutoUpgrade <$> Annex.getGitConfig)
<*> (annexDebug <$> Annex.getGitConfig) <*> (annexDebug <$> Annex.getGitConfig)
@ -89,7 +90,8 @@ getPrefs = PrefsForm
storePrefs :: PrefsForm -> Annex () storePrefs :: PrefsForm -> Annex ()
storePrefs p = do storePrefs p = do
setConfig (annexConfig "diskreserve") (T.unpack $ diskReserve p) setConfig (annexConfig "diskreserve") (T.unpack $ diskReserve p)
setConfig (annexConfig "numcopies") (show $ numCopies p) setGlobalNumCopies (NumCopies $ numCopies p)
unsetConfig (annexConfig "numcopies") -- deprecated
setConfig (annexConfig "autoupgrade") (fromAutoUpgrade $ autoUpgrade p) setConfig (annexConfig "autoupgrade") (fromAutoUpgrade $ autoUpgrade p)
unlessM ((==) <$> pure (autoStart p) <*> inAutoStartFile) $ do unlessM ((==) <$> pure (autoStart p) <*> inAutoStartFile) $ do
here <- fromRepo Git.repoPath here <- fromRepo Git.repoPath

View file

@ -0,0 +1,80 @@
{- git-annex assistant unused file preferences
-
- Copyright 2014 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings #-}
module Assistant.WebApp.Configurators.Unused where
import Assistant.WebApp.Common
import qualified Annex
import Utility.HumanTime
import Assistant.Unused
import Config
import Git.Config
import Logs.Unused
import Utility.Tense
import qualified Text.Hamlet as Hamlet
data UnusedForm = UnusedForm
{ enableExpire :: Bool
, expireWhen :: Integer
}
unusedForm :: UnusedForm -> Hamlet.Html -> MkMForm UnusedForm
unusedForm def msg = do
(enableRes, enableView) <- mreq (selectFieldList enabledisable) ""
(Just $ enableExpire def)
(whenRes, whenView) <- mreq intField ""
(Just $ expireWhen def)
let form = do
webAppFormAuthToken
$(widgetFile "configurators/unused/form")
return (UnusedForm <$> enableRes <*> whenRes, form)
where
enabledisable :: [(Text, Bool)]
enabledisable = [("Disable expiry", False), ("Enable expiry", True)]
getConfigUnusedR :: Handler Html
getConfigUnusedR = postConfigUnusedR
postConfigUnusedR :: Handler Html
postConfigUnusedR = page "Unused files" (Just Configuration) $ do
current <- liftAnnex getUnused
((res, form), enctype) <- liftH $ runFormPostNoToken $ unusedForm current
case res of
FormSuccess new -> liftH $ do
liftAnnex $ storeUnused new
redirect ConfigurationR
_ -> do
munuseddesc <- liftAssistant describeUnused
ts <- liftAnnex $ dateUnusedLog ""
mlastchecked <- case ts of
Nothing -> pure Nothing
Just t -> Just <$> liftIO (durationSince t)
$(widgetFile "configurators/unused")
getUnused :: Annex UnusedForm
getUnused = convert . annexExpireUnused <$> Annex.getGitConfig
where
convert Nothing = noexpire
convert (Just Nothing) = noexpire
convert (Just (Just n)) = UnusedForm True $ durationToDays n
-- The 7 is so that, if they enable expiry, they have to change
-- it to get faster than a week.
noexpire = UnusedForm False 7
storeUnused :: UnusedForm -> Annex ()
storeUnused f = setConfig (annexConfig "expireunused") $
if not (enableExpire f) || expireWhen f < 0
then boolConfig False
else fromDuration $ daysToDuration $ expireWhen f
getCleanupUnusedR :: Handler Html
getCleanupUnusedR = do
liftAssistant $ expireUnused Nothing
redirect ConfigUnusedR

View file

@ -161,7 +161,7 @@ buddyListDisplay = do
#ifdef WITH_XMPP #ifdef WITH_XMPP
getXMPPRemotes :: Assistant [(JID, Remote)] getXMPPRemotes :: Assistant [(JID, Remote)]
getXMPPRemotes = catMaybes . map pair . filter isXMPPRemote . syncGitRemotes getXMPPRemotes = catMaybes . map pair . filter Remote.isXMPPRemote . syncGitRemotes
<$> getDaemonStatus <$> getDaemonStatus
where where
pair r = maybe Nothing (\jid -> Just (jid, r)) $ pair r = maybe Nothing (\jid -> Just (jid, r)) $

View file

@ -164,7 +164,7 @@ repoList reposelector
| Remote.readonly r = False | Remote.readonly r = False
| onlyCloud reposelector = Git.repoIsUrl (Remote.repo r) | onlyCloud reposelector = Git.repoIsUrl (Remote.repo r)
&& Remote.uuid r /= NoUUID && Remote.uuid r /= NoUUID
&& not (isXMPPRemote r) && not (Remote.isXMPPRemote r)
| otherwise = True | otherwise = True
selectedremote Nothing = False selectedremote Nothing = False
selectedremote (Just (iscloud, _)) selectedremote (Just (iscloud, _))

View file

@ -25,6 +25,7 @@
/config/upgrade/start/#GitAnnexDistribution ConfigStartUpgradeR GET /config/upgrade/start/#GitAnnexDistribution ConfigStartUpgradeR GET
/config/upgrade/finish ConfigFinishUpgradeR GET /config/upgrade/finish ConfigFinishUpgradeR GET
/config/upgrade/automatically ConfigEnableAutomaticUpgradeR GET /config/upgrade/automatically ConfigEnableAutomaticUpgradeR GET
/config/unused ConfigUnusedR GET POST
/config/addrepository AddRepositoryR GET /config/addrepository AddRepositoryR GET
/config/repository/new NewRepositoryR GET POST /config/repository/new NewRepositoryR GET POST
@ -118,4 +119,6 @@
/repair/#UUID RepairRepositoryR GET POST /repair/#UUID RepairRepositoryR GET POST
/repair/run/#UUID RepairRepositoryRunR GET POST /repair/run/#UUID RepairRepositoryRunR GET POST
/unused/cleanup CleanupUnusedR GET
/static StaticR Static getStatic /static StaticR Static getStatic

View file

@ -125,8 +125,8 @@ getOutput c ps environ = do
putStrLn $ unwords [c, show ps] putStrLn $ unwords [c, show ps]
systemenviron <- getEnvironment systemenviron <- getEnvironment
let environ' = fromMaybe [] environ ++ systemenviron let environ' = fromMaybe [] environ ++ systemenviron
out@(s, ok) <- processTranscript' c ps (Just environ') Nothing out@(_, ok) <- processTranscript' c ps (Just environ') Nothing
putStrLn $ unwords [c, "finished", show ok, "output size:", show (length s)] putStrLn $ unwords [c, "finished", show ok]
return out return out
atFile :: FilePath -> String atFile :: FilePath -> String

View file

@ -141,4 +141,4 @@ parseLdd = catMaybes . map (getlib . dropWhile isSpace) . lines
- XXX Debian specific. -} - XXX Debian specific. -}
glibcLibs :: IO [FilePath] glibcLibs :: IO [FilePath]
glibcLibs = lines <$> readProcess "sh" glibcLibs = lines <$> readProcess "sh"
["-c", "dpkg -L libc6 libgcc1 | egrep '\\.so|gconv'"] ["-c", "dpkg -L libc6:$(dpkg --print-architecture) libgcc1:$(dpkg --print-architecture) | egrep '\\.so|gconv'"]

View file

@ -12,7 +12,7 @@ module Checks where
import Common.Annex import Common.Annex
import Types.Command import Types.Command
import Init import Annex.Init
import Config import Config
import Utility.Daemon import Utility.Daemon
import qualified Git import qualified Git

View file

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

70
CmdLine/Action.hs Normal file
View file

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

View file

@ -7,12 +7,11 @@
{-# LANGUAGE CPP, OverloadedStrings #-} {-# LANGUAGE CPP, OverloadedStrings #-}
module GitAnnex where module CmdLine.GitAnnex where
import qualified Git.CurrentRepo import qualified Git.CurrentRepo
import CmdLine import CmdLine
import Command import Command
import GitAnnex.Options
import qualified Command.Add import qualified Command.Add
import qualified Command.Unannex import qualified Command.Unannex
@ -50,6 +49,7 @@ import qualified Command.Info
import qualified Command.Status import qualified Command.Status
import qualified Command.Migrate import qualified Command.Migrate
import qualified Command.Uninit import qualified Command.Uninit
import qualified Command.NumCopies
import qualified Command.Trust import qualified Command.Trust
import qualified Command.Untrust import qualified Command.Untrust
import qualified Command.Semitrust import qualified Command.Semitrust
@ -117,6 +117,7 @@ cmds = concat
, Command.Unannex.def , Command.Unannex.def
, Command.Uninit.def , Command.Uninit.def
, Command.PreCommit.def , Command.PreCommit.def
, Command.NumCopies.def
, Command.Trust.def , Command.Trust.def
, Command.Untrust.def , Command.Untrust.def
, Command.Semitrust.def , Command.Semitrust.def
@ -178,4 +179,4 @@ run args = do
#ifdef WITH_EKG #ifdef WITH_EKG
_ <- forkServer "localhost" 4242 _ <- forkServer "localhost" 4242
#endif #endif
dispatch True args cmds options [] header Git.CurrentRepo.get dispatch True args cmds gitAnnexOptions [] header Git.CurrentRepo.get

View file

@ -5,23 +5,25 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module GitAnnex.Options where module CmdLine.GitAnnex.Options where
import System.Console.GetOpt import System.Console.GetOpt
import Common.Annex import Common.Annex
import qualified Git.Config import qualified Git.Config
import Git.Types import Git.Types
import Command
import Types.TrustLevel import Types.TrustLevel
import Types.NumCopies
import Types.Messages
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 qualified Option import CmdLine.Option
import CmdLine.Usage
options :: [Option] gitAnnexOptions :: [Option]
options = Option.common ++ gitAnnexOptions = commonOptions ++
[ Option ['N'] ["numcopies"] (ReqArg setnumcopies paramNumber) [ Option ['N'] ["numcopies"] (ReqArg setnumcopies paramNumber)
"override default number of copies" "override default number of copies"
, Option [] ["trust"] (trustArg Trusted) , Option [] ["trust"] (trustArg Trusted)
@ -40,6 +42,10 @@ options = Option.common ++
"match files present in a remote" "match files present in a remote"
, Option ['C'] ["copies"] (ReqArg Limit.addCopies paramNumber) , Option ['C'] ["copies"] (ReqArg Limit.addCopies paramNumber)
"skip files with fewer copies" "skip files with fewer copies"
, Option [] ["lackingcopies"] (ReqArg (Limit.addLackingCopies False) paramNumber)
"match files that need more copies"
, Option [] ["approxlackingcopies"] (ReqArg (Limit.addLackingCopies True) paramNumber)
"match files that need more copies (faster)"
, Option ['B'] ["inbackend"] (ReqArg Limit.addInBackend paramName) , Option ['B'] ["inbackend"] (ReqArg Limit.addInBackend paramName)
"match files using a key-value backend" "match files using a key-value backend"
, Option [] ["inallgroup"] (ReqArg Limit.addInAllGroup paramGroup) , Option [] ["inallgroup"] (ReqArg Limit.addInAllGroup paramGroup)
@ -58,11 +64,11 @@ options = Option.common ++
"override default User-Agent" "override default User-Agent"
, Option [] ["trust-glacier"] (NoArg (Annex.setFlag "trustglacier")) , Option [] ["trust-glacier"] (NoArg (Annex.setFlag "trustglacier"))
"Trust Amazon Glacier inventory" "Trust Amazon Glacier inventory"
] ++ Option.matcher ] ++ matcherOptions
where where
trustArg t = ReqArg (Remote.forceTrust t) paramRemote trustArg t = ReqArg (Remote.forceTrust t) paramRemote
setnumcopies v = maybe noop setnumcopies v = maybe noop
(\n -> Annex.changeState $ \s -> s { Annex.forcenumcopies = Just n }) (\n -> Annex.changeState $ \s -> s { Annex.forcenumcopies = Just $ NumCopies n })
(readish v) (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)
@ -75,13 +81,19 @@ keyOptions =
"operate on all versions of all files" "operate on all versions of all files"
, Option ['U'] ["unused"] (NoArg (Annex.setFlag "unused")) , Option ['U'] ["unused"] (NoArg (Annex.setFlag "unused"))
"operate on files found by last run of git-annex unused" "operate on files found by last run of git-annex unused"
, Option [] ["key"] (ReqArg (Annex.setField "key") paramKey)
"operate on specified key"
] ]
fromOption :: Option fromOption :: Option
fromOption = Option.field ['f'] "from" paramRemote "source remote" fromOption = fieldOption ['f'] "from" paramRemote "source remote"
toOption :: Option toOption :: Option
toOption = Option.field ['t'] "to" paramRemote "destination remote" toOption = fieldOption ['t'] "to" paramRemote "destination remote"
fromToOptions :: [Option] fromToOptions :: [Option]
fromToOptions = [fromOption, toOption] fromToOptions = [fromOption, toOption]
jsonOption :: Option
jsonOption = Option ['j'] ["json"] (NoArg (Annex.setOutput JSONOutput))
"enable JSON output"

View file

@ -5,7 +5,7 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module GitAnnexShell where module CmdLine.GitAnnexShell where
import System.Environment import System.Environment
import System.Console.GetOpt import System.Console.GetOpt
@ -16,12 +16,11 @@ import CmdLine
import Command import Command
import Annex.UUID import Annex.UUID
import Annex (setField) import Annex (setField)
import qualified Option import CmdLine.GitAnnexShell.Fields
import Fields
import Utility.UserInfo import Utility.UserInfo
import Remote.GCrypt (getGCryptUUID) import Remote.GCrypt (getGCryptUUID)
import qualified Annex import qualified Annex
import Init import Annex.Init
import qualified Command.ConfigList import qualified Command.ConfigList
import qualified Command.InAnnex import qualified Command.InAnnex
@ -54,7 +53,7 @@ cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c } adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c }
options :: [OptDescr (Annex ())] options :: [OptDescr (Annex ())]
options = Option.common ++ options = commonOptions ++
[ Option [] ["uuid"] (ReqArg checkUUID paramUUID) "local repository uuid" [ Option [] ["uuid"] (ReqArg checkUUID paramUUID) "local repository uuid"
] ]
where where
@ -104,7 +103,7 @@ builtin cmd dir params = do
Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath
where where
addrsyncopts opts seek k = setField "RsyncOptions" opts >> seek k addrsyncopts opts seek k = setField "RsyncOptions" opts >> seek k
newcmd opts c = c { cmdseek = map (addrsyncopts opts) (cmdseek c) } newcmd opts c = c { cmdseek = addrsyncopts opts (cmdseek c) }
external :: [String] -> IO () external :: [String] -> IO ()
external params = do external params = do

View file

@ -1,14 +1,15 @@
{- git-annex fields {- git-annex-shell fields
- -
- Copyright 2012 Joey Hess <joey@kitenet.net> - Copyright 2012 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Fields where module CmdLine.GitAnnexShell.Fields where
import Common.Annex import Common.Annex
import qualified Annex import qualified Annex
import Git.FilePath
import Data.Char import Data.Char
@ -29,7 +30,7 @@ remoteUUID = Field "remoteuuid" $
associatedFile :: Field associatedFile :: Field
associatedFile = Field "associatedfile" $ \f -> associatedFile = Field "associatedfile" $ \f ->
-- is the file a safe relative filename? -- is the file a safe relative filename?
not (isAbsolute f) && not ("../" `isPrefixOf` f) not (absoluteGitPath f) && not ("../" `isPrefixOf` f)
direct :: Field direct :: Field
direct = Field "direct" $ \f -> f == "1" direct = Field "direct" $ \f -> f == "1"

View file

@ -5,12 +5,12 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Option ( module CmdLine.Option (
common, commonOptions,
matcher, matcherOptions,
flag, flagOption,
field, fieldOption,
name, optionName,
ArgDescr(..), ArgDescr(..),
OptDescr(..), OptDescr(..),
) where ) where
@ -21,10 +21,10 @@ import Common.Annex
import qualified Annex import qualified Annex
import Types.Messages import Types.Messages
import Limit import Limit
import Usage import CmdLine.Usage
common :: [Option] commonOptions :: [Option]
common = commonOptions =
[ Option [] ["force"] (NoArg (setforce True)) [ Option [] ["force"] (NoArg (setforce True))
"allow actions that may lose annexed data" "allow actions that may lose annexed data"
, Option ['F'] ["fast"] (NoArg (setfast True)) , Option ['F'] ["fast"] (NoArg (setfast True))
@ -35,8 +35,6 @@ common =
"avoid verbose output" "avoid verbose output"
, Option ['v'] ["verbose"] (NoArg (Annex.setOutput NormalOutput)) , Option ['v'] ["verbose"] (NoArg (Annex.setOutput NormalOutput))
"allow verbose output (default)" "allow verbose output (default)"
, Option ['j'] ["json"] (NoArg (Annex.setOutput JSONOutput))
"enable JSON output"
, Option ['d'] ["debug"] (NoArg setdebug) , Option ['d'] ["debug"] (NoArg setdebug)
"show debug messages" "show debug messages"
, Option [] ["no-debug"] (NoArg unsetdebug) , Option [] ["no-debug"] (NoArg unsetdebug)
@ -52,8 +50,8 @@ common =
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 }
matcher :: [Option] matcherOptions :: [Option]
matcher = matcherOptions =
[ 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"
@ -65,15 +63,15 @@ matcher =
shortopt o = Option o [] $ NoArg $ addToken o shortopt o = Option o [] $ NoArg $ addToken o
{- An option that sets a flag. -} {- An option that sets a flag. -}
flag :: String -> String -> String -> Option flagOption :: String -> String -> String -> Option
flag short opt description = flagOption short opt description =
Option short [opt] (NoArg (Annex.setFlag opt)) description Option short [opt] (NoArg (Annex.setFlag opt)) description
{- An option that sets a field. -} {- An option that sets a field. -}
field :: String -> String -> String -> String -> Option fieldOption :: String -> String -> String -> String -> Option
field short opt paramdesc description = fieldOption short opt paramdesc description =
Option short [opt] (ReqArg (Annex.setField opt) paramdesc) description Option short [opt] (ReqArg (Annex.setField opt) paramdesc) description
{- The flag or field name used for an option. -} {- The flag or field name used for an option. -}
name :: Option -> String optionName :: Option -> String
name (Option _ o _ _) = Prelude.head o optionName (Option _ o _ _) = Prelude.head o

View file

@ -4,14 +4,12 @@
- the values a user passes to a command, and prepare actions operating - the values a user passes to a command, and prepare actions operating
- on them. - on them.
- -
- Copyright 2010-2013 Joey Hess <joey@kitenet.net> - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Seek where module CmdLine.Seek where
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Types.Command import Types.Command
@ -22,24 +20,15 @@ import qualified Git
import qualified Git.Command import qualified Git.Command
import qualified Git.LsFiles as LsFiles import qualified Git.LsFiles as LsFiles
import qualified Limit import qualified Limit
import qualified Option import CmdLine.Option
import Config import CmdLine.Action
import Logs.Location import Logs.Location
import Logs.Unused import Logs.Unused
import Annex.CatFile import Annex.CatFile
seekHelper :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> [FilePath] -> Annex [FilePath]
seekHelper a params = do
ll <- inRepo $ \g ->
runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) params
{- Show warnings only for files/directories that do not exist. -}
forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p ->
unlessM (isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)) $
fileNotFound p
return $ concat ll
withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek
withFilesInGit a params = prepFiltered a $ seekHelper LsFiles.inRepo params withFilesInGit a params = seekActions $ prepFiltered a $
seekHelper LsFiles.inRepo params
withFilesNotInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesNotInGit :: (FilePath -> CommandStart) -> CommandSeek
withFilesNotInGit a params = do withFilesNotInGit a params = do
@ -47,7 +36,8 @@ withFilesNotInGit a params = do
files <- filter (not . dotfile) <$> files <- filter (not . dotfile) <$>
seekunless (null ps && not (null params)) ps seekunless (null ps && not (null params)) ps
dotfiles <- seekunless (null dotps) dotps dotfiles <- seekunless (null dotps) dotps
prepFiltered a $ return $ concat $ segmentPaths params (files++dotfiles) seekActions $ prepFiltered a $
return $ concat $ segmentPaths params (files++dotfiles)
where where
(dotps, ps) = partition dotfile params (dotps, ps) = partition dotfile params
seekunless True _ = return [] seekunless True _ = return []
@ -57,7 +47,8 @@ withFilesNotInGit a params = do
liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g
withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek
withPathContents a params = map a . concat <$> liftIO (mapM get params) withPathContents a params = seekActions $
map a . concat <$> liftIO (mapM get params)
where where
get p = ifM (isDirectory <$> getFileStatus p) get p = ifM (isDirectory <$> getFileStatus p)
( map (\f -> (f, makeRelative (parentDir p) f)) ( map (\f -> (f, makeRelative (parentDir p) f))
@ -66,20 +57,20 @@ withPathContents a params = map a . concat <$> liftIO (mapM get params)
) )
withWords :: ([String] -> CommandStart) -> CommandSeek withWords :: ([String] -> CommandStart) -> CommandSeek
withWords a params = return [a params] withWords a params = seekActions $ return [a params]
withStrings :: (String -> CommandStart) -> CommandSeek withStrings :: (String -> CommandStart) -> CommandSeek
withStrings a params = return $ map a params withStrings a params = seekActions $ return $ map a params
withPairs :: ((String, String) -> CommandStart) -> CommandSeek withPairs :: ((String, String) -> CommandStart) -> CommandSeek
withPairs a params = return $ map a $ pairs [] params withPairs a params = seekActions $ return $ map a $ pairs [] params
where where
pairs c [] = reverse c pairs c [] = reverse c
pairs c (x:y:xs) = pairs ((x,y):c) xs pairs c (x:y:xs) = pairs ((x,y):c) xs
pairs _ _ = error "expected pairs" pairs _ _ = error "expected pairs"
withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek
withFilesToBeCommitted a params = prepFiltered a $ withFilesToBeCommitted a params = seekActions $ prepFiltered a $
seekHelper LsFiles.stagedNotDeleted params seekHelper LsFiles.stagedNotDeleted params
withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek
@ -94,7 +85,8 @@ withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged
- not some other sort of symlink. - not some other sort of symlink.
-} -}
withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> (FilePath -> CommandStart) -> CommandSeek
withFilesUnlocked' typechanged a params = prepFiltered a unlockedfiles withFilesUnlocked' typechanged a params = seekActions $
prepFiltered a unlockedfiles
where where
check f = liftIO (notSymlink f) <&&> check f = liftIO (notSymlink f) <&&>
(isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f) (isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f)
@ -102,32 +94,25 @@ withFilesUnlocked' typechanged a params = prepFiltered a unlockedfiles
{- Finds files that may be modified. -} {- Finds files that may be modified. -}
withFilesMaybeModified :: (FilePath -> CommandStart) -> CommandSeek withFilesMaybeModified :: (FilePath -> CommandStart) -> CommandSeek
withFilesMaybeModified a params = withFilesMaybeModified a params = seekActions $
prepFiltered a $ seekHelper LsFiles.modified params prepFiltered a $ seekHelper LsFiles.modified params
withKeys :: (Key -> CommandStart) -> CommandSeek withKeys :: (Key -> CommandStart) -> CommandSeek
withKeys a params = return $ map (a . parse) params withKeys a params = seekActions $ return $ map (a . parse) params
where where
parse p = fromMaybe (error "bad key") $ file2key p parse p = fromMaybe (error "bad key") $ file2key p
withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek {- Gets the value of a field options, which is fed into
withValue v a params = do - a conversion function.
r <- v
a r params
{- Modifies a seek action using the value of a field option, which is fed into
- a conversion function, and then is passed into the seek action.
- This ensures that the conversion function only runs once.
-} -}
withField :: Option -> (Maybe String -> Annex a) -> (a -> CommandSeek) -> CommandSeek getOptionField :: Option -> (Maybe String -> Annex a) -> Annex a
withField option converter = withValue $ getOptionField option converter = converter <=< Annex.getField $ optionName option
converter <=< Annex.getField $ Option.name option
withFlag :: Option -> (Bool -> CommandSeek) -> CommandSeek getOptionFlag :: Option -> Annex Bool
withFlag option = withValue $ Annex.getFlag (Option.name option) getOptionFlag option = Annex.getFlag (optionName option)
withNothing :: CommandStart -> CommandSeek withNothing :: CommandStart -> CommandSeek
withNothing a [] = return [a] withNothing a [] = seekActions $ return [a]
withNothing _ _ = error "This command takes no parameters." withNothing _ _ = error "This command takes no parameters."
{- If --all is specified, or in a bare repo, runs an action on all {- If --all is specified, or in a bare repo, runs an action on all
@ -136,6 +121,8 @@ withNothing _ _ = error "This command takes no parameters."
- If --unused is specified, runs an action on all keys found by - If --unused is specified, runs an action on all keys found by
- the last git annex unused scan. - the last git annex unused scan.
- -
- If --key is specified, operates only on that key.
-
- Otherwise, fall back to a regular CommandSeek action on - Otherwise, fall back to a regular CommandSeek action on
- whatever params were passed. -} - whatever params were passed. -}
withKeyOptions :: (Key -> CommandStart) -> CommandSeek -> CommandSeek withKeyOptions :: (Key -> CommandStart) -> CommandSeek -> CommandSeek
@ -143,36 +130,51 @@ withKeyOptions keyop fallbackop params = do
bare <- fromRepo Git.repoIsLocalBare bare <- fromRepo Git.repoIsLocalBare
allkeys <- Annex.getFlag "all" allkeys <- Annex.getFlag "all"
unused <- Annex.getFlag "unused" unused <- Annex.getFlag "unused"
specifickey <- Annex.getField "key"
auto <- Annex.getState Annex.auto auto <- Annex.getState Annex.auto
case (allkeys || bare , unused, auto ) of when (auto && bare) $
(True , False , False) -> go loggedKeys error "Cannot use --auto in a bare repository"
(False , True , False) -> go unusedKeys case (allkeys, unused, null params, specifickey) of
(True , True , _ ) (False , False , True , Nothing)
| bare && not allkeys -> go unusedKeys | bare -> go auto loggedKeys
| otherwise -> error "Cannot use --all with --unused." | otherwise -> fallbackop params
(False , False , _ ) -> fallbackop params (False , False , _ , Nothing) -> fallbackop params
(_ , _ , True ) (True , False , True , Nothing) -> go auto loggedKeys
| bare -> error "Cannot use --auto in a bare repository." (False , True , True , Nothing) -> go auto unusedKeys'
| otherwise -> error "Cannot use --auto with --all or --unused." (False , False , True , Just ks) -> case file2key ks of
Nothing -> error "Invalid key"
Just k -> go auto $ return [k]
_ -> error "Can only specify one of file names, --all, --unused, or --key"
where where
go a = do go True _ = error "Cannot use --auto with --all or --unused or --key"
unless (null params) $ go False a = do
error "Cannot mix --all or --unused with file names." matcher <- Limit.getMatcher
map keyop <$> a seekActions $ map (process matcher) <$> a
process matcher k = ifM (matcher $ MatchingKey k)
( keyop k , return Nothing)
prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart] prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart]
prepFiltered a fs = do prepFiltered a fs = do
matcher <- Limit.getMatcher matcher <- Limit.getMatcher
map (process matcher) <$> fs map (process matcher) <$> fs
where where
process matcher f = ifM (matcher $ FileInfo f f) process matcher f = ifM (matcher $ MatchingFile $ FileInfo f f)
( a f , return Nothing ) ( a f , return Nothing )
seekActions :: Annex [CommandStart] -> Annex ()
seekActions gen = do
as <- gen
mapM_ commandAction as
seekHelper :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> [FilePath] -> Annex [FilePath]
seekHelper a params = do
ll <- inRepo $ \g ->
runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) params
{- Show warnings only for files/directories that do not exist. -}
forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p ->
unlessM (isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)) $
fileNotFound p
return $ concat ll
notSymlink :: FilePath -> IO Bool notSymlink :: FilePath -> IO Bool
notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
whenNotDirect :: CommandSeek -> CommandSeek
whenNotDirect a params = ifM isDirect ( return [] , a params )
whenDirect :: CommandSeek -> CommandSeek
whenDirect a params = ifM isDirect ( a params, return [] )

View file

@ -5,7 +5,7 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Usage where module CmdLine.Usage where
import Common.Annex import Common.Annex

View file

@ -1,10 +1,12 @@
{- git-annex command infrastructure {- git-annex command infrastructure
- -
- Copyright 2010-2011 Joey Hess <joey@kitenet.net> - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE BangPatterns #-}
module Command ( module Command (
command, command,
noRepo, noRepo,
@ -14,13 +16,9 @@ module Command (
next, next,
stop, stop,
stopUnless, stopUnless,
prepCommand,
doCommand,
whenAnnexed, whenAnnexed,
ifAnnexed, ifAnnexed,
isBareRepo, isBareRepo,
numCopies,
numCopiesCheck,
checkAuto, checkAuto,
module ReExported module ReExported
) where ) where
@ -29,18 +27,17 @@ import Common.Annex
import qualified Backend import qualified Backend
import qualified Annex import qualified Annex
import qualified Git import qualified Git
import qualified Remote
import Types.Command as ReExported import Types.Command as ReExported
import Types.Option as ReExported import Types.Option as ReExported
import Seek as ReExported import CmdLine.Seek as ReExported
import Checks as ReExported import Checks as ReExported
import Usage as ReExported import CmdLine.Usage as ReExported
import Logs.Trust import CmdLine.Action as ReExported
import Config import CmdLine.Option as ReExported
import Annex.CheckAttr import CmdLine.GitAnnex.Options as ReExported
{- Generates a normal command -} {- Generates a normal command -}
command :: String -> String -> [CommandSeek] -> CommandSection -> String -> Command command :: String -> String -> CommandSeek -> CommandSection -> String -> Command
command = Command [] Nothing commonChecks False False command = Command [] Nothing commonChecks False False
{- Indicates that a command doesn't need to commit any changes to {- Indicates that a command doesn't need to commit any changes to
@ -74,25 +71,6 @@ stop = return Nothing
stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a) stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a)
stopUnless c a = ifM c ( a , stop ) stopUnless c a = ifM c ( a , stop )
{- Prepares to run a command via the check and seek stages, returning a
- list of actions to perform to run the command. -}
prepCommand :: Command -> [String] -> Annex [CommandCleanup]
prepCommand Command { cmdseek = seek, cmdcheck = c } params = do
mapM_ runCheck c
map doCommand . concat <$> mapM (\s -> s params) seek
{- Runs a command through the start, perform and cleanup stages -}
doCommand :: CommandStart -> CommandCleanup
doCommand = start
where
start = stage $ maybe skip perform
perform = stage $ maybe failure cleanup
cleanup = stage $ status
stage = (=<<)
skip = return True
failure = showEndFail >> return False
status r = showEndResult r >> return r
{- Modifies an action to only act on files that are already annexed, {- Modifies an action to only act on files that are already annexed,
- and passes the key and backend on to it. -} - and passes the key and backend on to it. -}
whenAnnexed :: (FilePath -> (Key, Backend) -> Annex (Maybe a)) -> FilePath -> Annex (Maybe a) whenAnnexed :: (FilePath -> (Key, Backend) -> Annex (Maybe a)) -> FilePath -> Annex (Maybe a)
@ -104,20 +82,6 @@ ifAnnexed file yes no = maybe no yes =<< Backend.lookupFile file
isBareRepo :: Annex Bool isBareRepo :: Annex Bool
isBareRepo = fromRepo Git.repoIsLocalBare isBareRepo = fromRepo Git.repoIsLocalBare
numCopies :: FilePath -> Annex (Maybe Int)
numCopies file = do
forced <- Annex.getState Annex.forcenumcopies
case forced of
Just n -> return $ Just n
Nothing -> readish <$> checkAttr "annex.numcopies" file
numCopiesCheck :: FilePath -> Key -> (Int -> Int -> v) -> Annex v
numCopiesCheck file key vs = do
numcopiesattr <- numCopies file
needed <- getNumCopies numcopiesattr
have <- trustExclude UnTrusted =<< Remote.keyLocations key
return $ length have `vs` needed
checkAuto :: Annex Bool -> Annex Bool checkAuto :: Annex Bool -> Annex Bool
checkAuto checker = ifM (Annex.getState Annex.auto) checkAuto checker = ifM (Annex.getState Annex.auto)
( checker , return True ) ( checker , return True )

View file

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

View file

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

View file

@ -21,7 +21,6 @@ import qualified Annex.Url as Url
import qualified Backend.URL import qualified Backend.URL
import Annex.Content import Annex.Content
import Logs.Web import Logs.Web
import qualified Option
import Types.Key import Types.Key
import Types.KeySource import Types.KeySource
import Config import Config
@ -39,19 +38,20 @@ def = [notBareRepo $ withOptions [fileOption, pathdepthOption, relaxedOption] $
SectionCommon "add urls to annex"] SectionCommon "add urls to annex"]
fileOption :: Option fileOption :: Option
fileOption = Option.field [] "file" paramFile "specify what file the url is added to" fileOption = fieldOption [] "file" paramFile "specify what file the url is added to"
pathdepthOption :: Option pathdepthOption :: Option
pathdepthOption = Option.field [] "pathdepth" paramNumber "path components to use in filename" pathdepthOption = fieldOption [] "pathdepth" paramNumber "path components to use in filename"
relaxedOption :: Option relaxedOption :: Option
relaxedOption = Option.flag [] "relaxed" "skip size check" relaxedOption = flagOption [] "relaxed" "skip size check"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField fileOption return $ \f -> seek ps = do
withFlag relaxedOption $ \relaxed -> f <- getOptionField fileOption return
withField pathdepthOption (return . maybe Nothing readish) $ \d -> relaxed <- getOptionFlag relaxedOption
withStrings $ start relaxed f d] d <- getOptionField pathdepthOption (return . maybe Nothing readish)
withStrings (start relaxed f d) ps
start :: Bool -> Maybe FilePath -> Maybe Int -> String -> CommandStart start :: Bool -> Maybe FilePath -> Maybe Int -> String -> CommandStart
start relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI s start relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI s

View file

@ -9,9 +9,8 @@ module Command.Assistant where
import Common.Annex import Common.Annex
import Command import Command
import qualified Option
import qualified Command.Watch import qualified Command.Watch
import Init import Annex.Init
import Config.Files import Config.Files
import qualified Build.SysConfig import qualified Build.SysConfig
import Utility.HumanTime import Utility.HumanTime
@ -32,17 +31,18 @@ options =
] ]
autoStartOption :: Option autoStartOption :: Option
autoStartOption = Option.flag [] "autostart" "start in known repositories" autoStartOption = flagOption [] "autostart" "start in known repositories"
startDelayOption :: Option startDelayOption :: Option
startDelayOption = Option.field [] "startdelay" paramNumber "delay before running startup scan" startDelayOption = fieldOption [] "startdelay" paramNumber "delay before running startup scan"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFlag Command.Watch.stopOption $ \stopdaemon -> seek ps = do
withFlag Command.Watch.foregroundOption $ \foreground -> stopdaemon <- getOptionFlag Command.Watch.stopOption
withFlag autoStartOption $ \autostart -> foreground <- getOptionFlag Command.Watch.foregroundOption
withField startDelayOption (pure . maybe Nothing parseDuration) $ \startdelay -> autostart <- getOptionFlag autoStartOption
withNothing $ start foreground stopdaemon autostart startdelay] startdelay <- getOptionField startDelayOption (pure . maybe Nothing parseDuration)
withNothing (start foreground stopdaemon autostart startdelay) ps
start :: Bool -> Bool -> Bool -> Maybe Duration -> CommandStart start :: Bool -> Bool -> Bool -> Maybe Duration -> CommandStart
start foreground stopdaemon autostart startdelay start foreground stopdaemon autostart startdelay

View file

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

View file

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

View file

@ -9,22 +9,23 @@ module Command.Copy where
import Common.Annex import Common.Annex
import Command import Command
import GitAnnex.Options
import qualified Command.Move import qualified Command.Move
import qualified Remote import qualified Remote
import Annex.Wanted import Annex.Wanted
import Config.NumCopies
def :: [Command] def :: [Command]
def = [withOptions Command.Move.moveOptions $ command "copy" paramPaths seek def = [withOptions Command.Move.moveOptions $ command "copy" paramPaths seek
SectionCommon "copy content of files to/from another repository"] SectionCommon "copy content of files to/from another repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField toOption Remote.byNameWithUUID $ \to -> to <- getOptionField toOption Remote.byNameWithUUID
withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (Command.Move.startKey to from False) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start to from (Command.Move.startKey to from False)
] (withFilesInGit $ whenAnnexed $ start to from)
ps
{- A copy is just a move that does not delete the source file. {- A copy is just a move that does not delete the source file.
- However, --auto mode avoids unnecessary copies, and avoids getting or - However, --auto mode avoids unnecessary copies, and avoids getting or
@ -35,5 +36,5 @@ start to from file (key, backend) = stopUnless shouldCopy $
where where
shouldCopy = checkAuto (check <||> numCopiesCheck file key (<)) shouldCopy = checkAuto (check <||> numCopiesCheck file key (<))
check = case to of check = case to of
Nothing -> wantGet False (Just file) Nothing -> wantGet False (Just key) (Just file)
Just r -> wantSend False (Just file) (Remote.uuid r) Just r -> wantSend False (Just key) (Just file) (Remote.uuid r)

View file

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

View file

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

View file

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

View file

@ -14,26 +14,25 @@ import qualified Annex
import Annex.UUID import Annex.UUID
import Logs.Location import Logs.Location
import Logs.Trust import Logs.Trust
import Config.NumCopies
import Annex.Content import Annex.Content
import Config
import qualified Option
import Annex.Wanted import Annex.Wanted
import Types.Key
def :: [Command] def :: [Command]
def = [withOptions [fromOption] $ command "drop" paramPaths seek def = [withOptions [dropFromOption] $ command "drop" paramPaths seek
SectionCommon "indicate content of files not currently wanted"] SectionCommon "indicate content of files not currently wanted"]
fromOption :: Option dropFromOption :: Option
fromOption = Option.field ['f'] "from" paramRemote "drop content from a remote" dropFromOption = fieldOption ['f'] "from" paramRemote "drop content from a remote"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField fromOption Remote.byNameWithUUID $ \from -> seek ps = do
withFilesInGit $ whenAnnexed $ start from] from <- getOptionField dropFromOption Remote.byNameWithUUID
withFilesInGit (whenAnnexed $ start from) ps
start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
start from file (key, _) = checkDropAuto from file key $ \numcopies -> start from file (key, _) = checkDropAuto from file key $ \numcopies ->
stopUnless (checkAuto $ wantDrop False (Remote.uuid <$> from) (Just file)) $ stopUnless (checkAuto $ wantDrop False (Remote.uuid <$> from) (Just key) (Just file)) $
case from of case from of
Nothing -> startLocal (Just file) numcopies key Nothing Nothing -> startLocal (Just file) numcopies key Nothing
Just remote -> do Just remote -> do
@ -42,17 +41,17 @@ start from file (key, _) = checkDropAuto from file key $ \numcopies ->
then startLocal (Just file) numcopies key Nothing then startLocal (Just file) numcopies key Nothing
else startRemote (Just file) numcopies key remote else startRemote (Just file) numcopies key remote
startLocal :: AssociatedFile -> Maybe Int -> 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
showStart "drop" (fromMaybe (key2file key) afile) showStart' "drop" key afile
next $ performLocal key numcopies knownpresentremote next $ performLocal key numcopies knownpresentremote
startRemote :: AssociatedFile -> Maybe Int -> Key -> Remote -> CommandStart startRemote :: AssociatedFile -> NumCopies -> Key -> Remote -> CommandStart
startRemote afile numcopies key remote = do startRemote afile numcopies key remote = do
showStart ("drop " ++ Remote.name remote) (fromMaybe (key2file key) afile) showStart' ("drop " ++ Remote.name remote) key afile
next $ performRemote key numcopies remote next $ performRemote key numcopies remote
performLocal :: Key -> Maybe Int -> Maybe Remote -> CommandPerform performLocal :: Key -> NumCopies -> Maybe Remote -> CommandPerform
performLocal key numcopies knownpresentremote = lockContent key $ do performLocal key numcopies knownpresentremote = lockContent key $ do
(remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key (remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key
let trusteduuids' = case knownpresentremote of let trusteduuids' = case knownpresentremote of
@ -64,7 +63,7 @@ performLocal key numcopies knownpresentremote = lockContent key $ do
removeAnnex key removeAnnex key
next $ cleanupLocal key next $ cleanupLocal key
performRemote :: Key -> Maybe Int -> Remote -> CommandPerform performRemote :: Key -> NumCopies -> Remote -> CommandPerform
performRemote key numcopies remote = lockContent key $ do performRemote key numcopies remote = lockContent key $ do
-- Filter the remote it's being dropped from out of the lists of -- Filter the remote it's being dropped from out of the lists of
-- places assumed to have the key, and places to check. -- places assumed to have the key, and places to check.
@ -97,23 +96,21 @@ cleanupRemote key remote ok = do
{- Checks specified remotes to verify that enough copies of a key exist to {- Checks specified remotes to verify that enough copies of a key exist to
- allow it to be safely removed (with no data loss). Can be provided with - allow it to be safely removed (with no data loss). Can be provided with
- some locations where the key is known/assumed to be present. -} - some locations where the key is known/assumed to be present. -}
canDropKey :: Key -> Maybe Int -> [UUID] -> [Remote] -> [UUID] -> Annex Bool canDropKey :: Key -> NumCopies -> [UUID] -> [Remote] -> [UUID] -> Annex Bool
canDropKey key numcopiesM have check skip = do canDropKey key numcopies have check skip = do
force <- Annex.getState Annex.force force <- Annex.getState Annex.force
if force || numcopiesM == Just 0 if force || numcopies == NumCopies 0
then return True then return True
else do else findCopies key numcopies skip have check
need <- getNumCopies numcopiesM
findCopies key need skip have check
findCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool findCopies :: Key -> NumCopies -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
findCopies key need skip = helper [] [] findCopies key need skip = helper [] []
where where
helper bad missing have [] helper bad missing have []
| length have >= need = return True | NumCopies (length have) >= need = return True
| otherwise = notEnoughCopies key need have (skip++missing) bad | otherwise = notEnoughCopies key need have (skip++missing) bad
helper bad missing have (r:rs) helper bad missing have (r:rs)
| length have >= need = return True | NumCopies (length have) >= need = return True
| otherwise = do | otherwise = do
let u = Remote.uuid r let u = Remote.uuid r
let duplicate = u `elem` have let duplicate = u `elem` have
@ -124,12 +121,12 @@ findCopies key need skip = helper [] []
(False, Right False) -> helper bad (u:missing) have rs (False, Right False) -> helper bad (u:missing) have rs
_ -> helper bad missing have rs _ -> helper bad missing have rs
notEnoughCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool notEnoughCopies :: Key -> NumCopies -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
notEnoughCopies key need have skip bad = do notEnoughCopies key need have skip bad = do
unsafe unsafe
showLongNote $ showLongNote $
"Could only verify the existence of " ++ "Could only verify the existence of " ++
show (length have) ++ " out of " ++ show need ++ show (length have) ++ " out of " ++ show (fromNumCopies need) ++
" necessary copies" " necessary copies"
Remote.showTriedRemotes bad Remote.showTriedRemotes bad
Remote.showLocations key (have++skip) Remote.showLocations key (have++skip)
@ -138,25 +135,21 @@ notEnoughCopies key need have skip bad = do
return False return False
where where
unsafe = showNote "unsafe" unsafe = showNote "unsafe"
hint = showLongNote "(Use --force to override this check, or adjust annex.numcopies.)" hint = showLongNote "(Use --force to override this check, or adjust numcopies.)"
{- 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 :: Maybe Remote -> FilePath -> Key -> (NumCopies -> CommandStart) -> CommandStart
- Passes any numcopies attribute of the file on to the action as an
- optimisation. -}
checkDropAuto :: Maybe Remote -> FilePath -> Key -> (Maybe Int -> CommandStart) -> CommandStart
checkDropAuto mremote file key a = do checkDropAuto mremote file key a = do
numcopiesattr <- numCopies file numcopies <- getFileNumCopies file
Annex.getState Annex.auto >>= auto numcopiesattr Annex.getState Annex.auto >>= auto numcopies
where where
auto numcopiesattr False = a numcopiesattr auto numcopies False = a numcopies
auto numcopiesattr True = do auto numcopies True = do
needed <- getNumCopies numcopiesattr
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
locs' <- trustExclude UnTrusted $ filter (/= remoteuuid) locs locs' <- trustExclude UnTrusted $ filter (/= remoteuuid) locs
if length locs' >= needed if NumCopies (length locs') >= numcopies
then a numcopiesattr then a numcopies
else stop else stop

View file

@ -12,20 +12,19 @@ import Command
import qualified Annex import qualified Annex
import Logs.Location import Logs.Location
import Annex.Content import Annex.Content
import Types.Key
def :: [Command] def :: [Command]
def = [noCommit $ command "dropkey" (paramRepeating paramKey) seek def = [noCommit $ command "dropkey" (paramRepeating paramKey) seek
SectionPlumbing "drops annexed content for specified keys"] SectionPlumbing "drops annexed content for specified keys"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withKeys start] seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart
start key = stopUnless (inAnnex key) $ do start key = stopUnless (inAnnex key) $ do
unlessM (Annex.getState Annex.force) $ unlessM (Annex.getState Annex.force) $
error "dropkey can cause data loss; use --force if you're sure you want to do this" error "dropkey can cause data loss; use --force if you're sure you want to do this"
showStart "dropkey" (key2file key) showStart' "dropkey" key Nothing
next $ perform key next $ perform key
perform :: Key -> CommandPerform perform :: Key -> CommandPerform

View file

@ -13,28 +13,30 @@ import qualified Annex
import qualified Command.Drop import qualified Command.Drop
import qualified Remote import qualified Remote
import qualified Git import qualified Git
import qualified Option
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused) import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
import Config.NumCopies
def :: [Command] def :: [Command]
def = [withOptions [Command.Drop.fromOption] $ def = [withOptions [Command.Drop.dropFromOption] $
command "dropunused" (paramRepeating paramNumRange) command "dropunused" (paramRepeating paramNumRange)
seek SectionMaintenance "drop unused file content"] seek SectionMaintenance "drop unused file content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withUnusedMaps start] seek ps = do
numcopies <- getNumCopies
withUnusedMaps (start numcopies) ps
start :: UnusedMaps -> Int -> CommandStart start :: NumCopies -> UnusedMaps -> Int -> CommandStart
start = startUnused "dropunused" perform (performOther gitAnnexBadLocation) (performOther gitAnnexTmpLocation) start numcopies = startUnused "dropunused" (perform numcopies) (performOther gitAnnexBadLocation) (performOther gitAnnexTmpLocation)
perform :: Key -> CommandPerform perform :: NumCopies -> Key -> CommandPerform
perform key = maybe droplocal dropremote =<< Remote.byNameWithUUID =<< from perform numcopies key = maybe droplocal dropremote =<< Remote.byNameWithUUID =<< from
where where
dropremote r = do dropremote r = do
showAction $ "from " ++ Remote.name r showAction $ "from " ++ Remote.name r
Command.Drop.performRemote key Nothing r Command.Drop.performRemote key numcopies r
droplocal = Command.Drop.performLocal key Nothing Nothing droplocal = Command.Drop.performLocal key numcopies Nothing
from = Annex.getField $ Option.name Command.Drop.fromOption 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

@ -20,8 +20,8 @@ def = [command "enableremote"
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue) (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
seek SectionSetup "enables use of an existing special remote"] seek SectionSetup "enables use of an existing special remote"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start [] = unknownNameError "Specify the name of the special remote to enable." start [] = unknownNameError "Specify the name of the special remote to enable."
@ -40,10 +40,10 @@ start (name:ws) = go =<< InitRemote.findExisting name
unknownNameError :: String -> Annex a unknownNameError :: String -> Annex a
unknownNameError prefix = do unknownNameError prefix = do
names <- InitRemote.remoteNames names <- InitRemote.remoteNames
error $ prefix ++ error $ prefix ++ "\n" ++
if null names if null names
then "" then "(No special remotes are currently known; perhaps use initremote instead?)"
else " Known special remotes: " ++ unwords names else "Known special remotes: " ++ unwords names
perform :: RemoteType -> UUID -> R.RemoteConfig -> CommandPerform perform :: RemoteType -> UUID -> R.RemoteConfig -> CommandPerform
perform t u c = do perform t u c = do

View file

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

View file

@ -17,26 +17,27 @@ import qualified Annex
import qualified Utility.Format import qualified Utility.Format
import Utility.DataUnits import Utility.DataUnits
import Types.Key import Types.Key
import qualified Option
def :: [Command] def :: [Command]
def = [noCommit $ noMessages $ withOptions [formatOption, print0Option] $ def = [noCommit $ noMessages $ withOptions [formatOption, print0Option, jsonOption] $
command "find" paramPaths seek SectionQuery "lists available files"] command "find" paramPaths seek SectionQuery "lists available files"]
formatOption :: Option formatOption :: Option
formatOption = Option.field [] "format" paramFormat "control format of output" formatOption = fieldOption [] "format" paramFormat "control format of output"
withFormat :: (Maybe Utility.Format.Format -> CommandSeek) -> CommandSeek getFormat :: Annex (Maybe Utility.Format.Format)
withFormat = withField formatOption $ return . fmap Utility.Format.gen getFormat = getOptionField formatOption $ return . fmap Utility.Format.gen
print0Option :: Option print0Option :: Option
print0Option = Option.Option [] ["print0"] (Option.NoArg set) print0Option = Option [] ["print0"] (NoArg set)
"terminate output with null" "terminate output with null"
where where
set = Annex.setField (Option.name formatOption) "${file}\0" set = Annex.setField (optionName formatOption) "${file}\0"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFormat $ \f -> withFilesInGit $ whenAnnexed $ start f] seek ps = do
format <- getFormat
withFilesInGit (whenAnnexed $ start format) ps
start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart
start format file (key, _) = do start format file (key, _) = do

View file

@ -9,8 +9,6 @@
module Command.Fix where module Command.Fix where
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Command import Command
import qualified Annex.Queue import qualified Annex.Queue
@ -24,8 +22,8 @@ def :: [Command]
def = [notDirect $ noCommit $ command "fix" paramPaths seek def = [notDirect $ noCommit $ command "fix" paramPaths seek
SectionMaintenance "fix up symlinks to point to annexed content"] SectionMaintenance "fix up symlinks to point to annexed content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFilesInGit $ whenAnnexed start] seek = withFilesInGit $ whenAnnexed start
{- Fixes the symlink to an annexed file. -} {- Fixes the symlink to an annexed file. -}
start :: FilePath -> (Key, Backend) -> CommandStart start :: FilePath -> (Key, Backend) -> CommandStart

View file

@ -12,7 +12,6 @@ import Command
import qualified Annex.Branch as Branch import qualified Annex.Branch as Branch
import Logs.Transitions import Logs.Transitions
import qualified Annex import qualified Annex
import qualified Option
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
@ -24,11 +23,12 @@ forgetOptions :: [Option]
forgetOptions = [dropDeadOption] forgetOptions = [dropDeadOption]
dropDeadOption :: Option dropDeadOption :: Option
dropDeadOption = Option.flag [] "drop-dead" "drop references to dead repositories" dropDeadOption = flagOption [] "drop-dead" "drop references to dead repositories"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withFlag dropDeadOption $ \dropdead -> seek ps = do
withNothing $ start dropdead] dropdead <- getOptionFlag dropDeadOption
withNothing (start dropdead) ps
start :: Bool -> CommandStart start :: Bool -> CommandStart
start dropdead = do start dropdead = do

View file

@ -7,8 +7,6 @@
module Command.FromKey where module Command.FromKey where
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Command import Command
import qualified Annex.Queue import qualified Annex.Queue
@ -20,8 +18,8 @@ def = [notDirect $ notBareRepo $
command "fromkey" (paramPair paramKey paramPath) seek command "fromkey" (paramPair paramKey paramPath) seek
SectionPlumbing "adds a file using a specific key"] SectionPlumbing "adds a file using a specific key"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start (keyname:file:[]) = do start (keyname:file:[]) = do

View file

@ -9,8 +9,6 @@
module Command.Fsck where module Command.Fsck where
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Command import Command
import qualified Annex import qualified Annex
@ -25,15 +23,14 @@ import Annex.Perms
import Annex.Link import Annex.Link
import Logs.Location import Logs.Location
import Logs.Trust import Logs.Trust
import Config.NumCopies
import Annex.UUID import Annex.UUID
import Utility.DataUnits import Utility.DataUnits
import Utility.FileMode import Utility.FileMode
import Config import Config
import qualified Option
import Types.Key import Types.Key
import Utility.HumanTime import Utility.HumanTime
import Git.FilePath import Git.FilePath
import GitAnnex.Options hiding (fromOption)
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
import System.Posix.Process (getProcessID) import System.Posix.Process (getProcessID)
@ -49,41 +46,42 @@ def :: [Command]
def = [withOptions fsckOptions $ command "fsck" paramPaths seek def = [withOptions fsckOptions $ command "fsck" paramPaths seek
SectionMaintenance "check for problems"] SectionMaintenance "check for problems"]
fromOption :: Option fsckFromOption :: Option
fromOption = Option.field ['f'] "from" paramRemote "check remote" fsckFromOption = fieldOption ['f'] "from" paramRemote "check remote"
startIncrementalOption :: Option startIncrementalOption :: Option
startIncrementalOption = Option.flag ['S'] "incremental" "start an incremental fsck" startIncrementalOption = flagOption ['S'] "incremental" "start an incremental fsck"
moreIncrementalOption :: Option moreIncrementalOption :: Option
moreIncrementalOption = Option.flag ['m'] "more" "continue an incremental fsck" moreIncrementalOption = flagOption ['m'] "more" "continue an incremental fsck"
incrementalScheduleOption :: Option incrementalScheduleOption :: Option
incrementalScheduleOption = Option.field [] "incremental-schedule" paramTime incrementalScheduleOption = fieldOption [] "incremental-schedule" paramTime
"schedule incremental fscking" "schedule incremental fscking"
fsckOptions :: [Option] fsckOptions :: [Option]
fsckOptions = fsckOptions =
[ fromOption [ fsckFromOption
, startIncrementalOption , startIncrementalOption
, moreIncrementalOption , moreIncrementalOption
, incrementalScheduleOption , incrementalScheduleOption
] ++ keyOptions ] ++ keyOptions
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fsckFromOption Remote.byNameWithUUID
withIncremental $ \i -> i <- getIncremental
withKeyOptions (startKey i) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start from i (startKey i)
] (withFilesInGit $ whenAnnexed $ start from i)
ps
withIncremental :: (Incremental -> CommandSeek) -> CommandSeek getIncremental :: Annex Incremental
withIncremental = withValue $ do getIncremental = do
i <- maybe (return False) (checkschedule . parseDuration) i <- maybe (return False) (checkschedule . parseDuration)
=<< Annex.getField (Option.name incrementalScheduleOption) =<< Annex.getField (optionName incrementalScheduleOption)
starti <- Annex.getFlag (Option.name startIncrementalOption) starti <- Annex.getFlag (optionName startIncrementalOption)
morei <- Annex.getFlag (Option.name moreIncrementalOption) morei <- Annex.getFlag (optionName moreIncrementalOption)
case (i, starti, morei) of case (i, starti, morei) of
(False, False, False) -> return NonIncremental (False, False, False) -> return NonIncremental
(False, True, _) -> startIncremental (False, True, _) -> startIncremental
@ -110,14 +108,14 @@ withIncremental = withValue $ do
start :: Maybe Remote -> Incremental -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> Incremental -> FilePath -> (Key, Backend) -> CommandStart
start from inc file (key, backend) = do start from inc file (key, backend) = do
numcopies <- numCopies file numcopies <- getFileNumCopies file
case from of case from of
Nothing -> go $ perform key file backend numcopies Nothing -> go $ perform key file backend numcopies
Just r -> go $ performRemote key file backend numcopies r Just r -> go $ performRemote key file backend numcopies r
where where
go = runFsck inc file key go = runFsck inc file key
perform :: Key -> FilePath -> Backend -> Maybe Int -> Annex Bool perform :: Key -> FilePath -> Backend -> NumCopies -> Annex Bool
perform key file backend numcopies = check perform key file backend numcopies = check
-- order matters -- order matters
[ fixLink key file [ fixLink key file
@ -131,7 +129,7 @@ perform key file backend numcopies = check
{- To fsck a remote, the content is retrieved to a tmp file, {- To fsck a remote, the content is retrieved to a tmp file,
- and checked locally. -} - and checked locally. -}
performRemote :: Key -> FilePath -> Backend -> Maybe Int -> Remote -> Annex Bool performRemote :: Key -> FilePath -> Backend -> NumCopies -> Remote -> Annex Bool
performRemote key file backend numcopies remote = performRemote key file backend numcopies remote =
dispatch =<< Remote.hasKey remote key dispatch =<< Remote.hasKey remote key
where where
@ -367,27 +365,26 @@ checkBackendOr' bad backend key file postcheck =
, return True , return True
) )
checkKeyNumCopies :: Key -> FilePath -> Maybe Int -> Annex Bool checkKeyNumCopies :: Key -> FilePath -> NumCopies -> Annex Bool
checkKeyNumCopies key file numcopies = do checkKeyNumCopies key file numcopies = do
needed <- getNumCopies numcopies
(untrustedlocations, safelocations) <- trustPartition UnTrusted =<< Remote.keyLocations key (untrustedlocations, safelocations) <- trustPartition UnTrusted =<< Remote.keyLocations key
let present = length safelocations let present = NumCopies (length safelocations)
if present < needed if present < numcopies
then do then do
ppuuids <- Remote.prettyPrintUUIDs "untrusted" untrustedlocations ppuuids <- Remote.prettyPrintUUIDs "untrusted" untrustedlocations
warning $ missingNote file present needed ppuuids warning $ missingNote file present numcopies ppuuids
return False return False
else return True else return True
missingNote :: String -> Int -> Int -> String -> String missingNote :: String -> NumCopies -> NumCopies -> String -> String
missingNote file 0 _ [] = missingNote file (NumCopies 0) _ [] =
"** No known copies exist of " ++ file "** No known copies exist of " ++ file
missingNote file 0 _ untrusted = missingNote file (NumCopies 0) _ untrusted =
"Only these untrusted locations may have copies of " ++ file ++ "Only these untrusted locations may have copies of " ++ file ++
"\n" ++ untrusted ++ "\n" ++ untrusted ++
"Back it up to trusted locations with git-annex copy." "Back it up to trusted locations with git-annex copy."
missingNote file present needed [] = missingNote file present needed [] =
"Only " ++ show present ++ " of " ++ show needed ++ "Only " ++ show (fromNumCopies present) ++ " of " ++ show (fromNumCopies needed) ++
" trustworthy copies exist of " ++ file ++ " trustworthy copies exist of " ++ file ++
"\nBack it up with git-annex copy." "\nBack it up with git-annex copy."
missingNote file present needed untrusted = missingNote file present needed untrusted =
@ -481,10 +478,9 @@ recordStartTime = do
createAnnexDirectory $ parentDir f createAnnexDirectory $ parentDir f
liftIO $ do liftIO $ do
nukeFile f nukeFile f
h <- openFile f WriteMode withFile f WriteMode $ \h -> do
t <- modificationTime <$> getFileStatus f t <- modificationTime <$> getFileStatus f
hPutStr h $ showTime $ realToFrac t hPutStr h $ showTime $ realToFrac t
hClose h
where where
showTime :: POSIXTime -> String showTime :: POSIXTime -> String
showTime = show showTime = show

View file

@ -25,8 +25,8 @@ def :: [Command]
def = [ notBareRepo $ command "fuzztest" paramNothing seek SectionPlumbing def = [ notBareRepo $ command "fuzztest" paramNothing seek SectionPlumbing
"generates fuzz test files"] "generates fuzz test files"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = do start = do
@ -146,13 +146,6 @@ genFuzzFile = do
genFuzzDir :: IO FuzzDir genFuzzDir :: IO FuzzDir
genFuzzDir = mkFuzzDir <$> (getStdRandom (randomR (1,16)) :: IO Int) genFuzzDir = mkFuzzDir <$> (getStdRandom (randomR (1,16)) :: IO Int)
localFile :: FilePath -> Bool
localFile f
| isAbsolute f = False
| ".." `isInfixOf` f = False
| ".git" `isPrefixOf` f = False
| otherwise = True
data TimeStampedFuzzAction data TimeStampedFuzzAction
= Started UTCTime FuzzAction = Started UTCTime FuzzAction
| Finished UTCTime Bool | Finished UTCTime Bool

View file

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

View file

@ -12,10 +12,9 @@ import Command
import qualified Remote import qualified Remote
import Annex.Content import Annex.Content
import Logs.Transfer import Logs.Transfer
import Config.NumCopies
import Annex.Wanted import Annex.Wanted
import GitAnnex.Options
import qualified Command.Move import qualified Command.Move
import Types.Key
def :: [Command] def :: [Command]
def = [withOptions getOptions $ command "get" paramPaths seek def = [withOptions getOptions $ command "get" paramPaths seek
@ -24,17 +23,18 @@ def = [withOptions getOptions $ command "get" paramPaths seek
getOptions :: [Option] getOptions :: [Option]
getOptions = fromOption : keyOptions getOptions = fromOption : keyOptions
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (startKeys from) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start from (startKeys from)
] (withFilesInGit $ whenAnnexed $ start from)
ps
start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
start from file (key, _) = start' expensivecheck from key (Just file) start from file (key, _) = start' expensivecheck from key (Just file)
where where
expensivecheck = checkAuto (numCopiesCheck file key (<) <||> wantGet False (Just file)) expensivecheck = checkAuto (numCopiesCheck file key (<) <||> wantGet False (Just key) (Just file))
startKeys :: Maybe Remote -> Key -> CommandStart startKeys :: Maybe Remote -> Key -> CommandStart
startKeys from key = start' (return True) from key Nothing startKeys from key = start' (return True) from key Nothing
@ -49,7 +49,7 @@ start' expensivecheck from key afile = stopUnless (not <$> inAnnex key) $
go $ Command.Move.fromPerform src False key afile go $ Command.Move.fromPerform src False key afile
where where
go a = do go a = do
showStart "get" (fromMaybe (key2file key) afile) showStart' "get" key afile
next a next a
perform :: Key -> AssociatedFile -> CommandPerform perform :: Key -> AssociatedFile -> CommandPerform
@ -59,7 +59,11 @@ perform key afile = stopUnless (getViaTmp key $ getKeyFile key afile) $
{- Try to find a copy of the file in one of the remotes, {- Try to find a copy of the file in one of the remotes,
- and copy it to here. -} - and copy it to here. -}
getKeyFile :: Key -> AssociatedFile -> FilePath -> Annex Bool getKeyFile :: Key -> AssociatedFile -> FilePath -> Annex Bool
getKeyFile key afile dest = dispatch =<< Remote.keyPossibilities key getKeyFile key afile dest = getKeyFile' key afile dest
=<< Remote.keyPossibilities key
getKeyFile' :: Key -> AssociatedFile -> FilePath -> [Remote] -> Annex Bool
getKeyFile' key afile dest = dispatch
where where
dispatch [] = do dispatch [] = do
showNote "not available" showNote "not available"

View file

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

View file

@ -18,7 +18,6 @@ import qualified Command.Copy
import qualified Command.Sync import qualified Command.Sync
import qualified Command.Whereis import qualified Command.Whereis
import qualified Command.Fsck import qualified Command.Fsck
import GitAnnex.Options
import System.Console.GetOpt import System.Console.GetOpt
@ -26,8 +25,8 @@ def :: [Command]
def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $ def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $
command "help" paramNothing seek SectionQuery "display help"] command "help" paramNothing seek SectionQuery "display help"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start params = do start params = do
@ -42,7 +41,7 @@ start' ["options"] = showCommonOptions
start' _ = showGeneralHelp start' _ = showGeneralHelp
showCommonOptions :: IO () showCommonOptions :: IO ()
showCommonOptions = putStrLn $ usageInfo "Common options:" options showCommonOptions = putStrLn $ usageInfo "Common options:" gitAnnexOptions
showGeneralHelp :: IO () showGeneralHelp :: IO ()
showGeneralHelp = putStrLn $ unlines showGeneralHelp = putStrLn $ unlines

View file

@ -7,13 +7,10 @@
module Command.Import where module Command.Import where
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Command import Command
import qualified Annex import qualified Annex
import qualified Command.Add import qualified Command.Add
import qualified Option
import Utility.CopyFile import Utility.CopyFile
import Backend import Backend
import Remote import Remote
@ -32,16 +29,16 @@ opts =
] ]
duplicateOption :: Option duplicateOption :: Option
duplicateOption = Option.flag [] "duplicate" "do not delete source files" duplicateOption = flagOption [] "duplicate" "do not delete source files"
deduplicateOption :: Option deduplicateOption :: Option
deduplicateOption = Option.flag [] "deduplicate" "delete source files whose content was imported before" deduplicateOption = flagOption [] "deduplicate" "delete source files whose content was imported before"
cleanDuplicatesOption :: Option cleanDuplicatesOption :: Option
cleanDuplicatesOption = Option.flag [] "clean-duplicates" "delete duplicate source files (import nothing)" cleanDuplicatesOption = flagOption [] "clean-duplicates" "delete duplicate source files (import nothing)"
skipDuplicatesOption :: Option skipDuplicatesOption :: Option
skipDuplicatesOption = Option.flag [] "skip-duplicates" "import only new files" skipDuplicatesOption = flagOption [] "skip-duplicates" "import only new files"
data DuplicateMode = Default | Duplicate | DeDuplicate | CleanDuplicates | SkipDuplicates data DuplicateMode = Default | Duplicate | DeDuplicate | CleanDuplicates | SkipDuplicates
deriving (Eq) deriving (Eq)
@ -53,7 +50,7 @@ getDuplicateMode = gen
<*> getflag cleanDuplicatesOption <*> getflag cleanDuplicatesOption
<*> getflag skipDuplicatesOption <*> getflag skipDuplicatesOption
where where
getflag = Annex.getFlag . Option.name getflag = Annex.getFlag . optionName
gen False False False False = Default gen False False False False = Default
gen True False False False = Duplicate gen True False False False = Duplicate
gen False True False False = DeDuplicate gen False True False False = DeDuplicate
@ -61,8 +58,10 @@ getDuplicateMode = gen
gen False False False True = SkipDuplicates gen False False False True = SkipDuplicates
gen _ _ _ _ = error "bad combination of --duplicate, --deduplicate, --clean-duplicates, --skip-duplicates" gen _ _ _ _ = error "bad combination of --duplicate, --deduplicate, --clean-duplicates, --skip-duplicates"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withValue getDuplicateMode $ \mode -> withPathContents $ start mode] seek ps = do
mode <- getDuplicateMode
withPathContents (start mode) ps
start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart
start mode (srcfile, destfile) = start mode (srcfile, destfile) =

View file

@ -21,7 +21,6 @@ import qualified Annex
import Command import Command
import qualified Annex.Url as Url import qualified Annex.Url as Url
import Logs.Web import Logs.Web
import qualified Option
import qualified Utility.Format import qualified Utility.Format
import Utility.Tmp import Utility.Tmp
import Command.AddUrl (addUrlFile, relaxedOption) import Command.AddUrl (addUrlFile, relaxedOption)
@ -39,13 +38,14 @@ def = [notBareRepo $ withOptions [templateOption, relaxedOption] $
SectionCommon "import files from podcast feeds"] SectionCommon "import files from podcast feeds"]
templateOption :: Option templateOption :: Option
templateOption = Option.field [] "template" paramFormat "template for filenames" templateOption = fieldOption [] "template" paramFormat "template for filenames"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withField templateOption return $ \tmpl -> seek ps = do
withFlag relaxedOption $ \relaxed -> tmpl <- getOptionField templateOption return
withValue (getCache tmpl) $ \cache -> relaxed <- getOptionFlag relaxedOption
withStrings $ start relaxed cache] cache <- getCache tmpl
withStrings (start relaxed cache) ps
start :: Bool -> Cache -> URLString -> CommandStart start :: Bool -> Cache -> URLString -> CommandStart
start relaxed cache url = do start relaxed cache url = do

View file

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

View file

@ -7,7 +7,6 @@
module Command.Indirect where module Command.Indirect where
import System.PosixCompat.Files
import Control.Exception.Extensible import Control.Exception.Extensible
import Common.Annex import Common.Annex
@ -23,7 +22,7 @@ import Annex.Content
import Annex.Content.Direct import Annex.Content.Direct
import Annex.CatFile import Annex.CatFile
import Annex.Exception import Annex.Exception
import Init import Annex.Init
import qualified Command.Add import qualified Command.Add
def :: [Command] def :: [Command]
@ -31,8 +30,8 @@ def = [notBareRepo $ noDaemonRunning $
command "indirect" paramNothing seek command "indirect" paramNothing seek
SectionSetup "switch repository to indirect mode"] SectionSetup "switch repository to indirect mode"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withNothing start] seek = withNothing start
start :: CommandStart start :: CommandStart
start = ifM isDirect start = ifM isDirect

View file

@ -14,7 +14,6 @@ import qualified Data.Map as M
import Text.JSON import Text.JSON
import Data.Tuple import Data.Tuple
import Data.Ord import Data.Ord
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import qualified Remote import qualified Remote
@ -28,6 +27,7 @@ import Annex.Content
import Types.Key import Types.Key
import Logs.UUID import Logs.UUID
import Logs.Trust import Logs.Trust
import Config.NumCopies
import Remote import Remote
import Config import Config
import Utility.Percentage import Utility.Percentage
@ -70,11 +70,12 @@ data StatInfo = StatInfo
type StatState = StateT StatInfo Annex type StatState = StateT StatInfo Annex
def :: [Command] def :: [Command]
def = [noCommit $ command "info" paramPaths seek def = [noCommit $ withOptions [jsonOption] $
SectionQuery "shows general information about the annex"] command "info" paramPaths seek SectionQuery
"shows general information about the annex"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [FilePath] -> CommandStart start :: [FilePath] -> CommandStart
start [] = do start [] = do
@ -310,7 +311,7 @@ getLocalStatInfo dir = do
where where
initial = (emptyKeyData, emptyKeyData, emptyNumCopiesStats) initial = (emptyKeyData, emptyKeyData, emptyNumCopiesStats)
update matcher fast key file vs@(presentdata, referenceddata, numcopiesstats) = update matcher fast key file vs@(presentdata, referenceddata, numcopiesstats) =
ifM (matcher $ FileInfo file file) ifM (matcher $ MatchingFile $ FileInfo file file)
( do ( do
!presentdata' <- ifM (inAnnex key) !presentdata' <- ifM (inAnnex key)
( return $ addKey key presentdata ( return $ addKey key presentdata

View file

@ -9,14 +9,14 @@ module Command.Init where
import Common.Annex import Common.Annex
import Command import Command
import Init import Annex.Init
def :: [Command] def :: [Command]
def = [dontCheck repoExists $ def = [dontCheck repoExists $
command "init" paramDesc seek SectionSetup "initialize git-annex"] command "init" paramDesc seek SectionSetup "initialize git-annex"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withWords start] seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start ws = do start ws = do

View file

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

View file

@ -20,7 +20,6 @@ import Remote
import Logs.Trust import Logs.Trust
import Logs.UUID import Logs.UUID
import Annex.UUID import Annex.UUID
import qualified Option
import qualified Annex import qualified Annex
import Git.Types (RemoteName) import Git.Types (RemoteName)
@ -29,16 +28,16 @@ def = [noCommit $ withOptions [allrepos] $ command "list" paramPaths seek
SectionQuery "show which remotes contain files"] SectionQuery "show which remotes contain files"]
allrepos :: Option allrepos :: Option
allrepos = Option.flag [] "allrepos" "show all repositories, not only remotes" allrepos = flagOption [] "allrepos" "show all repositories, not only remotes"
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withValue getList $ withNothing . startHeader list <- getList
, withValue getList $ withFilesInGit . whenAnnexed . start printHeader list
] withFilesInGit (whenAnnexed $ start list) ps
getList :: Annex [(UUID, RemoteName, TrustLevel)] getList :: Annex [(UUID, RemoteName, TrustLevel)]
getList = ifM (Annex.getFlag $ Option.name allrepos) getList = ifM (Annex.getFlag $ optionName allrepos)
( nubBy ((==) `on` fst3) <$> ((++) <$> getRemotes <*> getAll) ( nubBy ((==) `on` fst3) <$> ((++) <$> getRemotes <*> getAll)
, getRemotes , getRemotes
) )
@ -58,10 +57,8 @@ getList = ifM (Annex.getFlag $ Option.name allrepos)
return $ sortBy (comparing snd3) $ return $ sortBy (comparing snd3) $
filter (\t -> thd3 t /= DeadTrusted) rs3 filter (\t -> thd3 t /= DeadTrusted) rs3
startHeader :: [(UUID, RemoteName, TrustLevel)] -> CommandStart printHeader :: [(UUID, RemoteName, TrustLevel)] -> Annex ()
startHeader l = do printHeader l = liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l
liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l
stop
start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> (Key, Backend) -> CommandStart start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> (Key, Backend) -> CommandStart
start l file (key, _) = do start l file (key, _) = do

View file

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

View file

@ -24,7 +24,6 @@ import qualified Annex.Branch
import qualified Git import qualified Git
import Git.Command import Git.Command
import qualified Remote import qualified Remote
import qualified Option
import qualified Annex import qualified Annex
data RefChange = RefChange data RefChange = RefChange
@ -44,25 +43,26 @@ options = passthruOptions ++ [gourceOption]
passthruOptions :: [Option] passthruOptions :: [Option]
passthruOptions = map odate ["since", "after", "until", "before"] ++ passthruOptions = map odate ["since", "after", "until", "before"] ++
[ Option.field ['n'] "max-count" paramNumber [ fieldOption ['n'] "max-count" paramNumber
"limit number of logs displayed" "limit number of logs displayed"
] ]
where where
odate n = Option.field [] n paramDate $ "show log " ++ n ++ " date" odate n = fieldOption [] n paramDate $ "show log " ++ n ++ " date"
gourceOption :: Option gourceOption :: Option
gourceOption = Option.flag [] "gource" "format output for gource" gourceOption = flagOption [] "gource" "format output for gource"
seek :: [CommandSeek] seek :: CommandSeek
seek = [withValue Remote.uuidDescriptions $ \m -> seek ps = do
withValue (liftIO getCurrentTimeZone) $ \zone -> m <- Remote.uuidDescriptions
withValue (concat <$> mapM getoption passthruOptions) $ \os -> zone <- liftIO getCurrentTimeZone
withFlag gourceOption $ \gource -> os <- concat <$> mapM getoption passthruOptions
withFilesInGit $ whenAnnexed $ start m zone os gource] gource <- getOptionFlag gourceOption
withFilesInGit (whenAnnexed $ start m zone os gource) ps
where where
getoption o = maybe [] (use o) <$> getoption o = maybe [] (use o) <$>
Annex.getField (Option.name o) Annex.getField (optionName o)
use o v = [Param ("--" ++ Option.name o), Param v] use o v = [Param ("--" ++ optionName o), Param v]
start :: M.Map UUID String -> TimeZone -> [CommandParam] -> Bool -> start :: M.Map UUID String -> TimeZone -> [CommandParam] -> Bool ->
FilePath -> (Key, Backend) -> CommandStart FilePath -> (Key, Backend) -> CommandStart

View file

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

View file

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

View file

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

View file

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

View file

@ -9,34 +9,33 @@ module Command.Mirror where
import Common.Annex import Common.Annex
import Command import Command
import GitAnnex.Options
import qualified Command.Move import qualified Command.Move
import qualified Command.Drop import qualified Command.Drop
import qualified Command.Get import qualified Command.Get
import qualified Remote import qualified Remote
import Annex.Content import Annex.Content
import qualified Annex import qualified Annex
import Config.NumCopies
def :: [Command] def :: [Command]
def = [withOptions (fromToOptions ++ keyOptions) $ def = [withOptions (fromToOptions ++ keyOptions) $
command "mirror" paramPaths seek command "mirror" paramPaths seek
SectionCommon "mirror content of files to/from another repository"] SectionCommon "mirror content of files to/from another repository"]
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField toOption Remote.byNameWithUUID $ \to -> to <- getOptionField toOption Remote.byNameWithUUID
withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (startKey Nothing to from Nothing) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start to from (startKey to from Nothing)
] (withFilesInGit $ whenAnnexed $ start to from)
ps
start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
start to from file (key, _backend) = do start to from file (key, _backend) = startKey to from (Just file) key
numcopies <- numCopies file
startKey numcopies to from (Just file) key
startKey :: Maybe Int -> Maybe Remote -> Maybe Remote -> Maybe FilePath -> Key -> CommandStart startKey :: Maybe Remote -> Maybe Remote -> Maybe FilePath -> Key -> CommandStart
startKey numcopies to from afile key = do startKey to from afile key = do
noAuto noAuto
case (from, to) of case (from, to) of
(Nothing, Nothing) -> error "specify either --from or --to" (Nothing, Nothing) -> error "specify either --from or --to"
@ -48,7 +47,9 @@ startKey numcopies to from afile key = do
error "--auto is not supported for mirror" error "--auto is not supported for mirror"
mirrorto r = ifM (inAnnex key) mirrorto r = ifM (inAnnex key)
( Command.Move.toStart r False afile key ( Command.Move.toStart r False afile key
, Command.Drop.startRemote afile numcopies key r , do
numcopies <- getnumcopies
Command.Drop.startRemote afile numcopies key r
) )
mirrorfrom r = do mirrorfrom r = do
haskey <- Remote.hasKey r key haskey <- Remote.hasKey r key
@ -56,6 +57,9 @@ startKey numcopies to from afile key = do
Left _ -> stop Left _ -> stop
Right True -> Command.Get.start' (return True) Nothing key afile Right True -> Command.Get.start' (return True) Nothing key afile
Right False -> ifM (inAnnex key) Right False -> ifM (inAnnex key)
( Command.Drop.startLocal afile numcopies key Nothing ( do
numcopies <- getnumcopies
Command.Drop.startLocal afile numcopies key Nothing
, stop , stop
) )
getnumcopies = maybe getNumCopies getFileNumCopies afile

View file

@ -16,8 +16,6 @@ import qualified Remote
import Annex.UUID import Annex.UUID
import Logs.Presence import Logs.Presence
import Logs.Transfer import Logs.Transfer
import GitAnnex.Options
import Types.Key
def :: [Command] def :: [Command]
def = [withOptions moveOptions $ command "move" paramPaths seek def = [withOptions moveOptions $ command "move" paramPaths seek
@ -26,13 +24,14 @@ def = [withOptions moveOptions $ command "move" paramPaths seek
moveOptions :: [Option] moveOptions :: [Option]
moveOptions = fromToOptions ++ keyOptions moveOptions = fromToOptions ++ keyOptions
seek :: [CommandSeek] seek :: CommandSeek
seek = seek ps = do
[ withField toOption Remote.byNameWithUUID $ \to -> to <- getOptionField toOption Remote.byNameWithUUID
withField fromOption Remote.byNameWithUUID $ \from -> from <- getOptionField fromOption Remote.byNameWithUUID
withKeyOptions (startKey to from True) $ withKeyOptions
withFilesInGit $ whenAnnexed $ start to from True (startKey to from True)
] (withFilesInGit $ whenAnnexed $ start to from True)
ps
start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> (Key, Backend) -> CommandStart start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> (Key, Backend) -> CommandStart
start to from move file (key, _) = start' to from move (Just file) key start to from move file (key, _) = start' to from move (Just file) key
@ -53,17 +52,14 @@ start' to from move afile key = do
"--auto is not supported for move" "--auto is not supported for move"
showMoveAction :: Bool -> Key -> AssociatedFile -> Annex () showMoveAction :: Bool -> Key -> AssociatedFile -> Annex ()
showMoveAction True _ (Just file) = showStart "move" file showMoveAction move = showStart' (if move then "move" else "copy")
showMoveAction False _ (Just file) = showStart "copy" file
showMoveAction True key Nothing = showStart "move" (key2file key)
showMoveAction False key Nothing = showStart "copy" (key2file key)
{- Moves (or copies) the content of an annexed file to a remote. {- Moves (or copies) the content of an annexed file to a remote.
- -
- If the remote already has the content, it is still removed from - If the remote already has the content, it is still removed from
- the current repository. - the current repository.
- -
- Note that unlike drop, this does not honor annex.numcopies. - Note that unlike drop, this does not honor numcopies.
- 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.
-} -}

56
Command/NumCopies.hs Normal file
View file

@ -0,0 +1,56 @@
{- git-annex command
-
- Copyright 2014 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Command.NumCopies where
import Common.Annex
import qualified Annex
import Command
import Config.NumCopies
import Types.Messages
def :: [Command]
def = [command "numcopies" paramNumber seek
SectionSetup "configure desired number of copies"]
seek :: CommandSeek
seek = withWords start
start :: [String] -> CommandStart
start [] = startGet
start [s] = do
case readish s of
Nothing -> error $ "Bad number: " ++ s
Just n
| n > 0 -> startSet n
| n == 0 -> ifM (Annex.getState Annex.force)
( startSet n
, error "Setting numcopies to 0 is very unsafe. You will lose data! If you really want to do that, specify --force."
)
| otherwise -> error "Number cannot be negative!"
start _ = error "Specify a single number."
startGet :: CommandStart
startGet = next $ next $ do
Annex.setOutput QuietOutput
v <- getGlobalNumCopies
case v of
Just n -> liftIO $ putStrLn $ show $ fromNumCopies n
Nothing -> do
liftIO $ putStrLn $ "global numcopies is not set"
old <- deprecatedNumCopies
case old of
Nothing -> liftIO $ putStrLn "(default is 1)"
Just n -> liftIO $ putStrLn $ "(deprecated git config annex.numcopies is set to " ++ show (fromNumCopies n) ++ " locally)"
return True
startSet :: Int -> CommandStart
startSet n = do
showStart "numcopies" (show n)
next $ next $ do
setGlobalNumCopies $ NumCopies n
return True

View file

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

View file

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

View file

@ -7,8 +7,6 @@
module Command.RecvKey where module Command.RecvKey where
import System.PosixCompat.Files
import Common.Annex import Common.Annex
import Command import Command
import CmdLine import CmdLine
@ -17,7 +15,7 @@ import Annex
import Utility.Rsync import Utility.Rsync
import Logs.Transfer import Logs.Transfer
import Command.SendKey (fieldTransfer) import Command.SendKey (fieldTransfer)
import qualified Fields import qualified CmdLine.GitAnnexShell.Fields as Fields
import qualified Types.Key import qualified Types.Key
import qualified Types.Backend import qualified Types.Backend
import qualified Backend import qualified Backend
@ -26,8 +24,8 @@ def :: [Command]
def = [noCommit $ command "recvkey" paramKey seek def = [noCommit $ command "recvkey" paramKey seek
SectionPlumbing "runs rsync in server mode to receive content"] SectionPlumbing "runs rsync in server mode to receive content"]
seek :: [CommandSeek] seek :: CommandSeek
seek = [withKeys start] seek = withKeys start
start :: Key -> CommandStart start :: Key -> CommandStart
start key = ifM (inAnnex key) start key = ifM (inAnnex key)

View file

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

View file

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

View file

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

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