diff options
author | Joey Hess <joeyh@joeyh.name> | 2017-09-18 13:57:25 -0400 |
---|---|---|
committer | Joey Hess <joeyh@joeyh.name> | 2017-09-18 13:59:59 -0400 |
commit | a5e968bb8d4c608c33463160ea2b583a3e34b8fc (patch) | |
tree | ffd59e071fadf718ed4f270d2cf2b67fda9b6315 | |
parent | 9d2ac4d87dc98bd2ab60da38a7e98f0964fd1595 (diff) |
add ExportTree table to export db
New table needed to look up what filenames are used in the currently
exported tree, for reasons explained in export.mdwn.
Also, added smart constructors for ExportLocation and ExportDirectory to
make sure they contain filepaths with the right direction slashes.
And some code refactoring.
This commit was sponsored by Francois Marier on Patreon.
-rw-r--r-- | Annex/Export.hs | 35 | ||||
-rw-r--r-- | Command/Export.hs | 72 | ||||
-rw-r--r-- | Database/Export.hs | 127 | ||||
-rw-r--r-- | Database/Types.hs | 21 | ||||
-rw-r--r-- | Remote/Directory.hs | 6 | ||||
-rw-r--r-- | Remote/External/Types.hs | 8 | ||||
-rw-r--r-- | Remote/Helper/Export.hs | 12 | ||||
-rw-r--r-- | Remote/S3.hs | 2 | ||||
-rw-r--r-- | Remote/WebDAV.hs | 4 | ||||
-rw-r--r-- | Remote/WebDAV/DavLocation.hs | 2 | ||||
-rw-r--r-- | Types/Export.hs | 26 | ||||
-rw-r--r-- | doc/todo/export.mdwn | 24 | ||||
-rw-r--r-- | git-annex.cabal | 1 |
13 files changed, 247 insertions, 93 deletions
diff --git a/Annex/Export.hs b/Annex/Export.hs new file mode 100644 index 000000000..0afe3cdcc --- /dev/null +++ b/Annex/Export.hs @@ -0,0 +1,35 @@ +{- git-annex exports + - + - Copyright 2017 Joey Hess <id@joeyh.name> + - + - Licensed under the GNU GPL version 3 or higher. + -} + +module Annex.Export where + +import Annex +import Annex.CatFile +import Types.Key +import qualified Git + +-- An export includes both annexed files and files stored in git. +-- For the latter, a SHA1 key is synthesized. +data ExportKey = AnnexKey Key | GitKey Key + deriving (Show, Eq, Ord) + +asKey :: ExportKey -> Key +asKey (AnnexKey k) = k +asKey (GitKey k) = k + +exportKey :: Git.Sha -> Annex ExportKey +exportKey sha = mk <$> catKey sha + where + mk (Just k) = AnnexKey k + mk Nothing = GitKey $ Key + { keyName = show sha + , keyVariety = SHA1Key (HasExt False) + , keySize = Nothing + , keyMtime = Nothing + , keyChunkSize = Nothing + , keyChunkNum = Nothing + } diff --git a/Command/Export.hs b/Command/Export.hs index a9f474a19..f898c9e0d 100644 --- a/Command/Export.hs +++ b/Command/Export.hs @@ -21,6 +21,7 @@ import Git.Sha import Types.Key import Types.Remote import Types.Export +import Annex.Export import Annex.Content import Annex.CatFile import Annex.LockFile @@ -53,28 +54,6 @@ optParser _ = ExportOptions ( metavar paramTreeish ) --- An export includes both annexed files and files stored in git. --- For the latter, a SHA1 key is synthesized. -data ExportKey = AnnexKey Key | GitKey Key - deriving (Show, Eq, Ord) - -asKey :: ExportKey -> Key -asKey (AnnexKey k) = k -asKey (GitKey k) = k - -exportKey :: Git.Sha -> Annex ExportKey -exportKey sha = mk <$> catKey sha - where - mk (Just k) = AnnexKey k - mk Nothing = GitKey $ Key - { keyName = show sha - , keyVariety = SHA1Key (HasExt False) - , keySize = Nothing - , keyMtime = Nothing - , keyChunkSize = Nothing - , keyChunkNum = Nothing - } - -- To handle renames which swap files, the exported file is first renamed -- to a stable temporary name based on the key. exportTempName :: ExportKey -> ExportLocation @@ -153,7 +132,8 @@ seek' o r = do -- if this export is interrupted, there are no files left over -- from a previous export, that are not part of this export. c <- Annex.getState Annex.errcounter - when (c == 0) $ + when (c == 0) $ do + liftIO $ recordDataSource db new recordExport (uuid r) $ ExportChange { oldTreeish = map exportedTreeish old , newTreeish = new @@ -184,24 +164,24 @@ mkDiffMap old new = do where combinedm (srca, dsta) (srcb, dstb) = (srca <|> srcb, dsta <|> dstb) mkdm i = do - srcek <- getk (Git.DiffTree.srcsha i) - dstek <- getk (Git.DiffTree.dstsha i) + srcek <- getek (Git.DiffTree.srcsha i) + dstek <- getek (Git.DiffTree.dstsha i) return $ catMaybes [ (, (Just (Git.DiffTree.file i), Nothing)) <$> srcek , (, (Nothing, Just (Git.DiffTree.file i))) <$> dstek ] - getk sha + getek sha | sha == nullSha = return Nothing | otherwise = Just <$> exportKey sha startExport :: Remote -> ExportActions Annex -> ExportHandle -> Git.LsTree.TreeItem -> CommandStart startExport r ea db ti = do ek <- exportKey (Git.LsTree.sha ti) - stopUnless (liftIO $ notElem loc <$> getExportLocation db (asKey ek)) $ do + stopUnless (liftIO $ notElem loc <$> getExportedLocation db (asKey ek)) $ do showStart "export" f next $ performExport r ea db ek (Git.LsTree.sha ti) loc where - loc = ExportLocation $ toInternalGitPath f + loc = mkExportLocation f f = getTopFilePath $ Git.LsTree.file ti performExport :: Remote -> ExportActions Annex -> ExportHandle -> ExportKey -> Sha -> ExportLocation -> CommandPerform @@ -231,7 +211,7 @@ performExport r ea db ek contentsha loc = do cleanupExport :: Remote -> ExportHandle -> ExportKey -> ExportLocation -> CommandCleanup cleanupExport r db ek loc = do - liftIO $ addExportLocation db (asKey ek) loc + liftIO $ addExportedLocation db (asKey ek) loc logChange (asKey ek) (uuid r) InfoPresent return True @@ -244,7 +224,7 @@ startUnexport r ea db f shas = do showStart "unexport" f' next $ performUnexport r ea db eks loc where - loc = ExportLocation $ toInternalGitPath f' + loc = mkExportLocation f' f' = getTopFilePath f startUnexport' :: Remote -> ExportActions Annex -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart @@ -252,7 +232,7 @@ startUnexport' r ea db f ek = do showStart "unexport" f' next $ performUnexport r ea db [ek] loc where - loc = ExportLocation $ toInternalGitPath f' + loc = mkExportLocation f' f' = getTopFilePath f performUnexport :: Remote -> ExportActions Annex -> ExportHandle -> [ExportKey] -> ExportLocation -> CommandPerform @@ -266,11 +246,11 @@ cleanupUnexport :: Remote -> ExportActions Annex -> ExportHandle -> [ExportKey] cleanupUnexport r ea db eks loc = do liftIO $ do forM_ eks $ \ek -> - removeExportLocation db (asKey ek) loc + removeExportedLocation db (asKey ek) loc flushDbQueue db remaininglocs <- liftIO $ - concat <$> forM eks (\ek -> getExportLocation db (asKey ek)) + concat <$> forM eks (\ek -> getExportedLocation db (asKey ek)) when (null remaininglocs) $ forM_ eks $ \ek -> logChange (asKey ek) (uuid r) InfoMissing @@ -282,31 +262,31 @@ startRecoverIncomplete r ea db sha oldf | sha == nullSha = stop | otherwise = do ek <- exportKey sha - let loc@(ExportLocation f) = exportTempName ek - showStart "unexport" f - liftIO $ removeExportLocation db (asKey ek) oldloc + let loc = exportTempName ek + showStart "unexport" (fromExportLocation f) + liftIO $ removeExportedLocation db (asKey ek) oldloc next $ performUnexport r ea db [ek] loc where - oldloc = ExportLocation $ toInternalGitPath oldf' + oldloc = mkExportLocation oldf' oldf' = getTopFilePath oldf startMoveToTempName :: Remote -> ExportActions Annex -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart startMoveToTempName r ea db f ek = do - let tmploc@(ExportLocation tmpf) = exportTempName ek - showStart "rename" (f' ++ " -> " ++ tmpf) + showStart "rename" (f' ++ " -> " ++ fromExportLocation tmploc) next $ performRename r ea db ek loc tmploc where - loc = ExportLocation $ toInternalGitPath f' + loc = mkExportLocation f' f' = getTopFilePath f + tmploc = exportTempName ek startMoveFromTempName :: Remote -> ExportActions Annex -> ExportHandle -> ExportKey -> TopFilePath -> CommandStart startMoveFromTempName r ea db ek f = do - let tmploc@(ExportLocation tmpf) = exportTempName ek - stopUnless (liftIO $ elem tmploc <$> getExportLocation db (asKey ek)) $ do - showStart "rename" (tmpf ++ " -> " ++ f') + let tmploc = exportTempName ek + stopUnless (liftIO $ elem tmploc <$> getExportedLocation db (asKey ek)) $ do + showStart "rename" (exportLocation tmploc ++ " -> " ++ f') next $ performRename r ea db ek tmploc loc where - loc = ExportLocation $ toInternalGitPath f' + loc = mkExportLocation f' f' = getTopFilePath f performRename :: Remote -> ExportActions Annex -> ExportHandle -> ExportKey -> ExportLocation -> ExportLocation -> CommandPerform @@ -323,8 +303,8 @@ performRename r ea db ek src dest = do cleanupRename :: ExportActions Annex -> ExportHandle -> ExportKey -> ExportLocation -> ExportLocation -> CommandCleanup cleanupRename ea db ek src dest = do liftIO $ do - removeExportLocation db (asKey ek) src - addExportLocation db (asKey ek) dest + removeExportedLocation db (asKey ek) src + addExportedLocation db (asKey ek) dest flushDbQueue db if exportDirectories src /= exportDirectories dest then removeEmptyDirectories ea db src [asKey ek] diff --git a/Database/Export.hs b/Database/Export.hs index df3d92300..7dae408fa 100644 --- a/Database/Export.hs +++ b/Database/Export.hs @@ -14,13 +14,19 @@ module Database.Export ( ExportHandle, openDb, closeDb, - addExportLocation, - removeExportLocation, flushDbQueue, - getExportLocation, + recordDataSource, + getDataSource, + addExportedLocation, + removeExportedLocation, + getExportedLocation, isExportDirectoryEmpty, + getExportTree, + updateExportTree, ExportedId, + ExportTreeId, ExportedDirectoryId, + DataSourceId, ) where import Database.Types @@ -29,6 +35,11 @@ import Database.Init import Annex.Locations import Annex.Common hiding (delete) import Types.Export +import Annex.Export +import Git.Types +import Git.Sha +import Git.FilePath +import qualified Git.DiffTree import Database.Persist.TH import Database.Esqueleto hiding (Key) @@ -36,14 +47,26 @@ import Database.Esqueleto hiding (Key) newtype ExportHandle = ExportHandle H.DbQueue share [mkPersist sqlSettings, mkMigrate "migrateExport"] [persistLowerCase| +-- Files that have been exported to the remote. Exported key IKey file SFilePath - KeyFileIndex key file + ExportedIndex key file +-- The tree that has been exported to the remote. +-- Not all of these files are necessarily present on the remote yet. +ExportTree + key IKey + file SFilePath + ExportTreeIndex key file +-- Directories that exist on the remote, and the files that are in them. ExportedDirectory subdir SFilePath file SFilePath - SubdirFileIndex subdir file + ExportedDirectoryIndex subdir file +-- Record of what tree the current database content comes from. +DataSource + tree SRef + UniqueTree tree |] {- Opens the database, creating it if it doesn't exist yet. -} @@ -68,48 +91,110 @@ queueDb (ExportHandle h) = H.queueDb h checkcommit | sz > 1000 = return True | otherwise = return False -addExportLocation :: ExportHandle -> Key -> ExportLocation -> IO () -addExportLocation h k el@(ExportLocation f) = queueDb h $ do +flushDbQueue :: ExportHandle -> IO () +flushDbQueue (ExportHandle h) = H.flushDbQueue h + +recordDataSource :: ExportHandle -> Sha -> IO () +recordDataSource h s = queueDb h $ do + delete $ from $ \r -> do + where_ (r ^. DataSourceTree ==. r ^. DataSourceTree) + void $ insertUnique $ DataSource (toSRef s) + +getDataSource :: ExportHandle -> IO (Maybe Sha) +getDataSource (ExportHandle h) = H.queryDbQueue h $ do + l <- select $ from $ \r -> do + where_ (r ^. DataSourceTree ==. r ^. DataSourceTree) + return (r ^. DataSourceTree) + case l of + (s:[]) -> return (Just (fromSRef (unValue s))) + _ -> return Nothing + +addExportedLocation :: ExportHandle -> Key -> ExportLocation -> IO () +addExportedLocation h k el = queueDb h $ do void $ insertUnique $ Exported ik ef insertMany_ $ map - (\(ExportDirectory d) -> ExportedDirectory (toSFilePath d) ef) + (\ed -> ExportedDirectory (toSFilePath (fromExportDirectory ed)) ef) (exportDirectories el) where ik = toIKey k - ef = toSFilePath f + ef = toSFilePath (fromExportLocation el) -removeExportLocation :: ExportHandle -> Key -> ExportLocation -> IO () -removeExportLocation h k el@(ExportLocation f) = queueDb h $ do +removeExportedLocation :: ExportHandle -> Key -> ExportLocation -> IO () +removeExportedLocation h k el = queueDb h $ do delete $ from $ \r -> do where_ (r ^. ExportedKey ==. val ik &&. r ^. ExportedFile ==. val ef) - let subdirs = map (\(ExportDirectory d) -> toSFilePath d) + let subdirs = map (toSFilePath . fromExportDirectory) (exportDirectories el) delete $ from $ \r -> do where_ (r ^. ExportedDirectoryFile ==. val ef &&. r ^. ExportedDirectorySubdir `in_` valList subdirs) where ik = toIKey k - ef = toSFilePath f - -flushDbQueue :: ExportHandle -> IO () -flushDbQueue (ExportHandle h) = H.flushDbQueue h + ef = toSFilePath (fromExportLocation el) {- Note that this does not see recently queued changes. -} -getExportLocation :: ExportHandle -> Key -> IO [ExportLocation] -getExportLocation (ExportHandle h) k = H.queryDbQueue h $ do +getExportedLocation :: ExportHandle -> Key -> IO [ExportLocation] +getExportedLocation (ExportHandle h) k = H.queryDbQueue h $ do l <- select $ from $ \r -> do where_ (r ^. ExportedKey ==. val ik) return (r ^. ExportedFile) - return $ map (ExportLocation . fromSFilePath . unValue) l + return $ map (mkExportLocation . fromSFilePath . unValue) l where ik = toIKey k {- Note that this does not see recently queued changes. -} isExportDirectoryEmpty :: ExportHandle -> ExportDirectory -> IO Bool -isExportDirectoryEmpty (ExportHandle h) (ExportDirectory d) = H.queryDbQueue h $ do +isExportDirectoryEmpty (ExportHandle h) d = H.queryDbQueue h $ do l <- select $ from $ \r -> do where_ (r ^. ExportedDirectorySubdir ==. val ed) return (r ^. ExportedDirectoryFile) return $ null l where - ed = toSFilePath d + ed = toSFilePath $ fromExportDirectory d + +{- Get locations in the export that might contain a key. -} +getExportTree :: ExportHandle -> Key -> IO [ExportLocation] +getExportTree (ExportHandle h) k = H.queryDbQueue h $ do + l <- select $ from $ \r -> do + where_ (r ^. ExportTreeKey ==. val ik) + return (r ^. ExportTreeFile) + return $ map (mkExportLocation . fromSFilePath . unValue) l + where + ik = toIKey k + +addExportTree :: ExportHandle -> Key -> ExportLocation -> IO () +addExportTree h k loc = queueDb h $ + void $ insertUnique $ Exported ik ef + where + ik = toIKey k + ef = toSFilePath (fromExportLocation loc) + +removeExportTree :: ExportHandle -> Key -> ExportLocation -> IO () +removeExportTree h k loc = queueDb h $ + delete $ from $ \r -> + where_ (r ^. ExportTreeKey ==. val ik &&. r ^. ExportTreeFile ==. val ef) + where + ik = toIKey k + ef = toSFilePath (fromExportLocation loc) + +{- Diff from the old to the new tree and update the ExportTree table. -} +updateExportTree :: ExportHandle -> Sha -> Sha -> Annex () +updateExportTree h old new = do + (diff, cleanup) <- inRepo $ + Git.DiffTree.diffTreeRecursive old new + forM_ diff $ \i -> do + let loc = mkExportLocation $ getTopFilePath $ + Git.DiffTree.file i + srcek <- getek (Git.DiffTree.srcsha i) + case srcek of + Nothing -> return () + Just k -> liftIO $ removeExportTree h (asKey k) loc + dstek <- getek (Git.DiffTree.dstsha i) + case dstek of + Nothing -> return () + Just k -> liftIO $ addExportTree h (asKey k) loc + void $ liftIO cleanup + where + getek sha + | sha == nullSha = return Nothing + | otherwise = Just <$> exportKey sha diff --git a/Database/Types.hs b/Database/Types.hs index a4b5fbcb1..49a63f067 100644 --- a/Database/Types.hs +++ b/Database/Types.hs @@ -1,6 +1,6 @@ {- types for SQL databases - - - Copyright 2015-2016 Joey Hess <id@joeyh.name> + - Copyright 2015-2017 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -} @@ -16,6 +16,7 @@ import Data.Char import Utility.PartialPrelude import Key import Utility.InodeCache +import Git.Types (Ref(..)) -- A serialized Key newtype SKey = SKey String @@ -93,3 +94,21 @@ fromSFilePath (SFilePath s) = s derivePersistField "SFilePath" +-- A serialized Ref +newtype SRef = SRef Ref + +-- Note that Read instance does not work when used in any kind of complex +-- data structure. +instance Read SRef where + readsPrec _ s = [(SRef (Ref s), "")] + +instance Show SRef where + show (SRef (Ref s)) = s + +derivePersistField "SRef" + +toSRef :: Ref -> SRef +toSRef = SRef + +fromSRef :: SRef -> Ref +fromSRef (SRef r) = r diff --git a/Remote/Directory.hs b/Remote/Directory.hs index 2d2daff39..406af0fdc 100644 --- a/Remote/Directory.hs +++ b/Remote/Directory.hs @@ -274,14 +274,14 @@ renameExportM d _k oldloc newloc = liftIO $ catchBoolIO $ do dest = exportPath d newloc exportPath :: FilePath -> ExportLocation -> FilePath -exportPath d (ExportLocation loc) = d </> loc +exportPath d loc = d </> fromExportLocation loc {- Removes the ExportLocation directory and its parents, so long as - they're empty, up to but not including the topdir. -} removeExportLocation :: FilePath -> ExportLocation -> IO () -removeExportLocation topdir (ExportLocation loc) = go (Just loc) (Right ()) +removeExportLocation topdir loc = go (Just $ fromExportLocation loc) (Right ()) where go _ (Left _e) = return () go Nothing _ = return () go (Just loc') _ = go (upFrom loc') - =<< tryIO (removeDirectory $ exportPath topdir (ExportLocation loc')) + =<< tryIO (removeDirectory $ exportPath topdir (mkExportLocation loc')) diff --git a/Remote/External/Types.hs b/Remote/External/Types.hs index 0ddbbaf0a..77f3e837e 100644 --- a/Remote/External/Types.hs +++ b/Remote/External/Types.hs @@ -358,9 +358,9 @@ instance Proto.Serializable URI where deserialize = parseURI instance Proto.Serializable ExportLocation where - serialize (ExportLocation loc) = loc - deserialize = Just . ExportLocation + serialize = fromExportLocation + deserialize = Just . mkExportLocation instance Proto.Serializable ExportDirectory where - serialize (ExportDirectory loc) = loc - deserialize = Just . ExportDirectory + serialize = fromExportDirectory + deserialize = Just . mkExportDirectory diff --git a/Remote/Helper/Export.hs b/Remote/Helper/Export.hs index df75dacd0..6f4811285 100644 --- a/Remote/Helper/Export.hs +++ b/Remote/Helper/Export.hs @@ -93,7 +93,7 @@ adjustExportable r = case M.lookup "exporttree" (config r) of -- Storing a key on an export would need a way to -- look up the file(s) that the currently exported -- tree uses for a key; there's not currently an - -- inexpensive way to do that (getExportLocation + -- inexpensive way to do that (getExportedLocation -- only finds files that have been stored on the -- export already). { storeKey = \_ _ _ -> do @@ -105,7 +105,7 @@ adjustExportable r = case M.lookup "exporttree" (config r) of , retrieveKeyFile = \k _af dest p -> unVerified $ if maybe False (isJust . verifyKeyContent) (maybeLookupBackendVariety (keyVariety k)) then do - locs <- liftIO $ getExportLocation db k + locs <- liftIO $ getExportedLocation db k case locs of [] -> do warning "unknown export location" @@ -136,7 +136,7 @@ adjustExportable r = case M.lookup "exporttree" (config r) of , checkPresent = \k -> do ea <- exportActions r anyM (checkPresentExport ea k) - =<< liftIO (getExportLocation db k) + =<< liftIO (getExportedLocation db k) , mkUnavailable = return Nothing , getInfo = do is <- getInfo r @@ -155,10 +155,10 @@ removeEmptyDirectories ea db loc ks ok <- allM (go removeexportdirectory) (reverse (exportDirectories loc)) unless ok $ liftIO $ do - -- Add back to export database, so this is - -- tried again next time. + -- Add location back to export database, + -- so this is tried again next time. forM_ ks $ \k -> - addExportLocation db k loc + addExportedLocation db k loc flushDbQueue db return ok where diff --git a/Remote/S3.hs b/Remote/S3.hs index 398ca13b1..52d03ba94 100644 --- a/Remote/S3.hs +++ b/Remote/S3.hs @@ -615,7 +615,7 @@ getBucketObject c = munge . key2file _ -> getFilePrefix c ++ s getBucketExportLocation :: RemoteConfig -> ExportLocation -> FilePath -getBucketExportLocation c (ExportLocation loc) = getFilePrefix c ++ loc +getBucketExportLocation c loc = getFilePrefix c ++ fromExportLocation loc {- Internet Archive documentation limits filenames to a subset of ascii. - While other characters seem to work now, this entity encodes everything diff --git a/Remote/WebDAV.hs b/Remote/WebDAV.hs index 921146ebd..495b3f8fc 100644 --- a/Remote/WebDAV.hs +++ b/Remote/WebDAV.hs @@ -204,8 +204,8 @@ removeExportDav mh _k loc = runExport mh $ \_dav -> removeHelper (exportLocation loc) removeExportDirectoryDav :: Maybe DavHandle -> ExportDirectory -> Annex Bool -removeExportDirectoryDav mh (ExportDirectory dir) = runExport mh $ \_dav -> - safely (inLocation dir delContentM) +removeExportDirectoryDav mh dir = runExport mh $ \_dav -> + safely (inLocation (fromExportDirectory dir) delContentM) >>= maybe (return False) (const $ return True) renameExportDav :: Maybe DavHandle -> Key -> ExportLocation -> ExportLocation -> Annex Bool diff --git a/Remote/WebDAV/DavLocation.hs b/Remote/WebDAV/DavLocation.hs index 09f2b1b47..cbe87e6a7 100644 --- a/Remote/WebDAV/DavLocation.hs +++ b/Remote/WebDAV/DavLocation.hs @@ -47,7 +47,7 @@ keyLocation :: Key -> DavLocation keyLocation k = keyDir k ++ keyFile k exportLocation :: ExportLocation -> DavLocation -exportLocation (ExportLocation f) = f +exportLocation = fromExportLocation {- Where we store temporary data for a key as it's being uploaded. -} keyTmpLocation :: Key -> DavLocation diff --git a/Types/Export.hs b/Types/Export.hs index cc1b8debf..0e86f9684 100644 --- a/Types/Export.hs +++ b/Types/Export.hs @@ -5,19 +5,41 @@ - Licensed under the GNU GPL version 3 or higher. -} -module Types.Export where +module Types.Export ( + ExportLocation, + mkExportLocation, + fromExportLocation, + ExportDirectory, + mkExportDirectory, + fromExportDirectory, + exportDirectories, +) where + +import Git.FilePath import qualified System.FilePath.Posix as Posix -- A location on a remote that a key can be exported to. -- The FilePath will be relative to the top of the export, --- and may contain unix-style path separators. +-- and uses unix-style path separators. newtype ExportLocation = ExportLocation FilePath deriving (Show, Eq) +mkExportLocation :: FilePath -> ExportLocation +mkExportLocation = ExportLocation . toInternalGitPath + +fromExportLocation :: ExportLocation -> FilePath +fromExportLocation (ExportLocation f) = f + newtype ExportDirectory = ExportDirectory FilePath deriving (Show, Eq) +mkExportDirectory :: FilePath -> ExportDirectory +mkExportDirectory = ExportDirectory . toInternalGitPath + +fromExportDirectory :: ExportDirectory -> FilePath +fromExportDirectory (ExportDirectory f) = f + -- | All subdirectories down to the ExportLocation, with the deepest ones -- last. Does not include the top of the export. exportDirectories :: ExportLocation -> [ExportDirectory] diff --git a/doc/todo/export.mdwn b/doc/todo/export.mdwn index f23ed6866..3ddca0cf8 100644 --- a/doc/todo/export.mdwn +++ b/doc/todo/export.mdwn @@ -26,18 +26,30 @@ Work is in progress. Todo list: export database is not populated. So, seems that the export database needs to get populated based on the export log in these cases. - This needs a (local) record of what tree the (local) export db - was last updated for, which is updated at the same time as the export log. - One way to record that would be as a git ref. + This needs the db to contain a record of the data source, + the tree that most recently populated it. - When the export log contains a different tree than the local - record, the export was updated in another repository, and so the + When the export log contains a different tree than the data source, + the export was updated in another repository, and so the export db needs to be updated. - Updating the export db could diff the last exported treeish with the + Updating the export db could diff the data source with the logged treeish. Add/delete exported files from the database to get it to the same state as the remote database. + When an export is incomplete, the database is in some + state in between the data source tree and the incompletely + exported tree. Diffing won't resolve this. + + When to record the data source? If it's done at the same time the export + is recorded (as no longer incomplete) in the export log, all the files + have not yet been uploaded to the export, and the the database is not + fully updated to match the data source. + + Seems that we need a separate table, to be able to look up filenames + from the export tree by key. That table can be fully populated, + before the Exported table is. + * tracking exports * Support configuring export in the assistant diff --git a/git-annex.cabal b/git-annex.cabal index 1d74b358a..e8c3d2373 100644 --- a/git-annex.cabal +++ b/git-annex.cabal @@ -506,6 +506,7 @@ Executable git-annex Annex.Direct Annex.Drop Annex.Environment + Annex.Export Annex.FileMatcher Annex.Fixup Annex.GitOverlay |