use TopFilePath for associated files

Fixes several bugs with updates of pointer files. When eg, running
git annex drop --from localremote
it was updating the pointer file in the local repository, not the remote.
Also, fixes drop ../foo when run in a subdir, and probably lots of other
problems. Test suite drops from ~30 to 11 failures now.

TopFilePath is used to force thinking about what the filepath is relative
to.

The data stored in the sqlite db is still just a plain string, and
TopFilePath is a newtype, so there's no overhead involved in using it in
DataBase.Keys.
This commit is contained in:
Joey Hess 2016-01-05 17:22:19 -04:00
parent f3d6f9acb5
commit b3d60ca285
Failed to extract signature
11 changed files with 60 additions and 38 deletions

View file

@ -24,6 +24,7 @@ import qualified Git.Ref
import qualified Git import qualified Git
import qualified Git.Branch import qualified Git.Branch
import Git.Types (BlobType(..)) import Git.Types (BlobType(..))
import Git.FilePath
import Config import Config
import Annex.ReplaceFile import Annex.ReplaceFile
import Annex.VariantFile import Annex.VariantFile
@ -188,7 +189,7 @@ resolveMerge' unstagedmap (Just us) them u = do
writeFile dest (formatPointer key) writeFile dest (formatPointer key)
_ -> noop _ -> noop
stagePointerFile dest =<< hashPointerFile key stagePointerFile dest =<< hashPointerFile key
Database.Keys.addAssociatedFile key dest Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath dest)
{- Stage a graft of a directory or file from a branch. {- Stage a graft of a directory or file from a branch.
- -

View file

@ -65,6 +65,7 @@ import Utility.DataUnits
import Utility.CopyFile import Utility.CopyFile
import Utility.Metered import Utility.Metered
import Config import Config
import Git.FilePath
import Git.SharedRepository import Git.SharedRepository
import Annex.Perms import Annex.Perms
import Annex.Link import Annex.Link
@ -471,7 +472,9 @@ moveAnnex key src = withObjectLoc key storeobject storedirect
, modifyContent dest $ do , modifyContent dest $ do
freezeContent src freezeContent src
liftIO $ moveFile src dest liftIO $ moveFile src dest
fs <- Database.Keys.getAssociatedFiles key g <- Annex.gitRepo
fs <- map (`fromTopFilePath` g)
<$> Database.Keys.getAssociatedFiles key
unless (null fs) $ do unless (null fs) $ do
mapM_ (populatePointerFile key dest) fs mapM_ (populatePointerFile key dest) fs
Database.Keys.storeInodeCaches key (dest:fs) Database.Keys.storeInodeCaches key (dest:fs)
@ -722,7 +725,8 @@ removeAnnex (ContentRemovalLock key) = withObjectLoc key remove removedirect
remove file = cleanObjectLoc key $ do remove file = cleanObjectLoc key $ do
secureErase file secureErase file
liftIO $ nukeFile file liftIO $ nukeFile file
mapM_ (void . tryIO . resetpointer) g <- Annex.gitRepo
mapM_ (\f -> void $ tryIO $ resetpointer $ fromTopFilePath f g)
=<< Database.Keys.getAssociatedFiles key =<< Database.Keys.getAssociatedFiles key
Database.Keys.removeInodeCaches key Database.Keys.removeInodeCaches key
Direct.removeInodeCache key Direct.removeInodeCache key

View file

@ -19,6 +19,7 @@ import Annex.Wanted
import Config import Config
import Annex.Content.Direct import Annex.Content.Direct
import qualified Database.Keys import qualified Database.Keys
import Git.FilePath
import qualified Data.Set as S import qualified Data.Set as S
import System.Log.Logger (debugM) import System.Log.Logger (debugM)
@ -49,7 +50,7 @@ handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile
handleDropsFrom locs rs reason fromhere key afile preverified runner = do handleDropsFrom locs rs reason fromhere key afile preverified runner = do
l <- ifM isDirect l <- ifM isDirect
( associatedFilesRelative key ( associatedFilesRelative key
, Database.Keys.getAssociatedFiles key , mapM getTopFilePath <$> Database.Keys.getAssociatedFiles key
) )
let fs = if null l then maybeToList afile else l let fs = if null l then maybeToList afile else l
n <- getcopies fs n <- getcopies fs

View file

@ -37,6 +37,7 @@ import Utility.InodeCache
import Annex.ReplaceFile import Annex.ReplaceFile
import Utility.Tmp import Utility.Tmp
import Utility.CopyFile import Utility.CopyFile
import Git.FilePath
import Annex.InodeSentinal import Annex.InodeSentinal
#ifdef WITH_CLIBS #ifdef WITH_CLIBS
#ifndef __ANDROID__ #ifndef __ANDROID__
@ -186,15 +187,18 @@ finishIngestUnlocked key source = do
finishIngestUnlocked' :: Key -> KeySource -> Annex () finishIngestUnlocked' :: Key -> KeySource -> Annex ()
finishIngestUnlocked' key source = do finishIngestUnlocked' key source = do
Database.Keys.addAssociatedFile key (keyFilename source) Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath (keyFilename source))
populateAssociatedFiles key source populateAssociatedFiles key source
{- Copy to any other locations using the same key. -} {- Copy to any other locations using the same key. -}
populateAssociatedFiles :: Key -> KeySource -> Annex () populateAssociatedFiles :: Key -> KeySource -> Annex ()
populateAssociatedFiles key source = do populateAssociatedFiles key source = do
otherfs <- filter (/= keyFilename source) <$> Database.Keys.getAssociatedFiles key
obj <- calcRepo (gitAnnexLocation key) obj <- calcRepo (gitAnnexLocation key)
forM_ otherfs $ g <- Annex.gitRepo
ingestedf <- flip fromTopFilePath g
<$> inRepo (toTopFilePath (keyFilename source))
afs <- map (`fromTopFilePath` g) <$> Database.Keys.getAssociatedFiles key
forM_ (filter (/= ingestedf) afs) $
populatePointerFile key obj populatePointerFile key obj
cleanCruft :: KeySource -> Annex () cleanCruft :: KeySource -> Annex ()
@ -206,16 +210,18 @@ cleanCruft source = when (contentLocation source /= keyFilename source) $
-- content. Clean up from that. -- content. Clean up from that.
cleanOldKeys :: FilePath -> Key -> Annex () cleanOldKeys :: FilePath -> Key -> Annex ()
cleanOldKeys file newkey = do cleanOldKeys file newkey = do
g <- Annex.gitRepo
ingestedf <- flip fromTopFilePath g <$> inRepo (toTopFilePath file)
topf <- inRepo (toTopFilePath file)
oldkeys <- filter (/= newkey) oldkeys <- filter (/= newkey)
<$> Database.Keys.getAssociatedKey file <$> Database.Keys.getAssociatedKey topf
mapM_ go oldkeys forM_ oldkeys $ \key -> do
where
go key = do
obj <- calcRepo (gitAnnexLocation key) obj <- calcRepo (gitAnnexLocation key)
caches <- Database.Keys.getInodeCaches key caches <- Database.Keys.getInodeCaches key
unlessM (sameInodeCache obj caches) $ do unlessM (sameInodeCache obj caches) $ do
unlinkAnnex key unlinkAnnex key
fs <- filter (/= file) fs <- filter (/= ingestedf)
. map (`fromTopFilePath` g)
<$> Database.Keys.getAssociatedFiles key <$> Database.Keys.getAssociatedFiles key
fs' <- filterM (`sameInodeCache` caches) fs fs' <- filterM (`sameInodeCache` caches) fs
case fs' of case fs' of
@ -225,9 +231,7 @@ cleanOldKeys file newkey = do
(f:_) -> do (f:_) -> do
ic <- withTSDelta (liftIO . genInodeCache f) ic <- withTSDelta (liftIO . genInodeCache f)
void $ linkToAnnex key f ic void $ linkToAnnex key f ic
_ -> lostcontent _ -> logStatus key InfoMissing
where
lostcontent = logStatus key InfoMissing
{- On error, put the file back so it doesn't seem to have vanished. {- On error, put the file back so it doesn't seem to have vanished.
- This can be called before or after the symlink is in place. -} - This can be called before or after the symlink is in place. -}

