summaryrefslogtreecommitdiff
path: root/Commands.hs
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2010-10-14 03:18:11 -0400
committerGravatar Joey Hess <joey@kitenet.net>2010-10-14 03:18:11 -0400
commit6f3572e47f57bbe5cc76b58c8bcdc9c6c455dce0 (patch)
tree4f7f31a703051b9df3986e2a3e7dbfb146e2e032 /Commands.hs
parent0b55bd05de7b83a474ea58e9d45676934667f4bd (diff)
more reorg, spiffed up state monad
Diffstat (limited to 'Commands.hs')
-rw-r--r--Commands.hs189
1 files changed, 189 insertions, 0 deletions
diff --git a/Commands.hs b/Commands.hs
new file mode 100644
index 000000000..98e65b126
--- /dev/null
+++ b/Commands.hs
@@ -0,0 +1,189 @@
+{- git-annex subcommands -}
+
+module Commands (
+ start,
+ annexCmd,
+ unannexCmd,
+ getCmd,
+ wantCmd,
+ dropCmd,
+ pushCmd,
+ pullCmd
+) where
+
+import Control.Monad.State (liftIO)
+import System.Posix.Files
+import System.Directory
+import Data.String.Utils
+import List
+import qualified GitRepo as Git
+import qualified Annex
+import Utility
+import Locations
+import qualified Backend
+import BackendList
+import UUID
+import LocationLog
+import Types
+
+{- Create and returns an Annex state object.
+ - Examines and prepares the git repo.
+ -}
+start :: IO AnnexState
+start = do
+ g <- Git.repoFromCwd
+ let s = Annex.new g
+ (_,s') <- Annex.run s (prep g)
+ return s'
+ where
+ prep g = do
+ -- setup git and read its config; update state
+ g' <- liftIO $ Git.configRead g
+ Annex.gitRepoChange g'
+ liftIO $ gitSetup g'
+ Annex.backendsChange $ parseBackendList $
+ Git.configGet g' "annex.backends" ""
+ prepUUID
+
+inBackend file yes no = do
+ r <- liftIO $ Backend.lookupFile file
+ case (r) of
+ Just v -> yes v
+ Nothing -> no
+notinBackend file yes no = inBackend file no yes
+
+{- 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. -}
+annexCmd :: FilePath -> Annex ()
+annexCmd file = inBackend file err $ do
+ liftIO $ checkLegal file
+ stored <- Backend.storeFile file
+ g <- Annex.gitRepo
+ case (stored) of
+ Nothing -> error $ "no backend could store: " ++ file
+ Just (key, backend) -> do
+ logStatus key ValuePresent
+ liftIO $ setup g key backend
+ where
+ err = error $ "already annexed " ++ file
+ checkLegal file = do
+ s <- getSymbolicLinkStatus file
+ if ((isSymbolicLink s) || (not $ isRegularFile s))
+ then error $ "not a regular file: " ++ file
+ else return ()
+ setup g key backend = do
+ let dest = annexLocation g backend key
+ let reldest = annexLocationRelative g backend key
+ createDirectoryIfMissing True (parentDir dest)
+ renameFile file dest
+ createSymbolicLink ((linkTarget file) ++ reldest) file
+ Git.run g ["add", file]
+ Git.run g ["commit", "-m",
+ ("git-annex annexed " ++ file), file]
+ linkTarget file =
+ -- relies on file being relative to the top of the
+ -- git repo; just replace each subdirectory with ".."
+ if (subdirs > 0)
+ then (join "/" $ take subdirs $ repeat "..") ++ "/"
+ else ""
+ where
+ subdirs = (length $ split "/" file) - 1
+
+
+{- Inverse of annexCmd. -}
+unannexCmd :: FilePath -> Annex ()
+unannexCmd file = notinBackend file err $ \(key, backend) -> do
+ Backend.dropFile backend key
+ logStatus key ValueMissing
+ g <- Annex.gitRepo
+ let src = annexLocation g backend key
+ liftIO $ moveout g src
+ where
+ err = error $ "not annexed " ++ file
+ moveout g src = do
+ removeFile file
+ Git.run g ["rm", file]
+ Git.run g ["commit", "-m",
+ ("git-annex unannexed " ++ file), file]
+ -- git rm deletes empty directories;
+ -- put them back
+ createDirectoryIfMissing True (parentDir file)
+ renameFile src file
+ return ()
+
+{- Gets an annexed file from one of the backends. -}
+getCmd :: FilePath -> Annex ()
+getCmd file = notinBackend file err $ \(key, backend) -> do
+ inannex <- inAnnex backend key
+ if (inannex)
+ then return ()
+ else do
+ g <- Annex.gitRepo
+ let dest = annexLocation g backend key
+ liftIO $ createDirectoryIfMissing True (parentDir dest)
+ success <- Backend.retrieveFile backend key dest
+ if (success)
+ then do
+ logStatus key ValuePresent
+ return ()
+ else error $ "failed to get " ++ file
+ where
+ err = error $ "not annexed " ++ file
+
+{- Indicates a file is wanted. -}
+wantCmd :: FilePath -> Annex ()
+wantCmd file = do error "not implemented" -- TODO
+
+{- Indicates a file is not wanted. -}
+dropCmd :: FilePath -> Annex ()
+dropCmd file = do error "not implemented" -- TODO
+
+{- Pushes all files to a remote repository. -}
+pushCmd :: String -> Annex ()
+pushCmd reponame = do error "not implemented" -- TODO
+
+{- Pulls all files from a remote repository. -}
+pullCmd :: String -> Annex ()
+pullCmd reponame = do error "not implemented" -- TODO
+
+{- Sets up a git repo for git-annex. May be called repeatedly. -}
+gitSetup :: Git.Repo -> IO ()
+gitSetup repo = do
+ -- configure git to use union merge driver on state files
+ exists <- doesFileExist attributes
+ if (not exists)
+ then do
+ writeFile attributes $ attrLine ++ "\n"
+ commit
+ else do
+ content <- readFile attributes
+ if (all (/= attrLine) (lines content))
+ then do
+ appendFile attributes $ attrLine ++ "\n"
+ commit
+ else return ()
+ where
+ attrLine = stateLoc ++ "/*.log merge=union"
+ attributes = Git.attributes repo
+ commit = do
+ Git.run repo ["add", attributes]
+ Git.run repo ["commit", "-m", "git-annex setup",
+ attributes]
+
+{- Updates the LocationLog when a key's presence changes. -}
+logStatus :: Key -> LogStatus -> Annex ()
+logStatus key status = do
+ g <- Annex.gitRepo
+ u <- getUUID g
+ f <- liftIO $ logChange g key u status
+ liftIO $ commit g f
+ where
+ commit g f = do
+ Git.run g ["add", f]
+ Git.run g ["commit", "-m", "git-annex log update", f]
+
+{- Checks if a given key is currently present in the annexLocation -}
+inAnnex :: Backend -> Key -> Annex Bool
+inAnnex backend key = do
+ g <- Annex.gitRepo
+ liftIO $ doesFileExist $ annexLocation g backend key