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.)
This commit is contained in:
Joey Hess 2013-04-02 13:13:42 -04:00
parent 8c52b20cc7
commit 38d61f934d
8 changed files with 34 additions and 21 deletions

View file

@ -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 ()