summaryrefslogtreecommitdiff
path: root/Assistant
diff options
context:
space:
mode:
authorGravatar Joey Hess <joeyh@joeyh.name>2015-12-24 19:23:18 -0400
committerGravatar Joey Hess <joeyh@joeyh.name>2015-12-24 19:23:18 -0400
commit819389465d4caedd10e905f0945c60e3fc67c8ea (patch)
tree7c722fbf22d2461fd38fe1986d157de322c17922 /Assistant
parent378417c70338418ce2fd42643cad5b2f31d7ed8e (diff)
parenta9b36eb958b2dec1cefefe92262965b0f7dceb27 (diff)
Merge branch 'smudge'
Diffstat (limited to 'Assistant')
-rw-r--r--Assistant/Threads/Committer.hs130
-rw-r--r--Assistant/Threads/TransferScanner.hs4
-rw-r--r--Assistant/Threads/Watcher.hs66
-rw-r--r--Assistant/Types/Changes.hs37
4 files changed, 152 insertions, 85 deletions
diff --git a/Assistant/Threads/Committer.hs b/Assistant/Threads/Committer.hs
index 59ca69e88..0bdbb0378 100644
--- a/Assistant/Threads/Committer.hs
+++ b/Assistant/Threads/Committer.hs
@@ -21,18 +21,21 @@ import Logs.Transfer
import Logs.Location
import qualified Annex.Queue
import qualified Git.LsFiles
-import qualified Command.Add
import Utility.ThreadScheduler
import qualified Utility.Lsof as Lsof
import qualified Utility.DirWatcher as DirWatcher
import Types.KeySource
import Config
import Annex.Content
+import Annex.Ingest
import Annex.Link
import Annex.CatFile
+import Annex.InodeSentinal
+import Annex.Version
import qualified Annex
import Utility.InodeCache
import Annex.Content.Direct
+import qualified Database.Keys
import qualified Command.Sync
import qualified Git.Branch
@@ -52,7 +55,8 @@ commitThread = namedThread "Committer" $ do
=<< annexDelayAdd <$> Annex.getGitConfig
msg <- liftAnnex Command.Sync.commitMsg
waitChangeTime $ \(changes, time) -> do
- readychanges <- handleAdds havelsof delayadd changes
+ readychanges <- handleAdds havelsof delayadd $
+ simplifyChanges changes
if shouldCommit False time (length readychanges) readychanges
then do
debug
@@ -227,12 +231,11 @@ commitStaged msg = do
return ok
{- OSX needs a short delay after a file is added before locking it down,
- - when using a non-direct mode repository, as pasting a file seems to
- - try to set file permissions or otherwise access the file after closing
- - it. -}
+ - as pasting a file seems to try to set file permissions or otherwise
+ - access the file after closing it. -}
delayaddDefault :: Annex (Maybe Seconds)
#ifdef darwin_HOST_OS
-delayaddDefault = ifM isDirect
+delayaddDefault = ifM (isDirect || versionSupportsUnlockedPointers)
( return Nothing
, return $ Just $ Seconds 1
)
@@ -249,12 +252,11 @@ delayaddDefault = return Nothing
- for write by some other process, and faster checking with git-ls-files
- that the files are not already checked into git.
-
- - When a file is added, Inotify will notice the new symlink. So this waits
- - for additional Changes to arrive, so that the symlink has hopefully been
- - staged before returning, and will be committed immediately.
- -
- - OTOH, for kqueue, eventsCoalesce, so instead the symlink is directly
- - created and staged.
+ - When a file is added in locked mode, Inotify will notice the new symlink.
+ - So this waits for additional Changes to arrive, so that the symlink has
+ - hopefully been staged before returning, and will be committed immediately.
+ - (OTOH, for kqueue, eventsCoalesce, so instead the symlink is directly
+ - created and staged.)
-
- Returns a list of all changes that are ready to be committed.
- Any pending adds that are not ready yet are put back into the ChangeChan,
@@ -264,10 +266,13 @@ handleAdds :: Bool -> Maybe Seconds -> [Change] -> Assistant [Change]
handleAdds havelsof delayadd cs = returnWhen (null incomplete) $ do
let (pending, inprocess) = partition isPendingAddChange incomplete
direct <- liftAnnex isDirect
- (pending', cleanup) <- if direct
+ unlocked <- liftAnnex versionSupportsUnlockedPointers
+ let lockingfiles = not (unlocked || direct)
+ (pending', cleanup) <- if unlocked || direct
then return (pending, noop)
else findnew pending
- (postponed, toadd) <- partitionEithers <$> safeToAdd havelsof delayadd pending' inprocess
+ (postponed, toadd) <- partitionEithers
+ <$> safeToAdd lockingfiles havelsof delayadd pending' inprocess
cleanup
unless (null postponed) $
@@ -275,10 +280,11 @@ handleAdds havelsof delayadd cs = returnWhen (null incomplete) $ do
returnWhen (null toadd) $ do
added <- addaction toadd $
- catMaybes <$> if direct
- then adddirect toadd
- else forM toadd add
- if DirWatcher.eventsCoalesce || null added || direct
+ catMaybes <$>
+ if not lockingfiles
+ then addunlocked direct toadd
+ else forM toadd (add lockingfiles)
+ if DirWatcher.eventsCoalesce || null added || unlocked || direct
then return $ added ++ otherchanges
else do
r <- handleAdds havelsof delayadd =<< getChanges
@@ -304,52 +310,57 @@ handleAdds havelsof delayadd cs = returnWhen (null incomplete) $ do
| c = return otherchanges
| otherwise = a
- add :: Change -> Assistant (Maybe Change)
- add change@(InProcessAddChange { keySource = ks }) =
+ add :: Bool -> Change -> Assistant (Maybe Change)
+ add lockingfile change@(InProcessAddChange { lockedDown = ld }) =
catchDefaultIO Nothing <~> doadd
where
+ ks = keySource ld
doadd = sanitycheck ks $ do
(mkey, mcache) <- liftAnnex $ do
showStart "add" $ keyFilename ks
- Command.Add.ingest $ Just ks
+ ingest $ Just $ LockedDown lockingfile ks
maybe (failedingest change) (done change mcache $ keyFilename ks) mkey
- add _ = return Nothing
+ add _ _ = return Nothing
- {- In direct mode, avoid overhead of re-injesting a renamed
- - file, by examining the other Changes to see if a removed
- - file has the same InodeCache as the new file. If so,
- - we can just update bookkeeping, and stage the file in git.
+ {- Avoid overhead of re-injesting a renamed unlocked file, by
+ - examining the other Changes to see if a removed file has the
+ - same InodeCache as the new file. If so, we can just update
+ - bookkeeping, and stage the file in git.
-}
- adddirect :: [Change] -> Assistant [Maybe Change]
- adddirect toadd = do
+ addunlocked :: Bool -> [Change] -> Assistant [Maybe Change]
+ addunlocked isdirect toadd = do
ct <- liftAnnex compareInodeCachesWith
- m <- liftAnnex $ removedKeysMap ct cs
+ m <- liftAnnex $ removedKeysMap isdirect ct cs
delta <- liftAnnex getTSDelta
if M.null m
- then forM toadd add
+ then forM toadd (add False)
else forM toadd $ \c -> do
mcache <- liftIO $ genInodeCache (changeFile c) delta
case mcache of
- Nothing -> add c
+ Nothing -> add False c
Just cache ->
case M.lookup (inodeCacheToKey ct cache) m of
- Nothing -> add c
- Just k -> fastadd c k
-
- fastadd :: Change -> Key -> Assistant (Maybe Change)
- fastadd change key = do
- let source = keySource change
- liftAnnex $ Command.Add.finishIngestDirect key source
+ Nothing -> add False c
+ Just k -> fastadd isdirect c k
+
+ fastadd :: Bool -> Change -> Key -> Assistant (Maybe Change)
+ fastadd isdirect change key = do
+ let source = keySource $ lockedDown change
+ liftAnnex $ if isdirect
+ then finishIngestDirect key source
+ else finishIngestUnlocked key source
done change Nothing (keyFilename source) key
- removedKeysMap :: InodeComparisonType -> [Change] -> Annex (M.Map InodeCacheKey Key)
- removedKeysMap ct l = do
+ removedKeysMap :: Bool -> InodeComparisonType -> [Change] -> Annex (M.Map InodeCacheKey Key)
+ removedKeysMap isdirect ct l = do
mks <- forM (filter isRmChange l) $ \c ->
catKeyFile $ changeFile c
M.fromList . concat <$> mapM mkpairs (catMaybes mks)
where
mkpairs k = map (\c -> (inodeCacheToKey ct c, k)) <$>
- recordedInodeCache k
+ if isdirect
+ then recordedInodeCache k
+ else Database.Keys.getInodeCaches k
failedingest change = do
refill [retryChange change]
@@ -358,12 +369,16 @@ handleAdds havelsof delayadd cs = returnWhen (null incomplete) $ do
done change mcache file key = liftAnnex $ do
logStatus key InfoPresent
- link <- ifM isDirect
- ( calcRepo $ gitAnnexLink file key
- , Command.Add.link file key mcache
+ ifM versionSupportsUnlockedPointers
+ ( stagePointerFile file =<< hashPointerFile key
+ , do
+ link <- ifM isDirect
+ ( calcRepo $ gitAnnexLink file key
+ , makeLink file key mcache
+ )
+ whenM (pure DirWatcher.eventsCoalesce <||> isDirect) $
+ stageSymlink file =<< hashSymlink link
)
- whenM (pure DirWatcher.eventsCoalesce <||> isDirect) $
- stageSymlink file =<< hashSymlink link
showEndOk
return $ Just $ finishedChange change key
@@ -401,16 +416,16 @@ handleAdds havelsof delayadd cs = returnWhen (null incomplete) $ do
-
- Check by running lsof on the repository.
-}
-safeToAdd :: Bool -> Maybe Seconds -> [Change] -> [Change] -> Assistant [Either Change Change]
-safeToAdd _ _ [] [] = return []
-safeToAdd havelsof delayadd pending inprocess = do
+safeToAdd :: Bool -> Bool -> Maybe Seconds -> [Change] -> [Change] -> Assistant [Either Change Change]
+safeToAdd _ _ _ [] [] = return []
+safeToAdd lockingfiles havelsof delayadd pending inprocess = do
maybe noop (liftIO . threadDelaySeconds) delayadd
liftAnnex $ do
- keysources <- forM pending $ Command.Add.lockDown . changeFile
- let inprocess' = inprocess ++ mapMaybe mkinprocess (zip pending keysources)
+ lockeddown <- forM pending $ lockDown lockingfiles . changeFile
+ let inprocess' = inprocess ++ mapMaybe mkinprocess (zip pending lockeddown)
openfiles <- if havelsof
then S.fromList . map fst3 . filter openwrite <$>
- findopenfiles (map keySource inprocess')
+ findopenfiles (map (keySource . lockedDown) inprocess')
else pure S.empty
let checked = map (check openfiles) inprocess'
@@ -423,17 +438,18 @@ safeToAdd havelsof delayadd pending inprocess = do
allRight $ rights checked
else return checked
where
- check openfiles change@(InProcessAddChange { keySource = ks })
- | S.member (contentLocation ks) openfiles = Left change
+ check openfiles change@(InProcessAddChange { lockedDown = ld })
+ | S.member (contentLocation (keySource ld)) openfiles = Left change
check _ change = Right change
- mkinprocess (c, Just ks) = Just InProcessAddChange
+ mkinprocess (c, Just ld) = Just InProcessAddChange
{ changeTime = changeTime c
- , keySource = ks
+ , lockedDown = ld
}
mkinprocess (_, Nothing) = Nothing
- canceladd (InProcessAddChange { keySource = ks }) = do
+ canceladd (InProcessAddChange { lockedDown = ld }) = do
+ let ks = keySource ld
warning $ keyFilename ks
++ " still has writers, not adding"
-- remove the hard link
diff --git a/Assistant/Threads/TransferScanner.hs b/Assistant/Threads/TransferScanner.hs
index f35c1f1f5..7386d5528 100644
--- a/Assistant/Threads/TransferScanner.hs
+++ b/Assistant/Threads/TransferScanner.hs
@@ -25,7 +25,7 @@ import Utility.ThreadScheduler
import Utility.NotificationBroadcaster
import Utility.Batch
import qualified Git.LsFiles as LsFiles
-import qualified Backend
+import Annex.WorkTree
import Annex.Content
import Annex.Wanted
import CmdLine.Action
@@ -142,7 +142,7 @@ expensiveScan urlrenderer rs = batch <~> do
(unwanted', ts) <- maybe
(return (unwanted, []))
(findtransfers f unwanted)
- =<< liftAnnex (Backend.lookupFile f)
+ =<< liftAnnex (lookupFile f)
mapM_ (enqueue f) ts
scan unwanted' fs
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
index 8c6ff378d..bb9659b7c 100644
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -1,6 +1,6 @@
{- git-annex assistant tree watcher
-
- - Copyright 2012-2013 Joey Hess <id@joeyh.name>
+ - Copyright 2012-2015 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
@@ -28,7 +28,7 @@ import qualified Annex.Queue
import qualified Git
import qualified Git.UpdateIndex
import qualified Git.LsFiles as LsFiles
-import qualified Backend
+import Annex.WorkTree
import Annex.Direct
import Annex.Content.Direct
import Annex.CatFile
@@ -36,10 +36,15 @@ import Annex.CheckIgnore
import Annex.Link
import Annex.FileMatcher
import Types.FileMatcher
+import Annex.Content
import Annex.ReplaceFile
+import Annex.Version
+import Annex.InodeSentinal
import Git.Types
import Config
import Utility.ThreadScheduler
+import Logs.Location
+import qualified Database.Keys
#ifndef mingw32_HOST_OS
import qualified Utility.Lsof as Lsof
#endif
@@ -88,10 +93,13 @@ runWatcher = do
startup <- asIO1 startupScan
matcher <- liftAnnex largeFilesMatcher
direct <- liftAnnex isDirect
+ unlocked <- liftAnnex versionSupportsUnlockedPointers
symlinkssupported <- liftAnnex $ coreSymlinks <$> Annex.getGitConfig
- addhook <- hook $ if direct
- then onAddDirect symlinkssupported matcher
- else onAdd matcher
+ addhook <- hook $ if unlocked
+ then onAddUnlocked symlinkssupported matcher
+ else if direct
+ then onAddDirect symlinkssupported matcher
+ else onAdd matcher
delhook <- hook onDel
addsymlinkhook <- hook $ onAddSymlink direct
deldirhook <- hook onDelDir
@@ -216,15 +224,33 @@ onAdd matcher file filestatus
shouldRestage :: DaemonStatus -> Bool
shouldRestage ds = scanComplete ds || forceRestage ds
+onAddUnlocked :: Bool -> FileMatcher Annex -> Handler
+onAddUnlocked = onAddUnlocked' False contentchanged Database.Keys.addAssociatedFile samefilestatus
+ where
+ samefilestatus key file status = do
+ cache <- Database.Keys.getInodeCaches key
+ curr <- withTSDelta $ \delta -> liftIO $ toInodeCache delta file status
+ case (cache, curr) of
+ (_, Just c) -> elemInodeCaches c cache
+ ([], Nothing) -> return True
+ _ -> return False
+ contentchanged oldkey file = do
+ Database.Keys.removeAssociatedFile oldkey file
+ unlessM (inAnnex oldkey) $
+ logStatus oldkey InfoMissing
+
{- In direct mode, add events are received for both new files, and
- modified existing files.
-}
onAddDirect :: Bool -> FileMatcher Annex -> Handler
-onAddDirect symlinkssupported matcher file fs = do
+onAddDirect = onAddUnlocked' True changedDirect (\k f -> void $ addAssociatedFile k f) sameFileStatus
+
+onAddUnlocked' :: Bool -> (Key -> FilePath -> Annex ()) -> (Key -> FilePath -> Annex ()) -> (Key -> FilePath -> FileStatus -> Annex Bool) -> Bool -> FileMatcher Annex -> Handler
+onAddUnlocked' isdirect contentchanged addassociatedfile samefilestatus symlinkssupported matcher file fs = do
v <- liftAnnex $ catKeyFile file
case (v, fs) of
(Just key, Just filestatus) ->
- ifM (liftAnnex $ sameFileStatus key file filestatus)
+ ifM (liftAnnex $ samefilestatus key file filestatus)
{- It's possible to get an add event for
- an existing file that is not
- really modified, but it might have
@@ -237,13 +263,13 @@ onAddDirect symlinkssupported matcher file fs = do
, noChange
)
, guardSymlinkStandin (Just key) $ do
- debug ["changed direct", file]
- liftAnnex $ changedDirect key file
+ debug ["changed", file]
+ liftAnnex $ contentchanged key file
add matcher file
)
_ -> unlessIgnored file $
guardSymlinkStandin Nothing $ do
- debug ["add direct", file]
+ debug ["add", file]
add matcher file
where
{- On a filesystem without symlinks, we'll get changes for regular
@@ -259,9 +285,9 @@ onAddDirect symlinkssupported matcher file fs = do
Just lt -> do
case fileKey $ takeFileName lt of
Nothing -> noop
- Just key -> void $ liftAnnex $
- addAssociatedFile key file
- onAddSymlink' linktarget mk True file fs
+ Just key -> liftAnnex $
+ addassociatedfile key file
+ onAddSymlink' linktarget mk isdirect file fs
{- A symlink might be an arbitrary symlink, which is just added.
- Or, if it is a git-annex symlink, ensure it points to the content
@@ -270,7 +296,7 @@ onAddDirect symlinkssupported matcher file fs = do
onAddSymlink :: Bool -> Handler
onAddSymlink isdirect file filestatus = unlessIgnored file $ do
linktarget <- liftIO (catchMaybeIO $ readSymbolicLink file)
- kv <- liftAnnex (Backend.lookupFile file)
+ kv <- liftAnnex (lookupFile file)
onAddSymlink' linktarget kv isdirect file filestatus
onAddSymlink' :: Maybe String -> Maybe Key -> Bool -> Handler
@@ -330,13 +356,15 @@ onDel file _ = do
onDel' :: FilePath -> Annex ()
onDel' file = do
- whenM isDirect $ do
- mkey <- catKeyFile file
- case mkey of
- Nothing -> noop
- Just key -> void $ removeAssociatedFile key file
+ ifM versionSupportsUnlockedPointers
+ ( withkey $ flip Database.Keys.removeAssociatedFile file
+ , whenM isDirect $
+ withkey $ \key -> void $ removeAssociatedFile key file
+ )
Annex.Queue.addUpdateIndex =<<
inRepo (Git.UpdateIndex.unstageFile file)
+ where
+ withkey a = maybe noop a =<< catKeyFile file
{- A directory has been deleted, or moved, so tell git to remove anything
- that was inside it from its cache. Since it could reappear at any time,
diff --git a/Assistant/Types/Changes.hs b/Assistant/Types/Changes.hs
index 1d8b51775..70c40523a 100644
--- a/Assistant/Types/Changes.hs
+++ b/Assistant/Types/Changes.hs
@@ -1,18 +1,22 @@
{- git-annex assistant change tracking
-
- - Copyright 2012-2013 Joey Hess <id@joeyh.name>
+ - Copyright 2012-2015 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
+{-# LANGUAGE BangPatterns #-}
+
module Assistant.Types.Changes where
import Types.KeySource
import Types.Key
import Utility.TList
+import Annex.Ingest
import Control.Concurrent.STM
import Data.Time.Clock
+import qualified Data.Set as S
{- An un-ordered pool of Changes that have been noticed and should be
- staged and committed. Changes will typically be in order, but ordering
@@ -38,7 +42,7 @@ data Change
}
| InProcessAddChange
{ changeTime ::UTCTime
- , keySource :: KeySource
+ , lockedDown :: LockedDown
}
deriving (Show)
@@ -53,7 +57,7 @@ changeInfoKey _ = Nothing
changeFile :: Change -> FilePath
changeFile (Change _ f _) = f
changeFile (PendingAddChange _ f) = f
-changeFile (InProcessAddChange _ ks) = keyFilename ks
+changeFile (InProcessAddChange _ ld) = keyFilename $ keySource ld
isPendingAddChange :: Change -> Bool
isPendingAddChange (PendingAddChange {}) = True
@@ -64,14 +68,33 @@ isInProcessAddChange (InProcessAddChange {}) = True
isInProcessAddChange _ = False
retryChange :: Change -> Change
-retryChange (InProcessAddChange time ks) =
- PendingAddChange time (keyFilename ks)
+retryChange c@(InProcessAddChange time _) =
+ PendingAddChange time $ changeFile c
retryChange c = c
finishedChange :: Change -> Key -> Change
-finishedChange c@(InProcessAddChange { keySource = ks }) k = Change
+finishedChange c@(InProcessAddChange {}) k = Change
{ changeTime = changeTime c
- , _changeFile = keyFilename ks
+ , _changeFile = changeFile c
, changeInfo = AddKeyChange k
}
finishedChange c _ = c
+
+{- Combine PendingAddChanges that are for the same file.
+ - Multiple such often get noticed when eg, a file is opened and then
+ - closed in quick succession. -}
+simplifyChanges :: [Change] -> [Change]
+simplifyChanges [c] = [c]
+simplifyChanges cl = go cl S.empty []
+ where
+ go [] _ l = reverse l
+ go (c:cs) seen l
+ | isPendingAddChange c =
+ if S.member f seen
+ then go cs seen l
+ else
+ let !seen' = S.insert f seen
+ in go cs seen' (c:l)
+ | otherwise = go cs seen (c:l)
+ where
+ f = changeFile c