add dropkey subcommand and --quiet
Needed for better git annex move --from
This commit is contained in:
		
					parent
					
						
							
								8beed17168
							
						
					
				
			
			
				commit
				
					
						d0a9cdadaf
					
				
			
		
					 7 changed files with 87 additions and 56 deletions
				
			
		
							
								
								
									
										51
									
								
								Commands.hs
									
										
									
									
									
								
							
							
						
						
									
										51
									
								
								Commands.hs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -61,7 +61,8 @@ doSubCmd cmdname start param = do
 | 
			
		|||
 | 
			
		||||
{- A subcommand can broadly want one of several kinds of input parameters.
 | 
			
		||||
 - This allows a first stage of filtering before starting a subcommand. -}
 | 
			
		||||
data SubCmdWants = FilesInGit | FilesNotInGit | FilesMissing | Description
 | 
			
		||||
data SubCmdWants = FilesInGit | FilesNotInGit | FilesMissing
 | 
			
		||||
	| Description | Keys
 | 
			
		||||
 | 
			
		||||
data SubCommand = Command {
 | 
			
		||||
	subcmdname :: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -87,6 +88,8 @@ subCmds =  [
 | 
			
		|||
		"fix up files' symlinks to point to annexed content")
 | 
			
		||||
	, (Command "fromkey"	fromKeyStart	FilesMissing
 | 
			
		||||
		"adds a file using a specific key")
 | 
			
		||||
	, (Command "dropkey"	fromKeyStart	Keys
 | 
			
		||||
		"drops cached content for specified keys")
 | 
			
		||||
	]
 | 
			
		||||
 | 
			
		||||
-- Each dashed command-line option results in generation of an action
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +98,8 @@ options :: [OptDescr (Annex ())]
 | 
			
		|||