View file

@ -41,6 +41,7 @@ import Annex.ReplaceFile
import Annex.Version import Annex.Version
import Annex.InodeSentinal import Annex.InodeSentinal
import Git.Types import Git.Types
import Git.FilePath
import Config import Config
import Utility.ThreadScheduler import Utility.ThreadScheduler
import Logs.Location import Logs.Location
@ -225,8 +226,11 @@ shouldRestage :: DaemonStatus -> Bool
shouldRestage ds = scanComplete ds || forceRestage ds shouldRestage ds = scanComplete ds || forceRestage ds
onAddUnlocked :: Bool -> FileMatcher Annex -> Handler onAddUnlocked :: Bool -> FileMatcher Annex -> Handler
onAddUnlocked = onAddUnlocked' False contentchanged Database.Keys.addAssociatedFile samefilestatus onAddUnlocked = onAddUnlocked' False contentchanged addassociatedfile samefilestatus
where where
addassociatedfile key file =
Database.Keys.addAssociatedFile key
=<< inRepo (toTopFilePath file)
samefilestatus key file status = do samefilestatus key file status = do
cache <- Database.Keys.getInodeCaches key cache <- Database.Keys.getInodeCaches key
curr <- withTSDelta $ \delta -> liftIO $ toInodeCache delta file status curr <- withTSDelta $ \delta -> liftIO $ toInodeCache delta file status
@ -235,7 +239,8 @@ onAddUnlocked = onAddUnlocked' False contentchanged Database.Keys.addAssociatedF
([], Nothing) -> return True ([], Nothing) -> return True
_ -> return False _ -> return False
contentchanged oldkey file = do contentchanged oldkey file = do
Database.Keys.removeAssociatedFile oldkey file Database.Keys.removeAssociatedFile oldkey
=<< inRepo (toTopFilePath file)
unlessM (inAnnex oldkey) $ unlessM (inAnnex oldkey) $
logStatus oldkey InfoMissing logStatus oldkey InfoMissing
@ -356,8 +361,9 @@ onDel file _ = do
onDel' :: FilePath -> Annex () onDel' :: FilePath -> Annex ()
onDel' file = do onDel' file = do
topfile <- inRepo (toTopFilePath file)
ifM versionSupportsUnlockedPointers ifM versionSupportsUnlockedPointers
( withkey $ flip Database.Keys.removeAssociatedFile file ( withkey $ flip Database.Keys.removeAssociatedFile topfile
, whenM isDirect $ , whenM isDirect $
withkey $ \key -> void $ removeAssociatedFile key file withkey $ \key -> void $ removeAssociatedFile key file
) )

