summaryrefslogtreecommitdiff
path: root/Logs/Unused.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Logs/Unused.hs')
-rw-r--r--Logs/Unused.hs118
1 files changed, 118 insertions, 0 deletions
diff --git a/Logs/Unused.hs b/Logs/Unused.hs
new file mode 100644
index 000000000..cadf7ed9d
--- /dev/null
+++ b/Logs/Unused.hs
@@ -0,0 +1,118 @@
+{- git-annex unused log file
+ -
+ - This file is stored locally in .git/annex/, not in the git-annex branch.
+ -
+ - The format: "int key timestamp"
+ -
+ - The int is a short, stable identifier that the user can use to
+ - refer to this key. (Equivilant to a filename.)
+ -
+ - The timestamp indicates when the key was first determined to be unused.
+ - Older versions of the log omit the timestamp.
+ -
+ - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module Logs.Unused (
+ UnusedMap,
+ updateUnusedLog,
+ readUnusedLog,
+ readUnusedMap,
+ dateUnusedLog,
+ unusedKeys,
+ unusedKeys',
+ setUnusedKeys,
+) where
+
+import qualified Data.Map as M
+import qualified Data.Set as S
+import Data.Time.Clock.POSIX
+import Data.Time
+import System.Locale
+
+import Common.Annex
+import qualified Annex
+import Types.Key
+import Utility.Tmp
+
+-- everything that is stored in the unused log
+type UnusedLog = M.Map Key (Int, Maybe POSIXTime)
+
+-- used to look up unused keys specified by the user
+type UnusedMap = M.Map Int Key
+
+log2map :: UnusedLog -> UnusedMap
+log2map = M.fromList . map (\(k, (i, _t)) -> (i, k)) . M.toList
+
+map2log :: POSIXTime -> UnusedMap -> UnusedLog
+map2log t = M.fromList . map (\(i, k) -> (k, (i, Just t))) . M.toList
+
+{- Only keeps keys that are in the new log, but uses any timestamps
+ - those keys had in the old log. -}
+preserveTimestamps :: UnusedLog -> UnusedLog -> UnusedLog
+preserveTimestamps oldl newl = M.intersection (M.unionWith oldts oldl newl) newl
+ where
+ oldts _old@(_, ts) _new@(int, _) = (int, ts)
+
+updateUnusedLog :: FilePath -> UnusedMap -> Annex ()
+updateUnusedLog prefix m = do
+ oldl <- readUnusedLog prefix
+ newl <- preserveTimestamps oldl . flip map2log m <$> liftIO getPOSIXTime
+ writeUnusedLog prefix newl
+
+writeUnusedLog :: FilePath -> UnusedLog -> Annex ()
+writeUnusedLog prefix l = do
+ logfile <- fromRepo $ gitAnnexUnusedLog prefix
+ liftIO $ viaTmp writeFileAnyEncoding logfile $ unlines $ map format $ M.toList l
+ where
+ format (k, (i, Just t)) = show i ++ " " ++ key2file k ++ " " ++ show t
+ format (k, (i, Nothing)) = show i ++ " " ++ key2file k
+
+readUnusedLog :: FilePath -> Annex UnusedLog
+readUnusedLog prefix = do
+ f <- fromRepo $ gitAnnexUnusedLog prefix
+ ifM (liftIO $ doesFileExist f)
+ ( M.fromList . mapMaybe parse . lines
+ <$> liftIO (readFileStrictAnyEncoding f)
+ , return M.empty
+ )
+ where
+ parse line = case (readish sint, file2key skey, utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" ts) of
+ (Just int, Just key, mtimestamp) -> Just (key, (int, mtimestamp))
+ _ -> Nothing
+ where
+ (sint, rest) = separate (== ' ') line
+ (rts, rskey) = separate (== ' ') (reverse rest)
+ skey = reverse rskey
+ ts = reverse rts
+
+readUnusedMap :: FilePath -> Annex UnusedMap
+readUnusedMap = log2map <$$> readUnusedLog
+
+dateUnusedLog :: FilePath -> Annex (Maybe UTCTime)
+#if MIN_VERSION_directory(1,2,0)
+dateUnusedLog prefix = do
+ f <- fromRepo $ gitAnnexUnusedLog prefix
+ liftIO $ catchMaybeIO $ getModificationTime f
+#else
+-- old ghc's getModificationTime returned a ClockTime
+dateUnusedLog _prefix = return Nothing
+#endif
+
+{- Set of unused keys. This is cached for speed. -}
+unusedKeys :: Annex (S.Set Key)
+unusedKeys = maybe (setUnusedKeys =<< unusedKeys') return
+ =<< Annex.getState Annex.unusedkeys
+
+unusedKeys' :: Annex [Key]
+unusedKeys' = M.keys <$> readUnusedLog ""
+
+setUnusedKeys :: [Key] -> Annex (S.Set Key)
+setUnusedKeys ks = do
+ let v = S.fromList ks
+ Annex.changeState $ \s -> s { Annex.unusedkeys = Just v }
+ return v