diff options
author | Joey Hess <joey@kitenet.net> | 2013-04-02 13:13:42 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2013-04-02 15:02:00 -0400 |
commit | 56835693f551bdd07f6a840cce2d760098eebd82 (patch) | |
tree | 576a80009ea1cb6fe05f9a196f8421583457fcdf /Annex | |
parent | 8ba1f8f3327288bba9e27081d1aa651adbaa1e3e (diff) |
Update working tree files fully atomically
This avoids commit churn by the assistant when eg,
replacing a file with a symlink.
But, just as importantly, it prevents the working tree being left with a
deleted file if git-annex, or perhaps the whole system, crashes at the
wrong time.
(It also probably avoids confusing displays in file managers.)
Diffstat (limited to 'Annex')
-rw-r--r-- | Annex/Content.hs | 31 | ||||
-rw-r--r-- | Annex/Direct.hs | 8 | ||||
-rw-r--r-- | Annex/Link.hs | 4 |
3 files changed, 28 insertions, 15 deletions
diff --git a/Annex/Content.hs b/Annex/Content.hs index e9d5b6854..2a2b5641b 100644 --- a/Annex/Content.hs +++ b/Annex/Content.hs @@ -49,6 +49,7 @@ import Config import Annex.Exception import Git.SharedRepository import Annex.Perms +import Annex.Link import Annex.Content.Direct import Backend @@ -256,20 +257,33 @@ moveAnnex key src = withObjectLoc key storeobject storedirect updateInodeCache key src thawContent src replaceFile dest $ liftIO . moveFile src + {- Copy to any other locations. -} forM_ fs $ \f -> replaceFile f $ - void . liftIO . copyFileExternal dest + liftIO . void . copyFileExternal dest -{- Replaces any existing file with a new version, by running an action. - - First, makes sure the file is deleted. Or, if it didn't already exist, - - makes sure the parent directory exists. -} +{- Replaces a possibly already existing file with a new version, + - atomically, by running an action. + + - The action is passed a temp file, which it can write to, and once + - done the temp file is moved into place. + -} replaceFile :: FilePath -> (FilePath -> Annex ()) -> Annex () replaceFile file a = do + tmpdir <- fromRepo gitAnnexTmpDir + createAnnexDirectory tmpdir + tmpfile <- liftIO $ do + (tmpfile, h) <- openTempFileWithDefaultPermissions tmpdir $ + takeFileName file + hClose h + return tmpfile + a tmpfile liftIO $ do - r <- tryIO $ removeFile file + r <- tryIO $ rename tmpfile file case r of - Left _ -> createDirectoryIfMissing True $ parentDir file + Left _ -> do + createDirectoryIfMissing True $ parentDir file + rename tmpfile file _ -> noop - a file {- Runs an action to transfer an object's content. - @@ -366,8 +380,7 @@ removeAnnex key = withObjectLoc key remove removedirect cwd <- liftIO getCurrentDirectory let top' = fromMaybe top $ absNormPath cwd top let l' = relPathDirToFile top' (fromMaybe l $ absNormPath top' l) - replaceFile f $ const $ - makeAnnexLink l' f + replaceFile f $ makeAnnexLink l' {- Moves a key's file out of .git/annex/objects/ -} fromAnnex :: Key -> FilePath -> Annex () diff --git a/Annex/Direct.hs b/Annex/Direct.hs index 1bebb2cb7..a88a045e7 100644 --- a/Annex/Direct.hs +++ b/Annex/Direct.hs @@ -153,8 +153,7 @@ mergeDirectCleanup d oldsha newsha = do - Symlinks are replaced with their content, if it's available. -} movein k f = do l <- calcGitLink f k - replaceFile f $ - makeAnnexLink l + replaceFile f $ makeAnnexLink l toDirect k f {- Any new, modified, or renamed files were written to the temp @@ -179,15 +178,14 @@ toDirectGen k f = do {- Move content from annex to direct file. -} updateInodeCache k loc thawContent loc - replaceFile f $ - liftIO . moveFile loc + replaceFile f $ liftIO . moveFile loc , return Nothing ) (loc':_) -> ifM (isNothing <$> getAnnexLinkTarget loc') {- Another direct file has the content; copy it. -} ( return $ Just $ replaceFile f $ - void . liftIO . copyFileExternal loc' + liftIO . void . copyFileExternal loc' , return Nothing ) diff --git a/Annex/Link.hs b/Annex/Link.hs index 650fc19a1..931836d31 100644 --- a/Annex/Link.hs +++ b/Annex/Link.hs @@ -60,7 +60,9 @@ getAnnexLinkTarget file = do -} makeAnnexLink :: LinkTarget -> FilePath -> Annex () makeAnnexLink linktarget file = ifM (coreSymlinks <$> Annex.getGitConfig) - ( liftIO $ createSymbolicLink linktarget file + ( liftIO $ do + void $ tryIO $ removeFile file + createSymbolicLink linktarget file , liftIO $ writeFile file linktarget ) |