use a state monad
enormous reworking
This commit is contained in:
parent
e5c1db355f
commit
b160748516
11 changed files with 251 additions and 157 deletions
128
Annex.hs
128
Annex.hs
|
@ -12,6 +12,7 @@ module Annex (
|
||||||
annexPullRepo
|
annexPullRepo
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.State (liftIO)
|
||||||
import System.Posix.Files
|
import System.Posix.Files
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import Data.String.Utils
|
import Data.String.Utils
|
||||||
|
@ -25,22 +26,27 @@ import UUID
|
||||||
import LocationLog
|
import LocationLog
|
||||||
import Types
|
import Types
|
||||||
|
|
||||||
{- On startup, examine the git repo, prepare it, and record state for
|
{- Create and returns an Annex state object.
|
||||||
- later. -}
|
- Examines and prepares the git repo.
|
||||||
startAnnex :: IO State
|
-}
|
||||||
|
startAnnex :: IO AnnexState
|
||||||
startAnnex = do
|
startAnnex = do
|
||||||
r <- gitRepoFromCwd
|
g <- gitRepoFromCwd
|
||||||
r' <- gitConfigRead r
|
let s = makeAnnexState g
|
||||||
r'' <- prepUUID r'
|
(_,s') <- runAnnexState s (prep g)
|
||||||
gitSetup r''
|
return s'
|
||||||
|
where
|
||||||
return State {
|
prep g = do
|
||||||
repo = r',
|
-- setup git and read its config; update state
|
||||||
backends = parseBackendList $ gitConfig r' "annex.backends" ""
|
liftIO $ gitSetup g
|
||||||
}
|
g' <- liftIO $ gitConfigRead g
|
||||||
|
gitAnnexChange g'
|
||||||
|
backendsAnnexChange $ parseBackendList $
|
||||||
|
gitConfig g' "annex.backends" ""
|
||||||
|
prepUUID
|
||||||
|
|
||||||
inBackend file yes no = do
|
inBackend file yes no = do
|
||||||
r <- lookupFile file
|
r <- liftIO $ lookupFile file
|
||||||
case (r) of
|
case (r) of
|
||||||
Just v -> yes v
|
Just v -> yes v
|
||||||
Nothing -> no
|
Nothing -> no
|
||||||
|
@ -48,13 +54,16 @@ notinBackend file yes no = inBackend file no yes
|
||||||
|
|
||||||
{- Annexes a file, storing it in a backend, and then moving it into
|
{- Annexes a file, storing it in a backend, and then moving it into
|
||||||
- the annex directory and setting up the symlink pointing to its content. -}
|
- the annex directory and setting up the symlink pointing to its content. -}
|
||||||
annexFile :: State -> FilePath -> IO ()
|
annexFile :: FilePath -> Annex ()
|
||||||
annexFile state file = inBackend file err $ do
|
annexFile file = inBackend file err $ do
|
||||||
checkLegal file
|
liftIO $ checkLegal file
|
||||||
stored <- storeFile state file
|
stored <- storeFile file
|
||||||
|
g <- gitAnnex
|
||||||
case (stored) of
|
case (stored) of
|
||||||
Nothing -> error $ "no backend could store: " ++ file
|
Nothing -> error $ "no backend could store: " ++ file
|
||||||
Just (key, backend) -> setup key backend
|
Just (key, backend) -> do
|
||||||
|
logStatus key ValuePresent
|
||||||
|
liftIO $ setup g key backend
|
||||||
where
|
where
|
||||||
err = error $ "already annexed " ++ file
|
err = error $ "already annexed " ++ file
|
||||||
checkLegal file = do
|
checkLegal file = do
|
||||||
|
@ -62,15 +71,14 @@ annexFile state file = inBackend file err $ do
|
||||||
if ((isSymbolicLink s) || (not $ isRegularFile s))
|
if ((isSymbolicLink s) || (not $ isRegularFile s))
|
||||||
then error $ "not a regular file: " ++ file
|
then error $ "not a regular file: " ++ file
|
||||||
else return ()
|
else return ()
|
||||||
setup key backend = do
|
setup g key backend = do
|
||||||
logStatus state key ValuePresent
|
let dest = annexLocation g backend key
|
||||||
let dest = annexLocation (repo state) backend key
|
let reldest = annexLocationRelative g backend key
|
||||||
let reldest = annexLocationRelative (repo state) backend key
|
|
||||||
createDirectoryIfMissing True (parentDir dest)
|
createDirectoryIfMissing True (parentDir dest)
|
||||||
renameFile file dest
|
renameFile file dest
|
||||||
createSymbolicLink ((linkTarget file) ++ reldest) file
|
createSymbolicLink ((linkTarget file) ++ reldest) file
|
||||||
gitRun (repo state) ["add", file]
|
gitRun g ["add", file]
|
||||||
gitRun (repo state) ["commit", "-m",
|
gitRun g ["commit", "-m",
|
||||||
("git-annex annexed " ++ file), file]
|
("git-annex annexed " ++ file), file]
|
||||||
linkTarget file =
|
linkTarget file =
|
||||||
-- relies on file being relative to the top of the
|
-- relies on file being relative to the top of the
|
||||||
|
@ -83,56 +91,60 @@ annexFile state file = inBackend file err $ do
|
||||||
|
|
||||||
|
|
||||||
{- Inverse of annexFile. -}
|
{- Inverse of annexFile. -}
|
||||||
unannexFile :: State -> FilePath -> IO ()
|
unannexFile :: FilePath -> Annex ()
|
||||||
unannexFile state file = notinBackend file err $ \(key, backend) -> do
|
unannexFile file = notinBackend file err $ \(key, backend) -> do
|
||||||
dropFile state backend key
|
dropFile backend key
|
||||||
logStatus state key ValueMissing
|
logStatus key ValueMissing
|
||||||
|
g <- gitAnnex
|
||||||
|
let src = annexLocation g backend key
|
||||||
|
liftIO $ moveout g src
|
||||||
|
where
|
||||||
|
err = error $ "not annexed " ++ file
|
||||||
|
moveout g src = do
|
||||||
removeFile file
|
removeFile file
|
||||||
gitRun (repo state) ["rm", file]
|
gitRun g ["rm", file]
|
||||||
gitRun (repo state) ["commit", "-m",
|
gitRun g ["commit", "-m",
|
||||||
("git-annex unannexed " ++ file), file]
|
("git-annex unannexed " ++ file), file]
|
||||||
-- git rm deletes empty directories;
|
-- git rm deletes empty directories;
|
||||||
-- put them back
|
-- put them back
|
||||||
createDirectoryIfMissing True (parentDir file)
|
createDirectoryIfMissing True (parentDir file)
|
||||||
let src = annexLocation (repo state) backend key
|
|
||||||
renameFile src file
|
renameFile src file
|
||||||
return ()
|
return ()
|
||||||
where
|
|
||||||
err = error $ "not annexed " ++ file
|
|
||||||
|
|
||||||
{- Gets an annexed file from one of the backends. -}
|
{- Gets an annexed file from one of the backends. -}
|
||||||
annexGetFile :: State -> FilePath -> IO ()
|
annexGetFile :: FilePath -> Annex ()
|
||||||
annexGetFile state file = notinBackend file err $ \(key, backend) -> do
|
annexGetFile file = notinBackend file err $ \(key, backend) -> do
|
||||||
inannex <- inAnnex state backend key
|
inannex <- inAnnex backend key
|
||||||
if (inannex)
|
if (inannex)
|
||||||
then return ()
|
then return ()
|
||||||
else do
|
else do
|
||||||
let dest = annexLocation (repo state) backend key
|
g <- gitAnnex
|
||||||
createDirectoryIfMissing True (parentDir dest)
|
let dest = annexLocation g backend key
|
||||||
success <- retrieveFile state backend key dest
|
liftIO $ createDirectoryIfMissing True (parentDir dest)
|
||||||
|
success <- retrieveFile backend key dest
|
||||||
if (success)
|
if (success)
|
||||||
then do
|
then do
|
||||||
logStatus state key ValuePresent
|
logStatus key ValuePresent
|
||||||
return ()
|
return ()
|
||||||
else error $ "failed to get " ++ file
|
else error $ "failed to get " ++ file
|
||||||
where
|
where
|
||||||
err = error $ "not annexed " ++ file
|
err = error $ "not annexed " ++ file
|
||||||
|
|
||||||
{- Indicates a file is wanted. -}
|
{- Indicates a file is wanted. -}
|
||||||
annexWantFile :: State -> FilePath -> IO ()
|
annexWantFile :: FilePath -> Annex ()
|
||||||
annexWantFile state file = do error "not implemented" -- TODO
|
annexWantFile file = do error "not implemented" -- TODO
|
||||||
|
|
||||||
{- Indicates a file is not wanted. -}
|
{- Indicates a file is not wanted. -}
|
||||||
annexDropFile :: State -> FilePath -> IO ()
|
annexDropFile :: FilePath -> Annex ()
|
||||||
annexDropFile state file = do error "not implemented" -- TODO
|
annexDropFile file = do error "not implemented" -- TODO
|
||||||
|
|
||||||
{- Pushes all files to a remote repository. -}
|
{- Pushes all files to a remote repository. -}
|
||||||
annexPushRepo :: State -> String -> IO ()
|
annexPushRepo :: String -> Annex ()
|
||||||
annexPushRepo state reponame = do error "not implemented" -- TODO
|
annexPushRepo reponame = do error "not implemented" -- TODO
|
||||||
|
|
||||||
{- Pulls all files from a remote repository. -}
|
{- Pulls all files from a remote repository. -}
|
||||||
annexPullRepo :: State -> String -> IO ()
|
annexPullRepo :: String -> Annex ()
|
||||||
annexPullRepo state reponame = do error "not implemented" -- TODO
|
annexPullRepo reponame = do error "not implemented" -- TODO
|
||||||
|
|
||||||
{- Sets up a git repo for git-annex. May be called repeatedly. -}
|
{- Sets up a git repo for git-annex. May be called repeatedly. -}
|
||||||
gitSetup :: GitRepo -> IO ()
|
gitSetup :: GitRepo -> IO ()
|
||||||
|
@ -159,11 +171,19 @@ gitSetup repo = do
|
||||||
attributes]
|
attributes]
|
||||||
|
|
||||||
{- Updates the LocationLog when a key's presence changes. -}
|
{- Updates the LocationLog when a key's presence changes. -}
|
||||||
logStatus state key status = do
|
logStatus :: Key -> LogStatus -> Annex ()
|
||||||
f <- logChange (repo state) key (getUUID state (repo state)) status
|
logStatus key status = do
|
||||||
gitRun (repo state) ["add", f]
|
g <- gitAnnex
|
||||||
gitRun (repo state) ["commit", "-m", "git-annex log update", f]
|
u <- getUUID g
|
||||||
|
f <- liftIO $ logChange g key u status
|
||||||
|
liftIO $ commit g f
|
||||||
|
where
|
||||||
|
commit g f = do
|
||||||
|
gitRun g ["add", f]
|
||||||
|
gitRun g ["commit", "-m", "git-annex log update", f]
|
||||||
|
|
||||||
{- Checks if a given key is currently present in the annexLocation -}
|
{- Checks if a given key is currently present in the annexLocation -}
|
||||||
inAnnex :: State -> Backend -> Key -> IO Bool
|
inAnnex :: Backend -> Key -> Annex Bool
|
||||||
inAnnex state backend key = doesFileExist $ annexLocation (repo state) backend key
|
inAnnex backend key = do
|
||||||
|
g <- gitAnnex
|
||||||
|
liftIO $ doesFileExist $ annexLocation g backend key
|
||||||
|
|
25
Backend.hs
25
Backend.hs
|
@ -20,6 +20,7 @@ module Backend (
|
||||||
lookupFile
|
lookupFile
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
|
@ -32,30 +33,34 @@ import Utility
|
||||||
import Types
|
import Types
|
||||||
|
|
||||||
{- Attempts to store a file in one of the backends. -}
|
{- Attempts to store a file in one of the backends. -}
|
||||||
storeFile :: State -> FilePath -> IO (Maybe (Key, Backend))
|
storeFile :: FilePath -> Annex (Maybe (Key, Backend))
|
||||||
storeFile state file = storeFile' (backends state) state file
|
storeFile file = do
|
||||||
|
g <- gitAnnex
|
||||||
|
let relfile = gitRelative g file
|
||||||
|
b <- backendsAnnex
|
||||||
|
storeFile' b file relfile
|
||||||
storeFile' [] _ _ = return Nothing
|
storeFile' [] _ _ = return Nothing
|
||||||
storeFile' (b:bs) state file = do
|
storeFile' (b:bs) file relfile = do
|
||||||
try <- (getKey b) state (gitRelative (repo state) file)
|
try <- (getKey b) relfile
|
||||||
case (try) of
|
case (try) of
|
||||||
Nothing -> nextbackend
|
Nothing -> nextbackend
|
||||||
Just key -> do
|
Just key -> do
|
||||||
stored <- (storeFileKey b) state file key
|
stored <- (storeFileKey b) file key
|
||||||
if (not stored)
|
if (not stored)
|
||||||
then nextbackend
|
then nextbackend
|
||||||
else do
|
else do
|
||||||
return $ Just (key, b)
|
return $ Just (key, b)
|
||||||
where
|
where
|
||||||
nextbackend = storeFile' bs state file
|
nextbackend = storeFile' bs file relfile
|
||||||
|
|
||||||
{- Attempts to retrieve an key from one of the backends, saving it to
|
{- Attempts to retrieve an key from one of the backends, saving it to
|
||||||
- a specified location. -}
|
- a specified location. -}
|
||||||
retrieveFile :: State -> Backend -> Key -> FilePath -> IO Bool
|
retrieveFile :: Backend -> Key -> FilePath -> Annex Bool
|
||||||
retrieveFile state backend key dest = (retrieveKeyFile backend) state key dest
|
retrieveFile backend key dest = (retrieveKeyFile backend) key dest
|
||||||
|
|
||||||
{- Drops a key from a backend. -}
|
{- Drops a key from a backend. -}
|
||||||
dropFile :: State -> Backend -> Key -> IO Bool
|
dropFile :: Backend -> Key -> Annex Bool
|
||||||
dropFile state backend key = (removeKey backend) state key
|
dropFile backend key = (removeKey backend) key
|
||||||
|
|
||||||
{- Looks up the key and backend corresponding to an annexed file,
|
{- Looks up the key and backend corresponding to an annexed file,
|
||||||
- by examining what the file symlinks to. -}
|
- by examining what the file symlinks to. -}
|
||||||
|
|
|
@ -14,5 +14,5 @@ backend = BackendFile.backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
-- checksum the file to get its key
|
-- checksum the file to get its key
|
||||||
keyValue :: State -> FilePath -> IO (Maybe Key)
|
keyValue :: FilePath -> Annex (Maybe Key)
|
||||||
keyValue k = error "checksum keyValue unimplemented" -- TODO
|
keyValue k = error "checksum keyValue unimplemented" -- TODO
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
module BackendFile (backend) where
|
module BackendFile (backend) where
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Cmd
|
import System.Cmd
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
|
@ -21,28 +22,28 @@ backend = Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
-- direct mapping from filename to key
|
-- direct mapping from filename to key
|
||||||
keyValue :: State -> FilePath -> IO (Maybe Key)
|
keyValue :: FilePath -> Annex (Maybe Key)
|
||||||
keyValue state file = return $ Just $ Key file
|
keyValue file = return $ Just $ Key file
|
||||||
|
|
||||||
{- This backend does not really do any independant data storage,
|
{- This backend does not really do any independant data storage,
|
||||||
- it relies on the file contents in .git/annex/ in this repo,
|
- it relies on the file contents in .git/annex/ in this repo,
|
||||||
- and other accessible repos. So storing or removing a key is
|
- and other accessible repos. So storing or removing a key is
|
||||||
- a no-op. TODO until support is added for git annex --push otherrepo,
|
- a no-op. TODO until support is added for git annex --push otherrepo,
|
||||||
- then these could implement that.. -}
|
- then these could implement that.. -}
|
||||||
dummyStore :: State -> FilePath -> Key -> IO (Bool)
|
dummyStore :: FilePath -> Key -> Annex (Bool)
|
||||||
dummyStore state file key = return True
|
dummyStore file key = return True
|
||||||
dummyRemove :: State -> Key -> IO Bool
|
dummyRemove :: Key -> Annex Bool
|
||||||
dummyRemove state url = return False
|
dummyRemove url = return False
|
||||||
|
|
||||||
{- Try to find a copy of the file in one of the remotes,
|
{- Try to find a copy of the file in one of the remotes,
|
||||||
- and copy it over to this one. -}
|
- and copy it over to this one. -}
|
||||||
copyKeyFile :: State -> Key -> FilePath -> IO (Bool)
|
copyKeyFile :: Key -> FilePath -> Annex (Bool)
|
||||||
copyKeyFile state key file = do
|
copyKeyFile key file = do
|
||||||
remotes <- remotesWithKey state key
|
remotes <- remotesWithKey key
|
||||||
if (0 == length remotes)
|
if (0 == length remotes)
|
||||||
then error $ "no known remotes have: " ++ (keyFile key) ++ "\n" ++
|
then error $ "no known remotes have: " ++ (keyFile key) ++ "\n" ++
|
||||||
"(Perhaps you need to git remote add a repository?)"
|
"(Perhaps you need to git remote add a repository?)"
|
||||||
else trycopy remotes remotes
|
else liftIO $ trycopy remotes remotes
|
||||||
where
|
where
|
||||||
trycopy full [] = error $ "unable to get: " ++ (keyFile key) ++ "\n" ++
|
trycopy full [] = error $ "unable to get: " ++ (keyFile key) ++ "\n" ++
|
||||||
"To get that file, need access to one of these remotes: " ++
|
"To get that file, need access to one of these remotes: " ++
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
module BackendUrl (backend) where
|
module BackendUrl (backend) where
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
import System.Cmd
|
import System.Cmd
|
||||||
import IO
|
import IO
|
||||||
import Types
|
import Types
|
||||||
|
@ -16,19 +17,19 @@ backend = Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
-- cannot generate url from filename
|
-- cannot generate url from filename
|
||||||
keyValue :: State -> FilePath -> IO (Maybe Key)
|
keyValue :: FilePath -> Annex (Maybe Key)
|
||||||
keyValue repo file = return Nothing
|
keyValue file = return Nothing
|
||||||
|
|
||||||
-- cannot change url contents
|
-- cannot change url contents
|
||||||
dummyStore :: State -> FilePath -> Key -> IO Bool
|
dummyStore :: FilePath -> Key -> Annex Bool
|
||||||
dummyStore repo file url = return False
|
dummyStore file url = return False
|
||||||
dummyRemove :: State -> Key -> IO Bool
|
dummyRemove :: Key -> Annex Bool
|
||||||
dummyRemove state url = return False
|
dummyRemove url = return False
|
||||||
|
|
||||||
downloadUrl :: State -> Key -> FilePath -> IO Bool
|
downloadUrl :: Key -> FilePath -> Annex Bool
|
||||||
downloadUrl state url file = do
|
downloadUrl url file = do
|
||||||
putStrLn $ "download: " ++ (show url)
|
liftIO $ putStrLn $ "download: " ++ (show url)
|
||||||
result <- try $ rawSystem "curl" ["-#", "-o", file, (show url)]
|
result <- liftIO $ try $ rawSystem "curl" ["-#", "-o", file, (show url)]
|
||||||
case (result) of
|
case (result) of
|
||||||
Left _ -> return False
|
Left _ -> return False
|
||||||
Right _ -> return True
|
Right _ -> return True
|
||||||
|
|
21
CmdLine.hs
21
CmdLine.hs
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
module CmdLine (
|
module CmdLine (
|
||||||
argvToMode,
|
argvToMode,
|
||||||
dispatch
|
dispatch,
|
||||||
|
Mode
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import System.Console.GetOpt
|
import System.Console.GetOpt
|
||||||
|
@ -39,13 +40,13 @@ argvToMode argv = do
|
||||||
(_,files,errs) -> ioError (userError (concat errs ++ usageInfo header options))
|
(_,files,errs) -> ioError (userError (concat errs ++ usageInfo header options))
|
||||||
where header = "Usage: git-annex [mode] file"
|
where header = "Usage: git-annex [mode] file"
|
||||||
|
|
||||||
dispatch :: State -> Mode -> FilePath -> IO ()
|
dispatch :: Mode -> FilePath -> Annex ()
|
||||||
dispatch state mode item = do
|
dispatch mode item = do
|
||||||
case (mode) of
|
case (mode) of
|
||||||
Add -> annexFile state item
|
Add -> annexFile item
|
||||||
Push -> annexPushRepo state item
|
Push -> annexPushRepo item
|
||||||
Pull -> annexPullRepo state item
|
Pull -> annexPullRepo item
|
||||||
Want -> annexWantFile state item
|
Want -> annexWantFile item
|
||||||
Get -> annexGetFile state item
|
Get -> annexGetFile item
|
||||||
Drop -> annexDropFile state item
|
Drop -> annexDropFile item
|
||||||
Unannex -> unannexFile state item
|
Unannex -> unannexFile item
|
||||||
|
|
44
Remotes.hs
44
Remotes.hs
|
@ -5,6 +5,7 @@ module Remotes (
|
||||||
remotesWithKey
|
remotesWithKey
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.State (liftIO)
|
||||||
import Types
|
import Types
|
||||||
import GitRepo
|
import GitRepo
|
||||||
import LocationLog
|
import LocationLog
|
||||||
|
@ -17,34 +18,43 @@ remotesList :: [GitRepo] -> String
|
||||||
remotesList remotes = join " " $ map gitRepoDescribe remotes
|
remotesList remotes = join " " $ map gitRepoDescribe remotes
|
||||||
|
|
||||||
{- Cost ordered list of remotes that the LocationLog indicate may have a key. -}
|
{- Cost ordered list of remotes that the LocationLog indicate may have a key. -}
|
||||||
remotesWithKey :: State -> Key -> IO [GitRepo]
|
remotesWithKey :: Key -> Annex [GitRepo]
|
||||||
remotesWithKey state key = do
|
remotesWithKey key = do
|
||||||
uuids <- keyLocations (repo state) key
|
g <- gitAnnex
|
||||||
return $ reposByUUID state (remotesByCost state) uuids
|
uuids <- liftIO $ keyLocations g key
|
||||||
|
remotes <- remotesByCost
|
||||||
|
reposByUUID remotes uuids
|
||||||
|
|
||||||
{- Cost Ordered list of remotes. -}
|
{- Cost Ordered list of remotes. -}
|
||||||
remotesByCost :: State -> [GitRepo]
|
remotesByCost :: Annex [GitRepo]
|
||||||
remotesByCost state = reposByCost state $ gitConfigRemotes (repo state)
|
remotesByCost = do
|
||||||
|
g <- gitAnnex
|
||||||
|
reposByCost $ gitConfigRemotes g
|
||||||
|
|
||||||
{- Orders a list of git repos by cost. -}
|
{- Orders a list of git repos by cost. -}
|
||||||
reposByCost :: State -> [GitRepo] -> [GitRepo]
|
reposByCost :: [GitRepo] -> Annex [GitRepo]
|
||||||
reposByCost state l =
|
reposByCost l = do
|
||||||
fst $ unzip $ sortBy (\(r1, c1) (r2, c2) -> compare c1 c2) $ costpairs l
|
costpairs <- mapM costpair l
|
||||||
|
return $ fst $ unzip $ sortBy bycost $ costpairs
|
||||||
where
|
where
|
||||||
costpairs l = map (\r -> (r, repoCost state r)) l
|
costpair r = do
|
||||||
|
cost <- repoCost r
|
||||||
|
return (r, cost)
|
||||||
|
bycost (_, c1) (_, c2) = compare c1 c2
|
||||||
|
|
||||||
{- Calculates cost for a repo.
|
{- Calculates cost for a repo.
|
||||||
-
|
-
|
||||||
- The default cost is 100 for local repositories, and 200 for remote
|
- The default cost is 100 for local repositories, and 200 for remote
|
||||||
- repositories; it can also be configured by remote.<name>.annex-cost
|
- repositories; it can also be configured by remote.<name>.annex-cost
|
||||||
-}
|
-}
|
||||||
repoCost :: State -> GitRepo -> Int
|
repoCost :: GitRepo -> Annex Int
|
||||||
repoCost state r =
|
repoCost r = do
|
||||||
if ((length $ config state r) > 0)
|
g <- gitAnnex
|
||||||
then read $ config state r
|
if ((length $ config g r) > 0)
|
||||||
|
then return $ read $ config g r
|
||||||
else if (gitRepoIsLocal r)
|
else if (gitRepoIsLocal r)
|
||||||
then 100
|
then return 100
|
||||||
else 200
|
else return 200
|
||||||
where
|
where
|
||||||
config state r = gitConfig (repo state) (configkey r) ""
|
config g r = gitConfig g (configkey r) ""
|
||||||
configkey r = "remote." ++ (gitRepoRemoteName r) ++ ".annex-cost"
|
configkey r = "remote." ++ (gitRepoRemoteName r) ++ ".annex-cost"
|
||||||
|
|
4
TODO
4
TODO
|
@ -1,9 +1,9 @@
|
||||||
* bug when annexing files while in a subdir of a git repo
|
* bug when annexing files while in a subdir of a git repo
|
||||||
* bug when specifying absolute path to files when annexing
|
* bug when specifying absolute path to files when annexing
|
||||||
|
|
||||||
* implement retrieval for backendfile
|
* state monad
|
||||||
|
|
||||||
* query remotes for their annex.uuid settings
|
* query remotes for their annex.uuid settings and cache
|
||||||
|
|
||||||
* --push/--pull/--want/--drop
|
* --push/--pull/--want/--drop
|
||||||
|
|
||||||
|
|
51
Types.hs
51
Types.hs
|
@ -1,20 +1,59 @@
|
||||||
{- git-annex core data types -}
|
{- git-annex core data types -}
|
||||||
|
|
||||||
module Types (
|
module Types (
|
||||||
State(..),
|
Annex(..),
|
||||||
|
makeAnnexState,
|
||||||
|
runAnnexState,
|
||||||
|
gitAnnex,
|
||||||
|
gitAnnexChange,
|
||||||
|
backendsAnnex,
|
||||||
|
backendsAnnexChange,
|
||||||
|
|
||||||
|
AnnexState(..),
|
||||||
Key(..),
|
Key(..),
|
||||||
Backend(..)
|
Backend(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
import Data.String.Utils
|
import Data.String.Utils
|
||||||
import GitRepo
|
import GitRepo
|
||||||
|
|
||||||
-- git-annex's runtime state
|
-- git-annex's runtime state
|
||||||
data State = State {
|
data AnnexState = AnnexState {
|
||||||
repo :: GitRepo,
|
repo :: GitRepo,
|
||||||
backends :: [Backend]
|
backends :: [Backend]
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
|
-- git-annex's monad
|
||||||
|
type Annex = StateT AnnexState IO
|
||||||
|
|
||||||
|
-- constructor
|
||||||
|
makeAnnexState :: GitRepo -> AnnexState
|
||||||
|
makeAnnexState g = AnnexState { repo = g, backends = [] }
|
||||||
|
|
||||||
|
-- performs an action in the Annex monad
|
||||||
|
runAnnexState state action = runStateT (action) state
|
||||||
|
|
||||||
|
-- state accessors
|
||||||
|
gitAnnex :: Annex GitRepo
|
||||||
|
gitAnnex = do
|
||||||
|
state <- get
|
||||||
|
return (repo state)
|
||||||
|
gitAnnexChange :: GitRepo -> Annex ()
|
||||||
|
gitAnnexChange r = do
|
||||||
|
state <- get
|
||||||
|
put state { repo = r }
|
||||||
|
return ()
|
||||||
|
backendsAnnex :: Annex [Backend]
|
||||||
|
backendsAnnex = do
|
||||||
|
state <- get
|
||||||
|
return (backends state)
|
||||||
|
backendsAnnexChange :: [Backend] -> Annex ()
|
||||||
|
backendsAnnexChange b = do
|
||||||
|
state <- get
|
||||||
|
put state { backends = b }
|
||||||
|
return ()
|
||||||
|
|
||||||
-- annexed filenames are mapped into keys
|
-- annexed filenames are mapped into keys
|
||||||
data Key = Key String deriving (Eq)
|
data Key = Key String deriving (Eq)
|
||||||
|
|
||||||
|
@ -27,13 +66,13 @@ data Backend = Backend {
|
||||||
-- name of this backend
|
-- name of this backend
|
||||||
name :: String,
|
name :: String,
|
||||||
-- converts a filename to a key
|
-- converts a filename to a key
|
||||||
getKey :: State -> FilePath -> IO (Maybe Key),
|
getKey :: FilePath -> Annex (Maybe Key),
|
||||||
-- stores a file's contents to a key
|
-- stores a file's contents to a key
|
||||||
storeFileKey :: State -> FilePath -> Key -> IO Bool,
|
storeFileKey :: FilePath -> Key -> Annex Bool,
|
||||||
-- retrieves a key's contents to a file
|
-- retrieves a key's contents to a file
|
||||||
retrieveKeyFile :: State -> Key -> FilePath -> IO Bool,
|
retrieveKeyFile :: Key -> FilePath -> Annex Bool,
|
||||||
-- removes a key
|
-- removes a key
|
||||||
removeKey :: State -> Key -> IO Bool
|
removeKey :: Key -> Annex Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Show Backend where
|
instance Show Backend where
|
||||||
|
|
50
UUID.hs
50
UUID.hs
|
@ -13,6 +13,7 @@ module UUID (
|
||||||
reposByUUID
|
reposByUUID
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
import Maybe
|
import Maybe
|
||||||
import List
|
import List
|
||||||
import System.Cmd.Utils
|
import System.Cmd.Utils
|
||||||
|
@ -26,9 +27,8 @@ configkey="annex.uuid"
|
||||||
|
|
||||||
{- Generates a UUID. There is a library for this, but it's not packaged,
|
{- Generates a UUID. There is a library for this, but it's not packaged,
|
||||||
- so use the command line tool. -}
|
- so use the command line tool. -}
|
||||||
genUUID :: IO UUID
|
genUUID :: Annex UUID
|
||||||
genUUID = do
|
genUUID = liftIO $ pOpen ReadFromPipe "uuid" ["-m"] $ \h -> hGetLine h
|
||||||
pOpen ReadFromPipe "uuid" ["-m"] $ \h -> hGetLine h
|
|
||||||
|
|
||||||
{- Looks up a repo's UUID. May return "" if none is known.
|
{- Looks up a repo's UUID. May return "" if none is known.
|
||||||
-
|
-
|
||||||
|
@ -36,28 +36,38 @@ genUUID = do
|
||||||
- remote.<name>.annex-uuid
|
- remote.<name>.annex-uuid
|
||||||
-
|
-
|
||||||
- -}
|
- -}
|
||||||
getUUID :: State -> GitRepo -> UUID
|
getUUID :: GitRepo -> Annex UUID
|
||||||
getUUID s r =
|
getUUID r = do
|
||||||
if ("" /= getUUID' r)
|
if ("" /= configured r)
|
||||||
then getUUID' r
|
then return $ configured r
|
||||||
else cached s r
|
else cached r
|
||||||
where
|
where
|
||||||
cached s r = gitConfig (repo s) (configkey r) ""
|
configured r = gitConfig r "annex.uuid" ""
|
||||||
|
cached r = do
|
||||||
|
g <- gitAnnex
|
||||||
|
return $ gitConfig g (configkey r) ""
|
||||||
configkey r = "remote." ++ (gitRepoRemoteName r) ++ ".annex-uuid"
|
configkey r = "remote." ++ (gitRepoRemoteName r) ++ ".annex-uuid"
|
||||||
getUUID' r = gitConfig r "annex.uuid" ""
|
|
||||||
|
|
||||||
{- Make sure that the repo has an annex.uuid setting. -}
|
{- Make sure that the repo has an annex.uuid setting. -}
|
||||||
prepUUID :: GitRepo -> IO GitRepo
|
prepUUID :: Annex ()
|
||||||
prepUUID repo =
|
prepUUID = do
|
||||||
if ("" == getUUID' repo)
|
g <- gitAnnex
|
||||||
|
u <- getUUID g
|
||||||
|
if ("" == u)
|
||||||
then do
|
then do
|
||||||
uuid <- genUUID
|
uuid <- genUUID
|
||||||
gitRun repo ["config", configkey, uuid]
|
liftIO $ gitRun g ["config", configkey, uuid]
|
||||||
-- return new repo with updated config
|
-- re-read git config and update the repo's state
|
||||||
gitConfigRead repo
|
u' <- liftIO $ gitConfigRead g
|
||||||
else return repo
|
gitAnnexChange u'
|
||||||
|
return ()
|
||||||
|
else return ()
|
||||||
|
|
||||||
{- Filters a list of repos to ones that have listed UUIDs. -}
|
{- Filters a list of repos to ones that have listed UUIDs. -}
|
||||||
reposByUUID :: State -> [GitRepo] -> [UUID] -> [GitRepo]
|
reposByUUID :: [GitRepo] -> [UUID] -> Annex [GitRepo]
|
||||||
reposByUUID state repos uuids =
|
reposByUUID repos uuids = do
|
||||||
filter (\r -> isJust $ elemIndex (getUUID state r) uuids) repos
|
filterM match repos
|
||||||
|
where
|
||||||
|
match r = do
|
||||||
|
u <- getUUID r
|
||||||
|
return $ isJust $ elemIndex u uuids
|
||||||
|
|
31
git-annex.hs
31
git-annex.hs
|
@ -1,36 +1,43 @@
|
||||||
{- git-annex main program
|
{- git-annex main program
|
||||||
- -}
|
- -}
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import CmdLine
|
import CmdLine
|
||||||
|
import Types
|
||||||
import Annex
|
import Annex
|
||||||
|
|
||||||
main = do
|
main = do
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
(mode, files) <- argvToMode args
|
(mode, params) <- argvToMode args
|
||||||
|
|
||||||
state <- startAnnex
|
state <- startAnnex
|
||||||
|
tryRun state mode 0 0 params
|
||||||
|
|
||||||
tryRun 0 0 $ map (\f -> dispatch state mode f) files
|
{- Processes each param in the list by dispatching the handler function
|
||||||
|
- for the user-selection operation mode. Catches exceptions, not stopping
|
||||||
{- Tries to run a series of actions, not stopping if some error out,
|
- if some error out, and propigates an overall error status at the end.
|
||||||
- and propigating an overall error status at the end. -}
|
-
|
||||||
tryRun errnum oknum [] = do
|
- This runs in the IO monad, not in the Annex monad. It seems that
|
||||||
|
- exceptions can only be caught in the IO monad, not in a stacked monad;
|
||||||
|
- or more likely I missed an easy way to do it. So, I have to laboriously
|
||||||
|
- thread AnnexState through this function.
|
||||||
|
-}
|
||||||
|
tryRun :: AnnexState -> Mode -> Int -> Int -> [String] -> IO ()
|
||||||
|
tryRun state mode errnum oknum [] = do
|
||||||
if (errnum > 0)
|
if (errnum > 0)
|
||||||
then error $ (show errnum) ++ " failed ; " ++ show (oknum) ++ " ok"
|
then error $ (show errnum) ++ " failed ; " ++ show (oknum) ++ " ok"
|
||||||
else return ()
|
else return ()
|
||||||
tryRun errnum oknum (a:as) = do
|
tryRun state mode errnum oknum (f:fs) = do
|
||||||
result <- try (a)::IO (Either SomeException ())
|
result <- try (runAnnexState state (dispatch mode f))::IO (Either SomeException ((), AnnexState))
|
||||||
case (result) of
|
case (result) of
|
||||||
Left err -> do
|
Left err -> do
|
||||||
showErr err
|
showErr err
|
||||||
tryRun (errnum + 1) oknum as
|
tryRun state mode (errnum + 1) oknum fs
|
||||||
Right _ -> tryRun errnum (oknum + 1) as
|
Right (_,state') -> tryRun state' mode errnum (oknum + 1) fs
|
||||||
|
|
||||||
{- Exception pretty-printing. -}
|
{- Exception pretty-printing. -}
|
||||||
showErr :: SomeException -> IO ()
|
|
||||||
showErr e = do
|
showErr e = do
|
||||||
hPutStrLn stderr $ "git-annex: " ++ (show e)
|
hPutStrLn stderr $ "git-annex: " ++ (show e)
|
||||||
return ()
|
return ()
|
||||||
|
|
Loading…
Reference in a new issue