git annex sync --content to exports
Assistant still todo. This commit was sponsored by Boyd Stephen Smith Jr. on Patreon
This commit is contained in:
parent
527f734492
commit
2e69efea8d
6 changed files with 90 additions and 38 deletions
|
@ -10,8 +10,11 @@ module Annex.Export where
|
||||||
import Annex
|
import Annex
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
import Types.Key
|
import Types.Key
|
||||||
|
import Types.Remote
|
||||||
import qualified Git
|
import qualified Git
|
||||||
|
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
-- An export includes both annexed files and files stored in git.
|
-- An export includes both annexed files and files stored in git.
|
||||||
-- For the latter, a SHA1 key is synthesized.
|
-- For the latter, a SHA1 key is synthesized.
|
||||||
data ExportKey = AnnexKey Key | GitKey Key
|
data ExportKey = AnnexKey Key | GitKey Key
|
||||||
|
@ -33,3 +36,8 @@ exportKey sha = mk <$> catKey sha
|
||||||
, keyChunkSize = Nothing
|
, keyChunkSize = Nothing
|
||||||
, keyChunkNum = Nothing
|
, keyChunkNum = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exportTree :: RemoteConfig -> Bool
|
||||||
|
exportTree c = case M.lookup "exporttree" c of
|
||||||
|
Just "yes" -> True
|
||||||
|
_ -> False
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE TupleSections #-}
|
{-# LANGUAGE TupleSections, BangPatterns #-}
|
||||||
|
|
||||||
module Command.Export where
|
module Command.Export where
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import Utility.Tmp
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import Control.Concurrent
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = command "export" SectionCommon
|
cmd = command "export" SectionCommon
|
||||||
|
@ -70,23 +71,27 @@ seek o = do
|
||||||
r <- getParsed (exportRemote o)
|
r <- getParsed (exportRemote o)
|
||||||
unlessM (isExportSupported r) $
|
unlessM (isExportSupported r) $
|
||||||
giveup "That remote does not support exports."
|
giveup "That remote does not support exports."
|
||||||
withExclusiveLock (gitAnnexExportLock (uuid r)) (seek' o r)
|
when (exportTracking o) $
|
||||||
|
setConfig (remoteConfig r "export-tracking")
|
||||||
seek' :: ExportOptions -> Remote -> CommandSeek
|
(fromRef $ exportTreeish o)
|
||||||
seek' o r = do
|
|
||||||
new <- fromMaybe (giveup "unknown tree") <$>
|
new <- fromMaybe (giveup "unknown tree") <$>
|
||||||
-- Dereference the tree pointed to by the branch, commit,
|
-- Dereference the tree pointed to by the branch, commit,
|
||||||
-- or tag.
|
-- or tag.
|
||||||
inRepo (Git.Ref.tree (exportTreeish o))
|
inRepo (Git.Ref.tree (exportTreeish o))
|
||||||
|
withExclusiveLock (gitAnnexExportLock (uuid r)) $ do
|
||||||
|
db <- openDb (uuid r)
|
||||||
|
ea <- exportActions r
|
||||||
|
changeExport r ea db new
|
||||||
|
void $ fillExport r ea db new
|
||||||
|
closeDb db
|
||||||
|
|
||||||
|
-- | Changes what's exported to the remote. Does not upload any new
|
||||||
|
-- files, but does delete and rename files already exported to the remote.
|
||||||
|
changeExport :: Remote -> ExportActions Annex -> ExportHandle -> Git.Ref -> CommandSeek
|
||||||
|
changeExport r ea db new = do
|
||||||
old <- getExport (uuid r)
|
old <- getExport (uuid r)
|
||||||
db <- openDb (uuid r)
|
|
||||||
ea <- exportActions r
|
|
||||||
recordExportBeginning (uuid r) new
|
recordExportBeginning (uuid r) new
|
||||||
|
|
||||||
when (exportTracking o) $
|
|
||||||
setConfig (remoteConfig r "export-tracking")
|
|
||||||
(fromRef $ exportTreeish o)
|
|
||||||
|
|
||||||
-- Clean up after incomplete export of a tree, in which
|
-- Clean up after incomplete export of a tree, in which
|
||||||
-- the next block of code below may have renamed some files to
|
-- the next block of code below may have renamed some files to
|
||||||
-- temp files. Diff from the incomplete tree to the new tree,
|
-- temp files. Diff from the incomplete tree to the new tree,
|
||||||
|
@ -150,13 +155,6 @@ seek' o r = do
|
||||||
{ oldTreeish = map exportedTreeish old
|
{ oldTreeish = map exportedTreeish old
|
||||||
, newTreeish = new
|
, newTreeish = new
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Export everything that is not yet exported.
|
|
||||||
(l, cleanup') <- inRepo $ Git.LsTree.lsTree new
|
|
||||||
seekActions $ pure $ map (startExport r ea db) l
|
|
||||||
void $ liftIO cleanup'
|
|
||||||
|
|
||||||
closeDb db
|
|
||||||
where
|
where
|
||||||
mapdiff a oldtreesha newtreesha = do
|
mapdiff a oldtreesha newtreesha = do
|
||||||
(diff, cleanup) <- inRepo $
|
(diff, cleanup) <- inRepo $
|
||||||
|
@ -187,11 +185,22 @@ mkDiffMap old new db = do
|
||||||
| sha == nullSha = return Nothing
|
| sha == nullSha = return Nothing
|
||||||
| otherwise = Just <$> exportKey sha
|
| otherwise = Just <$> exportKey sha
|
||||||
|
|
||||||
startExport :: Remote -> ExportActions Annex -> ExportHandle -> Git.LsTree.TreeItem -> CommandStart
|
-- | Upload all exported files that are not yet in the remote,
|
||||||
startExport r ea db ti = do
|
-- Returns True when files were uploaded.
|
||||||
|
fillExport :: Remote -> ExportActions Annex -> ExportHandle -> Git.Ref -> Annex Bool
|
||||||
|
fillExport r ea db new = do
|
||||||
|
(l, cleanup) <- inRepo $ Git.LsTree.lsTree new
|
||||||
|
cvar <- liftIO $ newMVar False
|
||||||
|
seekActions $ pure $ map (startExport r ea db cvar) l
|
||||||
|
void $ liftIO $ cleanup
|
||||||
|
liftIO $ takeMVar cvar
|
||||||
|
|
||||||
|
startExport :: Remote -> ExportActions Annex -> ExportHandle -> MVar Bool -> Git.LsTree.TreeItem -> CommandStart
|
||||||
|
startExport r ea db cvar ti = do
|
||||||
ek <- exportKey (Git.LsTree.sha ti)
|
ek <- exportKey (Git.LsTree.sha ti)
|
||||||
stopUnless (liftIO $ notElem loc <$> getExportedLocation db (asKey ek)) $ do
|
stopUnless (liftIO $ notElem loc <$> getExportedLocation db (asKey ek)) $ do
|
||||||
showStart "export" f
|
showStart ("export " ++ name r) f
|
||||||
|
liftIO $ modifyMVar_ cvar (pure . const True)
|
||||||
next $ performExport r ea db ek (Git.LsTree.sha ti) loc
|
next $ performExport r ea db ek (Git.LsTree.sha ti) loc
|
||||||
where
|
where
|
||||||
loc = mkExportLocation f
|
loc = mkExportLocation f
|
||||||
|
@ -234,7 +243,7 @@ startUnexport r ea db f shas = do
|
||||||
if null eks
|
if null eks
|
||||||
then stop
|
then stop
|
||||||
else do
|
else do
|
||||||
showStart "unexport" f'
|
showStart ("unexport " ++ name r) f'
|
||||||
next $ performUnexport r ea db eks loc
|
next $ performUnexport r ea db eks loc
|
||||||
where
|
where
|
||||||
loc = mkExportLocation f'
|
loc = mkExportLocation f'
|
||||||
|
@ -242,7 +251,7 @@ startUnexport r ea db f shas = do
|
||||||
|
|
||||||
startUnexport' :: Remote -> ExportActions Annex -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart
|
startUnexport' :: Remote -> ExportActions Annex -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart
|
||||||
startUnexport' r ea db f ek = do
|
startUnexport' r ea db f ek = do
|
||||||
showStart "unexport" f'
|
showStart ("unexport " ++ name r) f'
|
||||||
next $ performUnexport r ea db [ek] loc
|
next $ performUnexport r ea db [ek] loc
|
||||||
where
|
where
|
||||||
loc = mkExportLocation f'
|
loc = mkExportLocation f'
|
||||||
|
@ -276,7 +285,7 @@ startRecoverIncomplete r ea db sha oldf
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
ek <- exportKey sha
|
ek <- exportKey sha
|
||||||
let loc = exportTempName ek
|
let loc = exportTempName ek
|
||||||
showStart "unexport" (fromExportLocation loc)
|
showStart ("unexport " ++ name r) (fromExportLocation loc)
|
||||||
liftIO $ removeExportedLocation db (asKey ek) oldloc
|
liftIO $ removeExportedLocation db (asKey ek) oldloc
|
||||||
next $ performUnexport r ea db [ek] loc
|
next $ performUnexport r ea db [ek] loc
|
||||||
where
|
where
|
||||||
|
@ -285,7 +294,7 @@ startRecoverIncomplete r ea db sha oldf
|
||||||
|
|
||||||
startMoveToTempName :: Remote -> ExportActions Annex -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart
|
startMoveToTempName :: Remote -> ExportActions Annex -> ExportHandle -> TopFilePath -> ExportKey -> CommandStart
|
||||||
startMoveToTempName r ea db f ek = do
|
startMoveToTempName r ea db f ek = do
|
||||||
showStart "rename" (f' ++ " -> " ++ fromExportLocation tmploc)
|
showStart ("rename " ++ name r) (f' ++ " -> " ++ fromExportLocation tmploc)
|
||||||
next $ performRename r ea db ek loc tmploc
|
next $ performRename r ea db ek loc tmploc
|
||||||
where
|
where
|
||||||
loc = mkExportLocation f'
|
loc = mkExportLocation f'
|
||||||
|
@ -296,7 +305,7 @@ startMoveFromTempName :: Remote -> ExportActions Annex -> ExportHandle -> Export
|
||||||
startMoveFromTempName r ea db ek f = do
|
startMoveFromTempName r ea db ek f = do
|
||||||
let tmploc = exportTempName ek
|
let tmploc = exportTempName ek
|
||||||
stopUnless (liftIO $ elem tmploc <$> getExportedLocation db (asKey ek)) $ do
|
stopUnless (liftIO $ elem tmploc <$> getExportedLocation db (asKey ek)) $ do
|
||||||
showStart "rename" (fromExportLocation tmploc ++ " -> " ++ f')
|
showStart ("rename " ++ name r) (fromExportLocation tmploc ++ " -> " ++ f')
|
||||||
next $ performRename r ea db ek tmploc loc
|
next $ performRename r ea db ek tmploc loc
|
||||||
where
|
where
|
||||||
loc = mkExportLocation f'
|
loc = mkExportLocation f'
|
||||||
|
|
|
@ -46,14 +46,19 @@ import Annex.Wanted
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import Command.Get (getKey')
|
import Command.Get (getKey')
|
||||||
import qualified Command.Move
|
import qualified Command.Move
|
||||||
|
import qualified Command.Export
|
||||||
import Annex.Drop
|
import Annex.Drop
|
||||||
import Annex.UUID
|
import Annex.UUID
|
||||||
import Logs.UUID
|
import Logs.UUID
|
||||||
|
import Logs.Export
|
||||||
import Annex.AutoMerge
|
import Annex.AutoMerge
|
||||||
import Annex.AdjustedBranch
|
import Annex.AdjustedBranch
|
||||||
import Annex.Ssh
|
import Annex.Ssh
|
||||||
import Annex.BloomFilter
|
import Annex.BloomFilter
|
||||||
import Annex.UpdateInstead
|
import Annex.UpdateInstead
|
||||||
|
import Annex.Export
|
||||||
|
import Annex.LockFile
|
||||||
|
import qualified Database.Export as Export
|
||||||
import Utility.Bloom
|
import Utility.Bloom
|
||||||
import Utility.OptParse
|
import Utility.OptParse
|
||||||
|
|
||||||
|
@ -153,7 +158,8 @@ seek o = allowConcurrentOutput $ do
|
||||||
|
|
||||||
remotes <- syncRemotes (syncWith o)
|
remotes <- syncRemotes (syncWith o)
|
||||||
let gitremotes = filter Remote.gitSyncableRemote remotes
|
let gitremotes = filter Remote.gitSyncableRemote remotes
|
||||||
dataremotes <- filter (\r -> Remote.uuid r /= NoUUID)
|
(exportremotes, dataremotes) <- partition (exportTree . Remote.config)
|
||||||
|
. filter (\r -> Remote.uuid r /= NoUUID)
|
||||||
<$> filterM (not <$$> liftIO . getDynamicConfig . remoteAnnexIgnore . Remote.gitconfig) remotes
|
<$> filterM (not <$$> liftIO . getDynamicConfig . remoteAnnexIgnore . Remote.gitconfig) remotes
|
||||||
|
|
||||||
-- Syncing involves many actions, any of which can independently
|
-- Syncing involves many actions, any of which can independently
|
||||||
|
@ -165,16 +171,19 @@ seek o = allowConcurrentOutput $ do
|
||||||
, map (withbranch . pullRemote o mergeConfig) gitremotes
|
, map (withbranch . pullRemote o mergeConfig) gitremotes
|
||||||
, [ mergeAnnex ]
|
, [ mergeAnnex ]
|
||||||
]
|
]
|
||||||
whenM (shouldsynccontent <&&> seekSyncContent o dataremotes) $
|
whenM shouldsynccontent $ do
|
||||||
|
syncedcontent <- seekSyncContent o dataremotes
|
||||||
|
exportedcontent <- seekExportContent exportremotes
|
||||||
-- Transferring content can take a while,
|
-- Transferring content can take a while,
|
||||||
-- and other changes can be pushed to the git-annex
|
-- and other changes can be pushed to the git-annex
|
||||||
-- branch on the remotes in the meantime, so pull
|
-- branch on the remotes in the meantime, so pull
|
||||||
-- and merge again to avoid our push overwriting
|
-- and merge again to avoid our push overwriting
|
||||||
-- those changes.
|
-- those changes.
|
||||||
mapM_ includeCommandAction $ concat
|
when (syncedcontent || exportedcontent) $ do
|
||||||
[ map (withbranch . pullRemote o mergeConfig) gitremotes
|
mapM_ includeCommandAction $ concat
|
||||||
, [ commitAnnex, mergeAnnex ]
|
[ map (withbranch . pullRemote o mergeConfig) gitremotes
|
||||||
]
|
, [ commitAnnex, mergeAnnex ]
|
||||||
|
]
|
||||||
|
|
||||||
void $ includeCommandAction $ withbranch pushLocal
|
void $ includeCommandAction $ withbranch pushLocal
|
||||||
-- Pushes to remotes can run concurrently.
|
-- Pushes to remotes can run concurrently.
|
||||||
|
@ -640,3 +649,32 @@ syncFile ebloom rs af k = do
|
||||||
)
|
)
|
||||||
put dest = includeCommandAction $
|
put dest = includeCommandAction $
|
||||||
Command.Move.toStart' dest False af k (mkActionItem af)
|
Command.Move.toStart' dest False af k (mkActionItem af)
|
||||||
|
|
||||||
|
{- When a remote has an export-tracking branch, change the export to
|
||||||
|
- follow the current content of the branch. Otherwise, transfer any files
|
||||||
|
- that were part of an export but are not in the remote yet. -}
|
||||||
|
seekExportContent :: [Remote] -> Annex Bool
|
||||||
|
seekExportContent rs = or <$> forM rs go
|
||||||
|
where
|
||||||
|
go r = withExclusiveLock (gitAnnexExportLock (Remote.uuid r)) $ do
|
||||||
|
db <- Export.openDb (Remote.uuid r)
|
||||||
|
ea <- Remote.exportActions r
|
||||||
|
exported <- case remoteAnnexExportTracking (Remote.gitconfig r) of
|
||||||
|
Nothing -> getExport (Remote.uuid r)
|
||||||
|
Just b -> do
|
||||||
|
mcur <- inRepo $ Git.Ref.tree b
|
||||||
|
case mcur of
|
||||||
|
Nothing -> getExport (Remote.uuid r)
|
||||||
|
Just cur -> do
|
||||||
|
Command.Export.changeExport r ea db cur
|
||||||
|
return [Exported cur []]
|
||||||
|
Export.closeDb db `after` fillexport r ea db exported
|
||||||
|
|
||||||
|
fillexport _ _ _ [] = return False
|
||||||
|
fillexport r ea db (Exported { exportedTreeish = t }:[]) =
|
||||||
|
Command.Export.fillExport r ea db t
|
||||||
|
fillexport r _ _ _ = do
|
||||||
|
warning $ "Export conflict detected. Different trees have been exported to " ++
|
||||||
|
Remote.name r ++
|
||||||
|
". Use git-annex export to resolve this conflict."
|
||||||
|
return False
|
||||||
|
|
|
@ -17,6 +17,7 @@ import Backend
|
||||||
import Remote.Helper.Encryptable (isEncrypted)
|
import Remote.Helper.Encryptable (isEncrypted)
|
||||||
import Database.Export
|
import Database.Export
|
||||||
import Logs.Export
|
import Logs.Export
|
||||||
|
import Annex.Export
|
||||||
import Annex.LockFile
|
import Annex.LockFile
|
||||||
import Git.Sha
|
import Git.Sha
|
||||||
|
|
||||||
|
@ -42,11 +43,6 @@ instance HasExportUnsupported (Annex (ExportActions Annex)) where
|
||||||
, renameExport = \_ _ _ -> return False
|
, renameExport = \_ _ _ -> return False
|
||||||
}
|
}
|
||||||
|
|
||||||
exportTree :: RemoteConfig -> Bool
|
|
||||||
exportTree c = case M.lookup "exporttree" c of
|
|
||||||
Just "yes" -> True
|
|
||||||
_ -> False
|
|
||||||
|
|
||||||
exportIsSupported :: RemoteConfig -> RemoteGitConfig -> Annex Bool
|
exportIsSupported :: RemoteConfig -> RemoteGitConfig -> Annex Bool
|
||||||
exportIsSupported = \_ _ -> return True
|
exportIsSupported = \_ _ -> return True
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import System.Log.Logger
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
import Types.Remote
|
import Types.Remote
|
||||||
import Types.Export
|
import Types.Export
|
||||||
|
import Annex.Export
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import Config
|
import Config
|
||||||
import Config.Cost
|
import Config.Cost
|
||||||
|
|
|
@ -17,7 +17,7 @@ there need to be a new interface in supported remotes?
|
||||||
|
|
||||||
Work is in progress. Todo list:
|
Work is in progress. Todo list:
|
||||||
|
|
||||||
* tracking exports
|
* Make assistant update tracking exports.
|
||||||
|
|
||||||
* Support configuring export in the assistant
|
* Support configuring export in the assistant
|
||||||
(when eg setting up a S3 special remote).
|
(when eg setting up a S3 special remote).
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue