git-annex/Annex/Journal.hs

120 lines
4.1 KiB
Haskell
Raw Normal View History

{- management of the git-annex journal
2011-12-12 22:03:28 +00:00
-
- The journal is used to queue up changes before they are committed to the
- git-annex branch. Among other things, it ensures that if git-annex is
2011-12-12 22:03:28 +00:00
- interrupted, its recorded data is not lost.
-
- Copyright 2011-2013 Joey Hess <id@joeyh.name>
2011-12-12 22:03:28 +00:00
-
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE CPP #-}
2011-12-12 22:03:28 +00:00
module Annex.Journal where
import Annex.Common
2011-12-12 22:03:28 +00:00
import qualified Git
import Annex.Perms
2014-07-10 04:32:23 +00:00
import Annex.LockFile
2011-12-12 22:03:28 +00:00
{- Records content for a file in the branch to the journal.
-
- Using the journal, rather than immediatly staging content to the index
- avoids git needing to rewrite the index after every change.
-
- The file in the journal is updated atomically, which allows
- getJournalFileStale to always return a consistent journal file
- content, although possibly not the most current one.
-}
setJournalFile :: JournalLocked -> FilePath -> String -> Annex ()
setJournalFile _jl file content = do
tmp <- fromRepo gitAnnexTmpMiscDir
createAnnexDirectory =<< fromRepo gitAnnexJournalDir
createAnnexDirectory tmp
-- journal file is written atomically
jfile <- fromRepo $ journalFile file
let tmpfile = tmp </> takeFileName jfile
liftIO $ do
withFile tmpfile WriteMode $ \h -> do
#ifdef mingw32_HOST_OS
hSetNewlineMode h noNewlineTranslation
#endif
hPutStr h content
moveFile tmpfile jfile
2011-12-12 22:03:28 +00:00
{- Gets any journalled content for a file in the branch. -}
getJournalFile :: JournalLocked -> FilePath -> Annex (Maybe String)
getJournalFile _jl = getJournalFileStale
{- Without locking, this is not guaranteed to be the most recent
- version of the file in the journal, so should not be used as a basis for
- changes. -}
getJournalFileStale :: FilePath -> Annex (Maybe String)
getJournalFileStale file = inRepo $ \g -> catchMaybeIO $
readFileStrict $ journalFile file g
2011-12-12 22:03:28 +00:00
{- List of files that have updated content in the journal. -}
getJournalledFiles :: JournalLocked -> Annex [FilePath]
getJournalledFiles jl = map fileJournal <$> getJournalFiles jl
getJournalledFilesStale :: Annex [FilePath]
getJournalledFilesStale = map fileJournal <$> getJournalFilesStale
2011-12-12 22:03:28 +00:00
{- List of existing journal files. -}
getJournalFiles :: JournalLocked -> Annex [FilePath]
getJournalFiles _jl = getJournalFilesStale
{- List of existing journal files, but without locking, may miss new ones
- just being added, or may have false positives if the journal is staged
- as it is run. -}
getJournalFilesStale :: Annex [FilePath]
getJournalFilesStale = do
2011-12-12 22:03:28 +00:00
g <- gitRepo
2012-09-17 04:18:07 +00:00
fs <- liftIO $ catchDefaultIO [] $
getDirectoryContents $ gitAnnexJournalDir g
2011-12-12 22:03:28 +00:00
return $ filter (`notElem` [".", ".."]) fs
withJournalHandle :: (DirectoryHandle -> IO a) -> Annex a
withJournalHandle a = do
d <- fromRepo gitAnnexJournalDir
bracketIO (openDirectory d) closeDirectory (liftIO . a)
2011-12-12 22:03:28 +00:00
{- Checks if there are changes in the journal. -}
journalDirty :: Annex Bool
2014-07-10 04:16:53 +00:00
journalDirty = do
d <- fromRepo gitAnnexJournalDir
liftIO $
(not <$> isDirectoryEmpty d)
`catchIO` (const $ doesDirectoryExist d)
2011-12-12 22:03:28 +00:00
{- Produces a filename to use in the journal for a file on the branch.
-
- The journal typically won't have a lot of files in it, so the hashing
- used in the branch is not necessary, and all the files are put directly
- in the journal directory.
-}
journalFile :: FilePath -> Git.Repo -> FilePath
journalFile file repo = gitAnnexJournalDir repo </> concatMap mangle file
2012-12-13 04:24:19 +00:00
where
2013-05-12 20:37:32 +00:00
mangle c
| c == pathSeparator = "_"
2013-05-12 20:43:59 +00:00
| c == '_' = "__"
| otherwise = [c]
2011-12-12 22:03:28 +00:00
{- Converts a journal file (relative to the journal dir) back to the
- filename on the branch. -}
fileJournal :: FilePath -> FilePath
2013-05-12 20:37:32 +00:00
fileJournal = replace [pathSeparator, pathSeparator] "_" .
replace "_" [pathSeparator]
2011-12-12 22:03:28 +00:00
{- Sentinal value, only produced by lockJournal; required
- as a parameter by things that need to ensure the journal is
- locked. -}
data JournalLocked = ProduceJournalLocked
2011-12-12 22:03:28 +00:00
{- Runs an action that modifies the journal, using locking to avoid
- contention with other git-annex processes. -}
lockJournal :: (JournalLocked -> Annex a) -> Annex a
2014-07-10 04:32:23 +00:00
lockJournal a = withExclusiveLock gitAnnexJournalLock $ a ProduceJournalLocked