git-annex 8.20200908 → 8.20201007
raw patch · 158 files changed
+2007/−1195 lines, 158 files
Files
- Annex.hs +5/−2
- Annex/Action.hs +0/−24
- Annex/Branch.hs +11/−8
- Annex/CheckIgnore.hs +10/−3
- Annex/Concurrent.hs +19/−10
- Annex/Concurrent/Utility.hs +8/−0
- Annex/Drop.hs +4/−4
- Annex/FileMatcher.hs +12/−6
- Annex/Import.hs +179/−97
- Annex/Ingest.hs +38/−31
- Annex/Magic.hs +16/−2
- Annex/PidLock.hs +1/−1
- Annex/SpecialRemote.hs +3/−3
- Annex/Ssh.hs +3/−2
- Annex/Transfer.hs +3/−4
- Annex/YoutubeDl.hs +61/−14
- Assistant/Drop.hs +5/−1
- Assistant/Threads/Committer.hs +2/−1
- Assistant/Threads/TransferScanner.hs +2/−1
- Assistant/Threads/Watcher.hs +1/−1
- Assistant/WebApp/Configurators/AWS.hs +2/−2
- Assistant/WebApp/Configurators/Delete.hs +1/−1
- Assistant/WebApp/Configurators/Edit.hs +5/−5
- Assistant/WebApp/Configurators/IA.hs +1/−1
- Assistant/WebApp/Configurators/Ssh.hs +3/−3
- Assistant/WebApp/Configurators/WebDAV.hs +1/−1
- Assistant/WebApp/Gpg.hs +1/−1
- Assistant/WebApp/RepoList.hs +1/−1
- Benchmark.hs +0/−5
- CHANGELOG +49/−0
- CmdLine/Action.hs +12/−16
- CmdLine/Batch.hs +51/−33
- CmdLine/GitAnnex/Options.hs +1/−1
- CmdLine/Seek.hs +143/−107
- Command.hs +6/−4
- Command/Add.hs +44/−34
- Command/AddUnused.hs +3/−1
- Command/AddUrl.hs +57/−47
- Command/Adjust.hs +1/−1
- Command/CalcKey.hs +2/−2
- Command/CheckPresentKey.hs +2/−1
- Command/Commit.hs +7/−4
- Command/Config.hs +11/−3
- Command/ContentLocation.hs +2/−2
- Command/Copy.hs +3/−3
- Command/Dead.hs +2/−2
- Command/Describe.hs +4/−1
- Command/Drop.hs +14/−14
- Command/DropKey.hs +2/−2
- Command/EnableRemote.hs +9/−3
- Command/EnableTor.hs +3/−1
- Command/ExamineKey.hs +2/−2
- Command/Expire.hs +4/−2
- Command/Export.hs +22/−9
- Command/Find.hs +5/−5
- Command/Fix.hs +3/−3
- Command/Forget.hs +5/−1
- Command/FromKey.hs +10/−7
- Command/Fsck.hs +9/−9
- Command/FuzzTest.hs +1/−1
- Command/GCryptSetup.hs +1/−1
- Command/Get.hs +8/−8
- Command/Group.hs +5/−2
- Command/GroupWanted.hs +4/−1
- Command/Import.hs +40/−27
- Command/ImportFeed.hs +37/−28
- Command/InAnnex.hs +2/−2
- Command/Info.hs +29/−28
- Command/Init.hs +8/−4
- Command/InitRemote.hs +19/−18
- Command/Inprogress.hs +2/−2
- Command/List.hs +2/−2
- Command/Lock.hs +4/−4
- Command/Log.hs +2/−2
- Command/LookupKey.hs +2/−2
- Command/Map.hs +1/−1
- Command/MatchExpression.hs +6/−5
- Command/Merge.hs +9/−2
- Command/MetaData.hs +10/−10
- Command/Migrate.hs +3/−3
- Command/Mirror.hs +8/−8
- Command/Move.hs +22/−22
- Command/Multicast.hs +10/−6
- Command/NumCopies.hs +4/−1
- Command/P2P.hs +8/−3
- Command/PreCommit.hs +10/−4
- Command/ReKey.hs +11/−7
- Command/RecvKey.hs +2/−2
- Command/RegisterUrl.hs +6/−4
- Command/Reinit.hs +1/−1
- Command/Reinject.hs +7/−3
- Command/RenameRemote.hs +5/−4
- Command/Repair.hs +1/−1
- Command/ResolveMerge.hs +1/−1
- Command/RmUrl.hs +5/−5
- Command/Schedule.hs +4/−2
- Command/SendKey.hs +2/−2
- Command/SetKey.hs +4/−1
- Command/SetPresentKey.hs +6/−5
- Command/Sync.hs +76/−41
- Command/TestRemote.hs +2/−1
- Command/TransferKey.hs +2/−2
- Command/Trust.hs +2/−1
- Command/Unannex.hs +3/−3
- Command/Undo.hs +4/−1
- Command/Ungroup.hs +1/−1
- Command/Uninit.hs +6/−1
- Command/Unlock.hs +5/−4
- Command/Unused.hs +2/−1
- Command/Upgrade.hs +2/−2
- Command/VAdd.hs +1/−1
- Command/VCycle.hs +1/−1
- Command/VFilter.hs +1/−1
- Command/VPop.hs +5/−1
- Command/View.hs +3/−1
- Command/Wanted.hs +4/−2
- Command/Whereis.hs +6/−6
- Config/GitConfig.hs +2/−2
- Database/Benchmark.hs +1/−2
- Git/Command.hs +1/−11
- Limit.hs +255/−90
- Limit/Wanted.hs +22/−6
- Logs/Location.hs +11/−6
- Logs/PreferredContent.hs +13/−2
- Logs/Remote.hs +16/−2
- Messages.hs +37/−29
- Messages/JSON.hs +9/−4
- NEWS +1/−1
- Remote.hs +37/−14
- Remote/BitTorrent.hs +3/−3
- Remote/GCrypt.hs +1/−1
- Remote/GitLFS.hs +2/−2
- Remote/HttpAlso.hs +3/−1
- Remote/List.hs +2/−2
- Remote/S3.hs +21/−20
- Remote/Web.hs +1/−3
- Test/Framework.hs +1/−3
- Types/Command.hs +9/−4
- Types/Concurrency.hs +5/−0
- Types/Export.hs +7/−1
- Types/FileMatcher.hs +40/−18
- Types/GitConfig.hs +9/−9
- Types/Import.hs +10/−2
- Upgrade/V7.hs +2/−1
- Utility/Matcher.hs +8/−3
- Utility/Metered.hs +42/−20
- Utility/Path.hs +23/−10
- Utility/Process.hs +18/−8
- Utility/Rsync.hs +3/−3
- Utility/Url.hs +30/−17
- doc/git-annex-add.mdwn +1/−1
- doc/git-annex-addurl.mdwn +6/−0
- doc/git-annex-import.mdwn +11/−23
- doc/git-annex-importfeed.mdwn +6/−0
- doc/git-annex-sync.mdwn +8/−6
- doc/git-annex.mdwn +2/−0
- git-annex.cabal +2/−2
- stack.yaml +2/−2
Annex.hs view
@@ -69,6 +69,7 @@ import Types.WorkerPool import Types.IndexFiles import Types.CatFileHandles+import Types.RemoteConfig import qualified Database.Keys.Handle as Keys import Utility.InodeCache import Utility.Url@@ -112,7 +113,7 @@ , backend :: Maybe (BackendA Annex) , remotes :: [Types.Remote.RemoteA Annex] , output :: MessageState- , concurrency :: Concurrency+ , concurrency :: ConcurrencySetting , force :: Bool , fast :: Bool , daemon :: Bool@@ -129,6 +130,7 @@ , uuiddescmap :: Maybe UUIDDescMap , preferredcontentmap :: Maybe (FileMatcherMap Annex) , requiredcontentmap :: Maybe (FileMatcherMap Annex)+ , remoteconfigmap :: Maybe (M.Map UUID RemoteConfig) , forcetrust :: TrustMap , trustmap :: Maybe TrustMap , groupmap :: Maybe GroupMap@@ -171,7 +173,7 @@ , backend = Nothing , remotes = [] , output = o- , concurrency = NonConcurrent+ , concurrency = ConcurrencyCmdLine NonConcurrent , force = False , fast = False , daemon = False@@ -188,6 +190,7 @@ , uuiddescmap = Nothing , preferredcontentmap = Nothing , requiredcontentmap = Nothing+ , remoteconfigmap = Nothing , forcetrust = M.empty , trustmap = Nothing , groupmap = Nothing
Annex/Action.hs view
@@ -5,20 +5,13 @@ - Licensed under the GNU AGPL version 3 or higher. -} -{-# LANGUAGE CPP #-}- module Annex.Action ( startup, shutdown, stopCoProcesses,- reapZombies, ) where import qualified Data.Map as M-#ifndef mingw32_HOST_OS-import System.Posix.Process (getAnyProcessStatus)-import Utility.Exception-#endif import Annex.Common import qualified Annex@@ -38,7 +31,6 @@ saveState nocommit sequence_ =<< M.elems <$> Annex.getState Annex.cleanup stopCoProcesses- liftIO reapZombies -- zombies from long-running git processes {- Stops all long-running git query processes. -} stopCoProcesses :: Annex ()@@ -47,19 +39,3 @@ checkAttrStop hashObjectStop checkIgnoreStop--{- Reaps any zombie processes that may be hanging around.- -- - Warning: Not thread safe. Anything that was expecting to wait- - on a process and get back an exit status is going to be confused- - if this reap gets there first. -}-reapZombies :: IO ()-#ifndef mingw32_HOST_OS-reapZombies =- -- throws an exception when there are no child processes- catchDefaultIO Nothing (getAnyProcessStatus False True)- >>= maybe (return ()) (const reapZombies)--#else-reapZombies = return ()-#endif
Annex/Branch.hs view
@@ -393,24 +393,26 @@ {- Lists all files on the branch. including ones in the journal - that have not been committed yet. There may be duplicates in the list. -}-files :: Annex [RawFilePath]+files :: Annex ([RawFilePath], IO Bool) files = do _ <- update+ (bfs, cleanup) <- branchFiles -- ++ forces the content of the first list to be buffered in memory, -- so use getJournalledFilesStale which should be much smaller most -- of the time. branchFiles will stream as the list is consumed.- (++)+ l <- (++) <$> (map toRawFilePath <$> getJournalledFilesStale)- <*> branchFiles+ <*> pure bfs+ return (l, cleanup) {- Files in the branch, not including any from journalled changes, - and without updating the branch. -}-branchFiles :: Annex [RawFilePath]+branchFiles :: Annex ([RawFilePath], IO Bool) branchFiles = withIndex $ inRepo branchFiles' -branchFiles' :: Git.Repo -> IO [RawFilePath]-branchFiles' = Git.Command.pipeNullSplitZombie'- (lsTreeParams Git.LsTree.LsTreeRecursive fullname [Param "--name-only"])+branchFiles' :: Git.Repo -> IO ([RawFilePath], IO Bool)+branchFiles' = Git.Command.pipeNullSplit' $+ lsTreeParams Git.LsTree.LsTreeRecursive fullname [Param "--name-only"] {- Populates the branch's index file with the current branch contents. - @@ -620,10 +622,11 @@ remoteconfigmap <- calcRemoteConfigMap <$> getStaged remoteLog -- partially apply, improves performance let changers' = map (\c -> c config trustmap remoteconfigmap) changers- fs <- branchFiles+ (fs, cleanup) <- branchFiles forM_ fs $ \f -> do content <- getStaged f apply changers' f content+ liftIO $ void cleanup apply [] _ _ = return () apply (changer:rest) file content = case changer file content of PreserveFile -> apply rest file content
Annex/CheckIgnore.hs view
@@ -7,6 +7,7 @@ -} module Annex.CheckIgnore (+ CheckGitIgnore(..), checkIgnored, checkIgnoreStop, mkConcurrentCheckIgnoreHandle,@@ -19,9 +20,15 @@ import Types.Concurrency import Annex.Concurrent.Utility -checkIgnored :: FilePath -> Annex Bool-checkIgnored file = withCheckIgnoreHandle $ \h ->- liftIO $ Git.checkIgnored h file+newtype CheckGitIgnore = CheckGitIgnore Bool++checkIgnored :: CheckGitIgnore -> FilePath -> Annex Bool+checkIgnored (CheckGitIgnore False) _ = pure False+checkIgnored (CheckGitIgnore True) file =+ ifM (Annex.getState Annex.force)+ ( pure False+ , withCheckIgnoreHandle $ \h -> liftIO $ Git.checkIgnored h file+ ) withCheckIgnoreHandle :: (Git.CheckIgnoreHandle -> Annex a) -> Annex a withCheckIgnoreHandle a =
Annex/Concurrent.hs view
@@ -5,10 +5,14 @@ - Licensed under the GNU AGPL version 3 or higher. -} -module Annex.Concurrent where+module Annex.Concurrent (+ module Annex.Concurrent,+ module Annex.Concurrent.Utility+) where import Annex import Annex.Common+import Annex.Concurrent.Utility import qualified Annex.Queue import Annex.Action import Types.Concurrency@@ -22,19 +26,24 @@ import Control.Concurrent.STM import qualified Data.Map as M -setConcurrency :: Concurrency -> Annex ()-setConcurrency NonConcurrent = Annex.changeState $ \s -> s - { Annex.concurrency = NonConcurrent- }-setConcurrency c = do- cfh <- Annex.getState Annex.catfilehandles+setConcurrency :: ConcurrencySetting -> Annex ()+setConcurrency (ConcurrencyCmdLine s) = setConcurrency' s ConcurrencyCmdLine+setConcurrency (ConcurrencyGitConfig s) = setConcurrency' s ConcurrencyGitConfig++setConcurrency' :: Concurrency -> (Concurrency -> ConcurrencySetting) -> Annex ()+setConcurrency' NonConcurrent f = + Annex.changeState $ \s -> s + { Annex.concurrency = f NonConcurrent+ }+setConcurrency' c f = do+ cfh <- getState Annex.catfilehandles cfh' <- case cfh of CatFileHandlesNonConcurrent _ -> liftIO catFileHandlesPool CatFileHandlesPool _ -> pure cfh cah <- mkConcurrentCheckAttrHandle c cih <- mkConcurrentCheckIgnoreHandle c Annex.changeState $ \s -> s- { Annex.concurrency = c+ { Annex.concurrency = f c , Annex.catfilehandles = cfh' , Annex.checkattrhandle = Just cah , Annex.checkignorehandle = Just cih@@ -74,9 +83,9 @@ st <- Annex.getState id -- Make sure that concurrency is enabled, if it was not already, -- so the concurrency-safe resource pools are set up.- st' <- case Annex.concurrency st of+ st' <- case getConcurrency' (Annex.concurrency st) of NonConcurrent -> do- setConcurrency (Concurrent 1)+ setConcurrency (ConcurrencyCmdLine (Concurrent 1)) Annex.getState id _ -> return st return $ st'
Annex/Concurrent/Utility.hs view
@@ -7,9 +7,17 @@ module Annex.Concurrent.Utility where +import Annex import Types.Concurrency import GHC.Conc++getConcurrency :: Annex Concurrency+getConcurrency = getConcurrency' <$> getState concurrency++getConcurrency' :: ConcurrencySetting -> Concurrency+getConcurrency' (ConcurrencyCmdLine c) = c+getConcurrency' (ConcurrencyGitConfig c) = c {- Honor the requested level of concurrency, but only up to the number of - CPU cores. Useful for things that are known to be CPU bound. -}
Annex/Drop.hs view
@@ -47,8 +47,8 @@ - The runner is used to run CommandStart sequentially, it's typically - callCommandAction. -}-handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile -> [VerifiedCopy] -> (CommandStart -> CommandCleanup) -> Annex ()-handleDropsFrom locs rs reason fromhere key afile preverified runner = do+handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile -> SeekInput -> [VerifiedCopy] -> (CommandStart -> CommandCleanup) -> Annex ()+handleDropsFrom locs rs reason fromhere key afile si preverified runner = do g <- Annex.gitRepo l <- map (`fromTopFilePath` g) <$> Database.Keys.getAssociatedFiles key@@ -120,10 +120,10 @@ dropl fs n = checkdrop fs n Nothing $ \numcopies -> stopUnless (inAnnex key) $- Command.Drop.startLocal afile ai numcopies key preverified+ Command.Drop.startLocal afile ai si numcopies key preverified dropr fs r n = checkdrop fs n (Just $ Remote.uuid r) $ \numcopies ->- Command.Drop.startRemote afile ai numcopies key r+ Command.Drop.startRemote afile ai si numcopies key r ai = mkActionItem (key, afile)
Annex/FileMatcher.hs view
@@ -78,14 +78,14 @@ checkMatcher' :: FileMatcher Annex -> MatchInfo -> AssumeNotPresent -> Annex Bool checkMatcher' matcher mi notpresent =- matchMrun matcher $ \a -> a notpresent mi+ matchMrun matcher $ \o -> matchAction o notpresent mi fileMatchInfo :: RawFilePath -> Annex MatchInfo fileMatchInfo file = do matchfile <- getTopFilePath <$> inRepo (toTopFilePath file) return $ MatchingFile FileInfo { matchFile = matchfile- , currFile = file+ , contentFile = Just file } matchAll :: FileMatcher Annex@@ -195,9 +195,9 @@ commonKeylessTokens LimitDiskFiles ++ #ifdef WITH_MAGICMIME [ mimer "mimetype" $- matchMagic "mimetype" getMagicMimeType providedMimeType+ matchMagic "mimetype" getMagicMimeType providedMimeType userProvidedMimeType , mimer "mimeencoding" $- matchMagic "mimeencoding" getMagicMimeEncoding providedMimeEncoding+ matchMagic "mimeencoding" getMagicMimeEncoding providedMimeEncoding userProvidedMimeEncoding ] #else [ mimer "mimetype"@@ -264,6 +264,12 @@ usev a v = Operation <$> a v call :: Either String (FileMatcher Annex) -> ParseResult (MatchFiles Annex)-call (Right sub) = Right $ Operation $ \notpresent mi ->- matchMrun sub $ \a -> a notpresent mi+call (Right sub) = Right $ Operation $ MatchFiles+ { matchAction = \notpresent mi ->+ matchMrun sub $ \o -> matchAction o notpresent mi+ , matchNeedsFileName = any matchNeedsFileName sub+ , matchNeedsFileContent = any matchNeedsFileContent sub+ , matchNeedsKey = any matchNeedsKey sub+ , matchNeedsLocationLog = any matchNeedsLocationLog sub+ } call (Left err) = Left err
Annex/Import.hs view
@@ -12,10 +12,10 @@ ImportCommitConfig(..), buildImportCommit, buildImportTrees,+ canImportKeys, importKeys,- filterImportableContents, makeImportMatcher,- listImportableContents,+ getImportableContents, ) where import Annex.Common@@ -36,6 +36,7 @@ import Annex.RemoteTrackingBranch import Annex.HashObject import Annex.Transfer+import Annex.CheckIgnore import Command import Backend import Types.Key@@ -48,7 +49,7 @@ import Logs.PreferredContent import Types.FileMatcher import Annex.FileMatcher-import Utility.Matcher (isEmpty)+import qualified Utility.Matcher import qualified Database.Export as Export import qualified Database.ContentIdentifier as CIDDb import qualified Logs.ContentIdentifier as CIDLog@@ -57,7 +58,7 @@ import Control.Concurrent.STM import qualified Data.Map.Strict as M import qualified Data.Set as S-import qualified System.FilePath.Posix as Posix+import qualified System.FilePath.Posix.ByteString as Posix import qualified System.FilePath.ByteString as P {- Configures how to build an import tree. -}@@ -281,6 +282,12 @@ topf = asTopFilePath $ maybe lf (\sd -> getTopFilePath sd P.</> lf) msubdir +canImportKeys :: Remote -> Bool -> Bool+canImportKeys remote importcontent =+ importcontent || isJust (Remote.importKey ia)+ where+ ia = Remote.importActions remote+ {- Downloads all new ContentIdentifiers, or when importcontent is False, - generates Keys without downloading. -@@ -304,7 +311,7 @@ -> ImportableContents (ContentIdentifier, ByteSize) -> Annex (Maybe (ImportableContents (Either Sha Key))) importKeys remote importtreeconfig importcontent importablecontents = do- when (not importcontent && isNothing (Remote.importKey ia)) $+ unless (canImportKeys remote importcontent) $ giveup "This remote does not support importing without downloading content." -- This map is used to remember content identifiers that -- were just imported, before they have necessarily been@@ -364,7 +371,8 @@ [] -> do job <- liftIO $ newEmptyTMVarIO let ai = ActionItemOther (Just (fromRawFilePath (fromImportLocation loc)))- let importaction = starting ("import " ++ Remote.name remote) ai $ do+ let si = SeekInput []+ let importaction = starting ("import " ++ Remote.name remote) ai si $ do when oldversion $ showNote "old version" tryNonAsync (importordownload cidmap db i largematcher) >>= \case@@ -383,41 +391,115 @@ importaction return (Right job) - importordownload- | not importcontent = doimport- | otherwise = dodownload+ importordownload cidmap db (loc, (cid, sz)) largematcher= do+ f <- locworktreefile loc+ matcher <- largematcher (fromRawFilePath f)+ -- When importing a key is supported, always use it rather+ -- than downloading and retrieving a key, to avoid+ -- generating trees with different keys for the same content.+ let act = if importcontent+ then case Remote.importKey ia of+ Nothing -> dodownload+ Just _ -> if Utility.Matcher.introspect matchNeedsFileContent matcher+ then dodownload+ else doimport+ else doimport+ act cidmap db (loc, (cid, sz)) f matcher - doimport cidmap db (loc, (cid, sz)) _largematcher =+ doimport cidmap db (loc, (cid, sz)) f matcher = case Remote.importKey ia of Nothing -> error "internal" -- checked earlier- Just a -> do- let importer p = do- unsizedk <- a loc cid p- -- This avoids every remote needing- -- to add the size.- let k = alterKey unsizedk $ \kd -> kd- { keySize = keySize kd <|> Just sz }- checkSecureHashes k >>= \case- Nothing -> do- recordcidkey cidmap db cid k- logChange k (Remote.uuid remote) InfoPresent- return (Right k)- Just msg -> giveup (msg ++ " to import")- let runimport p = tryNonAsync (importer p) >>= \case- Right k -> return $ Just (loc, k)+ Just importkey -> do+ when (Utility.Matcher.introspect matchNeedsFileContent matcher) $+ giveup "annex.largefiles configuration examines file contents, so cannot import without content."+ let mi = MatchingInfo ProvidedInfo+ { providedFilePath = f+ , providedKey = Nothing+ , providedFileSize = sz+ , providedMimeType = Nothing+ , providedMimeEncoding = Nothing+ }+ islargefile <- checkMatcher' matcher mi mempty+ metered Nothing sz $ const $ if islargefile+ then doimportlarge importkey cidmap db loc cid sz f+ else doimportsmall cidmap db loc cid sz+ + doimportlarge importkey cidmap db loc cid sz f p =+ tryNonAsync importer >>= \case+ Right (Just (k, True)) -> return $ Just (loc, Right k)+ Right _ -> return Nothing+ Left e -> do+ warning (show e)+ return Nothing+ where+ importer = do+ unsizedk <- importkey loc cid+ -- Don't display progress when generating+ -- key, if the content will later be+ -- downloaded, which is a more expensive+ -- operation generally.+ (if importcontent then nullMeterUpdate else p)+ -- This avoids every remote needing+ -- to add the size.+ let k = alterKey unsizedk $ \kd -> kd+ { keySize = keySize kd <|> Just sz }+ checkSecureHashes k >>= \case+ Nothing -> do+ recordcidkey cidmap db cid k+ logChange k (Remote.uuid remote) InfoPresent+ if importcontent+ then getcontent k+ else return (Just (k, True))+ Just msg -> giveup (msg ++ " to import")+ + getcontent :: Key -> Annex (Maybe (Key, Bool))+ getcontent k = do+ let af = AssociatedFile (Just f)+ let downloader p' tmpfile = do+ k' <- Remote.retrieveExportWithContentIdentifier+ ia loc cid tmpfile + (pure k)+ (combineMeterUpdate p' p)+ ok <- moveAnnex k' tmpfile+ when ok $+ logStatus k InfoPresent+ return (Just (k', ok))+ checkDiskSpaceToGet k Nothing $+ notifyTransfer Download af $+ download (Remote.uuid remote) k af stdRetry $ \p' ->+ withTmp k $ downloader p'+ + -- The file is small, so is added to git, so while importing+ -- without content does not retrieve annexed files, it does+ -- need to retrieve this file.+ doimportsmall cidmap db loc cid sz p = do+ let downloader tmpfile = do+ k <- Remote.retrieveExportWithContentIdentifier+ ia loc cid tmpfile+ (mkkey tmpfile)+ p+ case keyGitSha k of+ Just sha -> do+ recordcidkey cidmap db cid k+ return sha+ Nothing -> error "internal"+ checkDiskSpaceToGet tmpkey Nothing $+ withTmp tmpkey $ \tmpfile ->+ tryNonAsync (downloader tmpfile) >>= \case+ Right sha -> return $ Just (loc, Left sha) Left e -> do warning (show e) return Nothing- metered Nothing sz $- const runimport+ where+ tmpkey = importKey cid sz+ mkkey tmpfile = gitShaKey <$> hashFile tmpfile - dodownload cidmap db (loc, (cid, sz)) largematcher = do- f <- locworktreefile loc+ dodownload cidmap db (loc, (cid, sz)) f matcher = do let af = AssociatedFile (Just f) let downloader tmpfile p = do k <- Remote.retrieveExportWithContentIdentifier ia loc cid tmpfile - (mkkey f tmpfile largematcher)+ (mkkey tmpfile) p case keyGitSha k of Nothing -> do@@ -445,25 +527,24 @@ where tmpkey = importKey cid sz - ia = Remote.importActions remote+ mkkey tmpfile = do+ let mi = MatchingFile FileInfo+ { matchFile = f+ , contentFile = Just (toRawFilePath tmpfile)+ }+ islargefile <- checkMatcher' matcher mi mempty+ if islargefile+ then do+ backend <- chooseBackend (fromRawFilePath f)+ let ks = KeySource+ { keyFilename = f+ , contentLocation = toRawFilePath tmpfile+ , inodeCache = Nothing+ }+ fst <$> genKey ks nullMeterUpdate backend+ else gitShaKey <$> hashFile tmpfile - mkkey f tmpfile largematcher = do- matcher <- largematcher (fromRawFilePath f)- let mi = MatchingFile FileInfo- { matchFile = f- , currFile = toRawFilePath tmpfile- }- islargefile <- checkMatcher' matcher mi mempty- if islargefile- then do- backend <- chooseBackend (fromRawFilePath f)- let ks = KeySource- { keyFilename = f- , contentLocation = toRawFilePath tmpfile- , inodeCache = Nothing- }- fst <$> genKey ks nullMeterUpdate backend- else gitShaKey <$> hashFile tmpfile+ ia = Remote.importActions remote locworktreefile loc = fromRepo $ fromTopFilePath $ asTopFilePath $ case importtreeconfig of@@ -536,61 +617,62 @@ where load t = M.lookup (Remote.uuid r) . fst <$> preferredRequiredMapsLoad' t -wantImport :: FileMatcher Annex -> ImportLocation -> ByteSize -> Annex Bool-wantImport matcher loc sz = checkMatcher' matcher mi mempty- where- mi = MatchingInfo $ ProvidedInfo- { providedFilePath = Right $ fromRawFilePath $ fromImportLocation loc- , providedKey = unavail "key"- , providedFileSize = Right sz- , providedMimeType = unavail "mime"- , providedMimeEncoding = unavail "mime"- }- -- This should never run, as long as the FileMatcher was generated- -- using the preferredContentKeylessTokens.- unavail v = Left $ error $ "Internal error: unavailable " ++ v--{- If a file is not preferred content, but it was previously exported or- - imported to the remote, not importing it would result in a remote- - tracking branch that, when merged, would delete the file.- -- - To avoid that problem, such files are included in the import.- - The next export will remove them from the remote.- -}-shouldImport :: Export.ExportHandle -> FileMatcher Annex -> ImportLocation -> ByteSize -> Annex Bool-shouldImport dbhandle matcher loc sz = - wantImport matcher loc sz- <||>- liftIO (not . null <$> Export.getExportTreeKey dbhandle loc)--filterImportableContents :: Remote -> FileMatcher Annex -> ImportableContents (ContentIdentifier, ByteSize) -> Annex (ImportableContents (ContentIdentifier, ByteSize))-filterImportableContents r matcher importable- | isEmpty matcher = return importable- | otherwise = do- dbhandle <- Export.openDb (Remote.uuid r)- go dbhandle importable- where- go dbhandle ic = ImportableContents- <$> filterM (match dbhandle) (importableContents ic)- <*> mapM (go dbhandle) (importableHistory ic)- - match dbhandle (loc, (_cid, sz)) = shouldImport dbhandle matcher loc sz- {- Gets the ImportableContents from the remote. - - Filters out any paths that include a ".git" component, because git does - not allow storing ".git" in a git repository. While it is possible to - write a git tree that contains that, git will complain and refuse to - check it out.+ -+ - Filters out new things not matching the FileMatcher or that are+ - gitignored. However, files that are already in git get imported+ - regardless. (Similar to how git add behaves on gitignored files.)+ - This avoids creating a remote tracking branch that, when merged,+ - would delete the files. -}-listImportableContents :: Remote -> Annex (Maybe (ImportableContents (ContentIdentifier, ByteSize)))-listImportableContents r = fmap removegitspecial- <$> Remote.listImportableContents (Remote.importActions r)+getImportableContents :: Remote -> ImportTreeConfig -> CheckGitIgnore -> FileMatcher Annex -> Annex (Maybe (ImportableContents (ContentIdentifier, ByteSize)))+getImportableContents r importtreeconfig ci matcher = + Remote.listImportableContents (Remote.importActions r) >>= \case+ Nothing -> return Nothing+ Just importable -> do+ dbhandle <- Export.openDb (Remote.uuid r)+ Just <$> filterunwanted dbhandle importable where- removegitspecial ic = ImportableContents- { importableContents = - filter (not . gitspecial . fst) (importableContents ic)- , importableHistory =- map removegitspecial (importableHistory ic)+ filterunwanted dbhandle ic = ImportableContents+ <$> filterM (wanted dbhandle) (importableContents ic)+ <*> mapM (filterunwanted dbhandle) (importableHistory ic)+ + wanted dbhandle (loc, (_cid, sz))+ | ingitdir = pure False+ | otherwise =+ isknown <||> (matches <&&> notignored)+ where+ -- Checks, from least to most expensive.+ ingitdir = ".git" `elem` Posix.splitDirectories (fromImportLocation loc)+ matches = matchesImportLocation matcher loc sz+ isknown = isKnownImportLocation dbhandle loc+ notignored = notIgnoredImportLocation importtreeconfig ci loc++isKnownImportLocation :: Export.ExportHandle -> ImportLocation -> Annex Bool+isKnownImportLocation dbhandle loc = liftIO $+ not . null <$> Export.getExportTreeKey dbhandle loc++matchesImportLocation :: FileMatcher Annex -> ImportLocation -> Integer -> Annex Bool+matchesImportLocation matcher loc sz = checkMatcher' matcher mi mempty+ where+ mi = MatchingInfo $ ProvidedInfo+ { providedFilePath = fromImportLocation loc+ , providedKey = Nothing+ , providedFileSize = sz+ , providedMimeType = Nothing+ , providedMimeEncoding = Nothing }- gitspecial l = ".git" `elem` Posix.splitDirectories (fromRawFilePath (fromImportLocation l))++notIgnoredImportLocation :: ImportTreeConfig -> CheckGitIgnore -> ImportLocation -> Annex Bool+notIgnoredImportLocation importtreeconfig ci loc = not <$> checkIgnored ci f+ where+ f = fromRawFilePath $ case importtreeconfig of+ ImportSubTree dir _ ->+ getTopFilePath dir P.</> fromImportLocation loc+ ImportTree ->+ fromImportLocation loc
Annex/Ingest.hs view
@@ -1,6 +1,6 @@ {- git-annex content ingestion -- - Copyright 2010-2019 Joey Hess <id@joeyh.name>+ - Copyright 2010-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -18,7 +18,8 @@ addLink, makeLink, addUnlocked,- forceParams,+ CheckGitIgnore(..),+ gitAddParams, addAnnexedFile, ) where @@ -31,6 +32,7 @@ import Annex.Link import Annex.MetaData import Annex.CurrentBranch+import Annex.CheckIgnore import Logs.Location import qualified Annex import qualified Annex.Queue@@ -125,19 +127,19 @@ {- Ingests a locked down file into the annex. Updates the work tree and - index. -}-ingestAdd :: MeterUpdate -> Maybe LockedDown -> Annex (Maybe Key)-ingestAdd meterupdate ld = ingestAdd' meterupdate ld Nothing+ingestAdd :: CheckGitIgnore -> MeterUpdate -> Maybe LockedDown -> Annex (Maybe Key)+ingestAdd ci meterupdate ld = ingestAdd' ci meterupdate ld Nothing -ingestAdd' :: MeterUpdate -> Maybe LockedDown -> Maybe Key -> Annex (Maybe Key)-ingestAdd' _ Nothing _ = return Nothing-ingestAdd' meterupdate ld@(Just (LockedDown cfg source)) mk = do+ingestAdd' :: CheckGitIgnore -> MeterUpdate -> Maybe LockedDown -> Maybe Key -> Annex (Maybe Key)+ingestAdd' _ _ Nothing _ = return Nothing+ingestAdd' ci meterupdate ld@(Just (LockedDown cfg source)) mk = do (mk', mic) <- ingest meterupdate ld mk case mk' of Nothing -> return Nothing Just k -> do let f = keyFilename source if lockingFile cfg- then addLink (fromRawFilePath f) k mic+ then addLink ci (fromRawFilePath f) k mic else do mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus (contentLocation source)@@ -292,23 +294,28 @@ - Also, using git add allows it to skip gitignored files, unless forced - to include them. -}-addLink :: FilePath -> Key -> Maybe InodeCache -> Annex ()-addLink file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig)+addLink :: CheckGitIgnore -> FilePath -> Key -> Maybe InodeCache -> Annex ()+addLink ci file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig) ( do _ <- makeLink file key mcache- ps <- forceParams+ ps <- gitAddParams ci Annex.Queue.addCommand "add" (ps++[Param "--"]) [file] , do l <- makeLink file key mcache addAnnexLink l (toRawFilePath file) ) -{- Parameters to pass to git add, forcing addition of ignored files. -}-forceParams :: Annex [CommandParam]-forceParams = ifM (Annex.getState Annex.force)+{- Parameters to pass to git add, forcing addition of ignored files.+ -+ - Note that, when git add is being run on an ignored file that is already+ - checked in, CheckGitIgnore True has no effect.+ -}+gitAddParams :: CheckGitIgnore -> Annex [CommandParam]+gitAddParams (CheckGitIgnore True) = ifM (Annex.getState Annex.force) ( return [Param "-f"] , return [] )+gitAddParams (CheckGitIgnore False) = return [Param "-f"] {- Whether a file should be added unlocked or not. Default is to not, - unless symlinks are not supported. annex.addunlocked can override that.@@ -332,15 +339,15 @@ - - When the content of the key is not accepted into the annex, returns False. -}-addAnnexedFile :: AddUnlockedMatcher -> FilePath -> Key -> Maybe FilePath -> Annex Bool-addAnnexedFile matcher file key mtmp = ifM (addUnlocked matcher mi)+addAnnexedFile :: CheckGitIgnore -> AddUnlockedMatcher -> FilePath -> Key -> Maybe FilePath -> Annex Bool+addAnnexedFile ci matcher file key mtmp = ifM (addUnlocked matcher mi) ( do mode <- maybe (pure Nothing) (\tmp -> liftIO $ catchMaybeIO $ fileMode <$> getFileStatus tmp) mtmp- stagePointerFile (toRawFilePath file) mode =<< hashPointerFile key- Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath (toRawFilePath file))+ stagePointerFile file' mode =<< hashPointerFile key+ Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file') case mtmp of Just tmp -> ifM (moveAnnex key tmp) ( linkunlocked mode >> return True@@ -351,7 +358,7 @@ , writepointer mode >> return True ) , do- addLink file key Nothing+ addLink ci file key Nothing case mtmp of Just tmp -> moveAnnex key tmp Nothing -> return True@@ -359,24 +366,24 @@ where mi = case mtmp of Just tmp -> MatchingFile $ FileInfo- { currFile = toRawFilePath tmp- , matchFile = toRawFilePath file+ { contentFile = Just (toRawFilePath tmp)+ , matchFile = file' } -- Provide as much info as we can without access to the- -- file's content. It's better to provide wrong info- -- than for an operation to fail just because it can't- -- tell if a file should be unlocked or locked.+ -- file's content. Nothing -> MatchingInfo $ ProvidedInfo- { providedFilePath = Right file- , providedKey = Right key- , providedFileSize = Right $ fromMaybe 0 $+ { providedFilePath = file'+ , providedKey = Just key+ , providedFileSize = fromMaybe 0 $ keySize `fromKey` key- , providedMimeType = Right "application/octet-stream"- , providedMimeEncoding = Right "binary"+ , providedMimeType = Nothing+ , providedMimeEncoding = Nothing } linkunlocked mode = linkFromAnnex key file mode >>= \case LinkAnnexFailed -> liftIO $- writePointerFile (toRawFilePath file) key mode+ writePointerFile file' key mode _ -> return ()- writepointer mode = liftIO $ writePointerFile (toRawFilePath file) key mode+ writepointer mode = liftIO $ writePointerFile file' key mode++ file' = toRawFilePath file
Annex/Magic.hs view
@@ -1,6 +1,6 @@ {- Interface to libmagic -- - Copyright 2019 Joey Hess <id@joeyh.name>+ - Copyright 2019-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -21,6 +21,8 @@ #ifdef WITH_MAGICMIME import Magic import Utility.Env+import Control.Concurrent+import System.IO.Unsafe (unsafePerformIO) import Common #else type Magic = ()@@ -41,7 +43,7 @@ getMagicMime :: Magic -> FilePath -> IO (Maybe (MimeType, MimeEncoding)) #ifdef WITH_MAGICMIME-getMagicMime m f = Just . parse <$> magicFile m f+getMagicMime m f = Just . parse <$> magicConcurrentSafe (magicFile m f) where parse s = let (mimetype, rest) = separate (== ';') s@@ -58,3 +60,15 @@ getMagicMimeEncoding :: MonadIO m => Magic -> FilePath -> m(Maybe MimeEncoding) getMagicMimeEncoding m f = liftIO $ fmap snd <$> getMagicMime m f++#ifdef WITH_MAGICMIME+{-# NOINLINE mutex #-}+mutex :: MVar ()+mutex = unsafePerformIO $ newMVar ()++-- Work around a bug, the library is not concurrency safe and will+-- sometimes access the wrong memory if multiple ones are called at the+-- same time.+magicConcurrentSafe :: IO a -> IO a+magicConcurrentSafe = bracket_ (takeMVar mutex) (putMVar mutex ())+#endif
Annex/PidLock.hs view
@@ -67,7 +67,7 @@ let p' = p { env = Just ((v, PidF.pidLockEnvValue) : baseenv) } withCreateProcess p' a #else- gonopidlock+ liftIO gonopidlock #endif {- Wrap around actions that may run a git-annex child process via a git
Annex/SpecialRemote.hs view
@@ -36,7 +36,7 @@ headMaybe . sortBy (comparing $ \(u, _, _) -> Down $ M.lookup u t) . findByRemoteConfig (\c -> lookupName c == Just name)- <$> Logs.Remote.readRemoteLog+ <$> Logs.Remote.remoteConfigMap newConfig :: RemoteName@@ -56,7 +56,7 @@ specialRemoteMap :: Annex (M.Map UUID RemoteName) specialRemoteMap = do- m <- Logs.Remote.readRemoteLog+ m <- Logs.Remote.remoteConfigMap return $ M.fromList $ mapMaybe go (M.toList m) where go (u, c) = case lookupName c of@@ -79,7 +79,7 @@ autoEnable :: Annex () autoEnable = do- remotemap <- M.filter configured <$> readRemoteLog+ remotemap <- M.filter configured <$> remoteConfigMap enabled <- getenabledremotes forM_ (M.toList remotemap) $ \(cu, c) -> unless (cu `M.member` enabled) $ do let u = case findSameasUUID c of
Annex/Ssh.hs view
@@ -34,6 +34,7 @@ import Utility.Env import Utility.Hash import Types.CleanupActions+import Annex.Concurrent.Utility import Types.Concurrency import Git.Env import Git.Ssh@@ -107,7 +108,7 @@ -- No connection caching with concurrency is not a good -- combination, so warn the user. go (Left whynocaching) = do- Annex.getState Annex.concurrency >>= \case+ getConcurrency >>= \case NonConcurrent -> return () Concurrent {} -> warnnocaching whynocaching ConcurrentPerCpu -> warnnocaching whynocaching@@ -229,7 +230,7 @@ let socketlock = socket2lock socketfile - Annex.getState Annex.concurrency >>= \case+ getConcurrency >>= \case NonConcurrent -> return () Concurrent {} -> makeconnection socketlock ConcurrentPerCpu -> makeconnection socketlock
Annex/Transfer.hs view
@@ -31,6 +31,7 @@ import Types.Key import qualified Types.Remote as Remote import Types.Concurrency+import Annex.Concurrent.Utility import Types.WorkerPool import Annex.WorkerPool import Backend (isCryptographicallySecure)@@ -73,9 +74,7 @@ {- Like runTransfer, but ignores any existing transfer lock file for the - transfer, allowing re-running a transfer that is already in progress.- -- - Note that this may result in confusing progress meter display in the- - webapp, if multiple processes are writing to the transfer info file. -}+ -} alwaysRunTransfer :: Observable v => Transfer -> AssociatedFile -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v alwaysRunTransfer = runTransfer' True @@ -262,7 +261,7 @@ - increase total transfer speed. -} pickRemote :: Observable v => [Remote] -> (Remote -> Annex v) -> Annex v-pickRemote l a = debugLocks $ go l =<< Annex.getState Annex.concurrency+pickRemote l a = debugLocks $ go l =<< getConcurrency where go [] _ = return observeFailure go (r:[]) _ = a r
Annex/YoutubeDl.hs view
@@ -1,6 +1,6 @@ {- youtube-dl integration for git-annex -- - Copyright 2017-2018 Joey Hess <id@joeyh.name>+ - Copyright 2017-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -21,10 +21,15 @@ import Utility.DiskFree import Utility.HtmlDetect import Utility.Process.Transcript+import Utility.Metered+import Utility.DataUnits+import Messages.Progress import Logs.Transfer import Network.URI import Control.Concurrent.Async+import Data.Char+import Text.Read -- youtube-dl can follow redirects to anywhere, including potentially -- localhost or a private address. So, it's only allowed to download@@ -43,6 +48,8 @@ -- Runs youtube-dl in a work directory, to download a single media file -- from the url. Reutrns the path to the media file in the work directory. --+-- Displays a progress meter as youtube-dl downloads.+-- -- If youtube-dl fails without writing any files to the work directory, -- or is not installed, returns Right Nothing. --@@ -53,14 +60,14 @@ -- -- (Note that we can't use --output to specifiy the file to download to, -- due to <https://github.com/rg3/youtube-dl/issues/14864>)-youtubeDl :: URLString -> FilePath -> Annex (Either String (Maybe FilePath))-youtubeDl url workdir = ifM ipAddressesUnlimited- ( withUrlOptions $ youtubeDl' url workdir+youtubeDl :: URLString -> FilePath -> MeterUpdate -> Annex (Either String (Maybe FilePath))+youtubeDl url workdir p = ifM ipAddressesUnlimited+ ( withUrlOptions $ youtubeDl' url workdir p , return $ Left youtubeDlNotAllowedMessage ) -youtubeDl' :: URLString -> FilePath -> UrlOptions -> Annex (Either String (Maybe FilePath))-youtubeDl' url workdir uo+youtubeDl' :: URLString -> FilePath -> MeterUpdate -> UrlOptions -> Annex (Either String (Maybe FilePath))+youtubeDl' url workdir p uo | supportedScheme uo url = ifM (liftIO $ inPath "youtube-dl") ( runcmd >>= \case Right True -> workdirfiles >>= \case@@ -81,11 +88,17 @@ runcmd = youtubeDlMaxSize workdir >>= \case Left msg -> return (Left msg) Right maxsize -> do- quiet <- commandProgressDisabled- opts <- youtubeDlOpts $ dlopts ++ maxsize ++ - if quiet then [ Param "--quiet" ] else []- ok <- liftIO $ boolSystem' "youtube-dl" opts $- \p -> p { cwd = Just workdir }+ opts <- youtubeDlOpts (dlopts ++ maxsize)+ oh <- mkOutputHandlerQuiet+ -- The size is unknown to start. Once youtube-dl+ -- outputs some progress, the meter will be updated+ -- with the size, which is why it's important the+ -- meter is passed into commandMeter'+ let unknownsize = Nothing :: Maybe FileSize+ ok <- metered (Just p) unknownsize $ \meter meterupdate ->+ liftIO $ commandMeter' + parseYoutubeDlProgress oh (Just meter) meterupdate "youtube-dl" opts+ (\pr -> pr { cwd = Just workdir }) return (Right ok) dlopts = [ Param url@@ -125,10 +138,10 @@ ) -- Download a media file to a destination, -youtubeDlTo :: Key -> URLString -> FilePath -> Annex Bool-youtubeDlTo key url dest = do+youtubeDlTo :: Key -> URLString -> FilePath -> MeterUpdate -> Annex Bool+youtubeDlTo key url dest p = do res <- withTmpWorkDir key $ \workdir ->- youtubeDl url workdir >>= \case+ youtubeDl url workdir p >>= \case Right (Just mediafile) -> do liftIO $ renameFile mediafile dest return (Just True)@@ -239,3 +252,37 @@ -- involving youtube-dl in a ftp download "ftp:" -> False _ -> allowedScheme uo u++{- Strategy: Look for chunks prefixed with \r, which look approximately+ - like this:+ - "ESC[K[download] 26.6% of 60.22MiB at 254.69MiB/s ETA 00:00"+ - Look at the number before "% of " and the number and unit after,+ - to determine the number of bytes.+ -}+parseYoutubeDlProgress :: ProgressParser+parseYoutubeDlProgress = go [] . reverse . progresschunks+ where+ delim = '\r'++ progresschunks = drop 1 . splitc delim++ go remainder [] = (Nothing, Nothing, remainder)+ go remainder (x:xs) = case split "% of " x of+ (p:r:[]) -> case (parsepercent p, parsebytes r) of+ (Just percent, Just total) ->+ ( Just (toBytesProcessed (calc percent total))+ , Just (TotalSize total)+ , remainder+ )+ _ -> go (delim:x++remainder) xs+ _ -> go (delim:x++remainder) xs++ calc :: Double -> Integer -> Integer+ calc percent total = round (percent * fromIntegral total / 100)++ parsepercent :: String -> Maybe Double+ parsepercent = readMaybe . reverse . takeWhile (not . isSpace) . reverse++ parsebytes = readSize units . takeWhile (not . isSpace)++ units = memoryUnits ++ storageUnits
Assistant/Drop.hs view
@@ -16,6 +16,7 @@ import Logs.Location import CmdLine.Action import Types.NumCopies+import Types.Command {- Drop from local and/or remote when allowed by the preferred content and - numcopies settings. -}@@ -23,4 +24,7 @@ handleDrops reason fromhere key f preverified = do syncrs <- syncDataRemotes <$> getDaemonStatus locs <- liftAnnex $ loggedLocations key- liftAnnex $ handleDropsFrom locs syncrs reason fromhere key f preverified callCommandAction+ liftAnnex $ handleDropsFrom+ locs syncrs reason fromhere key f+ (SeekInput [])+ preverified callCommandAction
Assistant/Threads/Committer.hs view
@@ -24,6 +24,7 @@ import qualified Utility.Lsof as Lsof import qualified Utility.DirWatcher as DirWatcher import Types.KeySource+import Types.Command import Config import Annex.Content import Annex.Ingest@@ -286,7 +287,7 @@ ks = keySource ld doadd = sanitycheck ks $ do (mkey, _mcache) <- liftAnnex $ do- showStart "add" $ keyFilename ks+ showStart "add" (keyFilename ks) (SeekInput []) ingest nullMeterUpdate (Just $ LockedDown lockdownconfig ks) Nothing maybe (failedingest change) (done change $ fromRawFilePath $ keyFilename ks) mkey add _ _ = return Nothing
Assistant/Threads/TransferScanner.hs view
@@ -30,6 +30,7 @@ import Annex.Content import Annex.Wanted import CmdLine.Action+import Types.Command import qualified Data.Set as S import Control.Concurrent@@ -168,7 +169,7 @@ liftAnnex $ handleDropsFrom locs syncrs "expensive scan found too many copies of object"- present key af [] callCommandAction+ present key af (SeekInput []) [] callCommandAction ts <- if present then liftAnnex . filterM (wantSend True (Just key) af . Remote.uuid . fst) =<< use syncDataRemotes (genTransfer Upload False)
Assistant/Threads/Watcher.hs view
@@ -169,7 +169,7 @@ ig _ = False unlessIgnored :: FilePath -> Assistant (Maybe Change) -> Assistant (Maybe Change)-unlessIgnored file a = ifM (liftAnnex $ checkIgnored file)+unlessIgnored file a = ifM (liftAnnex $ checkIgnored (CheckGitIgnore True) file) ( noChange , a )
Assistant/WebApp/Configurators/AWS.hs view
@@ -154,7 +154,7 @@ getEnableS3R :: UUID -> Handler Html getEnableS3R uuid = do- m <- liftAnnex readRemoteLog+ m <- liftAnnex remoteConfigMap isia <- case M.lookup uuid m of Just c -> liftAnnex $ do pc <- parsedRemoteConfig S3.remote c@@ -180,7 +180,7 @@ runFormPostNoToken $ renderBootstrap3 bootstrapFormLayout $ awsCredsAForm defcreds case result of FormSuccess creds -> liftH $ do- m <- liftAnnex readRemoteLog+ m <- liftAnnex remoteConfigMap let name = fromJust $ lookupName $ fromJust $ M.lookup uuid m makeAWSRemote enableSpecialRemote remotetype SmallArchiveGroup creds name M.empty
Assistant/WebApp/Configurators/Delete.hs view
@@ -59,7 +59,7 @@ reponame <- liftAnnex $ Remote.prettyUUID uuid {- If it's not listed in the remote log, it must be a git repo. -}- gitrepo <- liftAnnex $ M.notMember uuid <$> readRemoteLog+ gitrepo <- liftAnnex $ M.notMember uuid <$> remoteConfigMap $(widgetFile "configurators/delete/finished") getDeleteCurrentRepositoryR :: Handler Html
Assistant/WebApp/Configurators/Edit.hs view
@@ -71,7 +71,7 @@ void uuidDescMapLoad groups <- lookupGroups uuid- remoteconfig <- M.lookup uuid <$> readRemoteLog+ remoteconfig <- M.lookup uuid <$> remoteConfigMap let (repogroup, associateddirectory) = case getStandardGroup groups of Nothing -> (RepoGroupCustom $ unwords $ map fromGroup $ S.toList groups, Nothing) Just g -> (RepoGroupStandard g, associatedDirectory remoteconfig g)@@ -122,7 +122,7 @@ | T.null t -> noop | otherwise -> liftAnnex $ do let dir = takeBaseName $ T.unpack t- m <- readRemoteLog+ m <- remoteConfigMap case M.lookup uuid m of Nothing -> noop Just remoteconfig -> configSet uuid $@@ -220,7 +220,7 @@ let istransfer = repoGroup curr == RepoGroupStandard TransferGroup config <- liftAnnex $ fromMaybe mempty . M.lookup uuid- <$> readRemoteLog+ <$> remoteConfigMap let repoInfo = getRepoInfo mremote config let repoEncryption = getRepoEncryption mremote (Just config) $(widgetFile "configurators/edit/repository")@@ -230,7 +230,7 @@ Just rmt -> do config <- liftAnnex $ fromMaybe mempty . M.lookup (Remote.uuid rmt)- <$> readRemoteLog+ <$> remoteConfigMap getRepoInfo mr config Nothing -> getRepoInfo Nothing mempty g <- liftAnnex gitRepo@@ -242,7 +242,7 @@ checkAssociatedDirectory :: RepoConfig -> Maybe Remote -> Annex () checkAssociatedDirectory _ Nothing = noop checkAssociatedDirectory cfg (Just r) = do- repoconfig <- M.lookup (Remote.uuid r) <$> readRemoteLog+ repoconfig <- M.lookup (Remote.uuid r) <$> remoteConfigMap case repoGroup cfg of RepoGroupStandard gr -> case associatedDirectory repoconfig gr of Just d -> do
Assistant/WebApp/Configurators/IA.hs view
@@ -158,7 +158,7 @@ runFormPostNoToken $ renderBootstrap3 bootstrapFormLayout $ iaCredsAForm defcreds case result of FormSuccess creds -> liftH $ do- m <- liftAnnex readRemoteLog+ m <- liftAnnex remoteConfigMap let name = fromJust $ lookupName $ fromJust $ M.lookup uuid m AWS.makeAWSRemote enableSpecialRemote S3.remote PublicGroup creds name M.empty
Assistant/WebApp/Configurators/Ssh.hs view
@@ -209,7 +209,7 @@ -} enableSshRemote :: (RemoteConfig -> Maybe SshData) -> (SshInput -> RemoteName -> Handler Html) -> (SshData -> UUID -> Handler Html) -> UUID -> Handler Html enableSshRemote getsshdata rsyncnetsetup genericsetup u = do- m <- fromMaybe M.empty . M.lookup u <$> liftAnnex readRemoteLog+ m <- fromMaybe M.empty . M.lookup u <$> liftAnnex remoteConfigMap case (unmangle <$> getsshdata m, lookupName m) of (Just sshdata, Just reponame) -> sshConfigurator $ do ((result, form), enctype) <- liftH $@@ -424,7 +424,7 @@ -- Not a UUID we know, so prompt about combining. $(widgetFile "configurators/ssh/combine") handleexisting (Just _) = prepSsh False sshdata $ \sshdata' -> do- m <- liftAnnex readRemoteLog+ m <- liftAnnex remoteConfigMap case fromProposedAccepted <$> (M.lookup typeField =<< M.lookup u m) of Just "gcrypt" -> combineExistingGCrypt sshdata' u _ -> makeSshRepo ExistingRepo sshdata'@@ -545,7 +545,7 @@ -- Record the location of the ssh remote in the remote log, so it -- can easily be enabled elsewhere using the webapp. setup r = do- m <- readRemoteLog+ m <- remoteConfigMap let c = fromMaybe M.empty (M.lookup (Remote.uuid r) m) let c' = M.insert (Proposed "location") (Proposed (genSshUrl sshdata)) $ M.insert typeField (Proposed "git") $
Assistant/WebApp/Configurators/WebDAV.hs view
@@ -53,7 +53,7 @@ getEnableWebDAVR = postEnableWebDAVR postEnableWebDAVR :: UUID -> Handler Html postEnableWebDAVR uuid = do- m <- liftAnnex readRemoteLog+ m <- liftAnnex remoteConfigMap let c = fromJust $ M.lookup uuid m let name = fromJust $ lookupName c let url = fromProposedAccepted $ fromJust $ M.lookup (Accepted "url") c
Assistant/WebApp/Gpg.hs view
@@ -80,7 +80,7 @@ mname <- ifM (inRepo $ Git.Command.runBool [Param "fetch", Param tmpremote]) ( do void Annex.Branch.forceUpdate- (lookupName <=< M.lookup u) <$> readRemoteLog+ (lookupName <=< M.lookup u) <$> remoteConfigMap , return Nothing ) void $ inRepo $ Git.Remote.Remove.remove tmpremote
Assistant/WebApp/RepoList.hs view
@@ -162,7 +162,7 @@ return $ here : l else return l unconfigured = liftAnnex $ do- m <- readRemoteLog+ m <- remoteConfigMap g <- gitRepo map snd . catMaybes . filter selectedremote . map (findinfo m g)
Benchmark.hs view
@@ -15,7 +15,6 @@ import CmdLine.GitAnnex.Options import qualified Annex import qualified Annex.Branch-import Annex.Action import qualified Options.Applicative as O @@ -35,10 +34,6 @@ -- The cmd is run for benchmarking without startup or -- shutdown actions. Annex.eval st $ performCommandAction cmd seek noop- -- Since the cmd will be run many times, some zombie- -- processes that normally only occur once per command- -- will build up; reap them.- reapZombies where -- Simplified versio of CmdLine.dispatch, without support for fuzzy -- matching or out-of-repo commands.
CHANGELOG view
@@ -1,3 +1,52 @@+git-annex (8.20201007) upstream; urgency=medium++ * --json output now includes a new field "input" which is the input+ value (filename, url, etc) that caused a json object to be output.+ * --batch combined with -J now runs batch requests concurrently for many+ commands. Before, the combination was accepted, but did not enable+ concurrency. Since the output of batch requests can be in any order,+ --json with the new "input" field is recommended to be used,+ to determine which batch request each response corresponds to.+ * aws-0.22 improved its support for setting etags, which improves+ support for versioned S3 buckets.+ * Serialize use of C magic library, which is not thread safe.+ This fixes failures uploading to S3 when using -J.+ * add, addurl, importfeed, import: Added --no-check-gitignore option+ for finer grained control than using --force.+ * import: Check gitignores when importing trees from special remotes.+ * addunused: Don't check .gitignores when adding files.+ * Improve the "Try making some of these repositories available"+ message, with some hints for the user for what to do.+ * Improve --debug output to show pid of processes that are started and+ stopped.+ * sync --all: Sped up seeking to around twice as fast, by avoiding a+ pass over the worktree files when preferred content expressions of the+ local repo and remotes don't use include=/exclude=.+ * Sped up seeking for files to operate on, when using options like+ --copies or --in, by around 20%+ * import --no-content: Check annex.largefiles, and import small+ files into git, the same as is done when importing with content.+ If the largefiles expression needs the file content available+ (due to mimetype or mimeencoding being used), the import will fail.+ * sync: When run without --content, import without copying from+ importtree=yes directory special remotes.+ (Other special remotes may support this later as well.)+ * addurl: Avoid a redundant git ignores check for speed.+ * upgrade: Avoid an upgrade failure of a bare repo in unusual circumstances.+ * httpalso: Support being used with special remotes that do not have+ encryption= in their config.+ * Parse youtube-dl progress output, which lets progress be displayed+ when doing concurrent downloads.+ * Fix build with Benchmark build flag.+ * Enable building with git-annex benchmark by default, only turning it+ off when the criterion library is not installed.+ * runshell: Fix a edge case where rm errors were sent to stdout, which+ could confuse things parsing git-annex output.+ * runshell: Update files atomically when preparing to run git-annex.+ * Fix a build failure on Windows.++ -- Joey Hess <id@joeyh.name> Wed, 07 Oct 2020 12:19:05 -0400+ git-annex (8.20200908) upstream; urgency=medium * Added httpalso special remote, which is useful for accessing
CmdLine/Action.hs view
@@ -53,9 +53,11 @@ - This should only be run in the seek stage. -} commandAction :: CommandStart -> Annex ()-commandAction start = Annex.getState Annex.concurrency >>= \case+commandAction start = getConcurrency >>= \case NonConcurrent -> runnonconcurrent- Concurrent _ -> runconcurrent+ Concurrent n+ | n > 1 -> runconcurrent+ | otherwise -> runnonconcurrent ConcurrentPerCpu -> runconcurrent where runnonconcurrent = void $ includeCommandAction start@@ -174,17 +176,11 @@ - stages, without catching errors and without incrementing error counter. - Useful if one command wants to run part of another command. -} callCommandAction :: CommandStart -> CommandCleanup-callCommandAction = fromMaybe True <$$> callCommandAction' --{- Like callCommandAction, but returns Nothing when the command did not- - perform any action. -}-callCommandAction' :: CommandStart -> Annex (Maybe Bool)-callCommandAction' start = - start >>= \case- Nothing -> return Nothing- Just (startmsg, perform) -> do- showStartMessage startmsg- Just <$> performCommandAction' startmsg perform+callCommandAction start = start >>= \case+ Just (startmsg, perform) -> do+ showStartMessage startmsg+ performCommandAction' startmsg perform+ Nothing -> return True performCommandAction' :: StartMessage -> CommandPerform -> CommandCleanup performCommandAction' startmsg perform = @@ -206,9 +202,9 @@ -} startConcurrency :: UsedStages -> Annex a -> Annex a startConcurrency usedstages a = do- fromcmdline <- Annex.getState Annex.concurrency+ fromcmdline <- getConcurrency fromgitcfg <- annexJobs <$> Annex.getGitConfig- let usegitcfg = setConcurrency fromgitcfg+ let usegitcfg = setConcurrency (ConcurrencyGitConfig fromgitcfg) case (fromcmdline, fromgitcfg) of (NonConcurrent, NonConcurrent) -> a (Concurrent n, _) ->@@ -264,7 +260,7 @@ - May be called repeatedly by the same thread without blocking. -} ensureOnlyActionOn :: Key -> Annex a -> Annex a ensureOnlyActionOn k a = debugLocks $- go =<< Annex.getState Annex.concurrency+ go =<< getConcurrency where go NonConcurrent = a go (Concurrent _) = goconcurrent
CmdLine/Batch.hs view
@@ -8,6 +8,7 @@ module CmdLine.Batch where import Annex.Common+import qualified Annex import Types.Command import CmdLine.Action import CmdLine.GitAnnex.Options@@ -18,6 +19,8 @@ import Annex.BranchState import Annex.WorkTree import Annex.Content+import Annex.Concurrent+import Types.Concurrency data BatchMode = Batch BatchFormat | NoBatch @@ -42,7 +45,9 @@ -- In batch mode, one line at a time is read, parsed, and a reply output to -- stdout. In non batch mode, the command's parameters are parsed and -- a reply output for each.-batchable :: (opts -> String -> Annex Bool) -> Parser opts -> CmdParamsDesc -> CommandParser+--+-- Note that the actions are not run concurrently.+batchable :: (opts -> SeekInput -> String -> Annex Bool) -> Parser opts -> CmdParamsDesc -> CommandParser batchable handler parser paramdesc = batchseeker <$> batchparser where batchparser = (,,)@@ -51,12 +56,12 @@ <*> cmdParams paramdesc batchseeker (opts, NoBatch, params) =- mapM_ (go NoBatch opts) params+ mapM_ (\p -> go NoBatch opts (SeekInput [p], p)) params batchseeker (opts, batchmode@(Batch fmt), _) = batchInput fmt (pure . Right) (go batchmode opts) - go batchmode opts p =- unlessM (handler opts p) $+ go batchmode opts (si, p) =+ unlessM (handler opts si p) $ batchBadInput batchmode -- bad input is indicated by an empty line in batch mode. In non batch@@ -72,17 +77,18 @@ -- be converted to relative. Normally, filename parameters are passed -- through git ls-files, which makes them relative, but batch mode does -- not use that, and absolute worktree files are likely to cause breakage.-batchInput :: BatchFormat -> (String -> Annex (Either String a)) -> (a -> Annex ()) -> Annex ()+batchInput :: BatchFormat -> (String -> Annex (Either String v)) -> ((SeekInput, v) -> Annex ()) -> Annex () batchInput fmt parser a = go =<< batchLines fmt where go [] = return () go (l:rest) = do- either parseerr a =<< parser l+ either parseerr (\v -> a (SeekInput [l], v)) =<< parser l go rest parseerr s = giveup $ "Batch input parse failure: " ++ s batchLines :: BatchFormat -> Annex [String] batchLines fmt = do+ checkBatchConcurrency enableInteractiveBranchAccess liftIO $ splitter <$> getContents where@@ -90,45 +96,57 @@ BatchLine -> lines BatchNull -> splitc '\0' --- Runs a CommandStart in batch mode.---+-- When concurrency is enabled at the command line, it is used in batch+-- mode. But, if it's only set in git config, don't use it, because the+-- program using batch mode may not expect interleaved output.+checkBatchConcurrency :: Annex ()+checkBatchConcurrency = Annex.getState Annex.concurrency >>= \case+ ConcurrencyCmdLine _ -> noop+ ConcurrencyGitConfig _ -> + setConcurrency (ConcurrencyGitConfig (Concurrent 1))++batchCommandAction :: CommandStart -> Annex ()+batchCommandAction = commandAction . batchCommandStart+ -- The batch mode user expects to read a line of output, and it's up to the -- CommandStart to generate that output as it succeeds or fails to do its -- job. However, if it stops without doing anything, it won't generate--- any output, so in that case, batchBadInput is used to provide the caller--- with an empty line.-batchCommandAction :: CommandStart -> Annex ()-batchCommandAction a = maybe (batchBadInput (Batch BatchLine)) (const noop)- =<< callCommandAction' a+-- any output. This modifies it so in that case, an empty line is printed.+batchCommandStart :: CommandStart -> CommandStart+batchCommandStart a = a >>= \case+ Just v -> return (Just v)+ Nothing -> do+ batchBadInput (Batch BatchLine)+ return Nothing -- Reads lines of batch input and passes the filepaths to a CommandStart -- to handle them. ----- Absolute filepaths are converted to relative.+-- Absolute filepaths are converted to relative, because in non-batch+-- mode, that is done when CmdLine.Seek uses git ls-files. ----- File matching options are not checked.-batchStart :: BatchFormat -> (FilePath -> CommandStart) -> Annex ()-batchStart fmt a = batchInput fmt (Right <$$> liftIO . relPathCwdToFile) $- batchCommandAction . a---- Like batchStart, but checks the file matching options--- and skips non-matching files.-batchFilesMatching :: BatchFormat -> (RawFilePath -> CommandStart) -> Annex ()+-- File matching options are checked, and non-matching files skipped.+batchFilesMatching :: BatchFormat -> ((SeekInput, RawFilePath) -> CommandStart) -> Annex () batchFilesMatching fmt a = do matcher <- getMatcher- batchStart fmt $ \f ->+ go $ \si f -> let f' = toRawFilePath f- in ifM (matcher $ MatchingFile $ FileInfo f' f')- ( a f'+ in ifM (matcher $ MatchingFile $ FileInfo (Just f') f')+ ( a (si, f') , return Nothing )+ where+ go a' = batchInput fmt + (Right <$$> liftIO . relPathCwdToFile)+ (batchCommandAction . uncurry a') batchAnnexedFilesMatching :: BatchFormat -> AnnexedFileSeeker -> Annex ()-batchAnnexedFilesMatching fmt seeker = batchFilesMatching fmt $- whenAnnexed $ \f k -> case checkContentPresent seeker of- Just v -> do- present <- inAnnex k- if present == v- then startAction seeker f k- else return Nothing- Nothing -> startAction seeker f k+batchAnnexedFilesMatching fmt seeker = batchFilesMatching fmt $ \(si, bf) ->+ flip whenAnnexed bf $ \f k -> + case checkContentPresent seeker of+ Just v -> do+ present <- inAnnex k+ if present == v+ then startAction seeker si f k+ else return Nothing+ Nothing -> startAction seeker si f k
CmdLine/GitAnnex/Options.hs view
@@ -392,7 +392,7 @@ -- action in `allowConcurrentOutput`. jobsOption :: [GlobalOption] jobsOption = - [ globalSetter setConcurrency $ + [ globalSetter (setConcurrency . ConcurrencyCmdLine) $ option (maybeReader parseConcurrency) ( long "jobs" <> short 'J' <> metavar (paramNumber `paramOr` "cpus")
CmdLine/Seek.hs view
@@ -18,7 +18,6 @@ import Types.FileMatcher import qualified Annex import qualified Git-import qualified Git.Command import qualified Git.LsFiles as LsFiles import qualified Git.LsTree as LsTree import qualified Git.Types as Git@@ -39,6 +38,7 @@ import Annex.Link import Annex.InodeSentinal import Annex.Concurrent+import Annex.CheckIgnore import qualified Annex.Branch import qualified Annex.BranchState import qualified Database.Keys@@ -50,7 +50,7 @@ import System.Posix.Types data AnnexedFileSeeker = AnnexedFileSeeker- { startAction :: RawFilePath -> Key -> CommandStart+ { startAction :: SeekInput -> RawFilePath -> Key -> CommandStart , checkContentPresent :: Maybe Bool , usesLocationLog :: Bool }@@ -67,32 +67,29 @@ else seekFilteredKeys a (getfiles [] l) ) where- getfiles c [] = return (reverse c)+ getfiles c [] = return (reverse c, pure True) getfiles c (p:ps) = do os <- seekOptions ww (fs, cleanup) <- inRepo $ LsFiles.inRepoDetails os [toRawFilePath p]- case fs of+ r <- case fs of [f] -> do void $ liftIO $ cleanup- getfiles (f:c) ps+ fst <$> getfiles ((SeekInput [p], f):c) ps [] -> do void $ liftIO $ cleanup- getfiles c ps- _ -> giveup needforce+ fst <$> getfiles c ps+ _ -> do+ void $ liftIO $ cleanup+ giveup needforce+ return (r, pure True) withFilesInGitAnnexNonRecursive _ _ _ NoWorkTreeItems = noop -withFilesNotInGit :: (RawFilePath -> CommandSeek) -> WorkTreeItems -> CommandSeek-withFilesNotInGit a (WorkTreeItems l) = go =<< seek- where- seek = do- force <- Annex.getState Annex.force- g <- gitRepo- liftIO $ Git.Command.leaveZombie- <$> LsFiles.notInRepo [] force l' g- go fs = seekFiltered a $- return $ concat $ segmentPaths id l' fs- l' = map toRawFilePath l-withFilesNotInGit _ NoWorkTreeItems = noop +withFilesNotInGit :: CheckGitIgnore -> WarnUnmatchWhen -> ((SeekInput, RawFilePath) -> CommandSeek) -> WorkTreeItems -> CommandSeek+withFilesNotInGit (CheckGitIgnore ci) ww a l = do+ force <- Annex.getState Annex.force+ let include_ignored = force || not ci+ seekFiltered (const (pure True)) a $+ seekHelper id ww (const $ LsFiles.notInRepo [] include_ignored) l withPathContents :: ((FilePath, FilePath) -> CommandSeek) -> CmdParams -> CommandSeek withPathContents a params = do@@ -109,7 +106,7 @@ , return [(p, takeFileName p)] ) checkmatch matcher (f, relf) = matcher $ MatchingFile $ FileInfo- { currFile = toRawFilePath f+ { contentFile = Just (toRawFilePath f) , matchFile = toRawFilePath relf } @@ -119,24 +116,24 @@ withStrings :: (String -> CommandSeek) -> CmdParams -> CommandSeek withStrings a params = sequence_ $ map a params -withPairs :: ((String, String) -> CommandSeek) -> CmdParams -> CommandSeek-withPairs a params = sequence_ $ map a $ pairs [] params+withPairs :: ((SeekInput, (String, String)) -> CommandSeek) -> CmdParams -> CommandSeek+withPairs a params = sequence_ $ + map (\p@(x,y) -> a (SeekInput [x,y], p)) (pairs [] params) where pairs c [] = reverse c pairs c (x:y:xs) = pairs ((x,y):c) xs pairs _ _ = giveup "expected pairs" -withFilesToBeCommitted :: (RawFilePath -> CommandSeek) -> WorkTreeItems -> CommandSeek-withFilesToBeCommitted a l = seekFiltered a $+withFilesToBeCommitted :: ((SeekInput, RawFilePath) -> CommandSeek) -> WorkTreeItems -> CommandSeek+withFilesToBeCommitted a l = seekFiltered (const (pure True)) a $ seekHelper id WarnUnmatchWorkTreeItems (const LsFiles.stagedNotDeleted) l {- unlocked pointer files that are staged, and whose content has not been - modified-}-withUnmodifiedUnlockedPointers :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> WorkTreeItems -> CommandSeek-withUnmodifiedUnlockedPointers ww a l = seekFiltered a unlockedfiles- where- unlockedfiles = filterM isUnmodifiedUnlocked - =<< seekHelper id ww (const LsFiles.typeChangedStaged) l+withUnmodifiedUnlockedPointers :: WarnUnmatchWhen -> ((SeekInput, RawFilePath) -> CommandSeek) -> WorkTreeItems -> CommandSeek+withUnmodifiedUnlockedPointers ww a l =+ seekFiltered (isUnmodifiedUnlocked . snd) a $+ seekHelper id ww (const LsFiles.typeChangedStaged) l isUnmodifiedUnlocked :: RawFilePath -> Annex Bool isUnmodifiedUnlocked f = catKeyFile f >>= \case@@ -144,12 +141,12 @@ Just k -> sameInodeCache f =<< Database.Keys.getInodeCaches k {- Finds files that may be modified. -}-withFilesMaybeModified :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> WorkTreeItems -> CommandSeek-withFilesMaybeModified ww a params = seekFiltered a $+withFilesMaybeModified :: WarnUnmatchWhen -> ((SeekInput, RawFilePath) -> CommandSeek) -> WorkTreeItems -> CommandSeek+withFilesMaybeModified ww a params = seekFiltered (const (pure True)) a $ seekHelper id ww LsFiles.modified params -withKeys :: (Key -> CommandSeek) -> CmdParams -> CommandSeek-withKeys a l = sequence_ $ map (a . parse) l+withKeys :: ((SeekInput, Key) -> CommandSeek) -> CmdParams -> CommandSeek+withKeys a ls = sequence_ $ map (\l -> a (SeekInput [l], parse l)) ls where parse p = fromMaybe (giveup "bad key") $ deserializeKey p @@ -170,7 +167,7 @@ :: Maybe KeyOptions -> Bool -> AnnexedFileSeeker- -> ((Key, ActionItem) -> CommandSeek)+ -> ((SeekInput, Key, ActionItem) -> CommandSeek) -> (WorkTreeItems -> CommandSeek) -> WorkTreeItems -> CommandSeek@@ -178,7 +175,7 @@ where mkkeyaction = do matcher <- Limit.getMatcher- return $ \v@(k, ai) -> checkseeker k $+ return $ \v@(_si, k, ai) -> checkseeker k $ let i = case ai of ActionItemBranchFilePath (BranchFilePath _ topf) _ -> MatchingKey k (AssociatedFile $ Just $ getTopFilePath topf)@@ -194,7 +191,7 @@ withKeyOptions' :: Maybe KeyOptions -> Bool- -> Annex ((Key, ActionItem) -> Annex ())+ -> Annex ((SeekInput, Key, ActionItem) -> Annex ()) -> (WorkTreeItems -> CommandSeek) -> WorkTreeItems -> CommandSeek@@ -245,7 +242,7 @@ Nothing -> return () Just ((k, f), content) -> do maybe noop (Annex.BranchState.setCache f) content- keyaction (k, mkActionItem k)+ keyaction (SeekInput [], k, mkActionItem k) go reader catObjectStreamLsTree l (getk . getTopFilePath . LsTree.file) g go liftIO $ void cleanup@@ -253,7 +250,7 @@ runkeyaction getks = do keyaction <- mkkeyaction ks <- getks- forM_ ks $ \k -> keyaction (k, mkActionItem k)+ forM_ ks $ \k -> keyaction (SeekInput [], k, mkActionItem k) runbranchkeys bs = do keyaction <- mkkeyaction@@ -263,7 +260,7 @@ Nothing -> noop Just k -> let bfp = mkActionItem (BranchFilePath b (LsTree.file i), k)- in keyaction (k, bfp)+ in keyaction (SeekInput [], k, bfp) unlessM (liftIO cleanup) $ error ("git ls-tree " ++ Git.fromRef b ++ " failed") @@ -272,127 +269,166 @@ rs <- remoteList ts <- concat <$> mapM (getFailedTransfers . Remote.uuid) rs forM_ ts $ \(t, i) ->- keyaction (transferKey t, mkActionItem (t, i))+ keyaction (SeekInput [], transferKey t, mkActionItem (t, i)) -seekFiltered :: (RawFilePath -> CommandSeek) -> Annex [RawFilePath] -> Annex ()-seekFiltered a fs = do+seekFiltered :: ((SeekInput, RawFilePath) -> Annex Bool) -> ((SeekInput, RawFilePath) -> CommandSeek) -> Annex ([(SeekInput, RawFilePath)], IO Bool) -> Annex ()+seekFiltered prefilter a listfs = do matcher <- Limit.getMatcher- sequence_ =<< (map (process matcher) <$> fs)+ (fs, cleanup) <- listfs+ sequence_ (map (process matcher) fs)+ liftIO $ void cleanup where- process matcher f =- whenM (matcher $ MatchingFile $ FileInfo f f) $ a f+ process matcher v@(_si, f) =+ whenM (prefilter v) $+ whenM (matcher $ MatchingFile $ FileInfo (Just f) f) $+ a v +data MatcherInfo = MatcherInfo+ { matcherAction :: MatchInfo -> Annex Bool+ , matcherNeedsFileName :: Bool+ , matcherNeedsKey :: Bool+ , matcherNeedsLocationLog :: Bool+ }++checkMatcherWhen :: MatcherInfo -> Bool -> MatchInfo -> Annex () -> Annex ()+checkMatcherWhen mi c i a+ | c = whenM (matcherAction mi i) a+ | otherwise = a+ -- This is significantly faster than using lookupKey after seekFiltered, -- because of the way data is streamed through git cat-file. -- -- It can also precache location logs using the same efficient streaming.-seekFilteredKeys :: AnnexedFileSeeker -> Annex [(RawFilePath, Git.Sha, FileMode)] -> Annex ()+seekFilteredKeys :: AnnexedFileSeeker -> Annex ([(SeekInput, (RawFilePath, Git.Sha, FileMode))], IO Bool) -> Annex () seekFilteredKeys seeker listfs = do g <- Annex.gitRepo- matcher <- Limit.getMatcher+ mi <- MatcherInfo+ <$> Limit.getMatcher+ <*> Limit.introspect matchNeedsFileName+ <*> Limit.introspect matchNeedsKey+ <*> Limit.introspect matchNeedsLocationLog config <- Annex.getGitConfig- -- Run here, not in the async, because it could throw an exception- -- The list should be built lazily.- l <- listfs+ (l, cleanup) <- listfs catObjectMetaDataStream g $ \mdfeeder mdcloser mdreader -> catObjectStream g $ \ofeeder ocloser oreader -> do processertid <- liftIO . async =<< forkState- (process matcher ofeeder mdfeeder mdcloser False l)+ (process mi ofeeder mdfeeder mdcloser False l) mdprocessertid <- liftIO . async =<< forkState- (mdprocess matcher mdreader ofeeder ocloser)- if usesLocationLog seeker+ (mdprocess mi mdreader ofeeder ocloser)+ if usesLocationLog seeker || matcherNeedsLocationLog mi then catObjectStream g $ \lfeeder lcloser lreader -> do precachertid <- liftIO . async =<< forkState- (precacher config oreader lfeeder lcloser)- precachefinisher lreader+ (precacher mi config oreader lfeeder lcloser)+ precachefinisher mi lreader join (liftIO (wait precachertid))- else finisher oreader+ else finisher mi oreader join (liftIO (wait mdprocessertid)) join (liftIO (wait processertid))+ liftIO $ void cleanup where- checkpresence k cont = case checkContentPresent seeker of- Just v -> do- present <- inAnnex k- when (present == v) cont- Nothing -> cont-- finisher oreader = liftIO oreader >>= \case- Just (f, content) -> do- case parseLinkTargetOrPointerLazy =<< content of- Just k -> checkpresence k $- commandAction $- startAction seeker f k- Nothing -> noop- finisher oreader+ finisher mi oreader = liftIO oreader >>= \case+ Just ((si, f), content) -> do+ keyaction f mi content $ + commandAction . startAction seeker si f+ finisher mi oreader Nothing -> return () - precachefinisher lreader = liftIO lreader >>= \case- Just ((logf, f, k), logcontent) -> do+ precachefinisher mi lreader = liftIO lreader >>= \case+ Just ((logf, (si, f), k), logcontent) -> do maybe noop (Annex.BranchState.setCache logf) logcontent- commandAction $ startAction seeker f k- precachefinisher lreader+ checkMatcherWhen mi+ (matcherNeedsLocationLog mi && not (matcherNeedsFileName mi))+ (MatchingKey k (AssociatedFile (Just f)))+ (commandAction $ startAction seeker si f k)+ precachefinisher mi lreader Nothing -> return () - precacher config oreader lfeeder lcloser = liftIO oreader >>= \case- Just (f, content) -> do- case parseLinkTargetOrPointerLazy =<< content of- Just k -> checkpresence k $- let logf = locationLogFile config k- ref = Git.Ref.branchFileRef Annex.Branch.fullname logf- in liftIO $ lfeeder ((logf, f, k), ref)- Nothing -> noop- precacher config oreader lfeeder lcloser+ precacher mi config oreader lfeeder lcloser = liftIO oreader >>= \case+ Just ((si, f), content) -> do+ keyaction f mi content $ \k -> + let logf = locationLogFile config k+ ref = Git.Ref.branchFileRef Annex.Branch.fullname logf+ in liftIO $ lfeeder ((logf, (si, f), k), ref)+ precacher mi config oreader lfeeder lcloser Nothing -> liftIO $ void lcloser - feedmatches matcher ofeeder f sha = - whenM (matcher $ MatchingFile $ FileInfo f f) $- liftIO $ ofeeder (f, sha)+ feedmatches mi ofeeder si f sha = checkMatcherWhen mi+ -- When the matcher needs a key or location log + -- (and does not need a worktree filename), it will be+ -- checked later, to avoid a slow lookup here.+ (not ((matcherNeedsKey mi || matcherNeedsLocationLog mi) + && not (matcherNeedsFileName mi)))+ (MatchingFile $ FileInfo (Just f) f)+ (liftIO $ ofeeder ((si, f), sha)) - process matcher ofeeder mdfeeder mdcloser seenpointer ((f, sha, mode):rest) =+ keyaction f mi content a = + case parseLinkTargetOrPointerLazy =<< content of+ Just k -> checkMatcherWhen mi+ (matcherNeedsKey mi && not (matcherNeedsFileName mi || matcherNeedsLocationLog mi))+ (MatchingKey k (AssociatedFile (Just f)))+ (checkpresence k (a k))+ Nothing -> noop+ + checkpresence k cont = case checkContentPresent seeker of+ Just v -> do+ present <- inAnnex k+ when (present == v) cont+ Nothing -> cont++ process mi ofeeder mdfeeder mdcloser seenpointer ((si, (f, sha, mode)):rest) = case Git.toTreeItemType mode of Just Git.TreeSymlink -> do whenM (exists f) $ -- Once a pointer file has been seen, -- symlinks have to be sent via the - -- metadata processor too. That is slightly- -- slower, but preserves the requested- -- file order.+ -- metadata processor too. That is+ -- slightly slower, but preserves the+ -- requested file order. if seenpointer- then liftIO $ mdfeeder (f, sha)- else feedmatches matcher ofeeder f sha- process matcher ofeeder mdfeeder mdcloser seenpointer rest+ then liftIO $ mdfeeder ((si, f), sha)+ else feedmatches mi ofeeder si f sha+ process mi ofeeder mdfeeder mdcloser seenpointer rest Just Git.TreeSubmodule ->- process matcher ofeeder mdfeeder mdcloser seenpointer rest+ process mi ofeeder mdfeeder mdcloser seenpointer rest -- Might be a pointer file, might be other -- file in git, possibly large. Avoid catting -- large files by first looking up the size. Just _ -> do whenM (exists f) $- liftIO $ mdfeeder (f, sha)- process matcher ofeeder mdfeeder mdcloser True rest+ liftIO $ mdfeeder ((si, f), sha)+ process mi ofeeder mdfeeder mdcloser True rest Nothing ->- process matcher ofeeder mdfeeder mdcloser seenpointer rest+ process mi ofeeder mdfeeder mdcloser seenpointer rest process _ _ _ mdcloser _ [] = liftIO $ void mdcloser -- Check if files exist, because a deleted file will still be -- listed by ls-tree, but should not be processed. exists p = isJust <$> liftIO (catchMaybeIO $ R.getSymbolicLinkStatus p) - mdprocess matcher mdreader ofeeder ocloser = liftIO mdreader >>= \case- Just (f, Just (sha, size, _type))+ mdprocess mi mdreader ofeeder ocloser = liftIO mdreader >>= \case+ Just ((si, f), Just (sha, size, _type)) | size < maxPointerSz -> do- feedmatches matcher ofeeder f sha- mdprocess matcher mdreader ofeeder ocloser- Just _ -> mdprocess matcher mdreader ofeeder ocloser+ feedmatches mi ofeeder si f sha+ mdprocess mi mdreader ofeeder ocloser+ Just _ -> mdprocess mi mdreader ofeeder ocloser Nothing -> liftIO $ void ocloser -seekHelper :: (a -> RawFilePath) -> WarnUnmatchWhen -> ([LsFiles.Options] -> [RawFilePath] -> Git.Repo -> IO ([a], IO Bool)) -> WorkTreeItems -> Annex [a]+seekHelper :: (a -> RawFilePath) -> WarnUnmatchWhen -> ([LsFiles.Options] -> [RawFilePath] -> Git.Repo -> IO ([a], IO Bool)) -> WorkTreeItems -> Annex ([(SeekInput, a)], IO Bool) seekHelper c ww a (WorkTreeItems l) = do os <- seekOptions ww- inRepo $ \g ->- concat . concat <$> forM (segmentXargsOrdered l)- (runSegmentPaths c (\fs -> Git.Command.leaveZombie <$> a os fs g) . map toRawFilePath)-seekHelper _ _ _ NoWorkTreeItems = return []+ inRepo $ \g -> combinelists <$> forM (segmentXargsOrdered l)+ (runSegmentPaths' mk c (\fs -> a os fs g) . map toRawFilePath)+ where+ mk (Just i) f = (SeekInput [fromRawFilePath i], f) + -- This is not accurate, but it only happens when there are a+ -- great many input WorkTreeItems.+ mk Nothing f = (SeekInput [fromRawFilePath (c f)], f)++ combinelists v = + let r = concat $ concat $ map fst v+ cleanup = and <$> sequence (map snd v)+ in (r, cleanup)+seekHelper _ _ _ NoWorkTreeItems = return ([], pure True) data WarnUnmatchWhen = WarnUnmatchLsFiles | WarnUnmatchWorkTreeItems
Command.hs view
@@ -74,13 +74,15 @@ withGlobalOptions os c = c { cmdglobaloptions = cmdglobaloptions c ++ concat os } {- For start stage to indicate what will be done. -}-starting:: MkActionItem t => String -> t -> CommandPerform -> CommandStart-starting msg t a = next (StartMessage msg (mkActionItem t), a)+starting:: MkActionItem actionitem => String -> actionitem -> SeekInput -> CommandPerform -> CommandStart+starting msg ai si a = next+ (StartMessage msg (mkActionItem ai) si, a) {- Use when noMessages was used but the command is going to output - usual messages after all. -}-startingUsualMessages :: MkActionItem t => String -> t -> CommandPerform -> CommandStart-startingUsualMessages msg t a = next (StartUsualMessages msg (mkActionItem t), a)+startingUsualMessages :: MkActionItem t => String -> t -> SeekInput -> CommandPerform -> CommandStart+startingUsualMessages msg t si a = next + (StartUsualMessages msg (mkActionItem t) si, a) {- When no message should be displayed at start/end, but messages can still - be displayed when using eg includeCommandAction. -}
Command/Add.hs view
@@ -24,6 +24,7 @@ import Config.GitConfig import qualified Git.UpdateIndex import Utility.FileMode+import Utility.OptParse import qualified Utility.RawFilePath as R cmd :: Command@@ -37,6 +38,7 @@ , batchOption :: BatchMode , updateOnly :: Bool , largeFilesOverride :: Maybe Bool+ , checkGitIgnoreOption :: CheckGitIgnore } optParser :: CmdParamsDesc -> Parser AddOptions@@ -49,6 +51,7 @@ <> help "only update tracked files" ) <*> (parseforcelarge <|> parseforcesmall)+ <*> checkGitIgnoreSwitch where parseforcelarge = flag Nothing (Just True) ( long "force-large"@@ -59,23 +62,28 @@ <> help "add all files to git, ignoring other configuration" ) +checkGitIgnoreSwitch :: Parser CheckGitIgnore+checkGitIgnoreSwitch = CheckGitIgnore <$>+ invertableSwitch "check-gitignore" True+ (help "Do not check .gitignore when adding files")+ seek :: AddOptions -> CommandSeek seek o = startConcurrency commandStages $ do largematcher <- largeFilesMatcher addunlockedmatcher <- addUnlockedMatcher annexdotfiles <- getGitConfigVal annexDotFiles - let gofile file = case largeFilesOverride o of+ let gofile (si, file) = case largeFilesOverride o of Nothing -> let file' = fromRawFilePath file in ifM (pure (annexdotfiles || not (dotfile file')) <&&> (checkFileMatcher largematcher file' <||> Annex.getState Annex.force))- ( start file addunlockedmatcher+ ( start o si file addunlockedmatcher , ifM (annexAddSmallFiles <$> Annex.getGitConfig)- ( startSmall file+ ( startSmall o si file , stop ) )- Just True -> start file addunlockedmatcher- Just False -> startSmallOverridden file+ Just True -> start o si file addunlockedmatcher+ Just False -> startSmallOverridden o si file case batchOption o of Batch fmt | updateOnly o ->@@ -90,31 +98,33 @@ l <- workTreeItems ww (addThese o) let go a = a ww (commandAction . gofile) l unless (updateOnly o) $- go (const withFilesNotInGit)+ go (withFilesNotInGit (checkGitIgnoreOption o)) go withFilesMaybeModified go withUnmodifiedUnlockedPointers {- Pass file off to git-add. -}-startSmall :: RawFilePath -> CommandStart-startSmall file = starting "add" (ActionItemWorkTreeFile file) $- next $ addSmall file+startSmall :: AddOptions -> SeekInput -> RawFilePath -> CommandStart+startSmall o si file =+ starting "add" (ActionItemWorkTreeFile file) si $+ next $ addSmall (checkGitIgnoreOption o) file -addSmall :: RawFilePath -> Annex Bool-addSmall file = do+addSmall :: CheckGitIgnore -> RawFilePath -> Annex Bool+addSmall ci file = do showNote "non-large file; adding content to git repository"- addFile file+ addFile ci file -startSmallOverridden :: RawFilePath -> CommandStart-startSmallOverridden file = starting "add" (ActionItemWorkTreeFile file) $- next $ addSmallOverridden file+startSmallOverridden :: AddOptions -> SeekInput -> RawFilePath -> CommandStart+startSmallOverridden o si file = + starting "add" (ActionItemWorkTreeFile file) si $+ next $ addSmallOverridden o file -addSmallOverridden :: RawFilePath -> Annex Bool-addSmallOverridden file = do+addSmallOverridden :: AddOptions -> RawFilePath -> Annex Bool+addSmallOverridden o file = do showNote "adding content to git repository" let file' = fromRawFilePath file s <- liftIO $ getSymbolicLinkStatus file' if not (isRegularFile s)- then addFile file + then addFile (checkGitIgnoreOption o) file else do -- Can't use addFile because the clean filter will -- honor annex.largefiles and it has been overridden.@@ -127,14 +137,14 @@ inRepo (Git.UpdateIndex.stageFile sha ty file') return True -addFile :: RawFilePath -> Annex Bool-addFile file = do- ps <- forceParams+addFile :: CheckGitIgnore -> RawFilePath -> Annex Bool+addFile ci file = do+ ps <- gitAddParams ci Annex.Queue.addCommand "add" (ps++[Param "--"]) [fromRawFilePath file] return True -start :: RawFilePath -> AddUnlockedMatcher -> CommandStart-start file addunlockedmatcher = do+start :: AddOptions -> SeekInput -> RawFilePath -> AddUnlockedMatcher -> CommandStart+start o si file addunlockedmatcher = do mk <- liftIO $ isPointerFile file maybe go fixuppointer mk where@@ -144,29 +154,29 @@ Just s | not (isRegularFile s) && not (isSymbolicLink s) -> stop | otherwise -> - starting "add" (ActionItemWorkTreeFile file) $+ starting "add" (ActionItemWorkTreeFile file) si $ if isSymbolicLink s- then next $ addFile file- else perform file addunlockedmatcher+ then next $ addFile (checkGitIgnoreOption o) file+ else perform o file addunlockedmatcher addpresent key = liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case Just s | isSymbolicLink s -> fixuplink key _ -> add- fixuplink key = starting "add" (ActionItemWorkTreeFile file) $ do+ fixuplink key = starting "add" (ActionItemWorkTreeFile file) si $ do -- the annexed symlink is present but not yet added to git liftIO $ removeFile (fromRawFilePath file)- addLink (fromRawFilePath file) key Nothing+ addLink (checkGitIgnoreOption o) (fromRawFilePath file) key Nothing next $ cleanup key =<< inAnnex key- fixuppointer key = starting "add" (ActionItemWorkTreeFile file) $ do+ fixuppointer key = starting "add" (ActionItemWorkTreeFile file) si $ do -- the pointer file is present, but not yet added to git Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file)- next $ addFile file+ next $ addFile (checkGitIgnoreOption o) file -perform :: RawFilePath -> AddUnlockedMatcher -> CommandPerform-perform file addunlockedmatcher = withOtherTmp $ \tmpdir -> do+perform :: AddOptions -> RawFilePath -> AddUnlockedMatcher -> CommandPerform+perform o file addunlockedmatcher = withOtherTmp $ \tmpdir -> do lockingfile <- not <$> addUnlocked addunlockedmatcher- (MatchingFile (FileInfo file file))+ (MatchingFile (FileInfo (Just file) file)) let cfg = LockDownConfig { lockingFile = lockingfile , hardlinkFileTmpDir = Just tmpdir@@ -174,7 +184,7 @@ ld <- lockDown cfg (fromRawFilePath file) let sizer = keySource <$> ld v <- metered Nothing sizer $ \_meter meterupdate ->- ingestAdd meterupdate ld+ ingestAdd (checkGitIgnoreOption o) meterupdate ld finish v where finish (Just key) = next $ cleanup key True
Command/AddUnused.hs view
@@ -28,7 +28,9 @@ perform :: Key -> CommandPerform perform key = next $ do logStatus key InfoPresent- addLink file key Nothing+ -- Ignore the usual git ignores because the user has explictly+ -- asked to add these files.+ addLink (CheckGitIgnore False) file key Nothing return True where file = "unused." ++ fromRawFilePath (keyFile key)
Command/AddUrl.hs view
@@ -54,6 +54,7 @@ , rawOption :: Bool , fileOption :: Maybe FilePath , preserveFilenameOption :: Bool+ , checkGitIgnoreOption :: CheckGitIgnore } optParser :: CmdParamsDesc -> Parser AddUrlOptions@@ -100,16 +101,18 @@ <> help "use filename provided by server as-is" ) else pure False)+ <*> Command.Add.checkGitIgnoreSwitch seek :: AddUrlOptions -> CommandSeek seek o = startConcurrency commandStages $ do addunlockedmatcher <- addUnlockedMatcher- let go (o', u) = do+ let go (si, (o', u)) = do r <- Remote.claimingUrl u if Remote.uuid r == webUUID || rawOption (downloadOptions o')- then void $ commandAction $ startWeb addunlockedmatcher o' u- else checkUrl addunlockedmatcher r o' u- forM_ (addUrls o) (\u -> go (o, u))+ then void $ commandAction $+ startWeb addunlockedmatcher o' si u+ else checkUrl addunlockedmatcher r o' si u+ forM_ (addUrls o) (\u -> go (SeekInput [u], (o, u))) case batchOption o of Batch fmt -> batchInput fmt (pure . parseBatchInput o) go NoBatch -> noop@@ -123,8 +126,8 @@ else Right (o { downloadOptions = (downloadOptions o) { fileOption = Just f } }, u) | otherwise = Right (o, s) -checkUrl :: AddUnlockedMatcher -> Remote -> AddUrlOptions -> URLString -> Annex ()-checkUrl addunlockedmatcher r o u = do+checkUrl :: AddUnlockedMatcher -> Remote -> AddUrlOptions -> SeekInput -> URLString -> Annex ()+checkUrl addunlockedmatcher r o si u = do pathmax <- liftIO $ fileNameLengthLimit "." let deffile = fromMaybe (urlString2file u (pathdepthOption o) pathmax) (fileOption (downloadOptions o)) go deffile =<< maybe@@ -133,35 +136,35 @@ (Remote.checkUrl r) where - go _ (Left e) = void $ commandAction $ startingAddUrl u o $ do+ go _ (Left e) = void $ commandAction $ startingAddUrl si u o $ do warning (show e) next $ return False go deffile (Right (UrlContents sz mf)) = do f <- maybe (pure deffile) (sanitizeOrPreserveFilePath o) mf let f' = adjustFile o (fromMaybe f (fileOption (downloadOptions o)))- void $ commandAction $ startRemote addunlockedmatcher r o f' u sz+ void $ commandAction $ startRemote addunlockedmatcher r o si f' u sz go deffile (Right (UrlMulti l)) = case fileOption (downloadOptions o) of Nothing -> forM_ l $ \(u', sz, f) -> do f' <- sanitizeOrPreserveFilePath o f let f'' = adjustFile o (deffile </> f')- void $ commandAction $ startRemote addunlockedmatcher r o f'' u' sz+ void $ commandAction $ startRemote addunlockedmatcher r o si f'' u' sz Just f -> case l of [] -> noop ((u',sz,_):[]) -> do let f' = adjustFile o f- void $ commandAction $ startRemote addunlockedmatcher r o f' u' sz+ void $ commandAction $ startRemote addunlockedmatcher r o si f' u' sz _ -> giveup $ unwords [ "That url contains multiple files according to the" , Remote.name r , " remote; cannot add it to a single file." ] -startRemote :: AddUnlockedMatcher -> Remote -> AddUrlOptions -> FilePath -> URLString -> Maybe Integer -> CommandStart-startRemote addunlockedmatcher r o file uri sz = do+startRemote :: AddUnlockedMatcher -> Remote -> AddUrlOptions -> SeekInput -> FilePath -> URLString -> Maybe Integer -> CommandStart+startRemote addunlockedmatcher r o si file uri sz = do pathmax <- liftIO $ fileNameLengthLimit "." let file' = joinPath $ map (truncateFilePath pathmax) $ splitDirectories file- startingAddUrl uri o $ do+ startingAddUrl si uri o $ do showNote $ "from " ++ Remote.name r showDestinationFile file' performRemote addunlockedmatcher r o uri file' sz@@ -177,12 +180,12 @@ geturi = next $ isJust <$> downloadRemoteFile addunlockedmatcher r (downloadOptions o) uri file sz downloadRemoteFile :: AddUnlockedMatcher -> Remote -> DownloadOptions -> URLString -> FilePath -> Maybe Integer -> Annex (Maybe Key)-downloadRemoteFile addunlockedmatcher r o uri file sz = checkCanAdd file $ do+downloadRemoteFile addunlockedmatcher r o uri file sz = checkCanAdd o file $ \canadd -> do let urlkey = Backend.URL.fromUrl uri sz createWorkTreeDirectory (parentDir file) ifM (Annex.getState Annex.fast <||> pure (relaxedOption o)) ( do- addWorkTree addunlockedmatcher (Remote.uuid r) loguri file urlkey Nothing+ addWorkTree canadd addunlockedmatcher (Remote.uuid r) loguri file urlkey Nothing return (Just urlkey) , do -- Set temporary url for the urlkey@@ -191,7 +194,7 @@ setTempUrl urlkey loguri let downloader = \dest p -> fst <$> Remote.verifiedAction (Remote.retrieveKeyFile r urlkey af dest p)- ret <- downloadWith addunlockedmatcher downloader urlkey (Remote.uuid r) loguri file+ ret <- downloadWith canadd addunlockedmatcher downloader urlkey (Remote.uuid r) loguri file removeTempUrl urlkey return ret )@@ -199,12 +202,12 @@ loguri = setDownloader uri OtherDownloader af = AssociatedFile (Just (toRawFilePath file)) -startWeb :: AddUnlockedMatcher -> AddUrlOptions -> URLString -> CommandStart-startWeb addunlockedmatcher o urlstring = go $ fromMaybe bad $ parseURI urlstring+startWeb :: AddUnlockedMatcher -> AddUrlOptions -> SeekInput -> URLString -> CommandStart+startWeb addunlockedmatcher o si urlstring = go $ fromMaybe bad $ parseURI urlstring where bad = fromMaybe (giveup $ "bad url " ++ urlstring) $ Url.parseURIRelaxed $ urlstring- go url = startingAddUrl urlstring o $+ go url = startingAddUrl si urlstring o $ if relaxedOption (downloadOptions o) then go' url Url.assumeUrlExists else Url.withUrlOptions (Url.getUrlInfo urlstring) >>= \case@@ -308,10 +311,10 @@ ( tryyoutubedl tmp , normalfinish tmp )- normalfinish tmp = checkCanAdd file $ do+ normalfinish tmp = checkCanAdd o file $ \canadd -> do showDestinationFile file createWorkTreeDirectory (parentDir file)- Just <$> finishDownloadWith addunlockedmatcher tmp webUUID url file+ Just <$> finishDownloadWith canadd addunlockedmatcher tmp webUUID url file -- Ask youtube-dl what filename it will download first, -- so it's only used when the file contains embedded media. tryyoutubedl tmp = youtubeDlFileNameHtmlOnly url >>= \case@@ -324,14 +327,15 @@ where dl dest = withTmpWorkDir mediakey $ \workdir -> do let cleanuptmp = pruneTmpWorkDirBefore tmp (liftIO . nukeFile)+ showNote "using youtube-dl" Transfer.notifyTransfer Transfer.Download url $- Transfer.download webUUID mediakey (AssociatedFile Nothing) Transfer.noRetry $ \_p ->- youtubeDl url workdir >>= \case+ Transfer.download webUUID mediakey (AssociatedFile Nothing) Transfer.noRetry $ \p ->+ youtubeDl url workdir p >>= \case Right (Just mediafile) -> do cleanuptmp- checkCanAdd dest $ do+ checkCanAdd o dest $ \canadd -> do showDestinationFile dest- addWorkTree addunlockedmatcher webUUID mediaurl dest mediakey (Just mediafile)+ addWorkTree canadd addunlockedmatcher webUUID mediaurl dest mediakey (Just mediafile) return $ Just mediakey Right Nothing -> normalfinish tmp Left msg -> do@@ -353,8 +357,8 @@ {- The destination file is not known at start time unless the user provided - a filename. It's not displayed then for output consistency, - but is added to the json when available. -}-startingAddUrl :: URLString -> AddUrlOptions -> CommandPerform -> CommandStart-startingAddUrl url o p = starting "addurl" (ActionItemOther (Just url)) $ do+startingAddUrl :: SeekInput -> URLString -> AddUrlOptions -> CommandPerform -> CommandStart+startingAddUrl si url o p = starting "addurl" (ActionItemOther (Just url)) si $ do case fileOption (downloadOptions o) of Nothing -> noop Just file -> maybeShowJSON $ JSONChunk [("file", file)]@@ -374,13 +378,13 @@ - Downloads the url, sets up the worktree file, and returns the - real key. -}-downloadWith :: AddUnlockedMatcher -> (FilePath -> MeterUpdate -> Annex Bool) -> Key -> UUID -> URLString -> FilePath -> Annex (Maybe Key)-downloadWith addunlockedmatcher downloader dummykey u url file =+downloadWith :: CanAddFile -> AddUnlockedMatcher -> (FilePath -> MeterUpdate -> Annex Bool) -> Key -> UUID -> URLString -> FilePath -> Annex (Maybe Key)+downloadWith canadd addunlockedmatcher downloader dummykey u url file = go =<< downloadWith' downloader dummykey u url afile where afile = AssociatedFile (Just (toRawFilePath file)) go Nothing = return Nothing- go (Just tmp) = Just <$> finishDownloadWith addunlockedmatcher tmp u url file+ go (Just tmp) = Just <$> finishDownloadWith canadd addunlockedmatcher tmp u url file {- Like downloadWith, but leaves the dummy key content in - the returned location. -}@@ -396,8 +400,8 @@ then return (Just tmp) else return Nothing -finishDownloadWith :: AddUnlockedMatcher -> FilePath -> UUID -> URLString -> FilePath -> Annex Key-finishDownloadWith addunlockedmatcher tmp u url file = do+finishDownloadWith :: CanAddFile -> AddUnlockedMatcher -> FilePath -> UUID -> URLString -> FilePath -> Annex Key+finishDownloadWith canadd addunlockedmatcher tmp u url file = do backend <- chooseBackend file let source = KeySource { keyFilename = toRawFilePath file@@ -405,7 +409,7 @@ , inodeCache = Nothing } key <- fst <$> genKey source nullMeterUpdate backend- addWorkTree addunlockedmatcher u url file key (Just tmp)+ addWorkTree canadd addunlockedmatcher u url file key (Just tmp) return key {- Adds the url size to the Key. -}@@ -415,8 +419,8 @@ } {- Adds worktree file to the repository. -}-addWorkTree :: AddUnlockedMatcher -> UUID -> URLString -> FilePath -> Key -> Maybe FilePath -> Annex ()-addWorkTree addunlockedmatcher u url file key mtmp = case mtmp of+addWorkTree :: CanAddFile -> AddUnlockedMatcher -> UUID -> URLString -> FilePath -> Key -> Maybe FilePath -> Annex ()+addWorkTree _ addunlockedmatcher u url file key mtmp = case mtmp of Nothing -> go Just tmp -> do -- Move to final location for large file check.@@ -432,18 +436,22 @@ -- than the work tree file. liftIO $ renameFile file tmp go- else void $ Command.Add.addSmall (toRawFilePath file)+ else void $ Command.Add.addSmall noci (toRawFilePath file) where go = do maybeShowJSON $ JSONChunk [("key", serializeKey key)] setUrlPresent key url logChange key u InfoPresent- ifM (addAnnexedFile addunlockedmatcher file key mtmp)+ ifM (addAnnexedFile noci addunlockedmatcher file key mtmp) ( do when (isJust mtmp) $ logStatus key InfoPresent , maybe noop (\tmp -> pruneTmpWorkDirBefore tmp (liftIO . nukeFile)) mtmp )+ + -- git does not need to check ignores, because that has already+ -- been done, as witnessed by the CannAddFile.+ noci = CheckGitIgnore False nodownloadWeb :: AddUnlockedMatcher -> DownloadOptions -> URLString -> Url.UrlInfo -> FilePath -> Annex (Maybe Key) nodownloadWeb addunlockedmatcher o url urlinfo file@@ -457,23 +465,23 @@ where nomedia = do let key = Backend.URL.fromUrl url (Url.urlSize urlinfo)- nodownloadWeb' addunlockedmatcher url key file+ nodownloadWeb' o addunlockedmatcher url key file usemedia mediafile = do let dest = youtubeDlDestFile o file mediafile let mediaurl = setDownloader url YoutubeDownloader let mediakey = Backend.URL.fromUrl mediaurl Nothing- nodownloadWeb' addunlockedmatcher mediaurl mediakey dest+ nodownloadWeb' o addunlockedmatcher mediaurl mediakey dest youtubeDlDestFile :: DownloadOptions -> FilePath -> FilePath -> FilePath youtubeDlDestFile o destfile mediafile | isJust (fileOption o) = destfile | otherwise = takeFileName mediafile -nodownloadWeb' :: AddUnlockedMatcher -> URLString -> Key -> FilePath -> Annex (Maybe Key)-nodownloadWeb' addunlockedmatcher url key file = checkCanAdd file $ do+nodownloadWeb' :: DownloadOptions -> AddUnlockedMatcher -> URLString -> Key -> FilePath -> Annex (Maybe Key)+nodownloadWeb' o addunlockedmatcher url key file = checkCanAdd o file $ \canadd -> do showDestinationFile file createWorkTreeDirectory (parentDir file)- addWorkTree addunlockedmatcher webUUID url file key Nothing+ addWorkTree canadd addunlockedmatcher webUUID url file key Nothing return (Just key) url2file :: URI -> Maybe Int -> Int -> FilePath@@ -505,15 +513,17 @@ addprefix f = maybe f (++ f) (prefixOption o) addsuffix f = maybe f (f ++) (suffixOption o) -checkCanAdd :: FilePath -> Annex (Maybe a) -> Annex (Maybe a)-checkCanAdd file a = ifM (isJust <$> (liftIO $ catchMaybeIO $ getSymbolicLinkStatus file))+data CanAddFile = CanAddFile++checkCanAdd :: DownloadOptions -> FilePath -> (CanAddFile -> Annex (Maybe a)) -> Annex (Maybe a)+checkCanAdd o file a = ifM (isJust <$> (liftIO $ catchMaybeIO $ getSymbolicLinkStatus file)) ( do warning $ file ++ " already exists; not overwriting" return Nothing- , ifM ((not <$> Annex.getState Annex.force) <&&> checkIgnored file)+ , ifM (checkIgnored (checkGitIgnoreOption o) file) ( do- warning $ "not adding " ++ file ++ " which is .gitignored (use --force to override)"+ warning $ "not adding " ++ file ++ " which is .gitignored (use --no-check-gitignore to override)" return Nothing- , a+ , a CanAddFile ) )
Command/Adjust.hs view
@@ -51,5 +51,5 @@ start :: Adjustment -> CommandStart start adj = do checkVersionSupported- starting "adjust" (ActionItemOther Nothing) $+ starting "adjust" (ActionItemOther Nothing) (SeekInput []) $ next $ enterAdjustedBranch adj
Command/CalcKey.hs view
@@ -19,8 +19,8 @@ (paramRepeating paramFile) (batchable run (pure ())) -run :: () -> String -> Annex Bool-run _ file = tryNonAsync (genKey ks nullMeterUpdate Nothing) >>= \case+run :: () -> SeekInput -> String -> Annex Bool+run _ _ file = tryNonAsync (genKey ks nullMeterUpdate Nothing) >>= \case Right (k, _) -> do liftIO $ putStrLn $ serializeKey k return True
Command/CheckPresentKey.hs view
@@ -39,7 +39,8 @@ (rn:[]) -> toRemote rn >>= \r -> return (flip check (Just r)) [] -> return (flip check Nothing) _ -> wrongnumparams- batchInput fmt (pure . Right) $ checker >=> batchResult+ batchInput fmt (pure . Right) $+ checker . snd >=> batchResult where wrongnumparams = giveup "Wrong number of parameters"
Command/Commit.hs view
@@ -10,6 +10,7 @@ import Command import qualified Annex.Branch import qualified Git+import Git.Types cmd :: Command cmd = command "commit" SectionPlumbing @@ -20,10 +21,12 @@ seek = withNothing (commandAction start) start :: CommandStart-start = starting "commit" (ActionItemOther (Just "git-annex")) $ do- Annex.Branch.commit =<< Annex.Branch.commitMessage- _ <- runhook <=< inRepo $ Git.hookPath "annex-content"- next $ return True+start = starting "commit" ai si $ do+ Annex.Branch.commit =<< Annex.Branch.commitMessage+ _ <- runhook <=< inRepo $ Git.hookPath "annex-content"+ next $ return True where runhook (Just hook) = liftIO $ boolSystem hook [] runhook Nothing = return True+ ai = ActionItemOther (Just (fromRef Annex.Branch.name))+ si = SeekInput []
Command/Config.hs view
@@ -55,24 +55,32 @@ seek :: Action -> CommandSeek seek (SetConfig ck@(ConfigKey name) val) = checkIsGlobalConfig ck $ commandAction $- startingUsualMessages (decodeBS' name) (ActionItemOther (Just (fromConfigValue val))) $ do+ startingUsualMessages (decodeBS' name) ai si $ do setGlobalConfig ck val when (needLocalUpdate ck) $ setConfig ck (fromConfigValue val) next $ return True+ where+ ai = ActionItemOther (Just (fromConfigValue val))+ si = SeekInput [decodeBS' name] seek (UnsetConfig ck@(ConfigKey name)) = checkIsGlobalConfig ck $ commandAction $- startingUsualMessages (decodeBS' name) (ActionItemOther (Just "unset")) $do+ startingUsualMessages (decodeBS' name) ai si $ do unsetGlobalConfig ck when (needLocalUpdate ck) $ unsetConfig ck next $ return True+ where+ ai = ActionItemOther (Just "unset")+ si = SeekInput [decodeBS' name] seek (GetConfig ck) = checkIsGlobalConfig ck $ commandAction $- startingCustomOutput (ActionItemOther Nothing) $ do+ startingCustomOutput ai $ do getGlobalConfig ck >>= \case Just (ConfigValue v) -> liftIO $ S8.putStrLn v Just NoConfigValue -> return () Nothing -> return () next $ return True+ where+ ai = ActionItemOther Nothing checkIsGlobalConfig :: ConfigKey -> Annex a -> Annex a checkIsGlobalConfig ck@(ConfigKey name) a
Command/ContentLocation.hs view
@@ -20,8 +20,8 @@ (paramRepeating paramKey) (batchable run (pure ())) -run :: () -> String -> Annex Bool-run _ p = do+run :: () -> SeekInput -> String -> Annex Bool+run _ _ p = do let k = fromMaybe (giveup "bad key") $ deserializeKey p maybe (return False) (\f -> liftIO (B8.putStrLn f) >> return True) =<< inAnnex' (pure True) Nothing check k
Command/Copy.hs view
@@ -67,9 +67,9 @@ {- A copy is just a move that does not delete the source file. - However, auto mode avoids unnecessary copies, and avoids getting or - sending non-preferred content. -}-start :: CopyOptions -> RawFilePath -> Key -> CommandStart-start o file key = stopUnless shouldCopy $ - Command.Move.start (fromToOptions o) Command.Move.RemoveNever file key+start :: CopyOptions -> SeekInput -> RawFilePath -> Key -> CommandStart+start o si file key = stopUnless shouldCopy $ + Command.Move.start (fromToOptions o) Command.Move.RemoveNever si file key where shouldCopy | autoMode o = want <||> numCopiesCheck (fromRawFilePath file) key (<)
Command/Dead.hs view
@@ -32,11 +32,11 @@ seek (DeadKeys ks) = commandActions $ map startKey ks startKey :: Key -> CommandStart-startKey key = starting "dead" (mkActionItem key) $+startKey key = starting "dead" (mkActionItem key) (SeekInput []) $ keyLocations key >>= \case [] -> performKey key _ -> giveup "This key is still known to be present in some locations; not marking as dead."- + performKey :: Key -> CommandPerform performKey key = do setDead key
Command/Describe.hs view
@@ -23,8 +23,11 @@ start :: [String] -> CommandStart start (name:description) | not (null description) = do u <- Remote.nameToUUID name- starting "describe" (ActionItemOther (Just name)) $+ starting "describe" ai si $ perform u $ unwords description+ where+ ai = ActionItemOther (Just name)+ si = SeekInput [name] start _ = giveup "Specify a repository and a description." perform :: UUID -> String -> CommandPerform
Command/Drop.hs view
@@ -76,35 +76,35 @@ where ww = WarnUnmatchLsFiles -start :: DropOptions -> Maybe Remote -> RawFilePath -> Key -> CommandStart-start o from file key = start' o from key afile ai+start :: DropOptions -> Maybe Remote -> SeekInput -> RawFilePath -> Key -> CommandStart+start o from si file key = start' o from key afile ai si where afile = AssociatedFile (Just file) ai = mkActionItem (key, afile) -start' :: DropOptions -> Maybe Remote -> Key -> AssociatedFile -> ActionItem -> CommandStart-start' o from key afile ai = +start' :: DropOptions -> Maybe Remote -> Key -> AssociatedFile -> ActionItem -> SeekInput -> CommandStart+start' o from key afile ai si = checkDropAuto (autoMode o) from afile key $ \numcopies -> stopUnless want $ case from of- Nothing -> startLocal afile ai numcopies key []- Just remote -> startRemote afile ai numcopies key remote+ Nothing -> startLocal afile ai si numcopies key []+ Just remote -> startRemote afile ai si numcopies key remote where want | autoMode o = wantDrop False (Remote.uuid <$> from) (Just key) afile | otherwise = return True -startKeys :: DropOptions -> Maybe Remote -> (Key, ActionItem) -> CommandStart-startKeys o from (key, ai) = start' o from key (AssociatedFile Nothing) ai+startKeys :: DropOptions -> Maybe Remote -> (SeekInput, Key, ActionItem) -> CommandStart+startKeys o from (si, key, ai) = start' o from key (AssociatedFile Nothing) ai si -startLocal :: AssociatedFile -> ActionItem -> NumCopies -> Key -> [VerifiedCopy] -> CommandStart-startLocal afile ai numcopies key preverified =- starting "drop" (OnlyActionOn key ai) $+startLocal :: AssociatedFile -> ActionItem -> SeekInput -> NumCopies -> Key -> [VerifiedCopy] -> CommandStart+startLocal afile ai si numcopies key preverified =+ starting "drop" (OnlyActionOn key ai) si $ performLocal key afile numcopies preverified -startRemote :: AssociatedFile -> ActionItem -> NumCopies -> Key -> Remote -> CommandStart-startRemote afile ai numcopies key remote = - starting ("drop " ++ Remote.name remote) (OnlyActionOn key ai) $+startRemote :: AssociatedFile -> ActionItem -> SeekInput -> NumCopies -> Key -> Remote -> CommandStart+startRemote afile ai si numcopies key remote = + starting ("drop " ++ Remote.name remote) (OnlyActionOn key ai) si $ performRemote key afile numcopies remote performLocal :: Key -> AssociatedFile -> NumCopies -> [VerifiedCopy] -> CommandPerform
Command/DropKey.hs view
@@ -41,8 +41,8 @@ where parsekey = maybe (Left "bad key") Right . deserializeKey -start :: Key -> CommandStart-start key = starting "dropkey" (mkActionItem key) $+start :: (SeekInput, Key) -> CommandStart+start (si, key) = starting "dropkey" (mkActionItem key) si $ perform key perform :: Key -> CommandPerform
Command/EnableRemote.hs view
@@ -58,31 +58,37 @@ -- the remote uuid. startNormalRemote :: Git.RemoteName -> [String] -> Git.Repo -> CommandStart startNormalRemote name restparams r- | null restparams = starting "enableremote" (ActionItemOther (Just name)) $ do+ | null restparams = starting "enableremote" ai si $ do setRemoteIgnore r False r' <- Remote.Git.configRead False r u <- getRepoUUID r' next $ return $ u /= NoUUID | otherwise = giveup $ "That is a normal git remote; passing these parameters does not make sense: " ++ unwords restparams+ where+ ai = ActionItemOther (Just name)+ si = SeekInput [name] startSpecialRemote :: Git.RemoteName -> Remote.RemoteConfig -> Maybe (UUID, Remote.RemoteConfig, Maybe (SpecialRemote.ConfigFrom UUID)) -> CommandStart startSpecialRemote name config Nothing = do m <- SpecialRemote.specialRemoteMap- confm <- Logs.Remote.readRemoteLog+ confm <- Logs.Remote.remoteConfigMap Remote.nameToUUID' name >>= \case Right u | u `M.member` m -> startSpecialRemote name config $ Just (u, fromMaybe M.empty (M.lookup u confm), Nothing) _ -> unknownNameError "Unknown remote name." startSpecialRemote name config (Just (u, c, mcu)) =- starting "enableremote" (ActionItemOther (Just name)) $ do+ starting "enableremote" ai si $ do let fullconfig = config `M.union` c t <- either giveup return (SpecialRemote.findType fullconfig) gc <- maybe (liftIO dummyRemoteGitConfig) (return . Remote.gitconfig) =<< Remote.byUUID u performSpecialRemote t u c fullconfig gc mcu+ where+ ai = ActionItemOther (Just name)+ si = SeekInput [name] performSpecialRemote :: RemoteType -> UUID -> R.RemoteConfig -> R.RemoteConfig -> RemoteGitConfig -> Maybe (SpecialRemote.ConfigFrom UUID) -> CommandPerform performSpecialRemote t u oldc c gc mcu = do
Command/EnableTor.hs view
@@ -47,12 +47,14 @@ start _os = do #endif #ifndef mingw32_HOST_OS+ let ai = ActionItemOther Nothing+ let si = SeekInput [] curruserid <- liftIO getEffectiveUserID if curruserid == 0 then case readish =<< headMaybe os of Nothing -> giveup "Need user-id parameter." Just userid -> go userid- else starting "enable-tor" (ActionItemOther Nothing) $ do+ else starting "enable-tor" ai si $ do gitannex <- liftIO programPath let ps = [Param (cmdname cmd), Param (show curruserid)] sucommand <- liftIO $ mkSuCommand gitannex ps
Command/ExamineKey.hs view
@@ -19,8 +19,8 @@ (paramRepeating paramKey) (batchable run (optional parseFormatOption)) -run :: Maybe Utility.Format.Format -> String -> Annex Bool-run format p = do+run :: Maybe Utility.Format.Format -> SeekInput -> String -> Annex Bool+run format _ p = do let k = fromMaybe (giveup "bad key") $ deserializeKey p showFormatted format (serializeKey' k) (keyVars k) return True
Command/Expire.hs view
@@ -60,13 +60,13 @@ start (Expire expire) noact actlog descs u = case lastact of Just ent | notexpired ent -> checktrust (== DeadTrusted) $- starting "unexpire" (ActionItemOther (Just desc)) $ do+ starting "unexpire" ai si $ do showNote =<< whenactive unless noact $ trustSet u SemiTrusted next $ return True _ -> checktrust (/= DeadTrusted) $- starting "expire" (ActionItemOther (Just desc)) $ do+ starting "expire" ai si $ do showNote =<< whenactive unless noact $ trustSet u DeadTrusted@@ -79,6 +79,8 @@ return $ "last active: " ++ fromDuration d ++ " ago" _ -> return "no activity" desc = fromUUID u ++ " " ++ fromUUIDDesc (fromMaybe mempty (M.lookup u descs))+ ai = ActionItemOther (Just desc)+ si = SeekInput [] notexpired ent = case ent of Unknown -> False VectorClock c -> case lookupexpire of
Command/Export.hs view
@@ -258,7 +258,7 @@ startExport r db cvar allfilledvar ti = do ek <- exportKey (Git.LsTree.sha ti) stopUnless (notrecordedpresent ek) $- starting ("export " ++ name r) (ActionItemOther (Just (fromRawFilePath f))) $+ starting ("export " ++ name r) ai si $ ifM (either (const False) id <$> tryNonAsync (checkPresentExport (exportActions r) (asKey ek) loc)) ( next $ cleanupExport r db ek loc False , do@@ -269,6 +269,8 @@ loc = mkExportLocation f f = getTopFilePath (Git.LsTree.file ti) af = AssociatedFile (Just f)+ ai = ActionItemOther (Just (fromRawFilePath f))+ si = SeekInput [] notrecordedpresent ek = (||) <$> liftIO (notElem loc <$> getExportedLocation db (asKey ek)) -- If content was removed from the remote, the export db@@ -321,18 +323,23 @@ eks <- forM (filter (`notElem` nullShas) shas) exportKey if null eks then stop- else starting ("unexport " ++ name r) (ActionItemOther (Just (fromRawFilePath f'))) $+ else starting ("unexport " ++ name r) ai si $ performUnexport r db eks loc where loc = mkExportLocation f' f' = getTopFilePath f+ ai = ActionItemOther (Just (fromRawFilePath f'))+ si = SeekInput [] startUnexport' :: Remote -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart-startUnexport' r db f ek = starting ("unexport " ++ name r) (ActionItemOther (Just (fromRawFilePath f'))) $- performUnexport r db [ek] loc+startUnexport' r db f ek =+ starting ("unexport " ++ name r) ai si $+ performUnexport r db [ek] loc where loc = mkExportLocation f' f' = getTopFilePath f+ ai = ActionItemOther (Just (fromRawFilePath f'))+ si = SeekInput [] -- Unlike a usual drop from a repository, this does not check that -- numcopies is satisfied before removing the content. Typically an export@@ -373,30 +380,36 @@ | otherwise = do ek <- exportKey sha let loc = exportTempName ek- starting ("unexport " ++ name r) (ActionItemOther (Just (fromRawFilePath (fromExportLocation loc)))) $ do+ let ai = ActionItemOther (Just (fromRawFilePath (fromExportLocation loc)))+ let si = SeekInput []+ starting ("unexport " ++ name r) ai si $ do liftIO $ removeExportedLocation db (asKey ek) oldloc performUnexport r db [ek] loc where oldloc = mkExportLocation $ getTopFilePath oldf startMoveToTempName :: Remote -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart-startMoveToTempName r db f ek = starting ("rename " ++ name r) - (ActionItemOther $ Just $ fromRawFilePath f' ++ " -> " ++ fromRawFilePath (fromExportLocation tmploc))- (performRename r db ek loc tmploc)+startMoveToTempName r db f ek = + starting ("rename " ++ name r) ai si $+ performRename r db ek loc tmploc where loc = mkExportLocation f' f' = getTopFilePath f tmploc = exportTempName ek+ ai = ActionItemOther $ Just $ fromRawFilePath f' ++ " -> " ++ fromRawFilePath (fromExportLocation tmploc)+ si = SeekInput [] startMoveFromTempName :: Remote -> ExportHandle -> ExportKey -> TopFilePath -> CommandStart startMoveFromTempName r db ek f = do let tmploc = exportTempName ek+ let ai = ActionItemOther (Just (fromRawFilePath (fromExportLocation tmploc) ++ " -> " ++ fromRawFilePath f')) stopUnless (liftIO $ elem tmploc <$> getExportedLocation db (asKey ek)) $- starting ("rename " ++ name r) (ActionItemOther (Just (fromRawFilePath (fromExportLocation tmploc) ++ " -> " ++ fromRawFilePath f'))) $+ starting ("rename " ++ name r) ai si $ performRename r db ek tmploc loc where loc = mkExportLocation f' f' = getTopFilePath f+ si = SeekInput [] performRename :: Remote -> ExportHandle -> ExportKey -> ExportLocation -> ExportLocation -> CommandPerform performRename r db ek src dest =
Command/Find.hs view
@@ -73,14 +73,14 @@ where ww = WarnUnmatchLsFiles -start :: FindOptions -> RawFilePath -> Key -> CommandStart-start o file key = startingCustomOutput key $ do+start :: FindOptions -> SeekInput -> RawFilePath -> Key -> CommandStart+start o _ file key = startingCustomOutput key $ do showFormatted (formatOption o) file $ ("file", fromRawFilePath file) : keyVars key next $ return True -startKeys :: FindOptions -> (Key, ActionItem) -> CommandStart-startKeys o (key, ActionItemBranchFilePath (BranchFilePath _ topf) _) = - start o (getTopFilePath topf) key+startKeys :: FindOptions -> (SeekInput, Key, ActionItem) -> CommandStart+startKeys o (si, key, ActionItemBranchFilePath (BranchFilePath _ topf) _) = + start o si (getTopFilePath topf) key startKeys _ _ = stop showFormatted :: Maybe Utility.Format.Format -> S.ByteString -> [(String, String)] -> Annex ()
Command/Fix.hs view
@@ -43,8 +43,8 @@ data FixWhat = FixSymlinks | FixAll -start :: FixWhat -> RawFilePath -> Key -> CommandStart-start fixwhat file key = do+start :: FixWhat -> SeekInput -> RawFilePath -> Key -> CommandStart+start fixwhat si file key = do currlink <- liftIO $ catchMaybeIO $ R.readSymbolicLink file wantlink <- calcRepo $ gitAnnexLink (fromRawFilePath file) key case currlink of@@ -56,7 +56,7 @@ FixAll -> fixthin FixSymlinks -> stop where- fixby = starting "fix" (mkActionItem (key, file))+ fixby = starting "fix" (mkActionItem (key, file)) si fixthin = do obj <- calcRepo (gitAnnexLocation key) stopUnless (isUnmodified key file <&&> isUnmodified key obj) $ do
Command/Forget.hs view
@@ -12,6 +12,7 @@ import Logs.Transitions import qualified Annex import Annex.VectorClock+import Git.Types cmd :: Command cmd = command "forget" SectionMaintenance @@ -33,13 +34,16 @@ seek = commandAction . start start :: ForgetOptions -> CommandStart-start o = starting "forget" (ActionItemOther (Just "git-annex")) $ do+start o = starting "forget" ai si $ do c <- liftIO currentVectorClock let basets = addTransition c ForgetGitHistory noTransitions let ts = if dropDead o then addTransition c ForgetDeadRemotes basets else basets perform ts =<< Annex.getState Annex.force+ where+ ai = ActionItemOther (Just (fromRef Branch.name))+ si = SeekInput [] perform :: Transitions -> Bool -> CommandPerform perform ts True = do
Command/FromKey.hs view
@@ -45,27 +45,30 @@ withPairs (commandAction . start force) ps seekBatch :: BatchFormat -> CommandSeek-seekBatch fmt = batchInput fmt parse commandAction+seekBatch fmt = batchInput fmt parse (commandAction . go) where parse s = do let (keyname, file) = separate (== ' ') s if not (null keyname) && not (null file) then do file' <- liftIO $ relPathCwdToFile file- return $ Right $ go file' (keyOpt keyname)+ return $ Right (file', keyOpt keyname) else return $ Left "Expected pairs of key and filename"- go file key = starting "fromkey" (mkActionItem (key, toRawFilePath file)) $- perform key file+ go (si, (file, key)) = + let ai = mkActionItem (key, toRawFilePath file)+ in starting "fromkey" ai si $+ perform key file -start :: Bool -> (String, FilePath) -> CommandStart-start force (keyname, file) = do+start :: Bool -> (SeekInput, (String, FilePath)) -> CommandStart+start force (si, (keyname, file)) = do let key = keyOpt keyname unless force $ do inbackend <- inAnnex key unless inbackend $ giveup $ "key ("++ keyname ++") is not present in backend (use --force to override this sanity check)"- starting "fromkey" (mkActionItem (key, toRawFilePath file)) $+ let ai = mkActionItem (key, toRawFilePath file)+ starting "fromkey" ai si $ perform key file -- From user input to a Key.
Command/Fsck.hs view
@@ -111,8 +111,8 @@ whenM ((==) DeadTrusted <$> lookupTrust u) $ earlyWarning "Warning: Fscking a repository that is currently marked as dead." -start :: Maybe Remote -> Incremental -> RawFilePath -> Key -> CommandStart-start from inc file key = Backend.getBackend (fromRawFilePath file) key >>= \case+start :: Maybe Remote -> Incremental -> SeekInput -> RawFilePath -> Key -> CommandStart+start from inc si file key = Backend.getBackend (fromRawFilePath file) key >>= \case Nothing -> stop Just backend -> do numcopies <- getFileNumCopies (fromRawFilePath file)@@ -120,7 +120,7 @@ Nothing -> go $ perform key file backend numcopies Just r -> go $ performRemote key afile backend numcopies r where- go = runFsck inc (mkActionItem (key, afile)) key+ go = runFsck inc si (mkActionItem (key, afile)) key afile = AssociatedFile (Just file) perform :: Key -> RawFilePath -> Backend -> NumCopies -> Annex Bool@@ -197,11 +197,11 @@ Just a -> isRight <$> tryNonAsync (a key afile tmp) Nothing -> return False -startKey :: Maybe Remote -> Incremental -> (Key, ActionItem) -> NumCopies -> CommandStart-startKey from inc (key, ai) numcopies =+startKey :: Maybe Remote -> Incremental -> (SeekInput, Key, ActionItem) -> NumCopies -> CommandStart+startKey from inc (si, key, ai) numcopies = Backend.maybeLookupBackendVariety (fromKey keyVariety key) >>= \case Nothing -> stop- Just backend -> runFsck inc ai key $+ Just backend -> runFsck inc si ai key $ case from of Nothing -> performKey key backend numcopies Just r -> performRemote key (AssociatedFile Nothing) backend numcopies r@@ -555,9 +555,9 @@ (False, Right ()) -> "dropped from " ++ Remote.name remote (_, Left e) -> "failed to drop from" ++ Remote.name remote ++ ": " ++ show e -runFsck :: Incremental -> ActionItem -> Key -> Annex Bool -> CommandStart-runFsck inc ai key a = stopUnless (needFsck inc key) $- starting "fsck" (OnlyActionOn key ai) $ do+runFsck :: Incremental -> SeekInput -> ActionItem -> Key -> Annex Bool -> CommandStart+runFsck inc si ai key a = stopUnless (needFsck inc key) $+ starting "fsck" (OnlyActionOn key ai) si $ do ok <- a when ok $ recordFsckTime inc key
Command/FuzzTest.hs view
@@ -36,7 +36,7 @@ start = do guardTest logf <- fromRepo gitAnnexFuzzTestLogFile- showStart "fuzztest" (toRawFilePath logf)+ showStart "fuzztest" (toRawFilePath logf) (SeekInput []) logh <- liftIO $ openFile logf WriteMode void $ forever $ fuzz logh stop
Command/GCryptSetup.hs view
@@ -22,7 +22,7 @@ seek = withStrings (commandAction . start) start :: String -> CommandStart-start gcryptid = starting "gcryptsetup" (ActionItemOther Nothing) $ do+start gcryptid = starting "gcryptsetup" (ActionItemOther Nothing) (SeekInput [gcryptid]) $ do u <- getUUID when (u /= NoUUID) $ giveup "gcryptsetup refusing to run; this repository already has a git-annex uuid!"
Command/Get.hs view
@@ -54,8 +54,8 @@ where ww = WarnUnmatchLsFiles -start :: GetOptions -> Maybe Remote -> RawFilePath -> Key -> CommandStart-start o from file key = start' expensivecheck from key afile ai+start :: GetOptions -> Maybe Remote -> SeekInput -> RawFilePath -> Key -> CommandStart+start o from si file key = start' expensivecheck from key afile ai si where afile = AssociatedFile (Just file) ai = mkActionItem (key, afile)@@ -64,12 +64,12 @@ <||> wantGet False (Just key) afile | otherwise = return True -startKeys :: Maybe Remote -> (Key, ActionItem) -> CommandStart-startKeys from (key, ai) = checkFailedTransferDirection ai Download $- start' (return True) from key (AssociatedFile Nothing) ai+startKeys :: Maybe Remote -> (SeekInput, Key, ActionItem) -> CommandStart+startKeys from (si, key, ai) = checkFailedTransferDirection ai Download $+ start' (return True) from key (AssociatedFile Nothing) ai si -start' :: Annex Bool -> Maybe Remote -> Key -> AssociatedFile -> ActionItem -> CommandStart-start' expensivecheck from key afile ai =+start' :: Annex Bool -> Maybe Remote -> Key -> AssociatedFile -> ActionItem -> SeekInput -> CommandStart+start' expensivecheck from key afile ai si = stopUnless expensivecheck $ case from of Nothing -> go $ perform key afile@@ -77,7 +77,7 @@ stopUnless (Command.Move.fromOk src key) $ go $ Command.Move.fromPerform src Command.Move.RemoveNever key afile where- go = starting "get" (OnlyActionOn key ai)+ go = starting "get" (OnlyActionOn key ai) si perform :: Key -> AssociatedFile -> CommandPerform perform key afile = stopUnless (getKey key afile) $
Command/Group.hs view
@@ -22,10 +22,13 @@ seek = withWords (commandAction . start) start :: [String] -> CommandStart-start (name:g:[]) = do+start ps@(name:g:[]) = do u <- Remote.nameToUUID name- startingUsualMessages "group" (ActionItemOther (Just name)) $+ startingUsualMessages "group" ai si $ setGroup u (toGroup g)+ where+ ai = ActionItemOther (Just name)+ si = SeekInput ps start (name:[]) = do u <- Remote.nameToUUID name startingCustomOutput (ActionItemOther Nothing) $ do
Command/GroupWanted.hs view
@@ -24,6 +24,9 @@ start :: [String] -> CommandStart start (g:[]) = startingCustomOutput (ActionItemOther Nothing) $ performGet groupPreferredContentMapRaw (toGroup g)-start (g:expr:[]) = startingUsualMessages "groupwanted" (ActionItemOther (Just g)) $+start ps@(g:expr:[]) = startingUsualMessages "groupwanted" ai si $ performSet groupPreferredContentSet expr (toGroup g)+ where+ ai = ActionItemOther (Just g)+ si = SeekInput ps start _ = giveup "Specify a group."
Command/Import.hs view
@@ -49,12 +49,14 @@ = LocalImportOptions { importFiles :: CmdParams , duplicateMode :: DuplicateMode+ , checkGitIgnoreOption :: CheckGitIgnore } | RemoteImportOptions { importFromRemote :: DeferredParse Remote , importToBranch :: Branch , importToSubDir :: Maybe FilePath , importContent :: Bool+ , checkGitIgnoreOption :: CheckGitIgnore } optParser :: CmdParamsDesc -> Parser ImportOptions@@ -65,8 +67,9 @@ ( help "do not get contents of imported files" ) dupmode <- fromMaybe Default <$> optional duplicateModeParser+ ic <- Command.Add.checkGitIgnoreSwitch return $ case mfromremote of- Nothing -> LocalImportOptions ps dupmode+ Nothing -> LocalImportOptions ps dupmode ic Just r -> case ps of [bs] -> let (branch, subdir) = separate (== ':') bs@@ -74,6 +77,7 @@ (Ref (encodeBS' branch)) (if null subdir then Nothing else Just subdir) content+ ic _ -> giveup "expected BRANCH[:SUBDIR]" data DuplicateMode = Default | Duplicate | DeDuplicate | CleanDuplicates | SkipDuplicates | ReinjectDuplicates@@ -110,7 +114,7 @@ giveup $ "cannot import files from inside the working tree (use git annex add instead): " ++ unwords inrepops largematcher <- largeFilesMatcher addunlockedmatcher <- addUnlockedMatcher- (commandAction . startLocal addunlockedmatcher largematcher (duplicateMode o))+ (commandAction . startLocal o addunlockedmatcher largematcher (duplicateMode o)) `withPathContents` importFiles o seek o@(RemoteImportOptions {}) = startConcurrency commandStages $ do r <- getParsed (importFromRemote o)@@ -120,16 +124,18 @@ (pure Nothing) (Just <$$> inRepo . toTopFilePath . toRawFilePath) (importToSubDir o)- seekRemote r (importToBranch o) subdir (importContent o)+ seekRemote r (importToBranch o) subdir (importContent o) (checkGitIgnoreOption o) -startLocal :: AddUnlockedMatcher -> GetFileMatcher -> DuplicateMode -> (FilePath, FilePath) -> CommandStart-startLocal addunlockedmatcher largematcher mode (srcfile, destfile) =+startLocal :: ImportOptions -> AddUnlockedMatcher -> GetFileMatcher -> DuplicateMode -> (FilePath, FilePath) -> CommandStart+startLocal o addunlockedmatcher largematcher mode (srcfile, destfile) = ifM (liftIO $ isRegularFile <$> getSymbolicLinkStatus srcfile)- ( starting "import" (ActionItemWorkTreeFile destfile')- pickaction+ ( starting "import" ai si pickaction , stop ) where+ ai = ActionItemWorkTreeFile destfile'+ si = SeekInput []+ destfile' = toRawFilePath destfile deletedup k = do@@ -146,10 +152,10 @@ showNote "reinjecting" Command.Reinject.perform srcfile k importfile ld k = checkdestdir $ do- ignored <- not <$> Annex.getState Annex.force <&&> checkIgnored destfile+ ignored <- checkIgnored (checkGitIgnoreOption o) destfile if ignored then do- warning $ "not importing " ++ destfile ++ " which is .gitignored (use --force to override)"+ warning $ "not importing " ++ destfile ++ " which is .gitignored (use --no-check-gitignore to override)" stop else do existing <- liftIO (catchMaybeIO $ getSymbolicLinkStatus destfile)@@ -208,18 +214,18 @@ } } ifM (checkFileMatcher largematcher destfile)- ( ingestAdd' nullMeterUpdate (Just ld') (Just k)+ ( ingestAdd' (checkGitIgnoreOption o) nullMeterUpdate (Just ld') (Just k) >>= maybe stop (\addedk -> next $ Command.Add.cleanup addedk True)- , next $ Command.Add.addSmall destfile'+ , next $ Command.Add.addSmall (checkGitIgnoreOption o) destfile' ) notoverwriting why = do warning $ "not overwriting existing " ++ destfile ++ " " ++ why stop lockdown a = do let mi = MatchingFile $ FileInfo- { currFile = toRawFilePath srcfile+ { contentFile = Just (toRawFilePath srcfile) , matchFile = toRawFilePath destfile } lockingfile <- not <$> addUnlocked addunlockedmatcher mi@@ -264,8 +270,8 @@ verifyEnoughCopiesToDrop [] key Nothing need [] preverified tocheck (const yes) no -seekRemote :: Remote -> Branch -> Maybe TopFilePath -> Bool -> CommandSeek-seekRemote remote branch msubdir importcontent = do+seekRemote :: Remote -> Branch -> Maybe TopFilePath -> Bool -> CheckGitIgnore -> CommandSeek+seekRemote remote branch msubdir importcontent ci = do importtreeconfig <- case msubdir of Nothing -> return ImportTree Just subdir ->@@ -282,7 +288,7 @@ let commitimport = commitRemote remote branch tb trackingcommit importtreeconfig importcommitconfig importabletvar <- liftIO $ newTVarIO Nothing- void $ includeCommandAction (listContents remote importabletvar)+ void $ includeCommandAction (listContents remote importtreeconfig ci importabletvar) liftIO (atomically (readTVar importabletvar)) >>= \case Nothing -> return () Just importable -> importKeys remote importtreeconfig importcontent importable >>= \case@@ -301,25 +307,32 @@ fromtrackingbranch a = inRepo $ a (fromRemoteTrackingBranch tb) -listContents :: Remote -> TVar (Maybe (ImportableContents (ContentIdentifier, Remote.ByteSize))) -> CommandStart-listContents remote tvar = starting "list" (ActionItemOther (Just (Remote.name remote))) $- listImportableContents remote >>= \case- Nothing -> giveup $ "Unable to list contents of " ++ Remote.name remote- Just importable -> do- importable' <- makeImportMatcher remote >>= \case- Right matcher -> filterImportableContents remote matcher importable- Left err -> giveup $ "Cannot import from " ++ Remote.name remote ++ " because of a problem with its configuration: " ++ err- next $ do- liftIO $ atomically $ writeTVar tvar (Just importable')+listContents :: Remote -> ImportTreeConfig -> CheckGitIgnore -> TVar (Maybe (ImportableContents (ContentIdentifier, Remote.ByteSize))) -> CommandStart+listContents remote importtreeconfig ci tvar = starting "list" ai si $+ makeImportMatcher remote >>= \case+ Right matcher -> getImportableContents remote importtreeconfig ci matcher >>= \case+ Just importable -> next $ do+ liftIO $ atomically $ writeTVar tvar (Just importable) return True+ Nothing -> giveup $ "Unable to list contents of " ++ Remote.name remote+ Left err -> giveup $ unwords + [ "Cannot import from"+ , Remote.name remote+ , "because of a problem with its configuration:"+ , err+ ]+ where+ ai = ActionItemOther (Just (Remote.name remote))+ si = SeekInput [] commitRemote :: Remote -> Branch -> RemoteTrackingBranch -> Maybe Sha -> ImportTreeConfig -> ImportCommitConfig -> ImportableContents (Either Sha Key) -> CommandStart commitRemote remote branch tb trackingcommit importtreeconfig importcommitconfig importable =- starting "update" (ActionItemOther (Just $ fromRef $ fromRemoteTrackingBranch tb)) $ do+ starting "update" ai si $ do importcommit <- buildImportCommit remote importtreeconfig importcommitconfig importable next $ updateremotetrackingbranch importcommit- where+ ai = ActionItemOther (Just $ fromRef $ fromRemoteTrackingBranch tb)+ si = SeekInput [] -- Update the tracking branch. Done even when there -- is nothing new to import, to make sure it exists. updateremotetrackingbranch importcommit =
Command/ImportFeed.hs view
@@ -34,7 +34,7 @@ import qualified Utility.Format import Utility.Tmp import Utility.Metered-import Command.AddUrl (addUrlFile, downloadRemoteFile, parseDownloadOptions, DownloadOptions(..))+import Command.AddUrl (addUrlFile, downloadRemoteFile, parseDownloadOptions, DownloadOptions(..), checkCanAdd) import Annex.UUID import Backend.URL (fromUrl) import Annex.Content@@ -78,7 +78,7 @@ getFeed :: AddUnlockedMatcher -> ImportFeedOptions -> Cache -> URLString -> CommandSeek getFeed addunlockedmatcher opts cache url = do- showStart' "importfeed" (Just url)+ showStartOther "importfeed" (Just url) (SeekInput []) downloadFeed url >>= \case Nothing -> showEndResult =<< feedProblem url "downloading the feed failed"@@ -124,7 +124,7 @@ getCache opttemplate = ifM (Annex.getState Annex.force) ( ret S.empty S.empty , do- showStart "importfeed" "checking known urls"+ showStart "importfeed" "checking known urls" (SeekInput []) (is, us) <- unzip <$> knownItems showEndOk ret (S.fromList us) (S.fromList (concat is))@@ -198,28 +198,28 @@ -- don't use youtube-dl , rawOption = True }- let go urlinfo = maybeToList <$> addUrlFile addunlockedmatcher dlopts url urlinfo f+ let go urlinfo = Just . maybeToList <$> addUrlFile addunlockedmatcher dlopts url urlinfo f if relaxedOption (downloadOptions opts) then go Url.assumeUrlExists else Url.withUrlOptions (Url.getUrlInfo url) >>= \case Right urlinfo -> go urlinfo Left err -> do warning err- return []+ return (Just []) else do res <- tryNonAsync $ maybe (error $ "unable to checkUrl of " ++ Remote.name r) (flip id url) (Remote.checkUrl r) case res of- Left _ -> return []+ Left _ -> return (Just []) Right (UrlContents sz _) ->- maybeToList <$>+ Just . maybeToList <$> downloadRemoteFile addunlockedmatcher r (downloadOptions opts) url f sz Right (UrlMulti l) -> do kl <- forM l $ \(url', sz, subf) -> downloadRemoteFile addunlockedmatcher r (downloadOptions opts) url' (f </> sanitizeFilePath subf) sz- return $ if all isJust kl+ return $ Just $ if all isJust kl then catMaybes kl else [] @@ -256,20 +256,27 @@ case dest of Nothing -> return True Just f -> do- showStart' "addurl" (Just url)- ks <- getter f- if null ks- then do+ showStartOther "addurl" (Just url) (SeekInput [])+ getter f >>= \case+ Just ks+ -- Download problem.+ | null ks -> do+ showEndFail+ checkFeedBroken (feedurl todownload)+ | otherwise -> do+ forM_ ks $ \key ->+ ifM (annexGenMetaData <$> Annex.getGitConfig)+ ( addMetaData key $ extractMetaData todownload+ , addMetaData key $ minimalMetaData todownload+ )+ showEndOk+ return True+ -- Was not able to add anything,+ -- but not because of a download + -- problem.+ Nothing -> do showEndFail- checkFeedBroken (feedurl todownload)- else do- forM_ ks $ \key ->- ifM (annexGenMetaData <$> Annex.getGitConfig)- ( addMetaData key $ extractMetaData todownload- , addMetaData key $ minimalMetaData todownload- )- showEndOk- return True+ return False {- Find a unique filename to save the url to. - If the file exists, prefixes it with a number.@@ -300,15 +307,16 @@ | rawOption (downloadOptions opts) = downloadlink | otherwise = do r <- withTmpWorkDir mediakey $ \workdir -> do- dl <- youtubeDl linkurl workdir+ dl <- youtubeDl linkurl workdir nullMeterUpdate case dl of Right (Just mediafile) -> do let ext = case takeExtension mediafile of [] -> ".m" s -> s- ok <- rundownload linkurl ext $ \f -> do- addWorkTree addunlockedmatcher webUUID mediaurl f mediakey (Just mediafile)- return [mediakey]+ ok <- rundownload linkurl ext $ \f ->+ checkCanAdd (downloadOptions opts) f $ \canadd -> do+ addWorkTree canadd addunlockedmatcher webUUID mediaurl f mediakey (Just mediafile)+ return (Just [mediakey]) return (Just ok) -- youtude-dl didn't support it, so -- download it as if the link were@@ -325,9 +333,10 @@ addmediafast linkurl mediaurl mediakey = ifM (pure (not (rawOption (downloadOptions opts))) <&&> youtubeDlSupported linkurl)- ( rundownload linkurl ".m" $ \f -> do- addWorkTree addunlockedmatcher webUUID mediaurl f mediakey Nothing- return [mediakey]+ ( rundownload linkurl ".m" $ \f ->+ checkCanAdd (downloadOptions opts) f $ \canadd -> do+ addWorkTree canadd addunlockedmatcher webUUID mediaurl f mediakey Nothing+ return (Just [mediakey]) , performDownload addunlockedmatcher opts cache todownload { location = Enclosure linkurl } )
Command/InAnnex.hs view
@@ -20,8 +20,8 @@ seek :: CmdParams -> CommandSeek seek = withKeys (commandAction . start) -start :: Key -> CommandStart-start key = inAnnexSafe key >>= dispatch+start :: (SeekInput, Key) -> CommandStart+start (_, key) = inAnnexSafe key >>= dispatch where dispatch (Just True) = stop dispatch (Just False) = exit 1
Command/Info.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -125,7 +125,7 @@ globalInfo o stop start o ps = do- mapM_ (itemInfo o) ps+ mapM_ (\p -> itemInfo o (SeekInput [p], p)) ps stop globalInfo :: InfoOptions -> Annex ()@@ -135,34 +135,34 @@ whenM ((==) DeadTrusted <$> lookupTrust u) $ earlyWarning "Warning: This repository is currently marked as dead." stats <- selStats global_fast_stats global_slow_stats- showCustom "info" $ do+ showCustom "info" (SeekInput []) $ do evalStateT (mapM_ showStat stats) (emptyStatInfo o) return True -itemInfo :: InfoOptions -> String -> Annex ()-itemInfo o p = ifM (isdir p)- ( dirInfo o p+itemInfo :: InfoOptions -> (SeekInput, String) -> Annex ()+itemInfo o (si, p) = ifM (isdir p)+ ( dirInfo o p si , do disallowMatchingOptions v <- Remote.byName' p case v of- Right r -> remoteInfo o r+ Right r -> remoteInfo o r si Left _ -> do v' <- Remote.nameToUUID' p case v' of- Right u -> uuidInfo o u+ Right u -> uuidInfo o u si Left _ -> do relp <- liftIO $ relPathCwdToFile p ifAnnexed (toRawFilePath relp)- (fileInfo o relp)- (treeishInfo o p)+ (fileInfo o relp si)+ (treeishInfo o p si) ) where isdir = liftIO . catchBoolIO . (isDirectory <$$> getFileStatus) -noInfo :: String -> Annex ()-noInfo s = do- showStart "info" (encodeBS' s)+noInfo :: String -> SeekInput -> Annex ()+noInfo s si = do+ showStart "info" (encodeBS' s) si showNote $ "not a directory or an annexed file or a treeish or a remote or a uuid" showEndFail @@ -170,8 +170,8 @@ disallowMatchingOptions = whenM Limit.limited $ giveup "File matching options can only be used when getting info on a directory." -dirInfo :: InfoOptions -> FilePath -> Annex ()-dirInfo o dir = showCustom (unwords ["info", dir]) $ do+dirInfo :: InfoOptions -> FilePath -> SeekInput -> Annex ()+dirInfo o dir si = showCustom (unwords ["info", dir]) si $ do stats <- selStats (tostats (dir_name:tree_fast_stats True)) (tostats tree_slow_stats)@@ -180,12 +180,12 @@ where tostats = map (\s -> s dir) -treeishInfo :: InfoOptions -> String -> Annex ()-treeishInfo o t = do+treeishInfo :: InfoOptions -> String -> SeekInput -> Annex ()+treeishInfo o t si = do mi <- getTreeStatInfo o (Git.Ref (encodeBS' t)) case mi of- Nothing -> noInfo t- Just i -> showCustom (unwords ["info", t]) $ do+ Nothing -> noInfo t si+ Just i -> showCustom (unwords ["info", t]) si $ do stats <- selStats (tostats (tree_name:tree_fast_stats False)) (tostats tree_slow_stats)@@ -194,13 +194,13 @@ where tostats = map (\s -> s t) -fileInfo :: InfoOptions -> FilePath -> Key -> Annex ()-fileInfo o file k = showCustom (unwords ["info", file]) $ do+fileInfo :: InfoOptions -> FilePath -> SeekInput -> Key -> Annex ()+fileInfo o file si k = showCustom (unwords ["info", file]) si $ do evalStateT (mapM_ showStat (file_stats file k)) (emptyStatInfo o) return True -remoteInfo :: InfoOptions -> Remote -> Annex ()-remoteInfo o r = showCustom (unwords ["info", Remote.name r]) $ do+remoteInfo :: InfoOptions -> Remote -> SeekInput -> Annex ()+remoteInfo o r si = showCustom (unwords ["info", Remote.name r]) si $ do i <- map (\(k, v) -> simpleStat k (pure v)) <$> Remote.getInfo r let u = Remote.uuid r l <- selStats @@ -209,8 +209,8 @@ evalStateT (mapM_ showStat l) (emptyStatInfo o) return True -uuidInfo :: InfoOptions -> UUID -> Annex ()-uuidInfo o u = showCustom (unwords ["info", fromUUID u]) $ do+uuidInfo :: InfoOptions -> UUID -> SeekInput -> Annex ()+uuidInfo o u si = showCustom (unwords ["info", fromUUID u]) si $ do l <- selStats (uuid_fast_stats u) (uuid_slow_stats u) evalStateT (mapM_ showStat l) (emptyStatInfo o) return True@@ -532,8 +532,9 @@ let combinedata d uk = finishCheck uk >>= \case Nothing -> return d Just k -> return $ addKey k d- v <- lift $ foldM combinedata emptyKeyInfo- =<< loggedKeysFor' u+ (ks, cleanup) <- lift $ loggedKeysFor' u+ v <- lift $ foldM combinedata emptyKeyInfo ks+ liftIO $ void cleanup put s { repoData = M.insert u v (repoData s) } return v @@ -567,7 +568,7 @@ where initial = (emptyKeyInfo, emptyKeyInfo, emptyNumCopiesStats, M.empty) update matcher fast key file vs@(presentdata, referenceddata, numcopiesstats, repodata) =- ifM (matcher $ MatchingFile $ FileInfo file file)+ ifM (matcher $ MatchingFile $ FileInfo (Just file) file) ( do !presentdata' <- ifM (inAnnex key) ( return $ addKey key presentdata
Command/Init.hs view
@@ -53,10 +53,14 @@ start :: InitOptions -> CommandStart start os- | autoEnableOnly os = starting "init" (ActionItemOther (Just "autoenable")) $- performAutoEnableOnly- | otherwise = starting "init" (ActionItemOther (Just $ initDesc os)) $- perform os+ | autoEnableOnly os = + starting "init" (ActionItemOther (Just "autoenable")) si $+ performAutoEnableOnly+ | otherwise = + starting "init" (ActionItemOther (Just $ initDesc os)) si $+ perform os+ where+ si = SeekInput [] perform :: InitOptions -> CommandPerform perform os = do
Command/InitRemote.hs view
@@ -62,25 +62,26 @@ start o (name:ws) = ifM (isJust <$> findExisting name) ( giveup $ "There is already a special remote named \"" ++ name ++ "\". (Use enableremote to enable an existing special remote.)"- , do- ifM (isJust <$> Remote.byNameOnly name)- ( giveup $ "There is already a remote named \"" ++ name ++ "\""- , do- sameasuuid <- maybe- (pure Nothing)- (Just . Sameas <$$> getParsed)- (sameas o) - c <- newConfig name sameasuuid- (Logs.Remote.keyValToConfig Proposed ws)- <$> readRemoteLog- t <- either giveup return (findType c)- if whatElse o- then startingCustomOutput (ActionItemOther Nothing) $- describeOtherParamsFor c t- else starting "initremote" (ActionItemOther (Just name)) $- perform t name c o- )+ , ifM (isJust <$> Remote.byNameOnly name)+ ( giveup $ "There is already a remote named \"" ++ name ++ "\""+ , do+ sameasuuid <- maybe+ (pure Nothing)+ (Just . Sameas <$$> getParsed)+ (sameas o) + c <- newConfig name sameasuuid+ (Logs.Remote.keyValToConfig Proposed ws)+ <$> remoteConfigMap+ t <- either giveup return (findType c)+ if whatElse o+ then startingCustomOutput (ActionItemOther Nothing) $+ describeOtherParamsFor c t+ else starting "initremote" (ActionItemOther (Just name)) si $+ perform t name c o+ ) )+ where+ si = SeekInput [name] perform :: RemoteType -> String -> R.RemoteConfig -> InitRemoteOptions -> CommandPerform perform t name c o = do
Command/Inprogress.hs view
@@ -48,8 +48,8 @@ where ww = WarnUnmatchLsFiles -start :: S.Set Key -> RawFilePath -> Key -> CommandStart-start s _file k+start :: S.Set Key -> SeekInput -> RawFilePath -> Key -> CommandStart+start s _si _file k | S.member k s = start' k | otherwise = stop
Command/List.hs view
@@ -77,8 +77,8 @@ printHeader :: [(UUID, RemoteName, TrustLevel)] -> Annex () printHeader l = liftIO $ putStrLn $ lheader $ map (\(_, n, t) -> (n, t)) l -start :: [(UUID, RemoteName, TrustLevel)] -> RawFilePath -> Key -> CommandStart-start l file key = do+start :: [(UUID, RemoteName, TrustLevel)] -> SeekInput -> RawFilePath -> Key -> CommandStart+start l _si file key = do ls <- S.fromList <$> keyLocations key liftIO $ putStrLn $ format (map (\(u, _, t) -> (t, S.member u ls)) l) file stop
Command/Lock.hs view
@@ -37,10 +37,10 @@ , usesLocationLog = False } -start :: RawFilePath -> Key -> CommandStart-start file key = ifM (isJust <$> isAnnexLink file)+start :: SeekInput -> RawFilePath -> Key -> CommandStart+start si file key = ifM (isJust <$> isAnnexLink file) ( stop- , starting "lock" (mkActionItem (key, file)) $+ , starting "lock" (mkActionItem (key, file)) si $ go =<< liftIO (isPointerFile file) ) where@@ -60,7 +60,7 @@ perform :: RawFilePath -> Key -> CommandPerform perform file key = do lockdown =<< calcRepo (gitAnnexLocation key)- addLink (fromRawFilePath file) key+ addLink (CheckGitIgnore False) (fromRawFilePath file) key =<< withTSDelta (liftIO . genInodeCache file) next $ cleanup file key where
Command/Log.hs view
@@ -100,8 +100,8 @@ where ww = WarnUnmatchLsFiles -start :: LogOptions -> (FilePath -> Outputter) -> RawFilePath -> Key -> CommandStart-start o outputter file key = do+start :: LogOptions -> (FilePath -> Outputter) -> SeekInput -> RawFilePath -> Key -> CommandStart+start o outputter _ file key = do (changes, cleanup) <- getKeyLog key (passthruOptions o) showLogIncremental (outputter (fromRawFilePath file)) changes void $ liftIO cleanup
Command/LookupKey.hs view
@@ -18,8 +18,8 @@ (paramRepeating paramFile) (batchable run (pure ())) -run :: () -> String -> Annex Bool-run _ file = seekSingleGitFile file >>= \case+run :: () -> SeekInput -> String -> Annex Bool+run _ _ file = seekSingleGitFile file >>= \case Nothing -> return False Just file' -> catKeyFile file' >>= \case Just k -> do
Command/Map.hs view
@@ -197,7 +197,7 @@ {- reads the config of a remote, with progress display -} scan :: Git.Repo -> Annex Git.Repo scan r = do- showStart' "map" (Just $ Git.repoDescribe r)+ showStartOther "map" (Just $ Git.repoDescribe r) (SeekInput []) v <- tryScan r case v of Just r' -> do
Command/MatchExpression.hs view
@@ -37,9 +37,9 @@ ( long "largefiles" <> help "parse as annex.largefiles expression" )- <*> (MatchingInfo . addkeysize <$> dataparser)+ <*> (MatchingUserInfo . addkeysize <$> dataparser) where- dataparser = ProvidedInfo+ dataparser = UserProvidedInfo <$> optinfo "file" (strOption ( long "file" <> metavar paramFile <> help "specify filename to match against"@@ -65,9 +65,9 @@ <|> (pure $ Left $ missingdata datadesc) missingdata datadesc = bail $ "cannot match this expression without " ++ datadesc ++ " data" -- When a key is provided, make its size also be provided.- addkeysize p = case providedKey p of+ addkeysize p = case userProvidedKey p of Right k -> case fromKey keySize k of- Just sz -> p { providedFileSize = Right sz }+ Just sz -> p { userProvidedFileSize = Right sz } Nothing -> p Left _ -> p @@ -91,7 +91,8 @@ , liftIO exitFailure ) where- checkmatcher matcher = matchMrun matcher $ \a -> a S.empty (matchinfo o)+ checkmatcher matcher = matchMrun matcher $ \op ->+ matchAction op S.empty (matchinfo o) bail :: String -> IO a bail s = do
Command/Merge.hs view
@@ -13,6 +13,7 @@ import qualified Git.Branch import Annex.CurrentBranch import Command.Sync (prepMerge, mergeLocal, mergeConfig, merge, SyncOptions(..))+import Git.Types cmd :: Command cmd = command "merge" SectionMaintenance@@ -29,17 +30,23 @@ forM_ bs (commandAction . mergeBranch . Git.Ref . encodeBS') mergeAnnexBranch :: CommandStart-mergeAnnexBranch = starting "merge" (ActionItemOther (Just "git-annex")) $ do+mergeAnnexBranch = starting "merge" ai si $ do _ <- Annex.Branch.update -- commit explicitly, in case no remote branches were merged Annex.Branch.commit =<< Annex.Branch.commitMessage next $ return True+ where+ ai = ActionItemOther (Just (fromRef Annex.Branch.name))+ si = SeekInput [] mergeSyncedBranch :: CommandStart mergeSyncedBranch = mergeLocal mergeConfig def =<< getCurrentBranch mergeBranch :: Git.Ref -> CommandStart-mergeBranch r = starting "merge" (ActionItemOther (Just (Git.fromRef r))) $ do+mergeBranch r = starting "merge" ai si $ do currbranch <- getCurrentBranch let o = def { notOnlyAnnexOption = True } next $ merge currbranch mergeConfig o Git.Branch.ManualCommit r+ where+ ai = ActionItemOther (Just (Git.fromRef r))+ si = SeekInput []
Command/MetaData.hs view
@@ -93,24 +93,24 @@ Batch fmt -> withMessageState $ \s -> case outputType s of JSONOutput _ -> ifM limited ( giveup "combining --batch with file matching options is not currently supported"- , batchInput fmt parseJSONInput $- commandAction . startBatch+ , batchInput fmt parseJSONInput + (commandAction . startBatch) ) _ -> giveup "--batch is currently only supported in --json mode" -start :: VectorClock -> MetaDataOptions -> RawFilePath -> Key -> CommandStart-start c o file k = startKeys c o (k, mkActionItem (k, afile))+start :: VectorClock -> MetaDataOptions -> SeekInput -> RawFilePath -> Key -> CommandStart+start c o si file k = startKeys c o (si, k, mkActionItem (k, afile)) where afile = AssociatedFile (Just file) -startKeys :: VectorClock -> MetaDataOptions -> (Key, ActionItem) -> CommandStart-startKeys c o (k, ai) = case getSet o of+startKeys :: VectorClock -> MetaDataOptions -> (SeekInput, Key, ActionItem) -> CommandStart+startKeys c o (si, k, ai) = case getSet o of Get f -> startingCustomOutput k $ do l <- S.toList . currentMetaDataValues f <$> getCurrentMetaData k liftIO $ forM_ l $ B8.putStrLn . fromMetaValue next $ return True- _ -> starting "metadata" ai $+ _ -> starting "metadata" ai si $ perform c o k perform :: VectorClock -> MetaDataOptions -> Key -> CommandPerform@@ -170,8 +170,8 @@ (Nothing, Nothing) -> return $ Left "JSON input is missing either file or key" -startBatch :: (Either RawFilePath Key, MetaData) -> CommandStart-startBatch (i, (MetaData m)) = case i of+startBatch :: (SeekInput, (Either RawFilePath Key, MetaData)) -> CommandStart+startBatch (si, (i, (MetaData m))) = case i of Left f -> do mk <- lookupKey f case mk of@@ -179,7 +179,7 @@ Nothing -> giveup $ "not an annexed file: " ++ fromRawFilePath f Right k -> go k (mkActionItem k) where- go k ai = starting "metadata" ai $ do+ go k ai = starting "metadata" ai si $ do let o = MetaDataOptions { forFiles = [] , getSet = if MetaData m == emptyMetaData
Command/Migrate.hs view
@@ -35,8 +35,8 @@ , usesLocationLog = False } -start :: RawFilePath -> Key -> CommandStart-start file key = do+start :: SeekInput -> RawFilePath -> Key -> CommandStart+start si file key = do forced <- Annex.getState Annex.force v <- Backend.getBackend (fromRawFilePath file) key case v of@@ -46,7 +46,7 @@ newbackend <- maybe defaultBackend return =<< chooseBackend (fromRawFilePath file) if (newbackend /= oldbackend || upgradableKey oldbackend key || forced) && exists- then starting "migrate" (mkActionItem (key, file)) $+ then starting "migrate" (mkActionItem (key, file)) si $ perform file key oldbackend newbackend else stop
Command/Mirror.hs view
@@ -57,19 +57,19 @@ , usesLocationLog = True } -start :: MirrorOptions -> RawFilePath -> Key -> CommandStart-start o file k = startKey o afile (k, ai)+start :: MirrorOptions -> SeekInput -> RawFilePath -> Key -> CommandStart+start o si file k = startKey o afile (si, k, ai) where afile = AssociatedFile (Just file) ai = mkActionItem (k, afile) -startKey :: MirrorOptions -> AssociatedFile -> (Key, ActionItem) -> CommandStart-startKey o afile (key, ai) = case fromToOptions o of+startKey :: MirrorOptions -> AssociatedFile -> (SeekInput, Key, ActionItem) -> CommandStart+startKey o afile (si, key, ai) = case fromToOptions o of ToRemote r -> checkFailedTransferDirection ai Upload $ ifM (inAnnex key)- ( Command.Move.toStart Command.Move.RemoveNever afile key ai =<< getParsed r+ ( Command.Move.toStart Command.Move.RemoveNever afile key ai si =<< getParsed r , do numcopies <- getnumcopies- Command.Drop.startRemote afile ai numcopies key =<< getParsed r+ Command.Drop.startRemote afile ai si numcopies key =<< getParsed r ) FromRemote r -> checkFailedTransferDirection ai Download $ do haskey <- flip Remote.hasKey key =<< getParsed r@@ -77,12 +77,12 @@ Left _ -> stop Right True -> ifM (inAnnex key) ( stop- , Command.Get.start' (return True) Nothing key afile ai+ , Command.Get.start' (return True) Nothing key afile ai si ) Right False -> ifM (inAnnex key) ( do numcopies <- getnumcopies- Command.Drop.startLocal afile ai numcopies key []+ Command.Drop.startLocal afile ai si numcopies key [] , stop ) where
Command/Move.hs view
@@ -76,42 +76,42 @@ Left ToHere -> downloadStages ww = WarnUnmatchLsFiles -start :: FromToHereOptions -> RemoveWhen -> RawFilePath -> Key -> CommandStart-start fromto removewhen f k = start' fromto removewhen afile k ai+start :: FromToHereOptions -> RemoveWhen -> SeekInput -> RawFilePath -> Key -> CommandStart+start fromto removewhen si f k = start' fromto removewhen afile si k ai where afile = AssociatedFile (Just f) ai = mkActionItem (k, afile) -startKey :: FromToHereOptions -> RemoveWhen -> (Key, ActionItem) -> CommandStart-startKey fromto removewhen = - uncurry $ start' fromto removewhen (AssociatedFile Nothing)+startKey :: FromToHereOptions -> RemoveWhen -> (SeekInput, Key, ActionItem) -> CommandStart+startKey fromto removewhen (si, k, ai) = + start' fromto removewhen (AssociatedFile Nothing) si k ai -start' :: FromToHereOptions -> RemoveWhen -> AssociatedFile -> Key -> ActionItem -> CommandStart-start' fromto removewhen afile key ai =+start' :: FromToHereOptions -> RemoveWhen -> AssociatedFile -> SeekInput -> Key -> ActionItem -> CommandStart+start' fromto removewhen afile si key ai = case fromto of Right (FromRemote src) -> checkFailedTransferDirection ai Download $- fromStart removewhen afile key ai =<< getParsed src+ fromStart removewhen afile key ai si =<< getParsed src Right (ToRemote dest) -> checkFailedTransferDirection ai Upload $- toStart removewhen afile key ai =<< getParsed dest+ toStart removewhen afile key ai si =<< getParsed dest Left ToHere -> checkFailedTransferDirection ai Download $- toHereStart removewhen afile key ai+ toHereStart removewhen afile key ai si describeMoveAction :: RemoveWhen -> String describeMoveAction RemoveNever = "copy" describeMoveAction _ = "move" -toStart :: RemoveWhen -> AssociatedFile -> Key -> ActionItem -> Remote -> CommandStart-toStart removewhen afile key ai dest = do+toStart :: RemoveWhen -> AssociatedFile -> Key -> ActionItem -> SeekInput -> Remote -> CommandStart+toStart removewhen afile key ai si dest = do u <- getUUID if u == Remote.uuid dest then stop- else toStart' dest removewhen afile key ai+ else toStart' dest removewhen afile key ai si -toStart' :: Remote -> RemoveWhen -> AssociatedFile -> Key -> ActionItem -> CommandStart-toStart' dest removewhen afile key ai = do+toStart' :: Remote -> RemoveWhen -> AssociatedFile -> Key -> ActionItem -> SeekInput -> CommandStart+toStart' dest removewhen afile key ai si = do fast <- Annex.getState Annex.fast if fast && removewhen == RemoveNever then ifM (expectedPresent dest key)@@ -121,7 +121,7 @@ else go False (Remote.hasKey dest key) where go fastcheck isthere =- starting (describeMoveAction removewhen) (OnlyActionOn key ai) $+ starting (describeMoveAction removewhen) (OnlyActionOn key ai) si $ toPerform dest removewhen key afile fastcheck =<< isthere expectedPresent :: Remote -> Key -> Annex Bool@@ -196,10 +196,10 @@ -- to be done except for cleaning up. lockfailed = next $ Command.Drop.cleanupLocal key -fromStart :: RemoveWhen -> AssociatedFile -> Key -> ActionItem -> Remote -> CommandStart-fromStart removewhen afile key ai src = +fromStart :: RemoveWhen -> AssociatedFile -> Key -> ActionItem -> SeekInput -> Remote -> CommandStart+fromStart removewhen afile key ai si src = stopUnless (fromOk src key) $- starting (describeMoveAction removewhen) (OnlyActionOn key ai) $+ starting (describeMoveAction removewhen) (OnlyActionOn key ai) si $ fromPerform src removewhen key afile fromOk :: Remote -> Key -> Annex Bool@@ -252,13 +252,13 @@ - - When moving, the content is removed from all the reachable remotes that - it can safely be removed from. -}-toHereStart :: RemoveWhen -> AssociatedFile -> Key -> ActionItem -> CommandStart-toHereStart removewhen afile key ai = +toHereStart :: RemoveWhen -> AssociatedFile -> Key -> ActionItem -> SeekInput -> CommandStart+toHereStart removewhen afile key ai si = startingNoMessage (OnlyActionOn key ai) $ do rs <- Remote.keyPossibilities key forM_ rs $ \r -> includeCommandAction $- starting (describeMoveAction removewhen) ai $+ starting (describeMoveAction removewhen) ai si $ fromPerform r removewhen key afile next $ return True
Command/Multicast.hs view
@@ -78,7 +78,7 @@ seek (MultiCastOptions Receive _ _) = giveup "Cannot specify list of files with --receive; this receives whatever files the sender chooses to send." genAddress :: CommandStart-genAddress = starting "gen-address" (ActionItemOther Nothing) $ do+genAddress = starting "gen-address" (ActionItemOther Nothing) (SeekInput []) $ do k <- uftpKey (s, ok) <- case k of KeyContainer s -> liftIO $ genkey (Param s)@@ -127,21 +127,22 @@ -- In a direct mode repository, the annex objects do not have -- the names of keys, and would have to be copied, which is too -- expensive.- starting "sending files" (ActionItemOther Nothing) $+ starting "sending files" (ActionItemOther Nothing) (SeekInput []) $ withTmpFile "send" $ \t h -> do let ww = WarnUnmatchLsFiles- fs' <- seekHelper id ww LsFiles.inRepo+ (fs', cleanup) <- seekHelper id ww LsFiles.inRepo =<< workTreeItems ww fs matcher <- Limit.getMatcher- let addlist f o = whenM (matcher $ MatchingFile $ FileInfo f f) $+ let addlist f o = whenM (matcher $ MatchingFile $ FileInfo (Just f) f) $ liftIO $ hPutStrLn h o- forM_ fs' $ \f -> do+ forM_ fs' $ \(_, f) -> do mk <- lookupKey f case mk of Nothing -> noop Just k -> withObjectLoc k $ addlist f . fromRawFilePath liftIO $ hClose h+ liftIO $ void cleanup serverkey <- uftpKey u <- getUUID@@ -166,7 +167,7 @@ next $ return True receive :: [CommandParam] -> CommandStart-receive ups = starting "receiving multicast files" (ActionItemOther Nothing) $ do+receive ups = starting "receiving multicast files" ai si $ do showNote "Will continue to run until stopped by ctrl-c" showOutput@@ -200,6 +201,9 @@ mapM_ storeReceived . lines =<< liftIO (hGetContents statush) showEndResult =<< liftIO (wait runner) next $ return True+ where+ ai = ActionItemOther Nothing+ si = SeekInput [] storeReceived :: FilePath -> Annex () storeReceived f = do
Command/NumCopies.hs view
@@ -46,6 +46,9 @@ return True startSet :: Int -> CommandStart-startSet n = startingUsualMessages "numcopies" (ActionItemOther (Just $ show n)) $ do+startSet n = startingUsualMessages "numcopies" ai si $ do setGlobalNumCopies $ NumCopies n next $ return True+ where+ ai = ActionItemOther (Just $ show n)+ si = SeekInput [show n]
Command/P2P.hs view
@@ -98,9 +98,11 @@ -- Address is read from stdin, to avoid leaking it in shell history. linkRemote :: RemoteName -> CommandStart-linkRemote remotename = starting "p2p link" (ActionItemOther (Just remotename)) $+linkRemote remotename = starting "p2p link" ai si $ next promptaddr where+ ai = ActionItemOther (Just remotename)+ si = SeekInput [] promptaddr = do liftIO $ putStrLn "" liftIO $ putStr "Enter peer address: "@@ -124,10 +126,13 @@ startPairing :: RemoteName -> [P2PAddress] -> CommandStart startPairing _ [] = giveup "No P2P networks are currrently available." startPairing remotename addrs = ifM (liftIO Wormhole.isInstalled)- ( starting "p2p pair" (ActionItemOther (Just remotename)) $+ ( starting "p2p pair" ai si $ performPairing remotename addrs , giveup "Magic Wormhole is not installed, and is needed for pairing. Install it from your distribution or from https://github.com/warner/magic-wormhole/"- ) + )+ where+ ai = ActionItemOther (Just remotename)+ si = SeekInput [] performPairing :: RemoteName -> [P2PAddress] -> CommandPerform performPairing remotename addrs = do
Command/PreCommit.hs view
@@ -35,8 +35,8 @@ let ww = WarnUnmatchWorkTreeItems l <- workTreeItems ww ps -- fix symlinks to files being committed- flip withFilesToBeCommitted l $ \f -> commandAction $- maybe stop (Command.Fix.start Command.Fix.FixSymlinks f)+ flip withFilesToBeCommitted l $ \(si, f) -> commandAction $+ maybe stop (Command.Fix.start Command.Fix.FixSymlinks si f) =<< isAnnexLink f -- after a merge conflict or git cherry-pick or stash, pointer -- files in the worktree won't be populated, so populate them here@@ -53,12 +53,18 @@ (removeViewMetaData v) addViewMetaData :: View -> ViewedFile -> Key -> CommandStart-addViewMetaData v f k = starting "metadata" (mkActionItem (k, toRawFilePath f)) $+addViewMetaData v f k = starting "metadata" ai si $ next $ changeMetaData k $ fromView v f+ where+ ai = mkActionItem (k, toRawFilePath f)+ si = SeekInput [] removeViewMetaData :: View -> ViewedFile -> Key -> CommandStart-removeViewMetaData v f k = starting "metadata" (mkActionItem (k, toRawFilePath f)) $+removeViewMetaData v f k = starting "metadata" ai si $ next $ changeMetaData k $ unsetMetaData $ fromView v f+ where+ ai = mkActionItem (k, toRawFilePath f)+ si = SeekInput [] changeMetaData :: Key -> MetaData -> CommandCleanup changeMetaData k metadata = do
Command/ReKey.hs view
@@ -52,21 +52,25 @@ seek :: ReKeyOptions -> CommandSeek seek o = case batchOption o of- Batch fmt -> batchInput fmt batchParser $- batchCommandAction . start- NoBatch -> withPairs (commandAction . start . parsekey) (reKeyThese o)+ Batch fmt -> batchInput fmt batchParser+ (batchCommandAction . uncurry start)+ NoBatch -> withPairs + (\(si, p) -> commandAction (start si (parsekey p))) + (reKeyThese o) where parsekey (file, skey) = (toRawFilePath file, fromMaybe (giveup "bad key") (deserializeKey skey)) -start :: (RawFilePath, Key) -> CommandStart-start (file, newkey) = ifAnnexed file go stop+start :: SeekInput -> (RawFilePath, Key) -> CommandStart+start si (file, newkey) = ifAnnexed file go stop where go oldkey | oldkey == newkey = stop- | otherwise = starting "rekey" (ActionItemWorkTreeFile file) $+ | otherwise = starting "rekey" ai si $ perform file oldkey newkey + ai = ActionItemWorkTreeFile file+ perform :: RawFilePath -> Key -> Key -> CommandPerform perform file oldkey newkey = do ifM (inAnnex oldkey) @@ -120,7 +124,7 @@ ( do -- Update symlink to use the new key. liftIO $ removeFile (fromRawFilePath file)- addLink (fromRawFilePath file) newkey Nothing+ addLink (CheckGitIgnore False) (fromRawFilePath file) newkey Nothing , do mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus file liftIO $ whenM (isJust <$> isPointerFile file) $
Command/RecvKey.hs view
@@ -24,8 +24,8 @@ seek :: CmdParams -> CommandSeek seek = withKeys (commandAction . start) -start :: Key -> CommandStart-start key = fieldTransfer Download key $ \_p -> do+start :: (SeekInput, Key) -> CommandStart+start (_, key) = fieldTransfer Download key $ \_p -> do -- Always verify content when a repo is sending an unlocked file, -- as the file could change while being transferred. fromunlocked <- (isJust <$> Fields.getField Fields.unlocked)
Command/RegisterUrl.hs view
@@ -40,14 +40,16 @@ start :: [String] -> CommandStart start (keyname:url:[]) = - starting "registerurl" (ActionItemOther (Just url)) $ do- let key = keyOpt keyname- perform key url+ starting "registerurl" ai si $+ perform (keyOpt keyname) url+ where+ ai = ActionItemOther (Just url)+ si = SeekInput [keyname, url] start _ = giveup "specify a key and an url" startMass :: BatchFormat -> CommandStart startMass fmt = - starting "registerurl" (ActionItemOther (Just "stdin")) $+ starting "registerurl" (ActionItemOther (Just "stdin")) (SeekInput []) $ massAdd fmt massAdd :: BatchFormat -> CommandPerform
Command/Reinit.hs view
@@ -24,7 +24,7 @@ seek = withWords (commandAction . start) start :: [String] -> CommandStart-start ws = starting "reinit" (ActionItemOther (Just s)) $+start ws = starting "reinit" (ActionItemOther (Just s)) (SeekInput ws) $ perform s where s = unwords ws
Command/Reinject.hs view
@@ -41,20 +41,22 @@ | otherwise = withWords (commandAction . startSrcDest) (params os) startSrcDest :: [FilePath] -> CommandStart-startSrcDest (src:dest:[])+startSrcDest ps@(src:dest:[]) | src == dest = stop | otherwise = notAnnexed src $ ifAnnexed (toRawFilePath dest) go stop where- go key = starting "reinject" (ActionItemOther (Just src)) $+ go key = starting "reinject" ai si $ ifM (verifyKeyContent RetrievalAllKeysSecure DefaultVerify UnVerified key src) ( perform src key , giveup $ src ++ " does not have expected content of " ++ dest )+ ai = ActionItemOther (Just src)+ si = SeekInput ps startSrcDest _ = giveup "specify a src file and a dest file" startKnown :: FilePath -> CommandStart startKnown src = notAnnexed src $- starting "reinject" (ActionItemOther (Just src)) $ do+ starting "reinject" ai si $ do (key, _) <- genKey ks nullMeterUpdate Nothing ifM (isKnownKey key) ( perform src key@@ -65,6 +67,8 @@ where ks = KeySource src' src' Nothing src' = toRawFilePath src+ ai = ActionItemOther (Just src)+ si = SeekInput [src] notAnnexed :: FilePath -> CommandStart -> CommandStart notAnnexed src a =
Command/RenameRemote.hs view
@@ -27,7 +27,7 @@ seek = withWords (commandAction . start) start :: [String] -> CommandStart-start (oldname:newname:[]) = Annex.SpecialRemote.findExisting oldname >>= \case+start ps@(oldname:newname:[]) = Annex.SpecialRemote.findExisting oldname >>= \case Just (u, cfg, mcu) -> Annex.SpecialRemote.findExisting newname >>= \case Just _ -> giveup $ "The name " ++ newname ++ " is already used by a special remote." Nothing -> go u cfg mcu@@ -37,13 +37,14 @@ Nothing -> Remote.nameToUUID' oldname >>= \case Left e -> giveup e Right u -> do- m <- Logs.Remote.readRemoteLog+ m <- Logs.Remote.remoteConfigMap case M.lookup u m of Nothing -> giveup "That is not a special remote." Just cfg -> go u cfg Nothing where- go u cfg mcu = starting "rename" (ActionItemOther Nothing) $- perform u cfg mcu newname+ ai = ActionItemOther Nothing+ si = SeekInput ps+ go u cfg mcu = starting "rename" ai si $ perform u cfg mcu newname start _ = giveup "Specify an old name (or uuid or description) and a new name." perform :: UUID -> R.RemoteConfig -> Maybe (Annex.SpecialRemote.ConfigFrom UUID) -> String -> CommandPerform
Command/Repair.hs view
@@ -25,7 +25,7 @@ seek = withNothing (commandAction start) start :: CommandStart-start = starting "repair" (ActionItemOther Nothing) $ +start = starting "repair" (ActionItemOther Nothing) (SeekInput []) $ next $ runRepair =<< Annex.getState Annex.force runRepair :: Bool -> Annex Bool
Command/ResolveMerge.hs view
@@ -24,7 +24,7 @@ seek = withNothing (commandAction start) start :: CommandStart-start = starting "resolvemerge" (ActionItemOther Nothing) $ do+start = starting "resolvemerge" (ActionItemOther Nothing) (SeekInput []) $ do us <- fromMaybe nobranch <$> inRepo Git.Branch.current d <- fromRawFilePath <$> fromRepo Git.localGitDir let merge_head = d </> "MERGE_HEAD"
Command/RmUrl.hs view
@@ -30,8 +30,7 @@ seek :: RmUrlOptions -> CommandSeek seek o = case batchOption o of- Batch fmt -> batchInput fmt batchParser- (batchCommandAction . start)+ Batch fmt -> batchInput fmt batchParser (batchCommandAction . start) NoBatch -> withPairs (commandAction . start) (rmThese o) -- Split on the last space, since a FilePath can contain whitespace,@@ -45,9 +44,10 @@ f' <- liftIO $ relPathCwdToFile f return $ Right (f', reverse ru) -start :: (FilePath, URLString) -> CommandStart-start (file, url) = flip whenAnnexed file' $ \_ key ->- starting "rmurl" (mkActionItem (key, AssociatedFile (Just file'))) $+start :: (SeekInput, (FilePath, URLString)) -> CommandStart+start (si, (file, url)) = flip whenAnnexed file' $ \_ key -> do+ let ai = mkActionItem (key, AssociatedFile (Just file'))+ starting "rmurl" ai si $ next $ cleanup url key where file' = toRawFilePath file
Command/Schedule.hs view
@@ -29,9 +29,11 @@ u <- Remote.nameToUUID name startingCustomOutput (ActionItemOther Nothing) $ performGet u- parse (name:expr:[]) = do+ parse ps@(name:expr:[]) = do u <- Remote.nameToUUID name- startingUsualMessages "schedule" (ActionItemOther (Just name)) $+ let ai = ActionItemOther (Just name)+ let si = SeekInput ps+ startingUsualMessages "schedule" ai si $ performSet expr u parse _ = giveup "Specify a repository."
Command/SendKey.hs view
@@ -26,8 +26,8 @@ seek :: CmdParams -> CommandSeek seek = withKeys (commandAction . start) -start :: Key -> CommandStart-start key = do+start :: (SeekInput, Key) -> CommandStart+start (_, key) = do opts <- filterRsyncSafeOptions . maybe [] words <$> getField "RsyncOptions" ifM (inAnnex key)
Command/SetKey.hs view
@@ -20,8 +20,11 @@ seek = withWords (commandAction . start) start :: [String] -> CommandStart-start (keyname:file:[]) = starting "setkey" (ActionItemOther (Just file)) $+start ps@(keyname:file:[]) = starting "setkey" ai si $ perform file (keyOpt keyname)+ where+ ai = ActionItemOther (Just file)+ si = SeekInput ps start _ = giveup "specify a key and a content file" keyOpt :: String -> Key
Command/SetPresentKey.hs view
@@ -32,8 +32,8 @@ seek o = case batchOption o of Batch fmt -> batchInput fmt (pure . parseKeyStatus . words)- (batchCommandAction . start)- NoBatch -> either giveup (commandAction . start)+ (batchCommandAction . uncurry start)+ NoBatch -> either giveup (commandAction . start (SeekInput (params o))) (parseKeyStatus $ params o) data KeyStatus = KeyStatus Key UUID LogStatus@@ -46,9 +46,10 @@ return $ KeyStatus k u s parseKeyStatus _ = Left "Bad input. Expected: key uuid value" -start :: KeyStatus -> CommandStart-start (KeyStatus k u s) = starting "setpresentkey" (mkActionItem k) $- perform k u s+start :: SeekInput -> KeyStatus -> CommandStart+start si (KeyStatus k u s) = starting "setpresentkey" ai si $ perform k u s+ where+ ai = mkActionItem k perform :: Key -> UUID -> LogStatus -> CommandPerform perform k u s = next $ do
Command/Sync.hs view
@@ -57,6 +57,7 @@ import Annex.UUID import Logs.UUID import Logs.Export+import Logs.PreferredContent import Annex.AutoMerge import Annex.AdjustedBranch import Annex.Ssh@@ -65,6 +66,9 @@ import Annex.Export import Annex.TaggedPush import Annex.CurrentBranch+import Annex.Import (canImportKeys)+import Annex.CheckIgnore+import Types.FileMatcher import qualified Database.Export as Export import Utility.Bloom import Utility.OptParse@@ -223,10 +227,13 @@ , map (withbranch . pullRemote o mergeConfig) gitremotes , [ mergeAnnex ] ]- - whenM (shouldSyncContent o) $ do- mapM_ (withbranch . importRemote o mergeConfig) importremotes + content <- shouldSyncContent o++ forM_ importremotes $+ withbranch . importRemote content o mergeConfig+ + when content $ do -- Send content to any exports before other -- repositories, in case that lets content -- be dropped from other repositories.@@ -308,7 +315,7 @@ fastest = fromMaybe [] . headMaybe . Remote.byCost commit :: SyncOptions -> CommandStart-commit o = stopUnless shouldcommit $ starting "commit" (ActionItemOther Nothing) $ do+commit o = stopUnless shouldcommit $ starting "commit" ai si $ do commitmessage <- maybe commitMsg return (messageOption o) Annex.Branch.commit =<< Annex.Branch.commitMessage next $ do@@ -324,6 +331,8 @@ ( pure (commitOption o) <||> (pure (not (noCommitOption o)) <&&> getGitConfigVal annexAutoCommit) )+ ai = ActionItemOther Nothing+ si = SeekInput [] commitMsg :: Annex String commitMsg = do@@ -350,14 +359,18 @@ mergeLocal' mergeconfig o currbranch@(Just branch, _) = needMerge currbranch branch >>= \case Nothing -> stop- Just syncbranch ->- starting "merge" (ActionItemOther (Just $ Git.Ref.describe syncbranch)) $+ Just syncbranch -> do+ let ai = ActionItemOther (Just $ Git.Ref.describe syncbranch)+ let si = SeekInput []+ starting "merge" ai si $ next $ merge currbranch mergeconfig o Git.Branch.ManualCommit syncbranch mergeLocal' _ _ currbranch@(Nothing, _) = inRepo Git.Branch.currentUnsafe >>= \case Just branch -> needMerge currbranch branch >>= \case Nothing -> stop- Just syncbranch ->- starting "merge" (ActionItemOther (Just $ Git.Ref.describe syncbranch)) $ do+ Just syncbranch -> do+ let ai = ActionItemOther (Just $ Git.Ref.describe syncbranch)+ let si = SeekInput []+ starting "merge" ai si $ do warning $ "There are no commits yet to branch " ++ Git.fromRef branch ++ ", so cannot merge " ++ Git.fromRef syncbranch ++ " into it." next $ return False Nothing -> stop@@ -421,7 +434,7 @@ pullRemote :: SyncOptions -> [Git.Merge.MergeConfig] -> Remote -> CurrBranch -> CommandStart pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && wantpull) $- starting "pull" (ActionItemOther (Just (Remote.name remote))) $ do+ starting "pull" ai si $ do showOutput ifM (onlyAnnex o) ( do@@ -443,9 +456,11 @@ [Param "fetch", Param $ Remote.name remote] ++ map Param bs wantpull = remoteAnnexPull (Remote.gitconfig remote)+ ai = ActionItemOther (Just (Remote.name remote))+ si = SeekInput [] -importRemote :: SyncOptions -> [Git.Merge.MergeConfig] -> Remote -> CurrBranch -> CommandSeek-importRemote o mergeconfig remote currbranch+importRemote :: Bool -> SyncOptions -> [Git.Merge.MergeConfig] -> Remote -> CurrBranch -> CommandSeek+importRemote importcontent o mergeconfig remote currbranch | not (pullOption o) || not wantpull = noop | otherwise = case remoteAnnexTrackingBranch (Remote.gitconfig remote) of Nothing -> noop@@ -455,8 +470,11 @@ let subdir = if S.null p then Nothing else Just (asTopFilePath p)- Command.Import.seekRemote remote branch subdir True- void $ mergeRemote remote currbranch mergeconfig o+ if canImportKeys remote importcontent+ then do+ Command.Import.seekRemote remote branch subdir importcontent (CheckGitIgnore True)+ void $ mergeRemote remote currbranch mergeconfig o+ else warning $ "Cannot import from " ++ Remote.name remote ++ " when not syncing content." where wantpull = remoteAnnexPull (Remote.gitconfig remote) @@ -489,7 +507,7 @@ onlyannex <- onlyAnnex o let mainbranch = if onlyannex then Nothing else Just branch stopUnless (pure (pushOption o) <&&> needpush mainbranch) $- starting "push" (ActionItemOther (Just (Remote.name remote))) $ next $ do+ starting "push" ai si $ next $ do repo <- Remote.getRepo remote showOutput ok <- inRepoWithSshOptionsTo repo gc $@@ -500,6 +518,8 @@ warning $ unwords [ "Pushing to " ++ Remote.name remote ++ " failed." ] return ok where+ ai = ActionItemOther (Just (Remote.name remote))+ si = SeekInput [] gc = Remote.gitconfig remote needpush mainbranch | remoteAnnexReadOnly gc = return False@@ -623,9 +643,11 @@ - (Or, when in an ajusted branch where some files are hidden, at files in - the original branch.) -- - With --all, makes a second pass over all keys.- - This ensures that preferred content expressions that match on- - filenames work, even when in --all mode.+ - With --all, when preferred content expressions look at filenames,+ - makes a first pass over the files in the work tree so those preferred+ - content expressions will match. The second pass is over all keys,+ - and only preferred content expressions that don't look at filenames+ - will match. - - Returns true if any file transfers were made. -@@ -636,7 +658,10 @@ seekSyncContent o rs currbranch = do mvar <- liftIO newEmptyMVar bloom <- case keyOptions o of- Just WantAllKeys -> Just <$> genBloomFilter (seekworktree mvar (WorkTreeItems []))+ Just WantAllKeys -> ifM preferredcontentmatchesfilenames+ ( Just <$> genBloomFilter (seekworktree mvar (WorkTreeItems []))+ , pure Nothing+ ) _ -> case currbranch of (Just origbranch, Just adj) | adjustmentHidesFiles adj -> do l <- workTreeItems' (AllowHidden True) ww (contentOfOption o)@@ -646,6 +671,7 @@ l <- workTreeItems ww (contentOfOption o) seekworktree mvar l (const noop) pure Nothing+ waitForAllRunningCommandActions withKeyOptions' (keyOptions o) False (return (commandAction . gokey mvar bloom)) (const noop)@@ -663,26 +689,30 @@ seekHelper fst3 ww LsFiles.inRepoDetails l seekincludinghidden origbranch mvar l bloomfeeder =- seekFiltered (\f -> ifAnnexed f (commandAction . gofile bloomfeeder mvar f) noop) $+ seekFiltered (const (pure True)) (\(si, f) -> ifAnnexed f (commandAction . gofile bloomfeeder mvar si f) noop) $ seekHelper id ww (LsFiles.inRepoOrBranch origbranch) l ww = WarnUnmatchLsFiles - gofile bloom mvar f k = + gofile bloom mvar _ f k = go (Right bloom) mvar (AssociatedFile (Just f)) k - gokey mvar bloom (k, _) =+ gokey mvar bloom (_, k, _) = go (Left bloom) mvar (AssociatedFile Nothing) k go ebloom mvar af k = do- -- Run syncFile as a command action so file transfers run- -- concurrently. let ai = OnlyActionOn k (ActionItemKey k) startingNoMessage ai $ do whenM (syncFile ebloom rs af k) $ void $ liftIO $ tryPutMVar mvar () next $ return True + preferredcontentmatchesfilenames =+ preferredcontentmatchesfilenames' Nothing+ <||> anyM (preferredcontentmatchesfilenames' . Just . Remote.uuid) rs+ preferredcontentmatchesfilenames' =+ introspectPreferredRequiredContent matchNeedsFileName+ {- If it's preferred content, and we don't have it, get it from one of the - listed remotes (preferring the cheaper earlier ones). -@@ -708,11 +738,13 @@ u <- getUUID let locs' = concat [if inhere || got then [u] else [], putrs, locs] - -- A bloom filter is populated with all the keys in the first pass.- -- On the second pass, avoid dropping keys that were seen in the- -- first pass, which would happen otherwise when preferred content- -- matches on the filename, which is not available in the second- -- pass.+ -- To handle --all, a bloom filter is populated with all the keys+ -- of files in the working tree in the first pass. On the second+ -- pass, avoid dropping keys that were seen in the first pass, which+ -- would happen otherwise when preferred content matches on the+ -- filename, which is not available in the second pass.+ -- (When the preferred content expressions do not match on+ -- filenames, the first pass is skipped for speed.) -- -- When there's a false positive in the bloom filter, the result -- is keeping a key that preferred content doesn't really want.@@ -725,7 +757,7 @@ -- includeCommandAction for drops, -- because a failure to drop does not mean -- the sync failed.- handleDropsFrom locs' rs "unwanted" True k af []+ handleDropsFrom locs' rs "unwanted" True k af si [] callCommandAction return (got || not (null putrs))@@ -739,7 +771,7 @@ ( return [ get have ] , return [] )- get have = includeCommandAction $ starting "get" ai $+ get have = includeCommandAction $ starting "get" ai si $ stopUnless (getKey' k af have) $ next $ return True @@ -755,9 +787,10 @@ , return [] ) put dest = includeCommandAction $ - Command.Move.toStart' dest Command.Move.RemoveNever af k ai+ Command.Move.toStart' dest Command.Move.RemoveNever af k ai si ai = mkActionItem (k, af)+ si = SeekInput [] {- When a remote has an annex-tracking-branch configuration, change the export - to contain the current content of the branch. Otherwise, transfer any files@@ -814,22 +847,21 @@ cleanupLocal :: CurrBranch -> CommandStart cleanupLocal (Nothing, _) = stop-cleanupLocal (Just currb, _) = - starting "cleanup" (ActionItemOther (Just "local")) $ - next $ do- delbranch $ syncBranch currb- delbranch $ syncBranch $ Git.Ref.base $ Annex.Branch.name- mapM_ (\(s,r) -> inRepo $ Git.Ref.delete s r)- =<< listTaggedBranches- return True+cleanupLocal (Just currb, _) = starting "cleanup" ai si $ next $ do+ delbranch $ syncBranch currb+ delbranch $ syncBranch $ Git.Ref.base $ Annex.Branch.name+ mapM_ (\(s,r) -> inRepo $ Git.Ref.delete s r) =<< listTaggedBranches+ return True where delbranch b = whenM (inRepo $ Git.Ref.exists $ Git.Ref.branchRef b) $ inRepo $ Git.Branch.delete b+ ai = ActionItemOther (Just "local")+ si = SeekInput [] cleanupRemote :: Remote -> CurrBranch -> CommandStart cleanupRemote _ (Nothing, _) = stop cleanupRemote remote (Just b, _) =- starting "cleanup" (ActionItemOther (Just (Remote.name remote))) $+ starting "cleanup" ai si $ next $ inRepo $ Git.Command.runBool [ Param "push" , Param "--quiet"@@ -839,7 +871,10 @@ , Param $ Git.fromRef $ syncBranch $ Git.Ref.base $ Annex.Branch.name ]- + where+ ai = ActionItemOther (Just (Remote.name remote))+ si = SeekInput []+ shouldSyncContent :: SyncOptions -> Annex Bool shouldSyncContent o | noContentOption o = pure False
Command/TestRemote.hs view
@@ -73,7 +73,7 @@ seek = commandAction . start start :: TestRemoteOptions -> CommandStart-start o = starting "testremote" (ActionItemOther (Just (testRemote o))) $ do+start o = starting "testremote" (ActionItemOther (Just (testRemote o))) si $ do fast <- Annex.getState Annex.fast cache <- liftIO newRemoteVariantCache r <- either giveup (disableExportTree cache)@@ -98,6 +98,7 @@ perform drs unavailr exportr ks where basesz = fromInteger $ sizeOption o+ si = SeekInput [testRemote o] perform :: [Described (Annex (Maybe Remote))] -> Maybe Remote -> Annex (Maybe Remote) -> [Key] -> CommandPerform perform drs unavailr exportr ks = do
Command/TransferKey.hs view
@@ -44,8 +44,8 @@ seek :: TransferKeyOptions -> CommandSeek seek o = withKeys (commandAction . start o) (keyOptions o) -start :: TransferKeyOptions -> Key -> CommandStart-start o key = startingCustomOutput key $ case fromToOptions o of+start :: TransferKeyOptions -> (SeekInput, Key) -> CommandStart+start o (_, key) = startingCustomOutput key $ case fromToOptions o of ToRemote dest -> toPerform key (fileOption o) =<< getParsed dest FromRemote src -> fromPerform key (fileOption o) =<< getParsed src
Command/Trust.hs view
@@ -28,7 +28,8 @@ start ws = do let name = unwords ws u <- Remote.nameToUUID name- starting c (ActionItemOther (Just name)) (perform u)+ let si = SeekInput ws+ starting c (ActionItemOther (Just name)) si (perform u) perform uuid = do trustSet uuid level when (level == DeadTrusted) $
Command/Unannex.hs view
@@ -33,9 +33,9 @@ , usesLocationLog = False } -start :: RawFilePath -> Key -> CommandStart-start file key = - starting "unannex" (mkActionItem (key, file)) $+start :: SeekInput -> RawFilePath -> Key -> CommandStart+start si file key = + starting "unannex" (mkActionItem (key, file)) si $ perform file key perform :: RawFilePath -> Key -> CommandPerform
Command/Undo.hs view
@@ -41,8 +41,11 @@ withStrings (commandAction . start) ps start :: FilePath -> CommandStart-start p = starting "undo" (ActionItemOther (Just p)) $+start p = starting "undo" ai si $ perform p+ where+ ai = ActionItemOther (Just p)+ si = SeekInput [p] perform :: FilePath -> CommandPerform perform p = do
Command/Ungroup.hs view
@@ -24,7 +24,7 @@ start :: [String] -> CommandStart start (name:g:[]) = do u <- Remote.nameToUUID name- starting "ungroup" (ActionItemOther (Just name)) $+ starting "ungroup" (ActionItemOther (Just name)) (SeekInput [name, g]) $ perform u (toGroup g) start _ = giveup "Specify a repository and a group."
Command/Uninit.hs view
@@ -16,6 +16,7 @@ import qualified Database.Keys import Annex.Content import Annex.Init+import Annex.CheckIgnore import Utility.FileMode import qualified Utility.RawFilePath as R @@ -42,7 +43,11 @@ seek :: CmdParams -> CommandSeek seek ps = do l <- workTreeItems ww ps- withFilesNotInGit (commandAction . whenAnnexed (startCheckIncomplete . fromRawFilePath)) l+ withFilesNotInGit+ (CheckGitIgnore False)+ WarnUnmatchWorkTreeItems + (\(_, f) -> commandAction $ whenAnnexed (startCheckIncomplete . fromRawFilePath) f)+ l Annex.changeState $ \s -> s { Annex.fast = True } withFilesInGitAnnex ww Command.Unannex.seeker l finish
Command/Unlock.hs view
@@ -36,12 +36,13 @@ , usesLocationLog = False } -start :: RawFilePath -> Key -> CommandStart-start file key = ifM (isJust <$> isAnnexLink file)- ( starting "unlock" (mkActionItem (key, AssociatedFile (Just file))) $- perform file key+start :: SeekInput -> RawFilePath -> Key -> CommandStart+start si file key = ifM (isJust <$> isAnnexLink file)+ ( starting "unlock" ai si $ perform file key , stop )+ where+ ai = mkActionItem (key, AssociatedFile (Just file)) perform :: RawFilePath -> Key -> CommandPerform perform dest key = do
Command/Unused.hs view
@@ -73,7 +73,7 @@ Just "." -> (".", checkUnused refspec) Just "here" -> (".", checkUnused refspec) Just n -> (n, checkRemoteUnused n refspec)- starting "unused" (ActionItemOther (Just name)) perform+ starting "unused" (ActionItemOther (Just name)) (SeekInput []) perform checkUnused :: RefSpec -> CommandPerform checkUnused refspec = chain 0@@ -337,4 +337,5 @@ Nothing -> search rest Just key -> starting message (ActionItemOther $ Just $ show n)+ (SeekInput []) (a key)
Command/Upgrade.hs view
@@ -39,10 +39,10 @@ start :: UpgradeOptions -> CommandStart start (UpgradeOptions { autoOnly = True }) = do- starting "upgrade" (ActionItemOther Nothing) $ do+ starting "upgrade" (ActionItemOther Nothing) (SeekInput []) $ do getVersion >>= maybe noop checkUpgrade next $ return True-start _ = starting "upgrade" (ActionItemOther Nothing) $ do+start _ = starting "upgrade" (ActionItemOther Nothing) (SeekInput []) $ do whenM (isNothing <$> getVersion) $ do initialize Nothing Nothing r <- upgrade False latestVersion
Command/VAdd.hs view
@@ -22,7 +22,7 @@ seek = withWords (commandAction . start) start :: [String] -> CommandStart-start params = starting "vadd" (ActionItemOther Nothing) $ +start params = starting "vadd" (ActionItemOther Nothing) (SeekInput params) $ withCurrentView $ \view -> do let (view', change) = refineView view $ map parseViewParam $ reverse params
Command/VCycle.hs view
@@ -26,7 +26,7 @@ start = go =<< currentView where go Nothing = giveup "Not in a view."- go (Just v) = starting "vcycle" (ActionItemOther Nothing) $ do+ go (Just v) = starting "vcycle" (ActionItemOther Nothing) (SeekInput [])$ do let v' = v { viewComponents = vcycle [] (viewComponents v) } if v == v' then do
Command/VFilter.hs view
@@ -20,7 +20,7 @@ seek = withWords (commandAction . start) start :: [String] -> CommandStart-start params = starting "vfilter" (ActionItemOther Nothing) $+start params = starting "vfilter" (ActionItemOther Nothing) (SeekInput params) $ withCurrentView $ \view -> do let view' = filterView view $ map parseViewParam $ reverse params
Command/VPop.hs view
@@ -27,7 +27,7 @@ start ps = go =<< currentView where go Nothing = giveup "Not in a view."- go (Just v) = starting "vpop" (ActionItemOther (Just $ show num)) $ do+ go (Just v) = starting "vpop" ai si $ do removeView v (oldvs, vs) <- splitAt (num - 1) . filter (sameparentbranch v) <$> recentViews@@ -46,3 +46,7 @@ sameparentbranch a b = viewParentBranch a == viewParentBranch b num = fromMaybe 1 $ readish =<< headMaybe ps + + ai = ActionItemOther (Just $ show num)+ + si = SeekInput ps
Command/View.hs view
@@ -36,7 +36,9 @@ , giveup "Not safe to enter view." ) where- go view Nothing = starting "view" (ActionItemOther Nothing) $+ ai = ActionItemOther Nothing+ si = SeekInput ps+ go view Nothing = starting "view" ai si $ perform view go view (Just v) | v == view = stop
Command/Wanted.hs view
@@ -36,9 +36,11 @@ u <- Remote.nameToUUID rname startingCustomOutput (ActionItemOther Nothing) $ performGet getter u- start (rname:expr:[]) = do+ start ps@(rname:expr:[]) = do u <- Remote.nameToUUID rname- startingUsualMessages name (ActionItemOther (Just rname)) $+ let si = SeekInput ps+ let ai = ActionItemOther (Just rname)+ startingUsualMessages name ai si $ performSet setter expr u start _ = giveup "Specify a repository."
Command/Whereis.hs view
@@ -66,16 +66,16 @@ where ww = WarnUnmatchLsFiles -start :: WhereisOptions -> M.Map UUID Remote -> RawFilePath -> Key -> CommandStart-start o remotemap file key = - startKeys o remotemap (key, mkActionItem (key, afile))+start :: WhereisOptions -> M.Map UUID Remote -> SeekInput -> RawFilePath -> Key -> CommandStart+start o remotemap si file key = + startKeys o remotemap (si, key, mkActionItem (key, afile)) where afile = AssociatedFile (Just file) -startKeys :: WhereisOptions -> M.Map UUID Remote -> (Key, ActionItem) -> CommandStart-startKeys o remotemap (key, ai)+startKeys :: WhereisOptions -> M.Map UUID Remote -> (SeekInput, Key, ActionItem) -> CommandStart+startKeys o remotemap (si, key, ai) | isJust (formatOption o) = startingCustomOutput ai go- | otherwise = starting "whereis" ai go+ | otherwise = starting "whereis" ai si go where go = perform o remotemap key ai
Config/GitConfig.hs view
@@ -20,13 +20,13 @@ - Note: Be sure to add the config to mergeGitConfig and to - globalConfigs. -}-getGitConfigVal :: (GitConfig -> Configurable a) -> Annex a+getGitConfigVal :: (GitConfig -> GlobalConfigurable a) -> Annex a getGitConfigVal f = getGitConfigVal' f >>= \case HasGlobalConfig c -> return c DefaultConfig d -> return d HasGitConfig c -> return c -getGitConfigVal' :: (GitConfig -> Configurable a) -> Annex (Configurable a)+getGitConfigVal' :: (GitConfig -> GlobalConfigurable a) -> Annex (GlobalConfigurable a) getGitConfigVal' f = (f <$> Annex.getGitConfig) >>= \case DefaultConfig _ -> do r <- Annex.gitRepo
Database/Benchmark.hs view
@@ -22,7 +22,6 @@ import Utility.DataUnits import Criterion.Main-import Control.Monad.IO.Class (liftIO) import qualified Data.ByteString.Char8 as B8 import System.Random import Control.Concurrent@@ -92,7 +91,7 @@ } fileN :: Integer -> TopFilePath-fileN n = asTopFilePath ("file" ++ show n)+fileN n = asTopFilePath (toRawFilePath ("file" ++ show n)) keyMiss :: Key keyMiss = keyN 0 -- 0 is never stored
Git/Command.hs view
@@ -55,7 +55,7 @@ - - Also returns an action that should be used when the output is all - read, that will wait on the command, and- - return True if it succeeded. Failure to wait will result in zombies.+ - return True if it succeeded. -} pipeReadLazy :: [CommandParam] -> Repo -> IO (L.ByteString, IO Bool) pipeReadLazy params repo = assertLocal repo $ do@@ -133,16 +133,6 @@ pipeNullSplitStrict params repo = do s <- pipeReadStrict params repo return $ filter (not . S.null) $ S.split 0 s--pipeNullSplitZombie :: [CommandParam] -> Repo -> IO [L.ByteString]-pipeNullSplitZombie params repo = leaveZombie <$> pipeNullSplit params repo--pipeNullSplitZombie' :: [CommandParam] -> Repo -> IO [S.ByteString]-pipeNullSplitZombie' params repo = leaveZombie <$> pipeNullSplit' params repo--{- Doesn't run the cleanup action. A zombie results. -}-leaveZombie :: (a, IO Bool) -> a-leaveZombie = fst {- Runs a git command as a coprocess. -} gitCoProcessStart :: Bool -> [CommandParam] -> Repo -> IO CoProcess.CoProcessHandle
Limit.hs view
@@ -1,6 +1,6 @@ {- user-specified limits on files to act on -- - Copyright 2011-2019 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -51,9 +51,12 @@ {- Gets a matcher for the user-specified limits. The matcher is cached for - speed; once it's obtained the user-specified limits can't change. -} getMatcher :: Annex (MatchInfo -> Annex Bool)-getMatcher = Utility.Matcher.matchM <$> getMatcher'+getMatcher = run <$> getMatcher'+ where+ run matcher i = Utility.Matcher.matchMrun matcher $ \o ->+ matchAction o S.empty i -getMatcher' :: Annex (Utility.Matcher.Matcher (MatchInfo -> Annex Bool))+getMatcher' :: Annex (Utility.Matcher.Matcher (MatchFiles Annex)) getMatcher' = go =<< Annex.getState Annex.limit where go (CompleteMatcher matcher) = return matcher@@ -63,11 +66,16 @@ s { Annex.limit = CompleteMatcher matcher } return matcher +{- Checks if the user-specified limits contains anything that meets the+ - condition. -}+introspect :: (MatchFiles Annex -> Bool) -> Annex Bool+introspect c = Utility.Matcher.introspect c <$> getMatcher'+ {- Adds something to the limit list, which is built up reversed. -}-add :: Utility.Matcher.Token (MatchInfo -> Annex Bool) -> Annex ()+add :: Utility.Matcher.Token (MatchFiles Annex) -> Annex () add l = Annex.changeState $ \s -> s { Annex.limit = prepend $ Annex.limit s } where- prepend (BuildingMatcher ls) = BuildingMatcher $ l:ls+ prepend (BuildingMatcher ls) = BuildingMatcher (l:ls) prepend _ = error "internal" {- Adds a new syntax token. -}@@ -76,41 +84,60 @@ {- Adds a new limit. -} addLimit :: Either String (MatchFiles Annex) -> Annex ()-addLimit = either giveup (\l -> add $ Utility.Matcher.Operation $ l S.empty)+addLimit = either giveup (add . Utility.Matcher.Operation) {- Add a limit to skip files that do not match the glob. -} addInclude :: String -> Annex () addInclude = addLimit . limitInclude limitInclude :: MkLimit Annex-limitInclude glob = Right $ const $ matchGlobFile glob+limitInclude glob = Right $ MatchFiles+ { matchAction = const $ matchGlobFile glob+ , matchNeedsFileName = True+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } {- Add a limit to skip files that match the glob. -} addExclude :: String -> Annex () addExclude = addLimit . limitExclude limitExclude :: MkLimit Annex-limitExclude glob = Right $ const $ not <$$> matchGlobFile glob+limitExclude glob = Right $ MatchFiles+ { matchAction = const $ not <$$> matchGlobFile glob+ , matchNeedsFileName = True+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } matchGlobFile :: String -> MatchInfo -> Annex Bool matchGlobFile glob = go where cglob = compileGlob glob CaseSensative -- memoized go (MatchingFile fi) = pure $ matchGlob cglob (fromRawFilePath (matchFile fi))- go (MatchingInfo p) = matchGlob cglob <$> getInfo (providedFilePath p)+ go (MatchingInfo p) = pure $ matchGlob cglob (fromRawFilePath (providedFilePath p))+ go (MatchingUserInfo p) = matchGlob cglob <$> getUserInfo (userProvidedFilePath p) go (MatchingKey _ (AssociatedFile Nothing)) = pure False go (MatchingKey _ (AssociatedFile (Just af))) = pure $ matchGlob cglob (fromRawFilePath af) addMimeType :: String -> Annex ()-addMimeType = addMagicLimit "mimetype" getMagicMimeType providedMimeType+addMimeType = addMagicLimit "mimetype" getMagicMimeType providedMimeType userProvidedMimeType addMimeEncoding :: String -> Annex ()-addMimeEncoding = addMagicLimit "mimeencoding" getMagicMimeEncoding providedMimeEncoding+addMimeEncoding = addMagicLimit "mimeencoding" getMagicMimeEncoding providedMimeEncoding userProvidedMimeEncoding -addMagicLimit :: String -> (Magic -> FilePath -> Annex (Maybe String)) -> (ProvidedInfo -> OptInfo String) -> String -> Annex ()-addMagicLimit limitname querymagic selectprovidedinfo glob = do+addMagicLimit+ :: String+ -> (Magic -> FilePath -> Annex (Maybe String))+ -> (ProvidedInfo -> Maybe String)+ -> (UserProvidedInfo -> UserInfo String)+ -> String+ -> Annex ()+addMagicLimit limitname querymagic selectprovidedinfo selectuserprovidedinfo glob = do magic <- liftIO initMagicMime- addLimit $ matchMagic limitname querymagic' selectprovidedinfo magic glob+ addLimit $ matchMagic limitname querymagic' selectprovidedinfo selectuserprovidedinfo magic glob where querymagic' magic f = liftIO (isPointerFile (toRawFilePath f)) >>= \case -- Avoid getting magic of a pointer file, which would@@ -123,34 +150,66 @@ querymagic magic . fromRawFilePath Nothing -> querymagic magic f -matchMagic :: String -> (Magic -> FilePath -> Annex (Maybe String)) -> (ProvidedInfo -> OptInfo String) -> Maybe Magic -> MkLimit Annex-matchMagic _limitname querymagic selectprovidedinfo (Just magic) glob = Right $ const go+matchMagic+ :: String+ -> (Magic -> FilePath -> Annex (Maybe String))+ -> (ProvidedInfo -> Maybe String)+ -> (UserProvidedInfo -> UserInfo String)+ -> Maybe Magic+ -> MkLimit Annex+matchMagic _limitname querymagic selectprovidedinfo selectuserprovidedinfo (Just magic) glob = + Right $ MatchFiles+ { matchAction = const go+ , matchNeedsFileName = True+ , matchNeedsFileContent = True+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } where cglob = compileGlob glob CaseSensative -- memoized go (MatchingKey _ _) = pure False- go (MatchingFile fi) = catchBoolIO $- maybe False (matchGlob cglob)- <$> querymagic magic (fromRawFilePath (currFile fi))- go (MatchingInfo p) =- matchGlob cglob <$> getInfo (selectprovidedinfo p)-matchMagic limitname _ _ Nothing _ = + go (MatchingFile fi) = case contentFile fi of+ Just f -> catchBoolIO $+ maybe False (matchGlob cglob)+ <$> querymagic magic (fromRawFilePath f)+ Nothing -> return False+ go (MatchingInfo p) = pure $+ maybe False (matchGlob cglob) (selectprovidedinfo p)+ go (MatchingUserInfo p) =+ matchGlob cglob <$> getUserInfo (selectuserprovidedinfo p)+matchMagic limitname _ _ _ Nothing _ = Left $ "unable to load magic database; \""++limitname++"\" cannot be used" addUnlocked :: Annex ()-addUnlocked = addLimit $ Right $ const $ matchLockStatus False+addUnlocked = addLimit $ Right $ MatchFiles+ { matchAction = const $ matchLockStatus False+ , matchNeedsFileName = True+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } addLocked :: Annex ()-addLocked = addLimit $ Right $ const $ matchLockStatus True+addLocked = addLimit $ Right $ MatchFiles+ { matchAction = const $ matchLockStatus True+ , matchNeedsFileName = True+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } matchLockStatus :: Bool -> MatchInfo -> Annex Bool matchLockStatus _ (MatchingKey _ _) = pure False matchLockStatus _ (MatchingInfo _) = pure False-matchLockStatus wantlocked (MatchingFile fi) = liftIO $ do- islocked <- isPointerFile (currFile fi) >>= \case- Just _key -> return False- Nothing -> isSymbolicLink- <$> getSymbolicLinkStatus (fromRawFilePath (currFile fi))- return (islocked == wantlocked)+matchLockStatus _ (MatchingUserInfo _) = pure False+matchLockStatus wantlocked (MatchingFile fi) = case contentFile fi of+ Just f -> liftIO $ do+ islocked <- isPointerFile f >>= \case+ Just _key -> return False+ Nothing -> isSymbolicLink+ <$> getSymbolicLinkStatus (fromRawFilePath f)+ return (islocked == wantlocked)+ Nothing -> return False {- Adds a limit to skip files not believed to be present - in a specfied repository. Optionally on a prior date. -}@@ -159,19 +218,25 @@ u <- Remote.nameToUUID name hereu <- getUUID addLimit $ if u == hereu && null date- then use inhere- else use (inuuid u)+ then use True checkinhere+ else use False (checkinuuid u) where (name, date) = separate (== '@') s- use a = Right $ checkKey . a- inuuid u notpresent key+ use inhere a = Right $ MatchFiles+ { matchAction = checkKey . a+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = not inhere+ }+ checkinuuid u notpresent key | null date = do us <- Remote.keyLocations key return $ u `elem` us && u `S.notMember` notpresent | otherwise = do us <- loggedLocationsHistorical (RefDate date) key return $ u `elem` us- inhere notpresent key+ checkinhere notpresent key | S.null notpresent = inAnnex key | otherwise = do u <- getUUID@@ -181,22 +246,35 @@ {- Limit to content that is currently present on a uuid. -} limitPresent :: Maybe UUID -> MatchFiles Annex-limitPresent u _ = checkKey $ \key -> do- hereu <- getUUID- if u == Just hereu || isNothing u- then inAnnex key- else do- us <- Remote.keyLocations key- return $ maybe False (`elem` us) u+limitPresent u = MatchFiles+ { matchAction = const $ checkKey $ \key -> do+ hereu <- getUUID+ if u == Just hereu || isNothing u+ then inAnnex key+ else do+ us <- Remote.keyLocations key+ return $ maybe False (`elem` us) u+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = not (isNothing u)+ } {- Limit to content that is in a directory, anywhere in the repository tree -} limitInDir :: FilePath -> MatchFiles Annex-limitInDir dir = const go+limitInDir dir = MatchFiles + { matchAction = const go+ , matchNeedsFileName = True+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } where go (MatchingFile fi) = checkf $ fromRawFilePath $ matchFile fi go (MatchingKey _ (AssociatedFile Nothing)) = return False- go (MatchingKey _ (AssociatedFile (Just af))) = checkf (fromRawFilePath af)- go (MatchingInfo p) = checkf =<< getInfo (providedFilePath p)+ go (MatchingKey _ (AssociatedFile (Just af))) = checkf $ fromRawFilePath af+ go (MatchingInfo p) = checkf $ fromRawFilePath $ providedFilePath p+ go (MatchingUserInfo p) = checkf =<< getUserInfo (userProvidedFilePath p) checkf = return . elem dir . splitPath . takeDirectory {- Adds a limit to skip files not believed to have the specified number@@ -216,8 +294,14 @@ where go num good = case readish num of Nothing -> Left "bad number for copies"- Just n -> Right $ \notpresent -> checkKey $- go' n good notpresent+ Just n -> Right $ MatchFiles+ { matchAction = \notpresent -> checkKey $+ go' n good notpresent+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = True+ } go' n good notpresent key = do us <- filter (`S.notMember` notpresent) <$> (filterM good =<< Remote.keyLocations key)@@ -234,8 +318,14 @@ limitLackingCopies :: Bool -> MkLimit Annex limitLackingCopies approx want = case readish want of- Just needed -> Right $ \notpresent mi -> flip checkKey mi $- go mi needed notpresent+ Just needed -> Right $ MatchFiles+ { matchAction = \notpresent mi -> flip checkKey mi $+ go mi needed notpresent+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = True+ } Nothing -> Left "bad value for number of lacking copies" where go mi needed notpresent key = do@@ -246,6 +336,7 @@ fromRawFilePath $ matchFile fi MatchingKey _ _ -> approxNumCopies MatchingInfo {} -> approxNumCopies+ MatchingUserInfo {} -> approxNumCopies us <- filter (`S.notMember` notpresent) <$> (trustExclude UnTrusted =<< Remote.keyLocations key) return $ numcopies - length us >= needed@@ -257,19 +348,42 @@ - its key is obviously not unused. -} limitUnused :: MatchFiles Annex-limitUnused _ (MatchingFile _) = return False-limitUnused _ (MatchingKey k _) = S.member k <$> unusedKeys-limitUnused _ (MatchingInfo p) = do- k <- getInfo (providedKey p)- S.member k <$> unusedKeys+limitUnused = MatchFiles+ { matchAction = go+ , matchNeedsFileName = True+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = False+ }+ where+ go _ (MatchingFile _) = return False+ go _ (MatchingKey k _) = isunused k+ go _ (MatchingInfo p) = maybe (pure False) isunused (providedKey p)+ go _ (MatchingUserInfo p) = do+ k <- getUserInfo (userProvidedKey p)+ isunused k+ + isunused k = S.member k <$> unusedKeys {- Limit that matches any version of any file or key. -} limitAnything :: MatchFiles Annex-limitAnything _ _ = return True+limitAnything = MatchFiles+ { matchAction = \_ _ -> return True+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } {- Limit that never matches. -} limitNothing :: MatchFiles Annex-limitNothing _ _ = return False+limitNothing = MatchFiles + { matchAction = \_ _ -> return False+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } {- Adds a limit to skip files not believed to be present in all - repositories in the specified group. -}@@ -277,15 +391,21 @@ addInAllGroup groupname = addLimit $ limitInAllGroup groupMap groupname limitInAllGroup :: Annex GroupMap -> MkLimit Annex-limitInAllGroup getgroupmap groupname = Right $ \notpresent mi -> do- m <- getgroupmap- let want = fromMaybe S.empty $ M.lookup (toGroup groupname) $ uuidsByGroup m- if S.null want- then return True- -- optimisation: Check if a wanted uuid is notpresent.- else if not (S.null (S.intersection want notpresent))- then return False- else checkKey (check want) mi+limitInAllGroup getgroupmap groupname = Right $ MatchFiles+ { matchAction = \notpresent mi -> do+ m <- getgroupmap+ let want = fromMaybe S.empty $ M.lookup (toGroup groupname) $ uuidsByGroup m+ if S.null want+ then return True+ -- optimisation: Check if a wanted uuid is notpresent.+ else if not (S.null (S.intersection want notpresent))+ then return False+ else checkKey (check want) mi+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = True+ } where check want key = do present <- S.fromList <$> Remote.keyLocations key@@ -296,7 +416,13 @@ addInBackend = addLimit . limitInBackend limitInBackend :: MkLimit Annex-limitInBackend name = Right $ const $ checkKey check+limitInBackend name = Right $ MatchFiles+ { matchAction = const $ checkKey check+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = False+ } where check key = pure $ fromKey keyVariety key == variety variety = parseKeyVariety (encodeBS name)@@ -306,7 +432,13 @@ addSecureHash = addLimit $ Right limitSecureHash limitSecureHash :: MatchFiles Annex-limitSecureHash _ = checkKey isCryptographicallySecure+limitSecureHash = MatchFiles+ { matchAction = const $ checkKey isCryptographicallySecure+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = False+ } {- Adds a limit to skip files that are too large or too small -} addLargerThan :: String -> Annex ()@@ -318,20 +450,33 @@ limitSize :: LimitBy -> (Maybe Integer -> Maybe Integer -> Bool) -> MkLimit Annex limitSize lb vs s = case readSize dataUnits s of Nothing -> Left "bad size"- Just sz -> Right $ go sz+ Just sz -> Right $ MatchFiles+ { matchAction = go sz+ , matchNeedsFileName = case lb of+ LimitAnnexFiles -> False+ LimitDiskFiles -> True+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } where go sz _ (MatchingFile fi) = case lb of- LimitAnnexFiles -> lookupFileKey fi >>= \case- Just key -> checkkey sz key- Nothing -> return False- LimitDiskFiles -> do- filesize <- liftIO $ catchMaybeIO $- getFileSize (fromRawFilePath (currFile fi))- return $ filesize `vs` Just sz+ LimitAnnexFiles -> goannexed sz fi+ LimitDiskFiles -> case contentFile fi of+ Just f -> do+ filesize <- liftIO $ catchMaybeIO $+ getFileSize (fromRawFilePath f)+ return $ filesize `vs` Just sz+ Nothing -> goannexed sz fi go sz _ (MatchingKey key _) = checkkey sz key- go sz _ (MatchingInfo p) =- getInfo (providedFileSize p) + go sz _ (MatchingInfo p) = return $+ Just (providedFileSize p) `vs` Just sz+ go sz _ (MatchingUserInfo p) =+ getUserInfo (userProvidedFileSize p) >>= \sz' -> return (Just sz' `vs` Just sz)+ goannexed sz fi = lookupFileKey fi >>= \case+ Just key -> checkkey sz key+ Nothing -> return False checkkey sz key = return $ fromKey keySize key `vs` Just sz addMetaData :: String -> Annex ()@@ -340,7 +485,13 @@ limitMetaData :: MkLimit Annex limitMetaData s = case parseMetaDataMatcher s of Left e -> Left e- Right (f, matching) -> Right $ const $ checkKey (check f matching)+ Right (f, matching) -> Right $ MatchFiles+ { matchAction = const $ checkKey (check f matching)+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = True+ , matchNeedsLocationLog = False+ } where check f matching k = not . S.null . S.filter matching@@ -350,19 +501,31 @@ addTimeLimit duration = do start <- liftIO getPOSIXTime let cutoff = start + durationToPOSIXTime duration- addLimit $ Right $ const $ const $ do- now <- liftIO getPOSIXTime- if now > cutoff- then do- warning $ "Time limit (" ++ fromDuration duration ++ ") reached!"- shutdown True- liftIO $ exitWith $ ExitFailure 101- else return True+ addLimit $ Right $ MatchFiles+ { matchAction = const $ const $ do+ now <- liftIO getPOSIXTime+ if now > cutoff+ then do+ warning $ "Time limit (" ++ fromDuration duration ++ ") reached!"+ shutdown True+ liftIO $ exitWith $ ExitFailure 101+ else return True+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } addAccessedWithin :: Duration -> Annex () addAccessedWithin duration = do now <- liftIO getPOSIXTime- addLimit $ Right $ const $ checkKey $ check now+ addLimit $ Right $ MatchFiles+ { matchAction = const $ checkKey $ check now+ , matchNeedsFileName = False+ , matchNeedsFileContent = False+ , matchNeedsKey = False+ , matchNeedsLocationLog = False+ } where check now k = inAnnexCheck k $ \f -> liftIO $ catchDefaultIO False $ do@@ -373,10 +536,12 @@ secs = fromIntegral (durationSeconds duration) lookupFileKey :: FileInfo -> Annex (Maybe Key)-lookupFileKey = lookupKey . currFile+lookupFileKey fi = case contentFile fi of+ Just f -> lookupKey f+ Nothing -> return Nothing checkKey :: (Key -> Annex Bool) -> MatchInfo -> Annex Bool checkKey a (MatchingFile fi) = lookupFileKey fi >>= maybe (return False) a checkKey a (MatchingKey k _) = a k-checkKey a (MatchingInfo p) = a =<< getInfo (providedKey p)-+checkKey a (MatchingInfo p) = maybe (return False) a (providedKey p)+checkKey a (MatchingUserInfo p) = a =<< getUserInfo (userProvidedKey p)
Limit/Wanted.hs view
@@ -1,6 +1,6 @@ {- git-annex limits by wanted status -- - Copyright 2012 Joey Hess <id@joeyh.name>+ - Copyright 2012-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -11,16 +11,32 @@ import Annex.Wanted import Limit import Types.FileMatcher+import Logs.PreferredContent addWantGet :: Annex ()-addWantGet = addLimit $ Right $ const $ checkWant $- wantGet False Nothing+addWantGet = addPreferredContentLimit $+ checkWant $ wantGet False Nothing addWantDrop :: Annex ()-addWantDrop = addLimit $ Right $ const $ checkWant $- wantDrop False Nothing Nothing+addWantDrop = addPreferredContentLimit $+ checkWant $ wantDrop False Nothing Nothing +addPreferredContentLimit :: (MatchInfo -> Annex Bool) -> Annex ()+addPreferredContentLimit a = do+ nfn <- introspectPreferredRequiredContent matchNeedsFileName Nothing+ nfc <- introspectPreferredRequiredContent matchNeedsFileContent Nothing+ nk <- introspectPreferredRequiredContent matchNeedsKey Nothing+ nl <- introspectPreferredRequiredContent matchNeedsLocationLog Nothing+ addLimit $ Right $ MatchFiles+ { matchAction = const a+ , matchNeedsFileName = nfn+ , matchNeedsFileContent = nfc+ , matchNeedsKey = nk+ , matchNeedsLocationLog = nl+ }+ checkWant :: (AssociatedFile -> Annex Bool) -> MatchInfo -> Annex Bool checkWant a (MatchingFile fi) = a (AssociatedFile (Just $ matchFile fi)) checkWant a (MatchingKey _ af) = a af-checkWant _ (MatchingInfo {}) = return False+checkWant a (MatchingInfo p) = a (AssociatedFile (Just $ providedFilePath p))+checkWant _ (MatchingUserInfo {}) = return False
Logs/Location.hs view
@@ -126,14 +126,15 @@ - - Keys that have been marked as dead are not included. -}-loggedKeys :: Annex [Unchecked Key]+loggedKeys :: Annex ([Unchecked Key], IO Bool) loggedKeys = loggedKeys' (not <$$> checkDead) -loggedKeys' :: (Key -> Annex Bool) -> Annex [Unchecked Key]+loggedKeys' :: (Key -> Annex Bool) -> Annex ([Unchecked Key], IO Bool) loggedKeys' check = do config <- Annex.getGitConfig- mapMaybe (defercheck <$$> locationLogFileKey config)- <$> Annex.Branch.files+ (bfs, cleanup) <- Annex.Branch.files+ let l = mapMaybe (defercheck <$$> locationLogFileKey config) bfs+ return (l, cleanup) where defercheck k = Unchecked $ ifM (check k) ( return (Just k)@@ -146,9 +147,13 @@ - This does not stream well; use loggedKeysFor' for lazy streaming. -} loggedKeysFor :: UUID -> Annex [Key]-loggedKeysFor u = catMaybes <$> (mapM finishCheck =<< loggedKeysFor' u)+loggedKeysFor u = do+ (l, cleanup) <- loggedKeysFor' u+ l' <- catMaybes <$> mapM finishCheck l+ liftIO $ void cleanup+ return l' -loggedKeysFor' :: UUID -> Annex [Unchecked Key]+loggedKeysFor' :: UUID -> Annex ([Unchecked Key], IO Bool) loggedKeysFor' u = loggedKeys' isthere where isthere k = do
Logs/PreferredContent.hs view
@@ -1,6 +1,6 @@ {- git-annex preferred content matcher configuration -- - Copyright 2012-2019 Joey Hess <id@joeyh.name>+ - Copyright 2012-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -21,6 +21,7 @@ defaultStandardGroup, preferredRequiredMapsLoad, preferredRequiredMapsLoad',+ introspectPreferredRequiredContent, prop_standardGroups_parse, ) where @@ -61,6 +62,16 @@ Nothing -> return d Just matcher -> checkMatcher matcher mkey afile notpresent (return d) (return d) +{- Checks if the preferred or required content for the specified repository+ - (or the current repository if none is specified) contains any terms+ - that meet the condition. -}+introspectPreferredRequiredContent :: (MatchFiles Annex -> Bool) -> Maybe UUID -> Annex Bool+introspectPreferredRequiredContent c mu = do+ u <- maybe getUUID return mu+ check u preferredContentMap <||> check u requiredContentMap+ where+ check u mk = mk >>= return . maybe False (any c) . M.lookup u+ preferredContentMap :: Annex (FileMatcherMap Annex) preferredContentMap = maybe (fst <$> preferredRequiredMapsLoad preferredContentTokens) return =<< Annex.getState Annex.preferredcontentmap@@ -86,7 +97,7 @@ preferredRequiredMapsLoad' :: (PreferredContentData -> [ParseToken (MatchFiles Annex)]) -> Annex (M.Map UUID (Either String (FileMatcher Annex)), M.Map UUID (Either String (FileMatcher Annex))) preferredRequiredMapsLoad' mktokens = do groupmap <- groupMap- configmap <- readRemoteLog+ configmap <- remoteConfigMap let genmap l gm = let mk u = makeMatcher groupmap configmap gm u mktokens in simpleMap
Logs/Remote.hs view
@@ -1,12 +1,13 @@ {- git-annex remote log - - - Copyright 2011-2019 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -} module Logs.Remote ( remoteLog,+ remoteConfigMap, readRemoteLog, configSet, keyValToConfig,@@ -18,6 +19,7 @@ ) where import Annex.Common+import qualified Annex import qualified Annex.Branch import Types.Remote import Logs@@ -35,8 +37,20 @@ buildRemoteConfigLog . changeLog c u (removeSameasInherited cfg) . parseRemoteConfigLog+ Annex.changeState $ \s -> s { Annex.remoteconfigmap = Nothing } -{- Map of remotes by uuid containing key/value config maps. -}+{- Map of remotes by uuid containing key/value config maps.+ - Cached for speed. -}+remoteConfigMap :: Annex (M.Map UUID RemoteConfig)+remoteConfigMap = maybe remoteConfigMapLoad return+ =<< Annex.getState Annex.remoteconfigmap++remoteConfigMapLoad :: Annex (M.Map UUID RemoteConfig)+remoteConfigMapLoad = do+ m <- readRemoteLog+ Annex.changeState $ \s -> s { Annex.remoteconfigmap = Just m }+ return m+ readRemoteLog :: Annex (M.Map UUID RemoteConfig) readRemoteLog = calcRemoteConfigMap <$> Annex.Branch.get remoteLog
Messages.hs view
@@ -9,7 +9,7 @@ module Messages ( showStart,- showStart',+ showStartOther, showStartMessage, showEndMessage, StartMessage(..),@@ -45,6 +45,7 @@ disableDebugOutput, debugEnabled, commandProgressDisabled,+ jsonOutputEnabled, outputMessage, withMessageState, prompt,@@ -64,45 +65,46 @@ import Types.Messages import Types.ActionItem import Types.Concurrency-import Types.Command (StartMessage(..))+import Types.Command (StartMessage(..), SeekInput) import Types.Transfer (transferKey) import Messages.Internal import Messages.Concurrent+import Annex.Concurrent.Utility import qualified Messages.JSON as JSON import qualified Annex -showStart :: String -> RawFilePath -> Annex ()-showStart command file = outputMessage json $+showStart :: String -> RawFilePath -> SeekInput -> Annex ()+showStart command file si = outputMessage json $ encodeBS' command <> " " <> file <> " " where- json = JSON.start command (Just file) Nothing+ json = JSON.start command (Just file) Nothing si -showStart' :: String -> Maybe String -> Annex ()-showStart' command mdesc = outputMessage json $ encodeBS' $- command ++ (maybe "" (" " ++) mdesc) ++ " "+showStartKey :: String -> Key -> ActionItem -> SeekInput -> Annex ()+showStartKey command key ai si = outputMessage json $+ encodeBS' command <> " " <> actionItemDesc ai <> " " where- json = JSON.start command Nothing Nothing+ json = JSON.start command (actionItemWorkTreeFile ai) (Just key) si -showStartKey :: String -> Key -> ActionItem -> Annex ()-showStartKey command key i = outputMessage json $- encodeBS' command <> " " <> actionItemDesc i <> " "+showStartOther :: String -> Maybe String -> SeekInput -> Annex ()+showStartOther command mdesc si = outputMessage json $ encodeBS' $+ command ++ (maybe "" (" " ++) mdesc) ++ " " where- json = JSON.start command (actionItemWorkTreeFile i) (Just key)+ json = JSON.start command Nothing Nothing si showStartMessage :: StartMessage -> Annex ()-showStartMessage (StartMessage command ai) = case ai of- ActionItemAssociatedFile _ k -> showStartKey command k ai- ActionItemKey k -> showStartKey command k ai- ActionItemBranchFilePath _ k -> showStartKey command k ai- ActionItemFailedTransfer t _ -> showStartKey command (transferKey t) ai- ActionItemWorkTreeFile file -> showStart command file- ActionItemOther msg -> showStart' command msg- OnlyActionOn _ ai' -> showStartMessage (StartMessage command ai')-showStartMessage (StartUsualMessages command ai) = do+showStartMessage (StartMessage command ai si) = case ai of+ ActionItemAssociatedFile _ k -> showStartKey command k ai si+ ActionItemKey k -> showStartKey command k ai si+ ActionItemBranchFilePath _ k -> showStartKey command k ai si+ ActionItemFailedTransfer t _ -> showStartKey command (transferKey t) ai si+ ActionItemWorkTreeFile file -> showStart command file si+ ActionItemOther msg -> showStartOther command msg si+ OnlyActionOn _ ai' -> showStartMessage (StartMessage command ai' si)+showStartMessage (StartUsualMessages command ai si) = do outputType <$> Annex.getState Annex.output >>= \case QuietOutput -> Annex.setOutput NormalOutput _ -> noop- showStartMessage (StartMessage command ai)+ showStartMessage (StartMessage command ai si) showStartMessage (StartNoMessage _) = noop showStartMessage (CustomOutput _) = outputType <$> Annex.getState Annex.output >>= \case@@ -111,8 +113,8 @@ -- Only show end result if the StartMessage is one that gets displayed. showEndMessage :: StartMessage -> Bool -> Annex ()-showEndMessage (StartMessage _ _) = showEndResult-showEndMessage (StartUsualMessages _ _) = showEndResult+showEndMessage (StartMessage _ _ _) = showEndResult+showEndMessage (StartUsualMessages _ _ _) = showEndResult showEndMessage (StartNoMessage _) = const noop showEndMessage (CustomOutput _) = const noop @@ -238,9 +240,9 @@ - a complete JSON document. - This is only needed when showStart and showEndOk is not used. -}-showCustom :: String -> Annex Bool -> Annex ()-showCustom command a = do- outputMessage (JSON.start command Nothing Nothing) ""+showCustom :: String -> SeekInput -> Annex Bool -> Annex ()+showCustom command si a = do+ outputMessage (JSON.start command Nothing Nothing si) "" r <- a outputMessage (JSON.end r) "" @@ -287,6 +289,12 @@ JSONOutput _ -> True NormalOutput -> concurrentOutputEnabled s +jsonOutputEnabled :: Annex Bool+jsonOutputEnabled = withMessageState $ \s -> return $+ case outputType s of+ JSONOutput _ -> True+ _ -> False+ {- Prevents any concurrent console access while running an action, so - that the action is the only thing using the console, and can eg prompt - the user.@@ -298,7 +306,7 @@ {- Like prompt, but for a non-annex action that prompts. -} mkPrompter :: (MonadMask m, MonadIO m) => Annex (m a -> m a)-mkPrompter = Annex.getState Annex.concurrency >>= \case+mkPrompter = getConcurrency >>= \case NonConcurrent -> return id (Concurrent _) -> goconcurrent ConcurrentPerCpu -> goconcurrent
Messages/JSON.hs view
@@ -1,6 +1,6 @@ {- git-annex command-line JSON output and input -- - Copyright 2011-2018 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -39,6 +39,7 @@ import Prelude import Types.Messages+import Types.Command (SeekInput(..)) import Key import Utility.Metered import Utility.Percentage@@ -64,8 +65,8 @@ none :: JSONBuilder none = id -start :: String -> Maybe RawFilePath -> Maybe Key -> JSONBuilder-start command file key _ = case j of+start :: String -> Maybe RawFilePath -> Maybe Key -> SeekInput -> JSONBuilder+start command file key si _ = case j of Object o -> Just (o, False) _ -> Nothing where@@ -74,6 +75,7 @@ , itemKey = key , itemFile = fromRawFilePath <$> file , itemAdded = Nothing+ , itemSeekInput = si } end :: Bool -> JSONBuilder@@ -176,6 +178,7 @@ , itemKey :: Maybe Key , itemFile :: Maybe FilePath , itemAdded :: Maybe a -- for additional fields added by `add`+ , itemSeekInput :: SeekInput } deriving (Show) @@ -183,10 +186,11 @@ toJSON' i = object $ catMaybes [ Just $ "command" .= itemCommand i , case itemKey i of- Nothing -> Nothing Just k -> Just $ "key" .= toJSON' k+ Nothing -> Nothing , Just $ "file" .= toJSON' (itemFile i) -- itemAdded is not included; must be added later by 'add'+ , Just $ "input" .= fromSeekInput (itemSeekInput i) ] instance FromJSON a => FromJSON (JSONActionItem a) where@@ -195,6 +199,7 @@ <*> (maybe (return Nothing) parseJSON =<< (v .:? "key")) <*> (v .:? "file") <*> parseadded+ <*> pure (SeekInput []) where parseadded = (Just <$> parseJSON (Object v)) <|> return Nothing parseJSON _ = mempty
NEWS view
@@ -1,4 +1,4 @@-git-annex (8.20200523) UNRELEASED; urgency=medium+git-annex (8.20200523) upstream; urgency=medium Transition notice: Commands like `git-annex get foo*` silently skip over files that are not checked into git. Since that can be surprising in many
Remote.hs view
@@ -1,6 +1,6 @@ {- git-annex remotes -- - Copyright 2011-2019 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -73,6 +73,7 @@ import Logs.UUID import Logs.Trust import Logs.Location hiding (logStatus)+import Logs.Remote import Logs.Web import Remote.List import Remote.List.Util@@ -337,31 +338,53 @@ return (sortBy (comparing cost) validremotes, validtrustedlocations) -{- Displays known locations of a key. -}+{- Displays known locations of a key and helps the user take action+ - to make them accessible. -} showLocations :: Bool -> Key -> [UUID] -> String -> Annex () showLocations separateuntrusted key exclude nolocmsg = do u <- getUUID+ remotes <- remoteList uuids <- keyLocations key untrusteduuids <- if separateuntrusted then trustGet UnTrusted else pure []- let uuidswanted = filteruuids uuids (u:exclude++untrusteduuids) + let uuidswanted = filteruuids uuids (u:exclude++untrusteduuids) let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted)- ppuuidswanted <- prettyPrintUUIDs "wanted" uuidswanted- ppuuidsskipped <- prettyPrintUUIDs "skipped" uuidsskipped- let msg = message ppuuidswanted ppuuidsskipped- unless (null msg) $- showLongNote msg- ignored <- remoteList- >>= filterM (liftIO . getDynamicConfig . remoteAnnexIgnore . gitconfig)+ let remoteuuids = map uuid remotes+ let isremoteuuid x = elem x remoteuuids+ let (remotesmakeavailable, uuidsothers) = + partition isremoteuuid uuidswanted+ isspecialremote <- flip M.member <$> remoteConfigMap+ let (enablespecialremotes, addgitremotes) = + partition isspecialremote uuidsothers+ + -- Add "wanted" field to the JSON. While it's since been split+ -- up more, this avoids breaking any JSON parsers that expect it.+ ifM jsonOutputEnabled+ ( void $ prettyPrintUUIDs "wanted" uuidswanted+ , do+ ppremotesmakeavailable <- pp "remotes" remotesmakeavailable+ "Try making some of these remotes available"+ ppenablespecialremotes <- pp "enableremote" enablespecialremotes+ "Maybe enable some of these special remotes (git annex initremote ...)"+ ppaddgitremotes <- pp "repos" addgitremotes+ "Maybe add some of these git remotes (git remote add ...)"+ ppuuidsskipped <- pp "skipped" uuidsskipped+ "Also these untrusted repositories may contain the file"+ showLongNote $ case ppremotesmakeavailable ++ ppenablespecialremotes ++ ppaddgitremotes ++ ppuuidsskipped of+ [] -> nolocmsg+ s -> s+ )+ ignored <- filterM (liftIO . getDynamicConfig . remoteAnnexIgnore . gitconfig) remotes unless (null ignored) $ showLongNote $ "(Note that these git remotes have annex-ignore set: " ++ unwords (map name ignored) ++ ")" where filteruuids l x = filter (`notElem` x) l- message [] [] = nolocmsg- message rs [] = "Try making some of these repositories available:\n" ++ rs- message [] us = "Also these untrusted repositories may contain the file:\n" ++ us- message rs us = message rs [] ++ message [] us++ pp jh l h = addheader h <$> prettyPrintUUIDs jh l++ addheader _ [] = []+ addheader h l = h ++ ":\n" ++ l showTriedRemotes :: [Remote] -> Annex () showTriedRemotes [] = noop
Remote/BitTorrent.hs view
@@ -307,16 +307,16 @@ ariaProgress Nothing _ ps = runAria ps ariaProgress (Just sz) meter ps = do oh <- mkOutputHandler- liftIO . commandMeter (parseAriaProgress sz) oh meter "aria2c"+ liftIO . commandMeter (parseAriaProgress sz) oh Nothing meter "aria2c" =<< ariaParams ps parseAriaProgress :: Integer -> ProgressParser parseAriaProgress totalsize = go [] . reverse . splitc '\r' where- go remainder [] = (Nothing, remainder)+ go remainder [] = (Nothing, Nothing, remainder) go remainder (x:xs) = case readish (findpercent x) of Nothing -> go (x++remainder) xs- Just p -> (Just (frompercent p), remainder)+ Just p -> (Just (frompercent p), Nothing, remainder) -- "(N%)" findpercent = takeWhile (/= '%') . drop 1 . dropWhile (/= '(')
Remote/GCrypt.hs view
@@ -109,7 +109,7 @@ -- correctly. resetup gcryptid r = do let u' = genUUIDInNameSpace gCryptNameSpace gcryptid- v <- M.lookup u' <$> readRemoteLog+ v <- M.lookup u' <$> remoteConfigMap case (Git.remoteName baser, v) of (Just remotename, Just rc') -> do pc <- parsedRemoteConfig remote rc'
Remote/GitLFS.hs view
@@ -187,9 +187,9 @@ configKnownUrl :: Git.Repo -> Annex (Maybe Git.Repo) configKnownUrl r | Git.repoIsUrl r = do- l <- readRemoteLog+ m <- remoteConfigMap g <- Annex.gitRepo- case Annex.SpecialRemote.Config.findByRemoteConfig (match g) l of+ case Annex.SpecialRemote.Config.findByRemoteConfig (match g) m of ((u, _, mcu):[]) -> Just <$> go u mcu _ -> return Nothing | otherwise = return Nothing
Remote/HttpAlso.hs view
@@ -105,7 +105,9 @@ _url <- maybe (giveup "Specify url=") (return . fromProposedAccepted) (M.lookup urlField c)- (c', _encsetup) <- encryptionSetup c gc+ c' <- if isJust (M.lookup encryptionField c)+ then fst <$> encryptionSetup c gc+ else pure c gitConfigSpecialRemote u c' [("httpalso", "true")] return (c', u)
Remote/List.hs view
@@ -72,7 +72,7 @@ remoteList' :: Bool -> Annex [Remote] remoteList' autoinit = do- m <- readRemoteLog+ m <- remoteConfigMap rs <- concat <$> mapM (process m) remoteTypes Annex.changeState $ \s -> s { Annex.remotes = rs } return rs@@ -96,7 +96,7 @@ {- Updates a local git Remote, re-reading its git config. -} updateRemote :: Remote -> Annex (Maybe Remote) updateRemote remote = do- m <- readRemoteLog+ m <- remoteConfigMap remote' <- updaterepo =<< getRepo remote remoteGen m (remotetype remote) remote' where
Remote/S3.hs view
@@ -36,6 +36,7 @@ import System.Log.Logger import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TVar+import Data.Maybe import Annex.Common import Types.Remote@@ -63,6 +64,7 @@ import Utility.DataUnits import Annex.Content import qualified Annex.Url as Url+import Utility.Url (extractFromResourceT) import Annex.Url (getUrlOptions, withUrlOptions, UrlOptions(..)) import Utility.Env @@ -343,12 +345,11 @@ let req = (putObject info object rbody) { S3.poContentType = encodeBS <$> contenttype } resp <- sendS3Handle h req- let vid = mkS3VersionID object (S3.porVersionId resp)- -- FIXME Actual aws version that supports this is not known,- -- patch not merged yet.- -- https://github.com/aristidb/aws/issues/258-#if MIN_VERSION_aws(0,99,0)- return (Just (S3.porETag resp), vid)+ vid <- mkS3VersionID object+ <$> extractFromResourceT (S3.porVersionId resp)+#if MIN_VERSION_aws(0,22,0)+ etag <- extractFromResourceT (Just (S3.porETag resp))+ return (etag, vid) #else return (Nothing, vid) #endif@@ -389,7 +390,9 @@ resp <- sendS3Handle h $ S3.postCompleteMultipartUpload (bucket info) object uploadid (zip [1..] etags)- return (Just (S3.cmurETag resp), mkS3VersionID object (S3.cmurVersionId resp))+ etag <- extractFromResourceT (Just (S3.cmurETag resp))+ vid <- extractFromResourceT (S3.cmurVersionId resp)+ return (etag, mkS3VersionID object vid) getcontenttype = maybe (pure Nothing) (flip getMagicMimeType f) magic {- Implemented as a fileRetriever, that uses conduit to stream the chunks@@ -474,7 +477,7 @@ checkKeyHelper' :: S3Info -> S3Handle -> S3.Object -> (S3.HeadObject -> S3.HeadObject) -> Annex Bool checkKeyHelper' info h o limit = liftIO $ runResourceT $ do rsp <- sendS3Handle h req- return (isJust $ S3.horMetadata rsp)+ extractFromResourceT (isJust $ S3.horMetadata rsp) where req = limit $ S3.headObject (bucket info) o @@ -552,7 +555,8 @@ Nothing -> do warning $ needS3Creds (uuid r) return Nothing- Just h -> catchMaybeIO $ liftIO $ runResourceT $ startlist h+ Just h -> catchMaybeIO $ liftIO $ runResourceT $+ extractFromResourceT =<< startlist h where startlist h | versioning info = do@@ -678,10 +682,7 @@ storeExportWithContentIdentifierS3 :: S3HandleVar -> Remote -> RemoteStateHandle -> S3Info -> Maybe Magic -> FilePath -> Key -> ExportLocation -> [ContentIdentifier] -> MeterUpdate -> Annex ContentIdentifier storeExportWithContentIdentifierS3 hv r rs info magic src k loc _overwritablecids p | versioning info = go- -- FIXME Actual aws version that supports getting Etag for a store- -- is not known; patch not merged yet.- -- https://github.com/aristidb/aws/issues/258-#if MIN_VERSION_aws(0,99,0)+#if MIN_VERSION_aws(0,22,0) | otherwise = go #else | otherwise = giveup "git-annex is built with too old a version of the aws library to support this operation"@@ -733,11 +734,12 @@ where go _ _ (Right True) = noop go info h _ = do- v <- liftIO $ tryNonAsync $ runResourceT $- sendS3Handle h (S3.getBucket $ bucket info)- case v of- Right _ -> noop- Left _ -> do+ r <- liftIO $ tryNonAsync $ runResourceT $ do+ void $ sendS3Handle h (S3.getBucket $ bucket info)+ return True+ case r of+ Right True -> noop+ _ -> do showAction $ "creating bucket in " ++ datacenter void $ liftIO $ runResourceT $ sendS3Handle h $ (S3.putBucket (bucket info))@@ -787,8 +789,7 @@ Left _ -> return False Right r -> do v <- AWS.loadToMemory r- let !ok = check v- return ok+ extractFromResourceT (check v) where check (S3.GetObjectMemoryResponse _meta rsp) = responseStatus rsp == ok200 && responseBody rsp == uuidb
Remote/Web.hs view
@@ -90,9 +90,7 @@ r <- untilTrue urls $ \u -> do let (u', downloader) = getDownloader u case downloader of- YoutubeDownloader -> do- showOutput- youtubeDlTo key u' dest+ YoutubeDownloader -> youtubeDlTo key u' dest p _ -> Url.withUrlOptions $ downloadUrl key p [u'] dest unless r $ giveup "download failed"
Test/Framework.hs view
@@ -245,15 +245,13 @@ finalCleanup :: IO () finalCleanup = whenM (doesDirectoryExist tmpdir) $ do- Annex.Action.reapZombies Command.Uninit.prepareRemoveAnnexDir' tmpdir catchIO (removeDirectoryForCleanup tmpdir) $ \e -> do print e putStrLn "sleeping 10 seconds and will retry directory cleanup" Utility.ThreadScheduler.threadDelaySeconds $ Utility.ThreadScheduler.Seconds 10- whenM (doesDirectoryExist tmpdir) $ do- Annex.Action.reapZombies+ whenM (doesDirectoryExist tmpdir) $ removeDirectoryForCleanup tmpdir checklink :: FilePath -> Assertion
Types/Command.hs view
@@ -38,13 +38,18 @@ - returns the overall success/fail of the command. -} type CommandCleanup = Annex Bool +{- Input that was seeked on to make an ActionItem. Eg, the input filename,+ - or directory name. -}+newtype SeekInput = SeekInput { fromSeekInput :: [String] }+ deriving (Show)+ {- Message that is displayed when starting to perform an action on - something. The String is typically the name of the command or action - being performed. -} data StartMessage- = StartMessage String ActionItem- | StartUsualMessages String ActionItem+ = StartMessage String ActionItem SeekInput+ | StartUsualMessages String ActionItem SeekInput -- ^ Like StartMessage, but makes sure to enable usual message -- display in case it was disabled by cmdnomessages. | StartNoMessage ActionItem@@ -56,8 +61,8 @@ deriving (Show) instance MkActionItem StartMessage where- mkActionItem (StartMessage _ ai) = ai- mkActionItem (StartUsualMessages _ ai) = ai+ mkActionItem (StartMessage _ ai _) = ai+ mkActionItem (StartUsualMessages _ ai _) = ai mkActionItem (StartNoMessage ai) = ai mkActionItem (CustomOutput ai) = ai
Types/Concurrency.hs view
@@ -17,3 +17,8 @@ parseConcurrency "cpus" = Just ConcurrentPerCpu parseConcurrency "cpu" = Just ConcurrentPerCpu parseConcurrency s = Concurrent <$> readish s++-- Concurrency can be configured at the command line or by git config.+data ConcurrencySetting+ = ConcurrencyCmdLine Concurrency+ | ConcurrencyGitConfig Concurrency
Types/Export.hs view
@@ -5,6 +5,8 @@ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE DeriveGeneric #-}+ module Types.Export ( ExportLocation, mkExportLocation,@@ -20,12 +22,16 @@ import Utility.FileSystemEncoding import qualified System.FilePath.Posix as Posix+import GHC.Generics+import Control.DeepSeq -- A location on a remote that a key can be exported to. -- The RawFilePath will be relative to the top of the remote, -- and uses unix-style path separators. newtype ExportLocation = ExportLocation RawFilePath- deriving (Show, Eq)+ deriving (Show, Eq, Generic)++instance NFData ExportLocation mkExportLocation :: RawFilePath -> ExportLocation mkExportLocation = ExportLocation . toInternalGitPath
Types/FileMatcher.hs view
@@ -1,6 +1,6 @@ {- git-annex file matcher types -- - Copyright 2013-2019 Joey Hess <id@joeyh.name>+ - Copyright 2013-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -23,31 +23,42 @@ = MatchingFile FileInfo | MatchingKey Key AssociatedFile | MatchingInfo ProvidedInfo+ | MatchingUserInfo UserProvidedInfo data FileInfo = FileInfo- { currFile :: RawFilePath- -- ^ current path to the file, for operations that examine it+ { contentFile :: Maybe RawFilePath+ -- ^ path to a file containing the content, for operations+ -- that examine it , matchFile :: RawFilePath -- ^ filepath to match on; may be relative to top of repo or cwd } --- This is used when testing a matcher, with values to match against--- provided in some way, rather than queried from files on disk. data ProvidedInfo = ProvidedInfo- { providedFilePath :: OptInfo FilePath- , providedKey :: OptInfo Key- , providedFileSize :: OptInfo FileSize- , providedMimeType :: OptInfo MimeType- , providedMimeEncoding :: OptInfo MimeEncoding+ { providedFilePath :: RawFilePath+ , providedKey :: Maybe Key+ , providedFileSize :: FileSize+ , providedMimeType :: Maybe MimeType+ , providedMimeEncoding :: Maybe MimeEncoding } -type OptInfo a = Either (IO a) a+-- This is used when testing a matcher, with values to match against+-- provided by the user.+data UserProvidedInfo = UserProvidedInfo+ { userProvidedFilePath :: UserInfo FilePath+ , userProvidedKey :: UserInfo Key+ , userProvidedFileSize :: UserInfo FileSize+ , userProvidedMimeType :: UserInfo MimeType+ , userProvidedMimeEncoding :: UserInfo MimeEncoding+ } --- If the OptInfo is not available, accessing it may result in eg an+-- This may fail if the user did not provide the information.+type UserInfo a = Either (IO a) a++-- If the UserInfo is not available, accessing it may result in eg an -- exception being thrown.-getInfo :: MonadIO m => OptInfo a -> m a-getInfo (Right i) = return i-getInfo (Left e) = liftIO e+getUserInfo :: MonadIO m => UserInfo a -> m a+getUserInfo (Right i) = return i+getUserInfo (Left e) = liftIO e type FileMatcherMap a = M.Map UUID (FileMatcher a) @@ -55,12 +66,23 @@ type AssumeNotPresent = S.Set UUID -type MatchFiles a = AssumeNotPresent -> MatchInfo -> a Bool+data MatchFiles a = MatchFiles + { matchAction :: AssumeNotPresent -> MatchInfo -> a Bool+ , matchNeedsFileName :: Bool+ -- ^ does the matchAction need a filename in order to match?+ , matchNeedsFileContent :: Bool+ -- ^ does the matchAction need the file content to be present in+ -- order to succeed?+ , matchNeedsKey :: Bool+ -- ^ does the matchAction look at information about the key?+ , matchNeedsLocationLog :: Bool+ -- ^ does the matchAction look at the location log?+ } type FileMatcher a = Matcher (MatchFiles a) -- This is a matcher that can have tokens added to it while it's being -- built, and once complete is compiled to an unchangable matcher. data ExpandableMatcher a- = BuildingMatcher [Token (MatchInfo -> a Bool)]- | CompleteMatcher (Matcher (MatchInfo -> a Bool))+ = BuildingMatcher [Token (MatchFiles a)]+ | CompleteMatcher (Matcher (MatchFiles a))
Types/GitConfig.hs view
@@ -9,7 +9,7 @@ {-# LANGUAGE OverloadedStrings #-} module Types.GitConfig ( - Configurable(..),+ GlobalConfigurable(..), ConfigSource(..), GitConfig(..), extractGitConfig,@@ -53,7 +53,7 @@ -- | A configurable value, that may not be fully determined yet because -- the global git config has not yet been loaded.-data Configurable a+data GlobalConfigurable a = HasGitConfig a -- ^ The git config has a value. | HasGlobalConfig a@@ -84,17 +84,17 @@ , annexDelayAdd :: Maybe Int , annexHttpHeaders :: [String] , annexHttpHeadersCommand :: Maybe String- , annexAutoCommit :: Configurable Bool- , annexResolveMerge :: Configurable Bool- , annexSyncContent :: Configurable Bool- , annexSyncOnlyAnnex :: Configurable Bool+ , annexAutoCommit :: GlobalConfigurable Bool+ , annexResolveMerge :: GlobalConfigurable Bool+ , annexSyncContent :: GlobalConfigurable Bool+ , annexSyncOnlyAnnex :: GlobalConfigurable Bool , annexDebug :: Bool , annexWebOptions :: [String] , annexYoutubeDlOptions :: [String] , annexAriaTorrentOptions :: [String] , annexCrippledFileSystem :: Bool- , annexLargeFiles :: Configurable (Maybe String)- , annexDotFiles :: Configurable Bool+ , annexLargeFiles :: GlobalConfigurable (Maybe String)+ , annexDotFiles :: GlobalConfigurable Bool , annexGitAddToAnnex :: Bool , annexAddSmallFiles :: Bool , annexFsckNudge :: Bool@@ -111,7 +111,7 @@ , annexVerify :: Bool , annexPidLock :: Bool , annexPidLockTimeout :: Seconds- , annexAddUnlocked :: Configurable (Maybe String)+ , annexAddUnlocked :: GlobalConfigurable (Maybe String) , annexSecureHashesOnly :: Bool , annexRetry :: Maybe Integer , annexForwardRetry :: Maybe Integer
Types/Import.hs view
@@ -5,10 +5,14 @@ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE DeriveGeneric #-}+ module Types.Import where import qualified Data.ByteString as S import Data.Char+import Control.DeepSeq+import GHC.Generics import Types.Export import Utility.QuickCheck@@ -29,8 +33,10 @@ - the repository. It should be reasonably short since it is stored in the - git-annex branch. -} newtype ContentIdentifier = ContentIdentifier S.ByteString- deriving (Eq, Ord, Show)+ deriving (Eq, Ord, Show, Generic) +instance NFData ContentIdentifier+ instance Arbitrary ContentIdentifier where -- Avoid non-ascii ContentIdentifiers because fully arbitrary -- strings may not be encoded using the filesystem@@ -47,4 +53,6 @@ -- files that are stored in them. This is equivilant to a git -- commit history. }- deriving (Show)+ deriving (Show, Generic)++instance NFData info => NFData (ImportableContents info)
Upgrade/V7.hs view
@@ -17,6 +17,7 @@ import qualified Git.LsFiles as LsFiles import qualified Git import Git.FilePath+import Config upgrade :: Bool -> Annex Bool upgrade automatic = do@@ -96,7 +97,7 @@ -- annex object. That work is only done once, and then the object will -- finally get its inode cached. populateKeysDb :: Annex ()-populateKeysDb = do+populateKeysDb = unlessM isBareRepo $ do top <- fromRepo Git.repoPath (l, cleanup) <- inRepo $ LsFiles.inodeCaches [top] forM_ l $ \case
Utility/Matcher.hs view
@@ -10,12 +10,12 @@ - Is forgiving about misplaced closing parens, so "foo and (bar or baz" - will be handled, as will "foo and ( bar or baz ) )" -- - Copyright 2011-2013 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -} -{-# LANGUAGE Rank2Types, KindSignatures #-}+{-# LANGUAGE Rank2Types, KindSignatures, DeriveFoldable #-} module Utility.Matcher ( Token(..),@@ -27,6 +27,7 @@ matchMrun, isEmpty, combineMatchers,+ introspect, prop_matcher_sane ) where@@ -43,7 +44,7 @@ | MOr (Matcher op) (Matcher op) | MNot (Matcher op) | MOp op- deriving (Show, Eq)+ deriving (Show, Eq, Foldable) {- Converts a word of syntax into a token. Doesn't handle operations. -} syntaxToken :: String -> Either String (Token op)@@ -146,6 +147,10 @@ | isEmpty a = b | isEmpty b = a | otherwise = a `MOr` b++{- Checks if anything in the matcher meets the condition. -}+introspect :: (a -> Bool) -> Matcher a -> Bool+introspect = any prop_matcher_sane :: Bool prop_matcher_sane = all (\m -> match dummy m ()) $ map generate
Utility/Metered.hs view
@@ -11,6 +11,7 @@ MeterUpdate, nullMeterUpdate, combineMeterUpdate,+ TotalSize(..), BytesProcessed(..), toBytesProcessed, fromBytesProcessed,@@ -29,6 +30,8 @@ ProgressParser, commandMeter, commandMeter',+ commandMeterExitCode,+ commandMeterExitCode', demeterCommand, demeterCommandEnv, avoidProgress,@@ -228,31 +231,44 @@ } {- Parses the String looking for a command's progress output, and returns- - Maybe the number of bytes done so far, and any any remainder of the- - string that could be an incomplete progress output. That remainder- - should be prepended to future output, and fed back in. This interface- - allows the command's output to be read in any desired size chunk, or- - even one character at a time.+ - Maybe the number of bytes done so far, optionally a total size, + - and any any remainder of the string that could be an incomplete+ - progress output. That remainder should be prepended to future output,+ - and fed back in. This interface allows the command's output to be read+ - in any desired size chunk, or even one character at a time. -}-type ProgressParser = String -> (Maybe BytesProcessed, String)+type ProgressParser = String -> (Maybe BytesProcessed, Maybe TotalSize, String) +newtype TotalSize = TotalSize Integer+ {- Runs a command and runs a ProgressParser on its output, in order - to update a meter.+ -+ - If the Meter is provided, the ProgressParser can report the total size,+ - which allows creating a Meter before the size is known. -}-commandMeter :: ProgressParser -> OutputHandler -> MeterUpdate -> FilePath -> [CommandParam] -> IO Bool-commandMeter progressparser oh meterupdate cmd params = do- ret <- commandMeter' progressparser oh meterupdate cmd params+commandMeter :: ProgressParser -> OutputHandler -> Maybe Meter -> MeterUpdate -> FilePath -> [CommandParam] -> IO Bool+commandMeter progressparser oh meter meterupdate cmd params =+ commandMeter' progressparser oh meter meterupdate cmd params id++commandMeter' :: ProgressParser -> OutputHandler -> Maybe Meter -> MeterUpdate -> FilePath -> [CommandParam] -> (CreateProcess -> CreateProcess) -> IO Bool+commandMeter' progressparser oh meter meterupdate cmd params mkprocess = do+ ret <- commandMeterExitCode' progressparser oh meter meterupdate cmd params mkprocess return $ case ret of Just ExitSuccess -> True _ -> False -commandMeter' :: ProgressParser -> OutputHandler -> MeterUpdate -> FilePath -> [CommandParam] -> IO (Maybe ExitCode)-commandMeter' progressparser oh meterupdate cmd params = - outputFilter cmd params Nothing- (feedprogress zeroBytesProcessed [])+commandMeterExitCode :: ProgressParser -> OutputHandler -> Maybe Meter -> MeterUpdate -> FilePath -> [CommandParam] -> IO (Maybe ExitCode)+commandMeterExitCode progressparser oh meter meterupdate cmd params =+ commandMeterExitCode' progressparser oh meter meterupdate cmd params id++commandMeterExitCode' :: ProgressParser -> OutputHandler -> Maybe Meter -> MeterUpdate -> FilePath -> [CommandParam] -> (CreateProcess -> CreateProcess) -> IO (Maybe ExitCode)+commandMeterExitCode' progressparser oh mmeter meterupdate cmd params mkprocess = + outputFilter cmd params mkprocess Nothing+ (feedprogress mmeter zeroBytesProcessed []) handlestderr where- feedprogress prev buf h = do+ feedprogress sendtotalsize prev buf h = do b <- S.hGetSome h 80 if S.null b then return ()@@ -261,13 +277,18 @@ S.hPut stdout b hFlush stdout let s = decodeBS b- let (mbytes, buf') = progressparser (buf++s)+ let (mbytes, mtotalsize, buf') = progressparser (buf++s)+ sendtotalsize' <- case (sendtotalsize, mtotalsize) of+ (Just meter, Just (TotalSize n)) -> do+ setMeterTotalSize meter n+ return Nothing+ _ -> return sendtotalsize case mbytes of- Nothing -> feedprogress prev buf' h+ Nothing -> feedprogress sendtotalsize' prev buf' h (Just bytes) -> do when (bytes /= prev) $ meterupdate bytes- feedprogress bytes buf' h+ feedprogress sendtotalsize' bytes buf' h handlestderr h = unlessM (hIsEOF h) $ do stderrHandler oh =<< hGetLine h@@ -283,7 +304,7 @@ demeterCommandEnv :: OutputHandler -> FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Bool demeterCommandEnv oh cmd params environ = do- ret <- outputFilter cmd params environ+ ret <- outputFilter cmd params id environ (\outh -> avoidProgress True outh stdouthandler) (\errh -> avoidProgress True errh $ stderrHandler oh) return $ case ret of@@ -308,11 +329,12 @@ outputFilter :: FilePath -> [CommandParam]+ -> (CreateProcess -> CreateProcess) -> Maybe [(String, String)] -> (Handle -> IO ()) -> (Handle -> IO ()) -> IO (Maybe ExitCode)-outputFilter cmd params environ outfilter errfilter = +outputFilter cmd params mkprocess environ outfilter errfilter = catchMaybeIO $ withCreateProcess p go where go _ (Just outh) (Just errh) pid = do@@ -338,7 +360,7 @@ return ret go _ _ _ _ = error "internal" - p = (proc cmd (toCommand params))+ p = mkprocess (proc cmd (toCommand params)) { env = environ , std_out = CreatePipe , std_err = CreatePipe
Utility/Path.hs view
@@ -19,7 +19,9 @@ relPathDirToFile, relPathDirToFileAbs, segmentPaths,+ segmentPaths', runSegmentPaths,+ runSegmentPaths', relHome, inPath, searchPath,@@ -215,22 +217,33 @@ - that many paths in doesn't care too much about order of the later ones. -} segmentPaths :: (a -> RawFilePath) -> [RawFilePath] -> [a] -> [[a]]-segmentPaths _ [] new = [new]-segmentPaths _ [_] new = [new] -- optimisation-segmentPaths c (l:ls) new = found : segmentPaths c ls rest+segmentPaths = segmentPaths' (\_ r -> r)++segmentPaths' :: (Maybe RawFilePath -> a -> r) -> (a -> RawFilePath) -> [RawFilePath] -> [a] -> [[r]]+segmentPaths' f _ [] new = [map (f Nothing) new]+segmentPaths' f _ [i] new = [map (f (Just i)) new] -- optimisation+segmentPaths' f c (i:is) new = + map (f (Just i)) found : segmentPaths' f c is rest where- (found, rest) = if length ls < 100- then partition inl new- else break (not . inl) new- inl f = l' `dirContains` fromRawFilePath (c f)- l' = fromRawFilePath l+ (found, rest) = if length is < 100+ then partition ini new+ else break (not . ini) new+ ini p = i' `dirContains` fromRawFilePath (c p)+ i' = fromRawFilePath i {- This assumes that it's cheaper to call segmentPaths on the result, - than it would be to run the action separately with each path. In - the case of git file list commands, that assumption tends to hold. -}-runSegmentPaths :: (a -> RawFilePath) -> ([RawFilePath] -> IO [a]) -> [RawFilePath] -> IO [[a]]-runSegmentPaths c a paths = segmentPaths c paths <$> a paths+runSegmentPaths :: (a -> RawFilePath) -> ([RawFilePath] -> IO ([a], v)) -> [RawFilePath] -> IO ([[a]], v)+runSegmentPaths c a paths = do+ (l, cleanup) <- a paths+ return (segmentPaths c paths l, cleanup)++runSegmentPaths' :: (Maybe RawFilePath -> a -> r) -> (a -> RawFilePath) -> ([RawFilePath] -> IO ([a], v)) -> [RawFilePath] -> IO ([[r]], v)+runSegmentPaths' si c a paths = do+ (l, cleanup) <- a paths+ return (segmentPaths' si c paths l, cleanup) {- Converts paths in the home directory to use ~/ -} relHome :: FilePath -> IO String
Utility/Process.hs view
@@ -173,8 +173,9 @@ -- | Wrapper around 'System.Process.createProcess' that does debug logging. createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) createProcess p = do- debugProcess p- Utility.Process.Shim.createProcess p+ r@(_, _, _, h) <- Utility.Process.Shim.createProcess p+ debugProcess p h+ return r -- | Wrapper around 'System.Process.withCreateProcess' that does debug logging. withCreateProcess :: CreateProcess -> (Maybe Handle -> Maybe Handle -> Maybe Handle -> ProcessHandle -> IO a) -> IO a@@ -182,11 +183,14 @@ (\(m_in, m_out, m_err, ph) -> action m_in m_out m_err ph) -- | Debugging trace for a CreateProcess.-debugProcess :: CreateProcess -> IO ()-debugProcess p = debugM "Utility.Process" $ unwords- [ action ++ ":"- , showCmd p- ]+debugProcess :: CreateProcess -> ProcessHandle -> IO ()+debugProcess p h = do+ pid <- getPid h+ debugM "Utility.Process" $ unwords+ [ describePid pid+ , action ++ ":"+ , showCmd p+ ] where action | piped (std_in p) && piped (std_out p) = "chat"@@ -196,11 +200,17 @@ piped Inherit = False piped _ = True +describePid :: Maybe Utility.Process.Shim.Pid -> String+describePid Nothing = "process"+describePid (Just p) = "process [" ++ show p ++ "]"+ -- | Wrapper around 'System.Process.waitForProcess' that does debug logging. waitForProcess :: ProcessHandle -> IO ExitCode waitForProcess h = do+ -- Have to get pid before waiting, which closes the ProcessHandle.+ pid <- getPid h r <- Utility.Process.Shim.waitForProcess h- debugM "Utility.Process" ("process done " ++ show r)+ debugM "Utility.Process" (describePid pid ++ " done " ++ show r) return r cleanupProcess :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO ()
Utility/Rsync.hs view
@@ -114,7 +114,7 @@ -} rsyncProgress :: OutputHandler -> MeterUpdate -> [CommandParam] -> IO Bool rsyncProgress oh meter ps =- commandMeter' parseRsyncProgress oh meter "rsync" (rsyncParamsFixup ps) >>= \case+ commandMeterExitCode parseRsyncProgress oh Nothing meter "rsync" (rsyncParamsFixup ps) >>= \case Just ExitSuccess -> return True Just (ExitFailure exitcode) -> do when (exitcode /= 1) $@@ -136,10 +136,10 @@ parseRsyncProgress :: ProgressParser parseRsyncProgress = go [] . reverse . progresschunks where- go remainder [] = (Nothing, remainder)+ go remainder [] = (Nothing, Nothing, remainder) go remainder (x:xs) = case parsebytes (findbytesstart x) of Nothing -> go (delim:x++remainder) xs- Just b -> (Just (toBytesProcessed b), remainder)+ Just b -> (Just (toBytesProcessed b), Nothing, remainder) delim = '\r'
Utility/Url.hs view
@@ -41,6 +41,7 @@ GetBasicAuth, noBasicAuth, applyBasicAuth',+ extractFromResourceT, ) where import Common@@ -60,8 +61,10 @@ import qualified Data.ByteString.UTF8 as B8 import qualified Data.ByteString.Lazy as L import qualified Data.Set as S-import Control.Exception (throwIO)+import Control.Exception (throwIO, evaluate) import Control.Monad.Trans.Resource+import Control.Monad.IO.Class (MonadIO)+import Control.DeepSeq import Network.HTTP.Conduit import Network.HTTP.Client import Network.HTTP.Simple (getResponseHeader)@@ -269,12 +272,10 @@ debugM "url" (show req') join $ runResourceT $ do resp <- http req' (httpManager uo)- -- forces processing the response while- -- within the runResourceT- liftIO $ if responseStatus resp == ok200+ if responseStatus resp == ok200 then do- let !len = extractlen resp- let !fn = extractfilename resp+ len <- extractFromResourceT (extractlen resp)+ fn <- extractFromResourceT (extractfilename resp) return $ found len fn else if responseStatus resp == unauthorized401 then return $ getBasicAuth uo' (show (getUri req)) >>= \case@@ -463,11 +464,13 @@ then do store zeroBytesProcessed WriteMode resp return (return ())- else if responseStatus resp == unauthorized401- then return $ getBasicAuth uo (show (getUri req')) >>= \case- Nothing -> respfailure resp- Just ba -> retryauthed ba- else return $ respfailure resp+ else do+ rf <- extractFromResourceT (respfailure resp)+ if responseStatus resp == unauthorized401+ then return $ getBasicAuth uo (show (getUri req')) >>= \case+ Nothing -> giveup rf+ Just ba -> retryauthed ba+ else return $ giveup rf where req' = applyRequest uo $ req -- Override http-client's default decompression of gzip@@ -498,11 +501,13 @@ then do store zeroBytesProcessed WriteMode resp return (return ())- else if responseStatus resp == unauthorized401- then return $ getBasicAuth uo (show (getUri req'')) >>= \case- Nothing -> respfailure resp- Just ba -> retryauthed ba- else return $ respfailure resp+ else do+ rf <- extractFromResourceT (respfailure resp)+ if responseStatus resp == unauthorized401+ then return $ getBasicAuth uo (show (getUri req'')) >>= \case+ Nothing -> giveup rf+ Just ba -> retryauthed ba+ else return $ giveup rf alreadydownloaded sz s h = s == requestedRangeNotSatisfiable416 && case lookup hContentRange h of@@ -521,7 +526,7 @@ store initialp mode resp = sinkResponseFile meterupdate initialp file mode resp - respfailure = giveup . B8.toString . statusMessage . responseStatus+ respfailure = B8.toString . statusMessage . responseStatus retryauthed (ba, signalsuccess) = do r <- tryNonAsync $ downloadConduit@@ -710,3 +715,11 @@ applyBasicAuth' ba = applyBasicAuth (encodeBS (basicAuthUser ba)) (encodeBS (basicAuthPassword ba))++{- Make sure whatever is returned is fully evaluated. Avoids any possible+ - issues with laziness deferring processing until a time when the resource+ - has been freed. -}+extractFromResourceT :: (MonadIO m, NFData a) => a -> ResourceT m a+extractFromResourceT v = do+ liftIO $ evaluate (rnf v)+ return v
doc/git-annex-add.mdwn view
@@ -38,7 +38,7 @@ # OPTIONS -* `--force`+* `--no-check-gitignore` Add gitignored files.
doc/git-annex-addurl.mdwn view
@@ -85,6 +85,12 @@ Use to adjust the filenames that are created by addurl. For example, `--suffix=.mp3` can be used to add an extension to the file. +* `--no-check-gitignore`++ By default, gitignores are honored and it will refuse to download an+ url to a file that would be ignored. This makes such files be added+ despite any ignores.+ * `--jobs=N` `-JN` Enables parallel downloads when multiple urls are being added.
doc/git-annex-import.mdwn view
@@ -31,7 +31,7 @@ You can only import from special remotes that were configured with `importtree=yes` when set up with [[git-annex-initremote]](1). Only some kinds of special remotes will let you configure them this way. A perhaps-non-exhastive list is the directory, s3, and adb special remotes.+non-exhaustive list is the directory, s3, and adb special remotes. To import from a special remote, you must specify the name of a branch. A corresponding remote tracking branch will be updated by `git annex import`.@@ -72,6 +72,9 @@ git config remote.myremote.annex-tracking-branch master git annex sync --content +Any files that are gitignored will not be included in the import,+but will be left on the remote.+ When the special remote has a preferred content expression set by [[git-annex-wanted]](1), it will be honored when importing from it. Files that are not preferred content of the remote will not be@@ -91,33 +94,14 @@ * `--content`, `--no-content` - Controls whether content is downloaded from the special remote.+ Controls whether annexed content is downloaded from the special remote.+ The default is to download content into the git-annex repository. With --no-content, git-annex keys are generated from information provided by the special remote, without downloading it. Commands like `git-annex get` can later be used to download files, as desired.-- The --no-content option is not supported by all special remotes,- and the kind of git-annex key that is generated is left up to- each special remote. So while the directory special remote hashes- the file and generates the same key it usually would, other- special remotes may use unusual keys like SHA1, or WORM, depending- on the limitations of the special remote. -- The annex.securehashesonly configuration, if set, will prevent- --no-content importing from a special remote that uses insecure keys.-- Using --no-content prevents annex.largefiles from being checked,- because the files are not downloaded. So, when using --no-content,- files that would usually be considered non-large will be added to the- annex, rather than adding them directly to the git repository.-- Note that a different git tree will often be generated when using- --no-content than would be generated when using --content, because- the options cause different kinds of keys to be used when importing- new/changed files. So mixing uses of --content and --no-content can- lead to merge conflicts in some situations.+ The --no-content option is not supported by all special remotes. # IMPORTING FROM A DIRECTORY @@ -200,6 +184,10 @@ For example: `-J4` Setting this to "cpus" will run one job per CPU core.++* `--no-check-gitignore`++ Add gitignored files. * `--json`
doc/git-annex-importfeed.mdwn view
@@ -89,6 +89,12 @@ (These use the UTC time zone, not the local time zone.) +* `--no-check-gitignore`++ By default, gitignores are honored and it will refuse to download an+ url to a file that would be ignored. This makes such files be added+ despite any ignores.+ # SEE ALSO [[git-annex]](1)
doc/git-annex-sync.mdwn view
@@ -108,12 +108,14 @@ This behavior can be overridden by configuring the preferred content of a repository. See [[git-annex-preferred-content]](1). - When `remote.<name>.annex-tracking-branch` is configured for a special remote- and that branch is checked out, syncing content will import changes from- the remote, merge them into the branch, and export any changes that have- been committed to the branch back to the remote. See - See [[git-annex-import]](1) and [[git-annex-export]](1) for details about- how importing and exporting work.+ When `remote.<name>.annex-tracking-branch` is configured for a special+ remote and that branch is checked out, syncing with --content will+ import changes from the remote, merge them into the branch, and export+ any changes that have been committed to the branch back to the remote.+ With --no-content, imports will only be made from special remotes that+ support importing without transferting files, and no exports will be done.+ See [[git-annex-import]](1) and [[git-annex-export]](1) for details+ about how importing and exporting work. * `--content-of=path` `-C path`
doc/git-annex.mdwn view
@@ -1014,6 +1014,8 @@ Setting this to "cpus" will run one job per CPU core. + When the `--batch` option is used, this configuration is ignored.+ * `annex.queuesize` git-annex builds a queue of git commands, in order to combine similar
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 8.20200908+Version: 8.20201007 Cabal-Version: >= 1.10 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -270,7 +270,7 @@ Flag Benchmark Description: Enable benchmarking- Default: False+ Default: True Flag DebugLocks Description: Debug location of MVar/STM deadlocks
stack.yaml view
@@ -8,7 +8,7 @@ magicmime: false dbus: false debuglocks: false- benchmark: false+ benchmark: true networkbsd: true gitlfs: true httpclientrestricted: true@@ -28,4 +28,4 @@ - network-3.1.0.1 explicit-setup-deps: git-annex: true-resolver: lts-16.10+resolver: lts-16.16