View file

@ -21,6 +21,7 @@ import Utility.InodeCache
import qualified Database.Keys import qualified Database.Keys
import Annex.Ingest import Annex.Ingest
import Logs.Location import Logs.Location
import Git.FilePath
cmd :: Command cmd :: Command
cmd = notDirect $ withGlobalOptions annexedMatchingOptions $ cmd = notDirect $ withGlobalOptions annexedMatchingOptions $
@ -85,7 +86,9 @@ performNew file key filemodified = do
-- Try to repopulate obj from an unmodified associated file. -- Try to repopulate obj from an unmodified associated file.
repopulate obj repopulate obj
| filemodified = modifyContent obj $ do | filemodified = modifyContent obj $ do
fs <- Database.Keys.getAssociatedFiles key g <- Annex.gitRepo
fs <- mapM (`fromTopFilePath` g)
<$> Database.Keys.getAssociatedFiles key
mfile <- firstM (isUnmodified key) fs mfile <- firstM (isUnmodified key) fs
liftIO $ nukeFile obj liftIO $ nukeFile obj
case mfile of case mfile of
@ -99,7 +102,7 @@ performNew file key filemodified = do
cleanupNew :: FilePath -> Key -> CommandCleanup cleanupNew :: FilePath -> Key -> CommandCleanup
cleanupNew file key = do cleanupNew file key = do
Database.Keys.removeAssociatedFile key file Database.Keys.removeAssociatedFile key =<< inRepo (toTopFilePath file)
return True return True
startOld :: FilePath -> CommandStart startOld :: FilePath -> CommandStart

