avoid taking journal lock unnecessarily

This commit is contained in:
Joey Hess 2011-10-03 17:27:48 -04:00
parent d357556141
commit 2636ea79c3

173
Branch.hs
View file

@ -18,14 +18,13 @@ module Branch (
name name
) where ) where
import Control.Monad (unless, liftM) import Control.Monad (unless, when, liftM, filterM)
import Control.Monad.State (liftIO) import Control.Monad.State (liftIO)
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
import System.FilePath import System.FilePath
import System.Directory import System.Directory
import Data.String.Utils import Data.String.Utils
import System.Cmd.Utils import System.Cmd.Utils
import Data.Maybe
import System.IO import System.IO
import System.IO.Binary import System.IO.Binary
import System.Posix.Process import System.Posix.Process
@ -131,8 +130,10 @@ create = unlessM hasBranch $ do
{- Stages the journal, and commits staged changes to the branch. -} {- Stages the journal, and commits staged changes to the branch. -}
commit :: String -> Annex () commit :: String -> Annex ()
commit message = lockJournal $ commit message = do
whenM stageJournalFiles $ do fs <- getJournalFiles
when (not $ null fs) $ lockJournal $ do
stageJournalFiles fs
g <- Annex.gitRepo g <- Annex.gitRepo
withIndex $ liftIO $ Git.commit g message fullname [fullname] withIndex $ liftIO $ Git.commit g message fullname [fullname]
@ -141,25 +142,54 @@ commit message = lockJournal $
update :: Annex () update :: Annex ()
update = do update = do
state <- getState state <- getState
unless (branchUpdated state) $ withIndex $ lockJournal $ do unless (branchUpdated state) $ do
{- Since branches get merged into the index, it's important to -- check what needs updating before taking the lock
- first stage the journal into the index. Otherwise, any fs <- getJournalFiles
- changes in the journal would later get staged, and might refs <- filterM checkref =<< siblingBranches
- overwrite changes made during the merge. unless (null fs && null refs) $ withIndex $ lockJournal $ do
- {- Before refs are merged into the index, it's
- It would be cleaner to handle the merge by updating the - important to first stage the journal into the
- journal, not the index, with changes from the branches. - index. Otherwise, any changes in the journal
-} - would later get staged, and might overwrite
staged <- stageJournalFiles - changes made during the merge.
-
refs <- siblingBranches - It would be cleaner to handle the merge by
updated <- catMaybes <$> mapM updateRef refs - updating the journal, not the index, with changes
g <- Annex.gitRepo - from the branches.
unless (null updated && not staged) $ liftIO $ -}
Git.commit g "update" fullname (fullname:updated) unless (null fs) $ stageJournalFiles fs
mapM_ mergeref refs
Annex.changeState $ \s -> s { Annex.branchstate = state { branchUpdated = True } } g <- Annex.gitRepo
invalidateCache liftIO $ Git.commit g "update" fullname (fullname:refs)
Annex.changeState $ \s -> s { Annex.branchstate = state { branchUpdated = True } }
invalidateCache
where
checkref ref = do
g <- Annex.gitRepo
-- checking with log to see if there have been changes
-- is less expensive than always merging
diffs <- liftIO $ Git.pipeRead g [
Param "log",
Param (name++".."++ref),
Params "--oneline -n1"
]
return $ not $ L.null diffs
mergeref ref = do
showSideAction $ "merging " ++
Git.refDescribe ref ++ " into " ++ name
{- By passing only one ref, it is actually
- merged into the index, preserving any
- changes that may already be staged.
-
- However, any changes in the git-annex
- branch that are *not* reflected in the
- index will be removed. So, documentation
- advises users not to directly modify the
- branch.
-}
g <- Annex.gitRepo
liftIO $ Git.UnionMerge.merge g [ref]
return $ Just ref
{- Checks if a git ref exists. -} {- Checks if a git ref exists. -}
refExists :: GitRef -> Annex Bool refExists :: GitRef -> Annex Bool
@ -188,35 +218,6 @@ siblingBranches = do
r <- liftIO $ Git.pipeRead g [Param "show-ref", Param name] r <- liftIO $ Git.pipeRead g [Param "show-ref", Param name]
return $ map (last . words . L.unpack) (L.lines r) return $ map (last . words . L.unpack) (L.lines r)
{- Ensures that a given ref has been merged into the index. -}
updateRef :: GitRef -> Annex (Maybe String)
updateRef ref
| ref == fullname = return Nothing
| otherwise = do
g <- Annex.gitRepo
-- checking with log to see if there have been changes
-- is less expensive than always merging
diffs <- liftIO $ Git.pipeRead g [
Param "log",
Param (name++".."++ref),
Params "--oneline -n1"
]
if L.null diffs
then return Nothing
else do
showSideAction $ "merging " ++ Git.refDescribe ref ++ " into " ++ name
-- By passing only one ref, it is actually
-- merged into the index, preserving any
-- changes that may already be staged.
--
-- However, any changes in the git-annex
-- branch that are *not* reflected in the
-- index will be removed. So, documentation
-- advises users not to directly modify the
-- branch.
liftIO $ Git.UnionMerge.merge g [ref]
return $ Just ref
{- Applies a function to modifiy the content of a file. -} {- Applies a function to modifiy the content of a file. -}
change :: FilePath -> (String -> String) -> Annex () change :: FilePath -> (String -> String) -> Annex ()
change file a = lockJournal $ get file >>= return . a >>= set file change file a = lockJournal $ get file >>= return . a >>= set file
@ -253,7 +254,7 @@ files = withIndexUpdate $ do
g <- Annex.gitRepo g <- Annex.gitRepo
bfiles <- liftIO $ Git.pipeNullSplit g bfiles <- liftIO $ Git.pipeNullSplit g
[Params "ls-tree --name-only -r -z", Param fullname] [Params "ls-tree --name-only -r -z", Param fullname]
jfiles <- getJournalFiles jfiles <- getJournalledFiles
return $ jfiles ++ bfiles return $ jfiles ++ bfiles
{- Records content for a file in the branch to the journal. {- Records content for a file in the branch to the journal.
@ -282,49 +283,43 @@ getJournalFile file = do
liftIO $ catch (liftM Just . readFileStrict $ journalFile g file) liftIO $ catch (liftM Just . readFileStrict $ journalFile g file)
(const $ return Nothing) (const $ return Nothing)
{- List of journal files. -} {- List of files that have updated content in the journal. -}
getJournalFiles :: Annex [FilePath] getJournalledFiles :: Annex [FilePath]
getJournalFiles = map fileJournal <$> getJournalFilesRaw getJournalledFiles = map fileJournal <$> getJournalFiles
getJournalFilesRaw :: Annex [FilePath] {- List of existing journal files. -}
getJournalFilesRaw = do getJournalFiles :: Annex [FilePath]
getJournalFiles = do
g <- Annex.gitRepo g <- Annex.gitRepo
fs <- liftIO $ catch (getDirectoryContents $ gitAnnexJournalDir g) fs <- liftIO $ catch (getDirectoryContents $ gitAnnexJournalDir g)
(const $ return []) (const $ return [])
return $ filter (`notElem` [".", ".."]) fs return $ filter (`notElem` [".", ".."]) fs
{- Stages all journal files into the index, and returns True if the index {- Stages the specified journalfiles. -}
- was modified. -} stageJournalFiles :: [FilePath] -> Annex ()
stageJournalFiles :: Annex Bool stageJournalFiles fs = do
stageJournalFiles = do g <- Annex.gitRepo
l <- getJournalFilesRaw withIndex $ liftIO $ do
if null l let dir = gitAnnexJournalDir g
then return False let paths = map (dir </>) fs
else do -- inject all the journal files directly into git
g <- Annex.gitRepo -- in one quick command
withIndex $ liftIO $ stage g l (pid, fromh, toh) <- hPipeBoth "git" $ toCommand $
return True Git.gitCommandLine g [Param "hash-object", Param "-w", Param "--stdin-paths"]
where _ <- forkProcess $ do
stage g fs = do hPutStr toh $ unlines paths
let dir = gitAnnexJournalDir g
let paths = map (dir </>) fs
-- inject all the journal files directly into git
-- in one quick command
(pid, fromh, toh) <- hPipeBoth "git" $ toCommand $
Git.gitCommandLine g [Param "hash-object", Param "-w", Param "--stdin-paths"]
_ <- forkProcess $ do
hPutStr toh $ unlines paths
hClose toh
exitSuccess
hClose toh hClose toh
s <- hGetContents fromh exitSuccess
-- update the index, also in just one command hClose toh
Git.UnionMerge.update_index g $ s <- hGetContents fromh
index_lines (lines s) $ map fileJournal fs -- update the index, also in just one command
hClose fromh Git.UnionMerge.update_index g $
forceSuccess pid index_lines (lines s) $ map fileJournal fs
mapM_ removeFile paths hClose fromh
index_lines shas fs = map genline $ zip shas fs forceSuccess pid
mapM_ removeFile paths
where
index_lines shas = map genline . zip shas
genline (sha, file) = Git.UnionMerge.update_index_line sha file genline (sha, file) = Git.UnionMerge.update_index_line sha file
{- Produces a filename to use in the journal for a file on the branch. {- Produces a filename to use in the journal for a file on the branch.