dropdead per-remote metadata
Had to refactor pure code into separate modules so it is accessible inside Annex.Branch.Transitions. This commit was sponsored by Peter on Patreon.
This commit is contained in:
parent
f1e5dfb7c7
commit
0a7c5a9982
9 changed files with 197 additions and 109 deletions
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex branch transitions
|
{- git-annex branch transitions
|
||||||
-
|
-
|
||||||
- Copyright 2013 Joey Hess <id@joeyh.name>
|
- Copyright 2013-2018 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -15,8 +15,10 @@ import Logs.Transitions
|
||||||
import qualified Logs.UUIDBased as UUIDBased
|
import qualified Logs.UUIDBased as UUIDBased
|
||||||
import qualified Logs.Presence.Pure as Presence
|
import qualified Logs.Presence.Pure as Presence
|
||||||
import qualified Logs.Chunk.Pure as Chunk
|
import qualified Logs.Chunk.Pure as Chunk
|
||||||
|
import qualified Logs.MetaData.Pure as MetaData
|
||||||
import Types.TrustLevel
|
import Types.TrustLevel
|
||||||
import Types.UUID
|
import Types.UUID
|
||||||
|
import Types.MetaData
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import Data.Default
|
import Data.Default
|
||||||
|
@ -49,16 +51,27 @@ dropDead f content trustmap = case getLogVariety f of
|
||||||
in if null newlog
|
in if null newlog
|
||||||
then RemoveFile
|
then RemoveFile
|
||||||
else ChangeFile $ Presence.showLog newlog
|
else ChangeFile $ Presence.showLog newlog
|
||||||
|
Just RemoteMetaDataLog ->
|
||||||
|
let newlog = dropDeadFromRemoteMetaDataLog trustmap $ MetaData.simplifyLog $ MetaData.parseLog content
|
||||||
|
in if null newlog
|
||||||
|
then RemoveFile
|
||||||
|
else ChangeFile $ MetaData.showLog newlog
|
||||||
Just OtherLog -> PreserveFile
|
Just OtherLog -> PreserveFile
|
||||||
Nothing -> PreserveFile
|
Nothing -> PreserveFile
|
||||||
|
|
||||||
dropDeadFromMapLog :: TrustMap -> (k -> UUID) -> M.Map k v -> M.Map k v
|
dropDeadFromMapLog :: TrustMap -> (k -> UUID) -> M.Map k v -> M.Map k v
|
||||||
dropDeadFromMapLog trustmap getuuid = M.filterWithKey $ \k _v -> notDead trustmap getuuid k
|
dropDeadFromMapLog trustmap getuuid =
|
||||||
|
M.filterWithKey $ \k _v -> notDead trustmap getuuid k
|
||||||
|
|
||||||
{- Presence logs can contain UUIDs or other values. Any line that matches
|
{- Presence logs can contain UUIDs or other values. Any line that matches
|
||||||
- a dead uuid is dropped; any other values are passed through. -}
|
- a dead uuid is dropped; any other values are passed through. -}
|
||||||
dropDeadFromPresenceLog :: TrustMap -> [Presence.LogLine] -> [Presence.LogLine]
|
dropDeadFromPresenceLog :: TrustMap -> [Presence.LogLine] -> [Presence.LogLine]
|
||||||
dropDeadFromPresenceLog trustmap = filter $ notDead trustmap (toUUID . Presence.info)
|
dropDeadFromPresenceLog trustmap =
|
||||||
|
filter $ notDead trustmap (toUUID . Presence.info)
|
||||||
|
|
||||||
|
dropDeadFromRemoteMetaDataLog :: TrustMap -> MetaData.Log MetaData -> MetaData.Log MetaData
|
||||||
|
dropDeadFromRemoteMetaDataLog trustmap =
|
||||||
|
MetaData.filterOutEmpty . MetaData.filterRemoteMetaData (notDead trustmap id)
|
||||||
|
|
||||||
notDead :: TrustMap -> (v -> UUID) -> v -> Bool
|
notDead :: TrustMap -> (v -> UUID) -> v -> Bool
|
||||||
notDead trustmap a v = M.findWithDefault def (a v) trustmap /= DeadTrusted
|
notDead trustmap a v = M.findWithDefault def (a v) trustmap /= DeadTrusted
|
||||||
|
|
4
Logs.hs
4
Logs.hs
|
@ -16,6 +16,7 @@ data LogVariety
|
||||||
| NewUUIDBasedLog
|
| NewUUIDBasedLog
|
||||||
| ChunkLog Key
|
| ChunkLog Key
|
||||||
| PresenceLog Key
|
| PresenceLog Key
|
||||||
|
| RemoteMetaDataLog
|
||||||
| OtherLog
|
| OtherLog
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
|
@ -26,7 +27,8 @@ getLogVariety f
|
||||||
| f `elem` topLevelUUIDBasedLogs = Just UUIDBasedLog
|
| f `elem` topLevelUUIDBasedLogs = Just UUIDBasedLog
|
||||||
| isRemoteStateLog f = Just NewUUIDBasedLog
|
| isRemoteStateLog f = Just NewUUIDBasedLog
|
||||||
| isChunkLog f = ChunkLog <$> chunkLogFileKey f
|
| isChunkLog f = ChunkLog <$> chunkLogFileKey f
|
||||||
| isMetaDataLog f || isRemoteMetaDataLog f || f `elem` otherLogs = Just OtherLog
|
| isRemoteMetaDataLog f = Just RemoteMetaDataLog
|
||||||
|
| isMetaDataLog f || f `elem` otherLogs = Just OtherLog
|
||||||
| otherwise = PresenceLog <$> firstJust (presenceLogs f)
|
| otherwise = PresenceLog <$> firstJust (presenceLogs f)
|
||||||
|
|
||||||
{- All the uuid-based logs stored in the top of the git-annex branch. -}
|
{- All the uuid-based logs stored in the top of the git-annex branch. -}
|
||||||
|
|
|
@ -20,13 +20,9 @@
|
||||||
- and so foo currently has no value.
|
- and so foo currently has no value.
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
- Copyright 2014-2018 Joey Hess <id@joeyh.name>
|
|
||||||
-
|
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
||||||
|
|
||||||
module Logs.MetaData (
|
module Logs.MetaData (
|
||||||
getCurrentMetaData,
|
getCurrentMetaData,
|
||||||
getCurrentRemoteMetaData,
|
getCurrentRemoteMetaData,
|
||||||
|
@ -44,19 +40,12 @@ import Annex.VectorClock
|
||||||
import qualified Annex.Branch
|
import qualified Annex.Branch
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import Logs
|
import Logs
|
||||||
import Logs.SingleValue
|
|
||||||
import Logs.TimeStamp
|
import Logs.TimeStamp
|
||||||
|
import Logs.MetaData.Pure
|
||||||
|
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
instance SingleValueSerializable MetaData where
|
|
||||||
serialize = Types.MetaData.serialize
|
|
||||||
deserialize = Types.MetaData.deserialize
|
|
||||||
|
|
||||||
logToCurrentMetaData :: [LogEntry MetaData] -> MetaData
|
|
||||||
logToCurrentMetaData = currentMetaData . combineMetaData . map value
|
|
||||||
|
|
||||||
{- Go through the log from oldest to newest, and combine it all
|
{- Go through the log from oldest to newest, and combine it all
|
||||||
- into a single MetaData representing the current state.
|
- into a single MetaData representing the current state.
|
||||||
-
|
-
|
||||||
|
@ -131,66 +120,6 @@ addRemoteMetaData :: Key -> RemoteMetaData -> Annex ()
|
||||||
addRemoteMetaData k m = do
|
addRemoteMetaData k m = do
|
||||||
addMetaData' remoteMetaDataLogFile k (fromRemoteMetaData m)
|
addMetaData' remoteMetaDataLogFile k (fromRemoteMetaData m)
|
||||||
|
|
||||||
{- Simplify a log, removing historical values that are no longer
|
|
||||||
- needed.
|
|
||||||
-
|
|
||||||
- This is not as simple as just making a single log line with the newest
|
|
||||||
- state of all metadata. Consider this case:
|
|
||||||
-
|
|
||||||
- We have:
|
|
||||||
-
|
|
||||||
- 100 foo +x bar +y
|
|
||||||
- 200 foo -x
|
|
||||||
-
|
|
||||||
- An unmerged remote has:
|
|
||||||
-
|
|
||||||
- 150 bar -y baz +w
|
|
||||||
-
|
|
||||||
- If what we have were simplified to "200 foo -x bar +y" then when the line
|
|
||||||
- from the remote became available, it would be older than the simplified
|
|
||||||
- line, and its change to bar would not take effect. That is wrong.
|
|
||||||
-
|
|
||||||
- Instead, simplify it to:
|
|
||||||
-
|
|
||||||
- 100 bar +y
|
|
||||||
- 200 foo -x
|
|
||||||
-
|
|
||||||
- (Note that this ends up with the same number of lines as the
|
|
||||||
- unsimplified version, so there's really no point in updating
|
|
||||||
- the log to this version. Doing so would only add data to git,
|
|
||||||
- with little benefit.)
|
|
||||||
-
|
|
||||||
- Now merging with the remote yields:
|
|
||||||
-
|
|
||||||
- 100 bar +y
|
|
||||||
- 150 bar -y baz +w
|
|
||||||
- 200 foo -x
|
|
||||||
-
|
|
||||||
- Simplifying again:
|
|
||||||
-
|
|
||||||
- 150 bar +z baz +w
|
|
||||||
- 200 foo -x
|
|
||||||
-}
|
|
||||||
simplifyLog :: Log MetaData -> Log MetaData
|
|
||||||
simplifyLog s = case sl of
|
|
||||||
(newest:rest) ->
|
|
||||||
let sl' = go [newest] (value newest) rest
|
|
||||||
in if length sl' < length sl
|
|
||||||
then S.fromList sl'
|
|
||||||
else s
|
|
||||||
_ -> s
|
|
||||||
where
|
|
||||||
sl = S.toDescList s
|
|
||||||
|
|
||||||
go c _ [] = c
|
|
||||||
go c newer (l:ls)
|
|
||||||
| unique == emptyMetaData = go c newer ls
|
|
||||||
| otherwise = go (l { value = unique } : c)
|
|
||||||
(unionMetaData unique newer) ls
|
|
||||||
where
|
|
||||||
older = value l
|
|
||||||
unique = older `differenceMetaData` newer
|
|
||||||
|
|
||||||
getMetaDataLog :: Key -> Annex (Log MetaData)
|
getMetaDataLog :: Key -> Annex (Log MetaData)
|
||||||
getMetaDataLog key = do
|
getMetaDataLog key = do
|
||||||
config <- Annex.getGitConfig
|
config <- Annex.getGitConfig
|
||||||
|
@ -218,3 +147,6 @@ copyMetaData oldkey newkey
|
||||||
Annex.Branch.change (metaDataLogFile config newkey) $
|
Annex.Branch.change (metaDataLogFile config newkey) $
|
||||||
const $ showLog l
|
const $ showLog l
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
readLog :: FilePath -> Annex (Log MetaData)
|
||||||
|
readLog = parseLog <$$> Annex.Branch.get
|
||||||
|
|
111
Logs/MetaData/Pure.hs
Normal file
111
Logs/MetaData/Pure.hs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
{- git-annex metadata log, pure operations
|
||||||
|
-
|
||||||
|
- Copyright 2014-2018 Joey Hess <id@joeyh.name>
|
||||||
|
-
|
||||||
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
|
|
||||||
|
module Logs.MetaData.Pure (
|
||||||
|
Log,
|
||||||
|
LogEntry(..),
|
||||||
|
parseLog,
|
||||||
|
showLog,
|
||||||
|
logToCurrentMetaData,
|
||||||
|
simplifyLog,
|
||||||
|
filterRemoteMetaData,
|
||||||
|
filterOutEmpty,
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Types.MetaData
|
||||||
|
import Logs.SingleValue.Pure
|
||||||
|
import Types.UUID
|
||||||
|
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
|
||||||
|
instance SingleValueSerializable MetaData where
|
||||||
|
serialize = Types.MetaData.serialize
|
||||||
|
deserialize = Types.MetaData.deserialize
|
||||||
|
|
||||||
|
logToCurrentMetaData :: [LogEntry MetaData] -> MetaData
|
||||||
|
logToCurrentMetaData = currentMetaData . combineMetaData . map value
|
||||||
|
|
||||||
|
{- Simplify a log, removing historical values that are no longer
|
||||||
|
- needed.
|
||||||
|
-
|
||||||
|
- This is not as simple as just making a single log line with the newest
|
||||||
|
- state of all metadata. Consider this case:
|
||||||
|
-
|
||||||
|
- We have:
|
||||||
|
-
|
||||||
|
- 100 foo +x bar +y
|
||||||
|
- 200 foo -x
|
||||||
|
-
|
||||||
|
- An unmerged remote has:
|
||||||
|
-
|
||||||
|
- 150 bar -y baz +w
|
||||||
|
-
|
||||||
|
- If what we have were simplified to "200 foo -x bar +y" then when the line
|
||||||
|
- from the remote became available, it would be older than the simplified
|
||||||
|
- line, and its change to bar would not take effect. That is wrong.
|
||||||
|
-
|
||||||
|
- Instead, simplify it to:
|
||||||
|
-
|
||||||
|
- 100 bar +y
|
||||||
|
- 200 foo -x
|
||||||
|
-
|
||||||
|
- (Note that this ends up with the same number of lines as the
|
||||||
|
- unsimplified version, so there's really no point in updating
|
||||||
|
- the log to this version. Doing so would only add data to git,
|
||||||
|
- with little benefit.)
|
||||||
|
-
|
||||||
|
- Now merging with the remote yields:
|
||||||
|
-
|
||||||
|
- 100 bar +y
|
||||||
|
- 150 bar -y baz +w
|
||||||
|
- 200 foo -x
|
||||||
|
-
|
||||||
|
- Simplifying again:
|
||||||
|
-
|
||||||
|
- 150 bar +z baz +w
|
||||||
|
- 200 foo -x
|
||||||
|
-}
|
||||||
|
simplifyLog :: Log MetaData -> Log MetaData
|
||||||
|
simplifyLog s = case sl of
|
||||||
|
(newest:rest) ->
|
||||||
|
let sl' = go [newest] (value newest) rest
|
||||||
|
in if length sl' < length sl
|
||||||
|
then S.fromList sl'
|
||||||
|
else s
|
||||||
|
_ -> s
|
||||||
|
where
|
||||||
|
sl = S.toDescList s
|
||||||
|
|
||||||
|
go c _ [] = c
|
||||||
|
go c newer (l:ls)
|
||||||
|
| unique == emptyMetaData = go c newer ls
|
||||||
|
| otherwise = go (l { value = unique } : c)
|
||||||
|
(unionMetaData unique newer) ls
|
||||||
|
where
|
||||||
|
older = value l
|
||||||
|
unique = older `differenceMetaData` newer
|
||||||
|
|
||||||
|
{- Filters per-remote metadata on the basis of UUID.
|
||||||
|
-
|
||||||
|
- Note that the LogEntry's clock is left the same, so this should not be
|
||||||
|
- used except for in a transition.
|
||||||
|
-}
|
||||||
|
filterRemoteMetaData :: (UUID -> Bool) -> Log MetaData -> Log MetaData
|
||||||
|
filterRemoteMetaData p = S.map go
|
||||||
|
where
|
||||||
|
go l@(LogEntry { value = MetaData m }) =
|
||||||
|
l { value = MetaData $ M.filterWithKey fil m }
|
||||||
|
fil f _v = case splitRemoteMetaDataField f of
|
||||||
|
Just (u, _) -> p u
|
||||||
|
Nothing -> True
|
||||||
|
|
||||||
|
{- Filters out log lines that are empty. -}
|
||||||
|
filterOutEmpty :: Log MetaData -> Log MetaData
|
||||||
|
filterOutEmpty = S.filter $ \l -> value l /= emptyMetaData
|
|
@ -11,46 +11,20 @@
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Logs.SingleValue where
|
module Logs.SingleValue (
|
||||||
|
module Logs.SingleValue.Pure,
|
||||||
|
readLog,
|
||||||
|
getLog,
|
||||||
|
setLog,
|
||||||
|
) where
|
||||||
|
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
import qualified Annex.Branch
|
import qualified Annex.Branch
|
||||||
import Logs.Line
|
import Logs.SingleValue.Pure
|
||||||
import Annex.VectorClock
|
import Annex.VectorClock
|
||||||
|
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
class SingleValueSerializable v where
|
|
||||||
serialize :: v -> String
|
|
||||||
deserialize :: String -> Maybe v
|
|
||||||
|
|
||||||
data LogEntry v = LogEntry
|
|
||||||
{ changed :: VectorClock
|
|
||||||
, value :: v
|
|
||||||
} deriving (Eq, Ord)
|
|
||||||
|
|
||||||
type Log v = S.Set (LogEntry v)
|
|
||||||
|
|
||||||
showLog :: (SingleValueSerializable v) => Log v -> String
|
|
||||||
showLog = unlines . map showline . S.toList
|
|
||||||
where
|
|
||||||
showline (LogEntry c v) = unwords [formatVectorClock c, serialize v]
|
|
||||||
|
|
||||||
parseLog :: (Ord v, SingleValueSerializable v) => String -> Log v
|
|
||||||
parseLog = S.fromList . mapMaybe parse . splitLines
|
|
||||||
where
|
|
||||||
parse line = do
|
|
||||||
let (sc, s) = splitword line
|
|
||||||
c <- parseVectorClock sc
|
|
||||||
v <- deserialize s
|
|
||||||
Just (LogEntry c v)
|
|
||||||
splitword = separate (== ' ')
|
|
||||||
|
|
||||||
newestValue :: Log v -> Maybe v
|
|
||||||
newestValue s
|
|
||||||
| S.null s = Nothing
|
|
||||||
| otherwise = Just (value $ S.findMax s)
|
|
||||||
|
|
||||||
readLog :: (Ord v, SingleValueSerializable v) => FilePath -> Annex (Log v)
|
readLog :: (Ord v, SingleValueSerializable v) => FilePath -> Annex (Log v)
|
||||||
readLog = parseLog <$$> Annex.Branch.get
|
readLog = parseLog <$$> Annex.Branch.get
|
||||||
|
|
||||||
|
|
45
Logs/SingleValue/Pure.hs
Normal file
45
Logs/SingleValue/Pure.hs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{- git-annex single-value log, pure operations
|
||||||
|
-
|
||||||
|
- Copyright 2014 Joey Hess <id@joeyh.name>
|
||||||
|
-
|
||||||
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Logs.SingleValue.Pure where
|
||||||
|
|
||||||
|
import Annex.Common
|
||||||
|
import Logs.Line
|
||||||
|
import Annex.VectorClock
|
||||||
|
|
||||||
|
import qualified Data.Set as S
|
||||||
|
|
||||||
|
class SingleValueSerializable v where
|
||||||
|
serialize :: v -> String
|
||||||
|
deserialize :: String -> Maybe v
|
||||||
|
|
||||||
|
data LogEntry v = LogEntry
|
||||||
|
{ changed :: VectorClock
|
||||||
|
, value :: v
|
||||||
|
} deriving (Eq, Ord)
|
||||||
|
|
||||||
|
type Log v = S.Set (LogEntry v)
|
||||||
|
|
||||||
|
showLog :: (SingleValueSerializable v) => Log v -> String
|
||||||
|
showLog = unlines . map showline . S.toList
|
||||||
|
where
|
||||||
|
showline (LogEntry c v) = unwords [formatVectorClock c, serialize v]
|
||||||
|
|
||||||
|
parseLog :: (Ord v, SingleValueSerializable v) => String -> Log v
|
||||||
|
parseLog = S.fromList . mapMaybe parse . splitLines
|
||||||
|
where
|
||||||
|
parse line = do
|
||||||
|
let (sc, s) = splitword line
|
||||||
|
c <- parseVectorClock sc
|
||||||
|
v <- deserialize s
|
||||||
|
Just (LogEntry c v)
|
||||||
|
splitword = separate (== ' ')
|
||||||
|
|
||||||
|
newestValue :: Log v -> Maybe v
|
||||||
|
newestValue s
|
||||||
|
| S.null s = Nothing
|
||||||
|
| otherwise = Just (value $ S.findMax s)
|
|
@ -38,6 +38,7 @@ module Types.MetaData (
|
||||||
modMeta,
|
modMeta,
|
||||||
RemoteMetaData(..),
|
RemoteMetaData(..),
|
||||||
extractRemoteMetaData,
|
extractRemoteMetaData,
|
||||||
|
splitRemoteMetaDataField,
|
||||||
fromRemoteMetaData,
|
fromRemoteMetaData,
|
||||||
prop_metadata_sane,
|
prop_metadata_sane,
|
||||||
prop_metadata_serialize
|
prop_metadata_serialize
|
||||||
|
@ -301,6 +302,12 @@ extractRemoteMetaData u (MetaData m) = RemoteMetaData u $ MetaData $
|
||||||
prefix = remoteMetaDataPrefix u
|
prefix = remoteMetaDataPrefix u
|
||||||
prefixlen = length prefix
|
prefixlen = length prefix
|
||||||
|
|
||||||
|
splitRemoteMetaDataField :: MetaField -> Maybe (UUID, MetaField)
|
||||||
|
splitRemoteMetaDataField (MetaField f) = do
|
||||||
|
let (su, sf) = separate (== ':') (CI.original f)
|
||||||
|
f' <- toMetaField sf
|
||||||
|
return $ (toUUID su, f')
|
||||||
|
|
||||||
remoteMetaDataPrefix :: UUID -> String
|
remoteMetaDataPrefix :: UUID -> String
|
||||||
remoteMetaDataPrefix u = fromUUID u ++ ":"
|
remoteMetaDataPrefix u = fromUUID u ++ ":"
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
The newly added per-remote metadata log files need to be scrubbed clean of
|
The newly added per-remote metadata log files need to be scrubbed clean of
|
||||||
dead remotes during a transition. --[[Joey]]
|
dead remotes during a transition. --[[Joey]]
|
||||||
|
|
||||||
|
> [[done]] --[[Joey]]
|
||||||
|
|
|
@ -889,6 +889,7 @@ Executable git-annex
|
||||||
Logs.Location
|
Logs.Location
|
||||||
Logs.MapLog
|
Logs.MapLog
|
||||||
Logs.MetaData
|
Logs.MetaData
|
||||||
|
Logs.MetaData.Pure
|
||||||
Logs.Multicast
|
Logs.Multicast
|
||||||
Logs.NumCopies
|
Logs.NumCopies
|
||||||
Logs.PreferredContent
|
Logs.PreferredContent
|
||||||
|
@ -899,6 +900,7 @@ Executable git-annex
|
||||||
Logs.RemoteState
|
Logs.RemoteState
|
||||||
Logs.Schedule
|
Logs.Schedule
|
||||||
Logs.SingleValue
|
Logs.SingleValue
|
||||||
|
Logs.SingleValue.Pure
|
||||||
Logs.TimeStamp
|
Logs.TimeStamp
|
||||||
Logs.Transfer
|
Logs.Transfer
|
||||||
Logs.Transitions
|
Logs.Transitions
|
||||||
|
|
Loading…
Reference in a new issue