git-annex 6.20160619 → 6.20160808
raw patch · 77 files changed
+827/−331 lines, 77 filesdep +unordered-containersdep −jsondep ~http-conduitdep ~persistentdep ~uuid
Dependencies added: unordered-containers
Dependencies removed: json
Dependency ranges changed: http-conduit, persistent, uuid
Files
- Annex/Content.hs +1/−0
- Annex/Drop.hs +2/−2
- Annex/MetaData.hs +1/−1
- Annex/Notification.hs +1/−1
- Annex/Transfer.hs +14/−17
- Annex/UUID.hs +2/−2
- Assistant/Alert.hs +1/−1
- Assistant/DaemonStatus.hs +1/−0
- Assistant/DeleteRemote.hs +1/−1
- Assistant/Sync.hs +1/−1
- Assistant/Threads/Committer.hs +1/−1
- Assistant/Threads/Cronner.hs +1/−1
- Assistant/Threads/Glacier.hs +1/−0
- Assistant/Threads/SanityChecker.hs +1/−1
- Assistant/Threads/TransferPoller.hs +1/−0
- Assistant/Threads/TransferScanner.hs +1/−0
- Assistant/Threads/TransferWatcher.hs +1/−0
- Assistant/Threads/Transferrer.hs +1/−1
- Assistant/TransferQueue.hs +1/−0
- Assistant/TransferSlots.hs +1/−0
- Assistant/TransferrerPool.hs +1/−1
- Assistant/Types/DaemonStatus.hs +1/−1
- Assistant/Types/TransferQueue.hs +1/−1
- Assistant/Upgrade.hs +1/−1
- Assistant/WebApp/DashBoard.hs +1/−0
- Assistant/WebApp/Types.hs +1/−1
- CHANGELOG +30/−0
- CmdLine/Action.hs +0/−1
- CmdLine/GitAnnex/Options.hs +28/−18
- CmdLine/Seek.hs +61/−24
- Command.hs +12/−1
- Command/Add.hs +1/−1
- Command/AddUrl.hs +3/−3
- Command/Drop.hs +16/−14
- Command/DropKey.hs +1/−1
- Command/Find.hs +1/−1
- Command/Fsck.hs +9/−9
- Command/Get.hs +10/−8
- Command/Info.hs +19/−12
- Command/MetaData.hs +91/−21
- Command/Mirror.hs +13/−10
- Command/Move.hs +23/−19
- Command/RecvKey.hs +1/−1
- Command/SendKey.hs +1/−1
- Command/SetPresentKey.hs +1/−1
- Command/Status.hs +1/−1
- Command/Sync.hs +4/−5
- Command/TransferInfo.hs +1/−0
- Command/TransferKey.hs +2/−2
- Command/TransferKeys.hs +2/−5
- Command/Whereis.hs +7/−8
- Git/FilePath.hs +9/−0
- Logs/Transfer.hs +1/−32
- Messages.hs +18/−10
- Messages/JSON.hs +66/−20
- Remote.hs +10/−10
- Remote/External.hs +1/−1
- Remote/External/Types.hs +1/−1
- Remote/GCrypt.hs +1/−1
- Remote/Git.hs +2/−2
- Remote/Helper/Ssh.hs +1/−1
- Remote/Rsync.hs +1/−1
- Test.hs +6/−5
- Types/ActionItem.hs +54/−0
- Types/Key.hs +10/−1
- Types/MetaData.hs +32/−4
- Types/Transfer.hs +47/−0
- Utility/JSONStream.hs +48/−24
- doc/git-annex-copy.mdwn +8/−0
- doc/git-annex-drop.mdwn +16/−0
- doc/git-annex-fsck.mdwn +4/−0
- doc/git-annex-get.mdwn +16/−5
- doc/git-annex-metadata.mdwn +59/−5
- doc/git-annex-mirror.mdwn +15/−0
- doc/git-annex-move.mdwn +8/−0
- doc/git-annex-whereis.mdwn +4/−0
- git-annex.cabal +10/−6
Annex/Content.hs view
@@ -54,6 +54,7 @@ import Annex.Common import Logs.Location+import Types.Transfer import Logs.Transfer import qualified Git import qualified Annex
Annex/Drop.hs view
@@ -117,10 +117,10 @@ ) dropl fs n = checkdrop fs n Nothing $ \numcopies ->- Command.Drop.startLocal afile numcopies key preverified+ Command.Drop.startLocal afile (mkActionItem afile) numcopies key preverified dropr fs r n = checkdrop fs n (Just $ Remote.uuid r) $ \numcopies ->- Command.Drop.startRemote afile numcopies key r+ Command.Drop.startRemote afile (mkActionItem afile) numcopies key r slocs = S.fromList locs
Annex/MetaData.hs view
@@ -63,7 +63,7 @@ Just '+' -> AddMeta <$> mkMetaField f' <*> v Just '-' -> DelMeta <$> mkMetaField f' <*> (Just <$> v) Just '?' -> MaybeSetMeta <$> mkMetaField f' <*> v- _ -> SetMeta <$> mkMetaField f <*> v+ _ -> SetMeta <$> mkMetaField f <*> (S.singleton <$> v) where (f, sv) = separate (== '=') p f' = beginning f
Annex/Notification.hs view
@@ -10,7 +10,7 @@ module Annex.Notification (NotifyWitness, notifyTransfer, notifyDrop) where import Annex.Common-import Logs.Transfer+import Types.Transfer #ifdef WITH_DBUS_NOTIFICATIONS import qualified Annex import Types.DesktopNotify
Annex/Transfer.hs view
@@ -9,7 +9,6 @@ module Annex.Transfer ( module X,- noObserver, upload, download, runTransfer,@@ -20,6 +19,7 @@ import Annex.Common import Logs.Transfer as X+import Types.Transfer as X import Annex.Notification as X import Annex.Perms import Utility.Metered@@ -28,8 +28,6 @@ import Control.Concurrent -type TransferObserver = Bool -> Transfer -> TransferInfo -> Annex ()- class Observable a where observeBool :: a -> Bool observeFailure :: a@@ -42,16 +40,13 @@ observeBool = fst observeFailure = (False, UnVerified) -noObserver :: TransferObserver-noObserver _ _ _ = noop--upload :: Observable v => UUID -> Key -> AssociatedFile -> RetryDecider -> TransferObserver -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v-upload u key f d o a _witness = guardHaveUUID u $ - runTransfer (Transfer Upload u key) f d o a+upload :: Observable v => UUID -> Key -> AssociatedFile -> RetryDecider -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v+upload u key f d a _witness = guardHaveUUID u $ + runTransfer (Transfer Upload u key) f d a -download :: Observable v => UUID -> Key -> AssociatedFile -> RetryDecider -> TransferObserver -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v-download u key f d o a _witness = guardHaveUUID u $- runTransfer (Transfer Download u key) f d o a+download :: Observable v => UUID -> Key -> AssociatedFile -> RetryDecider -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v+download u key f d a _witness = guardHaveUUID u $+ runTransfer (Transfer Download u key) f d a guardHaveUUID :: Observable v => UUID -> Annex v -> Annex v guardHaveUUID u a@@ -70,7 +65,7 @@ - An upload can be run from a read-only filesystem, and in this case - no transfer information or lock file is used. -}-runTransfer :: Observable v => Transfer -> Maybe FilePath -> RetryDecider -> TransferObserver -> (MeterUpdate -> Annex v) -> Annex v+runTransfer :: Observable v => Transfer -> Maybe FilePath -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v runTransfer = runTransfer' False {- Like runTransfer, but ignores any existing transfer lock file for the@@ -78,11 +73,11 @@ - - 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 -> Maybe FilePath -> RetryDecider -> TransferObserver -> (MeterUpdate -> Annex v) -> Annex v+alwaysRunTransfer :: Observable v => Transfer -> Maybe FilePath -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v alwaysRunTransfer = runTransfer' True -runTransfer' :: Observable v => Bool -> Transfer -> Maybe FilePath -> RetryDecider -> TransferObserver -> (MeterUpdate -> Annex v) -> Annex v-runTransfer' ignorelock t file shouldretry transferobserver transferaction = do+runTransfer' :: Observable v => Bool -> Transfer -> Maybe FilePath -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v+runTransfer' ignorelock t file shouldretry transferaction = do info <- liftIO $ startTransferInfo file (meter, tfile, metervar) <- mkProgressUpdater t info mode <- annexFileMode@@ -94,7 +89,9 @@ else do v <- retry info metervar $ transferaction meter liftIO $ cleanup tfile lck- transferobserver (observeBool v) t info+ if observeBool v+ then removeFailedTransfer t+ else recordFailedTransfer t info return v where #ifndef mingw32_HOST_OS
Annex/UUID.hs view
@@ -35,8 +35,8 @@ import Config import qualified Data.UUID as U+import qualified Data.UUID.V4 as U4 import qualified Data.UUID.V5 as U5-import System.Random import Data.Bits.Utils configkey :: ConfigKey@@ -44,7 +44,7 @@ {- Generates a random UUID, that does not include the MAC address. -} genUUID :: IO UUID-genUUID = UUID . show <$> (randomIO :: IO U.UUID)+genUUID = UUID . show <$> U4.nextRandom {- Generates a UUID from a given string, using a namespace. - Given the same namespace, the same string will always result
Assistant/Alert.hs view
@@ -14,7 +14,7 @@ import Assistant.Alert.Utility import qualified Remote import Utility.Tense-import Logs.Transfer+import Types.Transfer import Types.Distribution import Git.Types (RemoteName)
Assistant/DaemonStatus.hs view
@@ -14,6 +14,7 @@ import Utility.Tmp import Assistant.Types.NetMessager import Utility.NotificationBroadcaster+import Types.Transfer import Logs.Transfer import Logs.Trust import Logs.TimeStamp
Assistant/DeleteRemote.hs view
@@ -12,7 +12,7 @@ import Assistant.Common import Assistant.Types.UrlRenderer import Assistant.TransferQueue-import Logs.Transfer+import Types.Transfer import Logs.Location import Assistant.DaemonStatus import qualified Remote
Assistant/Sync.hs view
@@ -36,7 +36,7 @@ import Assistant.TransferSlots import Assistant.TransferQueue import Assistant.RepoProblem-import Logs.Transfer+import Types.Transfer import Data.Time.Clock import qualified Data.Map as M
Assistant/Threads/Committer.hs view
@@ -17,7 +17,7 @@ import Assistant.DaemonStatus import Assistant.TransferQueue import Assistant.Drop-import Logs.Transfer+import Types.Transfer import Logs.Location import qualified Annex.Queue import qualified Git.LsFiles
Assistant/Threads/Cronner.hs view
@@ -24,7 +24,7 @@ import Utility.Batch import Assistant.TransferQueue import Annex.Content-import Logs.Transfer+import Types.Transfer import Assistant.Types.UrlRenderer import Assistant.Alert import Remote
Assistant/Threads/Glacier.hs view
@@ -14,6 +14,7 @@ import Utility.ThreadScheduler import qualified Types.Remote as Remote import qualified Remote.Glacier as Glacier+import Types.Transfer import Logs.Transfer import Assistant.DaemonStatus import Assistant.TransferQueue
Assistant/Threads/SanityChecker.hs view
@@ -38,7 +38,7 @@ import Git.Index import Assistant.Unused import Logs.Unused-import Logs.Transfer+import Types.Transfer import Annex.Path import qualified Annex #ifdef WITH_WEBAPP
Assistant/Threads/TransferPoller.hs view
@@ -9,6 +9,7 @@ import Assistant.Common import Assistant.DaemonStatus+import Types.Transfer import Logs.Transfer import Utility.NotificationBroadcaster import qualified Assistant.Threads.TransferWatcher as TransferWatcher
Assistant/Threads/TransferScanner.hs view
@@ -16,6 +16,7 @@ import Assistant.Sync import Assistant.DeleteRemote import Assistant.Types.UrlRenderer+import Types.Transfer import Logs.Transfer import Logs.Location import Logs.Group
Assistant/Threads/TransferWatcher.hs view
@@ -10,6 +10,7 @@ import Assistant.Common import Assistant.DaemonStatus import Assistant.TransferSlots+import Types.Transfer import Logs.Transfer import Utility.DirWatcher import Utility.DirWatcher.Types
Assistant/Threads/Transferrer.hs view
@@ -10,7 +10,7 @@ import Assistant.Common import Assistant.TransferQueue import Assistant.TransferSlots-import Logs.Transfer+import Types.Transfer import Annex.Path import Utility.Batch
Assistant/TransferQueue.hs view
@@ -26,6 +26,7 @@ import Assistant.Common import Assistant.DaemonStatus import Assistant.Types.TransferQueue+import Types.Transfer import Logs.Transfer import Types.Remote import qualified Remote
Assistant/TransferSlots.hs view
@@ -21,6 +21,7 @@ import Assistant.Alert.Utility import Assistant.Commits import Assistant.Drop+import Types.Transfer import Logs.Transfer import Logs.Location import qualified Git
Assistant/TransferrerPool.hs view
@@ -9,7 +9,7 @@ import Assistant.Common import Assistant.Types.TransferrerPool-import Logs.Transfer+import Types.Transfer import Utility.Batch import qualified Command.TransferKeys as T
Assistant/Types/DaemonStatus.hs view
@@ -10,7 +10,7 @@ import Annex.Common import Assistant.Pairing import Utility.NotificationBroadcaster-import Logs.Transfer+import Types.Transfer import Assistant.Types.ThreadName import Assistant.Types.NetMessager import Assistant.Types.Alert
Assistant/Types/TransferQueue.hs view
@@ -8,7 +8,7 @@ module Assistant.Types.TransferQueue where import Annex.Common-import Logs.Transfer+import Types.Transfer import Control.Concurrent.STM import Utility.TList
Assistant/Upgrade.hs view
@@ -16,7 +16,7 @@ import Assistant.DaemonStatus import Utility.Env import Types.Distribution-import Logs.Transfer+import Types.Transfer import Logs.Web import Logs.Presence import Logs.Location
Assistant/WebApp/DashBoard.hs view
@@ -16,6 +16,7 @@ import Assistant.TransferSlots import Assistant.DaemonStatus import Utility.NotificationBroadcaster+import Types.Transfer import Logs.Transfer import Utility.Percentage import Utility.DataUnits
Assistant/WebApp/Types.hs view
@@ -19,7 +19,7 @@ import Utility.NotificationBroadcaster import Utility.WebApp import Utility.Yesod-import Logs.Transfer+import Types.Transfer import Utility.Gpg (KeyId) import Build.SysConfig (packageversion) import Types.ScheduledActivity
CHANGELOG view
@@ -1,3 +1,33 @@+git-annex (6.20160808) unstable; urgency=medium++ * metadata --json output format has changed, adding a inner json object+ named "fields" which contains only the fields and their values.+ This should be easier to parse than the old format, which mixed up+ metadata fields with other keys in the json object.+ Any consumers of the old format will need to be updated.+ * Added metadata --batch option, which allows getting, setting, deleting,+ and modifying metadata for multiple files/keys.+ * Added --branch option to copy, drop, fsck, get, metadata, mirror, move,+ and whereis commands. This option makes git-annex operate on files that+ are included in a specified branch (or other treeish).+ * git-annex.cabal: Temporarily limit to http-conduit <2.2.0+ since aws 0.14.0 is not compatible with the newer version.+ * git-annex.cabal: Temporarily limit to persistent <2.5+ since esqueleto 2.4.3 is not compatible with the newer version.+ * Removed dependency on json library; all JSON is now handled by aeson.+ * When built with uuid-1.3.12, generate more random UUIDs than before.+ (However, this did not impact git-annex much, so a hard depedency has+ not been added on uuid-1.3.12.)+ * info: When run on a file now includes an indication of whether+ the content is present locally.+ * get, move, copy, mirror: Added --failed switch which retries+ failed copies/moves.+ * Re-enable accumulating transfer failure log files for command-line+ actions (disabled in 5.20150522), and remove the log files after+ successful transfers.++ -- Joey Hess <id@joeyh.name> Mon, 08 Aug 2016 11:42:17 -0400+ git-annex (6.20160619) unstable; urgency=medium * get, drop: Add --batch and --json options.
CmdLine/Action.hs view
@@ -14,7 +14,6 @@ import Annex.Concurrent import Types.Command import Messages.Concurrent-import Messages.Internal import Types.Messages import Control.Concurrent.Async
CmdLine/GitAnnex/Options.hs view
@@ -139,27 +139,37 @@ data KeyOptions = WantAllKeys | WantUnusedKeys+ | WantFailedTransfers | WantSpecificKey Key | WantIncompleteKeys+ | WantBranchKeys [Branch] -parseKeyOptions :: Bool -> Parser KeyOptions-parseKeyOptions allowincomplete = if allowincomplete- then base- <|> flag' WantIncompleteKeys- ( long "incomplete"- <> help "resume previous downloads"- )- else base- where- base = parseAllOption- <|> flag' WantUnusedKeys- ( long "unused" <> short 'U'- <> help "operate on files found by last run of git-annex unused"- )- <|> (WantSpecificKey <$> option (str >>= parseKey)- ( long "key" <> metavar paramKey- <> help "operate on specified key"- ))+parseKeyOptions :: Parser KeyOptions+parseKeyOptions = parseAllOption+ <|> WantBranchKeys <$> some (option (str >>= pure . Ref)+ ( long "branch" <> metavar paramRef+ <> help "operate on files in the specified branch or treeish"+ ))+ <|> flag' WantUnusedKeys+ ( long "unused" <> short 'U'+ <> help "operate on files found by last run of git-annex unused"+ )+ <|> (WantSpecificKey <$> option (str >>= parseKey)+ ( long "key" <> metavar paramKey+ <> help "operate on specified key"+ ))++parseFailedTransfersOption :: Parser KeyOptions+parseFailedTransfersOption = flag' WantFailedTransfers+ ( long "failed"+ <> help "operate on files that recently failed to be transferred"+ )++parseIncompleteOption :: Parser KeyOptions+parseIncompleteOption = flag' WantIncompleteKeys+ ( long "incomplete"+ <> help "resume previous downloads"+ ) parseAllOption :: Parser KeyOptions parseAllOption = flag' WantAllKeys
CmdLine/Seek.hs view
@@ -4,7 +4,7 @@ - the values a user passes to a command, and prepare actions operating - on them. -- - Copyright 2010-2015 Joey Hess <id@joeyh.name>+ - Copyright 2010-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -}@@ -25,6 +25,10 @@ import CmdLine.Action import Logs.Location import Logs.Unused+import Types.Transfer+import Logs.Transfer+import Remote.List+import qualified Remote import Annex.CatFile import Annex.Content @@ -154,42 +158,77 @@ withNothing a [] = seekActions $ return [a] withNothing _ _ = error "This command takes no parameters." -{- Handles the --all, --unused, --key, and --incomplete options,- - which specify particular keys to run an action on.+{- Handles the --all, --branch, --unused, --failed, --key, and+ - --incomplete options, which specify particular keys to run an+ - action on. - - In a bare repo, --all is the default. - - Otherwise falls back to a regular CommandSeek action on - whatever params were passed. -}-withKeyOptions :: Maybe KeyOptions -> Bool -> (Key -> CommandStart) -> (CmdParams -> CommandSeek) -> CmdParams -> CommandSeek-withKeyOptions ko auto keyaction = withKeyOptions' ko auto $ \getkeys -> do- matcher <- Limit.getMatcher- seekActions $ map (process matcher) <$> getkeys+withKeyOptions + :: Maybe KeyOptions+ -> Bool+ -> (Key -> ActionItem -> CommandStart)+ -> (CmdParams -> CommandSeek)+ -> CmdParams+ -> CommandSeek+withKeyOptions ko auto keyaction = withKeyOptions' ko auto mkkeyaction where- process matcher k = ifM (matcher $ MatchingKey k)- ( keyaction k- , return Nothing- )+ mkkeyaction = do+ matcher <- Limit.getMatcher+ return $ \k i ->+ whenM (matcher $ MatchingKey k) $+ commandAction $ keyaction k i -withKeyOptions' :: Maybe KeyOptions -> Bool -> (Annex [Key] -> Annex ()) -> (CmdParams -> CommandSeek) -> CmdParams -> CommandSeek-withKeyOptions' ko auto keyaction fallbackaction params = do+withKeyOptions' + :: Maybe KeyOptions+ -> Bool+ -> Annex (Key -> ActionItem -> Annex ())+ -> (CmdParams -> CommandSeek)+ -> CmdParams+ -> CommandSeek+withKeyOptions' ko auto mkkeyaction fallbackaction params = do bare <- fromRepo Git.repoIsLocalBare when (auto && bare) $ error "Cannot use --auto in a bare repository" case (null params, ko) of (True, Nothing)- | bare -> go auto loggedKeys+ | bare -> noauto $ runkeyaction loggedKeys | otherwise -> fallbackaction params (False, Nothing) -> fallbackaction params- (True, Just WantAllKeys) -> go auto loggedKeys- (True, Just WantUnusedKeys) -> go auto unusedKeys'- (True, Just (WantSpecificKey k)) -> go auto $ return [k]- (True, Just WantIncompleteKeys) -> go auto incompletekeys- (False, Just _) -> error "Can only specify one of file names, --all, --unused, --key, or --incomplete"+ (True, Just WantAllKeys) -> noauto $ runkeyaction loggedKeys+ (True, Just WantUnusedKeys) -> noauto $ runkeyaction unusedKeys'+ (True, Just WantFailedTransfers) -> noauto runfailedtransfers+ (True, Just (WantSpecificKey k)) -> noauto $ runkeyaction (return [k])+ (True, Just WantIncompleteKeys) -> noauto $ runkeyaction incompletekeys+ (True, Just (WantBranchKeys bs)) -> noauto $ runbranchkeys bs+ (False, Just _) -> error "Can only specify one of file names, --all, --branch, --unused, --failed, --key, or --incomplete" where- go True _ = error "Cannot use --auto with --all or --unused or --key or --incomplete"- go False getkeys = keyaction getkeys+ noauto a+ | auto = error "Cannot use --auto with --all or --branch or --unused or --key or --incomplete"+ | otherwise = a incompletekeys = staleKeysPrune gitAnnexTmpObjectDir True+ runkeyaction getks = do+ keyaction <- mkkeyaction+ ks <- getks+ forM_ ks $ \k -> keyaction k (mkActionItem k)+ runbranchkeys bs = do+ keyaction <- mkkeyaction+ forM_ bs $ \b -> do+ (l, cleanup) <- inRepo $ LsTree.lsTree b+ forM_ l $ \i -> do+ let bfp = mkActionItem $ BranchFilePath b (LsTree.file i)+ maybe noop (\k -> keyaction k bfp)+ =<< catKey (LsTree.sha i)+ unlessM (liftIO cleanup) $+ error ("git ls-tree " ++ Git.fromRef b ++ " failed")+ runfailedtransfers = do+ keyaction <- mkkeyaction+ rs <- remoteList+ ts <- concat <$> mapM (getFailedTransfers . Remote.uuid) rs+ forM_ ts $ \(t, i) ->+ keyaction (transferKey t) (mkActionItem (t, i)) prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart] prepFiltered a fs = do@@ -200,9 +239,7 @@ ( a f , return Nothing ) seekActions :: Annex [CommandStart] -> Annex ()-seekActions gen = do- as <- gen- mapM_ commandAction as+seekActions gen = mapM_ commandAction =<< gen seekHelper :: ([FilePath] -> Git.Repo -> IO ([FilePath], IO Bool)) -> [FilePath] -> Annex [FilePath] seekHelper a params = do
Command.hs view
@@ -1,6 +1,6 @@ {- git-annex command infrastructure -- - Copyright 2010-2015 Joey Hess <id@joeyh.name>+ - Copyright 2010-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -}@@ -26,6 +26,8 @@ import Annex.Init import Config import Utility.Daemon+import Types.Transfer+import Types.ActionItem {- Generates a normal Command -} command :: String -> CommandSection -> String -> CmdParamsDesc -> (CmdParamsDesc -> CommandParser) -> Command@@ -90,6 +92,15 @@ {- Stops unless a condition is met. -} stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a) stopUnless c a = ifM c ( a , stop )++{- When acting on a failed transfer, stops unless it was in the specified+ - direction. -}+checkFailedTransferDirection :: ActionItem -> Direction -> Annex (Maybe a) -> Annex (Maybe a)+checkFailedTransferDirection ai d = stopUnless (pure check)+ where+ check = case actionItemTransferDirection ai of+ Nothing -> True+ Just d' -> d' == d commonChecks :: [CommandCheck] commonChecks = [repoExists]
Command/Add.hs view
@@ -146,7 +146,7 @@ cleanup :: Key -> Bool -> CommandCleanup cleanup key hascontent = do- maybeShowJSON [("key", key2file key)]+ maybeShowJSON $ JSONChunk [("key", key2file key)] when hascontent $ logStatus key InfoPresent return True
Command/AddUrl.hs view
@@ -254,7 +254,7 @@ tmp <- fromRepo $ gitAnnexTmpObjectLocation key showOutput ok <- Transfer.notifyTransfer Transfer.Download (Just file) $- Transfer.download webUUID key (Just file) Transfer.forwardRetry Transfer.noObserver $ \p -> do+ Transfer.download webUUID key (Just file) Transfer.forwardRetry $ \p -> do liftIO $ createDirectoryIfMissing True (parentDir tmp) downloadUrl key p [videourl] tmp if ok@@ -335,7 +335,7 @@ ) where runtransfer tmp = Transfer.notifyTransfer Transfer.Download (Just file) $- Transfer.download u dummykey (Just file) Transfer.forwardRetry Transfer.noObserver $ \p -> do+ Transfer.download u dummykey (Just file) Transfer.forwardRetry $ \p -> do liftIO $ createDirectoryIfMissing True (parentDir tmp) downloader tmp p @@ -356,7 +356,7 @@ ) where go = do- maybeShowJSON [("key", key2file key)]+ maybeShowJSON $ JSONChunk [("key", key2file key)] when (isJust mtmp) $ logStatus key InfoPresent setUrlPresent u key url
Command/Drop.hs view
@@ -41,7 +41,7 @@ <$> cmdParams desc <*> optional parseDropFromOption <*> parseAutoOption- <*> optional (parseKeyOptions False)+ <*> optional parseKeyOptions <*> parseBatchOption parseDropFromOption :: Parser (DeferredParse Remote)@@ -63,36 +63,38 @@ go = whenAnnexed $ start o start :: DropOptions -> FilePath -> Key -> CommandStart-start o file key = start' o key (Just file)+start o file key = start' o key afile (mkActionItem afile)+ where+ afile = Just file -start' :: DropOptions -> Key -> AssociatedFile -> CommandStart-start' o key afile = do+start' :: DropOptions -> Key -> AssociatedFile -> ActionItem -> CommandStart+start' o key afile ai = do from <- maybe (pure Nothing) (Just <$$> getParsed) (dropFrom o) checkDropAuto (autoMode o) from afile key $ \numcopies -> stopUnless (want from) $ case from of- Nothing -> startLocal afile numcopies key []+ Nothing -> startLocal afile ai numcopies key [] Just remote -> do u <- getUUID if Remote.uuid remote == u- then startLocal afile numcopies key []- else startRemote afile numcopies key remote+ then startLocal afile ai numcopies key []+ else startRemote afile ai numcopies key remote where want from | autoMode o = wantDrop False (Remote.uuid <$> from) (Just key) afile | otherwise = return True -startKeys :: DropOptions -> Key -> CommandStart+startKeys :: DropOptions -> Key -> ActionItem -> CommandStart startKeys o key = start' o key Nothing -startLocal :: AssociatedFile -> NumCopies -> Key -> [VerifiedCopy] -> CommandStart-startLocal afile numcopies key preverified = stopUnless (inAnnex key) $ do- showStart' "drop" key afile+startLocal :: AssociatedFile -> ActionItem -> NumCopies -> Key -> [VerifiedCopy] -> CommandStart+startLocal afile ai numcopies key preverified = stopUnless (inAnnex key) $ do+ showStart' "drop" key ai next $ performLocal key afile numcopies preverified -startRemote :: AssociatedFile -> NumCopies -> Key -> Remote -> CommandStart-startRemote afile numcopies key remote = do- showStart' ("drop " ++ Remote.name remote) key afile+startRemote :: AssociatedFile -> ActionItem -> NumCopies -> Key -> Remote -> CommandStart+startRemote afile ai numcopies key remote = do+ showStart' ("drop " ++ Remote.name remote) key ai next $ performRemote key afile numcopies remote performLocal :: Key -> AssociatedFile -> NumCopies -> [VerifiedCopy] -> CommandPerform
Command/DropKey.hs view
@@ -42,7 +42,7 @@ start :: Key -> CommandStart start key = do- showStart' "dropkey" key Nothing+ showStart' "dropkey" key (mkActionItem key) next $ perform key perform :: Key -> CommandPerform
Command/Find.hs view
@@ -66,7 +66,7 @@ showFormatted :: Maybe Utility.Format.Format -> String -> [(String, String)] -> Annex () showFormatted format unformatted vars =- unlessM (showFullJSON vars) $+ unlessM (showFullJSON $ JSONChunk vars) $ case format of Nothing -> liftIO $ putStrLn unformatted Just formatter -> liftIO $ putStr $
Command/Fsck.hs view
@@ -66,7 +66,7 @@ <> completeRemotes )) <*> optional parseincremental- <*> optional (parseKeyOptions False)+ <*> optional parseKeyOptions where parseincremental = flag' StartIncrementalO@@ -89,7 +89,7 @@ checkDeadRepo u i <- prepIncremental u (incrementalOpt o) withKeyOptions (keyOptions o) False- (\k -> startKey i k =<< getNumCopies)+ (\k ai -> startKey i k ai =<< getNumCopies) (withFilesInGit $ whenAnnexed $ start from i) (fsckFiles o) cleanupIncremental i@@ -111,7 +111,7 @@ Nothing -> go $ perform key file backend numcopies Just r -> go $ performRemote key file backend numcopies r where- go = runFsck inc file key+ go = runFsck inc (mkActionItem (Just file)) key perform :: Key -> FilePath -> Backend -> NumCopies -> Annex Bool perform key file backend numcopies = do@@ -173,11 +173,11 @@ ) dummymeter _ = noop -startKey :: Incremental -> Key -> NumCopies -> CommandStart-startKey inc key numcopies =+startKey :: Incremental -> Key -> ActionItem -> NumCopies -> CommandStart+startKey inc key ai numcopies = case Backend.maybeLookupBackendName (keyBackendName key) of Nothing -> stop- Just backend -> runFsck inc (key2file key) key $+ Just backend -> runFsck inc ai key $ performKey key backend numcopies performKey :: Key -> Backend -> NumCopies -> Annex Bool@@ -504,10 +504,10 @@ (False, True) -> "dropped from " ++ Remote.name remote (_, False) -> "failed to drop from" ++ Remote.name remote -runFsck :: Incremental -> FilePath -> Key -> Annex Bool -> CommandStart-runFsck inc file key a = ifM (needFsck inc key)+runFsck :: Incremental -> ActionItem -> Key -> Annex Bool -> CommandStart+runFsck inc ai key a = ifM (needFsck inc key) ( do- showStart "fsck" file+ showStart' "fsck" key ai next $ do ok <- a when ok $
Command/Get.hs view
@@ -34,7 +34,7 @@ <$> cmdParams desc <*> optional parseFromOption <*> parseAutoOption- <*> optional (parseKeyOptions True)+ <*> optional (parseIncompleteOption <|> parseKeyOptions <|> parseFailedTransfersOption) <*> parseBatchOption seek :: GetOptions -> CommandSeek@@ -49,17 +49,19 @@ (getFiles o) start :: GetOptions -> Maybe Remote -> FilePath -> Key -> CommandStart-start o from file key = start' expensivecheck from key (Just file)+start o from file key = start' expensivecheck from key afile (mkActionItem afile) where+ afile = Just file expensivecheck | autoMode o = numCopiesCheck file key (<) <||> wantGet False (Just key) (Just file) | otherwise = return True -startKeys :: Maybe Remote -> Key -> CommandStart-startKeys from key = start' (return True) from key Nothing+startKeys :: Maybe Remote -> Key -> ActionItem -> CommandStart+startKeys from key ai = checkFailedTransferDirection ai Download $+ start' (return True) from key Nothing ai -start' :: Annex Bool -> Maybe Remote -> Key -> AssociatedFile -> CommandStart-start' expensivecheck from key afile = stopUnless (not <$> inAnnex key) $+start' :: Annex Bool -> Maybe Remote -> Key -> AssociatedFile -> ActionItem -> CommandStart+start' expensivecheck from key afile ai = stopUnless (not <$> inAnnex key) $ stopUnless expensivecheck $ case from of Nothing -> go $ perform key afile@@ -68,7 +70,7 @@ go $ Command.Move.fromPerform src False key afile where go a = do- showStart' "get" key afile+ showStart' "get" key ai next a perform :: Key -> AssociatedFile -> CommandPerform@@ -106,7 +108,7 @@ either (const False) id <$> Remote.hasKey r key | otherwise = return True docopy r witness = getViaTmp (RemoteVerify r) key $ \dest ->- download (Remote.uuid r) key afile noRetry noObserver + download (Remote.uuid r) key afile noRetry (\p -> do showAction $ "from " ++ Remote.name r Remote.retrieveKeyFile r key afile dest p
Command/Info.hs view
@@ -11,8 +11,9 @@ import "mtl" Control.Monad.State.Strict import qualified Data.Map.Strict as M-import Text.JSON+import qualified Data.Text as T import Data.Ord+import Data.Aeson hiding (json) import Command import qualified Git@@ -29,12 +30,14 @@ import Annex.NumCopies import Remote import Config+import Git.Config (boolConfig) import Utility.Percentage+import Types.Transfer import Logs.Transfer import Types.TrustLevel import Types.FileMatcher import qualified Limit-import Messages.JSON (DualDisp(..))+import Messages.JSON (DualDisp(..), ObjectMap(..)) import Annex.BloomFilter import qualified Command.Unused @@ -219,6 +222,7 @@ [ file_name f , key_size k , key_name k+ , content_present k ] remote_fast_stats :: Remote -> [Stat]@@ -247,10 +251,10 @@ nostat :: Stat nostat = return Nothing -json :: JSON j => (j -> String) -> StatState j -> String -> StatState String+json :: ToJSON j => (j -> String) -> StatState j -> String -> StatState String json fmt a desc = do j <- a- lift $ maybeShowJSON [(desc, j)]+ lift $ maybeShowJSON $ JSONChunk [(desc, j)] return $ fmt j nojson :: StatState String -> String -> StatState String@@ -353,6 +357,9 @@ key_name :: Key -> Stat key_name k = simpleStat "key" $ pure $ key2file k +content_present :: Key -> Stat+content_present k = stat "present" $ json boolConfig $ lift $ inAnnex k+ bloom_info :: Stat bloom_info = simpleStat "bloom filter size" $ do localkeys <- countKeys <$> cachedPresentData@@ -374,7 +381,7 @@ transfer_list = stat desc $ nojson $ lift $ do uuidmap <- Remote.remoteMap id ts <- getTransfers- maybeShowJSON [(desc, map (uncurry jsonify) ts)]+ maybeShowJSON $ JSONChunk [(desc, map (uncurry jsonify) ts)] return $ if null ts then "none" else multiLine $@@ -388,11 +395,11 @@ , maybe (fromUUID $ transferUUID t) Remote.name $ M.lookup (transferUUID t) uuidmap ]- jsonify t i = toJSObject- [ ("transfer", showLcDirection (transferDirection t))- , ("key", key2file (transferKey t))- , ("file", fromMaybe "" (associatedFile i))- , ("remote", fromUUID (transferUUID t))+ jsonify t i = object $ map (\(k, v) -> (T.pack k, v)) $+ [ ("transfer", toJSON (showLcDirection (transferDirection t)))+ , ("key", toJSON (key2file (transferKey t)))+ , ("file", toJSON (associatedFile i))+ , ("remote", toJSON (fromUUID (transferUUID t))) ] disk_size :: Stat@@ -415,9 +422,9 @@ backend_usage :: Stat backend_usage = stat "backend usage" $ json fmt $- toJSObject . sort . M.toList . backendsKeys <$> cachedReferencedData+ ObjectMap . backendsKeys <$> cachedReferencedData where- fmt = multiLine . map (\(b, n) -> b ++ ": " ++ show n) . fromJSObject+ fmt = multiLine . map (\(b, n) -> b ++ ": " ++ show n) . sort . M.toList . fromObjectMap numcopies_stats :: Stat numcopies_stats = stat "numcopies stats" $ json fmt $
Command/MetaData.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2014 Joey Hess <id@joeyh.name>+ - Copyright 2014-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -}@@ -10,9 +10,16 @@ import Command import Annex.MetaData import Logs.MetaData+import Annex.WorkTree+import Messages.JSON (JSONActionItem(..))+import Types.Messages import qualified Data.Set as S+import qualified Data.Map as M+import qualified Data.Text as T+import qualified Data.ByteString.Lazy.UTF8 as BU import Data.Time.Clock.POSIX+import Data.Aeson cmd :: Command cmd = withGlobalOptions ([jsonOption] ++ annexedMatchingOptions) $ @@ -24,6 +31,7 @@ { forFiles :: CmdParams , getSet :: GetSet , keyOptions :: Maybe KeyOptions+ , batchOption :: BatchMode } data GetSet = Get MetaField | GetAll | Set [ModMeta]@@ -32,7 +40,8 @@ optParser desc = MetaDataOptions <$> cmdParams desc <*> ((Get <$> getopt) <|> (Set <$> some modopts) <|> pure GetAll)- <*> optional (parseKeyOptions False)+ <*> optional parseKeyOptions+ <*> parseBatchOption where getopt = option (eitherReader mkMetaField) ( long "get" <> short 'g' <> metavar paramField@@ -58,31 +67,36 @@ seek :: MetaDataOptions -> CommandSeek seek o = do now <- liftIO getPOSIXTime- let seeker = case getSet o of- Get _ -> withFilesInGit- GetAll -> withFilesInGit- Set _ -> withFilesInGitNonRecursive- "Not recursively setting metadata. Use --force to do that."- withKeyOptions (keyOptions o) False- (startKeys now o)- (seeker $ whenAnnexed $ start now o)- (forFiles o)+ case batchOption o of+ NoBatch -> do+ let seeker = case getSet o of+ Get _ -> withFilesInGit+ GetAll -> withFilesInGit+ Set _ -> withFilesInGitNonRecursive+ "Not recursively setting metadata. Use --force to do that."+ withKeyOptions (keyOptions o) False+ (startKeys now o)+ (seeker $ whenAnnexed $ start now o)+ (forFiles o)+ Batch -> withOutputType $ \ot -> case ot of+ JSONOutput -> batchInput parseJSONInput $+ commandAction . startBatch now+ _ -> error "--batch is currently only supported in --json mode" start :: POSIXTime -> MetaDataOptions -> FilePath -> Key -> CommandStart-start now o file = start' (Just file) now o--startKeys :: POSIXTime -> MetaDataOptions -> Key -> CommandStart-startKeys = start' Nothing+start now o file k = startKeys now o k (mkActionItem afile)+ where+ afile = Just file -start' :: AssociatedFile -> POSIXTime -> MetaDataOptions -> Key -> CommandStart-start' afile now o k = case getSet o of+startKeys :: POSIXTime -> MetaDataOptions -> Key -> ActionItem -> CommandStart+startKeys now o k ai = case getSet o of Get f -> do l <- S.toList . currentMetaDataValues f <$> getCurrentMetaData k liftIO $ forM_ l $ putStrLn . fromMetaValue stop _ -> do- showStart' "metadata" k afile+ showStart' "metadata" k ai next $ perform now o k perform :: POSIXTime -> MetaDataOptions -> Key -> CommandPerform@@ -96,10 +110,66 @@ cleanup :: Key -> CommandCleanup cleanup k = do- l <- map unwrapmeta . fromMetaData <$> getCurrentMetaData k- maybeShowJSON l- showLongNote $ unlines $ concatMap showmeta l+ m <- getCurrentMetaData k+ let Object o = toJSON (MetaDataFields m)+ maybeShowJSON $ AesonObject o+ showLongNote $ unlines $ concatMap showmeta $+ map unwrapmeta (fromMetaData m) return True where unwrapmeta (f, v) = (fromMetaField f, map fromMetaValue (S.toList v)) showmeta (f, vs) = map ((f ++ "=") ++) vs++-- Metadata serialized to JSON in the field named "fields" of+-- a larger object.+newtype MetaDataFields = MetaDataFields MetaData+ deriving (Show)++instance ToJSON MetaDataFields where+ toJSON (MetaDataFields m) = object [ (fieldsField, toJSON m) ]++instance FromJSON MetaDataFields where+ parseJSON (Object v) = do+ f <- v .: fieldsField+ case f of+ Nothing -> return (MetaDataFields emptyMetaData)+ Just v' -> MetaDataFields <$> parseJSON v'+ parseJSON _ = fail "expected an object"++fieldsField :: T.Text+fieldsField = T.pack "fields"++parseJSONInput :: String -> Either String (Either FilePath Key, MetaData)+parseJSONInput i = do+ v <- eitherDecode (BU.fromString i)+ let m = case itemAdded v of+ Nothing -> emptyMetaData+ Just (MetaDataFields m') -> m'+ case (itemKey v, itemFile v) of+ (Just k, _) -> Right (Right k, m)+ (Nothing, Just f) -> Right (Left f, m)+ (Nothing, Nothing) -> Left "JSON input is missing either file or key"++startBatch :: POSIXTime -> (Either FilePath Key, MetaData) -> CommandStart+startBatch now (i, (MetaData m)) = case i of+ Left f -> do+ mk <- lookupFile f+ case mk of+ Just k -> go k (mkActionItem (Just f))+ Nothing -> error $ "not an annexed file: " ++ f+ Right k -> go k (mkActionItem k)+ where+ go k ai = do+ showStart' "metadata" k ai+ let o = MetaDataOptions+ { forFiles = []+ , getSet = if MetaData m == emptyMetaData+ then GetAll+ else Set $ map mkModMeta (M.toList m)+ , keyOptions = Nothing+ , batchOption = NoBatch+ }+ next $ perform now o k+ mkModMeta (f, s)+ | S.null s = DelMeta f Nothing+ | otherwise = SetMeta f s
Command/Mirror.hs view
@@ -14,6 +14,7 @@ import qualified Remote import Annex.Content import Annex.NumCopies+import Types.Transfer cmd :: Command cmd = withGlobalOptions ([jobsOption] ++ annexedMatchingOptions) $@@ -31,7 +32,7 @@ optParser desc = MirrorOptions <$> cmdParams desc <*> parseFromToOptions- <*> optional (parseKeyOptions False)+ <*> optional (parseKeyOptions <|> parseFailedTransfersOption) instance DeferredParseClass MirrorOptions where finishParse v = MirrorOptions@@ -47,25 +48,27 @@ (mirrorFiles o) start :: MirrorOptions -> FilePath -> Key -> CommandStart-start o file = startKey o (Just file)+start o file k = startKey o afile k (mkActionItem afile)+ where+ afile = Just file -startKey :: MirrorOptions -> Maybe FilePath -> Key -> CommandStart-startKey o afile key = case fromToOptions o of- ToRemote r -> ifM (inAnnex key)- ( Command.Move.toStart False afile key =<< getParsed r+startKey :: MirrorOptions -> Maybe FilePath -> Key -> ActionItem -> CommandStart+startKey o afile key ai = case fromToOptions o of+ ToRemote r -> checkFailedTransferDirection ai Upload $ ifM (inAnnex key)+ ( Command.Move.toStart False afile key ai =<< getParsed r , do numcopies <- getnumcopies- Command.Drop.startRemote afile numcopies key =<< getParsed r+ Command.Drop.startRemote afile ai numcopies key =<< getParsed r )- FromRemote r -> do+ FromRemote r -> checkFailedTransferDirection ai Download $ do haskey <- flip Remote.hasKey key =<< getParsed r case haskey of Left _ -> stop- Right True -> Command.Get.start' (return True) Nothing key afile+ Right True -> Command.Get.start' (return True) Nothing key afile ai Right False -> ifM (inAnnex key) ( do numcopies <- getnumcopies- Command.Drop.startLocal afile numcopies key []+ Command.Drop.startLocal afile ai numcopies key [] , stop ) where
Command/Move.hs view
@@ -35,7 +35,7 @@ optParser desc = MoveOptions <$> cmdParams desc <*> parseFromToOptions- <*> optional (parseKeyOptions False)+ <*> optional (parseKeyOptions <|> parseFailedTransfersOption) instance DeferredParseClass MoveOptions where finishParse v = MoveOptions@@ -51,18 +51,22 @@ (moveFiles o) start :: MoveOptions -> Bool -> FilePath -> Key -> CommandStart-start o move = start' o move . Just+start o move f k = start' o move afile k (mkActionItem afile)+ where+ afile = Just f -startKey :: MoveOptions -> Bool -> Key -> CommandStart+startKey :: MoveOptions -> Bool -> Key -> ActionItem -> CommandStart startKey o move = start' o move Nothing -start' :: MoveOptions -> Bool -> AssociatedFile -> Key -> CommandStart-start' o move afile key = +start' :: MoveOptions -> Bool -> AssociatedFile -> Key -> ActionItem -> CommandStart+start' o move afile key ai = case fromToOptions o of- FromRemote src -> fromStart move afile key =<< getParsed src- ToRemote dest -> toStart move afile key =<< getParsed dest+ FromRemote src -> checkFailedTransferDirection ai Download $+ fromStart move afile key ai =<< getParsed src+ ToRemote dest -> checkFailedTransferDirection ai Upload $+ toStart move afile key ai =<< getParsed dest -showMoveAction :: Bool -> Key -> AssociatedFile -> Annex ()+showMoveAction :: Bool -> Key -> ActionItem -> Annex () showMoveAction move = showStart' (if move then "move" else "copy") {- Moves (or copies) the content of an annexed file to a remote.@@ -74,16 +78,16 @@ - A file's content can be moved even if there are insufficient copies to - allow it to be dropped. -}-toStart :: Bool -> AssociatedFile -> Key -> Remote -> CommandStart-toStart move afile key dest = do+toStart :: Bool -> AssociatedFile -> Key -> ActionItem -> Remote -> CommandStart+toStart move afile key ai dest = do u <- getUUID ishere <- inAnnex key if not ishere || u == Remote.uuid dest then stop -- not here, so nothing to do- else toStart' dest move afile key+ else toStart' dest move afile key ai -toStart' :: Remote -> Bool -> AssociatedFile -> Key -> CommandStart-toStart' dest move afile key = do+toStart' :: Remote -> Bool -> AssociatedFile -> Key -> ActionItem -> CommandStart+toStart' dest move afile key ai = do fast <- Annex.getState Annex.fast if fast && not move && not (Remote.hasKeyCheap dest) then ifM (expectedPresent dest key)@@ -93,7 +97,7 @@ else go False (Remote.hasKey dest key) where go fastcheck isthere = do- showMoveAction move key afile+ showMoveAction move key ai next $ toPerform dest move key afile fastcheck =<< isthere expectedPresent :: Remote -> Key -> Annex Bool@@ -110,7 +114,7 @@ Right False -> do showAction $ "to " ++ Remote.name dest ok <- notifyTransfer Upload afile $- upload (Remote.uuid dest) key afile noRetry noObserver $+ upload (Remote.uuid dest) key afile noRetry $ Remote.storeKey dest key afile if ok then finish $@@ -143,13 +147,13 @@ - If the current repository already has the content, it is still removed - from the remote. -}-fromStart :: Bool -> AssociatedFile -> Key -> Remote -> CommandStart-fromStart move afile key src+fromStart :: Bool -> AssociatedFile -> Key -> ActionItem -> Remote -> CommandStart+fromStart move afile key ai src | move = go | otherwise = stopUnless (not <$> inAnnex key) go where go = stopUnless (fromOk src key) $ do- showMoveAction move key afile+ showMoveAction move key ai next $ fromPerform src move key afile fromOk :: Remote -> Key -> Annex Bool@@ -173,7 +177,7 @@ ) where go = notifyTransfer Download afile $ - download (Remote.uuid src) key afile noRetry noObserver $ \p -> do+ download (Remote.uuid src) key afile noRetry $ \p -> do showAction $ "from " ++ Remote.name src getViaTmp (RemoteVerify src) key $ \t -> Remote.retrieveKeyFile src key afile t p
Command/RecvKey.hs view
@@ -12,7 +12,7 @@ import Annex.Action import Annex import Utility.Rsync-import Logs.Transfer+import Types.Transfer import Command.SendKey (fieldTransfer) import qualified CmdLine.GitAnnexShell.Fields as Fields
Command/SendKey.hs view
@@ -48,7 +48,7 @@ liftIO $ debugM "fieldTransfer" "transfer start" afile <- Fields.getField Fields.associatedFile ok <- maybe (a $ const noop)- (\u -> runner (Transfer direction (toUUID u) key) afile noRetry noObserver a)+ (\u -> runner (Transfer direction (toUUID u) key) afile noRetry a) =<< Fields.getField Fields.remoteUUID liftIO $ debugM "fieldTransfer" "transfer done" liftIO $ exitBool ok
Command/SetPresentKey.hs view
@@ -23,7 +23,7 @@ start :: [String] -> CommandStart start (ks:us:vs:[]) = do- showStart' "setpresentkey" k Nothing+ showStart' "setpresentkey" k (mkActionItem k) next $ perform k (toUUID us) s where k = fromMaybe (error "bad key") (file2key ks)
Command/Status.hs view
@@ -43,7 +43,7 @@ let c = statusChar s absf <- fromRepo $ fromTopFilePath (statusFile s) f <- liftIO $ relPathCwdToFile absf- unlessM (showFullJSON [("status", [c]), ("file", f)]) $+ unlessM (showFullJSON $ JSONChunk [("status", [c]), ("file", f)]) $ liftIO $ putStrLn $ [c] ++ " " ++ f -- Git thinks that present direct mode files are typechanged.
Command/Sync.hs view
@@ -441,7 +441,7 @@ Just WantAllKeys -> Just <$> genBloomFilter (seekworktree mvar []) _ -> seekworktree mvar [] (const noop) >> pure Nothing withKeyOptions' (keyOptions o) False- (seekkeys mvar bloom) + (return (seekkeys mvar bloom)) (const noop) [] finishCommandActions@@ -449,8 +449,7 @@ where seekworktree mvar l bloomfeeder = seekHelper LsFiles.inRepo l >>= mapM_ (\f -> ifAnnexed f (go (Right bloomfeeder) mvar (Just f)) noop)- seekkeys mvar bloom getkeys =- mapM_ (go (Left bloom) mvar Nothing) =<< getkeys+ seekkeys mvar bloom k _ = go (Left bloom) mvar Nothing k go ebloom mvar af k = commandAction $ do whenM (syncFile ebloom rs af k) $ void $ liftIO $ tryPutMVar mvar ()@@ -512,7 +511,7 @@ , return [] ) get have = includeCommandAction $ do- showStart' "get" k af+ showStart' "get" k (mkActionItem af) next $ next $ getKey' k af have wantput r@@ -527,4 +526,4 @@ , return [] ) put dest = includeCommandAction $ - Command.Move.toStart' dest False af k+ Command.Move.toStart' dest False af k (mkActionItem af)
Command/TransferInfo.hs view
@@ -9,6 +9,7 @@ import Command import Annex.Content+import Types.Transfer import Logs.Transfer import qualified CmdLine.GitAnnexShell.Fields as Fields import Utility.Metered
Command/TransferKey.hs view
@@ -51,7 +51,7 @@ toPerform :: Key -> AssociatedFile -> Remote -> CommandPerform toPerform key file remote = go Upload file $- upload (uuid remote) key file forwardRetry noObserver $ \p -> do+ upload (uuid remote) key file forwardRetry $ \p -> do ok <- Remote.storeKey remote key file p when ok $ Remote.logStatus remote key InfoPresent@@ -59,7 +59,7 @@ fromPerform :: Key -> AssociatedFile -> Remote -> CommandPerform fromPerform key file remote = go Upload file $- download (uuid remote) key file forwardRetry noObserver $ \p ->+ download (uuid remote) key file forwardRetry $ \p -> getViaTmp (RemoteVerify remote) key $ \t -> Remote.retrieveKeyFile remote key file t p
Command/TransferKeys.hs view
@@ -35,13 +35,13 @@ where runner (TransferRequest direction remote key file) | direction == Upload = notifyTransfer direction file $- upload (Remote.uuid remote) key file forwardRetry observer $ \p -> do+ upload (Remote.uuid remote) key file forwardRetry $ \p -> do ok <- Remote.storeKey remote key file p when ok $ Remote.logStatus remote key InfoPresent return ok | otherwise = notifyTransfer direction file $- download (Remote.uuid remote) key file forwardRetry observer $ \p ->+ download (Remote.uuid remote) key file forwardRetry $ \p -> getViaTmp (RemoteVerify remote) key $ \t -> do r <- Remote.retrieveKeyFile remote key file t p -- Make sure we get the current@@ -49,9 +49,6 @@ -- not old cached data. Database.Keys.closeDb return r- - observer False t tinfo = recordFailedTransfer t tinfo- observer True _ _ = noop runRequests :: Handle
Command/Whereis.hs view
@@ -31,7 +31,7 @@ optParser :: CmdParamsDesc -> Parser WhereisOptions optParser desc = WhereisOptions <$> cmdParams desc- <*> optional (parseKeyOptions False)+ <*> optional parseKeyOptions <*> parseBatchOption seek :: WhereisOptions -> CommandSeek@@ -47,14 +47,13 @@ (whereisFiles o) start :: M.Map UUID Remote -> FilePath -> Key -> CommandStart-start remotemap file key = start' remotemap key (Just file)--startKeys :: M.Map UUID Remote -> Key -> CommandStart-startKeys remotemap key = start' remotemap key Nothing+start remotemap file key = startKeys remotemap key (mkActionItem afile)+ where+ afile = Just file -start' :: M.Map UUID Remote -> Key -> AssociatedFile -> CommandStart-start' remotemap key afile = do- showStart' "whereis" key afile+startKeys :: M.Map UUID Remote -> Key -> ActionItem -> CommandStart+startKeys remotemap key ai = do+ showStart' "whereis" key ai next $ perform remotemap key perform :: M.Map UUID Remote -> Key -> CommandPerform
Git/FilePath.hs view
@@ -14,6 +14,8 @@ module Git.FilePath ( TopFilePath,+ BranchFilePath(..),+ descBranchFilePath, getTopFilePath, fromTopFilePath, toTopFilePath,@@ -32,6 +34,13 @@ {- A FilePath, relative to the top of the git repository. -} newtype TopFilePath = TopFilePath { getTopFilePath :: FilePath } deriving (Show, Eq, Ord)++{- A file in a branch or other treeish. -}+data BranchFilePath = BranchFilePath Ref TopFilePath++{- Git uses the branch:file form to refer to a BranchFilePath -}+descBranchFilePath :: BranchFilePath -> String+descBranchFilePath (BranchFilePath b f) = fromRef b ++ ':' : getTopFilePath f {- Path to a TopFilePath, within the provided git repo. -} fromTopFilePath :: TopFilePath -> Git.Repo -> FilePath
Logs/Transfer.hs view
@@ -9,6 +9,7 @@ module Logs.Transfer where +import Types.Transfer import Annex.Common import Annex.Perms import qualified Git@@ -22,38 +23,6 @@ import Data.Time.Clock import Data.Time.Clock.POSIX import Control.Concurrent--{- Enough information to uniquely identify a transfer, used as the filename- - of the transfer information file. -}-data Transfer = Transfer- { transferDirection :: Direction- , transferUUID :: UUID- , transferKey :: Key- }- deriving (Eq, Ord, Read, Show)--{- Information about a Transfer, stored in the transfer information file.- -- - Note that the associatedFile may not correspond to a file in the local- - git repository. It's some file, possibly relative to some directory,- - of some repository, that was acted on to initiate the transfer.- -}-data TransferInfo = TransferInfo- { startedTime :: Maybe POSIXTime- , transferPid :: Maybe PID- , transferTid :: Maybe ThreadId- , transferRemote :: Maybe Remote- , bytesComplete :: Maybe Integer- , associatedFile :: Maybe FilePath- , transferPaused :: Bool- }- deriving (Show, Eq, Ord)--stubTransferInfo :: TransferInfo-stubTransferInfo = TransferInfo Nothing Nothing Nothing Nothing Nothing Nothing False--data Direction = Upload | Download- deriving (Eq, Ord, Read, Show) showLcDirection :: Direction -> String showLcDirection Upload = "upload"
Messages.hs view
@@ -1,12 +1,14 @@ {- git-annex output messages -- - Copyright 2010-2014 Joey Hess <id@joeyh.name>+ - Copyright 2010-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -} module Messages ( showStart,+ ActionItem,+ mkActionItem, showStart', showNote, showAction,@@ -25,6 +27,7 @@ earlyWarning, warningIO, indent,+ JSONChunk(..), maybeShowJSON, showFullJSON, showCustom,@@ -37,9 +40,9 @@ commandProgressDisabled, outputMessage, implicitMessage,+ withOutputType, ) where -import Text.JSON import System.Log.Logger import System.Log.Formatter import System.Log.Handler (setFormatter)@@ -48,18 +51,23 @@ import Common import Types import Types.Messages+import Types.ActionItem import Messages.Internal import qualified Messages.JSON as JSON-import Types.Key+import Utility.JSONStream (JSONChunk(..)) import qualified Annex showStart :: String -> FilePath -> Annex ()-showStart command file = outputMessage (JSON.start command (Just file) Nothing) $+showStart command file = outputMessage json $ command ++ " " ++ file ++ " "+ where+ json = JSON.start command (Just file) Nothing -showStart' :: String -> Key -> Maybe FilePath -> Annex ()-showStart' command key afile = outputMessage (JSON.start command afile (Just key)) $- command ++ " " ++ fromMaybe (key2file key) afile ++ " "+showStart' :: String -> Key -> ActionItem -> Annex ()+showStart' command key i = outputMessage json $+ command ++ " " ++ actionItemDesc i key ++ " "+ where+ json = JSON.start command (actionItemWorkTreeFile i) (Just key) showNote :: String -> Annex () showNote s = outputMessage (JSON.note s) $ "(" ++ s ++ ") "@@ -145,15 +153,15 @@ indent :: String -> String indent = intercalate "\n" . map (\l -> " " ++ l) . lines -{- Shows a JSON fragment only when in json mode. -}-maybeShowJSON :: JSON a => [(String, a)] -> Annex ()+{- Shows a JSON chunk only when in json mode. -}+maybeShowJSON :: JSONChunk v -> Annex () maybeShowJSON v = withOutputType $ liftIO . go where go JSONOutput = JSON.add v go _ = return () {- Shows a complete JSON value, only when in json mode. -}-showFullJSON :: JSON a => [(String, a)] -> Annex Bool+showFullJSON :: JSONChunk v -> Annex Bool showFullJSON v = withOutputType $ liftIO . go where go JSONOutput = JSON.complete v >> return True
Messages/JSON.hs view
@@ -1,10 +1,12 @@-{- git-annex JSON output+{- git-annex command-line JSON output and input -- - Copyright 2011 Joey Hess <id@joeyh.name>+ - Copyright 2011-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -} +{-# LANGUAGE OverloadedStrings #-}+ module Messages.JSON ( start, end,@@ -12,35 +14,43 @@ add, complete, DualDisp(..),+ ObjectMap(..),+ JSONActionItem(..), ) where -import Text.JSON+import Data.Aeson+import Control.Applicative+import qualified Data.Map as M+import qualified Data.Text as T+import qualified Data.ByteString.Lazy as B+import System.IO+import Data.Monoid+import Prelude import qualified Utility.JSONStream as Stream import Types.Key-import Data.Maybe start :: String -> Maybe FilePath -> Maybe Key -> IO ()-start command file key = putStr $ Stream.start $ catMaybes- [ part "command" (Just command)- , part "file" file- , part "key" (fmap key2file key)- ]+start command file key = B.hPut stdout $ Stream.start $ Stream.AesonObject o where- part _ Nothing = Nothing- part l (Just v) = Just (l, v)+ Object o = toJSON $ JSONActionItem+ { itemCommand = Just command+ , itemKey = key+ , itemFile = file+ , itemAdded = Nothing+ } end :: Bool -> IO ()-end b = putStr $ Stream.add [("success", b)] ++ Stream.end+end b = B.hPut stdout $ Stream.add (Stream.JSONChunk [("success", b)]) `B.append` Stream.end note :: String -> IO ()-note s = add [("note", s)]+note s = add (Stream.JSONChunk [("note", s)]) -add :: JSON a => [(String, a)] -> IO ()-add v = putStr $ Stream.add v+add :: Stream.JSONChunk a -> IO ()+add = B.hPut stdout . Stream.add -complete :: JSON a => [(String, a)] -> IO ()-complete v = putStr $ Stream.start v ++ Stream.end+complete :: Stream.JSONChunk a -> IO ()+complete v = B.hPut stdout $ Stream.start v `B.append` Stream.end -- A value that can be displayed either normally, or as JSON. data DualDisp = DualDisp@@ -48,9 +58,45 @@ , dispJson :: String } -instance JSON DualDisp where- showJSON = JSString . toJSString . dispJson- readJSON _ = Error "stub"+instance ToJSON DualDisp where+ toJSON = toJSON . dispJson instance Show DualDisp where show = dispNormal++-- A Map that is serialized to JSON as an object, with each key being a+-- field of the object. This is different from Aeson's normal +-- serialization of Map, which uses "[key, value]".+data ObjectMap a = ObjectMap { fromObjectMap :: M.Map String a }++instance ToJSON a => ToJSON (ObjectMap a) where+ toJSON (ObjectMap m) = object $ map go $ M.toList m+ where+ go (k, v) = (T.pack k, toJSON v)++-- An item that a git-annex command acts on, and displays a JSON object about.+data JSONActionItem a = JSONActionItem+ { itemCommand :: Maybe String+ , itemKey :: Maybe Key+ , itemFile :: Maybe FilePath+ , itemAdded :: Maybe a -- for additional fields added by `add`+ }+ deriving (Show)++instance ToJSON (JSONActionItem a) where+ toJSON i = object+ [ "command" .= itemCommand i+ , "key" .= (toJSON (itemKey i))+ , "file" .= itemFile i+ -- itemAdded is not included; must be added later by 'add'+ ]++instance FromJSON a => FromJSON (JSONActionItem a) where+ parseJSON (Object v) = JSONActionItem+ <$> (v .:? "command")+ <*> (maybe (return Nothing) parseJSON =<< (v .:? "key"))+ <*> (v .:? "file")+ <*> parseadded+ where+ parseadded = (Just <$> parseJSON (Object v)) <|> return Nothing+ parseJSON _ = mempty
Remote.hs view
@@ -55,10 +55,10 @@ claimingUrl, ) where -import qualified Data.Map as M-import Text.JSON-import Text.JSON.Generic import Data.Ord+import Data.Aeson+import qualified Data.Map as M+import qualified Data.Text as T import Annex.Common import Types.Remote@@ -194,7 +194,7 @@ {- An optional field can be included in the list of UUIDs. -} prettyPrintUUIDsWith- :: JSON v+ :: ToJSON v => Maybe String -> String -> M.Map UUID RemoteName@@ -203,7 +203,7 @@ -> Annex String prettyPrintUUIDsWith optfield header descm showval uuidvals = do hereu <- getUUID- maybeShowJSON [(header, map (jsonify hereu) uuidvals)]+ maybeShowJSON $ JSONChunk [(header, map (jsonify hereu) uuidvals)] return $ unwords $ map (\u -> "\t" ++ prettify hereu u ++ "\n") uuidvals where finddescription u = M.findWithDefault "" u descm@@ -220,12 +220,12 @@ addoptval s = case showval =<< optval of Nothing -> s Just val -> val ++ ": " ++ s- jsonify hereu (u, optval) = toJSObject $ catMaybes- [ Just ("uuid", toJSON $ fromUUID u)- , Just ("description", toJSON $ finddescription u)- , Just ("here", toJSON $ hereu == u)+ jsonify hereu (u, optval) = object $ catMaybes+ [ Just (T.pack "uuid", toJSON $ fromUUID u)+ , Just (T.pack "description", toJSON $ finddescription u)+ , Just (T.pack "here", toJSON $ hereu == u) , case (optfield, optval) of- (Just field, Just val) -> Just (field, showJSON val)+ (Just field, Just val) -> Just (T.pack field, toJSON val) _ -> Nothing ]
Remote/External.hs view
@@ -22,7 +22,7 @@ import Remote.Helper.Messages import Utility.Metered import Messages.Progress-import Logs.Transfer+import Types.Transfer import Logs.PreferredContent.Raw import Logs.RemoteState import Logs.Web
Remote/External/Types.hs view
@@ -34,7 +34,7 @@ import Annex.Common import Types.StandardGroups (PreferredContentExpression) import Utility.Metered (BytesProcessed(..))-import Logs.Transfer (Direction(..))+import Types.Transfer (Direction(..)) import Config.Cost (Cost) import Types.Remote (RemoteConfig) import Types.Availability (Availability(..))
Remote/GCrypt.hs view
@@ -25,6 +25,7 @@ import Types.GitConfig import Types.Crypto import Types.Creds+import Types.Transfer import qualified Git import qualified Git.Command import qualified Git.Config@@ -47,7 +48,6 @@ import Utility.Rsync import Utility.Tmp import Logs.Remote-import Logs.Transfer import Utility.Gpg remote :: RemoteType
Remote/Git.hs view
@@ -439,7 +439,7 @@ Just (object, checksuccess) -> do copier <- mkCopier hardlink params runTransfer (Transfer Download u key)- file noRetry noObserver + file noRetry (\p -> copier object dest (combineMeterUpdate p meterupdate) checksuccess) | Git.repoIsSsh (repo r) = unVerified $ feedprogressback $ \p -> do Ssh.rsyncHelper (Just (combineMeterUpdate meterupdate p))@@ -565,7 +565,7 @@ ensureInitialized copier <- mkCopier hardlink params let verify = Annex.Content.RemoteVerify r- runTransfer (Transfer Download u key) file noRetry noObserver $ \p ->+ runTransfer (Transfer Download u key) file noRetry $ \p -> let p' = combineMeterUpdate meterupdate p in Annex.Content.saveState True `after` Annex.Content.getViaTmp verify key
Remote/Helper/Ssh.hs view
@@ -20,7 +20,7 @@ import Utility.Metered import Utility.Rsync import Types.Remote-import Logs.Transfer+import Types.Transfer import Config {- Generates parameters to ssh to a repository's host and run a command.
Remote/Rsync.hs view
@@ -34,7 +34,7 @@ import Utility.CopyFile import Messages.Progress import Utility.Metered-import Logs.Transfer+import Types.Transfer import Types.Creds import Annex.DirHashes import Utility.Tmp
Test.hs view
@@ -32,7 +32,8 @@ import Options.Applicative (switch, long, help) import qualified Data.Map as M-import qualified Text.JSON+import qualified Data.Aeson+import qualified Data.ByteString.Lazy.UTF8 as BU8 import Common @@ -924,10 +925,10 @@ test_info :: Assertion test_info = intmpclonerepo $ do- json <- git_annex_output "info" ["--json"]- case Text.JSON.decodeStrict json :: Text.JSON.Result (Text.JSON.JSObject Text.JSON.JSValue) of- Text.JSON.Ok _ -> return ()- Text.JSON.Error e -> assertFailure e+ json <- BU8.fromString <$> git_annex_output "info" ["--json"]+ case Data.Aeson.eitherDecode json :: Either String Data.Aeson.Value of+ Right _ -> return ()+ Left e -> assertFailure e test_version :: Assertion test_version = intmpclonerepo $
+ Types/ActionItem.hs view
@@ -0,0 +1,54 @@+{- items that a command can act on+ -+ - Copyright 2016 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}++module Types.ActionItem where++import Types.Key+import Types.Transfer+import Git.FilePath++import Data.Maybe++data ActionItem + = ActionItemAssociatedFile AssociatedFile+ | ActionItemKey+ | ActionItemBranchFilePath BranchFilePath+ | ActionItemFailedTransfer Transfer TransferInfo++class MkActionItem t where+ mkActionItem :: t -> ActionItem++instance MkActionItem AssociatedFile where+ mkActionItem = ActionItemAssociatedFile++instance MkActionItem Key where+ mkActionItem _ = ActionItemKey++instance MkActionItem BranchFilePath where+ mkActionItem = ActionItemBranchFilePath++instance MkActionItem (Transfer, TransferInfo) where+ mkActionItem = uncurry ActionItemFailedTransfer++actionItemDesc :: ActionItem -> Key -> String+actionItemDesc (ActionItemAssociatedFile (Just f)) _ = f+actionItemDesc (ActionItemAssociatedFile Nothing) k = key2file k+actionItemDesc ActionItemKey k = key2file k+actionItemDesc (ActionItemBranchFilePath bfp) _ = descBranchFilePath bfp+actionItemDesc (ActionItemFailedTransfer _ i) k = + fromMaybe (key2file k) (associatedFile i)++actionItemWorkTreeFile :: ActionItem -> Maybe FilePath+actionItemWorkTreeFile (ActionItemAssociatedFile af) = af+actionItemWorkTreeFile _ = Nothing++actionItemTransferDirection :: ActionItem -> Maybe Direction+actionItemTransferDirection (ActionItemFailedTransfer t _) = Just $+ transferDirection t+actionItemTransferDirection _ = Nothing
Types/Key.hs view
@@ -1,6 +1,6 @@ {- git-annex Key data type -- - Copyright 2011-2014 Joey Hess <id@joeyh.name>+ - Copyright 2011-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -}@@ -21,6 +21,8 @@ ) where import System.Posix.Types+import Data.Aeson+import qualified Data.Text as T import Common import Utility.QuickCheck@@ -119,6 +121,13 @@ return $ k { keyChunkNum = Just chunknum } _ -> return k addfield _ _ _ = Nothing++instance ToJSON Key where+ toJSON = toJSON . key2file++instance FromJSON Key where+ parseJSON (String t) = maybe mempty pure $ file2key $ T.unpack t+ parseJSON _ = mempty instance Arbitrary Key where arbitrary = Key
Types/MetaData.hs view
@@ -44,14 +44,32 @@ import Utility.Base64 import Utility.QuickCheck +import qualified Data.Text as T import qualified Data.Set as S import qualified Data.Map as M+import qualified Data.HashMap.Strict as HM import Data.Char import qualified Data.CaseInsensitive as CI+import Data.Aeson newtype MetaData = MetaData (M.Map MetaField (S.Set MetaValue)) deriving (Show, Eq, Ord) +instance ToJSON MetaData where+ toJSON (MetaData m) = object $ map go (M.toList m)+ where+ go (MetaField f, s) = (T.pack (CI.original f), toJSON s)++instance FromJSON MetaData where+ parseJSON (Object o) = do+ l <- HM.toList <$> parseJSON (Object o)+ MetaData . M.fromList <$> mapM go l+ where+ go (t, l) = case mkMetaField (T.unpack t) of+ Left e -> fail e+ Right f -> (,) <$> pure f <*> parseJSON l+ parseJSON _ = fail "expected an object"+ {- A metadata value can be currently be set (True), or may have been - set before and we're remembering it no longer is (False). -} newtype CurrentlySet = CurrentlySet Bool@@ -64,6 +82,13 @@ data MetaValue = MetaValue CurrentlySet String deriving (Read, Show) +instance ToJSON MetaValue where+ toJSON (MetaValue _ v) = toJSON v++instance FromJSON MetaValue where+ parseJSON (String v) = return $ MetaValue (CurrentlySet True) (T.unpack v)+ parseJSON _ = fail "expected a string"+ {- Metadata values compare and order the same whether currently set or not. -} instance Eq MetaValue where MetaValue _ a == MetaValue _ b = a == b@@ -179,9 +204,12 @@ {- Can be used to set a value, or to unset it, depending on whether - the MetaValue has CurrentlySet or not. -} updateMetaData :: MetaField -> MetaValue -> MetaData -> MetaData-updateMetaData f v (MetaData m) = MetaData $- M.insertWith' S.union f (S.singleton v) m+updateMetaData f v = updateMetaData' f (S.singleton v) +updateMetaData' :: MetaField -> S.Set MetaValue -> MetaData -> MetaData+updateMetaData' f s (MetaData m) = MetaData $+ M.insertWith' S.union f s m+ {- New metadata overrides old._-} unionMetaData :: MetaData -> MetaData -> MetaData unionMetaData (MetaData old) (MetaData new) = MetaData $@@ -222,7 +250,7 @@ | DelMeta MetaField (Maybe MetaValue) -- ^ delete value of a field. With Just, only that specific value -- is deleted; with Nothing, all current values are deleted.- | SetMeta MetaField MetaValue+ | SetMeta MetaField (S.Set MetaValue) -- ^ removes any existing values | MaybeSetMeta MetaField MetaValue -- ^ set when field has no existing value@@ -237,7 +265,7 @@ updateMetaData f (unsetMetaValue oldv) emptyMetaData modMeta m (DelMeta f Nothing) = MetaData $ M.singleton f $ S.fromList $ map unsetMetaValue $ S.toList $ currentMetaDataValues f m-modMeta m (SetMeta f v) = updateMetaData f v $+modMeta m (SetMeta f s) = updateMetaData' f s $ foldr (updateMetaData f) emptyMetaData $ map unsetMetaValue $ S.toList $ currentMetaDataValues f m modMeta m (MaybeSetMeta f v)
+ Types/Transfer.hs view
@@ -0,0 +1,47 @@+{- git-annex transfer types+ -+ - Copyright 2012 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Types.Transfer where++import Types+import Utility.PID++import Data.Time.Clock.POSIX+import Control.Concurrent++{- Enough information to uniquely identify a transfer, used as the filename+ - of the transfer information file. -}+data Transfer = Transfer+ { transferDirection :: Direction+ , transferUUID :: UUID+ , transferKey :: Key+ }+ deriving (Eq, Ord, Read, Show)++{- Information about a Transfer, stored in the transfer information file.+ -+ - Note that the associatedFile may not correspond to a file in the local+ - git repository. It's some file, possibly relative to some directory,+ - of some repository, that was acted on to initiate the transfer.+ -}+data TransferInfo = TransferInfo+ { startedTime :: Maybe POSIXTime+ , transferPid :: Maybe PID+ , transferTid :: Maybe ThreadId+ , transferRemote :: Maybe Remote+ , bytesComplete :: Maybe Integer+ , associatedFile :: Maybe FilePath+ , transferPaused :: Bool+ }+ deriving (Show, Eq, Ord)++stubTransferInfo :: TransferInfo+stubTransferInfo = TransferInfo Nothing Nothing Nothing Nothing Nothing Nothing False++data Direction = Upload | Download+ deriving (Eq, Ord, Read, Show)+
Utility/JSONStream.hs view
@@ -1,44 +1,68 @@ {- Streaming JSON output. -- - Copyright 2011 Joey Hess <id@joeyh.name>+ - Copyright 2011, 2016 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -} +{-# LANGUAGE GADTs #-}+ module Utility.JSONStream (+ JSONChunk(..), start, add, end ) where -import Text.JSON+import Data.Aeson+import qualified Data.Text as T+import qualified Data.ByteString.Lazy as B+import Data.Char+import Data.Word -{- Text.JSON does not support building up a larger JSON document piece by- - piece as a stream. To support streaming, a hack. The JSObject is converted- - to a string with its final "}" is left off, allowing it to be added to- - later. -}-start :: JSON a => [(String, a)] -> String-start l- | last s == endchar = init s- | otherwise = bad s+data JSONChunk v where+ JSONChunk :: ToJSON v => [(String, v)] -> JSONChunk [(String, v)]+ AesonObject :: Object -> JSONChunk Object++encodeJSONChunk :: JSONChunk v -> B.ByteString+encodeJSONChunk (JSONChunk l) = encode $ object $ map mkPair l where- s = encodeStrict $ toJSObject l+ mkPair (s, v) = (T.pack s, toJSON v)+encodeJSONChunk (AesonObject o) = encode o -add :: JSON a => [(String, a)] -> String-add l- | head s == startchar = ',' : drop 1 s- | otherwise = bad s+{- Aeson does not support building up a larger JSON object piece by piece+ - with streaming output. To support streaming, a hack:+ - The final "}" is left off the JSON, allowing more chunks to be added+ - to later. -}+start :: JSONChunk a -> B.ByteString+start a+ | not (B.null b) && B.last b == endchar = B.init b+ | otherwise = bad b where- s = start l+ b = encodeJSONChunk a -end :: String-end = [endchar, '\n']+add :: JSONChunk a -> B.ByteString+add a+ | not (B.null b) && B.head b == startchar = B.cons addchar (B.drop 1 b)+ | otherwise = bad b+ where+ b = start a -startchar :: Char-startchar = '{'+end :: B.ByteString+end = endchar `B.cons` sepchar `B.cons` B.empty -endchar :: Char-endchar = '}'+startchar :: Word8+startchar = fromIntegral (ord '{') -bad :: String -> a-bad s = error $ "Text.JSON returned unexpected string: " ++ s+endchar :: Word8+endchar = fromIntegral (ord '}')++addchar :: Word8+addchar = fromIntegral (ord ',')++sepchar :: Word8+sepchar = fromIntegral (ord '\n')++bad :: B.ByteString -> a+bad b = error $ "JSON encoder generated unexpected value: " ++ show b+
doc/git-annex-copy.mdwn view
@@ -51,9 +51,17 @@ This is the default behavior when running git-annex in a bare repository. +* `--branch=ref`++ Operate on files in the specified branch or treeish.+ * `--unused` Operate on files found by last run of git-annex unused.++* `--failed`++ Operate on files that have recently failed to be transferred. * `--key=keyname`
doc/git-annex-drop.mdwn view
@@ -42,13 +42,29 @@ This is the default behavior when running git-annex drop in a bare repository. + Note that this bypasses checking the .gitattributes annex.numcopies+ setting.++* `--branch=ref`++ Drop files in the specified branch or treeish.++ Note that this bypasses checking the .gitattributes annex.numcopies+ setting.+ * `--unused` Drop files found by last run of git-annex unused. + Note that this bypasses checking the .gitattributes annex.numcopies+ setting.+ * `--key=keyname` Use this option to drop a specified key.++ Note that this bypasses checking the .gitattributes annex.numcopies+ setting. * file matching options
doc/git-annex-fsck.mdwn view
@@ -72,6 +72,10 @@ This is the default behavior when running git-annex in a bare repository. +* `--branch=ref`++ Operate on files in the specified branch or treeish.+ * `--unused` Operate on files found by last run of git-annex unused.
doc/git-annex-get.mdwn view
@@ -32,6 +32,11 @@ Enables parallel download with up to the specified number of jobs running at once. For example: `-J10` +* file matching options+ + The [[git-annex-matching-options]](1)+ can be used to specify files to get.+ * `--incomplete` Resume any incomplete downloads of files that were started and@@ -45,11 +50,6 @@ as git-annex does not know the associated file, and the associated file may not even be in the current git working directory. -* file matching options- - The [[git-annex-matching-options]](1)- can be used to specify files to get.- * `--all` Rather than specifying a filename or path to get, this option can be@@ -57,9 +57,20 @@ This is the default behavior when running git-annex in a bare repository. +* `--branch=ref`++ Operate on files in the specified branch or treeish.+ * `--unused` Operate on files found by last run of git-annex unused.++* `--failed`++ Operate on files that have recently failed to be transferred.++ Not to be confused with `--incomplete` which resumes only downloads+ that managed to transfer part of the content of a file. * `--key=keyname`
doc/git-annex-metadata.mdwn view
@@ -17,6 +17,11 @@ When run without any -s or -t parameters, displays the current metadata. +Each metadata field has its own "field-lastchanged" metadata, which+contains the date the field was last changed. Unlike other metadata,+this cannot be directly modified by this command. It is updated+automatically.+ # OPTIONS * `-g field` / `--get field`@@ -66,15 +71,15 @@ The [[git-annex-matching-options]](1) can be used to specify files to act on. -* `--json`-- Enable JSON output. This is intended to be parsed by programs that use- git-annex. Each line of output is a JSON object.- * `--all` Specify instead of a file to get/set metadata on all known keys. +* `--branch=ref`++ Specify instead of a file to get/set metadata on all files in the+ specified branch or treeish.+ * `--unused` Specify instead of a file to get/set metadata on@@ -83,6 +88,55 @@ * `--key=keyname` Specify instead of a file to get/set metadata of the specified key.++* `--json`++ Enable JSON output (and input). Each line is a JSON object.++ The format of the JSON objects changed in git-annex version 6.20160726.++ Example of the new format:++ {"command":"metadata","file":"foo","key":"...","fields":{"author":["bar"],...},"note":"...","success":true}++ Example of the old format, which lacks the inner fields object:++ {"command":"metadata","file":"foo","key":"...","author":["bar"],...,"note":"...","success":true}++* `--batch`++ Enables batch mode, which can be used to both get, store, and unset+ metadata for multiple files or keys.++ Batch currently only supports JSON input. So, you must+ enable `--json` along with `--batch`.++ In batch mode, git-annex reads lines from stdin, which contain+ JSON objects. It replies to each input with an output JSON object.++ The format of the JSON sent to git-annex can be the same as the JSON that+ it outputs. Or, a simplified version. Only the "file" (or "key") field+ is actually necessary.++ For example, to get the current metadata of file foo:++ {"file":"foo"}++ To get the current metadata of the key k:+ + {"key":"k"}++ Any metadata fields included in the JSON object will be stored,+ replacing whatever values the fields had before.+ To unset a field, include it with an empty list of values.++ To change the author of file foo to bar:++ {"file":"foo","fields":{"author":["bar"]}}++ To remove the author of file foo:++ {"file":"foo","fields":{"author":[]}} # EXAMPLES
doc/git-annex-mirror.mdwn view
@@ -46,6 +46,21 @@ This is the default behavior when running git-annex in a bare repository. +* `--branch=ref`++ Operate on files in the specified branch or treeish.++ Like --all, this bypasses checking the .gitattributes annex.numcopies+ setting when dropping files.++* `--unused`++ Operate on files found by last run of git-annex unused.++* `--failed`++ Operate on files that have recently failed to be transferred.+ * file matching options The [[git-annex-matching-options]](1)
doc/git-annex-move.mdwn view
@@ -34,9 +34,17 @@ This is the default behavior when running git-annex in a bare repository. +* `--branch=ref`++ Operate on files in the specified branch or treeish.+ * `--unused` Operate on files found by last run of git-annex unused.++* `--failed`++ Operate on files that have recently failed to be transferred. * `--key=keyname`
doc/git-annex-whereis.mdwn view
@@ -35,6 +35,10 @@ Show whereis information for all known keys. +* `--branch=ref`++ Show whereis information for files in the specified branch or treeish.+ * `--unused` Show whereis information for files found by last run of git-annex unused.
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 6.20160619+Version: 6.20160808 Cabal-Version: >= 1.8 License: GPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -323,21 +323,23 @@ Build-Depends: base (>= 4.5 && < 5.0), optparse-applicative (>= 0.11.0), - containers (>= 0.5.0.0), + containers (>= 0.5.0.0), exceptions (>= 0.6), QuickCheck (>= 2.1), stm (>= 2.3), mtl (>= 2),- process, data-default, case-insensitive, uuid, random, dlist,+ uuid (>= 1.2.6),+ process, data-default, case-insensitive, random, dlist, unix-compat, SafeSemaphore, async, directory, filepath, IfElse, MissingH, hslogger, monad-logger,- utf8-string, bytestring, text, sandi, json,+ utf8-string, bytestring, text, sandi, monad-control, transformers, bloomfilter, edit-distance,- resourcet, http-conduit, http-client, http-types,+ resourcet, http-conduit (<2.2.0), http-client, http-types, time, old-locale,- esqueleto, persistent-sqlite, persistent, persistent-template,+ esqueleto, persistent-sqlite, persistent (<2.5), persistent-template, aeson,+ unordered-containers, feed, regex-tdfa CC-Options: -Wall@@ -911,6 +913,7 @@ RemoteDaemon.Types Test Types+ Types.ActionItem Types.Availability Types.Backend Types.BranchState@@ -937,6 +940,7 @@ Types.StandardGroups Types.StoreRetrieve Types.Test+ Types.Transfer Types.TrustLevel Types.UUID Types.UrlContents