View file

@ -15,6 +15,7 @@ import Annex.FileMatcher
import Annex.Ingest import Annex.Ingest
import Logs.Location import Logs.Location
import qualified Database.Keys import qualified Database.Keys
import Git.FilePath
import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as B
@ -58,7 +59,7 @@ smudge file = do
=<< catchMaybeIO (B.readFile content) =<< catchMaybeIO (B.readFile content)
, liftIO $ B.putStr b , liftIO $ B.putStr b
) )
Database.Keys.addAssociatedFile k file Database.Keys.addAssociatedFile k =<< inRepo (toTopFilePath file)
stop stop
-- Clean filter is fed file content on stdin, decides if a file -- Clean filter is fed file content on stdin, decides if a file

View file

@ -23,6 +23,7 @@ import qualified Git.DiffTree as DiffTree
import Utility.CopyFile import Utility.CopyFile
import Command.PreCommit (lockPreCommitHook) import Command.PreCommit (lockPreCommitHook)
import qualified Database.Keys import qualified Database.Keys
import Git.FilePath
cmd :: Command cmd :: Command
cmd = withGlobalOptions annexedMatchingOptions $ cmd = withGlobalOptions annexedMatchingOptions $
@ -87,7 +88,7 @@ performIndirect file key = do
cleanupIndirect :: FilePath -> Key -> CommandCleanup cleanupIndirect :: FilePath -> Key -> CommandCleanup
cleanupIndirect file key = do cleanupIndirect file key = do
Database.Keys.removeAssociatedFile key file Database.Keys.removeAssociatedFile key =<< inRepo (toTopFilePath file)
src <- calcRepo $ gitAnnexLocation key src <- calcRepo $ gitAnnexLocation key
ifM (Annex.getState Annex.fast) ifM (Annex.getState Annex.fast)
( do ( do

View file

@ -165,50 +165,50 @@ openDb createdb _ = withExclusiveLock gitAnnexKeysDbLock $ do
where where
open db = liftIO $ DbOpen <$> H.openDbQueue db "content" open db = liftIO $ DbOpen <$> H.openDbQueue db "content"
addAssociatedFile :: Key -> FilePath -> Annex () addAssociatedFile :: Key -> TopFilePath -> Annex ()
addAssociatedFile k f = runWriter $ addAssociatedFile' k f addAssociatedFile k f = runWriter $ addAssociatedFile' k f
addAssociatedFile' :: Key -> FilePath -> Writer addAssociatedFile' :: Key -> TopFilePath -> Writer
addAssociatedFile' k f = queueDb $ do addAssociatedFile' k f = queueDb $ do
-- If the same file was associated with a different key before, -- If the same file was associated with a different key before,
-- remove that. -- remove that.
delete $ from $ \r -> do delete $ from $ \r -> do
where_ (r ^. AssociatedFile ==. val f &&. r ^. AssociatedKey ==. val sk) where_ (r ^. AssociatedFile ==. val (getTopFilePath f) &&. r ^. AssociatedKey ==. val sk)
void $ insertUnique $ Associated sk f void $ insertUnique $ Associated sk (getTopFilePath f)
where where
sk = toSKey k sk = toSKey k
{- Note that the files returned were once associated with the key, but {- Note that the files returned were once associated with the key, but
- some of them may not be any longer. -} - some of them may not be any longer. -}
getAssociatedFiles :: Key -> Annex [FilePath] getAssociatedFiles :: Key -> Annex [TopFilePath]
getAssociatedFiles = runReader . getAssociatedFiles' . toSKey getAssociatedFiles = runReader . getAssociatedFiles' . toSKey
getAssociatedFiles' :: SKey -> Reader [FilePath] getAssociatedFiles' :: SKey -> Reader [TopFilePath]
getAssociatedFiles' sk = readDb $ do getAssociatedFiles' sk = readDb $ do
l <- select $ from $ \r -> do l <- select $ from $ \r -> do
where_ (r ^. AssociatedKey ==. val sk) where_ (r ^. AssociatedKey ==. val sk)
return (r ^. AssociatedFile) return (r ^. AssociatedFile)
return $ map unValue l return $ map (TopFilePath . unValue) l
{- Gets any keys that are on record as having a particular associated file. {- Gets any keys that are on record as having a particular associated file.
- (Should be one or none but the database doesn't enforce that.) -} - (Should be one or none but the database doesn't enforce that.) -}
getAssociatedKey :: FilePath -> Annex [Key] getAssociatedKey :: TopFilePath -> Annex [Key]
getAssociatedKey = runReader . getAssociatedKey' getAssociatedKey = runReader . getAssociatedKey'
getAssociatedKey' :: FilePath -> Reader [Key] getAssociatedKey' :: TopFilePath -> Reader [Key]
getAssociatedKey' f = readDb $ do getAssociatedKey' f = readDb $ do
l <- select $ from $ \r -> do l <- select $ from $ \r -> do
where_ (r ^. AssociatedFile ==. val f) where_ (r ^. AssociatedFile ==. val (getTopFilePath f))
return (r ^. AssociatedKey) return (r ^. AssociatedKey)
return $ map (fromSKey . unValue) l return $ map (fromSKey . unValue) l
removeAssociatedFile :: Key -> FilePath -> Annex () removeAssociatedFile :: Key -> TopFilePath -> Annex ()
removeAssociatedFile k = runWriter . removeAssociatedFile' (toSKey k) removeAssociatedFile k = runWriter . removeAssociatedFile' (toSKey k)
removeAssociatedFile' :: SKey -> FilePath -> Writer removeAssociatedFile' :: SKey -> TopFilePath -> Writer
removeAssociatedFile' sk f = queueDb $ removeAssociatedFile' sk f = queueDb $
delete $ from $ \r -> do delete $ from $ \r -> do
where_ (r ^. AssociatedKey ==. val sk &&. r ^. AssociatedFile ==. val f) where_ (r ^. AssociatedKey ==. val sk &&. r ^. AssociatedFile ==. val (getTopFilePath f))
{- Find all unlocked associated files. This is expensive, and so normally {- Find all unlocked associated files. This is expensive, and so normally
- the associated files are updated incrementally when changes are noticed. -} - the associated files are updated incrementally when changes are noticed. -}

View file

@ -13,9 +13,8 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
module Git.FilePath ( module Git.FilePath (
TopFilePath, TopFilePath(..),
fromTopFilePath, fromTopFilePath,
getTopFilePath,
toTopFilePath, toTopFilePath,
asTopFilePath, asTopFilePath,
InternalGitPath, InternalGitPath,

View file

@ -20,6 +20,7 @@ import qualified Annex.Content.Direct as Direct
import qualified Git import qualified Git
import qualified Git.LsFiles import qualified Git.LsFiles
import qualified Git.Branch import qualified Git.Branch
import Git.FilePath
import Git.FileMode import Git.FileMode
import Git.Config import Git.Config
import Utility.InodeCache import Utility.InodeCache
@ -89,7 +90,8 @@ upgradeDirectWorkTree = do
, fromdirect f k , fromdirect f k
) )
stagePointerFile f =<< hashPointerFile k stagePointerFile f =<< hashPointerFile k
Database.Keys.addAssociatedFile k f Database.Keys.addAssociatedFile k
=<< inRepo (toTopFilePath f)
return () return ()
go _ = noop go _ = noop