2010-10-27 20:53:54 +00:00
|
|
|
{- git-annex remote repositories
|
|
|
|
-
|
|
|
|
- Copyright 2010 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
2010-10-13 19:55:18 +00:00
|
|
|
|
|
|
|
module Remotes (
|
2010-10-14 06:41:54 +00:00
|
|
|
list,
|
2010-10-23 17:18:47 +00:00
|
|
|
keyPossibilities,
|
|
|
|
tryGitConfigRead,
|
2010-10-23 18:14:36 +00:00
|
|
|
inAnnex,
|
|
|
|
commandLineRemote,
|
2010-10-23 18:58:14 +00:00
|
|
|
copyFromRemote,
|
2010-10-25 21:17:03 +00:00
|
|
|
copyToRemote,
|
2010-10-25 22:32:29 +00:00
|
|
|
runCmd
|
2010-10-13 19:55:18 +00:00
|
|
|
) where
|
|
|
|
|
2010-10-25 22:32:29 +00:00
|
|
|
import IO (bracket_)
|
2010-10-30 21:29:11 +00:00
|
|
|
import Control.Exception.Extensible hiding (bracket_)
|
2010-10-14 01:28:47 +00:00
|
|
|
import Control.Monad.State (liftIO)
|
2010-10-22 18:28:47 +00:00
|
|
|
import Control.Monad (filterM)
|
2010-10-14 02:59:43 +00:00
|
|
|
import qualified Data.Map as Map
|
2010-10-14 03:18:58 +00:00
|
|
|
import Data.String.Utils
|
2010-10-25 21:17:03 +00:00
|
|
|
import System.Directory
|
2010-10-25 22:32:29 +00:00
|
|
|
import System.Posix.Directory
|
2010-10-14 17:11:42 +00:00
|
|
|
import List
|
2010-10-28 16:40:05 +00:00
|
|
|
import Monad (when, unless)
|
2010-10-16 20:20:49 +00:00
|
|
|
|
2010-10-14 07:18:11 +00:00
|
|
|
import Types
|
2010-10-14 06:36:41 +00:00
|
|
|
import qualified GitRepo as Git
|
2010-10-14 07:18:11 +00:00
|
|
|
import qualified Annex
|
2010-10-13 19:55:18 +00:00
|
|
|
import LocationLog
|
2010-10-14 03:18:58 +00:00
|
|
|
import Locations
|
2010-10-13 19:55:18 +00:00
|
|
|
import UUID
|
2010-10-23 18:14:36 +00:00
|
|
|
import Utility
|
2010-10-29 18:10:55 +00:00
|
|
|
import qualified Core
|
2010-10-13 19:55:18 +00:00
|
|
|
|
|
|
|
{- Human visible list of remotes. -}
|
2010-10-14 06:41:54 +00:00
|
|
|
list :: [Git.Repo] -> String
|
2010-10-22 19:21:23 +00:00
|
|
|
list remotes = join ", " $ map Git.repoDescribe remotes
|
2010-10-13 19:55:18 +00:00
|
|
|
|
|
|
|
{- Cost ordered list of remotes that the LocationLog indicate may have a key. -}
|
2010-10-23 17:18:47 +00:00
|
|
|
keyPossibilities :: Key -> Annex [Git.Repo]
|
|
|
|
keyPossibilities key = do
|
2010-10-14 07:18:11 +00:00
|
|
|
g <- Annex.gitRepo
|
2010-10-14 01:28:47 +00:00
|
|
|
uuids <- liftIO $ keyLocations g key
|
2010-10-14 03:18:58 +00:00
|
|
|
allremotes <- remotesByCost
|
2010-10-22 18:28:47 +00:00
|
|
|
-- To determine if a remote has a key, its UUID needs to be known.
|
|
|
|
-- The locally cached UIIDs of remotes can fall out of date if
|
|
|
|
-- eg, a different drive is mounted at the same location.
|
|
|
|
-- But, reading the config of remotes can be expensive, so make
|
|
|
|
-- sure we only do it once per git-annex run.
|
2010-10-21 20:30:16 +00:00
|
|
|
remotesread <- Annex.flagIsSet "remotesread"
|
2010-10-22 18:28:47 +00:00
|
|
|
if (remotesread)
|
|
|
|
then reposByUUID allremotes uuids
|
|
|
|
else do
|
|
|
|
-- We assume that it's cheap to read the config
|
|
|
|
-- of non-URL remotes, so that is done each time.
|
|
|
|
-- But reading the config of an URL remote is
|
|
|
|
-- only done when there is no cached UUID value.
|
|
|
|
let cheap = filter (not . Git.repoIsUrl) allremotes
|
|
|
|
let expensive = filter Git.repoIsUrl allremotes
|
|
|
|
doexpensive <- filterM cachedUUID expensive
|
2010-10-28 16:40:05 +00:00
|
|
|
unless (null doexpensive) $ do
|
|
|
|
Core.showNote $ "getting UUID for " ++
|
|
|
|
(list doexpensive) ++ "..."
|
2010-10-22 18:28:47 +00:00
|
|
|
let todo = cheap ++ doexpensive
|
2010-10-23 00:47:14 +00:00
|
|
|
if (not $ null todo)
|
2010-10-22 18:28:47 +00:00
|
|
|
then do
|
2010-10-31 18:23:51 +00:00
|
|
|
_ <- mapM tryGitConfigRead todo
|
2010-10-22 18:28:47 +00:00
|
|
|
Annex.flagChange "remotesread" $ FlagBool True
|
2010-10-23 17:18:47 +00:00
|
|
|
keyPossibilities key
|
2010-10-22 18:28:47 +00:00
|
|
|
else reposByUUID allremotes uuids
|
2010-10-14 17:11:42 +00:00
|
|
|
where
|
2010-10-22 18:28:47 +00:00
|
|
|
cachedUUID r = do
|
|
|
|
u <- getUUID r
|
2010-10-23 00:47:14 +00:00
|
|
|
return $ null u
|
2010-10-13 19:55:18 +00:00
|
|
|
|
2010-10-23 17:18:47 +00:00
|
|
|
{- Checks if a given remote has the content for a key inAnnex.
|
|
|
|
- If the remote cannot be accessed, returns a Left error.
|
|
|
|
-}
|
|
|
|
inAnnex :: Git.Repo -> Key -> Annex (Either IOException Bool)
|
|
|
|
inAnnex remote key = do
|
2010-10-23 18:58:14 +00:00
|
|
|
-- the check needs to run in an Annex monad using the remote
|
2010-10-24 02:25:07 +00:00
|
|
|
liftIO $ ((try $ check)::IO (Either IOException Bool))
|
2010-10-23 17:18:47 +00:00
|
|
|
where
|
2010-10-24 02:25:07 +00:00
|
|
|
check = do
|
|
|
|
a <- Annex.new remote []
|
2010-10-23 17:18:47 +00:00
|
|
|
(result, _) <- Annex.run a (Core.inAnnex key)
|
|
|
|
return result
|
|
|
|
|
2010-10-13 19:55:18 +00:00
|
|
|
{- Cost Ordered list of remotes. -}
|
2010-10-14 06:36:41 +00:00
|
|
|
remotesByCost :: Annex [Git.Repo]
|
2010-10-14 01:28:47 +00:00
|
|
|
remotesByCost = do
|
2010-10-14 07:18:11 +00:00
|
|
|
g <- Annex.gitRepo
|
2010-10-14 06:36:41 +00:00
|
|
|
reposByCost $ Git.remotes g
|
2010-10-13 19:55:18 +00:00
|
|
|
|
2010-10-23 00:35:39 +00:00
|
|
|
{- Orders a list of git repos by cost. Throws out ignored ones. -}
|
2010-10-14 06:36:41 +00:00
|
|
|
reposByCost :: [Git.Repo] -> Annex [Git.Repo]
|
2010-10-14 01:28:47 +00:00
|
|
|
reposByCost l = do
|
2010-10-22 18:28:47 +00:00
|
|
|
notignored <- filterM repoNotIgnored l
|
|
|
|
costpairs <- mapM costpair notignored
|
2010-10-14 01:28:47 +00:00
|
|
|
return $ fst $ unzip $ sortBy bycost $ costpairs
|
2010-10-13 19:55:18 +00:00
|
|
|
where
|
2010-10-14 01:28:47 +00:00
|
|
|
costpair r = do
|
|
|
|
cost <- repoCost r
|
|
|
|
return (r, cost)
|
|
|
|
bycost (_, c1) (_, c2) = compare c1 c2
|
2010-10-13 19:55:18 +00:00
|
|
|
|
|
|
|
{- Calculates cost for a repo.
|
|
|
|
-
|
|
|
|
- The default cost is 100 for local repositories, and 200 for remote
|
|
|
|
- repositories; it can also be configured by remote.<name>.annex-cost
|
|
|
|
-}
|
2010-10-14 06:36:41 +00:00
|
|
|
repoCost :: Git.Repo -> Annex Int
|
2010-10-14 01:28:47 +00:00
|
|
|
repoCost r = do
|
2010-10-14 07:18:11 +00:00
|
|
|
g <- Annex.gitRepo
|
2010-10-31 18:23:51 +00:00
|
|
|
if (not $ null $ config g)
|
|
|
|
then return $ read $ config g
|
2010-10-22 18:28:47 +00:00
|
|
|
else if (Git.repoIsUrl r)
|
|
|
|
then return 200
|
|
|
|
else return 100
|
2010-10-13 19:55:18 +00:00
|
|
|
where
|
2010-10-31 18:23:51 +00:00
|
|
|
config g = Git.configGet g configkey ""
|
|
|
|
configkey = "remote." ++ (Git.repoRemoteName r) ++ ".annex-cost"
|
2010-10-14 02:59:43 +00:00
|
|
|
|
2010-10-23 00:35:39 +00:00
|
|
|
{- Checks if a repo should be ignored, based either on annex-ignore
|
|
|
|
- setting, or on command-line options. Allows command-line to override
|
|
|
|
- annex-ignore. -}
|
2010-10-22 18:28:47 +00:00
|
|
|
repoNotIgnored :: Git.Repo -> Annex Bool
|
|
|
|
repoNotIgnored r = do
|
|
|
|
g <- Annex.gitRepo
|
2010-10-23 16:35:10 +00:00
|
|
|
fromName <- Annex.flagGet "fromrepository"
|
|
|
|
toName <- Annex.flagGet "torepository"
|
|
|
|
let name = if (not $ null fromName) then fromName else toName
|
2010-10-23 00:35:39 +00:00
|
|
|
if (not $ null name)
|
|
|
|
then return $ match name
|
2010-10-28 16:15:21 +00:00
|
|
|
else return $ not $ ignored g
|
2010-10-22 18:28:47 +00:00
|
|
|
where
|
2010-10-23 00:35:39 +00:00
|
|
|
match name = name == Git.repoRemoteName r
|
2010-10-28 16:15:21 +00:00
|
|
|
ignored g = Git.configTrue $ config g
|
2010-10-23 00:35:39 +00:00
|
|
|
config g = Git.configGet g configkey ""
|
|
|
|
configkey = "remote." ++ (Git.repoRemoteName r) ++ ".annex-ignore"
|
2010-10-22 18:28:47 +00:00
|
|
|
|
2010-10-23 18:14:36 +00:00
|
|
|
{- Returns the remote specified by --from or --to, may fail with error. -}
|
|
|
|
commandLineRemote :: Annex Git.Repo
|
|
|
|
commandLineRemote = do
|
|
|
|
fromName <- Annex.flagGet "fromrepository"
|
|
|
|
toName <- Annex.flagGet "torepository"
|
|
|
|
let name = if (not $ null fromName) then fromName else toName
|
2010-10-28 18:20:02 +00:00
|
|
|
when (null name) $ error "no remote specified"
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
let match = filter (\r -> name == Git.repoRemoteName r) $
|
|
|
|
Git.remotes g
|
|
|
|
when (null match) $ error $
|
|
|
|
"there is no git remote named \"" ++ name ++ "\""
|
|
|
|
return $ match !! 0
|
2010-10-23 18:14:36 +00:00
|
|
|
|
2010-10-14 02:59:43 +00:00
|
|
|
{- The git configs for the git repo's remotes is not read on startup
|
2010-10-14 17:11:42 +00:00
|
|
|
- because reading it may be expensive. This function tries to read the
|
|
|
|
- config for a specified remote, and updates state. If successful, it
|
|
|
|
- returns the updated git repo. -}
|
2010-10-19 18:13:48 +00:00
|
|
|
tryGitConfigRead :: Git.Repo -> Annex (Either Git.Repo Git.Repo)
|
2010-10-14 17:11:42 +00:00
|
|
|
tryGitConfigRead r = do
|
2010-10-14 06:36:41 +00:00
|
|
|
if (Map.null $ Git.configMap r)
|
2010-10-14 02:59:43 +00:00
|
|
|
then do
|
2010-10-16 21:44:59 +00:00
|
|
|
-- configRead can fail due to IO error or
|
|
|
|
-- for other reasons; catch all possible exceptions
|
|
|
|
result <- liftIO $ (try (Git.configRead r)::IO (Either SomeException (Git.Repo)))
|
2010-10-14 17:11:42 +00:00
|
|
|
case (result) of
|
2010-10-31 18:23:51 +00:00
|
|
|
Left _ -> return $ Left r
|
2010-10-14 17:11:42 +00:00
|
|
|
Right r' -> do
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
let l = Git.remotes g
|
|
|
|
let g' = Git.remotesAdd g $
|
|
|
|
exchange l r'
|
|
|
|
Annex.gitRepoChange g'
|
2010-10-19 18:13:48 +00:00
|
|
|
return $ Right r'
|
|
|
|
else return $ Right r -- config already read
|
2010-10-14 02:59:43 +00:00
|
|
|
where
|
2010-10-31 18:23:51 +00:00
|
|
|
exchange [] _ = []
|
2010-10-14 02:59:43 +00:00
|
|
|
exchange (old:ls) new =
|
2010-10-28 18:20:02 +00:00
|
|
|
if (Git.repoRemoteName old == Git.repoRemoteName new)
|
2010-10-14 02:59:43 +00:00
|
|
|
then new:(exchange ls new)
|
|
|
|
else old:(exchange ls new)
|
2010-10-23 18:14:36 +00:00
|
|
|
|
2010-10-23 18:58:14 +00:00
|
|
|
{- Tries to copy a key's content from a remote to a file. -}
|
2010-10-23 18:14:36 +00:00
|
|
|
copyFromRemote :: Git.Repo -> Key -> FilePath -> Annex Bool
|
|
|
|
copyFromRemote r key file = do
|
2010-10-25 21:31:07 +00:00
|
|
|
if (not $ Git.repoIsUrl r)
|
|
|
|
then getlocal
|
|
|
|
else if (Git.repoIsSsh r)
|
|
|
|
then getssh
|
|
|
|
else error "copying from non-ssh repo not supported"
|
2010-10-23 18:14:36 +00:00
|
|
|
where
|
2010-10-26 00:19:08 +00:00
|
|
|
getlocal = liftIO $ boolSystem "cp" ["-a", keyloc, file]
|
2010-10-25 21:31:07 +00:00
|
|
|
getssh = do
|
2010-10-29 18:10:55 +00:00
|
|
|
Core.showProgress -- make way for scp progress bar
|
2010-10-26 00:19:08 +00:00
|
|
|
liftIO $ boolSystem "scp" [sshLocation r keyloc, file]
|
|
|
|
keyloc = annexLocation r key
|
2010-10-23 18:58:14 +00:00
|
|
|
|
2010-10-26 00:19:08 +00:00
|
|
|
{- Tries to copy a key's content to a file on a remote. -}
|
|
|
|
copyToRemote :: Git.Repo -> Key -> FilePath -> Annex Bool
|
|
|
|
copyToRemote r key file = do
|
2010-10-23 18:58:14 +00:00
|
|
|
g <- Annex.gitRepo
|
2010-10-26 00:19:08 +00:00
|
|
|
let keyloc = annexLocation g key
|
2010-10-23 18:58:14 +00:00
|
|
|
if (not $ Git.repoIsUrl r)
|
2010-10-26 00:19:08 +00:00
|
|
|
then putlocal keyloc
|
2010-10-23 18:58:14 +00:00
|
|
|
else if (Git.repoIsSsh r)
|
2010-10-26 00:19:08 +00:00
|
|
|
then putssh keyloc
|
2010-10-23 18:58:14 +00:00
|
|
|
else error "copying to non-ssh repo not supported"
|
|
|
|
where
|
2010-10-26 00:19:08 +00:00
|
|
|
putlocal src = liftIO $ boolSystem "cp" ["-a", src, file]
|
|
|
|
putssh src = do
|
2010-10-29 18:10:55 +00:00
|
|
|
Core.showProgress -- make way for scp progress bar
|
2010-10-26 00:19:08 +00:00
|
|
|
liftIO $ boolSystem "scp" [src, sshLocation r file]
|
|
|
|
|
|
|
|
sshLocation :: Git.Repo -> FilePath -> FilePath
|
2010-11-01 02:13:43 +00:00
|
|
|
sshLocation r file = (Git.urlHost r) ++ ":" ++ shellEscape file
|
2010-10-25 21:17:03 +00:00
|
|
|
|
2010-10-25 22:32:29 +00:00
|
|
|
{- Runs a command in a remote. -}
|
|
|
|
runCmd :: Git.Repo -> String -> [String] -> Annex Bool
|
|
|
|
runCmd r command params = do
|
2010-10-25 21:17:03 +00:00
|
|
|
if (not $ Git.repoIsUrl r)
|
2010-10-25 22:32:29 +00:00
|
|
|
then do
|
|
|
|
cwd <- liftIO $ getCurrentDirectory
|
|
|
|
liftIO $ bracket_ (changeWorkingDirectory (Git.workTree r))
|
|
|
|
(\_ -> changeWorkingDirectory cwd) $
|
|
|
|
boolSystem command params
|
2010-10-25 21:17:03 +00:00
|
|
|
else if (Git.repoIsSsh r)
|
|
|
|
then do
|
2010-10-25 22:32:29 +00:00
|
|
|
liftIO $ boolSystem "ssh" [Git.urlHost r,
|
|
|
|
"cd " ++ (shellEscape $ Git.workTree r) ++
|
2010-11-01 02:19:25 +00:00
|
|
|
" && " ++ (shellEscape command) ++ " " ++
|
|
|
|
(unwords $ map shellEscape params)]
|
2010-10-25 22:32:29 +00:00
|
|
|
else error "running command in non-ssh repo not supported"
|