git-annex 10.20221103 → 10.20221212
raw patch · 24 files changed
+300/−126 lines, 24 filesdep ~Win32
Dependency ranges changed: Win32
Files
- Annex/Init.hs +7/−10
- Annex/Locations.hs +5/−0
- Annex/YoutubeDl.hs +11/−5
- Assistant/MakeRepo.hs +1/−1
- CHANGELOG +25/−0
- Command/ConfigList.hs +1/−1
- Command/Export.hs +18/−17
- Command/Init.hs +1/−1
- Command/Reinit.hs +1/−1
- Command/Upgrade.hs +1/−1
- Database/Keys.hs +19/−7
- Database/Keys/SQL.hs +12/−3
- Logs/File.hs +17/−4
- Logs/Restage.hs +43/−11
- Remote/S3.hs +74/−46
- Test.hs +14/−2
- Test/Framework.hs +27/−8
- Types/Test.hs +1/−0
- Utility/DataUnits.hs +3/−1
- Utility/LockFile/Windows.hs +1/−1
- doc/git-annex-sync.mdwn +2/−1
- doc/git-annex-test.mdwn +10/−0
- doc/git-annex.mdwn +4/−3
- git-annex.cabal +2/−2
Annex/Init.hs view
@@ -37,7 +37,6 @@ import Annex.Version import Annex.Difference import Annex.UUID-import Annex.WorkTree import Annex.Fixup import Annex.Path import Config@@ -102,8 +101,8 @@ Right username -> [username, at, hostname, ":", reldir] Left _ -> [hostname, ":", reldir] -initialize :: Bool -> Maybe String -> Maybe RepoVersion -> Annex ()-initialize autoinit mdescription mversion = checkInitializeAllowed $ \initallowed -> do+initialize :: Maybe String -> Maybe RepoVersion -> Annex ()+initialize mdescription mversion = checkInitializeAllowed $ \initallowed -> do {- Has to come before any commits are made as the shared - clone heuristic expects no local objects. -} sharedclone <- checkSharedClone@@ -113,7 +112,7 @@ ensureCommit $ Annex.Branch.create prepUUID- initialize' autoinit mversion initallowed+ initialize' mversion initallowed initSharedClone sharedclone @@ -125,8 +124,8 @@ -- Everything except for uuid setup, shared clone setup, and initial -- description.-initialize' :: Bool -> Maybe RepoVersion -> InitializeAllowed -> Annex ()-initialize' autoinit mversion _initallowed = do+initialize' :: Maybe RepoVersion -> InitializeAllowed -> Annex ()+initialize' mversion _initallowed = do checkLockSupport checkFifoSupport checkCrippledFileSystem@@ -143,8 +142,6 @@ unlessM isBareRepo $ do hookWrite postCheckoutHook hookWrite postMergeHook- unless autoinit $- scanAnnexedFiles AdjustedBranch.checkAdjustedClone >>= \case AdjustedBranch.InAdjustedClone -> return ()@@ -206,7 +203,7 @@ where needsinit = ifM autoInitializeAllowed ( do- tryNonAsync (initialize True Nothing Nothing) >>= \case+ tryNonAsync (initialize Nothing Nothing) >>= \case Right () -> noop Left e -> giveup $ show e ++ "\n" ++ "git-annex: automatic initialization failed due to above problems"@@ -259,7 +256,7 @@ where needsinit = whenM (initializeAllowed <&&> autoInitializeAllowed) $ do- initialize True Nothing Nothing+ initialize Nothing Nothing autoEnableSpecialRemotes remotelist {- Checks if a repository is initialized. Does not check version for ugrade. -}
Annex/Locations.hs view
@@ -50,6 +50,7 @@ gitAnnexSmudgeLog, gitAnnexSmudgeLock, gitAnnexRestageLog,+ gitAnnexRestageLogOld, gitAnnexRestageLock, gitAnnexMoveLog, gitAnnexMoveLock,@@ -384,6 +385,10 @@ - restaged in git -} gitAnnexRestageLog :: Git.Repo -> RawFilePath gitAnnexRestageLog r = gitAnnexDir r P.</> "restage.log"++{- .git/annex/restage.old is used while restaging files in git -}+gitAnnexRestageLogOld :: Git.Repo -> RawFilePath+gitAnnexRestageLogOld r = gitAnnexDir r P.</> "restage.old" gitAnnexRestageLock :: Git.Repo -> RawFilePath gitAnnexRestageLock r = gitAnnexDir r P.</> "restage.lck"
Annex/YoutubeDl.hs view
@@ -249,8 +249,9 @@ return (opts ++ addopts) youtubeDlCommand :: Annex String-youtubeDlCommand = fromMaybe "youtube-dl" . annexYoutubeDlCommand - <$> Annex.getGitConfig+youtubeDlCommand = annexYoutubeDlCommand <$> Annex.getGitConfig >>= \case+ Just c -> pure c+ Nothing -> fromMaybe "yt-dlp" <$> liftIO (searchPath "youtube-dl") supportedScheme :: UrlOptions -> URLString -> Bool supportedScheme uo url = case parseURIRelaxed url of@@ -264,8 +265,10 @@ _ -> allowedScheme uo u {- Strategy: Look for chunks prefixed with \r, which look approximately- - like this:+ - like this for youtube-dl: - "ESC[K[download] 26.6% of 60.22MiB at 254.69MiB/s ETA 00:00"+ - or for yt-dlp, like this:+ - "\r[download] 1.8% of 1.14GiB at 1.04MiB/s ETA 18:23" - Look at the number before "% of " and the number and unit after, - to determine the number of bytes. -}@@ -291,8 +294,11 @@ calc percent total = round (percent * fromIntegral total / 100) parsepercent :: String -> Maybe Double- parsepercent = readMaybe . reverse . takeWhile (not . isSpace) . reverse+ parsepercent = readMaybe + . reverse . takeWhile (not . isSpace) . reverse+ . dropWhile isSpace - parsebytes = readSize units . takeWhile (not . isSpace)+ parsebytes = readSize units . takeWhile (not . isSpace) + . dropWhile isSpace units = committeeUnits ++ storageUnits
Assistant/MakeRepo.hs view
@@ -85,7 +85,7 @@ initRepo' :: Maybe String -> Maybe StandardGroup -> Annex () initRepo' desc mgroup = unlessM isInitialized $ do- initialize False desc Nothing+ initialize desc Nothing u <- getUUID maybe noop (defaultStandardGroup u) mgroup {- Ensure branch gets committed right away so it is
CHANGELOG view
@@ -1,3 +1,28 @@+git-annex (10.20221212) upstream; urgency=medium++ * Fix a hang that occasionally occurred during commands such as move,+ when operating on unlocked files. (A bug introduced in 10.20220927)+ * When youtube-dl is not available in PATH, use yt-dlp instead.+ * Support parsing yt-dpl output to display download progress.+ * init: Avoid scanning for annexed files, which can be lengthy in a+ large repository. Instead that scan is done on demand.+ * Sped up the initial scan for annexed files by 21%.+ * test: Add --test-debug option.+ * Support quettabyte and yottabyte.++ -- Joey Hess <id@joeyh.name> Mon, 12 Dec 2022 12:20:06 -0400++git-annex (10.20221104) upstream; urgency=medium++ * export: Fix a bug that left a file on a special remote when+ two files with the same content were both deleted in the exported tree.+ * S3: Support signature=anonymous to access a S3 bucket anonymously.+ This can be used, for example, with importtree=yes to import from+ a public bucket.+ This feature needs git-annex to be built with aws-0.23.++ -- Joey Hess <id@joeyh.name> Thu, 03 Nov 2022 14:07:31 -0400+ git-annex (10.20221103) upstream; urgency=medium * Doubled the speed of git-annex drop when operating on many files,
Command/ConfigList.hs view
@@ -47,7 +47,7 @@ else ifM (Annex.Branch.hasSibling <||> (isJust <$> Fields.getField Fields.autoInit)) ( do liftIO checkNotReadOnly- initialize True Nothing Nothing+ initialize Nothing Nothing getUUID , return NoUUID )
Command/Export.hs view
@@ -149,23 +149,24 @@ [] -> updateExportTree db emptyTree new [oldtreesha] -> do diffmap <- mkDiffMap oldtreesha new db- let seekdiffmap a = commandActions $ - map a (M.toList diffmap)+ let seekdiffmap a = mapM_ a (M.toList diffmap) -- Rename old files to temp, or delete.- seekdiffmap $ \(ek, (moldf, mnewf)) -> do- case (moldf, mnewf) of- (Just oldf, Just _newf) ->+ let deleteoldf = \ek oldf -> commandAction $+ startUnexport' r db oldf ek+ seekdiffmap $ \case+ (ek, (oldf:oldfs, _newf:_)) -> do+ commandAction $ startMoveToTempName r db oldf ek- (Just oldf, Nothing) ->- startUnexport' r db oldf ek- _ -> stop+ forM_ oldfs (deleteoldf ek)+ (ek, (oldfs, [])) ->+ forM_ oldfs (deleteoldf ek)+ (_ek, ([], _)) -> noop waitForAllRunningCommandActions -- Rename from temp to new files.- seekdiffmap $ \(ek, (moldf, mnewf)) ->- case (moldf, mnewf) of- (Just _oldf, Just newf) ->- startMoveFromTempName r db ek newf- _ -> stop+ seekdiffmap $ \case+ (ek, (_oldf:_, newf:_)) -> commandAction $+ startMoveFromTempName r db ek newf+ _ -> noop waitForAllRunningCommandActions ts -> do warning "Resolving export conflict.."@@ -204,7 +205,7 @@ void $ liftIO cleanup -- Map of old and new filenames for each changed Key in a diff.-type DiffMap = M.Map Key (Maybe TopFilePath, Maybe TopFilePath)+type DiffMap = M.Map Key ([TopFilePath], [TopFilePath]) mkDiffMap :: Git.Ref -> Git.Ref -> ExportHandle -> Annex DiffMap mkDiffMap old new db = do@@ -213,14 +214,14 @@ void $ liftIO cleanup return diffmap where- combinedm (srca, dsta) (srcb, dstb) = (srca <|> srcb, dsta <|> dstb)+ combinedm (srca, dsta) (srcb, dstb) = (srca ++ srcb, dsta ++ dstb) mkdm i = do srcek <- getek (Git.DiffTree.srcsha i) dstek <- getek (Git.DiffTree.dstsha i) updateExportTree' db srcek dstek i return $ catMaybes- [ (, (Just (Git.DiffTree.file i), Nothing)) <$> srcek- , (, (Nothing, Just (Git.DiffTree.file i))) <$> dstek+ [ (, ([Git.DiffTree.file i], [])) <$> srcek+ , (, ([], [Git.DiffTree.file i])) <$> dstek ] getek sha | sha `elem` nullShas = return Nothing
Command/Init.hs view
@@ -75,7 +75,7 @@ Just v | v /= wantversion -> giveup $ "This repository is already a initialized with version " ++ show (fromRepoVersion v) ++ ", not changing to requested version." _ -> noop- initialize False+ initialize (if null (initDesc os) then Nothing else Just (initDesc os)) (initVersion os) unless (noAutoEnable os)
Command/Reinit.hs view
@@ -35,6 +35,6 @@ then return $ toUUID s else Remote.nameToUUID s storeUUID u- checkInitializeAllowed $ initialize' False Nothing+ checkInitializeAllowed $ initialize' Nothing Annex.SpecialRemote.autoEnable next $ return True
Command/Upgrade.hs view
@@ -45,6 +45,6 @@ start _ = starting "upgrade" (ActionItemOther Nothing) (SeekInput []) $ do whenM (isNothing <$> getVersion) $ do- initialize False Nothing Nothing+ initialize Nothing Nothing r <- upgrade False latestVersion next $ return r
Database/Keys.hs view
@@ -132,10 +132,10 @@ let db = dbdir P.</> "db" dbexists <- liftIO $ R.doesPathExist db case dbexists of- True -> open db+ True -> open db False False -> do initDb db SQL.createTables- open db+ open db True where -- If permissions don't allow opening the database, and it's being -- opened for read, treat it as if it does not exist.@@ -143,9 +143,9 @@ | forwrite = throwM e | otherwise = return DbUnavailable - open db = do+ open db dbisnew = do qh <- liftIO $ H.openDbQueue db SQL.containedTable- tc <- reconcileStaged qh+ tc <- reconcileStaged dbisnew qh return $ DbOpen (qh, tc) {- Closes the database if it was open. Any writes will be flushed to it.@@ -260,8 +260,8 @@ - So when using getAssociatedFiles, have to make sure the file still - is an associated file. -}-reconcileStaged :: H.DbQueue -> Annex DbTablesChanged-reconcileStaged qh = ifM (Git.Config.isBare <$> gitRepo)+reconcileStaged :: Bool -> H.DbQueue -> Annex DbTablesChanged+reconcileStaged dbisnew qh = ifM (Git.Config.isBare <$> gitRepo) ( return mempty , do gitindex <- inRepo currentIndexFile@@ -384,7 +384,7 @@ Nothing -> return False send mdfeeder (Ref dstsha) $ \case Just key -> do- liftIO $ SQL.addAssociatedFile key+ liftIO $ addassociatedfile key (asTopFilePath file) (SQL.WriteHandle qh) when (dstmode /= fmtTreeItemType TreeSymlink) $@@ -496,6 +496,18 @@ -- takes less than half a second, so that seems about right. largediff :: Int largediff = 1000++ -- When the database is known to have been newly created and empty+ -- before reconcileStaged started, it is more efficient to use + -- newAssociatedFile. It's safe to use it here because this is run+ -- with a lock held that blocks any other process that opens the+ -- database, and when the database is newly created, there is no+ -- existing process that has it open already. And it's not possible+ -- for reconcileStaged to call this twice on the same filename with+ -- two different keys.+ addassociatedfile+ | dbisnew = SQL.newAssociatedFile+ | otherwise = SQL.addAssociatedFile {- Normally the keys database is updated incrementally when opened, - by reconcileStaged. Calling this explicitly allows running the
Database/Keys/SQL.hs view
@@ -1,6 +1,6 @@ {- Sqlite database of information about Keys -- - Copyright 2015-2021 Joey Hess <id@joeyh.name>+ - Copyright 2015-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -73,8 +73,8 @@ queueDb :: SqlPersistM () -> WriteHandle -> IO () queueDb a (WriteHandle h) = H.queueDb h checkcommit a where- -- commit queue after 1000 changes- checkcommit sz _lastcommittime = pure (sz > 1000)+ -- commit queue after 10000 changes+ checkcommit sz _lastcommittime = pure (sz > 10000) -- Insert the associated file. -- When the file was associated with a different key before,@@ -85,6 +85,15 @@ (FileKeyIndex af k) (Associated k af) [AssociatedFile =. af, AssociatedKey =. k]+ where+ af = SFilePath (getTopFilePath f)++-- Faster than addAssociatedFile, but only safe to use when the file+-- was not associated with a different key before, as it does not delete+-- any old key.+newAssociatedFile :: Key -> TopFilePath -> WriteHandle -> IO ()+newAssociatedFile k f = queueDb $+ void $ insert $ Associated k af where af = SFilePath (getTopFilePath f)
Logs/File.hs view
@@ -13,8 +13,10 @@ appendLogFile, modifyLogFile, streamLogFile,+ streamLogFileUnsafe, checkLogFile, calcLogFile,+ calcLogFileUnsafe, ) where import Annex.Common@@ -98,7 +100,12 @@ -- | Folds a function over lines of a log file to calculate a value. calcLogFile :: RawFilePath -> RawFilePath -> t -> (L.ByteString -> t -> t) -> Annex t-calcLogFile f lck start update = withSharedLock lck $ bracket setup cleanup go+calcLogFile f lck start update =+ withSharedLock lck $ calcLogFileUnsafe f start update++-- | Unsafe version that does not do locking.+calcLogFileUnsafe :: RawFilePath -> t -> (L.ByteString -> t -> t) -> Annex t+calcLogFileUnsafe f start update = bracket setup cleanup go where setup = liftIO $ tryWhenExists $ openFile f' ReadMode cleanup Nothing = noop@@ -125,7 +132,15 @@ -- is running. streamLogFile :: FilePath -> RawFilePath -> Annex () -> (String -> Annex ()) -> Annex () streamLogFile f lck finalizer processor = - withExclusiveLock lck $ bracketOnError setup cleanup go+ withExclusiveLock lck $ do+ streamLogFileUnsafe f finalizer processor+ liftIO $ writeFile f ""+ setAnnexFilePerm (toRawFilePath f)++-- Unsafe version that does not do locking, and does not empty the file+-- at the end.+streamLogFileUnsafe :: FilePath -> Annex () -> (String -> Annex ()) -> Annex ()+streamLogFileUnsafe f finalizer processor = bracketOnError setup cleanup go where setup = liftIO $ tryWhenExists $ openFile f ReadMode cleanup Nothing = noop@@ -135,8 +150,6 @@ mapM_ processor =<< liftIO (lines <$> hGetContents h) liftIO $ hClose h finalizer- liftIO $ writeFile f ""- setAnnexFilePerm (toRawFilePath f) createDirWhenNeeded :: RawFilePath -> Annex () -> Annex () createDirWhenNeeded f a = a `catchNonAsync` \_e -> do
Logs/Restage.hs view
@@ -13,9 +13,11 @@ import Git.FilePath import Logs.File import Utility.InodeCache+import Annex.LockFile import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L+import qualified Utility.RawFilePath as R -- | Log a file whose pointer needs to be restaged in git. -- The content of the file may not be a pointer, if it is populated with@@ -27,31 +29,61 @@ lckf <- fromRepo gitAnnexRestageLock appendLogFile logf lckf $ L.fromStrict $ formatRestageLog f ic --- | Streams the content of the restage log, and then empties the log at--- the end.+-- | Streams the content of the restage log. ----- If the processor or finalizer is interrupted or throws an exception,--- the log file is left unchanged.+-- First, the content of the log file is moved to the restage.old file.+-- If that file already exists, the content is appended, otherwise it's+-- renamed to that. ----- Locking is used to prevent new items being added to the log while this--- is running.+-- The log file is kept locked during that, but the lock is then+-- released. The processor may do something that itself needs to take the+-- lock, so it's important that the lock not be held while running it.+--+-- The content of restage.old file is then streamed to the processor, +-- and then the finalizer is run, ending with emptying restage.old.+--+-- If the processor or finalizer is interrupted or throws an exception,+-- restage.old is left populated to be processed later. streamRestageLog :: Annex () -> (TopFilePath -> InodeCache -> Annex ()) -> Annex () streamRestageLog finalizer processor = do logf <- fromRepo gitAnnexRestageLog+ oldf <- fromRepo gitAnnexRestageLogOld+ let oldf' = fromRawFilePath oldf lckf <- fromRepo gitAnnexRestageLock- streamLogFile (fromRawFilePath logf) lckf finalizer $ \l -> + + withExclusiveLock lckf $ liftIO $+ whenM (R.doesPathExist logf) $+ ifM (R.doesPathExist oldf)+ ( do+ h <- openFile oldf' AppendMode+ hPutStr h =<< readFile (fromRawFilePath logf)+ hClose h+ liftIO $ removeWhenExistsWith R.removeLink logf+ , moveFile logf oldf+ )++ streamLogFileUnsafe oldf' finalizer $ \l -> case parseRestageLog l of Just (f, ic) -> processor f ic Nothing -> noop+ + liftIO $ removeWhenExistsWith R.removeLink oldf +-- | Calculate over both the current restage log, and also over the old+-- one if it had started to be processed but did not get finished due+-- to an interruption. calcRestageLog :: t -> ((TopFilePath, InodeCache) -> t -> t) -> Annex t calcRestageLog start update = do logf <- fromRepo gitAnnexRestageLog+ oldf <- fromRepo gitAnnexRestageLogOld lckf <- fromRepo gitAnnexRestageLock- calcLogFile logf lckf start $ \l v -> - case parseRestageLog (decodeBL l) of- Just pl -> update pl v- Nothing -> v+ withSharedLock lckf $ do+ mid <- calcLogFileUnsafe logf start process+ calcLogFileUnsafe oldf mid process+ where+ process l v = case parseRestageLog (decodeBL l) of+ Just pl -> update pl v+ Nothing -> v formatRestageLog :: TopFilePath -> InodeCache -> S.ByteString formatRestageLog f ic = encodeBS (showInodeCache ic) <> ":" <> getTopFilePath f
Remote/S3.hs view
@@ -1,6 +1,6 @@ {- S3 remotes -- - Copyright 2011-2020 Joey Hess <id@joeyh.name>+ - Copyright 2011-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -156,7 +156,9 @@ signatureField :: RemoteConfigField signatureField = Accepted "signature" -newtype SignatureVersion = SignatureVersion Int+data SignatureVersion + = SignatureVersion Int+ | Anonymous signatureVersionParser :: RemoteConfigField -> FieldDesc -> RemoteConfigFieldParser signatureVersionParser f fd =@@ -165,10 +167,17 @@ where go "v2" = Just (SignatureVersion 2) go "v4" = Just (SignatureVersion 4)+ go "anonymous" = Just Anonymous go _ = Nothing defver = SignatureVersion 2 +isAnonymous :: ParsedRemoteConfig -> Bool+isAnonymous c = + case getRemoteConfigValue signatureField c of+ Just Anonymous -> True+ _ -> False+ portField :: RemoteConfigField portField = Accepted "port" @@ -272,7 +281,9 @@ (c', encsetup) <- encryptionSetup (c `M.union` defaults) gc pc <- either giveup return . parseRemoteConfig c' =<< configParser remote c'- c'' <- setRemoteCredPair ss encsetup pc gc (AWS.creds u) mcreds+ c'' <- if isAnonymous pc+ then pure c'+ else setRemoteCredPair ss encsetup pc gc (AWS.creds u) mcreds pc' <- either giveup return . parseRemoteConfig c'' =<< configParser remote c'' info <- extractS3Info pc'@@ -286,7 +297,9 @@ showNote "Internet Archive mode" pc <- either giveup return . parseRemoteConfig c =<< configParser remote c- c' <- setRemoteCredPair ss noEncryptionUsed pc gc (AWS.creds u) mcreds+ c' <- if isAnonymous pc+ then pure c+ else setRemoteCredPair ss noEncryptionUsed pc gc (AWS.creds u) mcreds -- Ensure user enters a valid bucket name, since -- this determines the name of the archive.org item. let validbucket = replace " " "-" $ map toLower $@@ -402,19 +415,20 @@ - that is difficult. -} retrieve :: S3HandleVar -> Remote -> RemoteStateHandle -> ParsedRemoteConfig -> S3Info -> Retriever retrieve hv r rs c info = fileRetriever' $ \f k p iv -> withS3Handle hv $ \case- (Just h) -> + Right h -> eitherS3VersionID info rs c k (T.pack $ bucketObject info k) >>= \case Left failreason -> do warning failreason giveup "cannot download content" Right loc -> retrieveHelper info h loc (fromRawFilePath f) p iv- Nothing ->+ Left S3HandleNeedCreds -> getPublicWebUrls' (uuid r) rs info c k >>= \case Left failreason -> do warning failreason giveup "cannot download content" Right us -> unlessM (withUrlOptions $ downloadUrl False k p iv us (fromRawFilePath f)) $ giveup "failed to download content"+ Left S3HandleAnonymousOldAws -> giveupS3HandleProblem S3HandleAnonymousOldAws (uuid r) retrieveHelper :: S3Info -> S3Handle -> (Either S3.Object S3VersionID) -> FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> Annex () retrieveHelper info h loc f p iv = retrieveHelper' h f p iv $@@ -449,12 +463,12 @@ checkKey :: S3HandleVar -> Remote -> RemoteStateHandle -> ParsedRemoteConfig -> S3Info -> CheckPresent checkKey hv r rs c info k = withS3Handle hv $ \case- Just h -> eitherS3VersionID info rs c k (T.pack $ bucketObject info k) >>= \case+ Right h -> eitherS3VersionID info rs c k (T.pack $ bucketObject info k) >>= \case Left failreason -> do warning failreason giveup "cannot check content" Right loc -> checkKeyHelper info h loc- Nothing ->+ Left S3HandleNeedCreds -> getPublicWebUrls' (uuid r) rs info c k >>= \case Left failreason -> do warning failreason@@ -463,6 +477,7 @@ let check u = withUrlOptions $ Url.checkBoth u (fromKey keySize k) anyM check us+ Left S3HandleAnonymousOldAws -> giveupS3HandleProblem S3HandleAnonymousOldAws (uuid r) checkKeyHelper :: S3Info -> S3Handle -> (Either S3.Object S3VersionID) -> Annex Bool checkKeyHelper info h loc = checkKeyHelper' info h o limit@@ -485,8 +500,8 @@ storeExportS3' :: S3HandleVar -> Remote -> RemoteStateHandle -> S3Info -> Maybe Magic -> FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex (Maybe S3Etag, Maybe S3VersionID) storeExportS3' hv r rs info magic f k loc p = withS3Handle hv $ \case- Just h -> go h- Nothing -> giveup $ needS3Creds (uuid r)+ Right h -> go h+ Left pr -> giveupS3HandleProblem pr (uuid r) where go h = do let o = T.pack $ bucketExportLocation info loc@@ -497,45 +512,44 @@ retrieveExportS3 :: S3HandleVar -> Remote -> S3Info -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification retrieveExportS3 hv r info k loc f p = verifyKeyContentIncrementally AlwaysVerify k $ \iv -> withS3Handle hv $ \case- Just h -> retrieveHelper info h (Left (T.pack exportloc)) f p iv- Nothing -> case getPublicUrlMaker info of+ Right h -> retrieveHelper info h (Left (T.pack exportloc)) f p iv+ Left S3HandleNeedCreds -> case getPublicUrlMaker info of Just geturl -> either giveup return =<< Url.withUrlOptions (Url.download' p iv (geturl exportloc) f) Nothing -> giveup $ needS3Creds (uuid r)+ Left S3HandleAnonymousOldAws -> giveupS3HandleProblem S3HandleAnonymousOldAws (uuid r) where exportloc = bucketExportLocation info loc removeExportS3 :: S3HandleVar -> Remote -> RemoteStateHandle -> S3Info -> Key -> ExportLocation -> Annex () removeExportS3 hv r rs info k loc = withS3Handle hv $ \case- Just h -> do+ Right h -> do checkVersioning info rs k liftIO $ runResourceT $ do S3.DeleteObjectResponse <- sendS3Handle h $ S3.DeleteObject (T.pack $ bucketExportLocation info loc) (bucket info) return ()- Nothing -> giveup $ needS3Creds (uuid r)- where+ Left p -> giveupS3HandleProblem p (uuid r) checkPresentExportS3 :: S3HandleVar -> Remote -> S3Info -> Key -> ExportLocation -> Annex Bool checkPresentExportS3 hv r info k loc = withS3Handle hv $ \case- Just h -> checkKeyHelper info h (Left (T.pack $ bucketExportLocation info loc))- Nothing -> case getPublicUrlMaker info of+ Right h -> checkKeyHelper info h (Left (T.pack $ bucketExportLocation info loc))+ Left S3HandleNeedCreds -> case getPublicUrlMaker info of Just geturl -> withUrlOptions $ Url.checkBoth (geturl $ bucketExportLocation info loc) (fromKey keySize k)- Nothing -> do- warning $ needS3Creds (uuid r)- giveup "No S3 credentials configured"+ Nothing -> giveupS3HandleProblem S3HandleNeedCreds (uuid r)+ Left S3HandleAnonymousOldAws -> giveupS3HandleProblem S3HandleAnonymousOldAws (uuid r) -- S3 has no move primitive; copy and delete. renameExportS3 :: S3HandleVar -> Remote -> RemoteStateHandle -> S3Info -> Key -> ExportLocation -> ExportLocation -> Annex (Maybe ()) renameExportS3 hv r rs info k src dest = Just <$> go where go = withS3Handle hv $ \case- Just h -> do+ Right h -> do checkVersioning info rs k go' h- Nothing -> giveup $ needS3Creds (uuid r)+ Left p -> giveupS3HandleProblem p (uuid r) go' h = liftIO $ runResourceT $ do let co = S3.copyObject (bucket info) dstobject@@ -551,8 +565,8 @@ listImportableContentsS3 :: S3HandleVar -> Remote -> S3Info -> ParsedRemoteConfig -> Annex (Maybe (ImportableContentsChunkable Annex (ContentIdentifier, ByteSize))) listImportableContentsS3 hv r info c = withS3Handle hv $ \case- Nothing -> giveup $ needS3Creds (uuid r)- Just h -> Just <$> go h+ Right h -> Just <$> go h+ Left p -> giveupS3HandleProblem p (uuid r) where go h = do ic <- liftIO $ runResourceT $ extractFromResourceT =<< startlist h@@ -665,7 +679,7 @@ return (k, v) where go iv = withS3Handle hv $ \case- Just h -> do+ Right h -> do rewritePreconditionException $ retrieveHelper' h dest p iv $ limitGetToContentIdentifier cid $ S3.getObject (bucket info) o@@ -677,7 +691,7 @@ setS3VersionID info rs k vid Left _ -> noop return k- Nothing -> giveup $ needS3Creds (uuid r)+ Left pr -> giveupS3HandleProblem pr (uuid r) o = T.pack $ bucketExportLocation info loc retrieveExportWithContentIdentifierS3 _ _ _ _ _ [] _ _ _ = giveup "missing content identifier" @@ -726,12 +740,9 @@ checkPresentExportWithContentIdentifierS3 :: S3HandleVar -> Remote -> S3Info -> Key -> ExportLocation -> [ContentIdentifier] -> Annex Bool checkPresentExportWithContentIdentifierS3 hv r info _k loc knowncids =- withS3Handle hv $ \case- Just h -> flip anyM knowncids $+ withS3HandleOrFail (uuid r) hv $ \h ->+ flip anyM knowncids $ checkKeyHelper' info h o . limitHeadToContentIdentifier- Nothing -> do- warning $ needS3Creds (uuid r)- giveup "No S3 credentials configured" where o = T.pack $ bucketExportLocation info loc @@ -841,24 +852,43 @@ -> ResourceT IO a sendS3Handle h r = AWS.pureAws (hawscfg h) (hs3cfg h) (hmanager h) r -type S3HandleVar = TVar (Either (Annex (Maybe S3Handle)) (Maybe S3Handle))+type S3HandleVar = TVar (Either (Annex (Either S3HandleProblem S3Handle)) (Either S3HandleProblem S3Handle)) +data S3HandleProblem+ = S3HandleNeedCreds+ | S3HandleAnonymousOldAws++giveupS3HandleProblem :: S3HandleProblem -> UUID -> Annex a+giveupS3HandleProblem S3HandleNeedCreds u = do+ warning $ needS3Creds u+ giveup "No S3 credentials configured"+giveupS3HandleProblem S3HandleAnonymousOldAws _ =+ giveup "This S3 special remote is configured with signature=anonymous, but git-annex is buit with too old a version of the aws library to support that."+ {- Prepares a S3Handle for later use. Does not connect to S3 or do anything - else expensive. -} mkS3HandleVar :: ParsedRemoteConfig -> RemoteGitConfig -> UUID -> Annex S3HandleVar-mkS3HandleVar c gc u = liftIO $ newTVarIO $ Left $ do- mcreds <- getRemoteCredPair c gc (AWS.creds u)- case mcreds of- Just creds -> do- awscreds <- liftIO $ genCredentials creds- let awscfg = AWS.Configuration AWS.Timestamp awscreds debugMapper Nothing- ou <- getUrlOptions- return $ Just $ S3Handle (httpManager ou) awscfg s3cfg- Nothing -> return Nothing+mkS3HandleVar c gc u = liftIO $ newTVarIO $ Left $+ if isAnonymous c+ then +#if MIN_VERSION_aws(0,23,0)+ go =<< liftIO AWS.anonymousCredentials+#else+ return (Left S3HandleAnonymousOldAws)+#endif+ else do+ mcreds <- getRemoteCredPair c gc (AWS.creds u)+ case mcreds of+ Just creds -> go =<< liftIO (genCredentials creds)+ Nothing -> return (Left S3HandleNeedCreds) where s3cfg = s3Configuration c+ go awscreds = do+ let awscfg = AWS.Configuration AWS.Timestamp awscreds debugMapper Nothing+ ou <- getUrlOptions+ return $ Right $ S3Handle (httpManager ou) awscfg s3cfg -withS3Handle :: S3HandleVar -> (Maybe S3Handle -> Annex a) -> Annex a+withS3Handle :: S3HandleVar -> (Either S3HandleProblem S3Handle -> Annex a) -> Annex a withS3Handle hv a = liftIO (readTVarIO hv) >>= \case Right hdl -> a hdl Left mkhdl -> do@@ -868,10 +898,8 @@ withS3HandleOrFail :: UUID -> S3HandleVar -> (S3Handle -> Annex a) -> Annex a withS3HandleOrFail u hv a = withS3Handle hv $ \case- Just hdl -> a hdl- Nothing -> do- warning $ needS3Creds u- giveup "No S3 credentials configured"+ Right hdl -> a hdl+ Left p -> giveupS3HandleProblem p u needS3Creds :: UUID -> String needS3Creds u = missingCredPairFor "S3" (AWS.creds u)
Test.hs view
@@ -92,7 +92,7 @@ optParser :: Parser TestOptions optParser = TestOptions- <$> snd (tastyParser (tests 1 False True (TestOptions mempty False False Nothing mempty mempty)))+ <$> snd (tastyParser (tests 1 False True (TestOptions mempty False False Nothing mempty False mempty))) <*> switch ( long "keep-failures" <> help "preserve repositories on test failure"@@ -111,6 +111,10 @@ <> help "run tests with a git config set" <> metavar "NAME=VALUE" ))+ <*> switch+ ( long "test-debug"+ <> help "show debug messages for commands run by test suite"+ ) <*> cmdParams "non-options are for internal use only" where parseconfigvalue s = case break (== '=') s of@@ -355,7 +359,15 @@ mk l = testGroup groupname (initTests : map adddep l) adddep = Test.Tasty.after AllSucceed (groupname ++ "." ++ initTestsName) groupname = "Repo Tests " ++ note- sep = sep' (replicate numparts [])+ sep l = + -- Avoid separating into parts that contain less than+ -- 5 tests each. Since the tests depend on the+ -- initTests, this avoids spending too much work running+ -- the initTests once per part.+ let numparts' = if length l `div` numparts > 5+ then numparts+ else length l `div` 5+ in sep' (replicate numparts' []) l sep' (p:ps) (l:ls) = sep' (ps++[l:p]) ls sep' ps [] = ps sep' [] _ = []
Test/Framework.hs view
@@ -65,11 +65,20 @@ -- Run a process. The output and stderr is captured, and is only -- displayed if the process does not return the expected value.+--+-- In debug mode, the output is allowed to pass through. testProcess :: String -> [String] -> Maybe [(String, String)] -> (Bool -> Bool) -> String -> Assertion testProcess command params environ expectedret faildesc = do let p = (proc command params) { env = environ }- (transcript, ret) <- Utility.Process.Transcript.processTranscript' p Nothing- (expectedret ret) @? (faildesc ++ " failed (transcript follows)\n" ++ transcript)+ debug <- testDebug . testOptions <$> getTestMode+ if debug+ then do+ ret <- withCreateProcess p $ \_ _ _ pid ->+ waitForProcess pid+ (expectedret (ret == ExitSuccess)) @? (faildesc ++ " failed")+ else do+ (transcript, ret) <- Utility.Process.Transcript.processTranscript' p Nothing+ (expectedret ret) @? (faildesc ++ " failed (transcript follows)\n" ++ transcript) -- Run git. (Do not use to run git-annex as the one being tested -- may not be in path.)@@ -98,7 +107,11 @@ git_annex'' :: (Bool -> Bool) -> String -> [String] -> Maybe [(String, String)] -> String -> Assertion git_annex'' expectedret command params environ faildesc = do pp <- Annex.Path.programPath- testProcess pp (command:params) environ expectedret faildesc+ debug <- testDebug . testOptions <$> getTestMode+ let params' = if debug+ then "--debug":params+ else params+ testProcess pp (command:params') environ expectedret faildesc {- Runs git-annex and returns its standard output. -} git_annex_output :: String -> [String] -> IO String@@ -736,7 +749,12 @@ -- Make more parts than there are jobs, because some parts -- are larger, and this allows the smaller parts to be packed -- in more efficiently, speeding up the test suite overall.- numparts = numjobs * 2+ --+ -- When there is a pattern, splitting into parts will cause+ -- extra work.+ numparts = if haspattern+ then 1+ else numjobs * 2 worker rs nvar a = do (n, m) <- atomically $ do@@ -801,15 +819,16 @@ , exitFailure ) - tastyopts = case lookupOption (tastyOptionSet opts) of+ (haspattern, tastyopts) = case lookupOption (tastyOptionSet opts) of -- Work around limitation of tasty; when tests to run -- are limited to a pattern, it does not include their -- dependencies. So, add another pattern including the -- init tests, which are a dependency of most tests. TestPattern (Just p) -> - setOption (TestPattern (Just (TP.Or p (TP.ERE initTestsName))))- (tastyOptionSet opts)- TestPattern Nothing -> tastyOptionSet opts+ (True, setOption (TestPattern (Just (TP.Or p (TP.ERE initTestsName))))+ (tastyOptionSet opts))+ TestPattern Nothing ->+ (False, tastyOptionSet opts) topLevelTestGroup :: [TestTree] -> TestTree topLevelTestGroup = testGroup "Tests"
Types/Test.hs view
@@ -19,6 +19,7 @@ , fakeSsh :: Bool , concurrentJobs :: Maybe Concurrency , testGitConfig :: [(ConfigKey, ConfigValue)]+ , testDebug :: Bool , internalData :: CmdParams }
Utility/DataUnits.hs view
@@ -73,7 +73,9 @@ {- Storage units are (stupidly) powers of ten. -} storageUnits :: [Unit] storageUnits =- [ Unit (p 8) "YB" "yottabyte"+ [ Unit (p 10) "QB" "quettabyte"+ , Unit (p 9) "RB" "ronnabyte"+ , Unit (p 8) "YB" "yottabyte" , Unit (p 7) "ZB" "zettabyte" , Unit (p 6) "EB" "exabyte" , Unit (p 5) "PB" "petabyte"
Utility/LockFile/Windows.hs view
@@ -61,7 +61,7 @@ openLock :: ShareMode -> LockFile -> IO (Maybe LockHandle) openLock sharemode f = do f' <- convertToNativeNamespace f-#if MIN_VERSION_Win32(2,13,3)+#if MIN_VERSION_Win32(2,13,4) r <- tryNonAsync $ createFile_NoRetry f' gENERIC_READ sharemode security_attributes oPEN_ALWAYS fILE_ATTRIBUTE_NORMAL (maybePtr Nothing)
doc/git-annex-sync.mdwn view
@@ -175,7 +175,8 @@ * `--cleanup` Removes the local and remote `synced/` branches, which were created- and pushed by `git-annex sync`.+ and pushed by `git-annex sync`. This option prevents all other syncing+ activities. This can come in handy when you've synced a change to remotes and now want to reset your master branch back before that change. So you
doc/git-annex-test.mdwn view
@@ -44,6 +44,16 @@ One valid use of this is to change a git configuration to a value that is planned to be the new default in a future version of git. +* `--test-debug`++ Normally output of commands run by the test suite is hidden, so even+ when annex.debug or --debug is enabled, it will not be displayed.+ This option makes the full output of commands run by the test suite be+ displayed. It also makes the test suite run git-annex with --debug.++ It's a good idea to use `-J1` in combinaton with this, otherwise+ the output of concurrent tests will be mixed together.+ # SEE ALSO [[git-annex]](1)
doc/git-annex.mdwn view
@@ -1719,8 +1719,8 @@ * `annex.youtube-dl-options` - Options to pass to youtube-dl when using it to find the url to download- for a video.+ Options to pass to youtube-dl (or yt-dlp) when using it to find the url+ to download for a video. Some options may break git-annex's integration with youtube-dl. For example, the --output option could cause it to store files somewhere@@ -1730,7 +1730,8 @@ * `annex.youtube-dl-command` - Command to run for youtube-dl. Default is "youtube-dl".+ Command to run for youtube-dl. Default is to use "youtube-dl" or + if that is not available in the PATH, to use "yt-dlp". * `annex.aria-torrent-options`
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 10.20221103+Version: 10.20221212 Cabal-Version: 1.12 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -416,7 +416,7 @@ if (os(windows)) Build-Depends:- Win32 ((>= 2.6.1.0 && < 2.12.0.0) || >= 2.13.3.1),+ Win32 ((>= 2.6.1.0 && < 2.12.0.0) || >= 2.13.4.0), setenv, process (>= 1.6.2.0), silently (>= 1.2.5.1)