{- git-annex branch state management - - Runtime state about the git-annex branch, and a small cache. - - Copyright 2011-2020 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} module Annex.BranchState where import Annex.Common import Types.BranchState import qualified Annex import Logs import qualified Data.ByteString.Lazy as L getState :: Annex BranchState getState = Annex.getState Annex.branchstate changeState :: (BranchState -> BranchState) -> Annex () changeState changer = Annex.changeState $ \s -> s { Annex.branchstate = changer (Annex.branchstate s) } {- Runs an action to check that the index file exists, if it's not been - checked before in this run of git-annex. -} checkIndexOnce :: Annex () -> Annex () checkIndexOnce a = unlessM (indexChecked <$> getState) $ do a changeState $ \s -> s { indexChecked = True } {- Runs an action to update the branch, if it's not been updated before - in this run of git-annex. - - The action should return True if anything that was in the journal - before got staged (or if the journal was empty). That lets an opmisation - be done: The journal then does not need to be checked going forward, - until new information gets written to it. -} runUpdateOnce :: Annex Bool -> Annex BranchState runUpdateOnce a = do st <- getState if branchUpdated st then return st else do journalstaged <- a let stf = \st' -> st' { branchUpdated = True , journalIgnorable = journalstaged && not (needInteractiveAccess st') } changeState stf return (stf st) {- Avoids updating the branch. A useful optimisation when the branch - is known to have not changed, or git-annex won't be relying on info - from it. -} disableUpdate :: Annex () disableUpdate = changeState $ \s -> s { branchUpdated = True } {- Called when a change is made to the journal. -} journalChanged :: Annex () journalChanged = do -- Optimisation: Typically journalIgnorable will already be True -- (when one thing gets journalled, often other things do to), -- so avoid an unnecessary write to the MVar that changeState -- would do. -- -- This assumes that another thread is not changing journalIgnorable -- at the same time, but since runUpdateOnce is the only -- thing that changes it, and it only runs once, that -- should not happen. st <- getState when (journalIgnorable st) $ changeState $ \st' -> st' { journalIgnorable = False } {- When git-annex is somehow interactive, eg in --batch mode, - and needs to always notice changes made to the journal by other - processes, this disables optimisations that avoid normally reading the - journal. - - It also avoids using the cache, so changes committed by other processes - will be seen. -} enableInteractiveBranchAccess :: Annex () enableInteractiveBranchAccess = changeState $ \s -> s { needInteractiveAccess = True } setCache :: RawFilePath -> L.ByteString -> Annex () setCache file content = changeState $ \s -> s { cachedFileContents = add (cachedFileContents s) } where add l | length l < logFilesToCache = (file, content) : l | otherwise = (file, content) : Prelude.init l getCache :: RawFilePath -> Annex (Maybe L.ByteString) getCache file = (\st -> go (cachedFileContents st) st) <$> getState where go [] _ = Nothing go ((f,c):rest) state | f == file && not (needInteractiveAccess state) = Just c | otherwise = go rest state invalidateCache :: Annex () invalidateCache = changeState $ \s -> s { cachedFileContents = [] }