summaryrefslogtreecommitdiff
path: root/Commands.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Commands.hs')
-rw-r--r--Commands.hs235
1 files changed, 235 insertions, 0 deletions
diff --git a/Commands.hs b/Commands.hs
new file mode 100644
index 000000000..2addf714e
--- /dev/null
+++ b/Commands.hs
@@ -0,0 +1,235 @@
+{- git-annex command line -}
+
+module Commands (parseCmd) where
+
+import System.Console.GetOpt
+import Control.Monad.State (liftIO)
+import System.Posix.Files
+import System.Directory
+import System.Path
+import Data.String.Utils
+import List
+import IO
+
+import qualified GitRepo as Git
+import qualified Annex
+import Utility
+import Locations
+import qualified Backend
+import UUID
+import LocationLog
+import Types
+import Core
+import qualified Remotes
+import qualified TypeInternals
+
+data CmdWants = FilesInGit | FilesNotInGit | RepoName | SingleString
+data Command = Command {
+ cmdname :: String,
+ cmdaction :: (String -> Annex ()),
+ cmdwants :: CmdWants,
+ cmddesc :: String
+}
+
+cmds :: [Command]
+cmds = [
+ (Command "add" addCmd FilesNotInGit
+ "add files to annex")
+ , (Command "get" getCmd FilesInGit
+ "make content of annexed files available")
+ , (Command "drop" dropCmd FilesInGit
+ "indicate content of files not currently wanted")
+ , (Command "unannex" unannexCmd FilesInGit
+ "undo accidential add command")
+ , (Command "init" initCmd SingleString
+ "initialize git-annex with repository description")
+ , (Command "fix" fixCmd FilesInGit
+ "fix up files' symlinks to point to annexed content")
+ ]
+
+options = [
+ Option ['f'] ["force"] (NoArg Force) "allow actions that may lose annexed data"
+ ]
+
+header = "Usage: git-annex " ++ (join "|" $ map cmdname cmds) ++ " [path ...]"
+
+usage = usageInfo header options ++ "\nSubcommands:\n" ++ cmddescs
+ where
+ cmddescs = unlines $ map (\c -> indent $ showcmd c) cmds
+ showcmd c =
+ (cmdname c) ++
+ (take (10 - (length (cmdname c))) $ repeat ' ') ++
+ (cmddesc c)
+ indent l = " " ++ l
+
+{- Finds the type of parameters a command wants, from among the passed
+ - parameter list. -}
+findWanted :: CmdWants -> [String] -> Git.Repo -> IO [String]
+findWanted FilesNotInGit params repo = do
+ files <- mapM (Git.notInRepo repo) params
+ return $ foldl (++) [] files
+findWanted FilesInGit params repo = do
+ files <- mapM (Git.inRepo repo) params
+ return $ foldl (++) [] files
+findWanted SingleString params _ = do
+ return $ [unwords params]
+findWanted RepoName params _ = do
+ return $ params
+
+{- Parses command line and returns a list of flags and a list of
+ - actions to be run in the Annex monad. -}
+parseCmd :: [String] -> AnnexState -> IO ([Flag], [Annex ()])
+parseCmd argv state = do
+ (flags, params) <- getopt
+ case (length params) of
+ 0 -> error usage
+ _ -> case (lookupCmd (params !! 0)) of
+ [] -> error usage
+ [Command _ action want _] -> do
+ f <- findWanted want (drop 1 params)
+ (TypeInternals.repo state)
+ return (flags, map action $ filter notstate f)
+ where
+ -- never include files from the state directory
+ notstate f = stateLoc /= take (length stateLoc) f
+ getopt = case getOpt Permute options argv of
+ (flags, params, []) -> return (flags, params)
+ (_, _, errs) -> ioError (userError (concat errs ++ usage))
+ lookupCmd cmd = filter (\c -> cmd == cmdname c) cmds
+
+{- 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. -}
+addCmd :: FilePath -> Annex ()
+addCmd file = inBackend file $ do
+ s <- liftIO $ getSymbolicLinkStatus file
+ if ((isSymbolicLink s) || (not $ isRegularFile s))
+ then return ()
+ else do
+ showStart "add" file
+ g <- Annex.gitRepo
+ stored <- Backend.storeFileKey file
+ case (stored) of
+ Nothing -> showEndFail
+ Just (key, backend) -> do
+ logStatus key ValuePresent
+ setup g key
+ where
+ setup g key = do
+ let dest = annexLocation g key
+ liftIO $ createDirectoryIfMissing True (parentDir dest)
+ liftIO $ renameFile file dest
+ link <- calcGitLink file key
+ liftIO $ createSymbolicLink link file
+ liftIO $ Git.run g ["add", file]
+ showEndOk
+
+{- Undo addCmd. -}
+unannexCmd :: FilePath -> Annex ()
+unannexCmd file = notinBackend file $ \(key, backend) -> do
+ showStart "unannex" file
+ Annex.flagChange Force True -- force backend to always remove
+ Backend.removeKey backend key
+ logStatus key ValueMissing
+ g <- Annex.gitRepo
+ let src = annexLocation g key
+ moveout g src
+ where
+ moveout g src = do
+ liftIO $ removeFile file
+ liftIO $ Git.run g ["rm", "--quiet", file]
+ -- git rm deletes empty directories;
+ -- put them back
+ liftIO $ createDirectoryIfMissing True (parentDir file)
+ liftIO $ renameFile src file
+ showEndOk
+
+{- Gets an annexed file from one of the backends. -}
+getCmd :: FilePath -> Annex ()
+getCmd file = notinBackend file $ \(key, backend) -> do
+ inannex <- inAnnex key
+ if (inannex)
+ then return ()
+ else do
+ showStart "get" file
+ g <- Annex.gitRepo
+ let dest = annexLocation g key
+ let tmp = (annexTmpLocation g) ++ (keyFile key)
+ liftIO $ createDirectoryIfMissing True (parentDir tmp)
+ success <- Backend.retrieveKeyFile backend key tmp
+ if (success)
+ then do
+ liftIO $ renameFile tmp dest
+ logStatus key ValuePresent
+ showEndOk
+ else do
+ showEndFail
+
+{- Indicates a file's content is not wanted anymore, and should be removed
+ - if it's safe to do so. -}
+dropCmd :: FilePath -> Annex ()
+dropCmd file = notinBackend file $ \(key, backend) -> do
+ inbackend <- Backend.hasKey key
+ if (not inbackend)
+ then return () -- no-op
+ else do
+ showStart "drop" file
+ success <- Backend.removeKey backend key
+ if (success)
+ then do
+ cleanup key
+ showEndOk
+ else showEndFail
+ where
+ cleanup key = do
+ logStatus key ValueMissing
+ inannex <- inAnnex key
+ if (inannex)
+ then do
+ g <- Annex.gitRepo
+ let loc = annexLocation g key
+ liftIO $ removeFile loc
+ return ()
+ else return ()
+
+{- Fixes the symlink to an annexed file. -}
+fixCmd :: String -> Annex ()
+fixCmd file = notinBackend file $ \(key, backend) -> do
+ link <- calcGitLink file key
+ l <- liftIO $ readSymbolicLink file
+ if (link == l)
+ then return ()
+ else do
+ showStart "fix" file
+ liftIO $ createDirectoryIfMissing True (parentDir file)
+ liftIO $ removeFile file
+ liftIO $ createSymbolicLink link file
+ g <- Annex.gitRepo
+ liftIO $ Git.run g ["add", file]
+ showEndOk
+
+{- Stores description for the repository. -}
+initCmd :: String -> Annex ()
+initCmd description = do
+ if (0 == length description)
+ then error $
+ "please specify a description of this repository\n" ++
+ usage
+ else do
+ g <- Annex.gitRepo
+ u <- getUUID g
+ describeUUID u description
+ log <- uuidLog
+ liftIO $ Git.run g ["add", log]
+ liftIO $ putStrLn "description set"
+
+-- helpers
+inBackend file a = do
+ r <- Backend.lookupFile file
+ case (r) of
+ Just v -> return ()
+ Nothing -> a
+notinBackend file a = do
+ r <- Backend.lookupFile file
+ case (r) of
+ Just v -> a v
+ Nothing -> return ()