git-annex-10.20261113: Command/Transfers.hs
{- git-annex command
-
- Copyright 2026 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
{-# LANGUAGE OverloadedStrings #-}
module Command.Transfers where
import Command
import Types.Transfer
import Logs.Transfer
import Remote
import Messages.Progress
import Messages.Concurrent
import Utility.ThreadScheduler
import Utility.Metered
import Annex.Concurrent
import Database.Keys
import Git.FilePath
import qualified Data.Map as M
import Control.Concurrent.STM
import Control.Concurrent.Async
cmd :: Command
cmd = noCommit $ withAnnexOptions [jsonOptions, jsonProgressOption] $
command "transfers" SectionQuery
"display transfers"
paramNothing (seek <$$> optParser)
data TransfersOptions = TransfersOptions
{ localFilenames :: Bool
}
optParser :: CmdParamsDesc -> Parser TransfersOptions
optParser _ = TransfersOptions
<$> switch
( long "localfilenames"
<> short 'l'
<> help "display local filenames, not remote filenames"
)
seek :: TransfersOptions -> CommandSeek
seek o = withConcurrentOutput $ do
when (localFilenames o)
updateDatabase
rs <- remoteList
let remotemap = M.fromList $ zip (map uuid rs) rs
go remotemap M.empty
where
go remotemap transfermap = do
l <- getTransfers
new <- forM l $ \(t, ti) -> do
case M.lookup t transfermap of
Nothing -> startone remotemap t ti
Just v -> return (t, v)
let newmap = M.fromList new
forM_ (M.toList transfermap) $ \(t, (endvar, tid)) ->
unless (M.member t newmap) $ do
liftIO $ atomically $ putTMVar endvar ()
void $ join $ liftIO $ wait tid
liftIO $ threadDelaySeconds (SecondsDelay 1)
go remotemap newmap
startone remotemap t ti = do
endvar <- liftIO newEmptyTMVarIO
a <- forkState $
withMessageState $ \s ->
inOwnConsoleRegion s $
includeCommandAction $
start o t ti endvar remotemap
tid <- liftIO $ async a
return (t, (endvar, tid))
start :: TransfersOptions -> Transfer -> TransferInfo -> TMVar () -> M.Map UUID Remote -> CommandStart
start o t ti endvar remotemap = do
af <- if localFilenames o
then headMaybe <$> getAssociatedFiles k >>= \case
Just topf -> relaf =<< fromRepo (fromTopFilePath topf)
Nothing -> pure $ AssociatedFile Nothing
else pure $ associatedFile ti
let ai = ActionItemAssociatedFile af k
startingNoMessage ai $ do
showStartMessage (StartMessage verb ai si)
showNote $ UnquotedString $ fromto ++ " " ++ remotename
maybeAddJSONField "remote-uuid" (transferUUID t)
(tfile, _, _) <- fromRepo $ transferFileAndLockFile t
metered Nothing k Nothing $ \_ p ->
watchSizeWith (getprogress tfile) p $
const waitend
showEnd "done"
next $ return True
where
k = transferKey t
si = SeekInput []
verb = case transferDirection t of
Upload -> "send"
Download -> "receive"
fromto = case transferDirection t of
Upload -> "to"
Download -> "from"
remotename =
maybe (fromUUID (transferUUID t)) name $
M.lookup (transferUUID t) remotemap
waitend =
liftIO $ atomically $ takeTMVar endvar
relaf f = AssociatedFile . Just <$> liftIO (relPathCwdToFile f)
getprogress tfile = readTransferInfoFile Nothing tfile
>>= return . \case
Just ti' -> maybe zeroBytesProcessed toBytesProcessed
(bytesComplete ti')
Nothing -> zeroBytesProcessed