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:
commit
f78b81f463
416 changed files with 7498 additions and 1684 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,3 +28,4 @@ cabal-dev
|
|||
# OSX related
|
||||
.DS_Store
|
||||
.virthualenv
|
||||
.tasty-rerun-log
|
||||
|
|
6
.mailmap
Normal file
6
.mailmap
Normal 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>
|
18
Annex.hs
18
Annex.hs
|
@ -34,7 +34,6 @@ module Annex (
|
|||
|
||||
import "mtl" Control.Monad.Reader
|
||||
import "MonadCatchIO-transformers" Control.Monad.CatchIO
|
||||
import System.Posix.Types (Fd)
|
||||
import Control.Concurrent
|
||||
|
||||
import Common
|
||||
|
@ -46,6 +45,7 @@ import Git.CheckAttr
|
|||
import Git.CheckIgnore
|
||||
import Git.SharedRepository
|
||||
import qualified Git.Queue
|
||||
import Types.Key
|
||||
import Types.Backend
|
||||
import Types.GitConfig
|
||||
import qualified Types.Remote
|
||||
|
@ -56,6 +56,8 @@ import Types.Group
|
|||
import Types.Messages
|
||||
import Types.UUID
|
||||
import Types.FileMatcher
|
||||
import Types.NumCopies
|
||||
import Types.LockPool
|
||||
import qualified Utility.Matcher
|
||||
import qualified Data.Map as M
|
||||
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 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
|
||||
data AnnexState = AnnexState
|
||||
|
@ -94,8 +96,9 @@ data AnnexState = AnnexState
|
|||
, checkattrhandle :: Maybe CheckAttrHandle
|
||||
, checkignorehandle :: Maybe (Maybe CheckIgnoreHandle)
|
||||
, forcebackend :: Maybe String
|
||||
, forcenumcopies :: Maybe Int
|
||||
, limit :: Matcher (FileInfo -> Annex Bool)
|
||||
, globalnumcopies :: Maybe NumCopies
|
||||
, forcenumcopies :: Maybe NumCopies
|
||||
, limit :: Matcher (MatchInfo -> Annex Bool)
|
||||
, uuidmap :: Maybe UUIDMap
|
||||
, preferredcontentmap :: Maybe PreferredContentMap
|
||||
, shared :: Maybe SharedRepository
|
||||
|
@ -103,12 +106,14 @@ data AnnexState = AnnexState
|
|||
, trustmap :: Maybe TrustMap
|
||||
, groupmap :: Maybe GroupMap
|
||||
, ciphers :: M.Map StorableCipher Cipher
|
||||
, lockpool :: M.Map FilePath Fd
|
||||
, lockpool :: LockPool
|
||||
, flags :: M.Map String Bool
|
||||
, fields :: M.Map String String
|
||||
, cleanup :: M.Map String (Annex ())
|
||||
, inodeschanged :: Maybe Bool
|
||||
, useragent :: Maybe String
|
||||
, errcounter :: Integer
|
||||
, unusedkeys :: Maybe (S.Set Key)
|
||||
}
|
||||
|
||||
newState :: GitConfig -> Git.Repo -> AnnexState
|
||||
|
@ -128,6 +133,7 @@ newState c r = AnnexState
|
|||
, checkattrhandle = Nothing
|
||||
, checkignorehandle = Nothing
|
||||
, forcebackend = Nothing
|
||||
, globalnumcopies = Nothing
|
||||
, forcenumcopies = Nothing
|
||||
, limit = Left []
|
||||
, uuidmap = Nothing
|
||||
|
@ -143,6 +149,8 @@ newState c r = AnnexState
|
|||
, cleanup = M.empty
|
||||
, inodeschanged = Nothing
|
||||
, useragent = Nothing
|
||||
, errcounter = 0
|
||||
, unusedkeys = Nothing
|
||||
}
|
||||
|
||||
{- Makes an Annex state object for the specified git repo.
|
||||
|
|
|
@ -18,6 +18,7 @@ module Annex.Branch (
|
|||
forceUpdate,
|
||||
updateTo,
|
||||
get,
|
||||
getHistorical,
|
||||
change,
|
||||
commit,
|
||||
forceCommit,
|
||||
|
@ -197,7 +198,13 @@ getLocal file = go =<< getJournalFileStale file
|
|||
go Nothing = getRaw file
|
||||
|
||||
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.
|
||||
-
|
||||
|
@ -252,8 +259,7 @@ commitIndex' jl branchref message parents = do
|
|||
committedref <- inRepo $ Git.Branch.commitAlways message fullname parents
|
||||
setIndexSha committedref
|
||||
parentrefs <- commitparents <$> catObject committedref
|
||||
when (racedetected branchref parentrefs) $ do
|
||||
liftIO $ print ("race detected", branchref, parentrefs, "committing", (branchref, parents))
|
||||
when (racedetected branchref parentrefs) $
|
||||
fixrace committedref parentrefs
|
||||
where
|
||||
-- look for "parent ref" lines and return the refs
|
||||
|
|
|
@ -41,6 +41,7 @@ dropDead f content trustmap = case getLogVariety f of
|
|||
in if null newlog
|
||||
then RemoveFile
|
||||
else ChangeFile $ Presence.showLog newlog
|
||||
Just SingleValueLog -> PreserveFile
|
||||
Nothing -> PreserveFile
|
||||
|
||||
dropDeadFromUUIDBasedLog :: TrustMap -> UUIDBased.Log String -> UUIDBased.Log String
|
||||
|
|
128
Annex/Content.hs
128
Annex/Content.hs
|
@ -1,6 +1,6 @@
|
|||
{- 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.
|
||||
-}
|
||||
|
@ -35,7 +35,6 @@ module Annex.Content (
|
|||
) where
|
||||
|
||||
import System.IO.Unsafe (unsafeInterleaveIO)
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Logs.Location
|
||||
|
@ -57,6 +56,10 @@ import Annex.Content.Direct
|
|||
import Annex.ReplaceFile
|
||||
import Annex.Exception
|
||||
|
||||
#ifdef mingw32_HOST_OS
|
||||
import Utility.WinLock
|
||||
#endif
|
||||
|
||||
{- Checks if a given key's content is currently present. -}
|
||||
inAnnex :: Key -> Annex Bool
|
||||
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
|
||||
- is not in the process of being removed. -}
|
||||
inAnnexSafe :: Key -> Annex (Maybe Bool)
|
||||
inAnnexSafe = inAnnex' (fromMaybe False) (Just False) go
|
||||
inAnnexSafe key = inAnnex' (fromMaybe False) (Just False) go key
|
||||
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
|
||||
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 $
|
||||
openFd f ReadOnly Nothing defaultFileFlags
|
||||
#else
|
||||
openforlock _ = return $ Just ()
|
||||
#endif
|
||||
check Nothing = return is_missing
|
||||
#ifndef mingw32_HOST_OS
|
||||
check (Just h) = do
|
||||
check _ (Just h) = do
|
||||
v <- getLock h (ReadLock, AbsoluteSeek, 0, 0)
|
||||
closeFd h
|
||||
return $ case v of
|
||||
Just _ -> is_locked
|
||||
Nothing -> is_unlocked
|
||||
check def Nothing = return def
|
||||
#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
|
||||
#ifndef mingw32_HOST_OS
|
||||
is_locked = Nothing
|
||||
#endif
|
||||
is_unlocked = Just True
|
||||
is_missing = Just False
|
||||
|
||||
{- Direct mode and especially Windows has to use a separate lock
|
||||
- file from the content, since locking the actual content file
|
||||
- would interfere with the user's use of it. -}
|
||||
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
|
||||
- it. (If the content is not present, no locking is done.) -}
|
||||
lockContent :: Key -> Annex a -> Annex a
|
||||
#ifndef mingw32_HOST_OS
|
||||
lockContent key a = do
|
||||
file <- calcRepo $ gitAnnexLocation key
|
||||
bracketIO (openforlock file >>= lock) unlock (const a)
|
||||
contentfile <- calcRepo $ gitAnnexLocation key
|
||||
lockfile <- contentLockFile key
|
||||
maybe noop setuplockfile lockfile
|
||||
bracketAnnex (liftIO $ lock contentfile lockfile) (unlock lockfile) (const a)
|
||||
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. -}
|
||||
openforlock f = catchMaybeIO $ ifM (doesFileExist f)
|
||||
opencontentforlock f = catchMaybeIO $ ifM (doesFileExist f)
|
||||
( withModifiedFileMode f
|
||||
(`unionFileModes` ownerWriteMode)
|
||||
open
|
||||
, open
|
||||
(openforlock f)
|
||||
, openforlock f
|
||||
)
|
||||
where
|
||||
open = openFd f ReadWrite Nothing defaultFileFlags
|
||||
lock Nothing = return Nothing
|
||||
lock (Just fd) = do
|
||||
openforlock f = openFd f ReadWrite Nothing defaultFileFlags
|
||||
dolock Nothing = return Nothing
|
||||
dolock (Just fd) = do
|
||||
v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||
case v of
|
||||
Left _ -> error "content is locked"
|
||||
Left _ -> alreadylocked
|
||||
Right _ -> return $ Just fd
|
||||
unlock Nothing = noop
|
||||
unlock (Just l) = closeFd l
|
||||
unlock mlockfile mfd = do
|
||||
maybe noop cleanuplockfile mlockfile
|
||||
liftIO $ maybe noop closeFd mfd
|
||||
#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
|
||||
|
||||
{- Runs an action, passing it a temporary filename to get,
|
||||
|
@ -377,6 +425,7 @@ removeAnnex :: Key -> Annex ()
|
|||
removeAnnex key = withObjectLoc key remove removedirect
|
||||
where
|
||||
remove file = cleanObjectLoc key $ do
|
||||
secureErase file
|
||||
liftIO $ nukeFile file
|
||||
removeInodeCache key
|
||||
removedirect fs = do
|
||||
|
@ -385,11 +434,18 @@ removeAnnex key = withObjectLoc key remove removedirect
|
|||
mapM_ (resetfile cache) fs
|
||||
resetfile cache f = whenM (sameInodeCache f cache) $ do
|
||||
l <- inRepo $ gitAnnexLink f key
|
||||
top <- fromRepo Git.repoPath
|
||||
cwd <- liftIO getCurrentDirectory
|
||||
let top' = fromMaybe top $ absNormPath cwd top
|
||||
let l' = relPathDirToFile top' (fromMaybe l $ absNormPath top' l)
|
||||
replaceFile f $ makeAnnexLink l'
|
||||
secureErase f
|
||||
replaceFile f $ makeAnnexLink l
|
||||
|
||||
{- Runs the secure erase command if set, otherwise does nothing.
|
||||
- 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/ -}
|
||||
fromAnnex :: Key -> FilePath -> Annex ()
|
||||
|
|
|
@ -52,10 +52,12 @@ associatedFiles key = do
|
|||
associatedFilesRelative :: Key -> Annex [FilePath]
|
||||
associatedFilesRelative key = do
|
||||
mapping <- calcRepo $ gitAnnexMapping key
|
||||
liftIO $ catchDefaultIO [] $ do
|
||||
h <- openFile mapping ReadMode
|
||||
liftIO $ catchDefaultIO [] $ withFile mapping ReadMode $ \h -> do
|
||||
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
|
||||
- transformation to the list. Returns new associatedFiles value. -}
|
||||
|
@ -66,15 +68,10 @@ changeAssociatedFiles key transform = do
|
|||
let files' = transform files
|
||||
when (files /= files') $ do
|
||||
modifyContent mapping $
|
||||
liftIO $ viaTmp write mapping $ unlines files'
|
||||
liftIO $ viaTmp writeFileAnyEncoding mapping $
|
||||
unlines files'
|
||||
top <- fromRepo Git.repoPath
|
||||
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. -}
|
||||
removeAssociatedFiles :: Key -> Annex ()
|
||||
|
|
124
Annex/Drop.hs
Normal file
124
Annex/Drop.hs
Normal 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
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
module Annex.Exception (
|
||||
bracketIO,
|
||||
bracketAnnex,
|
||||
tryAnnex,
|
||||
tryAnnexIO,
|
||||
throwAnnex,
|
||||
|
@ -29,6 +30,9 @@ import Common.Annex
|
|||
bracketIO :: IO v -> (v -> IO b) -> (v -> Annex a) -> Annex a
|
||||
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 -}
|
||||
tryAnnex :: Annex a -> Annex (Either SomeException a)
|
||||
tryAnnex = M.try
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{- 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.
|
||||
-}
|
||||
|
@ -28,18 +28,25 @@ import qualified Data.Set as S
|
|||
type FileMatcher = Matcher MatchFiles
|
||||
|
||||
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
|
||||
checkFileMatcher' matcher file notpresent def
|
||||
checkMatcher :: FileMatcher -> Maybe Key -> AssociatedFile -> AssumeNotPresent -> Bool -> Annex Bool
|
||||
checkMatcher matcher mkey afile notpresent 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)
|
||||
let fi = FileInfo
|
||||
return $ MatchingFile $ FileInfo
|
||||
{ matchFile = matchfile
|
||||
, relFile = file
|
||||
}
|
||||
matchMrun matcher $ \a -> a notpresent fi
|
||||
|
||||
matchAll :: FileMatcher
|
||||
matchAll = generate []
|
||||
|
@ -65,11 +72,14 @@ parseToken checkpresent checkpreferreddir groupmap t
|
|||
| t `elem` tokens = Right $ token t
|
||||
| t == "present" = use checkpresent
|
||||
| t == "inpreferreddir" = use checkpreferreddir
|
||||
| t == "unused" = Right (Operation limitUnused)
|
||||
| otherwise = maybe (Left $ "near " ++ show t) use $ M.lookup k $
|
||||
M.fromList
|
||||
[ ("include", limitInclude)
|
||||
, ("exclude", limitExclude)
|
||||
, ("copies", limitCopies)
|
||||
, ("lackingcopies", limitLackingCopies False)
|
||||
, ("approxlackingcopies", limitLackingCopies True)
|
||||
, ("inbackend", limitInBackend)
|
||||
, ("largerthan", limitSize (>))
|
||||
, ("smallerthan", limitSize (<))
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
{-# LANGUAGE CPP #-}
|
||||
|
||||
module Init (
|
||||
module Annex.Init (
|
||||
ensureInitialized,
|
||||
isInitialized,
|
||||
initialize,
|
|
@ -20,6 +20,10 @@ import Annex.Exception
|
|||
import qualified Git
|
||||
import Annex.Perms
|
||||
|
||||
#ifdef mingw32_HOST_OS
|
||||
import Utility.WinLock
|
||||
#endif
|
||||
|
||||
{- Records content for a file in the branch to the journal.
|
||||
-
|
||||
- Using the journal, rather than immediatly staging content to the index
|
||||
|
@ -116,13 +120,8 @@ lockJournal a = do
|
|||
l <- noUmask mode $ createFile lockfile mode
|
||||
waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0)
|
||||
return l
|
||||
#else
|
||||
lock lockfile _mode = do
|
||||
writeFile lockfile ""
|
||||
return lockfile
|
||||
#endif
|
||||
#ifndef mingw32_HOST_OS
|
||||
unlock = closeFd
|
||||
#else
|
||||
unlock = removeFile
|
||||
lock lockfile _mode = waitToLock $ lockExclusive lockfile
|
||||
unlock = dropLock
|
||||
#endif
|
||||
|
|
|
@ -51,19 +51,15 @@ getAnnexLinkTarget file = ifM (coreSymlinks <$> Annex.getGitConfig)
|
|||
| otherwise -> return Nothing
|
||||
Nothing -> fallback
|
||||
|
||||
probefilecontent f = do
|
||||
h <- openFile f ReadMode
|
||||
probefilecontent f = withFile f ReadMode $ \h -> do
|
||||
fileEncoding h
|
||||
-- The first 8k is more than enough to read; link
|
||||
-- files are small.
|
||||
s <- take 8192 <$> hGetContents h
|
||||
-- If we got the full 8k, the file is too large
|
||||
if length s == 8192
|
||||
then do
|
||||
hClose h
|
||||
return ""
|
||||
else do
|
||||
hClose h
|
||||
then return ""
|
||||
else
|
||||
-- If there are any NUL or newline
|
||||
-- characters, or whitespace, we
|
||||
-- certianly don't have a link to a
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{- 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.
|
||||
-}
|
||||
|
@ -9,13 +9,16 @@
|
|||
|
||||
module Annex.LockPool where
|
||||
|
||||
import qualified Data.Map as M
|
||||
import System.Posix.Types (Fd)
|
||||
|
||||
import Common.Annex
|
||||
import Annex
|
||||
import Types.LockPool
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
#ifndef mingw32_HOST_OS
|
||||
import Annex.Perms
|
||||
#else
|
||||
import Utility.WinLock
|
||||
#endif
|
||||
|
||||
{- Create a specified lock file, and takes a shared lock. -}
|
||||
|
@ -26,31 +29,32 @@ lockFile file = go =<< fromPool file
|
|||
go Nothing = do
|
||||
#ifndef mingw32_HOST_OS
|
||||
mode <- annexFileMode
|
||||
fd <- liftIO $ noUmask mode $
|
||||
lockhandle <- liftIO $ noUmask mode $
|
||||
openFd file ReadOnly (Just mode) defaultFileFlags
|
||||
liftIO $ waitToSetLock fd (ReadLock, AbsoluteSeek, 0, 0)
|
||||
liftIO $ waitToSetLock lockhandle (ReadLock, AbsoluteSeek, 0, 0)
|
||||
#else
|
||||
liftIO $ writeFile file ""
|
||||
let fd = 0
|
||||
lockhandle <- liftIO $ waitToLock $ lockShared file
|
||||
#endif
|
||||
changePool $ M.insert file fd
|
||||
changePool $ M.insert file lockhandle
|
||||
|
||||
unlockFile :: FilePath -> Annex ()
|
||||
unlockFile file = maybe noop go =<< fromPool file
|
||||
where
|
||||
go fd = do
|
||||
go lockhandle = do
|
||||
#ifndef mingw32_HOST_OS
|
||||
liftIO $ closeFd fd
|
||||
liftIO $ closeFd lockhandle
|
||||
#else
|
||||
liftIO $ dropLock lockhandle
|
||||
#endif
|
||||
changePool $ M.delete file
|
||||
|
||||
getPool :: Annex (M.Map FilePath Fd)
|
||||
getPool :: Annex LockPool
|
||||
getPool = getState lockpool
|
||||
|
||||
fromPool :: FilePath -> Annex (Maybe Fd)
|
||||
fromPool :: FilePath -> Annex (Maybe LockHandle)
|
||||
fromPool file = M.lookup file <$> getPool
|
||||
|
||||
changePool :: (M.Map FilePath Fd -> M.Map FilePath Fd) -> Annex ()
|
||||
changePool :: (LockPool -> LockPool) -> Annex ()
|
||||
changePool a = do
|
||||
m <- getPool
|
||||
changeState $ \s -> s { lockpool = a m }
|
||||
|
|
|
@ -14,19 +14,16 @@ import Annex.UUID
|
|||
import qualified Data.Set as S
|
||||
|
||||
{- Check if a file is preferred content for the local repository. -}
|
||||
wantGet :: Bool -> AssociatedFile -> Annex Bool
|
||||
wantGet def Nothing = return def
|
||||
wantGet def (Just file) = isPreferredContent Nothing S.empty file def
|
||||
wantGet :: Bool -> Maybe Key -> AssociatedFile -> Annex Bool
|
||||
wantGet def key file = isPreferredContent Nothing S.empty key file def
|
||||
|
||||
{- Check if a file is preferred content for a remote. -}
|
||||
wantSend :: Bool -> AssociatedFile -> UUID -> Annex Bool
|
||||
wantSend def Nothing _ = return def
|
||||
wantSend def (Just file) to = isPreferredContent (Just to) S.empty file def
|
||||
wantSend :: Bool -> Maybe Key -> AssociatedFile -> UUID -> Annex Bool
|
||||
wantSend def key file to = isPreferredContent (Just to) S.empty key file def
|
||||
|
||||
{- Check if a file can be dropped, maybe from a remote.
|
||||
- Don't drop files that are preferred content. -}
|
||||
wantDrop :: Bool -> Maybe UUID -> AssociatedFile -> Annex Bool
|
||||
wantDrop def _ Nothing = return $ not def
|
||||
wantDrop def from (Just file) = do
|
||||
wantDrop :: Bool -> Maybe UUID -> Maybe Key -> AssociatedFile -> Annex Bool
|
||||
wantDrop def from key file = do
|
||||
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
|
||||
|
|
|
@ -145,7 +145,7 @@ startDaemon assistant foreground startdelay cannotrun listenhost startbrowser =
|
|||
, assist $ transferPollerThread
|
||||
, assist $ transfererThread
|
||||
, assist $ daemonStatusThread
|
||||
, assist $ sanityCheckerDailyThread
|
||||
, assist $ sanityCheckerDailyThread urlrenderer
|
||||
, assist $ sanityCheckerHourlyThread
|
||||
, assist $ problemFixerThread urlrenderer
|
||||
#ifdef WITH_CLIBS
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{- 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.
|
||||
-}
|
||||
|
@ -253,13 +253,32 @@ upgradingAlert = activityAlert Nothing [ fromString "Upgrading git-annex" ]
|
|||
|
||||
upgradeFinishedAlert :: Maybe AlertButton -> GitAnnexVersion -> Alert
|
||||
upgradeFinishedAlert button version =
|
||||
baseUpgradeAlert (maybe [] (:[]) button) $ fromString $
|
||||
baseUpgradeAlert (maybeToList button) $ fromString $
|
||||
"Finished upgrading git-annex to version " ++ version
|
||||
|
||||
upgradeFailedAlert :: String -> Alert
|
||||
upgradeFailedAlert msg = (errorAlert msg [])
|
||||
{ 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 = errorAlert "Serious problems have been detected with your repository. This needs your immediate attention!"
|
||||
|
||||
|
@ -298,7 +317,7 @@ pairRequestAcknowledgedAlert who button = baseActivityAlert
|
|||
, alertPriority = High
|
||||
, alertName = Just $ PairAlert who
|
||||
, alertCombiner = Just $ dataCombiner $ \_old new -> new
|
||||
, alertButtons = maybe [] (:[]) button
|
||||
, alertButtons = maybeToList button
|
||||
}
|
||||
|
||||
xmppNeededAlert :: AlertButton -> Alert
|
||||
|
|
|
@ -55,11 +55,11 @@ calcSyncRemotes = do
|
|||
let good r = Remote.uuid r `elem` alive
|
||||
let syncable = filter good rs
|
||||
let syncdata = filter (not . remoteAnnexIgnore . Remote.gitconfig) $
|
||||
filter (not . isXMPPRemote) syncable
|
||||
filter (not . Remote.isXMPPRemote) syncable
|
||||
|
||||
return $ \dstatus -> dstatus
|
||||
{ syncRemotes = syncable
|
||||
, syncGitRemotes = filter Remote.syncableRemote syncable
|
||||
, syncGitRemotes = filter Remote.gitSyncableRemote syncable
|
||||
, syncDataRemotes = syncdata
|
||||
, syncingToCloudRemote = any iscloud syncdata
|
||||
}
|
||||
|
@ -257,11 +257,5 @@ alertDuring alert a = do
|
|||
i <- addAlert $ alert { alertClass = Activity }
|
||||
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 r = T.pack $ drop (length "xmpp::") (Git.repoLocation (Remote.repo r))
|
||||
|
|
|
@ -5,108 +5,21 @@
|
|||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
module Assistant.Drop where
|
||||
module Assistant.Drop (
|
||||
handleDrops,
|
||||
handleDropsFrom,
|
||||
) where
|
||||
|
||||
import Assistant.Common
|
||||
import Assistant.DaemonStatus
|
||||
import Annex.Drop (handleDropsFrom, Reason)
|
||||
import Logs.Location
|
||||
import Logs.Trust
|
||||
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
|
||||
import CmdLine.Action
|
||||
|
||||
{- Drop from local and/or remote when allowed by the preferred content and
|
||||
- numcopies settings. -}
|
||||
handleDrops :: Reason -> Bool -> Key -> AssociatedFile -> Maybe Remote -> Assistant ()
|
||||
handleDrops _ _ _ Nothing _ = noop
|
||||
handleDrops reason fromhere key f knownpresentremote = do
|
||||
syncrs <- syncDataRemotes <$> getDaemonStatus
|
||||
locs <- liftAnnex $ loggedLocations key
|
||||
handleDropsFrom locs syncrs reason fromhere key f knownpresentremote
|
||||
|
||||
{- 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
|
||||
liftAnnex $ handleDropsFrom locs syncrs reason fromhere key f knownpresentremote callCommandAction
|
||||
|
|
|
@ -71,7 +71,7 @@ reconnectRemotes notifypushes rs = void $ do
|
|||
mapM_ signal $ filter (`notElem` failedrs) rs'
|
||||
where
|
||||
gitremotes = filter (notspecialremote . Remote.repo) rs
|
||||
(xmppremotes, nonxmppremotes) = partition isXMPPRemote rs
|
||||
(xmppremotes, nonxmppremotes) = partition Remote.isXMPPRemote rs
|
||||
notspecialremote r
|
||||
| Git.repoIsUrl r = True
|
||||
| Git.repoIsLocal r = True
|
||||
|
@ -133,7 +133,7 @@ pushToRemotes' now notifypushes remotes = do
|
|||
<$> gitRepo
|
||||
<*> inRepo Git.Branch.current
|
||||
<*> getUUID
|
||||
let (xmppremotes, normalremotes) = partition isXMPPRemote remotes
|
||||
let (xmppremotes, normalremotes) = partition Remote.isXMPPRemote remotes
|
||||
ret <- go True branch g u normalremotes
|
||||
unless (null xmppremotes) $ do
|
||||
shas <- liftAnnex $ map fst <$>
|
||||
|
@ -206,7 +206,7 @@ syncAction rs a
|
|||
return failed
|
||||
where
|
||||
visibleremotes = filter (not . Remote.readonly) $
|
||||
filter (not . isXMPPRemote) rs
|
||||
filter (not . Remote.isXMPPRemote) rs
|
||||
|
||||
{- Manually pull from remotes and merge their branches. Returns any
|
||||
- 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 currentbranch remotes = do
|
||||
g <- liftAnnex gitRepo
|
||||
let (xmppremotes, normalremotes) = partition isXMPPRemote remotes
|
||||
let (xmppremotes, normalremotes) = partition Remote.isXMPPRemote remotes
|
||||
failed <- liftIO $ forM normalremotes $ \r ->
|
||||
ifM (Git.Command.runBool [Param "fetch", Param $ Remote.name r] g)
|
||||
( return Nothing
|
||||
|
|
|
@ -464,7 +464,7 @@ checkChangeContent change@(Change { changeInfo = i }) =
|
|||
Nothing -> noop
|
||||
Just k -> whenM (scanComplete <$> getDaemonStatus) $ do
|
||||
present <- liftAnnex $ inAnnex k
|
||||
if present
|
||||
void $ if present
|
||||
then queueTransfers "new file created" Next k (Just f) Upload
|
||||
else queueTransfers "new or renamed file wanted" Next k (Just f) Download
|
||||
handleDrops "file renamed" present k (Just f) Nothing
|
||||
|
|
|
@ -17,6 +17,7 @@ import Logs.UUID
|
|||
import Logs.Trust
|
||||
import Logs.PreferredContent
|
||||
import Logs.Group
|
||||
import Logs.NumCopies
|
||||
import Remote.List (remoteListRefresh)
|
||||
import qualified Git.LsTree as LsTree
|
||||
import Git.FilePath
|
||||
|
@ -59,6 +60,7 @@ configFilesActions =
|
|||
, (remoteLog, void $ liftAnnex remoteListRefresh)
|
||||
, (trustLog, void $ liftAnnex trustMapLoad)
|
||||
, (groupLog, void $ liftAnnex groupMapLoad)
|
||||
, (numcopiesLog, void $ liftAnnex globalNumCopiesLoad)
|
||||
, (scheduleLog, void updateScheduleLog)
|
||||
-- Preferred content settings depend on most of the other configs,
|
||||
-- so will be reloaded whenever any configs change.
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
{-# LANGUAGE CPP #-}
|
||||
|
||||
module Assistant.Threads.SanityChecker (
|
||||
sanityCheckerStartupThread,
|
||||
sanityCheckerDailyThread,
|
||||
|
@ -15,7 +17,10 @@ import Assistant.Common
|
|||
import Assistant.DaemonStatus
|
||||
import Assistant.Alert
|
||||
import Assistant.Repair
|
||||
import Assistant.Drop
|
||||
import Assistant.Ssh
|
||||
import Assistant.TransferQueue
|
||||
import Assistant.Types.UrlRenderer
|
||||
import qualified Annex.Branch
|
||||
import qualified Git.LsFiles
|
||||
import qualified Git.Command
|
||||
|
@ -27,10 +32,20 @@ import Utility.Batch
|
|||
import Utility.NotificationBroadcaster
|
||||
import Config
|
||||
import Utility.HumanTime
|
||||
import Utility.Tense
|
||||
import Git.Repair
|
||||
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 qualified Data.Text as T
|
||||
|
||||
{- 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
|
||||
|
@ -78,8 +93,8 @@ sanityCheckerHourlyThread = namedThread "SanityCheckerHourly" $ forever $ do
|
|||
hourlyCheck
|
||||
|
||||
{- This thread wakes up daily to make sure the tree is in good shape. -}
|
||||
sanityCheckerDailyThread :: NamedThread
|
||||
sanityCheckerDailyThread = namedThread "SanityCheckerDaily" $ forever $ do
|
||||
sanityCheckerDailyThread :: UrlRenderer -> NamedThread
|
||||
sanityCheckerDailyThread urlrenderer = namedThread "SanityCheckerDaily" $ forever $ do
|
||||
waitForNextCheck
|
||||
|
||||
debug ["starting sanity check"]
|
||||
|
@ -90,7 +105,8 @@ sanityCheckerDailyThread = namedThread "SanityCheckerDaily" $ forever $ do
|
|||
modifyDaemonStatus_ $ \s -> s { sanityCheckRunning = True }
|
||||
|
||||
now <- liftIO getPOSIXTime -- before check started
|
||||
r <- either showerr return =<< (tryIO . batch) <~> dailyCheck
|
||||
r <- either showerr return
|
||||
=<< (tryIO . batch) <~> dailyCheck urlrenderer
|
||||
|
||||
modifyDaemonStatus_ $ \s -> s
|
||||
{ sanityCheckRunning = False
|
||||
|
@ -119,9 +135,10 @@ waitForNextCheck = do
|
|||
{- 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
|
||||
- will block the watcher. -}
|
||||
dailyCheck :: Assistant Bool
|
||||
dailyCheck = do
|
||||
dailyCheck :: UrlRenderer -> Assistant Bool
|
||||
dailyCheck urlrenderer = do
|
||||
g <- liftAnnex gitRepo
|
||||
batchmaker <- liftIO getBatchCommandMaker
|
||||
|
||||
-- Find old unstaged symlinks, and add them to git.
|
||||
(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
|
||||
- significant size. -}
|
||||
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 "gc"
|
||||
, Param "--auto"
|
||||
] 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
|
||||
where
|
||||
toonew timestamp now = now < (realToFrac (timestamp + slop) :: POSIXTime)
|
||||
|
@ -159,7 +193,8 @@ dailyCheck = do
|
|||
insanity $ "found unstaged symlink: " ++ file
|
||||
|
||||
hourlyCheck :: Assistant ()
|
||||
hourlyCheck = checkLogSize 0
|
||||
hourlyCheck = do
|
||||
checkLogSize 0
|
||||
|
||||
{- Rotate logs until log file size is < 1 mb. -}
|
||||
checkLogSize :: Int -> Assistant ()
|
||||
|
@ -184,3 +219,23 @@ oneHour = 60 * 60
|
|||
oneDay :: Int
|
||||
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
|
||||
|
|
|
@ -29,6 +29,7 @@ import qualified Git.LsFiles as LsFiles
|
|||
import qualified Backend
|
||||
import Annex.Content
|
||||
import Annex.Wanted
|
||||
import CmdLine.Action
|
||||
|
||||
import qualified Data.Set as S
|
||||
|
||||
|
@ -156,16 +157,16 @@ expensiveScan urlrenderer rs = unless onlyweb $ batch <~> do
|
|||
syncrs <- syncDataRemotes <$> getDaemonStatus
|
||||
locs <- liftAnnex $ loggedLocations key
|
||||
present <- liftAnnex $ inAnnex key
|
||||
handleDropsFrom locs syncrs
|
||||
liftAnnex $ handleDropsFrom locs syncrs
|
||||
"expensive scan found too many copies of object"
|
||||
present key (Just f) Nothing
|
||||
present key (Just f) Nothing callCommandAction
|
||||
liftAnnex $ do
|
||||
let slocs = S.fromList locs
|
||||
let use a = return $ mapMaybe (a key slocs) syncrs
|
||||
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)
|
||||
else ifM (wantGet True $ Just f)
|
||||
else ifM (wantGet True (Just key) (Just f))
|
||||
( use (genTransfer Download True) , return [] )
|
||||
let unwanted' = S.difference unwanted slocs
|
||||
return (unwanted', ts)
|
||||
|
|
|
@ -27,6 +27,7 @@ import Assistant.WebApp.Configurators.IA
|
|||
import Assistant.WebApp.Configurators.WebDAV
|
||||
import Assistant.WebApp.Configurators.XMPP
|
||||
import Assistant.WebApp.Configurators.Preferences
|
||||
import Assistant.WebApp.Configurators.Unused
|
||||
import Assistant.WebApp.Configurators.Edit
|
||||
import Assistant.WebApp.Configurators.Delete
|
||||
import Assistant.WebApp.Configurators.Fsck
|
||||
|
|
|
@ -322,7 +322,7 @@ pairMsgReceived urlrenderer PairReq theiruuid selfjid theirjid
|
|||
| baseJID selfjid == baseJID theirjid = autoaccept
|
||||
| otherwise = do
|
||||
knownjids <- mapMaybe (parseJID . getXMPPClientID)
|
||||
. filter isXMPPRemote . syncRemotes <$> getDaemonStatus
|
||||
. filter Remote.isXMPPRemote . syncRemotes <$> getDaemonStatus
|
||||
um <- liftAnnex uuidMap
|
||||
if elem (baseJID theirjid) knownjids && M.member theiruuid um
|
||||
then autoaccept
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{- 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.
|
||||
-}
|
||||
|
@ -51,14 +51,17 @@ stubInfo f r = stubTransferInfo
|
|||
|
||||
{- Adds transfers to queue for some of the known remotes.
|
||||
- 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)
|
||||
|
||||
{- Adds transfers to queue for some of the known remotes, that match a
|
||||
- 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
|
||||
| direction == Download = whenM (liftAnnex $ wantGet True f) go
|
||||
| direction == Download = ifM (liftAnnex $ wantGet True (Just k) f)
|
||||
( go
|
||||
, return False
|
||||
)
|
||||
| otherwise = go
|
||||
where
|
||||
go = do
|
||||
|
@ -67,9 +70,13 @@ queueTransfersMatching matching reason schedule k f direction
|
|||
=<< syncDataRemotes <$> getDaemonStatus
|
||||
let matchingrs = filter (matching . Remote.uuid) rs
|
||||
if null matchingrs
|
||||
then defer
|
||||
else forM_ matchingrs $ \r ->
|
||||
then do
|
||||
defer
|
||||
return False
|
||||
else do
|
||||
forM_ matchingrs $ \r ->
|
||||
enqueue reason schedule (gentransfer r) (stubInfo f r)
|
||||
return True
|
||||
selectremotes rs
|
||||
{- Queue downloads from all remotes that
|
||||
- have the key. The list of remotes is ordered with
|
||||
|
@ -82,7 +89,7 @@ queueTransfersMatching matching reason schedule k f direction
|
|||
- already have it. -}
|
||||
| otherwise = do
|
||||
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
|
||||
where
|
||||
locs = S.fromList <$> Remote.keyLocations k
|
||||
|
|
|
@ -103,8 +103,8 @@ runTransferThread' program batchmaker d run = go
|
|||
{- By the time this is called, the daemonstatus's currentTransfers map should
|
||||
- already have been updated to include the transfer. -}
|
||||
genTransfer :: Transfer -> TransferInfo -> TransferGenerator
|
||||
genTransfer t info = case (transferRemote info, associatedFile info) of
|
||||
(Just remote, Just file)
|
||||
genTransfer t info = case transferRemote info of
|
||||
Just remote
|
||||
| Git.repoIsLocalUnknown (Remote.repo remote) -> do
|
||||
-- optimisation for removable drives not plugged in
|
||||
liftAnnex $ recordFailedTransfer t info
|
||||
|
@ -114,7 +114,7 @@ genTransfer t info = case (transferRemote info, associatedFile info) of
|
|||
( do
|
||||
debug [ "Transferring:" , describeTransfer t info ]
|
||||
notifyTransfer
|
||||
return $ Just (t, info, go remote file)
|
||||
return $ Just (t, info, go remote)
|
||||
, do
|
||||
debug [ "Skipping unnecessary transfer:",
|
||||
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
|
||||
- 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
|
||||
void $ addAlert $ makeAlertFiller True $
|
||||
transferFileAlert direction True file
|
||||
maybe noop
|
||||
(void . addAlert . makeAlertFiller True
|
||||
. transferFileAlert direction True)
|
||||
(associatedFile info)
|
||||
unless isdownload $
|
||||
handleDrops
|
||||
("object uploaded to " ++ show remote)
|
||||
|
@ -188,11 +190,11 @@ genTransfer t info = case (transferRemote info, associatedFile info) of
|
|||
shouldTransfer :: Transfer -> TransferInfo -> Annex Bool
|
||||
shouldTransfer t info
|
||||
| transferDirection t == Download =
|
||||
(not <$> inAnnex key) <&&> wantGet True file
|
||||
(not <$> inAnnex key) <&&> wantGet True (Just key) file
|
||||
| transferDirection t == Upload = case transferRemote info of
|
||||
Nothing -> return False
|
||||
Just r -> notinremote r
|
||||
<&&> wantSend True file (Remote.uuid r)
|
||||
<&&> wantSend True (Just key) file (Remote.uuid r)
|
||||
| otherwise = return False
|
||||
where
|
||||
key = transferKey t
|
||||
|
@ -216,7 +218,7 @@ finishedTransfer t (Just info)
|
|||
| transferDirection t == Download =
|
||||
whenM (liftAnnex $ inAnnex $ transferKey t) $ do
|
||||
dodrops False
|
||||
queueTransfersMatching (/= transferUUID t)
|
||||
void $ queueTransfersMatching (/= transferUUID t)
|
||||
"newly received object"
|
||||
Later (transferKey t) (associatedFile info) Upload
|
||||
| otherwise = dodrops True
|
||||
|
|
|
@ -32,6 +32,7 @@ data AlertName
|
|||
| SyncAlert
|
||||
| NotFsckedAlert
|
||||
| UpgradeAlert
|
||||
| UnusedFilesAlert
|
||||
deriving (Eq)
|
||||
|
||||
{- The first alert is the new alert, the second is an old alert.
|
||||
|
|
86
Assistant/Unused.hs
Normal file
86
Assistant/Unused.hs
Normal 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
|
|
@ -276,7 +276,6 @@ deleteFromManifest dir = do
|
|||
|
||||
removeEmptyRecursive :: FilePath -> IO ()
|
||||
removeEmptyRecursive dir = do
|
||||
print ("remove", dir)
|
||||
mapM_ removeEmptyRecursive =<< dirContents dir
|
||||
void $ tryIO $ removeDirectory dir
|
||||
|
||||
|
|
|
@ -96,12 +96,11 @@ deleteCurrentRepository = dangerPage $ do
|
|||
rs <- syncRemotes <$> getDaemonStatus
|
||||
mapM_ (\r -> changeSyncable (Just r) False) rs
|
||||
|
||||
{- Make all directories writable, so all annexed
|
||||
- content can be deleted. -}
|
||||
{- Make all directories writable and files writable
|
||||
- so all annexed content can be deleted. -}
|
||||
liftIO $ do
|
||||
recurseDir SystemFS dir >>=
|
||||
filterM doesDirectoryExist >>=
|
||||
mapM_ allowWrite
|
||||
recurseDir SystemFS dir
|
||||
>>= mapM_ (void . tryIO . allowWrite)
|
||||
removeDirectoryRecursive dir
|
||||
|
||||
redirect ShutdownConfirmedR
|
||||
|
|
|
@ -264,6 +264,7 @@ getUpgradeRepositoryR r = go =<< liftAnnex (repoIdRemote r)
|
|||
liftAnnex $ setConfig
|
||||
(remoteConfig (Remote.repo rmt) "ignore")
|
||||
(Git.Config.boolConfig False)
|
||||
liftAssistant $ syncRemote rmt
|
||||
liftAnnex $ void Remote.remoteListRefresh
|
||||
liftAssistant updateSyncRemotes
|
||||
liftAssistant $ syncRemote rmt
|
||||
redirect DashboardR
|
||||
|
|
|
@ -14,7 +14,7 @@ import Assistant.WebApp.Gpg
|
|||
import Assistant.WebApp.MakeRemote
|
||||
import Assistant.Sync
|
||||
import Assistant.Restart
|
||||
import Init
|
||||
import Annex.Init
|
||||
import qualified Git
|
||||
import qualified Git.Construct
|
||||
import qualified Git.Config
|
||||
|
|
|
@ -17,6 +17,7 @@ import qualified Annex
|
|||
import qualified Git
|
||||
import Config
|
||||
import Config.Files
|
||||
import Config.NumCopies
|
||||
import Utility.DataUnits
|
||||
import Git.Config
|
||||
import Types.Distribution
|
||||
|
@ -81,7 +82,7 @@ prefsAForm def = PrefsForm
|
|||
getPrefs :: Annex PrefsForm
|
||||
getPrefs = PrefsForm
|
||||
<$> (T.pack . roughSize storageUnits False . annexDiskReserve <$> Annex.getGitConfig)
|
||||
<*> (annexNumCopies <$> Annex.getGitConfig)
|
||||
<*> (fromNumCopies <$> getNumCopies)
|
||||
<*> inAutoStartFile
|
||||
<*> (annexAutoUpgrade <$> Annex.getGitConfig)
|
||||
<*> (annexDebug <$> Annex.getGitConfig)
|
||||
|
@ -89,7 +90,8 @@ getPrefs = PrefsForm
|
|||
storePrefs :: PrefsForm -> Annex ()
|
||||
storePrefs p = do
|
||||
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)
|
||||
unlessM ((==) <$> pure (autoStart p) <*> inAutoStartFile) $ do
|
||||
here <- fromRepo Git.repoPath
|
||||
|
|
80
Assistant/WebApp/Configurators/Unused.hs
Normal file
80
Assistant/WebApp/Configurators/Unused.hs
Normal 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
|
|
@ -161,7 +161,7 @@ buddyListDisplay = do
|
|||
#ifdef WITH_XMPP
|
||||
|
||||
getXMPPRemotes :: Assistant [(JID, Remote)]
|
||||
getXMPPRemotes = catMaybes . map pair . filter isXMPPRemote . syncGitRemotes
|
||||
getXMPPRemotes = catMaybes . map pair . filter Remote.isXMPPRemote . syncGitRemotes
|
||||
<$> getDaemonStatus
|
||||
where
|
||||
pair r = maybe Nothing (\jid -> Just (jid, r)) $
|
||||
|
|
|
@ -164,7 +164,7 @@ repoList reposelector
|
|||
| Remote.readonly r = False
|
||||
| onlyCloud reposelector = Git.repoIsUrl (Remote.repo r)
|
||||
&& Remote.uuid r /= NoUUID
|
||||
&& not (isXMPPRemote r)
|
||||
&& not (Remote.isXMPPRemote r)
|
||||
| otherwise = True
|
||||
selectedremote Nothing = False
|
||||
selectedremote (Just (iscloud, _))
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
/config/upgrade/start/#GitAnnexDistribution ConfigStartUpgradeR GET
|
||||
/config/upgrade/finish ConfigFinishUpgradeR GET
|
||||
/config/upgrade/automatically ConfigEnableAutomaticUpgradeR GET
|
||||
/config/unused ConfigUnusedR GET POST
|
||||
|
||||
/config/addrepository AddRepositoryR GET
|
||||
/config/repository/new NewRepositoryR GET POST
|
||||
|
@ -118,4 +119,6 @@
|
|||
/repair/#UUID RepairRepositoryR GET POST
|
||||
/repair/run/#UUID RepairRepositoryRunR GET POST
|
||||
|
||||
/unused/cleanup CleanupUnusedR GET
|
||||
|
||||
/static StaticR Static getStatic
|
||||
|
|
|
@ -125,8 +125,8 @@ getOutput c ps environ = do
|
|||
putStrLn $ unwords [c, show ps]
|
||||
systemenviron <- getEnvironment
|
||||
let environ' = fromMaybe [] environ ++ systemenviron
|
||||
out@(s, ok) <- processTranscript' c ps (Just environ') Nothing
|
||||
putStrLn $ unwords [c, "finished", show ok, "output size:", show (length s)]
|
||||
out@(_, ok) <- processTranscript' c ps (Just environ') Nothing
|
||||
putStrLn $ unwords [c, "finished", show ok]
|
||||
return out
|
||||
|
||||
atFile :: FilePath -> String
|
||||
|
|
|
@ -141,4 +141,4 @@ parseLdd = catMaybes . map (getlib . dropWhile isSpace) . lines
|
|||
- XXX Debian specific. -}
|
||||
glibcLibs :: IO [FilePath]
|
||||
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'"]
|
||||
|
|
|
@ -12,7 +12,7 @@ module Checks where
|
|||
|
||||
import Common.Annex
|
||||
import Types.Command
|
||||
import Init
|
||||
import Annex.Init
|
||||
import Config
|
||||
import Utility.Daemon
|
||||
import qualified Git
|
||||
|
|
45
CmdLine.hs
45
CmdLine.hs
|
@ -23,7 +23,6 @@ import System.Posix.Signals
|
|||
|
||||
import Common.Annex
|
||||
import qualified Annex
|
||||
import qualified Annex.Queue
|
||||
import qualified Git
|
||||
import qualified Git.AutoCorrect
|
||||
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)
|
||||
Right g -> do
|
||||
state <- Annex.new g
|
||||
(actions, state') <- Annex.run state $ do
|
||||
Annex.eval state $ do
|
||||
checkEnvironment
|
||||
checkfuzzy
|
||||
forM_ fields $ uncurry Annex.setField
|
||||
|
@ -50,8 +49,9 @@ dispatch fuzzyok allargs allcmds commonoptions fields header getgitrepo = do
|
|||
sequence_ flags
|
||||
whenM (annexDebug <$> Annex.getGitConfig) $
|
||||
liftIO enableDebugOutput
|
||||
prepCommand cmd params
|
||||
tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdnocommit cmd]
|
||||
startup
|
||||
performCommandAction cmd params
|
||||
shutdown $ cmdnocommit cmd
|
||||
where
|
||||
err msg = msg ++ "\n\n" ++ usage header allcmds
|
||||
cmd = Prelude.head cmds
|
||||
|
@ -92,44 +92,19 @@ getOptCmd argv cmd commonoptions = check $
|
|||
, 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. -}
|
||||
startup :: Annex Bool
|
||||
startup = liftIO $ do
|
||||
startup :: Annex ()
|
||||
startup =
|
||||
#ifndef mingw32_HOST_OS
|
||||
void $ installHandler sigINT Default Nothing
|
||||
liftIO $ void $ installHandler sigINT Default Nothing
|
||||
#else
|
||||
return ()
|
||||
#endif
|
||||
return True
|
||||
|
||||
{- Cleanup actions. -}
|
||||
shutdown :: Bool -> Annex Bool
|
||||
shutdown :: Bool -> Annex ()
|
||||
shutdown nocommit = do
|
||||
saveState nocommit
|
||||
sequence_ =<< M.elems <$> Annex.getState Annex.cleanup
|
||||
liftIO reapZombies -- zombies from long-running git processes
|
||||
sshCleanup -- ssh connection caching
|
||||
return True
|
||||
|
|
70
CmdLine/Action.hs
Normal file
70
CmdLine/Action.hs
Normal 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
|
|
@ -7,12 +7,11 @@
|
|||
|
||||
{-# LANGUAGE CPP, OverloadedStrings #-}
|
||||
|
||||
module GitAnnex where
|
||||
module CmdLine.GitAnnex where
|
||||
|
||||
import qualified Git.CurrentRepo
|
||||
import CmdLine
|
||||
import Command
|
||||
import GitAnnex.Options
|
||||
|
||||
import qualified Command.Add
|
||||
import qualified Command.Unannex
|
||||
|
@ -50,6 +49,7 @@ import qualified Command.Info
|
|||
import qualified Command.Status
|
||||
import qualified Command.Migrate
|
||||
import qualified Command.Uninit
|
||||
import qualified Command.NumCopies
|
||||
import qualified Command.Trust
|
||||
import qualified Command.Untrust
|
||||
import qualified Command.Semitrust
|
||||
|
@ -117,6 +117,7 @@ cmds = concat
|
|||
, Command.Unannex.def
|
||||
, Command.Uninit.def
|
||||
, Command.PreCommit.def
|
||||
, Command.NumCopies.def
|
||||
, Command.Trust.def
|
||||
, Command.Untrust.def
|
||||
, Command.Semitrust.def
|
||||
|
@ -178,4 +179,4 @@ run args = do
|
|||
#ifdef WITH_EKG
|
||||
_ <- forkServer "localhost" 4242
|
||||
#endif
|
||||
dispatch True args cmds options [] header Git.CurrentRepo.get
|
||||
dispatch True args cmds gitAnnexOptions [] header Git.CurrentRepo.get
|
|
@ -5,23 +5,25 @@
|
|||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
module GitAnnex.Options where
|
||||
module CmdLine.GitAnnex.Options where
|
||||
|
||||
import System.Console.GetOpt
|
||||
|
||||
import Common.Annex
|
||||
import qualified Git.Config
|
||||
import Git.Types
|
||||
import Command
|
||||
import Types.TrustLevel
|
||||
import Types.NumCopies
|
||||
import Types.Messages
|
||||
import qualified Annex
|
||||
import qualified Remote
|
||||
import qualified Limit
|
||||
import qualified Limit.Wanted
|
||||
import qualified Option
|
||||
import CmdLine.Option
|
||||
import CmdLine.Usage
|
||||
|
||||
options :: [Option]
|
||||
options = Option.common ++
|
||||
gitAnnexOptions :: [Option]
|
||||
gitAnnexOptions = commonOptions ++
|
||||
[ Option ['N'] ["numcopies"] (ReqArg setnumcopies paramNumber)
|
||||
"override default number of copies"
|
||||
, Option [] ["trust"] (trustArg Trusted)
|
||||
|
@ -40,6 +42,10 @@ options = Option.common ++
|
|||
"match files present in a remote"
|
||||
, Option ['C'] ["copies"] (ReqArg Limit.addCopies paramNumber)
|
||||
"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)
|
||||
"match files using a key-value backend"
|
||||
, Option [] ["inallgroup"] (ReqArg Limit.addInAllGroup paramGroup)
|
||||
|
@ -58,11 +64,11 @@ options = Option.common ++
|
|||
"override default User-Agent"
|
||||
, Option [] ["trust-glacier"] (NoArg (Annex.setFlag "trustglacier"))
|
||||
"Trust Amazon Glacier inventory"
|
||||
] ++ Option.matcher
|
||||
] ++ matcherOptions
|
||||
where
|
||||
trustArg t = ReqArg (Remote.forceTrust t) paramRemote
|
||||
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)
|
||||
setuseragent v = Annex.changeState $ \s -> s { Annex.useragent = Just v }
|
||||
setgitconfig v = inRepo (Git.Config.store v)
|
||||
|
@ -75,13 +81,19 @@ keyOptions =
|
|||
"operate on all versions of all files"
|
||||
, Option ['U'] ["unused"] (NoArg (Annex.setFlag "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.field ['f'] "from" paramRemote "source remote"
|
||||
fromOption = fieldOption ['f'] "from" paramRemote "source remote"
|
||||
|
||||
toOption :: Option
|
||||
toOption = Option.field ['t'] "to" paramRemote "destination remote"
|
||||
toOption = fieldOption ['t'] "to" paramRemote "destination remote"
|
||||
|
||||
fromToOptions :: [Option]
|
||||
fromToOptions = [fromOption, toOption]
|
||||
|
||||
jsonOption :: Option
|
||||
jsonOption = Option ['j'] ["json"] (NoArg (Annex.setOutput JSONOutput))
|
||||
"enable JSON output"
|
|
@ -5,7 +5,7 @@
|
|||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
module GitAnnexShell where
|
||||
module CmdLine.GitAnnexShell where
|
||||
|
||||
import System.Environment
|
||||
import System.Console.GetOpt
|
||||
|
@ -16,12 +16,11 @@ import CmdLine
|
|||
import Command
|
||||
import Annex.UUID
|
||||
import Annex (setField)
|
||||
import qualified Option
|
||||
import Fields
|
||||
import CmdLine.GitAnnexShell.Fields
|
||||
import Utility.UserInfo
|
||||
import Remote.GCrypt (getGCryptUUID)
|
||||
import qualified Annex
|
||||
import Init
|
||||
import Annex.Init
|
||||
|
||||
import qualified Command.ConfigList
|
||||
import qualified Command.InAnnex
|
||||
|
@ -54,7 +53,7 @@ cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
|
|||
adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c }
|
||||
|
||||
options :: [OptDescr (Annex ())]
|
||||
options = Option.common ++
|
||||
options = commonOptions ++
|
||||
[ Option [] ["uuid"] (ReqArg checkUUID paramUUID) "local repository uuid"
|
||||
]
|
||||
where
|
||||
|
@ -104,7 +103,7 @@ builtin cmd dir params = do
|
|||
Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath
|
||||
where
|
||||
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 params = do
|
|
@ -1,14 +1,15 @@
|
|||
{- git-annex fields
|
||||
{- git-annex-shell fields
|
||||
-
|
||||
- Copyright 2012 Joey Hess <joey@kitenet.net>
|
||||
-
|
||||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
module Fields where
|
||||
module CmdLine.GitAnnexShell.Fields where
|
||||
|
||||
import Common.Annex
|
||||
import qualified Annex
|
||||
import Git.FilePath
|
||||
|
||||
import Data.Char
|
||||
|
||||
|
@ -29,7 +30,7 @@ remoteUUID = Field "remoteuuid" $
|
|||
associatedFile :: Field
|
||||
associatedFile = Field "associatedfile" $ \f ->
|
||||
-- is the file a safe relative filename?
|
||||
not (isAbsolute f) && not ("../" `isPrefixOf` f)
|
||||
not (absoluteGitPath f) && not ("../" `isPrefixOf` f)
|
||||
|
||||
direct :: Field
|
||||
direct = Field "direct" $ \f -> f == "1"
|
|
@ -5,12 +5,12 @@
|
|||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
module Option (
|
||||
common,
|
||||
matcher,
|
||||
flag,
|
||||
field,
|
||||
name,
|
||||
module CmdLine.Option (
|
||||
commonOptions,
|
||||
matcherOptions,
|
||||
flagOption,
|
||||
fieldOption,
|
||||
optionName,
|
||||
ArgDescr(..),
|
||||
OptDescr(..),
|
||||
) where
|
||||
|
@ -21,10 +21,10 @@ import Common.Annex
|
|||
import qualified Annex
|
||||
import Types.Messages
|
||||
import Limit
|
||||
import Usage
|
||||
import CmdLine.Usage
|
||||
|
||||
common :: [Option]
|
||||
common =
|
||||
commonOptions :: [Option]
|
||||
commonOptions =
|
||||
[ Option [] ["force"] (NoArg (setforce True))
|
||||
"allow actions that may lose annexed data"
|
||||
, Option ['F'] ["fast"] (NoArg (setfast True))
|
||||
|
@ -35,8 +35,6 @@ common =
|
|||
"avoid verbose output"
|
||||
, Option ['v'] ["verbose"] (NoArg (Annex.setOutput NormalOutput))
|
||||
"allow verbose output (default)"
|
||||
, Option ['j'] ["json"] (NoArg (Annex.setOutput JSONOutput))
|
||||
"enable JSON output"
|
||||
, Option ['d'] ["debug"] (NoArg setdebug)
|
||||
"show debug messages"
|
||||
, Option [] ["no-debug"] (NoArg unsetdebug)
|
||||
|
@ -52,8 +50,8 @@ common =
|
|||
setdebug = Annex.changeGitConfig $ \c -> c { annexDebug = True }
|
||||
unsetdebug = Annex.changeGitConfig $ \c -> c { annexDebug = False }
|
||||
|
||||
matcher :: [Option]
|
||||
matcher =
|
||||
matcherOptions :: [Option]
|
||||
matcherOptions =
|
||||
[ longopt "not" "negate next option"
|
||||
, longopt "and" "both previous and 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
|
||||
|
||||
{- An option that sets a flag. -}
|
||||
flag :: String -> String -> String -> Option
|
||||
flag short opt description =
|
||||
flagOption :: String -> String -> String -> Option
|
||||
flagOption short opt description =
|
||||
Option short [opt] (NoArg (Annex.setFlag opt)) description
|
||||
|
||||
{- An option that sets a field. -}
|
||||
field :: String -> String -> String -> String -> Option
|
||||
field short opt paramdesc description =
|
||||
fieldOption :: String -> String -> String -> String -> Option
|
||||
fieldOption short opt paramdesc description =
|
||||
Option short [opt] (ReqArg (Annex.setField opt) paramdesc) description
|
||||
|
||||
{- The flag or field name used for an option. -}
|
||||
name :: Option -> String
|
||||
name (Option _ o _ _) = Prelude.head o
|
||||
optionName :: Option -> String
|
||||
optionName (Option _ o _ _) = Prelude.head o
|
|
@ -4,14 +4,12 @@
|
|||
- the values a user passes to a command, and prepare actions operating
|
||||
- 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.
|
||||
-}
|
||||
|
||||
module Seek where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
module CmdLine.Seek where
|
||||
|
||||
import Common.Annex
|
||||
import Types.Command
|
||||
|
@ -22,24 +20,15 @@ import qualified Git
|
|||
import qualified Git.Command
|
||||
import qualified Git.LsFiles as LsFiles
|
||||
import qualified Limit
|
||||
import qualified Option
|
||||
import Config
|
||||
import CmdLine.Option
|
||||
import CmdLine.Action
|
||||
import Logs.Location
|
||||
import Logs.Unused
|
||||
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 a params = prepFiltered a $ seekHelper LsFiles.inRepo params
|
||||
withFilesInGit a params = seekActions $ prepFiltered a $
|
||||
seekHelper LsFiles.inRepo params
|
||||
|
||||
withFilesNotInGit :: (FilePath -> CommandStart) -> CommandSeek
|
||||
withFilesNotInGit a params = do
|
||||
|
@ -47,7 +36,8 @@ withFilesNotInGit a params = do
|
|||
files <- filter (not . dotfile) <$>
|
||||
seekunless (null ps && not (null params)) ps
|
||||
dotfiles <- seekunless (null dotps) dotps
|
||||
prepFiltered a $ return $ concat $ segmentPaths params (files++dotfiles)
|
||||
seekActions $ prepFiltered a $
|
||||
return $ concat $ segmentPaths params (files++dotfiles)
|
||||
where
|
||||
(dotps, ps) = partition dotfile params
|
||||
seekunless True _ = return []
|
||||
|
@ -57,7 +47,8 @@ withFilesNotInGit a params = do
|
|||
liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g
|
||||
|
||||
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
|
||||
get p = ifM (isDirectory <$> getFileStatus p)
|
||||
( 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 a params = return [a params]
|
||||
withWords a params = seekActions $ return [a params]
|
||||
|
||||
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 a params = return $ map a $ pairs [] params
|
||||
withPairs a params = seekActions $ return $ map a $ pairs [] params
|
||||
where
|
||||
pairs c [] = reverse c
|
||||
pairs c (x:y:xs) = pairs ((x,y):c) xs
|
||||
pairs _ _ = error "expected pairs"
|
||||
|
||||
withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek
|
||||
withFilesToBeCommitted a params = prepFiltered a $
|
||||
withFilesToBeCommitted a params = seekActions $ prepFiltered a $
|
||||
seekHelper LsFiles.stagedNotDeleted params
|
||||
|
||||
withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek
|
||||
|
@ -94,7 +85,8 @@ withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged
|
|||
- not some other sort of symlink.
|
||||
-}
|
||||
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
|
||||
check f = liftIO (notSymlink f) <&&>
|
||||
(isJust <$> catKeyFile f <||> isJust <$> catKeyFileHEAD f)
|
||||
|
@ -102,32 +94,25 @@ withFilesUnlocked' typechanged a params = prepFiltered a unlockedfiles
|
|||
|
||||
{- Finds files that may be modified. -}
|
||||
withFilesMaybeModified :: (FilePath -> CommandStart) -> CommandSeek
|
||||
withFilesMaybeModified a params =
|
||||
withFilesMaybeModified a params = seekActions $
|
||||
prepFiltered a $ seekHelper LsFiles.modified params
|
||||
|
||||
withKeys :: (Key -> CommandStart) -> CommandSeek
|
||||
withKeys a params = return $ map (a . parse) params
|
||||
withKeys a params = seekActions $ return $ map (a . parse) params
|
||||
where
|
||||
parse p = fromMaybe (error "bad key") $ file2key p
|
||||
|
||||
withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek
|
||||
withValue v a params = do
|
||||
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.
|
||||
{- Gets the value of a field options, which is fed into
|
||||
- a conversion function.
|
||||
-}
|
||||
withField :: Option -> (Maybe String -> Annex a) -> (a -> CommandSeek) -> CommandSeek
|
||||
withField option converter = withValue $
|
||||
converter <=< Annex.getField $ Option.name option
|
||||
getOptionField :: Option -> (Maybe String -> Annex a) -> Annex a
|
||||
getOptionField option converter = converter <=< Annex.getField $ optionName option
|
||||
|
||||
withFlag :: Option -> (Bool -> CommandSeek) -> CommandSeek
|
||||
withFlag option = withValue $ Annex.getFlag (Option.name option)
|
||||
getOptionFlag :: Option -> Annex Bool
|
||||
getOptionFlag option = Annex.getFlag (optionName option)
|
||||
|
||||
withNothing :: CommandStart -> CommandSeek
|
||||
withNothing a [] = return [a]
|
||||
withNothing a [] = seekActions $ return [a]
|
||||
withNothing _ _ = error "This command takes no parameters."
|
||||
|
||||
{- 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
|
||||
- the last git annex unused scan.
|
||||
-
|
||||
- If --key is specified, operates only on that key.
|
||||
-
|
||||
- Otherwise, fall back to a regular CommandSeek action on
|
||||
- whatever params were passed. -}
|
||||
withKeyOptions :: (Key -> CommandStart) -> CommandSeek -> CommandSeek
|
||||
|
@ -143,36 +130,51 @@ withKeyOptions keyop fallbackop params = do
|
|||
bare <- fromRepo Git.repoIsLocalBare
|
||||
allkeys <- Annex.getFlag "all"
|
||||
unused <- Annex.getFlag "unused"
|
||||
specifickey <- Annex.getField "key"
|
||||
auto <- Annex.getState Annex.auto
|
||||
case (allkeys || bare , unused, auto ) of
|
||||
(True , False , False) -> go loggedKeys
|
||||
(False , True , False) -> go unusedKeys
|
||||
(True , True , _ )
|
||||
| bare && not allkeys -> go unusedKeys
|
||||
| otherwise -> error "Cannot use --all with --unused."
|
||||
(False , False , _ ) -> fallbackop params
|
||||
(_ , _ , True )
|
||||
| bare -> error "Cannot use --auto in a bare repository."
|
||||
| otherwise -> error "Cannot use --auto with --all or --unused."
|
||||
when (auto && bare) $
|
||||
error "Cannot use --auto in a bare repository"
|
||||
case (allkeys, unused, null params, specifickey) of
|
||||
(False , False , True , Nothing)
|
||||
| bare -> go auto loggedKeys
|
||||
| otherwise -> fallbackop params
|
||||
(False , False , _ , Nothing) -> fallbackop params
|
||||
(True , False , True , Nothing) -> go auto loggedKeys
|
||||
(False , True , True , Nothing) -> go auto unusedKeys'
|
||||
(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
|
||||
go a = do
|
||||
unless (null params) $
|
||||
error "Cannot mix --all or --unused with file names."
|
||||
map keyop <$> a
|
||||
go True _ = error "Cannot use --auto with --all or --unused or --key"
|
||||
go False a = do
|
||||
matcher <- Limit.getMatcher
|
||||
seekActions $ map (process matcher) <$> a
|
||||
process matcher k = ifM (matcher $ MatchingKey k)
|
||||
( keyop k , return Nothing)
|
||||
|
||||
prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart]
|
||||
prepFiltered a fs = do
|
||||
matcher <- Limit.getMatcher
|
||||
map (process matcher) <$> fs
|
||||
where
|
||||
process matcher f = ifM (matcher $ FileInfo f f)
|
||||
process matcher f = ifM (matcher $ MatchingFile $ FileInfo f f)
|
||||
( 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 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 [] )
|
|
@ -5,7 +5,7 @@
|
|||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
||||
module Usage where
|
||||
module CmdLine.Usage where
|
||||
|
||||
import Common.Annex
|
||||
|
54
Command.hs
54
Command.hs
|
@ -1,10 +1,12 @@
|
|||
{- 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.
|
||||
-}
|
||||
|
||||
{-# LANGUAGE BangPatterns #-}
|
||||
|
||||
module Command (
|
||||
command,
|
||||
noRepo,
|
||||
|
@ -14,13 +16,9 @@ module Command (
|
|||
next,
|
||||
stop,
|
||||
stopUnless,
|
||||
prepCommand,
|
||||
doCommand,
|
||||
whenAnnexed,
|
||||
ifAnnexed,
|
||||
isBareRepo,
|
||||
numCopies,
|
||||
numCopiesCheck,
|
||||
checkAuto,
|
||||
module ReExported
|
||||
) where
|
||||
|
@ -29,18 +27,17 @@ import Common.Annex
|
|||
import qualified Backend
|
||||
import qualified Annex
|
||||
import qualified Git
|
||||
import qualified Remote
|
||||
import Types.Command as ReExported
|
||||
import Types.Option as ReExported
|
||||
import Seek as ReExported
|
||||
import CmdLine.Seek as ReExported
|
||||
import Checks as ReExported
|
||||
import Usage as ReExported
|
||||
import Logs.Trust
|
||||
import Config
|
||||
import Annex.CheckAttr
|
||||
import CmdLine.Usage as ReExported
|
||||
import CmdLine.Action as ReExported
|
||||
import CmdLine.Option as ReExported
|
||||
import CmdLine.GitAnnex.Options as ReExported
|
||||
|
||||
{- Generates a normal command -}
|
||||
command :: String -> String -> [CommandSeek] -> CommandSection -> String -> Command
|
||||
command :: String -> String -> CommandSeek -> CommandSection -> String -> Command
|
||||
command = Command [] Nothing commonChecks False False
|
||||
|
||||
{- 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 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,
|
||||
- and passes the key and backend on to it. -}
|
||||
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 = 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 checker = ifM (Annex.getState Annex.auto)
|
||||
( checker , return True )
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
module Command.Add where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Annex.Exception
|
||||
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.
|
||||
-
|
||||
- In direct mode, it acts on any files that have changed. -}
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ go withFilesNotInGit
|
||||
, whenNotDirect $ go withFilesUnlocked
|
||||
, whenDirect $ go withFilesMaybeModified
|
||||
]
|
||||
where
|
||||
go a = withValue largeFilesMatcher $ \matcher ->
|
||||
a $ \file -> ifM (checkFileMatcher matcher file <||> Annex.getState Annex.force)
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
matcher <- largeFilesMatcher
|
||||
let go a = flip a ps $ \file -> ifM (checkFileMatcher matcher file <||> Annex.getState Annex.force)
|
||||
( start file
|
||||
, stop
|
||||
)
|
||||
go withFilesNotInGit
|
||||
ifM isDirect
|
||||
( go withFilesMaybeModified
|
||||
, go withFilesUnlocked
|
||||
)
|
||||
|
||||
{- 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
|
||||
|
|
|
@ -18,8 +18,8 @@ def :: [Command]
|
|||
def = [notDirect $ command "addunused" (paramRepeating paramNumRange)
|
||||
seek SectionMaintenance "add back unused files"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withUnusedMaps start]
|
||||
seek :: CommandSeek
|
||||
seek = withUnusedMaps start
|
||||
|
||||
start :: UnusedMaps -> Int -> CommandStart
|
||||
start = startUnused "addunused" perform
|
||||
|
|
|
@ -21,7 +21,6 @@ import qualified Annex.Url as Url
|
|||
import qualified Backend.URL
|
||||
import Annex.Content
|
||||
import Logs.Web
|
||||
import qualified Option
|
||||
import Types.Key
|
||||
import Types.KeySource
|
||||
import Config
|
||||
|
@ -39,19 +38,20 @@ def = [notBareRepo $ withOptions [fileOption, pathdepthOption, relaxedOption] $
|
|||
SectionCommon "add urls to annex"]
|
||||
|
||||
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.field [] "pathdepth" paramNumber "path components to use in filename"
|
||||
pathdepthOption = fieldOption [] "pathdepth" paramNumber "path components to use in filename"
|
||||
|
||||
relaxedOption :: Option
|
||||
relaxedOption = Option.flag [] "relaxed" "skip size check"
|
||||
relaxedOption = flagOption [] "relaxed" "skip size check"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withField fileOption return $ \f ->
|
||||
withFlag relaxedOption $ \relaxed ->
|
||||
withField pathdepthOption (return . maybe Nothing readish) $ \d ->
|
||||
withStrings $ start relaxed f d]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
f <- getOptionField fileOption return
|
||||
relaxed <- getOptionFlag relaxedOption
|
||||
d <- getOptionField pathdepthOption (return . maybe Nothing readish)
|
||||
withStrings (start relaxed f d) ps
|
||||
|
||||
start :: Bool -> Maybe FilePath -> Maybe Int -> String -> CommandStart
|
||||
start relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI s
|
||||
|
|
|
@ -9,9 +9,8 @@ module Command.Assistant where
|
|||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import qualified Option
|
||||
import qualified Command.Watch
|
||||
import Init
|
||||
import Annex.Init
|
||||
import Config.Files
|
||||
import qualified Build.SysConfig
|
||||
import Utility.HumanTime
|
||||
|
@ -32,17 +31,18 @@ options =
|
|||
]
|
||||
|
||||
autoStartOption :: Option
|
||||
autoStartOption = Option.flag [] "autostart" "start in known repositories"
|
||||
autoStartOption = flagOption [] "autostart" "start in known repositories"
|
||||
|
||||
startDelayOption :: Option
|
||||
startDelayOption = Option.field [] "startdelay" paramNumber "delay before running startup scan"
|
||||
startDelayOption = fieldOption [] "startdelay" paramNumber "delay before running startup scan"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFlag Command.Watch.stopOption $ \stopdaemon ->
|
||||
withFlag Command.Watch.foregroundOption $ \foreground ->
|
||||
withFlag autoStartOption $ \autostart ->
|
||||
withField startDelayOption (pure . maybe Nothing parseDuration) $ \startdelay ->
|
||||
withNothing $ start foreground stopdaemon autostart startdelay]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
stopdaemon <- getOptionFlag Command.Watch.stopOption
|
||||
foreground <- getOptionFlag Command.Watch.foregroundOption
|
||||
autostart <- getOptionFlag autoStartOption
|
||||
startdelay <- getOptionField startDelayOption (pure . maybe Nothing parseDuration)
|
||||
withNothing (start foreground stopdaemon autostart startdelay) ps
|
||||
|
||||
start :: Bool -> Bool -> Bool -> Maybe Duration -> CommandStart
|
||||
start foreground stopdaemon autostart startdelay
|
||||
|
|
|
@ -16,8 +16,8 @@ def :: [Command]
|
|||
def = [command "commit" paramNothing seek
|
||||
SectionPlumbing "commits any staged changes to the git-annex branch"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = next $ next $ do
|
||||
|
|
|
@ -17,8 +17,8 @@ def :: [Command]
|
|||
def = [noCommit $ command "configlist" paramNothing seek
|
||||
SectionPlumbing "outputs relevant git configuration"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = do
|
||||
|
|
|
@ -9,22 +9,23 @@ module Command.Copy where
|
|||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import GitAnnex.Options
|
||||
import qualified Command.Move
|
||||
import qualified Remote
|
||||
import Annex.Wanted
|
||||
import Config.NumCopies
|
||||
|
||||
def :: [Command]
|
||||
def = [withOptions Command.Move.moveOptions $ command "copy" paramPaths seek
|
||||
SectionCommon "copy content of files to/from another repository"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withField toOption Remote.byNameWithUUID $ \to ->
|
||||
withField fromOption Remote.byNameWithUUID $ \from ->
|
||||
withKeyOptions (Command.Move.startKey to from False) $
|
||||
withFilesInGit $ whenAnnexed $ start to from
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
to <- getOptionField toOption Remote.byNameWithUUID
|
||||
from <- getOptionField fromOption Remote.byNameWithUUID
|
||||
withKeyOptions
|
||||
(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.
|
||||
- However, --auto mode avoids unnecessary copies, and avoids getting or
|
||||
|
@ -35,5 +36,5 @@ start to from file (key, backend) = stopUnless shouldCopy $
|
|||
where
|
||||
shouldCopy = checkAuto (check <||> numCopiesCheck file key (<))
|
||||
check = case to of
|
||||
Nothing -> wantGet False (Just file)
|
||||
Just r -> wantSend False (Just file) (Remote.uuid r)
|
||||
Nothing -> wantGet False (Just key) (Just file)
|
||||
Just r -> wantSend False (Just key) (Just file) (Remote.uuid r)
|
||||
|
|
|
@ -19,8 +19,8 @@ def :: [Command]
|
|||
def = [command "dead" (paramRepeating paramRemote) seek
|
||||
SectionSetup "hide a lost repository"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start ws = do
|
||||
|
|
|
@ -16,8 +16,8 @@ def :: [Command]
|
|||
def = [command "describe" (paramPair paramRemote paramDesc) seek
|
||||
SectionSetup "change description of a repository"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start (name:description) = do
|
||||
|
|
|
@ -23,8 +23,8 @@ def = [notBareRepo $ noDaemonRunning $
|
|||
command "direct" paramNothing seek
|
||||
SectionSetup "switch repository to direct mode"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = ifM isDirect ( stop , next perform )
|
||||
|
|
|
@ -14,26 +14,25 @@ import qualified Annex
|
|||
import Annex.UUID
|
||||
import Logs.Location
|
||||
import Logs.Trust
|
||||
import Config.NumCopies
|
||||
import Annex.Content
|
||||
import Config
|
||||
import qualified Option
|
||||
import Annex.Wanted
|
||||
import Types.Key
|
||||
|
||||
def :: [Command]
|
||||
def = [withOptions [fromOption] $ command "drop" paramPaths seek
|
||||
def = [withOptions [dropFromOption] $ command "drop" paramPaths seek
|
||||
SectionCommon "indicate content of files not currently wanted"]
|
||||
|
||||
fromOption :: Option
|
||||
fromOption = Option.field ['f'] "from" paramRemote "drop content from a remote"
|
||||
dropFromOption :: Option
|
||||
dropFromOption = fieldOption ['f'] "from" paramRemote "drop content from a remote"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withField fromOption Remote.byNameWithUUID $ \from ->
|
||||
withFilesInGit $ whenAnnexed $ start from]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
from <- getOptionField dropFromOption Remote.byNameWithUUID
|
||||
withFilesInGit (whenAnnexed $ start from) ps
|
||||
|
||||
start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
|
||||
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
|
||||
Nothing -> startLocal (Just file) numcopies key Nothing
|
||||
Just remote -> do
|
||||
|
@ -42,17 +41,17 @@ start from file (key, _) = checkDropAuto from file key $ \numcopies ->
|
|||
then startLocal (Just file) numcopies key Nothing
|
||||
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
|
||||
showStart "drop" (fromMaybe (key2file key) afile)
|
||||
showStart' "drop" key afile
|
||||
next $ performLocal key numcopies knownpresentremote
|
||||
|
||||
startRemote :: AssociatedFile -> Maybe Int -> Key -> Remote -> CommandStart
|
||||
startRemote :: AssociatedFile -> NumCopies -> Key -> Remote -> CommandStart
|
||||
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
|
||||
|
||||
performLocal :: Key -> Maybe Int -> Maybe Remote -> CommandPerform
|
||||
performLocal :: Key -> NumCopies -> Maybe Remote -> CommandPerform
|
||||
performLocal key numcopies knownpresentremote = lockContent key $ do
|
||||
(remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key
|
||||
let trusteduuids' = case knownpresentremote of
|
||||
|
@ -64,7 +63,7 @@ performLocal key numcopies knownpresentremote = lockContent key $ do
|
|||
removeAnnex key
|
||||
next $ cleanupLocal key
|
||||
|
||||
performRemote :: Key -> Maybe Int -> Remote -> CommandPerform
|
||||
performRemote :: Key -> NumCopies -> Remote -> CommandPerform
|
||||
performRemote key numcopies remote = lockContent key $ do
|
||||
-- Filter the remote it's being dropped from out of the lists of
|
||||
-- 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
|
||||
- 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. -}
|
||||
canDropKey :: Key -> Maybe Int -> [UUID] -> [Remote] -> [UUID] -> Annex Bool
|
||||
canDropKey key numcopiesM have check skip = do
|
||||
canDropKey :: Key -> NumCopies -> [UUID] -> [Remote] -> [UUID] -> Annex Bool
|
||||
canDropKey key numcopies have check skip = do
|
||||
force <- Annex.getState Annex.force
|
||||
if force || numcopiesM == Just 0
|
||||
if force || numcopies == NumCopies 0
|
||||
then return True
|
||||
else do
|
||||
need <- getNumCopies numcopiesM
|
||||
findCopies key need skip have check
|
||||
else findCopies key numcopies skip have check
|
||||
|
||||
findCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
|
||||
findCopies :: Key -> NumCopies -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
|
||||
findCopies key need skip = helper [] []
|
||||
where
|
||||
helper bad missing have []
|
||||
| length have >= need = return True
|
||||
| NumCopies (length have) >= need = return True
|
||||
| otherwise = notEnoughCopies key need have (skip++missing) bad
|
||||
helper bad missing have (r:rs)
|
||||
| length have >= need = return True
|
||||
| NumCopies (length have) >= need = return True
|
||||
| otherwise = do
|
||||
let u = Remote.uuid r
|
||||
let duplicate = u `elem` have
|
||||
|
@ -124,12 +121,12 @@ findCopies key need skip = helper [] []
|
|||
(False, Right False) -> helper bad (u: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
|
||||
unsafe
|
||||
showLongNote $
|
||||
"Could only verify the existence of " ++
|
||||
show (length have) ++ " out of " ++ show need ++
|
||||
show (length have) ++ " out of " ++ show (fromNumCopies need) ++
|
||||
" necessary copies"
|
||||
Remote.showTriedRemotes bad
|
||||
Remote.showLocations key (have++skip)
|
||||
|
@ -138,25 +135,21 @@ notEnoughCopies key need have skip bad = do
|
|||
return False
|
||||
where
|
||||
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
|
||||
- copies on other semitrusted repositories.
|
||||
-
|
||||
- Passes any numcopies attribute of the file on to the action as an
|
||||
- optimisation. -}
|
||||
checkDropAuto :: Maybe Remote -> FilePath -> Key -> (Maybe Int -> CommandStart) -> CommandStart
|
||||
- copies on other semitrusted repositories. -}
|
||||
checkDropAuto :: Maybe Remote -> FilePath -> Key -> (NumCopies -> CommandStart) -> CommandStart
|
||||
checkDropAuto mremote file key a = do
|
||||
numcopiesattr <- numCopies file
|
||||
Annex.getState Annex.auto >>= auto numcopiesattr
|
||||
numcopies <- getFileNumCopies file
|
||||
Annex.getState Annex.auto >>= auto numcopies
|
||||
where
|
||||
auto numcopiesattr False = a numcopiesattr
|
||||
auto numcopiesattr True = do
|
||||
needed <- getNumCopies numcopiesattr
|
||||
auto numcopies False = a numcopies
|
||||
auto numcopies True = do
|
||||
locs <- Remote.keyLocations key
|
||||
uuid <- getUUID
|
||||
let remoteuuid = fromMaybe uuid $ Remote.uuid <$> mremote
|
||||
locs' <- trustExclude UnTrusted $ filter (/= remoteuuid) locs
|
||||
if length locs' >= needed
|
||||
then a numcopiesattr
|
||||
if NumCopies (length locs') >= numcopies
|
||||
then a numcopies
|
||||
else stop
|
||||
|
|
|
@ -12,20 +12,19 @@ import Command
|
|||
import qualified Annex
|
||||
import Logs.Location
|
||||
import Annex.Content
|
||||
import Types.Key
|
||||
|
||||
def :: [Command]
|
||||
def = [noCommit $ command "dropkey" (paramRepeating paramKey) seek
|
||||
SectionPlumbing "drops annexed content for specified keys"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withKeys start]
|
||||
seek :: CommandSeek
|
||||
seek = withKeys start
|
||||
|
||||
start :: Key -> CommandStart
|
||||
start key = stopUnless (inAnnex key) $ do
|
||||
unlessM (Annex.getState Annex.force) $
|
||||
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
|
||||
|
||||
perform :: Key -> CommandPerform
|
||||
|
|
|
@ -13,28 +13,30 @@ import qualified Annex
|
|||
import qualified Command.Drop
|
||||
import qualified Remote
|
||||
import qualified Git
|
||||
import qualified Option
|
||||
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
|
||||
import Config.NumCopies
|
||||
|
||||
def :: [Command]
|
||||
def = [withOptions [Command.Drop.fromOption] $
|
||||
def = [withOptions [Command.Drop.dropFromOption] $
|
||||
command "dropunused" (paramRepeating paramNumRange)
|
||||
seek SectionMaintenance "drop unused file content"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withUnusedMaps start]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
numcopies <- getNumCopies
|
||||
withUnusedMaps (start numcopies) ps
|
||||
|
||||
start :: UnusedMaps -> Int -> CommandStart
|
||||
start = startUnused "dropunused" perform (performOther gitAnnexBadLocation) (performOther gitAnnexTmpLocation)
|
||||
start :: NumCopies -> UnusedMaps -> Int -> CommandStart
|
||||
start numcopies = startUnused "dropunused" (perform numcopies) (performOther gitAnnexBadLocation) (performOther gitAnnexTmpLocation)
|
||||
|
||||
perform :: Key -> CommandPerform
|
||||
perform key = maybe droplocal dropremote =<< Remote.byNameWithUUID =<< from
|
||||
perform :: NumCopies -> Key -> CommandPerform
|
||||
perform numcopies key = maybe droplocal dropremote =<< Remote.byNameWithUUID =<< from
|
||||
where
|
||||
dropremote r = do
|
||||
showAction $ "from " ++ Remote.name r
|
||||
Command.Drop.performRemote key Nothing r
|
||||
droplocal = Command.Drop.performLocal key Nothing Nothing
|
||||
from = Annex.getField $ Option.name Command.Drop.fromOption
|
||||
Command.Drop.performRemote key numcopies r
|
||||
droplocal = Command.Drop.performLocal key numcopies Nothing
|
||||
from = Annex.getField $ optionName Command.Drop.dropFromOption
|
||||
|
||||
performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform
|
||||
performOther filespec key = do
|
||||
|
|
|
@ -20,8 +20,8 @@ def = [command "enableremote"
|
|||
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
|
||||
seek SectionSetup "enables use of an existing special remote"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
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 prefix = do
|
||||
names <- InitRemote.remoteNames
|
||||
error $ prefix ++
|
||||
error $ prefix ++ "\n" ++
|
||||
if null names
|
||||
then ""
|
||||
else " Known special remotes: " ++ unwords names
|
||||
then "(No special remotes are currently known; perhaps use initremote instead?)"
|
||||
else "Known special remotes: " ++ unwords names
|
||||
|
||||
perform :: RemoteType -> UUID -> R.RemoteConfig -> CommandPerform
|
||||
perform t u c = do
|
||||
|
|
|
@ -10,16 +10,18 @@ module Command.ExamineKey where
|
|||
import Common.Annex
|
||||
import Command
|
||||
import qualified Utility.Format
|
||||
import Command.Find (formatOption, withFormat, showFormatted, keyVars)
|
||||
import Command.Find (formatOption, getFormat, showFormatted, keyVars)
|
||||
import Types.Key
|
||||
|
||||
def :: [Command]
|
||||
def = [noCommit $ noMessages $ withOptions [formatOption] $
|
||||
def = [noCommit $ noMessages $ withOptions [formatOption, jsonOption] $
|
||||
command "examinekey" (paramRepeating paramKey) seek
|
||||
SectionPlumbing "prints information from a key"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFormat $ \f -> withKeys $ start f]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
format <- getFormat
|
||||
withKeys (start format) ps
|
||||
|
||||
start :: Maybe Utility.Format.Format -> Key -> CommandStart
|
||||
start format key = do
|
||||
|
|
|
@ -17,26 +17,27 @@ import qualified Annex
|
|||
import qualified Utility.Format
|
||||
import Utility.DataUnits
|
||||
import Types.Key
|
||||
import qualified Option
|
||||
|
||||
def :: [Command]
|
||||
def = [noCommit $ noMessages $ withOptions [formatOption, print0Option] $
|
||||
def = [noCommit $ noMessages $ withOptions [formatOption, print0Option, jsonOption] $
|
||||
command "find" paramPaths seek SectionQuery "lists available files"]
|
||||
|
||||
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
|
||||
withFormat = withField formatOption $ return . fmap Utility.Format.gen
|
||||
getFormat :: Annex (Maybe Utility.Format.Format)
|
||||
getFormat = getOptionField formatOption $ return . fmap Utility.Format.gen
|
||||
|
||||
print0Option :: Option
|
||||
print0Option = Option.Option [] ["print0"] (Option.NoArg set)
|
||||
print0Option = Option [] ["print0"] (NoArg set)
|
||||
"terminate output with null"
|
||||
where
|
||||
set = Annex.setField (Option.name formatOption) "${file}\0"
|
||||
set = Annex.setField (optionName formatOption) "${file}\0"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFormat $ \f -> withFilesInGit $ whenAnnexed $ start f]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
format <- getFormat
|
||||
withFilesInGit (whenAnnexed $ start format) ps
|
||||
|
||||
start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart
|
||||
start format file (key, _) = do
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
module Command.Fix where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import qualified Annex.Queue
|
||||
|
@ -24,8 +22,8 @@ def :: [Command]
|
|||
def = [notDirect $ noCommit $ command "fix" paramPaths seek
|
||||
SectionMaintenance "fix up symlinks to point to annexed content"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFilesInGit $ whenAnnexed start]
|
||||
seek :: CommandSeek
|
||||
seek = withFilesInGit $ whenAnnexed start
|
||||
|
||||
{- Fixes the symlink to an annexed file. -}
|
||||
start :: FilePath -> (Key, Backend) -> CommandStart
|
||||
|
|
|
@ -12,7 +12,6 @@ import Command
|
|||
import qualified Annex.Branch as Branch
|
||||
import Logs.Transitions
|
||||
import qualified Annex
|
||||
import qualified Option
|
||||
|
||||
import Data.Time.Clock.POSIX
|
||||
|
||||
|
@ -24,11 +23,12 @@ forgetOptions :: [Option]
|
|||
forgetOptions = [dropDeadOption]
|
||||
|
||||
dropDeadOption :: Option
|
||||
dropDeadOption = Option.flag [] "drop-dead" "drop references to dead repositories"
|
||||
dropDeadOption = flagOption [] "drop-dead" "drop references to dead repositories"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFlag dropDeadOption $ \dropdead ->
|
||||
withNothing $ start dropdead]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
dropdead <- getOptionFlag dropDeadOption
|
||||
withNothing (start dropdead) ps
|
||||
|
||||
start :: Bool -> CommandStart
|
||||
start dropdead = do
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
module Command.FromKey where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import qualified Annex.Queue
|
||||
|
@ -20,8 +18,8 @@ def = [notDirect $ notBareRepo $
|
|||
command "fromkey" (paramPair paramKey paramPath) seek
|
||||
SectionPlumbing "adds a file using a specific key"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start (keyname:file:[]) = do
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
module Command.Fsck where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import qualified Annex
|
||||
|
@ -25,15 +23,14 @@ import Annex.Perms
|
|||
import Annex.Link
|
||||
import Logs.Location
|
||||
import Logs.Trust
|
||||
import Config.NumCopies
|
||||
import Annex.UUID
|
||||
import Utility.DataUnits
|
||||
import Utility.FileMode
|
||||
import Config
|
||||
import qualified Option
|
||||
import Types.Key
|
||||
import Utility.HumanTime
|
||||
import Git.FilePath
|
||||
import GitAnnex.Options hiding (fromOption)
|
||||
|
||||
#ifndef mingw32_HOST_OS
|
||||
import System.Posix.Process (getProcessID)
|
||||
|
@ -49,41 +46,42 @@ def :: [Command]
|
|||
def = [withOptions fsckOptions $ command "fsck" paramPaths seek
|
||||
SectionMaintenance "check for problems"]
|
||||
|
||||
fromOption :: Option
|
||||
fromOption = Option.field ['f'] "from" paramRemote "check remote"
|
||||
fsckFromOption :: Option
|
||||
fsckFromOption = fieldOption ['f'] "from" paramRemote "check remote"
|
||||
|
||||
startIncrementalOption :: Option
|
||||
startIncrementalOption = Option.flag ['S'] "incremental" "start an incremental fsck"
|
||||
startIncrementalOption = flagOption ['S'] "incremental" "start an incremental fsck"
|
||||
|
||||
moreIncrementalOption :: Option
|
||||
moreIncrementalOption = Option.flag ['m'] "more" "continue an incremental fsck"
|
||||
moreIncrementalOption = flagOption ['m'] "more" "continue an incremental fsck"
|
||||
|
||||
incrementalScheduleOption :: Option
|
||||
incrementalScheduleOption = Option.field [] "incremental-schedule" paramTime
|
||||
incrementalScheduleOption = fieldOption [] "incremental-schedule" paramTime
|
||||
"schedule incremental fscking"
|
||||
|
||||
fsckOptions :: [Option]
|
||||
fsckOptions =
|
||||
[ fromOption
|
||||
[ fsckFromOption
|
||||
, startIncrementalOption
|
||||
, moreIncrementalOption
|
||||
, incrementalScheduleOption
|
||||
] ++ keyOptions
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withField fromOption Remote.byNameWithUUID $ \from ->
|
||||
withIncremental $ \i ->
|
||||
withKeyOptions (startKey i) $
|
||||
withFilesInGit $ whenAnnexed $ start from i
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
from <- getOptionField fsckFromOption Remote.byNameWithUUID
|
||||
i <- getIncremental
|
||||
withKeyOptions
|
||||
(startKey i)
|
||||
(withFilesInGit $ whenAnnexed $ start from i)
|
||||
ps
|
||||
|
||||
withIncremental :: (Incremental -> CommandSeek) -> CommandSeek
|
||||
withIncremental = withValue $ do
|
||||
getIncremental :: Annex Incremental
|
||||
getIncremental = do
|
||||
i <- maybe (return False) (checkschedule . parseDuration)
|
||||
=<< Annex.getField (Option.name incrementalScheduleOption)
|
||||
starti <- Annex.getFlag (Option.name startIncrementalOption)
|
||||
morei <- Annex.getFlag (Option.name moreIncrementalOption)
|
||||
=<< Annex.getField (optionName incrementalScheduleOption)
|
||||
starti <- Annex.getFlag (optionName startIncrementalOption)
|
||||
morei <- Annex.getFlag (optionName moreIncrementalOption)
|
||||
case (i, starti, morei) of
|
||||
(False, False, False) -> return NonIncremental
|
||||
(False, True, _) -> startIncremental
|
||||
|
@ -110,14 +108,14 @@ withIncremental = withValue $ do
|
|||
|
||||
start :: Maybe Remote -> Incremental -> FilePath -> (Key, Backend) -> CommandStart
|
||||
start from inc file (key, backend) = do
|
||||
numcopies <- numCopies file
|
||||
numcopies <- getFileNumCopies file
|
||||
case from of
|
||||
Nothing -> go $ perform key file backend numcopies
|
||||
Just r -> go $ performRemote key file backend numcopies r
|
||||
where
|
||||
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
|
||||
-- order matters
|
||||
[ 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,
|
||||
- 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 =
|
||||
dispatch =<< Remote.hasKey remote key
|
||||
where
|
||||
|
@ -367,27 +365,26 @@ checkBackendOr' bad backend key file postcheck =
|
|||
, return True
|
||||
)
|
||||
|
||||
checkKeyNumCopies :: Key -> FilePath -> Maybe Int -> Annex Bool
|
||||
checkKeyNumCopies :: Key -> FilePath -> NumCopies -> Annex Bool
|
||||
checkKeyNumCopies key file numcopies = do
|
||||
needed <- getNumCopies numcopies
|
||||
(untrustedlocations, safelocations) <- trustPartition UnTrusted =<< Remote.keyLocations key
|
||||
let present = length safelocations
|
||||
if present < needed
|
||||
let present = NumCopies (length safelocations)
|
||||
if present < numcopies
|
||||
then do
|
||||
ppuuids <- Remote.prettyPrintUUIDs "untrusted" untrustedlocations
|
||||
warning $ missingNote file present needed ppuuids
|
||||
warning $ missingNote file present numcopies ppuuids
|
||||
return False
|
||||
else return True
|
||||
|
||||
missingNote :: String -> Int -> Int -> String -> String
|
||||
missingNote file 0 _ [] =
|
||||
missingNote :: String -> NumCopies -> NumCopies -> String -> String
|
||||
missingNote file (NumCopies 0) _ [] =
|
||||
"** No known copies exist of " ++ file
|
||||
missingNote file 0 _ untrusted =
|
||||
missingNote file (NumCopies 0) _ untrusted =
|
||||
"Only these untrusted locations may have copies of " ++ file ++
|
||||
"\n" ++ untrusted ++
|
||||
"Back it up to trusted locations with git-annex copy."
|
||||
missingNote file present needed [] =
|
||||
"Only " ++ show present ++ " of " ++ show needed ++
|
||||
"Only " ++ show (fromNumCopies present) ++ " of " ++ show (fromNumCopies needed) ++
|
||||
" trustworthy copies exist of " ++ file ++
|
||||
"\nBack it up with git-annex copy."
|
||||
missingNote file present needed untrusted =
|
||||
|
@ -481,10 +478,9 @@ recordStartTime = do
|
|||
createAnnexDirectory $ parentDir f
|
||||
liftIO $ do
|
||||
nukeFile f
|
||||
h <- openFile f WriteMode
|
||||
withFile f WriteMode $ \h -> do
|
||||
t <- modificationTime <$> getFileStatus f
|
||||
hPutStr h $ showTime $ realToFrac t
|
||||
hClose h
|
||||
where
|
||||
showTime :: POSIXTime -> String
|
||||
showTime = show
|
||||
|
|
|
@ -25,8 +25,8 @@ def :: [Command]
|
|||
def = [ notBareRepo $ command "fuzztest" paramNothing seek SectionPlumbing
|
||||
"generates fuzz test files"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = do
|
||||
|
@ -146,13 +146,6 @@ genFuzzFile = do
|
|||
genFuzzDir :: IO FuzzDir
|
||||
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
|
||||
= Started UTCTime FuzzAction
|
||||
| Finished UTCTime Bool
|
||||
|
|
|
@ -18,8 +18,8 @@ def = [dontCheck repoExists $ noCommit $
|
|||
command "gcryptsetup" paramValue seek
|
||||
SectionPlumbing "sets up gcrypt repository"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withStrings start]
|
||||
seek :: CommandSeek
|
||||
seek = withStrings start
|
||||
|
||||
start :: String -> CommandStart
|
||||
start gcryptid = next $ next $ do
|
||||
|
|
|
@ -12,10 +12,9 @@ import Command
|
|||
import qualified Remote
|
||||
import Annex.Content
|
||||
import Logs.Transfer
|
||||
import Config.NumCopies
|
||||
import Annex.Wanted
|
||||
import GitAnnex.Options
|
||||
import qualified Command.Move
|
||||
import Types.Key
|
||||
|
||||
def :: [Command]
|
||||
def = [withOptions getOptions $ command "get" paramPaths seek
|
||||
|
@ -24,17 +23,18 @@ def = [withOptions getOptions $ command "get" paramPaths seek
|
|||
getOptions :: [Option]
|
||||
getOptions = fromOption : keyOptions
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withField fromOption Remote.byNameWithUUID $ \from ->
|
||||
withKeyOptions (startKeys from) $
|
||||
withFilesInGit $ whenAnnexed $ start from
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
from <- getOptionField fromOption Remote.byNameWithUUID
|
||||
withKeyOptions
|
||||
(startKeys from)
|
||||
(withFilesInGit $ whenAnnexed $ start from)
|
||||
ps
|
||||
|
||||
start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
|
||||
start from file (key, _) = start' expensivecheck from key (Just file)
|
||||
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 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
|
||||
where
|
||||
go a = do
|
||||
showStart "get" (fromMaybe (key2file key) afile)
|
||||
showStart' "get" key afile
|
||||
next a
|
||||
|
||||
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,
|
||||
- and copy it to here. -}
|
||||
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
|
||||
dispatch [] = do
|
||||
showNote "not available"
|
||||
|
|
|
@ -19,8 +19,8 @@ def :: [Command]
|
|||
def = [command "group" (paramPair paramRemote paramDesc) seek
|
||||
SectionSetup "add a repository to a group"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start (name:g:[]) = do
|
||||
|
|
|
@ -18,7 +18,6 @@ import qualified Command.Copy
|
|||
import qualified Command.Sync
|
||||
import qualified Command.Whereis
|
||||
import qualified Command.Fsck
|
||||
import GitAnnex.Options
|
||||
|
||||
import System.Console.GetOpt
|
||||
|
||||
|
@ -26,8 +25,8 @@ def :: [Command]
|
|||
def = [noCommit $ noRepo startNoRepo $ dontCheck repoExists $
|
||||
command "help" paramNothing seek SectionQuery "display help"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start params = do
|
||||
|
@ -42,7 +41,7 @@ start' ["options"] = showCommonOptions
|
|||
start' _ = showGeneralHelp
|
||||
|
||||
showCommonOptions :: IO ()
|
||||
showCommonOptions = putStrLn $ usageInfo "Common options:" options
|
||||
showCommonOptions = putStrLn $ usageInfo "Common options:" gitAnnexOptions
|
||||
|
||||
showGeneralHelp :: IO ()
|
||||
showGeneralHelp = putStrLn $ unlines
|
||||
|
|
|
@ -7,13 +7,10 @@
|
|||
|
||||
module Command.Import where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import qualified Annex
|
||||
import qualified Command.Add
|
||||
import qualified Option
|
||||
import Utility.CopyFile
|
||||
import Backend
|
||||
import Remote
|
||||
|
@ -32,16 +29,16 @@ opts =
|
|||
]
|
||||
|
||||
duplicateOption :: Option
|
||||
duplicateOption = Option.flag [] "duplicate" "do not delete source files"
|
||||
duplicateOption = flagOption [] "duplicate" "do not delete source files"
|
||||
|
||||
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.flag [] "clean-duplicates" "delete duplicate source files (import nothing)"
|
||||
cleanDuplicatesOption = flagOption [] "clean-duplicates" "delete duplicate source files (import nothing)"
|
||||
|
||||
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
|
||||
deriving (Eq)
|
||||
|
@ -53,7 +50,7 @@ getDuplicateMode = gen
|
|||
<*> getflag cleanDuplicatesOption
|
||||
<*> getflag skipDuplicatesOption
|
||||
where
|
||||
getflag = Annex.getFlag . Option.name
|
||||
getflag = Annex.getFlag . optionName
|
||||
gen False False False False = Default
|
||||
gen True False False False = Duplicate
|
||||
gen False True False False = DeDuplicate
|
||||
|
@ -61,8 +58,10 @@ getDuplicateMode = gen
|
|||
gen False False False True = SkipDuplicates
|
||||
gen _ _ _ _ = error "bad combination of --duplicate, --deduplicate, --clean-duplicates, --skip-duplicates"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withValue getDuplicateMode $ \mode -> withPathContents $ start mode]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
mode <- getDuplicateMode
|
||||
withPathContents (start mode) ps
|
||||
|
||||
start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart
|
||||
start mode (srcfile, destfile) =
|
||||
|
|
|
@ -21,7 +21,6 @@ import qualified Annex
|
|||
import Command
|
||||
import qualified Annex.Url as Url
|
||||
import Logs.Web
|
||||
import qualified Option
|
||||
import qualified Utility.Format
|
||||
import Utility.Tmp
|
||||
import Command.AddUrl (addUrlFile, relaxedOption)
|
||||
|
@ -39,13 +38,14 @@ def = [notBareRepo $ withOptions [templateOption, relaxedOption] $
|
|||
SectionCommon "import files from podcast feeds"]
|
||||
|
||||
templateOption :: Option
|
||||
templateOption = Option.field [] "template" paramFormat "template for filenames"
|
||||
templateOption = fieldOption [] "template" paramFormat "template for filenames"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withField templateOption return $ \tmpl ->
|
||||
withFlag relaxedOption $ \relaxed ->
|
||||
withValue (getCache tmpl) $ \cache ->
|
||||
withStrings $ start relaxed cache]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
tmpl <- getOptionField templateOption return
|
||||
relaxed <- getOptionFlag relaxedOption
|
||||
cache <- getCache tmpl
|
||||
withStrings (start relaxed cache) ps
|
||||
|
||||
start :: Bool -> Cache -> URLString -> CommandStart
|
||||
start relaxed cache url = do
|
||||
|
|
|
@ -15,8 +15,8 @@ def :: [Command]
|
|||
def = [noCommit $ command "inannex" (paramRepeating paramKey) seek
|
||||
SectionPlumbing "checks if keys are present in the annex"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withKeys start]
|
||||
seek :: CommandSeek
|
||||
seek = withKeys start
|
||||
|
||||
start :: Key -> CommandStart
|
||||
start key = inAnnexSafe key >>= dispatch
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
module Command.Indirect where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
import Control.Exception.Extensible
|
||||
|
||||
import Common.Annex
|
||||
|
@ -23,7 +22,7 @@ import Annex.Content
|
|||
import Annex.Content.Direct
|
||||
import Annex.CatFile
|
||||
import Annex.Exception
|
||||
import Init
|
||||
import Annex.Init
|
||||
import qualified Command.Add
|
||||
|
||||
def :: [Command]
|
||||
|
@ -31,8 +30,8 @@ def = [notBareRepo $ noDaemonRunning $
|
|||
command "indirect" paramNothing seek
|
||||
SectionSetup "switch repository to indirect mode"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = ifM isDirect
|
||||
|
|
|
@ -14,7 +14,6 @@ import qualified Data.Map as M
|
|||
import Text.JSON
|
||||
import Data.Tuple
|
||||
import Data.Ord
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import qualified Remote
|
||||
|
@ -28,6 +27,7 @@ import Annex.Content
|
|||
import Types.Key
|
||||
import Logs.UUID
|
||||
import Logs.Trust
|
||||
import Config.NumCopies
|
||||
import Remote
|
||||
import Config
|
||||
import Utility.Percentage
|
||||
|
@ -70,11 +70,12 @@ data StatInfo = StatInfo
|
|||
type StatState = StateT StatInfo Annex
|
||||
|
||||
def :: [Command]
|
||||
def = [noCommit $ command "info" paramPaths seek
|
||||
SectionQuery "shows general information about the annex"]
|
||||
def = [noCommit $ withOptions [jsonOption] $
|
||||
command "info" paramPaths seek SectionQuery
|
||||
"shows general information about the annex"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [FilePath] -> CommandStart
|
||||
start [] = do
|
||||
|
@ -310,7 +311,7 @@ getLocalStatInfo dir = do
|
|||
where
|
||||
initial = (emptyKeyData, emptyKeyData, emptyNumCopiesStats)
|
||||
update matcher fast key file vs@(presentdata, referenceddata, numcopiesstats) =
|
||||
ifM (matcher $ FileInfo file file)
|
||||
ifM (matcher $ MatchingFile $ FileInfo file file)
|
||||
( do
|
||||
!presentdata' <- ifM (inAnnex key)
|
||||
( return $ addKey key presentdata
|
||||
|
|
|
@ -9,14 +9,14 @@ module Command.Init where
|
|||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import Init
|
||||
import Annex.Init
|
||||
|
||||
def :: [Command]
|
||||
def = [dontCheck repoExists $
|
||||
command "init" paramDesc seek SectionSetup "initialize git-annex"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start ws = do
|
||||
|
|
|
@ -24,8 +24,8 @@ def = [command "initremote"
|
|||
(paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
|
||||
seek SectionSetup "creates a special (non-git) remote"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [String] -> CommandStart
|
||||
start [] = error "Specify a name for the remote."
|
||||
|
|
|
@ -20,7 +20,6 @@ import Remote
|
|||
import Logs.Trust
|
||||
import Logs.UUID
|
||||
import Annex.UUID
|
||||
import qualified Option
|
||||
import qualified Annex
|
||||
import Git.Types (RemoteName)
|
||||
|
||||
|
@ -29,16 +28,16 @@ def = [noCommit $ withOptions [allrepos] $ command "list" paramPaths seek
|
|||
SectionQuery "show which remotes contain files"]
|
||||
|
||||
allrepos :: Option
|
||||
allrepos = Option.flag [] "allrepos" "show all repositories, not only remotes"
|
||||
allrepos = flagOption [] "allrepos" "show all repositories, not only remotes"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withValue getList $ withNothing . startHeader
|
||||
, withValue getList $ withFilesInGit . whenAnnexed . start
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
list <- getList
|
||||
printHeader list
|
||||
withFilesInGit (whenAnnexed $ start list) ps
|
||||
|
||||
getList :: Annex [(UUID, RemoteName, TrustLevel)]
|
||||
getList = ifM (Annex.getFlag $ Option.name allrepos)
|
||||
getList = ifM (Annex.getFlag $ optionName allrepos)
|
||||
( nubBy ((==) `on` fst3) <$> ((++) <$> getRemotes <*> getAll)
|
||||
, getRemotes
|
||||
)
|
||||
|
@ -58,10 +57,8 @@ getList = ifM (Annex.getFlag $ Option.name allrepos)
|
|||
return $ sortBy (comparing snd3) $
|
||||
filter (\t -> thd3 t /= DeadTrusted) rs3
|
||||
|
||||
startHeader :: [(UUID, RemoteName, TrustLevel)] -> CommandStart
|
||||
startHeader l = do
|
||||
liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l
|
||||
stop
|
||||
printHeader :: [(UUID, RemoteName, TrustLevel)] -> Annex ()
|
||||
printHeader l = liftIO $ putStrLn $ header $ map (\(_, n, t) -> (n, t)) l
|
||||
|
||||
start :: [(UUID, RemoteName, TrustLevel)] -> FilePath -> (Key, Backend) -> CommandStart
|
||||
start l file (key, _) = do
|
||||
|
|
|
@ -16,8 +16,10 @@ def :: [Command]
|
|||
def = [notDirect $ command "lock" paramPaths seek SectionCommon
|
||||
"undo unlock command"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFilesUnlocked start, withFilesUnlockedToBeCommitted start]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
withFilesUnlocked start ps
|
||||
withFilesUnlockedToBeCommitted start ps
|
||||
|
||||
start :: FilePath -> CommandStart
|
||||
start file = do
|
||||
|
|
|
@ -24,7 +24,6 @@ import qualified Annex.Branch
|
|||
import qualified Git
|
||||
import Git.Command
|
||||
import qualified Remote
|
||||
import qualified Option
|
||||
import qualified Annex
|
||||
|
||||
data RefChange = RefChange
|
||||
|
@ -44,25 +43,26 @@ options = passthruOptions ++ [gourceOption]
|
|||
|
||||
passthruOptions :: [Option]
|
||||
passthruOptions = map odate ["since", "after", "until", "before"] ++
|
||||
[ Option.field ['n'] "max-count" paramNumber
|
||||
[ fieldOption ['n'] "max-count" paramNumber
|
||||
"limit number of logs displayed"
|
||||
]
|
||||
where
|
||||
odate n = Option.field [] n paramDate $ "show log " ++ n ++ " date"
|
||||
odate n = fieldOption [] n paramDate $ "show log " ++ n ++ " date"
|
||||
|
||||
gourceOption :: Option
|
||||
gourceOption = Option.flag [] "gource" "format output for gource"
|
||||
gourceOption = flagOption [] "gource" "format output for gource"
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withValue Remote.uuidDescriptions $ \m ->
|
||||
withValue (liftIO getCurrentTimeZone) $ \zone ->
|
||||
withValue (concat <$> mapM getoption passthruOptions) $ \os ->
|
||||
withFlag gourceOption $ \gource ->
|
||||
withFilesInGit $ whenAnnexed $ start m zone os gource]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
m <- Remote.uuidDescriptions
|
||||
zone <- liftIO getCurrentTimeZone
|
||||
os <- concat <$> mapM getoption passthruOptions
|
||||
gource <- getOptionFlag gourceOption
|
||||
withFilesInGit (whenAnnexed $ start m zone os gource) ps
|
||||
where
|
||||
getoption o = maybe [] (use o) <$>
|
||||
Annex.getField (Option.name o)
|
||||
use o v = [Param ("--" ++ Option.name o), Param v]
|
||||
Annex.getField (optionName o)
|
||||
use o v = [Param ("--" ++ optionName o), Param v]
|
||||
|
||||
start :: M.Map UUID String -> TimeZone -> [CommandParam] -> Bool ->
|
||||
FilePath -> (Key, Backend) -> CommandStart
|
||||
|
|
|
@ -17,8 +17,8 @@ def = [notBareRepo $ noCommit $ noMessages $
|
|||
command "lookupkey" (paramRepeating paramFile) seek
|
||||
SectionPlumbing "looks up key used for file"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withStrings start]
|
||||
seek :: CommandSeek
|
||||
seek = withStrings start
|
||||
|
||||
start :: String -> CommandStart
|
||||
start file = do
|
||||
|
|
|
@ -31,8 +31,8 @@ def = [dontCheck repoExists $
|
|||
command "map" paramNothing seek SectionQuery
|
||||
"generate map of repositories"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = do
|
||||
|
|
|
@ -17,11 +17,10 @@ def :: [Command]
|
|||
def = [command "merge" paramNothing seek SectionMaintenance
|
||||
"automatically merge changes from remotes"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withNothing mergeBranch
|
||||
, withNothing mergeSynced
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
withNothing mergeBranch ps
|
||||
withNothing mergeSynced ps
|
||||
|
||||
mergeBranch :: CommandStart
|
||||
mergeBranch = do
|
||||
|
|
|
@ -22,8 +22,8 @@ def = [notDirect $
|
|||
command "migrate" paramPaths seek
|
||||
SectionUtility "switch data to different backend"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withFilesInGit $ whenAnnexed start]
|
||||
seek :: CommandSeek
|
||||
seek = withFilesInGit $ whenAnnexed start
|
||||
|
||||
start :: FilePath -> (Key, Backend) -> CommandStart
|
||||
start file (key, oldbackend) = do
|
||||
|
|
|
@ -9,34 +9,33 @@ module Command.Mirror where
|
|||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import GitAnnex.Options
|
||||
import qualified Command.Move
|
||||
import qualified Command.Drop
|
||||
import qualified Command.Get
|
||||
import qualified Remote
|
||||
import Annex.Content
|
||||
import qualified Annex
|
||||
import Config.NumCopies
|
||||
|
||||
def :: [Command]
|
||||
def = [withOptions (fromToOptions ++ keyOptions) $
|
||||
command "mirror" paramPaths seek
|
||||
SectionCommon "mirror content of files to/from another repository"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withField toOption Remote.byNameWithUUID $ \to ->
|
||||
withField fromOption Remote.byNameWithUUID $ \from ->
|
||||
withKeyOptions (startKey Nothing to from Nothing) $
|
||||
withFilesInGit $ whenAnnexed $ start to from
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
to <- getOptionField toOption Remote.byNameWithUUID
|
||||
from <- getOptionField fromOption Remote.byNameWithUUID
|
||||
withKeyOptions
|
||||
(startKey to from Nothing)
|
||||
(withFilesInGit $ whenAnnexed $ start to from)
|
||||
ps
|
||||
|
||||
start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart
|
||||
start to from file (key, _backend) = do
|
||||
numcopies <- numCopies file
|
||||
startKey numcopies to from (Just file) key
|
||||
start to from file (key, _backend) = startKey to from (Just file) key
|
||||
|
||||
startKey :: Maybe Int -> Maybe Remote -> Maybe Remote -> Maybe FilePath -> Key -> CommandStart
|
||||
startKey numcopies to from afile key = do
|
||||
startKey :: Maybe Remote -> Maybe Remote -> Maybe FilePath -> Key -> CommandStart
|
||||
startKey to from afile key = do
|
||||
noAuto
|
||||
case (from, to) of
|
||||
(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"
|
||||
mirrorto r = ifM (inAnnex 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
|
||||
haskey <- Remote.hasKey r key
|
||||
|
@ -56,6 +57,9 @@ startKey numcopies to from afile key = do
|
|||
Left _ -> stop
|
||||
Right True -> Command.Get.start' (return True) Nothing key afile
|
||||
Right False -> ifM (inAnnex key)
|
||||
( Command.Drop.startLocal afile numcopies key Nothing
|
||||
( do
|
||||
numcopies <- getnumcopies
|
||||
Command.Drop.startLocal afile numcopies key Nothing
|
||||
, stop
|
||||
)
|
||||
getnumcopies = maybe getNumCopies getFileNumCopies afile
|
||||
|
|
|
@ -16,8 +16,6 @@ import qualified Remote
|
|||
import Annex.UUID
|
||||
import Logs.Presence
|
||||
import Logs.Transfer
|
||||
import GitAnnex.Options
|
||||
import Types.Key
|
||||
|
||||
def :: [Command]
|
||||
def = [withOptions moveOptions $ command "move" paramPaths seek
|
||||
|
@ -26,13 +24,14 @@ def = [withOptions moveOptions $ command "move" paramPaths seek
|
|||
moveOptions :: [Option]
|
||||
moveOptions = fromToOptions ++ keyOptions
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
[ withField toOption Remote.byNameWithUUID $ \to ->
|
||||
withField fromOption Remote.byNameWithUUID $ \from ->
|
||||
withKeyOptions (startKey to from True) $
|
||||
withFilesInGit $ whenAnnexed $ start to from True
|
||||
]
|
||||
seek :: CommandSeek
|
||||
seek ps = do
|
||||
to <- getOptionField toOption Remote.byNameWithUUID
|
||||
from <- getOptionField fromOption Remote.byNameWithUUID
|
||||
withKeyOptions
|
||||
(startKey to from True)
|
||||
(withFilesInGit $ whenAnnexed $ start to from True)
|
||||
ps
|
||||
|
||||
start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> (Key, Backend) -> CommandStart
|
||||
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"
|
||||
|
||||
showMoveAction :: Bool -> Key -> AssociatedFile -> Annex ()
|
||||
showMoveAction True _ (Just file) = showStart "move" file
|
||||
showMoveAction False _ (Just file) = showStart "copy" file
|
||||
showMoveAction True key Nothing = showStart "move" (key2file key)
|
||||
showMoveAction False key Nothing = showStart "copy" (key2file key)
|
||||
showMoveAction move = showStart' (if move then "move" else "copy")
|
||||
|
||||
{- Moves (or copies) the content of an annexed file to a remote.
|
||||
-
|
||||
- If the remote already has the content, it is still removed from
|
||||
- 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
|
||||
- allow it to be dropped.
|
||||
-}
|
||||
|
|
56
Command/NumCopies.hs
Normal file
56
Command/NumCopies.hs
Normal 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
|
|
@ -9,6 +9,7 @@ module Command.PreCommit where
|
|||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import Config
|
||||
import qualified Command.Add
|
||||
import qualified Command.Fix
|
||||
import Annex.Direct
|
||||
|
@ -17,19 +18,20 @@ def :: [Command]
|
|||
def = [command "pre-commit" paramPaths seek SectionPlumbing
|
||||
"run by git pre-commit hook"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek =
|
||||
-- fix symlinks to files being committed
|
||||
[ whenNotDirect $ withFilesToBeCommitted $ whenAnnexed Command.Fix.start
|
||||
-- inject unlocked files into the annex
|
||||
, whenNotDirect $ withFilesUnlockedToBeCommitted startIndirect
|
||||
seek :: CommandSeek
|
||||
seek ps = ifM isDirect
|
||||
-- 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 file = next $ do
|
||||
unlessM (doCommand $ Command.Add.start file) $
|
||||
unlessM (callCommandAction $ Command.Add.start file) $
|
||||
error $ "failed to add " ++ file ++ "; canceling commit"
|
||||
next $ return True
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ def = [notDirect $ command "rekey"
|
|||
(paramOptional $ paramRepeating $ paramPair paramPath paramKey)
|
||||
seek SectionPlumbing "change keys used for files"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withPairs start]
|
||||
seek :: CommandSeek
|
||||
seek = withPairs start
|
||||
|
||||
start :: (FilePath, String) -> CommandStart
|
||||
start (file, keyname) = ifAnnexed file go stop
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
module Command.RecvKey where
|
||||
|
||||
import System.PosixCompat.Files
|
||||
|
||||
import Common.Annex
|
||||
import Command
|
||||
import CmdLine
|
||||
|
@ -17,7 +15,7 @@ import Annex
|
|||
import Utility.Rsync
|
||||
import Logs.Transfer
|
||||
import Command.SendKey (fieldTransfer)
|
||||
import qualified Fields
|
||||
import qualified CmdLine.GitAnnexShell.Fields as Fields
|
||||
import qualified Types.Key
|
||||
import qualified Types.Backend
|
||||
import qualified Backend
|
||||
|
@ -26,8 +24,8 @@ def :: [Command]
|
|||
def = [noCommit $ command "recvkey" paramKey seek
|
||||
SectionPlumbing "runs rsync in server mode to receive content"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withKeys start]
|
||||
seek :: CommandSeek
|
||||
seek = withKeys start
|
||||
|
||||
start :: Key -> CommandStart
|
||||
start key = ifM (inAnnex key)
|
||||
|
|
|
@ -17,8 +17,8 @@ def :: [Command]
|
|||
def = [command "reinject" (paramPair "SRC" "DEST") seek
|
||||
SectionUtility "sets content of annexed file"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withWords start]
|
||||
seek :: CommandSeek
|
||||
seek = withWords start
|
||||
|
||||
start :: [FilePath] -> CommandStart
|
||||
start (src:dest:[])
|
||||
|
|
|
@ -20,8 +20,8 @@ def :: [Command]
|
|||
def = [noCommit $ dontCheck repoExists $
|
||||
command "repair" paramNothing seek SectionMaintenance "recover broken git repository"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withNothing start]
|
||||
seek :: CommandSeek
|
||||
seek = withNothing start
|
||||
|
||||
start :: CommandStart
|
||||
start = next $ next $ runRepair =<< Annex.getState Annex.force
|
||||
|
|
|
@ -16,8 +16,8 @@ def = [notBareRepo $
|
|||
command "rmurl" (paramPair paramFile paramUrl) seek
|
||||
SectionCommon "record file is not available at url"]
|
||||
|
||||
seek :: [CommandSeek]
|
||||
seek = [withPairs start]
|
||||
seek :: CommandSeek
|
||||
seek = withPairs start
|
||||
|
||||
start :: (FilePath, String) -> CommandStart
|
||||
start (file, url) = flip whenAnnexed file $ \_ (key, _) -> do
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue