git-annex/LocationLog.hs

166 lines
4.8 KiB
Haskell
Raw Normal View History

2010-10-09 23:22:40 +00:00
{- git-annex location log
-
- git-annex keeps track of on which repository it last saw a file's content.
- This can be useful when using it for archiving with offline storage.
- When you indicate you --want a file, git-annex will tell you which
- repositories have the file's content.
-
2010-10-10 02:46:35 +00:00
- Location tracking information is stored in `.git-annex/filename.log`.
2010-10-12 22:06:34 +00:00
- Repositories record their UUID and the date when they --get or --drop
2010-10-10 16:31:14 +00:00
- a file's content.
2010-10-09 23:22:40 +00:00
-
2010-10-12 22:06:34 +00:00
- A line of the log will look like: "date N UUID"
2010-10-10 02:29:16 +00:00
- Where N=1 when the repo has the file, and 0 otherwise.
2010-10-10 16:31:14 +00:00
-
- Git is configured to use a union merge for this file,
- so the lines may be in arbitrary order, but it will never conflict.
2010-10-09 23:22:40 +00:00
-}
2010-10-11 21:52:46 +00:00
module LocationLog (
2010-10-12 22:25:41 +00:00
LogStatus(..),
logChange
2010-10-11 21:52:46 +00:00
) where
2010-10-09 23:22:40 +00:00
import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
2010-10-10 16:31:14 +00:00
import qualified Data.Map as Map
2010-10-09 23:22:40 +00:00
import System.IO
2010-10-10 03:35:05 +00:00
import System.Directory
import Data.Char
2010-10-10 02:14:13 +00:00
import GitRepo
2010-10-10 02:46:35 +00:00
import Utility
2010-10-12 22:06:34 +00:00
import UUID
import Locations
2010-10-09 23:22:40 +00:00
data LogLine = LogLine {
date :: POSIXTime,
status :: LogStatus,
2010-10-12 22:06:34 +00:00
uuid :: UUID
} deriving (Eq)
2010-10-10 02:29:16 +00:00
data LogStatus = FilePresent | FileMissing | Undefined
deriving (Eq)
instance Show LogStatus where
show FilePresent = "1"
show FileMissing = "0"
show Undefined = "undefined"
instance Read LogStatus where
readsPrec _ "1" = [(FilePresent, "")]
readsPrec _ "0" = [(FileMissing, "")]
readsPrec _ _ = [(Undefined, "")]
2010-10-09 23:22:40 +00:00
instance Show LogLine where
2010-10-12 22:06:34 +00:00
show (LogLine date status uuid) = unwords
[(show date), (show status), uuid]
2010-10-09 23:22:40 +00:00
instance Read LogLine where
2010-10-10 02:29:16 +00:00
-- This parser is robust in that even unparsable log lines are
-- read without an exception being thrown.
-- Such lines have a status of Undefined.
readsPrec _ string =
if (length w >= 3)
then case (pdate) of
Just v -> good v
Nothing -> undefined
else undefined
2010-10-09 23:22:40 +00:00
where
w = words string
date = w !! 0
2010-10-10 02:29:16 +00:00
status = read $ w !! 1
uuid = w !! 3
pdate = (parseTime defaultTimeLocale "%s%Qs" date) :: Maybe UTCTime
2010-10-12 22:06:34 +00:00
good v = ret $ LogLine (utcTimeToPOSIXSeconds v) status uuid
undefined = ret $ LogLine (0) Undefined ""
ret v = [(v, "")]
2010-10-09 23:22:40 +00:00
2010-10-12 22:25:41 +00:00
{- Log a change in the presence of a file in a repository,
- and add the log to git so it will propigate to other repos. -}
logChange :: GitRepo -> FilePath -> UUID -> LogStatus -> IO ()
logChange repo file uuid status = do
log <- logNow status uuid
if (status == FilePresent)
-- file added; just append to log
then appendLog logfile log
-- file removed; compact log
else do
ls <- readLog logfile
writeLog logfile (log:ls)
gitAdd repo logfile
where
logfile = logFile repo file
2010-10-10 03:35:05 +00:00
{- Reads a log file.
- Note that the LogLines returned may be in any order. -}
2010-10-10 16:41:20 +00:00
readLog :: FilePath -> IO [LogLine]
2010-10-09 23:22:40 +00:00
readLog file = do
2010-10-10 03:35:05 +00:00
exists <- doesFileExist file
if exists
then do
2010-10-10 15:08:40 +00:00
s <- withFileLocked file ReadMode $ \h ->
hGetContentsStrict h
2010-10-10 03:35:05 +00:00
-- filter out any unparsable lines
return $ filter (\l -> (status l) /= Undefined )
$ map read $ lines s
else do
return []
2010-10-09 23:22:40 +00:00
{- Adds a LogLine to a log file -}
2010-10-10 16:41:20 +00:00
appendLog :: FilePath -> LogLine -> IO ()
2010-10-10 16:31:14 +00:00
appendLog file line = do
2010-10-10 03:35:05 +00:00
createDirectoryIfMissing True (parentDir file)
2010-10-10 15:08:40 +00:00
withFileLocked file AppendMode $ \h ->
hPutStrLn h $ show line
2010-10-10 19:04:18 +00:00
-- TODO git add log
2010-10-09 23:22:40 +00:00
2010-10-10 16:31:14 +00:00
{- Writes a set of lines to a log file -}
2010-10-10 16:41:20 +00:00
writeLog :: FilePath -> [LogLine] -> IO ()
2010-10-10 16:31:14 +00:00
writeLog file lines = do
createDirectoryIfMissing True (parentDir file)
withFileLocked file WriteMode $ \h ->
hPutStr h $ unlines $ map show lines
2010-10-10 02:46:35 +00:00
{- Generates a new LogLine with the current date. -}
2010-10-12 22:25:41 +00:00
logNow :: LogStatus -> UUID -> IO LogLine
2010-10-12 22:06:34 +00:00
logNow status uuid = do
now <- getPOSIXTime
2010-10-12 22:06:34 +00:00
return $ LogLine now status uuid
2010-10-10 02:14:13 +00:00
{- Returns the filename of the log file for a given annexed file. -}
2010-10-12 22:25:41 +00:00
logFile :: GitRepo -> FilePath -> String
logFile repo annexedFile = (gitStateDir repo) ++
2010-10-10 02:14:13 +00:00
(gitRelative repo annexedFile) ++ ".log"
2010-10-10 02:46:35 +00:00
2010-10-12 22:25:41 +00:00
{- Returns a list of repository UUIDs that, according to the log, have
2010-10-10 02:46:35 +00:00
- the content of a file -}
2010-10-12 22:25:41 +00:00
fileLocations :: GitRepo -> FilePath -> IO [UUID]
2010-10-10 19:04:18 +00:00
fileLocations thisrepo file = do
2010-10-12 22:25:41 +00:00
lines <- readLog $ logFile thisrepo file
2010-10-12 22:06:34 +00:00
return $ map uuid (filterPresent lines)
2010-10-10 02:46:35 +00:00
2010-10-10 16:41:20 +00:00
{- Filters the list of LogLines to find ones where the file
2010-10-10 02:46:35 +00:00
- is (or should still be) present. -}
filterPresent :: [LogLine] -> [LogLine]
2010-10-10 16:41:20 +00:00
filterPresent lines = filter (\l -> FilePresent == status l) $ compactLog lines
2010-10-10 16:31:14 +00:00
{- Compacts a set of logs, returning a subset that contains the current
- status. -}
compactLog :: [LogLine] -> [LogLine]
compactLog lines = compactLog' Map.empty lines
compactLog' map [] = Map.elems map
compactLog' map (l:ls) = compactLog' (mapLog map l) ls
{- Inserts a log into a map of logs, if the log has better (ie, newer)
- information about a repo than the other logs in the map -}
mapLog map log =
if (better)
2010-10-12 22:06:34 +00:00
then Map.insert (uuid log) log map
2010-10-10 16:31:14 +00:00
else map
where
2010-10-12 22:06:34 +00:00
better = case (Map.lookup (uuid log) map) of
2010-10-10 16:31:14 +00:00
Just l -> (date l <= date log)
Nothing -> True