options = [
 | 
			
		||||
	    Option ['f'] ["force"] (NoArg (storebool "force" True))
 | 
			
		||||
		"allow actions that may lose annexed data"
 | 
			
		||||
	  , Option ['q'] ["quiet"] (NoArg (storebool "quiet" True))
 | 
			
		||||
		"avoid verbose output"
 | 
			
		||||
	  , Option ['b'] ["backend"] (ReqArg (storestring "backend") "NAME")
 | 
			
		||||
		"specify default key-value backend to use"
 | 
			
		||||
	  , Option ['k'] ["key"] (ReqArg (storestring "key") "KEY")
 | 
			
		||||
| 
						 | 
				
			
			@ -127,6 +132,7 @@ usage = usageInfo header options ++ "\nSubcommands:\n" ++ cmddescs
 | 
			
		|||
{- Generate descriptions of wanted parameters for subcommands. -}
 | 
			
		||||
descWanted :: SubCmdWants -> String
 | 
			
		||||
descWanted Description = "DESCRIPTION"
 | 
			
		||||
descWanted Keys = "KEY ..."
 | 
			
		||||
descWanted _ = "PATH ..."
 | 
			
		||||
 | 
			
		||||
{- Finds the type of parameters a subcommand wants, from among the passed
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +153,7 @@ findWanted FilesMissing params repo = do
 | 
			
		|||
			if (e) then return False else return True
 | 
			
		||||
findWanted Description params _ = do
 | 
			
		||||
	return $ [unwords params]
 | 
			
		||||
findWanted Keys params _ = return params
 | 
			
		||||
 | 
			
		||||
{- Parses command line and returns two lists of actions to be 
 | 
			
		||||
 - run in the Annex monad. The first actions configure it
 | 
			
		||||
| 
						 | 
				
			
			@ -243,9 +250,9 @@ dropStart file = isAnnexed file $ \(key, backend) -> do
 | 
			
		|||
	inbackend <- Backend.hasKey key
 | 
			
		||||
	if (not inbackend)
 | 
			
		||||
		then return Nothing
 | 
			
		||||
		else return $ Just $ dropPerform file key backend
 | 
			
		||||
dropPerform :: FilePath -> Key -> Backend -> Annex (Maybe SubCmdCleanup)
 | 
			
		||||
dropPerform file key backend = do
 | 
			
		||||
		else return $ Just $ dropPerform key backend
 | 
			
		||||
dropPerform :: Key -> Backend -> Annex (Maybe SubCmdCleanup)
 | 
			
		||||
dropPerform key backend = do
 | 
			
		||||
	success <- Backend.removeKey backend key
 | 
			
		||||
	if (success)
 | 
			
		||||
		then return $ Just $ dropCleanup key
 | 
			
		||||
| 
						 | 
				
			
			@ -262,6 +269,29 @@ dropCleanup key = do
 | 
			
		|||
			return True
 | 
			
		||||
		else return True
 | 
			
		||||
 | 
			
		||||
{- Drops cached content for a key. -}
 | 
			
		||||
dropKeyStart :: String -> Annex (Maybe SubCmdPerform)
 | 
			
		||||
dropKeyStart keyname = do
 | 
			
		||||
	backends <- Backend.list
 | 
			
		||||
	let key = genKey (backends !! 0) keyname
 | 
			
		||||
	present <- inAnnex key
 | 
			
		||||
	force <- Annex.flagIsSet "force"
 | 
			
		||||
	if (not present)
 | 
			
		||||
		then return Nothing
 | 
			
		||||
		else if (not force)
 | 
			
		||||
			then error "dropkey is can cause data loss; use --force if you're sure you want to do this"
 | 
			
		||||
			else return $ Just $ dropKeyPerform key
 | 
			
		||||
dropKeyPerform :: Key -> Annex (Maybe SubCmdCleanup)
 | 
			
		||||
dropKeyPerform key = do
 | 
			
		||||
	g <- Annex.gitRepo
 | 
			
		||||
	let loc = annexLocation g key
 | 
			
		||||
	liftIO $ removeFile loc
 | 
			
		||||
	return $ Just $ dropKeyCleanup key
 | 
			
		||||
dropKeyCleanup :: Key -> Annex Bool
 | 
			
		||||
dropKeyCleanup key = do
 | 
			
		||||
	logStatus key ValueMissing
 | 
			
		||||
	return True
 | 
			
		||||
 | 
			
		||||
{- Fixes the symlink to an annexed file. -}
 | 
			
		||||
fixStart :: FilePath -> Annex (Maybe SubCmdPerform)
 | 
			
		||||
fixStart file = isAnnexed file $ \(key, backend) -> do
 | 
			
		||||
| 
						 | 
				
			
			@ -423,11 +453,14 @@ moveFromPerform file key = do
 | 
			
		|||
			return $ Just $ moveFromCleanup remote key
 | 
			
		||||
moveFromCleanup :: Git.Repo -> Key -> Annex Bool
 | 
			
		||||
moveFromCleanup remote key = do
 | 
			
		||||
	Remotes.removeRemoteFile remote $ annexLocation remote key
 | 
			
		||||
	-- Record that the key is not on the remote.
 | 
			
		||||
	u <- getUUID remote
 | 
			
		||||
	liftIO $ logChange remote key u ValueMissing
 | 
			
		||||
	Remotes.updateRemoteLogStatus remote key
 | 
			
		||||
	-- Force drop content from the remote.
 | 
			
		||||
	Remotes.runCmd remote "git-annex" ["dropkey", "--quiet", "--force",
 | 
			
		||||
		"--backend=" ++ (backendName key),
 | 
			
		||||
		keyName key]
 | 
			
		||||
	-- Record locally that the key is not on the remote.
 | 
			
		||||
	remoteuuid <- getUUID remote
 | 
			
		||||
	g <- Annex.gitRepo
 | 
			
		||||
	liftIO $ logChange g key remoteuuid ValueMissing
 | 
			
		||||
	return True
 | 
			
		||||
 | 
			
		||||
-- helpers
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								Core.hs
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								Core.hs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -115,22 +115,26 @@ getViaTmp key action = do
 | 
			
		|||
			return False
 | 
			
		||||
 | 
			
		||||
{- Output logging -}
 | 
			
		||||
verbose :: Annex () -> Annex ()
 | 
			
		||||
verbose a = do
 | 
			
		||||
	q <- Annex.flagIsSet "quiet"
 | 
			
		||||
	if (q) then return () else a
 | 
			
		||||
showStart :: String -> String -> Annex ()
 | 
			
		||||
showStart command file = do
 | 
			
		||||
showStart command file = verbose $ do
 | 
			
		||||
	liftIO $ putStr $ command ++ " " ++ file ++ " "
 | 
			
		||||
	liftIO $ hFlush stdout
 | 
			
		||||
showNote :: String -> Annex ()
 | 
			
		||||
showNote s = do
 | 
			
		||||
showNote s = verbose $ do
 | 
			
		||||
	liftIO $ putStr $ "(" ++ s ++ ") "
 | 
			
		||||
	liftIO $ hFlush stdout
 | 
			
		||||
showLongNote :: String -> Annex ()
 | 
			
		||||
showLongNote s = do
 | 
			
		||||
showLongNote s = verbose $ do
 | 
			
		||||
	liftIO $ putStr $ "\n" ++ (indent s)
 | 
			
		||||
	where
 | 
			
		||||
		indent s = join "\n" $ map (\l -> "  " ++ l) $ lines s 
 | 
			
		||||
showEndOk :: Annex ()
 | 
			
		||||
showEndOk = do
 | 
			
		||||
showEndOk = verbose $ do
 | 
			
		||||
	liftIO $ putStrLn "ok"
 | 
			
		||||
showEndFail :: Annex ()
 | 
			
		||||
showEndFail = do
 | 
			
		||||
showEndFail = verbose $ do
 | 
			
		||||
	liftIO $ putStrLn "\nfailed"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,7 +156,7 @@ workTree repo =
 | 
			
		|||
 - name to use to refer to the file relative to a git repository's top.
 | 
			
		||||
 - This is the same form displayed and used by git. -}
 | 
			
		||||
relative :: Repo -> String -> String
 | 
			
		||||
relative repo file = drop (length absrepo) absfile
 | 
			
		||||
relative repo file = assertLocal repo $ drop (length absrepo) absfile
 | 
			
		||||
	where
 | 
			
		||||
		-- normalize both repo and file, so that repo
 | 
			
		||||
		-- will be substring of file
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										54
									
								
								Remotes.hs
									
										
									
									
									
								
							
							
						
						
									
										54
									
								
								Remotes.hs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -8,11 +8,11 @@ module Remotes (
 | 
			
		|||
	commandLineRemote,
 | 
			
		||||
	copyFromRemote,
 | 
			
		||||
	copyToRemote,
 | 
			
		||||
	removeRemoteFile,
 | 
			
		||||
	updateRemoteLogStatus
 | 
			
		||||
	runCmd
 | 
			
		||||
) where
 | 
			
		||||
 | 
			
		||||
import Control.Exception
 | 
			
		||||
import IO (bracket_)
 | 
			
		||||
import Control.Exception hiding (bracket_)
 | 
			
		||||
import Control.Monad.State (liftIO)
 | 
			
		||||
import Control.Monad (filterM)
 | 
			
		||||
import qualified Data.Map as Map
 | 
			
		||||
| 
						 | 
				
			
			@ -20,9 +20,9 @@ import Data.String.Utils
 | 
			
		|||
import Data.Either.Utils
 | 
			
		||||
import System.Cmd.Utils
 | 
			
		||||
import System.Directory
 | 
			
		||||
import System.Posix.Directory
 | 
			
		||||
import List
 | 
			
		||||
import Maybe
 | 
			
		||||
import IO (hPutStrLn)
 | 
			
		||||
 | 
			
		||||
import Types
 | 
			
		||||
import qualified GitRepo as Git
 | 
			
		||||
| 
						 | 
				
			
			@ -221,39 +221,19 @@ copyToRemote r key = do
 | 
			
		|||
		sshlocation = (Git.urlHost r) ++ ":" ++ file
 | 
			
		||||
		file = error "TODO"
 | 
			
		||||
 | 
			
		||||
{- Removes a file from a remote. -}
 | 
			
		||||
removeRemoteFile :: Git.Repo -> FilePath -> Annex ()
 | 
			
		||||
removeRemoteFile r file = do
 | 
			
		||||
{- Runs a command in a remote. -}
 | 
			
		||||
runCmd :: Git.Repo -> String -> [String] -> Annex Bool
 | 
			
		||||
runCmd r command params = do
 | 
			
		||||
	if (not $ Git.repoIsUrl r)
 | 
			
		||||
		then liftIO $ removeFile file
 | 
			
		||||
		then do
 | 
			
		||||
			cwd <- liftIO $ getCurrentDirectory
 | 
			
		||||
			liftIO $ bracket_ (changeWorkingDirectory (Git.workTree r))
 | 
			
		||||
				(\_ -> changeWorkingDirectory cwd) $
 | 
			
		||||
					boolSystem command params
 | 
			
		||||
		else if (Git.repoIsSsh r)
 | 
			
		||||
			then do
 | 
			
		||||
				ok <- liftIO $ boolSystem "ssh"
 | 
			
		||||
					[Git.urlHost r, "rm -f " ++
 | 
			
		||||
					(shellEscape file)]
 | 
			
		||||
				if (ok)
 | 
			
		||||
					then return ()
 | 
			
		||||
					else error "failed to remove file from remote"
 | 
			
		||||
			else error "removing file from non-ssh repo not supported"
 | 
			
		||||
 | 
			
		||||
{- Update's a remote's location log for a key, by merging the local
 | 
			
		||||
 - location log into it. -}
 | 
			
		||||
updateRemoteLogStatus :: Git.Repo -> Key -> Annex ()
 | 
			
		||||
updateRemoteLogStatus r key = do
 | 
			
		||||
	-- To merge, just append data to the remote's
 | 
			
		||||
	-- log. Since the log is timestamped, the presumably newer
 | 
			
		||||
	-- information from the local will superscede the older
 | 
			
		||||
	-- information in the remote's log.
 | 
			
		||||
	-- TODO: remote log locking
 | 
			
		||||
	let mergecmd = "cat >> " ++ (shellEscape $ logFile r key) ++ " && " ++
 | 
			
		||||
		"cd " ++ (shellEscape $ Git.workTree r) ++ " && " ++
 | 
			
		||||
		"git add " ++ (shellEscape $ stateLoc)
 | 
			
		||||
	let shellcmd = if (not $ Git.repoIsUrl r)
 | 
			
		||||
		then pOpen WriteToPipe "sh" ["-c", mergecmd]
 | 
			
		||||
		else if (Git.repoIsSsh r)
 | 
			
		||||
			then pOpen WriteToPipe "ssh" [Git.urlHost r, mergecmd]
 | 
			
		||||
			else error "updating non-ssh repo not supported"
 | 
			
		||||
	g <- Annex.gitRepo
 | 
			
		||||
	liftIO $ shellcmd $ \h -> do
 | 
			
		||||
		lines <- readLog $ logFile g key
 | 
			
		||||
		hPutStrLn h $ unlines $ map show lines
 | 
			
		||||
				liftIO $ boolSystem "ssh" [Git.urlHost r,
 | 
			
		||||
					"cd " ++ (shellEscape $ Git.workTree r) ++
 | 
			
		||||
					" && " ++ command ++ " " ++
 | 
			
		||||
					unwords params]
 | 
			
		||||
			else error "running command in non-ssh repo not supported"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,12 +31,12 @@ data AnnexState = AnnexState {
 | 
			
		|||
type Annex = StateT AnnexState IO
 | 
			
		||||
 | 
			
		||||
-- annexed filenames are mapped through a backend into keys
 | 
			
		||||
type KeyFrag = String
 | 
			
		||||
type KeyName = String
 | 
			
		||||
type BackendName = String
 | 
			
		||||
data Key = Key (BackendName, KeyFrag) deriving (Eq)
 | 
			
		||||
data Key = Key (BackendName, KeyName) deriving (Eq)
 | 
			
		||||
 | 
			
		||||
-- constructs a key in a backend
 | 
			
		||||
genKey :: Backend -> KeyFrag -> Key
 | 
			
		||||
genKey :: Backend -> KeyName -> Key
 | 
			
		||||
genKey b f = Key (name b,f)
 | 
			
		||||
 | 
			
		||||
-- show a key to convert it to a string; the string includes the
 | 
			
		||||
| 
						 | 
				
			
			@ -51,9 +51,10 @@ instance Read Key where
 | 
			
		|||
			b = l !! 0
 | 
			
		||||
			k = join ":" $ drop 1 l
 | 
			
		||||
 | 
			
		||||
-- pulls the backend name out
 | 
			
		||||
backendName :: Key -> BackendName
 | 
			
		||||
backendName (Key (b,k)) = b
 | 
			
		||||
keyName :: Key -> KeyName
 | 
			
		||||
keyName (Key (b,k)) = k
 | 
			
		||||
 | 
			
		||||
-- this structure represents a key-value backend
 | 
			
		||||
data Backend = Backend {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								Types.hs
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								Types.hs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -7,6 +7,7 @@ module Types (
 | 
			
		|||
	Key,
 | 
			
		||||
	genKey,
 | 
			
		||||
	backendName,
 | 
			
		||||
	keyName,
 | 
			
		||||
	FlagName,
 | 
			
		||||
	Flag(..)
 | 
			
		||||
) where
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,6 +116,13 @@ Many git-annex subcommands will stage changes for later `git commit` by you.
 | 
			
		|||
 | 
			
		||||
	git annex fromkey --backend=URL --key=http://www.archive.org/somefile somefile
 | 
			
		||||
 | 
			
		||||
* dropkey [key ...]
 | 
			
		||||
 | 
			
		||||
  Drops the cached data for the specified keys from this repository.
 | 
			
		||||
 | 
			
		||||
  This can be used to drop content for arbitrary keys, which do not need
 | 
			
		||||
  to have a file in the git repository pointing at them.
 | 
			
		||||
 | 
			
		||||
# OPTIONS
 | 
			
		||||
 | 
			
		||||
* --force
 | 
			
		||||
| 
						 | 
				
			
			@ -123,6 +130,11 @@ Many git-annex subcommands will stage changes for later `git commit` by you.
 | 
			
		|||
  Force unsafe actions, such as dropping a file's content when no other
 | 
			
		||||
  source of it can be verified to still exist. Use with care.
 | 
			
		||||
 | 
			
		||||
* --quiet
 | 
			
		||||
 | 
			
		||||
  Avoid the default verbose logging of what is done; only show errors
 | 
			
		||||
  and progress displays.
 | 
			
		||||
 | 
			
		||||
* --backend=name
 | 
			
		||||
 | 
			
		||||
  Specify the default key-value backend to use, adding it to the front
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue