where indenting

This commit is contained in:
Joey Hess 2012-11-11 00:51:07 -04:00
parent 6a0756d2fb
commit 2172cc586e
42 changed files with 1193 additions and 1209 deletions

View file

@ -57,24 +57,23 @@ shaN shasize file filesize = do
Left sha -> liftIO $ sha <$> L.readFile file Left sha -> liftIO $ sha <$> L.readFile file
Right command -> liftIO $ parse command . lines <$> Right command -> liftIO $ parse command . lines <$>
readsha command (toCommand [File file]) readsha command (toCommand [File file])
where where
parse command [] = bad command parse command [] = bad command
parse command (l:_) parse command (l:_)
| null sha = bad command | null sha = bad command
| otherwise = sha | otherwise = sha
where where
sha = fst $ separate (== ' ') l sha = fst $ separate (== ' ') l
bad command = error $ command ++ " parse error" bad command = error $ command ++ " parse error"
{- sha commands output the filename, so need to set fileEncoding -} {- sha commands output the filename, so need to set fileEncoding -}
readsha command args = readsha command args =
withHandle StdoutHandle createProcessSuccess p $ \h -> do withHandle StdoutHandle createProcessSuccess p $ \h -> do
fileEncoding h fileEncoding h
output <- hGetContentsStrict h output <- hGetContentsStrict h
hClose h hClose h
return output return output
where where
p = (proc command args) p = (proc command args) { std_out = CreatePipe }
{ std_out = CreatePipe }
shaCommand :: SHASize -> Integer -> Either (L.ByteString -> String) String shaCommand :: SHASize -> Integer -> Either (L.ByteString -> String) String
shaCommand shasize filesize shaCommand shasize filesize
@ -84,14 +83,14 @@ shaCommand shasize filesize
| shasize == 384 = use SysConfig.sha384 sha384 | shasize == 384 = use SysConfig.sha384 sha384
| shasize == 512 = use SysConfig.sha512 sha512 | shasize == 512 = use SysConfig.sha512 sha512
| otherwise = error $ "bad sha size " ++ show shasize | otherwise = error $ "bad sha size " ++ show shasize
where where
use Nothing sha = Left $ showDigest . sha use Nothing sha = Left $ showDigest . sha
use (Just c) sha use (Just c) sha
-- use builtin, but slower sha for small files {- use builtin, but slower sha for small files
-- benchmarking indicates it's faster up to - benchmarking indicates it's faster up to
-- and slightly beyond 50 kb files - and slightly beyond 50 kb files -}
| filesize < 51200 = use Nothing sha | filesize < 51200 = use Nothing sha
| otherwise = Right c | otherwise = Right c
{- A key is a checksum of its contents. -} {- A key is a checksum of its contents. -}
keyValue :: SHASize -> KeySource -> Annex (Maybe Key) keyValue :: SHASize -> KeySource -> Annex (Maybe Key)
@ -109,23 +108,23 @@ keyValue shasize source = do
{- Extension preserving keys. -} {- Extension preserving keys. -}
keyValueE :: SHASize -> KeySource -> Annex (Maybe Key) keyValueE :: SHASize -> KeySource -> Annex (Maybe Key)
keyValueE size source = keyValue size source >>= maybe (return Nothing) addE keyValueE size source = keyValue size source >>= maybe (return Nothing) addE
where where
addE k = return $ Just $ k addE k = return $ Just $ k
{ keyName = keyName k ++ selectExtension (keyFilename source) { keyName = keyName k ++ selectExtension (keyFilename source)
, keyBackendName = shaNameE size , keyBackendName = shaNameE size
} }
selectExtension :: FilePath -> String selectExtension :: FilePath -> String
selectExtension f selectExtension f
| null es = "" | null es = ""
| otherwise = join "." ("":es) | otherwise = join "." ("":es)
where where
es = filter (not . null) $ reverse $ es = filter (not . null) $ reverse $
take 2 $ takeWhile shortenough $ take 2 $ takeWhile shortenough $
reverse $ split "." $ takeExtensions f reverse $ split "." $ takeExtensions f
shortenough e shortenough e
| '\n' `elem` e = False -- newline in extension?! | '\n' `elem` e = False -- newline in extension?!
| otherwise = length e <= 4 -- long enough for "jpeg" | otherwise = length e <= 4 -- long enough for "jpeg"
{- A key's checksum is checked during fsck. -} {- A key's checksum is checked during fsck. -}
checkKeyChecksum :: SHASize -> Key -> FilePath -> Annex Bool checkKeyChecksum :: SHASize -> Key -> FilePath -> Annex Bool
@ -137,7 +136,7 @@ checkKeyChecksum size key file = do
let filesize = fromIntegral $ fileSize stat let filesize = fromIntegral $ fileSize stat
check <$> shaN size file filesize check <$> shaN size file filesize
_ -> return True _ -> return True
where where
check s check s
| s == dropExtensions (keyName key) = True | s == dropExtensions (keyName key) = True
| otherwise = False | otherwise = False

View file

@ -32,10 +32,10 @@ fromUrl url size = stubKey
, keyBackendName = "URL" , keyBackendName = "URL"
, keySize = size , keySize = size
} }
where where
-- when it's not too long, use the url as the key name {- when it's not too long, use the url as the key name
-- 256 is the absolute filename max, but use a shorter - 256 is the absolute filename max, but use a shorter
-- length because this is not the entire key filename. - length because this is not the entire key filename. -}
key key
| length url < 128 = url | length url < 128 = url
| otherwise = take 128 url ++ "-" ++ md5s (Str url) | otherwise = take 128 url ++ "-" ++ md5s (Str url)

View file

@ -45,19 +45,18 @@ tests =
- known-good hashes. -} - known-good hashes. -}
shaTestCases :: [(Int, String)] -> [TestCase] shaTestCases :: [(Int, String)] -> [TestCase]
shaTestCases l = map make l shaTestCases l = map make l
where where
make (n, knowngood) = make (n, knowngood) = TestCase key $ maybeSelectCmd key $
TestCase key $ maybeSelectCmd key $ zip (shacmds n) (repeat check)
zip (shacmds n) (repeat check) where
where key = "sha" ++ show n
key = "sha" ++ show n check = "</dev/null | grep -q '" ++ knowngood ++ "'"
check = "</dev/null | grep -q '" ++ knowngood ++ "'" shacmds n = concatMap (\x -> [x, 'g':x, osxpath </> x]) $
shacmds n = concatMap (\x -> [x, 'g':x, osxpath </> x]) $ map (\x -> "sha" ++ show n ++ x) ["sum", ""]
map (\x -> "sha" ++ show n ++ x) ["sum", ""] {- Max OSX sometimes puts GNU tools outside PATH, so look in
{- Max OSX sometimes puts GNU tools outside PATH, so look in - the location it uses, and remember where to run them
- the location it uses, and remember where to run them - from. -}
- from. -} osxpath = "/opt/local/libexec/gnubin"
osxpath = "/opt/local/libexec/gnubin"
tmpDir :: String tmpDir :: String
tmpDir = "tmp" tmpDir = "tmp"
@ -67,9 +66,9 @@ testFile = tmpDir ++ "/testfile"
testCp :: ConfigKey -> String -> TestCase testCp :: ConfigKey -> String -> TestCase
testCp k option = TestCase cmd $ testCmd k cmdline testCp k option = TestCase cmd $ testCmd k cmdline
where where
cmd = "cp " ++ option cmd = "cp " ++ option
cmdline = cmd ++ " " ++ testFile ++ " " ++ testFile ++ ".new" cmdline = cmd ++ " " ++ testFile ++ " " ++ testFile ++ ".new"
{- Pulls package version out of the changelog. -} {- Pulls package version out of the changelog. -}
getVersion :: Test getVersion :: Test
@ -82,8 +81,8 @@ getVersionString = do
changelog <- readFile "CHANGELOG" changelog <- readFile "CHANGELOG"
let verline = head $ lines changelog let verline = head $ lines changelog
return $ middle (words verline !! 1) return $ middle (words verline !! 1)
where where
middle = drop 1 . init middle = drop 1 . init
getGitVersion :: Test getGitVersion :: Test
getGitVersion = do getGitVersion = do
@ -104,14 +103,14 @@ cabalSetup = do
map (setfield "Version" version) $ map (setfield "Version" version) $
lines cabal lines cabal
renameFile tmpcabalfile cabalfile renameFile tmpcabalfile cabalfile
where where
cabalfile = "git-annex.cabal" cabalfile = "git-annex.cabal"
tmpcabalfile = cabalfile++".tmp" tmpcabalfile = cabalfile++".tmp"
setfield field value s setfield field value s
| fullfield `isPrefixOf` s = fullfield ++ value | fullfield `isPrefixOf` s = fullfield ++ value
| otherwise = s | otherwise = s
where where
fullfield = field ++ ": " fullfield = field ++ ": "
setup :: IO () setup :: IO ()
setup = do setup = do

View file

@ -46,11 +46,11 @@ autostart command = genDesktopEntry
systemwideInstall :: IO Bool systemwideInstall :: IO Bool
systemwideInstall = isroot <||> destdirset systemwideInstall = isroot <||> destdirset
where where
isroot = do isroot = do
uid <- fromIntegral <$> getRealUserID uid <- fromIntegral <$> getRealUserID
return $ uid == (0 :: Int) return $ uid == (0 :: Int)
destdirset = isJust <$> catchMaybeIO (getEnv "DESTDIR") destdirset = isJust <$> catchMaybeIO (getEnv "DESTDIR")
inDestDir :: FilePath -> IO FilePath inDestDir :: FilePath -> IO FilePath
inDestDir f = do inDestDir f = do
@ -91,6 +91,6 @@ install command = do
main :: IO () main :: IO ()
main = getArgs >>= go main = getArgs >>= go
where where
go [] = error "specify git-annex command" go [] = error "specify git-annex command"
go (command:_) = install command go (command:_) = install command

View file

@ -29,22 +29,22 @@ instance Show Config where
[ key ++ " :: " ++ valuetype value [ key ++ " :: " ++ valuetype value
, key ++ " = " ++ show value , key ++ " = " ++ show value
] ]
where where
valuetype (BoolConfig _) = "Bool" valuetype (BoolConfig _) = "Bool"
valuetype (StringConfig _) = "String" valuetype (StringConfig _) = "String"
valuetype (MaybeStringConfig _) = "Maybe String" valuetype (MaybeStringConfig _) = "Maybe String"
valuetype (MaybeBoolConfig _) = "Maybe Bool" valuetype (MaybeBoolConfig _) = "Maybe Bool"
writeSysConfig :: [Config] -> IO () writeSysConfig :: [Config] -> IO ()
writeSysConfig config = writeFile "Build/SysConfig.hs" body writeSysConfig config = writeFile "Build/SysConfig.hs" body
where where
body = unlines $ header ++ map show config ++ footer body = unlines $ header ++ map show config ++ footer
header = [ header = [
"{- Automatically generated. -}" "{- Automatically generated. -}"
, "module Build.SysConfig where" , "module Build.SysConfig where"
, "" , ""
] ]
footer = [] footer = []
runTests :: [TestCase] -> IO [Config] runTests :: [TestCase] -> IO [Config]
runTests [] = return [] runTests [] = return []
@ -60,12 +60,12 @@ requireCmd :: ConfigKey -> String -> Test
requireCmd k cmdline = do requireCmd k cmdline = do
ret <- testCmd k cmdline ret <- testCmd k cmdline
handle ret handle ret
where where
handle r@(Config _ (BoolConfig True)) = return r handle r@(Config _ (BoolConfig True)) = return r
handle r = do handle r = do
testEnd r testEnd r
error $ "** the " ++ c ++ " command is required" error $ "** the " ++ c ++ " command is required"
c = head $ words cmdline c = head $ words cmdline
{- Checks if a command is available by running a command line. -} {- Checks if a command is available by running a command line. -}
testCmd :: ConfigKey -> String -> Test testCmd :: ConfigKey -> String -> Test
@ -90,13 +90,13 @@ maybeSelectCmd k = searchCmd
searchCmd :: (String -> Test) -> ([String] -> Test) -> [(String, String)] -> Test searchCmd :: (String -> Test) -> ([String] -> Test) -> [(String, String)] -> Test
searchCmd success failure cmdsparams = search cmdsparams searchCmd success failure cmdsparams = search cmdsparams
where where
search [] = failure $ fst $ unzip cmdsparams search [] = failure $ fst $ unzip cmdsparams
search ((c, params):cs) = do search ((c, params):cs) = do
ret <- system $ quiet $ c ++ " " ++ params ret <- system $ quiet $ c ++ " " ++ params
if ret == ExitSuccess if ret == ExitSuccess
then success c then success c
else search cs else search cs
quiet :: String -> String quiet :: String -> String
quiet s = s ++ " >/dev/null 2>&1" quiet s = s ++ " >/dev/null 2>&1"

View file

@ -44,13 +44,13 @@ dispatch fuzzyok allargs allcmds commonoptions fields header getgitrepo = do
sequence_ flags sequence_ flags
prepCommand cmd params prepCommand cmd params
tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdnocommit cmd] tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdnocommit cmd]
where where
err msg = msg ++ "\n\n" ++ usage header allcmds commonoptions err msg = msg ++ "\n\n" ++ usage header allcmds commonoptions
cmd = Prelude.head cmds cmd = Prelude.head cmds
(fuzzy, cmds, name, args) = findCmd fuzzyok allargs allcmds err (fuzzy, cmds, name, args) = findCmd fuzzyok allargs allcmds err
(flags, params) = getOptCmd args cmd commonoptions err (flags, params) = getOptCmd args cmd commonoptions err
checkfuzzy = when fuzzy $ checkfuzzy = when fuzzy $
inRepo $ Git.AutoCorrect.prepare name cmdname cmds inRepo $ Git.AutoCorrect.prepare name cmdname cmds
{- Parses command line params far enough to find the Command to run, and {- Parses command line params far enough to find the Command to run, and
- returns the remaining params. - returns the remaining params.
@ -61,25 +61,25 @@ findCmd fuzzyok argv cmds err
| not (null exactcmds) = (False, exactcmds, fromJust name, args) | not (null exactcmds) = (False, exactcmds, fromJust name, args)
| fuzzyok && not (null inexactcmds) = (True, inexactcmds, fromJust name, args) | fuzzyok && not (null inexactcmds) = (True, inexactcmds, fromJust name, args)
| otherwise = error $ err $ "unknown command " ++ fromJust name | otherwise = error $ err $ "unknown command " ++ fromJust name
where where
(name, args) = findname argv [] (name, args) = findname argv []
findname [] c = (Nothing, reverse c) findname [] c = (Nothing, reverse c)
findname (a:as) c findname (a:as) c
| "-" `isPrefixOf` a = findname as (a:c) | "-" `isPrefixOf` a = findname as (a:c)
| otherwise = (Just a, reverse c ++ as) | otherwise = (Just a, reverse c ++ as)
exactcmds = filter (\c -> name == Just (cmdname c)) cmds exactcmds = filter (\c -> name == Just (cmdname c)) cmds
inexactcmds = case name of inexactcmds = case name of
Nothing -> [] Nothing -> []
Just n -> Git.AutoCorrect.fuzzymatches n cmdname cmds Just n -> Git.AutoCorrect.fuzzymatches n cmdname cmds
{- Parses command line options, and returns actions to run to configure flags {- Parses command line options, and returns actions to run to configure flags
- and the remaining parameters for the command. -} - and the remaining parameters for the command. -}
getOptCmd :: Params -> Command -> [Option] -> (String -> String) -> (Flags, Params) getOptCmd :: Params -> Command -> [Option] -> (String -> String) -> (Flags, Params)
getOptCmd argv cmd commonoptions err = check $ getOptCmd argv cmd commonoptions err = check $
getOpt Permute (commonoptions ++ cmdoptions cmd) argv getOpt Permute (commonoptions ++ cmdoptions cmd) argv
where where
check (flags, rest, []) = (flags, rest) check (flags, rest, []) = (flags, rest)
check (_, _, errs) = error $ err $ concat errs check (_, _, errs) = error $ err $ concat errs
{- Runs a list of Annex actions. Catches IO errors and continues {- Runs a list of Annex actions. Catches IO errors and continues
- (but explicitly thrown errors terminate the whole command). - (but explicitly thrown errors terminate the whole command).
@ -93,18 +93,18 @@ tryRun' errnum _ cmd []
tryRun' errnum state cmd (a:as) = do tryRun' errnum state cmd (a:as) = do
r <- run r <- run
handle $! r handle $! r
where where
run = tryIO $ Annex.run state $ do run = tryIO $ Annex.run state $ do
Annex.Queue.flushWhenFull Annex.Queue.flushWhenFull
a a
handle (Left err) = showerr err >> cont False state handle (Left err) = showerr err >> cont False state
handle (Right (success, state')) = cont success state' handle (Right (success, state')) = cont success state'
cont success s = do cont success s = do
let errnum' = if success then errnum else errnum + 1 let errnum' = if success then errnum else errnum + 1
(tryRun' $! errnum') s cmd as (tryRun' $! errnum') s cmd as
showerr err = Annex.eval state $ do showerr err = Annex.eval state $ do
showErr err showErr err
showEndFail showEndFail
{- Actions to perform each time ran. -} {- Actions to perform each time ran. -}
startup :: Annex Bool startup :: Annex Bool

View file

@ -80,14 +80,14 @@ prepCommand Command { cmdseek = seek, cmdcheck = c } params = do
{- Runs a command through the start, perform and cleanup stages -} {- Runs a command through the start, perform and cleanup stages -}
doCommand :: CommandStart -> CommandCleanup doCommand :: CommandStart -> CommandCleanup
doCommand = start doCommand = start
where where
start = stage $ maybe skip perform start = stage $ maybe skip perform
perform = stage $ maybe failure cleanup perform = stage $ maybe failure cleanup
cleanup = stage $ status cleanup = stage $ status
stage = (=<<) stage = (=<<)
skip = return True skip = return True
failure = showEndFail >> return False failure = showEndFail >> return False
status r = showEndResult r >> return r status r = showEndResult r >> return r
{- Modifies an action to only act on files that are already annexed, {- Modifies an action to only act on files that are already annexed,
- and passes the key and backend on to it. -} - and passes the key and backend on to it. -}
@ -118,26 +118,26 @@ numCopies file = readish <$> checkAttr "annex.numcopies" file
-} -}
autoCopies :: FilePath -> Key -> (Int -> Int -> Bool) -> CommandStart -> CommandStart autoCopies :: FilePath -> Key -> (Int -> Int -> Bool) -> CommandStart -> CommandStart
autoCopies file key vs a = Annex.getState Annex.auto >>= go autoCopies file key vs a = Annex.getState Annex.auto >>= go
where where
go False = a go False = a
go True = do go True = do
numcopiesattr <- numCopies file numcopiesattr <- numCopies file
needed <- getNumCopies numcopiesattr needed <- getNumCopies numcopiesattr
(_, have) <- trustPartition UnTrusted =<< Remote.keyLocations key have <- trustExclude UnTrusted =<< Remote.keyLocations key
if length have `vs` needed then a else stop if length have `vs` needed then a else stop
autoCopiesWith :: FilePath -> Key -> (Int -> Int -> Bool) -> (Maybe Int -> CommandStart) -> CommandStart autoCopiesWith :: FilePath -> Key -> (Int -> Int -> Bool) -> (Maybe Int -> CommandStart) -> CommandStart
autoCopiesWith file key vs a = do autoCopiesWith file key vs a = do
numcopiesattr <- numCopies file numcopiesattr <- numCopies file
Annex.getState Annex.auto >>= auto numcopiesattr Annex.getState Annex.auto >>= auto numcopiesattr
where where
auto numcopiesattr False = a numcopiesattr auto numcopiesattr False = a numcopiesattr
auto numcopiesattr True = do auto numcopiesattr True = do
needed <- getNumCopies numcopiesattr needed <- getNumCopies numcopiesattr
(_, have) <- trustPartition UnTrusted =<< Remote.keyLocations key have <- trustExclude UnTrusted =<< Remote.keyLocations key
if length have `vs` needed if length have `vs` needed
then a numcopiesattr then a numcopiesattr
else stop else stop
checkAuto :: Annex Bool -> Annex Bool checkAuto :: Annex Bool -> Annex Bool
checkAuto checker = ifM (Annex.getState Annex.auto) checkAuto checker = ifM (Annex.getState Annex.auto)

8
Git.hs
View file

@ -81,8 +81,8 @@ repoIsSsh Repo { location = Url url }
| scheme == "git+ssh:" = True | scheme == "git+ssh:" = True
| scheme == "ssh+git:" = True | scheme == "ssh+git:" = True
| otherwise = False | otherwise = False
where where
scheme = uriScheme url scheme = uriScheme url
repoIsSsh _ = False repoIsSsh _ = False
repoIsHttp :: Repo -> Bool repoIsHttp :: Repo -> Bool
@ -126,5 +126,5 @@ hookPath script repo = do
let hook = localGitDir repo </> "hooks" </> script let hook = localGitDir repo </> "hooks" </> script
ifM (catchBoolIO $ isexecutable hook) ifM (catchBoolIO $ isexecutable hook)
( return $ Just hook , return Nothing ) ( return $ Just hook , return Nothing )
where where
isexecutable f = isExecutable . fileMode <$> getFileStatus f isexecutable f = isExecutable . fileMode <$> getFileStatus f

View file

@ -165,12 +165,13 @@ options = Option.common ++
, Option ['T'] ["time-limit"] (ReqArg Limit.addTimeLimit paramTime) , Option ['T'] ["time-limit"] (ReqArg Limit.addTimeLimit paramTime)
"stop after the specified amount of time" "stop after the specified amount of time"
] ++ Option.matcher ] ++ Option.matcher
where where
setnumcopies v = Annex.changeState $ \s -> s { Annex.forcenumcopies = readish v } setnumcopies v = Annex.changeState $
setgitconfig :: String -> Annex () \s -> s { Annex.forcenumcopies = readish v }
setgitconfig v = do setgitconfig :: String -> Annex ()
newg <- inRepo $ Git.Config.store v setgitconfig v = do
Annex.changeState $ \s -> s { Annex.repo = newg } newg <- inRepo $ Git.Config.store v
Annex.changeState $ \s -> s { Annex.repo = newg }
header :: String header :: String
header = "Usage: git-annex command [option ..]" header = "Usage: git-annex command [option ..]"

View file

@ -44,24 +44,22 @@ cmds_notreadonly = concat
cmds :: [Command] cmds :: [Command]
cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
where where
adddirparam c = c adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c }
{ cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c
}
options :: [OptDescr (Annex ())] options :: [OptDescr (Annex ())]
options = Option.common ++ options = Option.common ++
[ Option [] ["uuid"] (ReqArg checkuuid paramUUID) "local repository uuid" [ Option [] ["uuid"] (ReqArg checkuuid paramUUID) "local repository uuid"
] ]
where where
checkuuid expected = getUUID >>= check checkuuid expected = getUUID >>= check
where where
check u | u == toUUID expected = noop check u | u == toUUID expected = noop
check NoUUID = unexpected "uninitialized repository" check NoUUID = unexpected "uninitialized repository"
check u = unexpected $ "UUID " ++ fromUUID u check u = unexpected $ "UUID " ++ fromUUID u
unexpected s = error $ unexpected s = error $
"expected repository UUID " ++ "expected repository UUID " ++
expected ++ " but found " ++ s expected ++ " but found " ++ s
header :: String header :: String
header = "Usage: git-annex-shell [-c] command [parameters ...] [option ..]" header = "Usage: git-annex-shell [-c] command [parameters ...] [option ..]"
@ -152,20 +150,20 @@ checkDirectory mdir = do
if d' `equalFilePath` dir' if d' `equalFilePath` dir'
then noop then noop
else req d' (Just dir') else req d' (Just dir')
where where
req d mdir' = error $ unwords req d mdir' = error $ unwords
[ "Only allowed to access" [ "Only allowed to access"
, d , d
, maybe "and could not determine directory from command line" ("not " ++) mdir' , maybe "and could not determine directory from command line" ("not " ++) mdir'
] ]
{- A directory may start with ~/ or in some cases, even /~/, {- A directory may start with ~/ or in some cases, even /~/,
- or could just be relative to home, or of course could - or could just be relative to home, or of course could
- be absolute. -} - be absolute. -}
canondir home d canondir home d
| "~/" `isPrefixOf` d = return d | "~/" `isPrefixOf` d = return d
| "/~/" `isPrefixOf` d = return $ drop 1 d | "/~/" `isPrefixOf` d = return $ drop 1 d
| otherwise = relHome $ absPathFrom home d | otherwise = relHome $ absPathFrom home d
checkEnv :: String -> IO () checkEnv :: String -> IO ()
checkEnv var = do checkEnv var = do

View file

@ -64,10 +64,10 @@ groupMapLoad = do
makeGroupMap :: M.Map UUID (S.Set Group) -> GroupMap makeGroupMap :: M.Map UUID (S.Set Group) -> GroupMap
makeGroupMap byuuid = GroupMap byuuid bygroup makeGroupMap byuuid = GroupMap byuuid bygroup
where where
bygroup = M.fromListWith S.union $ bygroup = M.fromListWith S.union $
concat $ map explode $ M.toList byuuid concat $ map explode $ M.toList byuuid
explode (u, s) = map (\g -> (g, S.singleton u)) (S.toList s) explode (u, s) = map (\g -> (g, S.singleton u)) (S.toList s)
{- If a repository is in exactly one standard group, returns it. -} {- If a repository is in exactly one standard group, returns it. -}
getStandardGroup :: S.Set Group -> Maybe StandardGroup getStandardGroup :: S.Set Group -> Maybe StandardGroup

View file

@ -47,13 +47,13 @@ loggedKeys = mapMaybe (logFileKey . takeFileName) <$> Annex.Branch.files
- they are present for the specified repository. -} - they are present for the specified repository. -}
loggedKeysFor :: UUID -> Annex [Key] loggedKeysFor :: UUID -> Annex [Key]
loggedKeysFor u = filterM isthere =<< loggedKeys loggedKeysFor u = filterM isthere =<< loggedKeys
where where
{- This should run strictly to avoid the filterM {- This should run strictly to avoid the filterM
- building many thunks containing keyLocations data. -} - building many thunks containing keyLocations data. -}
isthere k = do isthere k = do
us <- loggedLocations k us <- loggedLocations k
let !there = u `elem` us let !there = u `elem` us
return there return there
{- The filename of the log file for a given key. -} {- The filename of the log file for a given key. -}
logFile :: Key -> String logFile :: Key -> String
@ -64,5 +64,5 @@ logFileKey :: FilePath -> Maybe Key
logFileKey file logFileKey file
| ext == ".log" = fileKey base | ext == ".log" = fileKey base
| otherwise = Nothing | otherwise = Nothing
where where
(base, ext) = splitAt (length file - 4) file (base, ext) = splitAt (length file - 4) file

View file

@ -90,8 +90,8 @@ makeMatcher groupmap u s
| s == "standard" = standardMatcher groupmap u | s == "standard" = standardMatcher groupmap u
| null (lefts tokens) = Utility.Matcher.generate $ rights tokens | null (lefts tokens) = Utility.Matcher.generate $ rights tokens
| otherwise = matchAll | otherwise = matchAll
where where
tokens = map (parseToken (Just u) groupmap) (tokenizeMatcher s) tokens = map (parseToken (Just u) groupmap) (tokenizeMatcher s)
{- Standard matchers are pre-defined for some groups. If none is defined, {- Standard matchers are pre-defined for some groups. If none is defined,
- or a repository is in multiple groups with standard matchers, match all. -} - or a repository is in multiple groups with standard matchers, match all. -}
@ -124,17 +124,17 @@ parseToken mu groupmap t
, ("smallerthan", limitSize (<)) , ("smallerthan", limitSize (<))
, ("inallgroup", limitInAllGroup groupmap) , ("inallgroup", limitInAllGroup groupmap)
] ]
where where
(k, v) = separate (== '=') t (k, v) = separate (== '=') t
use a = Utility.Matcher.Operation <$> a v use a = Utility.Matcher.Operation <$> a v
{- This is really dumb tokenization; there's no support for quoted values. {- This is really dumb tokenization; there's no support for quoted values.
- Open and close parens are always treated as standalone tokens; - Open and close parens are always treated as standalone tokens;
- otherwise tokens must be separated by whitespace. -} - otherwise tokens must be separated by whitespace. -}
tokenizeMatcher :: String -> [String] tokenizeMatcher :: String -> [String]
tokenizeMatcher = filter (not . null ) . concatMap splitparens . words tokenizeMatcher = filter (not . null ) . concatMap splitparens . words
where where
splitparens = segmentDelim (`elem` "()") splitparens = segmentDelim (`elem` "()")
{- Puts a UUID in a standard group, and sets its preferred content to use {- Puts a UUID in a standard group, and sets its preferred content to use
- the standard expression for that group, unless something is already set. -} - the standard expression for that group, unless something is already set. -}

View file

@ -53,23 +53,23 @@ readLog = parseLog <$$> Annex.Branch.get
{- Parses a log file. Unparseable lines are ignored. -} {- Parses a log file. Unparseable lines are ignored. -}
parseLog :: String -> [LogLine] parseLog :: String -> [LogLine]
parseLog = mapMaybe (parseline . words) . lines parseLog = mapMaybe (parseline . words) . lines
where where
parseline (a:b:c:_) = do parseline (a:b:c:_) = do
d <- parseTime defaultTimeLocale "%s%Qs" a d <- parseTime defaultTimeLocale "%s%Qs" a
s <- parsestatus b s <- parsestatus b
Just $ LogLine (utcTimeToPOSIXSeconds d) s c Just $ LogLine (utcTimeToPOSIXSeconds d) s c
parseline _ = Nothing parseline _ = Nothing
parsestatus "1" = Just InfoPresent parsestatus "1" = Just InfoPresent
parsestatus "0" = Just InfoMissing parsestatus "0" = Just InfoMissing
parsestatus _ = Nothing parsestatus _ = Nothing
{- Generates a log file. -} {- Generates a log file. -}
showLog :: [LogLine] -> String showLog :: [LogLine] -> String
showLog = unlines . map genline showLog = unlines . map genline
where where
genline (LogLine d s i) = unwords [show d, genstatus s, i] genline (LogLine d s i) = unwords [show d, genstatus s, i]
genstatus InfoPresent = "1" genstatus InfoPresent = "1"
genstatus InfoMissing = "0" genstatus InfoMissing = "0"
{- Generates a new LogLine with the current date. -} {- Generates a new LogLine with the current date. -}
logNow :: LogStatus -> String -> Annex LogLine logNow :: LogStatus -> String -> Annex LogLine
@ -102,7 +102,7 @@ mapLog :: LogLine -> LogMap -> LogMap
mapLog l m mapLog l m
| better = M.insert i l m | better = M.insert i l m
| otherwise = m | otherwise = m
where where
better = maybe True newer $ M.lookup i m better = maybe True newer $ M.lookup i m
newer l' = date l' <= date l newer l' = date l' <= date l
i = info l i = info l

View file

@ -48,40 +48,40 @@ showConfig = unwords . configToKeyVal
{- Given Strings like "key=value", generates a RemoteConfig. -} {- Given Strings like "key=value", generates a RemoteConfig. -}
keyValToConfig :: [String] -> RemoteConfig keyValToConfig :: [String] -> RemoteConfig
keyValToConfig ws = M.fromList $ map (/=/) ws keyValToConfig ws = M.fromList $ map (/=/) ws
where where
(/=/) s = (k, v) (/=/) s = (k, v)
where where
k = takeWhile (/= '=') s k = takeWhile (/= '=') s
v = configUnEscape $ drop (1 + length k) s v = configUnEscape $ drop (1 + length k) s
configToKeyVal :: M.Map String String -> [String] configToKeyVal :: M.Map String String -> [String]
configToKeyVal m = map toword $ sort $ M.toList m configToKeyVal m = map toword $ sort $ M.toList m
where where
toword (k, v) = k ++ "=" ++ configEscape v toword (k, v) = k ++ "=" ++ configEscape v
configEscape :: String -> String configEscape :: String -> String
configEscape = concatMap escape configEscape = concatMap escape
where where
escape c escape c
| isSpace c || c `elem` "&" = "&" ++ show (ord c) ++ ";" | isSpace c || c `elem` "&" = "&" ++ show (ord c) ++ ";"
| otherwise = [c] | otherwise = [c]
configUnEscape :: String -> String configUnEscape :: String -> String
configUnEscape = unescape configUnEscape = unescape
where where
unescape [] = [] unescape [] = []
unescape (c:rest) unescape (c:rest)
| c == '&' = entity rest | c == '&' = entity rest
| otherwise = c : unescape rest | otherwise = c : unescape rest
entity s entity s
| not (null num) && ";" `isPrefixOf` r = | not (null num) && ";" `isPrefixOf` r =
chr (Prelude.read num) : unescape rest chr (Prelude.read num) : unescape rest
| otherwise = | otherwise =
'&' : unescape s '&' : unescape s
where where
num = takeWhile isNumber s num = takeWhile isNumber s
r = drop (length num) s r = drop (length num) s
rest = drop 1 r rest = drop 1 r
{- for quickcheck -} {- for quickcheck -}
prop_idempotent_configEscape :: String -> Bool prop_idempotent_configEscape :: String -> Bool

View file

@ -109,43 +109,42 @@ runTransfer t file shouldretry a = do
bracketIO (prep tfile mode info) (cleanup tfile) (a meter) bracketIO (prep tfile mode info) (cleanup tfile) (a meter)
unless ok $ failed info unless ok $ failed info
return ok return ok
where where
prep tfile mode info = catchMaybeIO $ do prep tfile mode info = catchMaybeIO $ do
fd <- openFd (transferLockFile tfile) ReadWrite (Just mode) fd <- openFd (transferLockFile tfile) ReadWrite (Just mode)
defaultFileFlags { trunc = True } defaultFileFlags { trunc = True }
locked <- catchMaybeIO $ locked <- catchMaybeIO $
setLock fd (WriteLock, AbsoluteSeek, 0, 0) setLock fd (WriteLock, AbsoluteSeek, 0, 0)
when (locked == Nothing) $ when (locked == Nothing) $
error $ "transfer already in progress" error $ "transfer already in progress"
writeTransferInfoFile info tfile writeTransferInfoFile info tfile
return fd return fd
cleanup _ Nothing = noop cleanup _ Nothing = noop
cleanup tfile (Just fd) = do cleanup tfile (Just fd) = do
void $ tryIO $ removeFile tfile void $ tryIO $ removeFile tfile
void $ tryIO $ removeFile $ transferLockFile tfile void $ tryIO $ removeFile $ transferLockFile tfile
closeFd fd closeFd fd
failed info = do failed info = do
failedtfile <- fromRepo $ failedTransferFile t failedtfile <- fromRepo $ failedTransferFile t
createAnnexDirectory $ takeDirectory failedtfile createAnnexDirectory $ takeDirectory failedtfile
liftIO $ writeTransferInfoFile info failedtfile liftIO $ writeTransferInfoFile info failedtfile
retry oldinfo metervar run = do retry oldinfo metervar run = do
v <- tryAnnex run v <- tryAnnex run
case v of case v of
Right b -> return b Right b -> return b
Left _ -> do Left _ -> do
b <- getbytescomplete metervar b <- getbytescomplete metervar
let newinfo = oldinfo { bytesComplete = Just b } let newinfo = oldinfo { bytesComplete = Just b }
if shouldretry oldinfo newinfo if shouldretry oldinfo newinfo
then retry newinfo metervar run then retry newinfo metervar run
else return False else return False
getbytescomplete metervar getbytescomplete metervar
| transferDirection t == Upload = | transferDirection t == Upload =
liftIO $ readMVar metervar liftIO $ readMVar metervar
| otherwise = do | otherwise = do
f <- fromRepo $ gitAnnexTmpLocation (transferKey t) f <- fromRepo $ gitAnnexTmpLocation (transferKey t)
liftIO $ catchDefaultIO 0 $ liftIO $ catchDefaultIO 0 $
fromIntegral . fileSize fromIntegral . fileSize <$> getFileStatus f
<$> getFileStatus f
{- Generates a callback that can be called as transfer progresses to update {- Generates a callback that can be called as transfer progresses to update
- the transfer info file. Also returns the file it'll be updating, and a - the transfer info file. Also returns the file it'll be updating, and a
@ -156,20 +155,20 @@ mkProgressUpdater t info = do
_ <- tryAnnex $ createAnnexDirectory $ takeDirectory tfile _ <- tryAnnex $ createAnnexDirectory $ takeDirectory tfile
mvar <- liftIO $ newMVar 0 mvar <- liftIO $ newMVar 0
return (liftIO . updater tfile mvar, tfile, mvar) return (liftIO . updater tfile mvar, tfile, mvar)
where where
updater tfile mvar bytes = modifyMVar_ mvar $ \oldbytes -> do updater tfile mvar bytes = modifyMVar_ mvar $ \oldbytes -> do
if (bytes - oldbytes >= mindelta) if (bytes - oldbytes >= mindelta)
then do then do
let info' = info { bytesComplete = Just bytes } let info' = info { bytesComplete = Just bytes }
_ <- tryIO $ writeTransferInfoFile info' tfile _ <- tryIO $ writeTransferInfoFile info' tfile
return bytes return bytes
else return oldbytes else return oldbytes
{- The minimum change in bytesComplete that is worth {- The minimum change in bytesComplete that is worth
- updating a transfer info file for is 1% of the total - updating a transfer info file for is 1% of the total
- keySize, rounded down. -} - keySize, rounded down. -}
mindelta = case keySize (transferKey t) of mindelta = case keySize (transferKey t) of
Just sz -> sz `div` 100 Just sz -> sz `div` 100
Nothing -> 100 * 1024 -- arbitrarily, 100 kb Nothing -> 100 * 1024 -- arbitrarily, 100 kb
startTransferInfo :: Maybe FilePath -> IO TransferInfo startTransferInfo :: Maybe FilePath -> IO TransferInfo
startTransferInfo file = TransferInfo startTransferInfo file = TransferInfo
@ -206,25 +205,23 @@ getTransfers = do
infos <- mapM checkTransfer transfers infos <- mapM checkTransfer transfers
return $ map (\(t, Just i) -> (t, i)) $ return $ map (\(t, Just i) -> (t, i)) $
filter running $ zip transfers infos filter running $ zip transfers infos
where where
findfiles = liftIO . mapM dirContentsRecursive findfiles = liftIO . mapM dirContentsRecursive
=<< mapM (fromRepo . transferDir) =<< mapM (fromRepo . transferDir) [Download, Upload]
[Download, Upload] running (_, i) = isJust i
running (_, i) = isJust i
{- Gets failed transfers for a given remote UUID. -} {- Gets failed transfers for a given remote UUID. -}
getFailedTransfers :: UUID -> Annex [(Transfer, TransferInfo)] getFailedTransfers :: UUID -> Annex [(Transfer, TransferInfo)]
getFailedTransfers u = catMaybes <$> (liftIO . getpairs =<< concat <$> findfiles) getFailedTransfers u = catMaybes <$> (liftIO . getpairs =<< concat <$> findfiles)
where where
getpairs = mapM $ \f -> do getpairs = mapM $ \f -> do
let mt = parseTransferFile f let mt = parseTransferFile f
mi <- readTransferInfoFile Nothing f mi <- readTransferInfoFile Nothing f
return $ case (mt, mi) of return $ case (mt, mi) of
(Just t, Just i) -> Just (t, i) (Just t, Just i) -> Just (t, i)
_ -> Nothing _ -> Nothing
findfiles = liftIO . mapM dirContentsRecursive findfiles = liftIO . mapM dirContentsRecursive
=<< mapM (fromRepo . failedTransferDir u) =<< mapM (fromRepo . failedTransferDir u) [Download, Upload]
[Download, Upload]
removeFailedTransfer :: Transfer -> Annex () removeFailedTransfer :: Transfer -> Annex ()
removeFailedTransfer t = do removeFailedTransfer t = do
@ -257,8 +254,8 @@ parseTransferFile file
<*> pure (toUUID u) <*> pure (toUUID u)
<*> fileKey key <*> fileKey key
_ -> Nothing _ -> Nothing
where where
bits = splitDirectories file bits = splitDirectories file
writeTransferInfoFile :: TransferInfo -> FilePath -> IO () writeTransferInfoFile :: TransferInfo -> FilePath -> IO ()
writeTransferInfoFile info tfile = do writeTransferInfoFile info tfile = do
@ -295,16 +292,16 @@ readTransferInfo mpid s = TransferInfo
<*> bytes <*> bytes
<*> pure (if null filename then Nothing else Just filename) <*> pure (if null filename then Nothing else Just filename)
<*> pure False <*> pure False
where where
(firstline, filename) = separate (== '\n') s (firstline, filename) = separate (== '\n') s
bits = split " " firstline bits = split " " firstline
numbits = length bits numbits = length bits
time = if numbits > 0 time = if numbits > 0
then Just <$> parsePOSIXTime =<< headMaybe bits then Just <$> parsePOSIXTime =<< headMaybe bits
else pure Nothing -- not failure else pure Nothing -- not failure
bytes = if numbits > 1 bytes = if numbits > 1
then Just <$> readish =<< headMaybe (drop 1 bits) then Just <$> readish =<< headMaybe (drop 1 bits)
else pure Nothing -- not failure else pure Nothing -- not failure
parsePOSIXTime :: String -> Maybe POSIXTime parsePOSIXTime :: String -> Maybe POSIXTime
parsePOSIXTime s = utcTimeToPOSIXSeconds parsePOSIXTime s = utcTimeToPOSIXSeconds

View file

@ -87,11 +87,10 @@ trustMapLoad = do
let m = M.union overrides $ M.union configured logged let m = M.union overrides $ M.union configured logged
Annex.changeState $ \s -> s { Annex.trustmap = Just m } Annex.changeState $ \s -> s { Annex.trustmap = Just m }
return m return m
where where
configuredtrust r = configuredtrust r = maybe Nothing (\l -> Just (Types.Remote.uuid r, l))
maybe Nothing (\l -> Just (Types.Remote.uuid r, l)) <$> <$> maybe Nothing readTrustLevel
maybe Nothing readTrustLevel <$> getTrustLevel (Types.Remote.repo r)
<$> getTrustLevel (Types.Remote.repo r)
{- Does not include forcetrust or git config values, just those from the {- Does not include forcetrust or git config values, just those from the
- log file. -} - log file. -}
@ -103,11 +102,11 @@ trustMapRaw = simpleMap . parseLog (Just . parseTrustLog)
- trust status, which is why this defaults to Trusted. -} - trust status, which is why this defaults to Trusted. -}
parseTrustLog :: String -> TrustLevel parseTrustLog :: String -> TrustLevel
parseTrustLog s = maybe Trusted parse $ headMaybe $ words s parseTrustLog s = maybe Trusted parse $ headMaybe $ words s
where where
parse "1" = Trusted parse "1" = Trusted
parse "0" = UnTrusted parse "0" = UnTrusted
parse "X" = DeadTrusted parse "X" = DeadTrusted
parse _ = SemiTrusted parse _ = SemiTrusted
showTrustLog :: TrustLevel -> String showTrustLog :: TrustLevel -> String
showTrustLog Trusted = "1" showTrustLog Trusted = "1"

View file

@ -53,32 +53,32 @@ describeUUID uuid desc = do
-} -}
fixBadUUID :: Log String -> Log String fixBadUUID :: Log String -> Log String
fixBadUUID = M.fromList . map fixup . M.toList fixBadUUID = M.fromList . map fixup . M.toList
where where
fixup (k, v) fixup (k, v)
| isbad = (fixeduuid, LogEntry (Date $ newertime v) fixedvalue) | isbad = (fixeduuid, LogEntry (Date $ newertime v) fixedvalue)
| otherwise = (k, v) | otherwise = (k, v)
where where
kuuid = fromUUID k kuuid = fromUUID k
isbad = not (isuuid kuuid) && isuuid lastword isbad = not (isuuid kuuid) && isuuid lastword
ws = words $ value v ws = words $ value v
lastword = Prelude.last ws lastword = Prelude.last ws
fixeduuid = toUUID lastword fixeduuid = toUUID lastword
fixedvalue = unwords $ kuuid: Prelude.init ws fixedvalue = unwords $ kuuid: Prelude.init ws
-- For the fixed line to take precidence, it should be -- For the fixed line to take precidence, it should be
-- slightly newer, but only slightly. -- slightly newer, but only slightly.
newertime (LogEntry (Date d) _) = d + minimumPOSIXTimeSlice newertime (LogEntry (Date d) _) = d + minimumPOSIXTimeSlice
newertime (LogEntry Unknown _) = minimumPOSIXTimeSlice newertime (LogEntry Unknown _) = minimumPOSIXTimeSlice
minimumPOSIXTimeSlice = 0.000001 minimumPOSIXTimeSlice = 0.000001
isuuid s = length s == 36 && length (split "-" s) == 5 isuuid s = length s == 36 && length (split "-" s) == 5
{- Records the uuid in the log, if it's not already there. -} {- Records the uuid in the log, if it's not already there. -}
recordUUID :: UUID -> Annex () recordUUID :: UUID -> Annex ()
recordUUID u = go . M.lookup u =<< uuidMap recordUUID u = go . M.lookup u =<< uuidMap
where where
go (Just "") = set go (Just "") = set
go Nothing = set go Nothing = set
go _ = noop go _ = noop
set = describeUUID u "" set = describeUUID u ""
{- The map is cached for speed. -} {- The map is cached for speed. -}
uuidMap :: Annex UUIDMap uuidMap :: Annex UUIDMap
@ -95,5 +95,5 @@ uuidMapLoad = do
let m' = M.insertWith' preferold u "" m let m' = M.insertWith' preferold u "" m
Annex.changeState $ \s -> s { Annex.uuidmap = Just m' } Annex.changeState $ \s -> s { Annex.uuidmap = Just m' }
return m' return m'
where where
preferold = flip const preferold = flip const

View file

@ -50,36 +50,36 @@ tskey = "timestamp="
showLog :: (a -> String) -> Log a -> String showLog :: (a -> String) -> Log a -> String
showLog shower = unlines . map showpair . M.toList showLog shower = unlines . map showpair . M.toList
where where
showpair (k, LogEntry (Date p) v) = showpair (k, LogEntry (Date p) v) =
unwords [fromUUID k, shower v, tskey ++ show p] unwords [fromUUID k, shower v, tskey ++ show p]
showpair (k, LogEntry Unknown v) = showpair (k, LogEntry Unknown v) =
unwords [fromUUID k, shower v] unwords [fromUUID k, shower v]
parseLog :: (String -> Maybe a) -> String -> Log a parseLog :: (String -> Maybe a) -> String -> Log a
parseLog = parseLogWithUUID . const parseLog = parseLogWithUUID . const
parseLogWithUUID :: (UUID -> String -> Maybe a) -> String -> Log a parseLogWithUUID :: (UUID -> String -> Maybe a) -> String -> Log a
parseLogWithUUID parser = M.fromListWith best . mapMaybe parse . lines parseLogWithUUID parser = M.fromListWith best . mapMaybe parse . lines
where where
parse line parse line
| null ws = Nothing | null ws = Nothing
| otherwise = parser u (unwords info) >>= makepair | otherwise = parser u (unwords info) >>= makepair
where where
makepair v = Just (u, LogEntry ts v) makepair v = Just (u, LogEntry ts v)
ws = words line ws = words line
u = toUUID $ Prelude.head ws u = toUUID $ Prelude.head ws
t = Prelude.last ws t = Prelude.last ws
ts ts
| tskey `isPrefixOf` t = | tskey `isPrefixOf` t =
pdate $ drop 1 $ dropWhile (/= '=') t pdate $ drop 1 $ dropWhile (/= '=') t
| otherwise = Unknown | otherwise = Unknown
info info
| ts == Unknown = drop 1 ws | ts == Unknown = drop 1 ws
| otherwise = drop 1 $ beginning ws | otherwise = drop 1 $ beginning ws
pdate s = case parseTime defaultTimeLocale "%s%Qs" s of pdate s = case parseTime defaultTimeLocale "%s%Qs" s of
Nothing -> Unknown Nothing -> Unknown
Just d -> Date $ utcTimeToPOSIXSeconds d Just d -> Date $ utcTimeToPOSIXSeconds d
changeLog :: POSIXTime -> UUID -> a -> Log a -> Log a changeLog :: POSIXTime -> UUID -> a -> Log a -> Log a
changeLog t u v = M.insert u $ LogEntry (Date t) v changeLog t u v = M.insert u $ LogEntry (Date t) v
@ -106,9 +106,9 @@ prop_TimeStamp_sane = Unknown < Date 1
prop_addLog_sane :: Bool prop_addLog_sane :: Bool
prop_addLog_sane = newWins && newestWins prop_addLog_sane = newWins && newestWins
where where
newWins = addLog (UUID "foo") (LogEntry (Date 1) "new") l == l2 newWins = addLog (UUID "foo") (LogEntry (Date 1) "new") l == l2
newestWins = addLog (UUID "foo") (LogEntry (Date 1) "newest") l2 /= l2 newestWins = addLog (UUID "foo") (LogEntry (Date 1) "newest") l2 /= l2
l = M.fromList [(UUID "foo", LogEntry (Date 0) "old")] l = M.fromList [(UUID "foo", LogEntry (Date 0) "old")]
l2 = M.fromList [(UUID "foo", LogEntry (Date 1) "new")] l2 = M.fromList [(UUID "foo", LogEntry (Date 1) "new")]

View file

@ -35,13 +35,12 @@ readUnusedLog prefix = do
<$> liftIO (readFile f) <$> liftIO (readFile f)
, return M.empty , return M.empty
) )
where where
parse line = parse line = case (readish tag, file2key rest) of
case (readish tag, file2key rest) of (Just num, Just key) -> Just (num, key)
(Just num, Just key) -> Just (num, key) _ -> Nothing
_ -> Nothing where
where (tag, rest) = separate (== ' ') line
(tag, rest) = separate (== ' ') line
type UnusedMap = M.Map Int Key type UnusedMap = M.Map Int Key
@ -64,10 +63,10 @@ unusedSpec :: String -> [Int]
unusedSpec spec unusedSpec spec
| "-" `isInfixOf` spec = range $ separate (== '-') spec | "-" `isInfixOf` spec = range $ separate (== '-') spec
| otherwise = catMaybes [readish spec] | otherwise = catMaybes [readish spec]
where where
range (a, b) = case (readish a, readish b) of range (a, b) = case (readish a, readish b) of
(Just x, Just y) -> [x..y] (Just x, Just y) -> [x..y]
_ -> [] _ -> []
{- Start action for unused content. Finds the number in the maps, and {- Start action for unused content. Finds the number in the maps, and
- calls either of 3 actions, depending on the type of unused file. -} - calls either of 3 actions, depending on the type of unused file. -}
@ -81,11 +80,11 @@ startUnused message unused badunused tmpunused maps n = search
, (unusedBadMap maps, badunused) , (unusedBadMap maps, badunused)
, (unusedTmpMap maps, tmpunused) , (unusedTmpMap maps, tmpunused)
] ]
where where
search [] = stop search [] = stop
search ((m, a):rest) = search ((m, a):rest) =
case M.lookup n m of case M.lookup n m of
Nothing -> search rest Nothing -> search rest
Just key -> do Just key -> do
showStart message (show n) showStart message (show n)
next $ a key next $ a key

View file

@ -37,13 +37,13 @@ oldurlLogs key =
{- Gets all urls that a key might be available from. -} {- Gets all urls that a key might be available from. -}
getUrls :: Key -> Annex [URLString] getUrls :: Key -> Annex [URLString]
getUrls key = go $ urlLog key : oldurlLogs key getUrls key = go $ urlLog key : oldurlLogs key
where where
go [] = return [] go [] = return []
go (l:ls) = do go (l:ls) = do
us <- currentLog l us <- currentLog l
if null us if null us
then go ls then go ls
else return us else return us
{- Records a change in an url for a key. -} {- Records a change in an url for a key. -}
setUrl :: Key -> URLString -> LogStatus -> Annex () setUrl :: Key -> URLString -> LogStatus -> Annex ()

View file

@ -20,9 +20,9 @@ import qualified Utility.JSONStream as Stream
start :: String -> Maybe String -> IO () start :: String -> Maybe String -> IO ()
start command file = start command file =
putStr $ Stream.start $ ("command", command) : filepart file putStr $ Stream.start $ ("command", command) : filepart file
where where
filepart Nothing = [] filepart Nothing = []
filepart (Just f) = [("file", f)] filepart (Just f) = [("file", f)]
end :: Bool -> IO () end :: Bool -> IO ()
end b = putStr $ Stream.add [("success", b)] ++ Stream.end end b = putStr $ Stream.add [("success", b)] ++ Stream.end

View file

@ -143,9 +143,9 @@ retrieveEncrypted buprepo (cipher, enck) _ f = liftIO $ catchBoolIO $
withHandle StdoutHandle createProcessSuccess p $ \h -> do withHandle StdoutHandle createProcessSuccess p $ \h -> do
withDecryptedContent cipher (L.hGetContents h) $ L.writeFile f withDecryptedContent cipher (L.hGetContents h) $ L.writeFile f
return True return True
where where
params = bupParams "join" buprepo [Param $ bupRef enck] params = bupParams "join" buprepo [Param $ bupRef enck]
p = proc "bup" $ toCommand params p = proc "bup" $ toCommand params
remove :: Key -> Annex Bool remove :: Key -> Annex Bool
remove _ = do remove _ = do
@ -164,10 +164,11 @@ checkPresent r bupr k
return $ Right ok return $ Right ok
| otherwise = liftIO $ catchMsgIO $ | otherwise = liftIO $ catchMsgIO $
boolSystem "git" $ Git.Command.gitCommandLine params bupr boolSystem "git" $ Git.Command.gitCommandLine params bupr
where where
params = params =
[ Params "show-ref --quiet --verify" [ Params "show-ref --quiet --verify"
, Param $ "refs/heads/" ++ bupRef k] , Param $ "refs/heads/" ++ bupRef k
]
{- Store UUID in the annex.uuid setting of the bup repository. -} {- Store UUID in the annex.uuid setting of the bup repository. -}
storeBupUUID :: UUID -> BupRepo -> Annex () storeBupUUID :: UUID -> BupRepo -> Annex ()
@ -185,8 +186,8 @@ storeBupUUID u buprepo = do
when (olduuid == "") $ when (olduuid == "") $
Git.Command.run "config" Git.Command.run "config"
[Param "annex.uuid", Param v] r' [Param "annex.uuid", Param v] r'
where where
v = fromUUID u v = fromUUID u
onBupRemote :: Git.Repo -> (FilePath -> [CommandParam] -> IO a) -> FilePath -> [CommandParam] -> Annex a onBupRemote :: Git.Repo -> (FilePath -> [CommandParam] -> IO a) -> FilePath -> [CommandParam] -> Annex a
onBupRemote r a command params = do onBupRemote r a command params = do
@ -227,17 +228,17 @@ bup2GitRemote r
then Git.Construct.fromAbsPath r then Git.Construct.fromAbsPath r
else error "please specify an absolute path" else error "please specify an absolute path"
| otherwise = Git.Construct.fromUrl $ "ssh://" ++ host ++ slash dir | otherwise = Git.Construct.fromUrl $ "ssh://" ++ host ++ slash dir
where where
bits = split ":" r bits = split ":" r
host = Prelude.head bits host = Prelude.head bits
dir = join ":" $ drop 1 bits dir = join ":" $ drop 1 bits
-- "host:~user/dir" is not supported specially by bup; -- "host:~user/dir" is not supported specially by bup;
-- "host:dir" is relative to the home directory; -- "host:dir" is relative to the home directory;
-- "host:" goes in ~/.bup -- "host:" goes in ~/.bup
slash d slash d
| null d = "/~/.bup" | null d = "/~/.bup"
| "/" `isPrefixOf` d = d | "/" `isPrefixOf` d = d
| otherwise = "/~/" ++ d | otherwise = "/~/" ++ d
{- Converts a key into a git ref name, which bup-split -n will use to point {- Converts a key into a git ref name, which bup-split -n will use to point
- to it. -} - to it. -}
@ -245,8 +246,8 @@ bupRef :: Key -> String
bupRef k bupRef k
| Git.Ref.legal True shown = shown | Git.Ref.legal True shown = shown
| otherwise = "git-annex-" ++ showDigest (sha256 (fromString shown)) | otherwise = "git-annex-" ++ showDigest (sha256 (fromString shown))
where where
shown = key2file k shown = key2file k
bupLocal :: BupRepo -> Bool bupLocal :: BupRepo -> Bool
bupLocal = notElem ':' bupLocal = notElem ':'

View file

@ -57,7 +57,6 @@ gen r u c = do
readonly = False, readonly = False,
remotetype = remote remotetype = remote
} }
where
type ChunkSize = Maybe Int64 type ChunkSize = Maybe Int64
@ -101,25 +100,25 @@ chunkCount f = f ++ ".chunkcount"
withCheckedFiles :: (FilePath -> IO Bool) -> ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool withCheckedFiles :: (FilePath -> IO Bool) -> ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool
withCheckedFiles _ _ [] _ _ = return False withCheckedFiles _ _ [] _ _ = return False
withCheckedFiles check Nothing d k a = go $ locations d k withCheckedFiles check Nothing d k a = go $ locations d k
where where
go [] = return False go [] = return False
go (f:fs) = ifM (check f) ( a [f] , go fs ) go (f:fs) = ifM (check f) ( a [f] , go fs )
withCheckedFiles check (Just _) d k a = go $ locations d k withCheckedFiles check (Just _) d k a = go $ locations d k
where where
go [] = return False go [] = return False
go (f:fs) = do go (f:fs) = do
let chunkcount = chunkCount f let chunkcount = chunkCount f
use <- check chunkcount ifM (check chunkcount)
if use ( do
then do count <- readcount chunkcount
count <- readcount chunkcount let chunks = take count $ chunkStream f
let chunks = take count $ chunkStream f ifM (all id <$> mapM check chunks)
ifM (all id <$> mapM check chunks) ( a chunks , return False )
( a chunks , return False ) , go fs
else go fs )
readcount f = fromMaybe (error $ "cannot parse " ++ f) readcount f = fromMaybe (error $ "cannot parse " ++ f)
. (readish :: String -> Maybe Int) . (readish :: String -> Maybe Int)
<$> readFile f <$> readFile f
withStoredFiles :: ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool withStoredFiles :: ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool
withStoredFiles = withCheckedFiles doesFileExist withStoredFiles = withCheckedFiles doesFileExist
@ -170,39 +169,39 @@ storeSplit' _ _ _ [] c = return $ reverse c
storeSplit' meterupdate chunksize (d:dests) bs c = do storeSplit' meterupdate chunksize (d:dests) bs c = do
bs' <- E.bracket (openFile d WriteMode) hClose (feed chunksize bs) bs' <- E.bracket (openFile d WriteMode) hClose (feed chunksize bs)
storeSplit' meterupdate chunksize dests bs' (d:c) storeSplit' meterupdate chunksize dests bs' (d:c)
where where
feed _ [] _ = return [] feed _ [] _ = return []
feed sz (l:ls) h = do feed sz (l:ls) h = do
let s = fromIntegral $ S.length l let s = fromIntegral $ S.length l
if s <= sz if s <= sz
then do then do
S.hPut h l S.hPut h l
meterupdate $ toInteger s meterupdate $ toInteger s
feed (sz - s) ls h feed (sz - s) ls h
else return (l:ls) else return (l:ls)
{- Write a L.ByteString to a file, updating a progress meter {- Write a L.ByteString to a file, updating a progress meter
- after each chunk of the L.ByteString, typically every 64 kb or so. -} - after each chunk of the L.ByteString, typically every 64 kb or so. -}
meteredWriteFile :: MeterUpdate -> FilePath -> L.ByteString -> IO () meteredWriteFile :: MeterUpdate -> FilePath -> L.ByteString -> IO ()
meteredWriteFile meterupdate dest b = meteredWriteFile meterupdate dest b =
meteredWriteFile' meterupdate dest (L.toChunks b) feeder meteredWriteFile' meterupdate dest (L.toChunks b) feeder
where where
feeder chunks = return ([], chunks) feeder chunks = return ([], chunks)
{- Writes a series of S.ByteString chunks to a file, updating a progress {- Writes a series of S.ByteString chunks to a file, updating a progress
- meter after each chunk. The feeder is called to get more chunks. -} - meter after each chunk. The feeder is called to get more chunks. -}
meteredWriteFile' :: MeterUpdate -> FilePath -> s -> (s -> IO (s, [S.ByteString])) -> IO () meteredWriteFile' :: MeterUpdate -> FilePath -> s -> (s -> IO (s, [S.ByteString])) -> IO ()
meteredWriteFile' meterupdate dest startstate feeder = meteredWriteFile' meterupdate dest startstate feeder =
E.bracket (openFile dest WriteMode) hClose (feed startstate []) E.bracket (openFile dest WriteMode) hClose (feed startstate [])
where where
feed state [] h = do feed state [] h = do
(state', cs) <- feeder state (state', cs) <- feeder state
unless (null cs) $ unless (null cs) $
feed state' cs h feed state' cs h
feed state (c:cs) h = do feed state (c:cs) h = do
S.hPut h c S.hPut h c
meterupdate $ toInteger $ S.length c meterupdate $ toInteger $ S.length c
feed state cs h feed state cs h
{- Generates a list of destinations to write to in order to store a key. {- Generates a list of destinations to write to in order to store a key.
- When chunksize is specified, this list will be a list of chunks. - When chunksize is specified, this list will be a list of chunks.
@ -213,36 +212,36 @@ meteredWriteFile' meterupdate dest startstate feeder =
-} -}
storeHelper :: FilePath -> ChunkSize -> Key -> ([FilePath] -> IO [FilePath]) -> Annex Bool storeHelper :: FilePath -> ChunkSize -> Key -> ([FilePath] -> IO [FilePath]) -> Annex Bool
storeHelper d chunksize key a = prep <&&> check <&&> go storeHelper d chunksize key a = prep <&&> check <&&> go
where where
desttemplate = Prelude.head $ locations d key desttemplate = Prelude.head $ locations d key
dir = parentDir desttemplate dir = parentDir desttemplate
tmpdests = case chunksize of tmpdests = case chunksize of
Nothing -> [desttemplate ++ tmpprefix] Nothing -> [desttemplate ++ tmpprefix]
Just _ -> map (++ tmpprefix) (chunkStream desttemplate) Just _ -> map (++ tmpprefix) (chunkStream desttemplate)
tmpprefix = ".tmp" tmpprefix = ".tmp"
detmpprefix f = take (length f - tmpprefixlen) f detmpprefix f = take (length f - tmpprefixlen) f
tmpprefixlen = length tmpprefix tmpprefixlen = length tmpprefix
prep = liftIO $ catchBoolIO $ do prep = liftIO $ catchBoolIO $ do
createDirectoryIfMissing True dir createDirectoryIfMissing True dir
allowWrite dir allowWrite dir
return True return True
{- The size is not exactly known when encrypting the key; {- The size is not exactly known when encrypting the key;
- this assumes that at least the size of the key is - this assumes that at least the size of the key is
- needed as free space. -} - needed as free space. -}
check = checkDiskSpace (Just dir) key 0 check = checkDiskSpace (Just dir) key 0
go = liftIO $ catchBoolIO $ do go = liftIO $ catchBoolIO $ do
stored <- a tmpdests stored <- a tmpdests
forM_ stored $ \f -> do forM_ stored $ \f -> do
let dest = detmpprefix f let dest = detmpprefix f
renameFile f dest renameFile f dest
preventWrite dest preventWrite dest
when (chunksize /= Nothing) $ do when (chunksize /= Nothing) $ do
let chunkcount = chunkCount desttemplate let chunkcount = chunkCount desttemplate
_ <- tryIO $ allowWrite chunkcount _ <- tryIO $ allowWrite chunkcount
writeFile chunkcount (show $ length stored) writeFile chunkcount (show $ length stored)
preventWrite chunkcount preventWrite chunkcount
preventWrite dir preventWrite dir
return (not $ null stored) return (not $ null stored)
retrieve :: FilePath -> ChunkSize -> Key -> AssociatedFile -> FilePath -> Annex Bool retrieve :: FilePath -> ChunkSize -> Key -> AssociatedFile -> FilePath -> Annex Bool
retrieve d chunksize k _ f = metered Nothing k $ \meterupdate -> retrieve d chunksize k _ f = metered Nothing k $ \meterupdate ->
@ -250,11 +249,11 @@ retrieve d chunksize k _ f = metered Nothing k $ \meterupdate ->
catchBoolIO $ do catchBoolIO $ do
meteredWriteFile' meterupdate f files feeder meteredWriteFile' meterupdate f files feeder
return True return True
where where
feeder [] = return ([], []) feeder [] = return ([], [])
feeder (x:xs) = do feeder (x:xs) = do
chunks <- L.toChunks <$> L.readFile x chunks <- L.toChunks <$> L.readFile x
return (xs, chunks) return (xs, chunks)
retrieveEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> FilePath -> Annex Bool retrieveEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> FilePath -> Annex Bool
retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupdate -> retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupdate ->
@ -267,20 +266,20 @@ retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupd
retrieveCheap :: FilePath -> ChunkSize -> Key -> FilePath -> Annex Bool retrieveCheap :: FilePath -> ChunkSize -> Key -> FilePath -> Annex Bool
retrieveCheap _ (Just _) _ _ = return False -- no cheap retrieval for chunks retrieveCheap _ (Just _) _ _ = return False -- no cheap retrieval for chunks
retrieveCheap d _ k f = liftIO $ withStoredFiles Nothing d k go retrieveCheap d _ k f = liftIO $ withStoredFiles Nothing d k go
where where
go [file] = catchBoolIO $ createSymbolicLink file f >> return True go [file] = catchBoolIO $ createSymbolicLink file f >> return True
go _files = return False go _files = return False
remove :: FilePath -> ChunkSize -> Key -> Annex Bool remove :: FilePath -> ChunkSize -> Key -> Annex Bool
remove d chunksize k = liftIO $ withStoredFiles chunksize d k go remove d chunksize k = liftIO $ withStoredFiles chunksize d k go
where where
go = all id <$$> mapM removefile go = all id <$$> mapM removefile
removefile file = catchBoolIO $ do removefile file = catchBoolIO $ do
let dir = parentDir file let dir = parentDir file
allowWrite dir allowWrite dir
removeFile file removeFile file
_ <- tryIO $ removeDirectory dir _ <- tryIO $ removeDirectory dir
return True return True
checkPresent :: FilePath -> ChunkSize -> Key -> Annex (Either String Bool) checkPresent :: FilePath -> ChunkSize -> Key -> Annex (Either String Bool)
checkPresent d chunksize k = liftIO $ catchMsgIO $ withStoredFiles chunksize d k $ checkPresent d chunksize k = liftIO $ catchMsgIO $ withStoredFiles chunksize d k $

View file

@ -32,12 +32,12 @@ encryptionSetup c = case (M.lookup "encryption" c, extractCipher c) of
(Just "shared", Nothing) -> use "encryption setup" $ genSharedCipher (Just "shared", Nothing) -> use "encryption setup" $ genSharedCipher
(Just keyid, Nothing) -> use "encryption setup" $ genEncryptedCipher keyid (Just keyid, Nothing) -> use "encryption setup" $ genEncryptedCipher keyid
(Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v (Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v
where where
cannotchange = error "Cannot change encryption type of existing remote." cannotchange = error "Cannot change encryption type of existing remote."
use m a = do use m a = do
cipher <- liftIO a cipher <- liftIO a
showNote $ m ++ " " ++ describeCipher cipher showNote $ m ++ " " ++ describeCipher cipher
return $ M.delete "encryption" $ storeCipher c cipher return $ M.delete "encryption" $ storeCipher c cipher
{- Modifies a Remote to support encryption. {- Modifies a Remote to support encryption.
- -
@ -58,35 +58,35 @@ encryptableRemote c storeKeyEncrypted retrieveKeyFileEncrypted r =
hasKey = withkey $ hasKey r, hasKey = withkey $ hasKey r,
cost = cost r + encryptedRemoteCostAdj cost = cost r + encryptedRemoteCostAdj
} }
where where
store k f p = cip k >>= maybe store k f p = cip k >>= maybe
(storeKey r k f p) (storeKey r k f p)
(\enck -> storeKeyEncrypted enck k p) (\enck -> storeKeyEncrypted enck k p)
retrieve k f d = cip k >>= maybe retrieve k f d = cip k >>= maybe
(retrieveKeyFile r k f d) (retrieveKeyFile r k f d)
(\enck -> retrieveKeyFileEncrypted enck k d) (\enck -> retrieveKeyFileEncrypted enck k d)
retrieveCheap k d = cip k >>= maybe retrieveCheap k d = cip k >>= maybe
(retrieveKeyFileCheap r k d) (retrieveKeyFileCheap r k d)
(\_ -> return False) (\_ -> return False)
withkey a k = cip k >>= maybe (a k) (a . snd) withkey a k = cip k >>= maybe (a k) (a . snd)
cip = cipherKey c cip = cipherKey c
{- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex {- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex
- state. -} - state. -}
remoteCipher :: RemoteConfig -> Annex (Maybe Cipher) remoteCipher :: RemoteConfig -> Annex (Maybe Cipher)
remoteCipher c = go $ extractCipher c remoteCipher c = go $ extractCipher c
where where
go Nothing = return Nothing go Nothing = return Nothing
go (Just encipher) = do go (Just encipher) = do
cache <- Annex.getState Annex.ciphers cache <- Annex.getState Annex.ciphers
case M.lookup encipher cache of case M.lookup encipher cache of
Just cipher -> return $ Just cipher Just cipher -> return $ Just cipher
Nothing -> decrypt encipher cache Nothing -> decrypt encipher cache
decrypt encipher cache = do decrypt encipher cache = do
showNote "gpg" showNote "gpg"
cipher <- liftIO $ decryptCipher encipher cipher <- liftIO $ decryptCipher encipher
Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache }) Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })
return $ Just cipher return $ Just cipher
{- Checks if there is a trusted (non-shared) cipher. -} {- Checks if there is a trusted (non-shared) cipher. -}
isTrustedCipher :: RemoteConfig -> Bool isTrustedCipher :: RemoteConfig -> Bool
@ -97,16 +97,16 @@ isTrustedCipher c =
cipherKey :: Maybe RemoteConfig -> Key -> Annex (Maybe (Cipher, Key)) cipherKey :: Maybe RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
cipherKey Nothing _ = return Nothing cipherKey Nothing _ = return Nothing
cipherKey (Just c) k = maybe Nothing encrypt <$> remoteCipher c cipherKey (Just c) k = maybe Nothing encrypt <$> remoteCipher c
where where
encrypt ciphertext = Just (ciphertext, encryptKey ciphertext k) encrypt ciphertext = Just (ciphertext, encryptKey ciphertext k)
{- Stores an StorableCipher in a remote's configuration. -} {- Stores an StorableCipher in a remote's configuration. -}
storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig
storeCipher c (SharedCipher t) = M.insert "cipher" (toB64 t) c storeCipher c (SharedCipher t) = M.insert "cipher" (toB64 t) c
storeCipher c (EncryptedCipher t ks) = storeCipher c (EncryptedCipher t ks) =
M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c
where where
showkeys (KeyIds l) = join "," l showkeys (KeyIds l) = join "," l
{- Extracts an StorableCipher from a remote's configuration. -} {- Extracts an StorableCipher from a remote's configuration. -}
extractCipher :: RemoteConfig -> Maybe StorableCipher extractCipher :: RemoteConfig -> Maybe StorableCipher
@ -115,5 +115,5 @@ extractCipher c =
(Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks) (Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks)
(Just t, Nothing) -> Just $ SharedCipher (fromB64 t) (Just t, Nothing) -> Just $ SharedCipher (fromB64 t)
_ -> Nothing _ -> Nothing
where where
readkeys = KeyIds . split "," readkeys = KeyIds . split ","

View file

@ -25,16 +25,16 @@ addHooks r = addHooks' r <$> lookupHook r "start" <*> lookupHook r "stop"
addHooks' :: Remote -> Maybe String -> Maybe String -> Remote addHooks' :: Remote -> Maybe String -> Maybe String -> Remote
addHooks' r Nothing Nothing = r addHooks' r Nothing Nothing = r
addHooks' r starthook stophook = r' addHooks' r starthook stophook = r'
where where
r' = r r' = r
{ storeKey = \k f p -> wrapper $ storeKey r k f p { storeKey = \k f p -> wrapper $ storeKey r k f p
, retrieveKeyFile = \k f d -> wrapper $ retrieveKeyFile r k f d , retrieveKeyFile = \k f d -> wrapper $ retrieveKeyFile r k f d
, retrieveKeyFileCheap = \k f -> wrapper $ retrieveKeyFileCheap r k f , retrieveKeyFileCheap = \k f -> wrapper $ retrieveKeyFileCheap r k f
, removeKey = \k -> wrapper $ removeKey r k , removeKey = \k -> wrapper $ removeKey r k
, hasKey = \k -> wrapper $ hasKey r k , hasKey = \k -> wrapper $ hasKey r k
} }
where where
wrapper = runHooks r' starthook stophook wrapper = runHooks r' starthook stophook
runHooks :: Remote -> Maybe String -> Maybe String -> Annex a -> Annex a runHooks :: Remote -> Maybe String -> Maybe String -> Annex a -> Annex a
runHooks r starthook stophook a = do runHooks r starthook stophook a = do
@ -44,50 +44,49 @@ runHooks r starthook stophook a = do
liftIO $ createDirectoryIfMissing True dir liftIO $ createDirectoryIfMissing True dir
firstrun lck firstrun lck
a a
where where
remoteid = show (uuid r) remoteid = show (uuid r)
run Nothing = noop run Nothing = noop
run (Just command) = void $ liftIO $ run (Just command) = void $ liftIO $
boolSystem "sh" [Param "-c", Param command] boolSystem "sh" [Param "-c", Param command]
firstrun lck = do firstrun lck = do
-- Take a shared lock; This indicates that git-annex -- Take a shared lock; This indicates that git-annex
-- is using the remote, and prevents other instances -- is using the remote, and prevents other instances
-- of it from running the stophook. If another -- of it from running the stophook. If another
-- instance is shutting down right now, this -- instance is shutting down right now, this
-- will block waiting for its exclusive lock to clear. -- will block waiting for its exclusive lock to clear.
lockFile lck lockFile lck
-- The starthook is run even if some other git-annex -- The starthook is run even if some other git-annex
-- is already running, and ran it before. -- is already running, and ran it before.
-- It would be difficult to use locking to ensure -- It would be difficult to use locking to ensure
-- it's only run once, and it's also possible for -- it's only run once, and it's also possible for
-- git-annex to be interrupted before it can run the -- git-annex to be interrupted before it can run the
-- stophook, in which case the starthook -- stophook, in which case the starthook
-- would be run again by the next git-annex. -- would be run again by the next git-annex.
-- So, requiring idempotency is the right approach. -- So, requiring idempotency is the right approach.
run starthook run starthook
Annex.addCleanup (remoteid ++ "-stop-command") $ Annex.addCleanup (remoteid ++ "-stop-command") $ runstop lck
runstop lck runstop lck = do
runstop lck = do -- Drop any shared lock we have, and take an
-- Drop any shared lock we have, and take an -- exclusive lock, without blocking. If the lock
-- exclusive lock, without blocking. If the lock -- succeeds, we're the only process using this remote,
-- succeeds, we're the only process using this remote, -- so can stop it.
-- so can stop it. unlockFile lck
unlockFile lck mode <- annexFileMode
mode <- annexFileMode fd <- liftIO $ noUmask mode $
fd <- liftIO $ noUmask mode $ openFd lck ReadWrite (Just mode) defaultFileFlags
openFd lck ReadWrite (Just mode) defaultFileFlags v <- liftIO $ tryIO $
v <- liftIO $ tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
setLock fd (WriteLock, AbsoluteSeek, 0, 0) case v of
case v of Left _ -> noop
Left _ -> noop Right _ -> run stophook
Right _ -> run stophook liftIO $ closeFd fd
liftIO $ closeFd fd
lookupHook :: Remote -> String -> Annex (Maybe String) lookupHook :: Remote -> String -> Annex (Maybe String)
lookupHook r n = go =<< getRemoteConfig (repo r) hookname "" lookupHook r n = go =<< getRemoteConfig (repo r) hookname ""
where where
go "" = return Nothing go "" = return Nothing
go command = return $ Just command go command = return $ Just command
hookname = n ++ "-command" hookname = n ++ "-command"

View file

@ -23,18 +23,18 @@ findSpecialRemotes :: String -> Annex [Git.Repo]
findSpecialRemotes s = do findSpecialRemotes s = do
m <- fromRepo Git.config m <- fromRepo Git.config
liftIO $ mapM construct $ remotepairs m liftIO $ mapM construct $ remotepairs m
where where
remotepairs = M.toList . M.filterWithKey match remotepairs = M.toList . M.filterWithKey match
construct (k,_) = Git.Construct.remoteNamedFromKey k Git.Construct.fromUnknown construct (k,_) = Git.Construct.remoteNamedFromKey k Git.Construct.fromUnknown
match k _ = startswith "remote." k && endswith (".annex-"++s) k match k _ = startswith "remote." k && endswith (".annex-"++s) k
{- Sets up configuration for a special remote in .git/config. -} {- Sets up configuration for a special remote in .git/config. -}
gitConfigSpecialRemote :: UUID -> RemoteConfig -> String -> String -> Annex () gitConfigSpecialRemote :: UUID -> RemoteConfig -> String -> String -> Annex ()
gitConfigSpecialRemote u c k v = do gitConfigSpecialRemote u c k v = do
set ("annex-"++k) v set ("annex-"++k) v
set ("annex-uuid") (fromUUID u) set ("annex-uuid") (fromUUID u)
where where
set a b = inRepo $ Git.Command.run "config" set a b = inRepo $ Git.Command.run "config"
[Param (configsetting a), Param b] [Param (configsetting a), Param b]
remotename = fromJust (M.lookup "name" c) remotename = fromJust (M.lookup "name" c)
configsetting s = "remote." ++ remotename ++ "." ++ s configsetting s = "remote." ++ remotename ++ "." ++ s

View file

@ -1,6 +1,6 @@
{- git-annex remote access with ssh {- git-annex remote access with ssh
- -
- Copyright 2011.2012 Joey Hess <joey@kitenet.net> - Copyright 2011,2012 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -34,22 +34,22 @@ git_annex_shell r command params fields
sshparams <- sshToRepo r [Param $ sshcmd uuid ] sshparams <- sshToRepo r [Param $ sshcmd uuid ]
return $ Just ("ssh", sshparams) return $ Just ("ssh", sshparams)
| otherwise = return Nothing | otherwise = return Nothing
where where
dir = Git.repoPath r dir = Git.repoPath r
shellcmd = "git-annex-shell" shellcmd = "git-annex-shell"
shellopts = Param command : File dir : params shellopts = Param command : File dir : params
sshcmd uuid = unwords $ sshcmd uuid = unwords $
shellcmd : map shellEscape (toCommand shellopts) ++ shellcmd : map shellEscape (toCommand shellopts) ++
uuidcheck uuid ++ uuidcheck uuid ++
map shellEscape (toCommand fieldopts) map shellEscape (toCommand fieldopts)
uuidcheck NoUUID = [] uuidcheck NoUUID = []
uuidcheck (UUID u) = ["--uuid", u] uuidcheck (UUID u) = ["--uuid", u]
fieldopts fieldopts
| null fields = [] | null fields = []
| otherwise = fieldsep : map fieldopt fields ++ [fieldsep] | otherwise = fieldsep : map fieldopt fields ++ [fieldsep]
fieldsep = Param "--" fieldsep = Param "--"
fieldopt (field, value) = Param $ fieldopt (field, value) = Param $
fieldName field ++ "=" ++ value fieldName field ++ "=" ++ value
{- Uses a supplied function (such as boolSystem) to run a git-annex-shell {- Uses a supplied function (such as boolSystem) to run a git-annex-shell
- command on a remote. - command on a remote.

View file

@ -64,19 +64,18 @@ hookSetup u c = do
hookEnv :: Key -> Maybe FilePath -> IO (Maybe [(String, String)]) hookEnv :: Key -> Maybe FilePath -> IO (Maybe [(String, String)])
hookEnv k f = Just <$> mergeenv (fileenv f ++ keyenv) hookEnv k f = Just <$> mergeenv (fileenv f ++ keyenv)
where where
mergeenv l = M.toList . mergeenv l = M.toList . M.union (M.fromList l)
M.union (M.fromList l) <$> M.fromList <$> getEnvironment
<$> M.fromList <$> getEnvironment env s v = ("ANNEX_" ++ s, v)
env s v = ("ANNEX_" ++ s, v) keyenv = catMaybes
keyenv = catMaybes [ Just $ env "KEY" (key2file k)
[ Just $ env "KEY" (key2file k) , env "HASH_1" <$> headMaybe hashbits
, env "HASH_1" <$> headMaybe hashbits , env "HASH_2" <$> headMaybe (drop 1 hashbits)
, env "HASH_2" <$> headMaybe (drop 1 hashbits) ]
] fileenv Nothing = []
fileenv Nothing = [] fileenv (Just file) = [env "FILE" file]
fileenv (Just file) = [env "FILE" file] hashbits = map takeDirectory $ splitPath $ hashDirMixed k
hashbits = map takeDirectory $ splitPath $ hashDirMixed k
lookupHook :: String -> String -> Annex (Maybe String) lookupHook :: String -> String -> Annex (Maybe String)
lookupHook hooktype hook =do lookupHook hooktype hook =do
@ -86,22 +85,20 @@ lookupHook hooktype hook =do
warning $ "missing configuration for " ++ hookname warning $ "missing configuration for " ++ hookname
return Nothing return Nothing
else return $ Just command else return $ Just command
where where
hookname = hooktype ++ "-" ++ hook ++ "-hook" hookname = hooktype ++ "-" ++ hook ++ "-hook"
runHook :: String -> String -> Key -> Maybe FilePath -> Annex Bool -> Annex Bool runHook :: String -> String -> Key -> Maybe FilePath -> Annex Bool -> Annex Bool
runHook hooktype hook k f a = maybe (return False) run =<< lookupHook hooktype hook runHook hooktype hook k f a = maybe (return False) run =<< lookupHook hooktype hook
where where
run command = do run command = do
showOutput -- make way for hook output showOutput -- make way for hook output
ifM (liftIO $ ifM (liftIO $ boolSystemEnv "sh" [Param "-c", Param command] =<< hookEnv k f)
boolSystemEnv "sh" [Param "-c", Param command] ( a
=<< hookEnv k f) , do
( a warning $ hook ++ " hook exited nonzero!"
, do return False
warning $ hook ++ " hook exited nonzero!" )
return False
)
store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store h k _f _p = do store h k _f _p = do
@ -134,9 +131,9 @@ checkPresent r h k = do
showAction $ "checking " ++ Git.repoDescribe r showAction $ "checking " ++ Git.repoDescribe r
v <- lookupHook h "checkpresent" v <- lookupHook h "checkpresent"
liftIO $ catchMsgIO $ check v liftIO $ catchMsgIO $ check v
where where
findkey s = key2file k `elem` lines s findkey s = key2file k `elem` lines s
check Nothing = error "checkpresent hook misconfigured" check Nothing = error "checkpresent hook misconfigured"
check (Just hook) = do check (Just hook) = do
env <- hookEnv k Nothing env <- hookEnv k Nothing
findkey <$> readProcessEnv "sh" ["-c", hook] env findkey <$> readProcessEnv "sh" ["-c", hook] env

View file

@ -56,8 +56,8 @@ remoteList = do
Annex.changeState $ \s -> s { Annex.remotes = rs' } Annex.changeState $ \s -> s { Annex.remotes = rs' }
return rs' return rs'
else return rs else return rs
where where
process m t = enumerate t >>= mapM (remoteGen m t) process m t = enumerate t >>= mapM (remoteGen m t)
{- Forces the remoteList to be re-generated, re-reading the git config. -} {- Forces the remoteList to be re-generated, re-reading the git config. -}
remoteListRefresh :: Annex [Remote] remoteListRefresh :: Annex [Remote]
@ -81,11 +81,11 @@ updateRemote remote = do
m <- readRemoteLog m <- readRemoteLog
remote' <- updaterepo $ repo remote remote' <- updaterepo $ repo remote
remoteGen m (remotetype remote) remote' remoteGen m (remotetype remote) remote'
where where
updaterepo r updaterepo r
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = | Git.repoIsLocal r || Git.repoIsLocalUnknown r =
Remote.Git.configRead r Remote.Git.configRead r
| otherwise = return r | otherwise = return r
{- All remotes that are not ignored. -} {- All remotes that are not ignored. -}
enabledRemoteList :: Annex [Remote] enabledRemoteList :: Annex [Remote]

View file

@ -72,14 +72,14 @@ genRsyncOpts r c = do
<$> getRemoteConfig r "rsync-options" "" <$> getRemoteConfig r "rsync-options" ""
let escape = maybe True (\m -> M.lookup "shellescape" m /= Just "no") c let escape = maybe True (\m -> M.lookup "shellescape" m /= Just "no") c
return $ RsyncOpts url opts escape return $ RsyncOpts url opts escape
where where
safe o safe o
-- Don't allow user to pass --delete to rsync; -- Don't allow user to pass --delete to rsync;
-- that could cause it to delete other keys -- that could cause it to delete other keys
-- in the same hash bucket as a key it sends. -- in the same hash bucket as a key it sends.
| o == "--delete" = False | o == "--delete" = False
| o == "--delete-excluded" = False | o == "--delete-excluded" = False
| otherwise = True | otherwise = True
rsyncSetup :: UUID -> RemoteConfig -> Annex RemoteConfig rsyncSetup :: UUID -> RemoteConfig -> Annex RemoteConfig
rsyncSetup u c = do rsyncSetup u c = do
@ -100,9 +100,9 @@ rsyncEscape o s
rsyncUrls :: RsyncOpts -> Key -> [String] rsyncUrls :: RsyncOpts -> Key -> [String]
rsyncUrls o k = map use annexHashes rsyncUrls o k = map use annexHashes
where where
use h = rsyncUrl o </> h k </> rsyncEscape o (f </> f) use h = rsyncUrl o </> h k </> rsyncEscape o (f </> f)
f = keyFile k f = keyFile k
store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store o k _f p = rsyncSend o p k <=< inRepo $ gitAnnexLocation k store o k _f p = rsyncSend o p k <=< inRepo $ gitAnnexLocation k
@ -146,18 +146,18 @@ remove o k = withRsyncScratchDir $ \tmp -> liftIO $ do
, Param $ addTrailingPathSeparator dummy , Param $ addTrailingPathSeparator dummy
, Param $ rsyncUrl o , Param $ rsyncUrl o
] ]
where where
{- Specify include rules to match the directories where the {- Specify include rules to match the directories where the
- content could be. Note that the parent directories have - content could be. Note that the parent directories have
- to also be explicitly included, due to how rsync - to also be explicitly included, due to how rsync
- traverses directories. -} - traverses directories. -}
includes = concatMap use annexHashes includes = concatMap use annexHashes
use h = let dir = h k in use h = let dir = h k in
[ parentDir dir [ parentDir dir
, dir , dir
-- match content directory and anything in it -- match content directory and anything in it
, dir </> keyFile k </> "***" , dir </> keyFile k </> "***"
] ]
checkPresent :: Git.Repo -> RsyncOpts -> Key -> Annex (Either String Bool) checkPresent :: Git.Repo -> RsyncOpts -> Key -> Annex (Either String Bool)
checkPresent r o k = do checkPresent r o k = do
@ -165,13 +165,13 @@ checkPresent r o k = do
-- note: Does not currently differentiate between rsync failing -- note: Does not currently differentiate between rsync failing
-- to connect, and the file not being present. -- to connect, and the file not being present.
Right <$> check Right <$> check
where where
check = untilTrue (rsyncUrls o k) $ \u -> check = untilTrue (rsyncUrls o k) $ \u ->
liftIO $ catchBoolIO $ do liftIO $ catchBoolIO $ do
withQuietOutput createProcessSuccess $ withQuietOutput createProcessSuccess $
proc "rsync" $ toCommand $ proc "rsync" $ toCommand $
rsyncOptions o ++ [Param u] rsyncOptions o ++ [Param u]
return True return True
{- Rsync params to enable resumes of sending files safely, {- Rsync params to enable resumes of sending files safely,
- ensure that files are only moved into place once complete - ensure that files are only moved into place once complete
@ -190,9 +190,9 @@ withRsyncScratchDir a = do
nuke tmp nuke tmp
liftIO $ createDirectoryIfMissing True tmp liftIO $ createDirectoryIfMissing True tmp
nuke tmp `after` a tmp nuke tmp `after` a tmp
where where
nuke d = liftIO $ whenM (doesDirectoryExist d) $ nuke d = liftIO $ whenM (doesDirectoryExist d) $
removeDirectoryRecursive d removeDirectoryRecursive d
rsyncRemote :: RsyncOpts -> (Maybe MeterUpdate) -> [CommandParam] -> Annex Bool rsyncRemote :: RsyncOpts -> (Maybe MeterUpdate) -> [CommandParam] -> Annex Bool
rsyncRemote o callback params = do rsyncRemote o callback params = do
@ -203,9 +203,9 @@ rsyncRemote o callback params = do
showLongNote "rsync failed -- run git annex again to resume file transfer" showLongNote "rsync failed -- run git annex again to resume file transfer"
return False return False
) )
where where
defaultParams = [Params "--progress"] defaultParams = [Params "--progress"]
ps = rsyncOptions o ++ defaultParams ++ params ps = rsyncOptions o ++ defaultParams ++ params
{- To send a single key is slightly tricky; need to build up a temporary {- To send a single key is slightly tricky; need to build up a temporary
directory structure to pass to rsync so it can create the hash directory structure to pass to rsync so it can create the hash

View file

@ -48,74 +48,71 @@ gen' r u c cst =
(storeEncrypted this) (storeEncrypted this)
(retrieveEncrypted this) (retrieveEncrypted this)
this this
where where
this = Remote { this = Remote {
uuid = u, uuid = u,
cost = cst, cost = cst,
name = Git.repoDescribe r, name = Git.repoDescribe r,
storeKey = store this, storeKey = store this,
retrieveKeyFile = retrieve this, retrieveKeyFile = retrieve this,
retrieveKeyFileCheap = retrieveCheap this, retrieveKeyFileCheap = retrieveCheap this,
removeKey = remove this, removeKey = remove this,
hasKey = checkPresent this, hasKey = checkPresent this,
hasKeyCheap = False, hasKeyCheap = False,
whereisKey = Nothing, whereisKey = Nothing,
config = c, config = c,
repo = r, repo = r,
localpath = Nothing, localpath = Nothing,
readonly = False, readonly = False,
remotetype = remote remotetype = remote
} }
s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig
s3Setup u c = handlehost $ M.lookup "host" c s3Setup u c = handlehost $ M.lookup "host" c
where where
remotename = fromJust (M.lookup "name" c) remotename = fromJust (M.lookup "name" c)
defbucket = remotename ++ "-" ++ fromUUID u defbucket = remotename ++ "-" ++ fromUUID u
defaults = M.fromList defaults = M.fromList
[ ("datacenter", "US") [ ("datacenter", "US")
, ("storageclass", "STANDARD") , ("storageclass", "STANDARD")
, ("host", defaultAmazonS3Host) , ("host", defaultAmazonS3Host)
, ("port", show defaultAmazonS3Port) , ("port", show defaultAmazonS3Port)
, ("bucket", defbucket) , ("bucket", defbucket)
] ]
handlehost Nothing = defaulthost handlehost Nothing = defaulthost
handlehost (Just h) handlehost (Just h)
| ".archive.org" `isSuffixOf` map toLower h = archiveorg | ".archive.org" `isSuffixOf` map toLower h = archiveorg
| otherwise = defaulthost | otherwise = defaulthost
use fullconfig = do use fullconfig = do
gitConfigSpecialRemote u fullconfig "s3" "true" gitConfigSpecialRemote u fullconfig "s3" "true"
s3SetCreds fullconfig u s3SetCreds fullconfig u
defaulthost = do defaulthost = do
c' <- encryptionSetup c c' <- encryptionSetup c
let fullconfig = c' `M.union` defaults let fullconfig = c' `M.union` defaults
genBucket fullconfig u genBucket fullconfig u
use fullconfig use fullconfig
archiveorg = do archiveorg = do
showNote "Internet Archive mode" showNote "Internet Archive mode"
maybe (error "specify bucket=") (const noop) $ maybe (error "specify bucket=") (const noop) $
M.lookup "bucket" archiveconfig M.lookup "bucket" archiveconfig
use archiveconfig use archiveconfig
where where
archiveconfig = archiveconfig =
-- hS3 does not pass through -- hS3 does not pass through x-archive-* headers
-- x-archive-* headers M.mapKeys (replace "x-archive-" "x-amz-") $
M.mapKeys (replace "x-archive-" "x-amz-") $ -- encryption does not make sense here
-- encryption does not make sense here M.insert "encryption" "none" $
M.insert "encryption" "none" $ M.union c $
M.union c $ -- special constraints on key names
-- special constraints on key names M.insert "mungekeys" "ia" $
M.insert "mungekeys" "ia" $ -- bucket created only when files are uploaded
-- bucket created only when files M.insert "x-amz-auto-make-bucket" "1" $
-- are uploaded -- no default bucket name; should be human-readable
M.insert "x-amz-auto-make-bucket" "1" $ M.delete "bucket" defaults
-- no default bucket name; should
-- be human-readable
M.delete "bucket" defaults
store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store r k _f _p = s3Action r False $ \(conn, bucket) -> do store r k _f _p = s3Action r False $ \(conn, bucket) -> do
@ -143,15 +140,15 @@ storeHelper (conn, bucket) r k file = do
S3Object bucket (bucketFile r k) "" S3Object bucket (bucketFile r k) ""
(("Content-Length", show size) : xheaders) content (("Content-Length", show size) : xheaders) content
sendObject conn object sendObject conn object
where where
storageclass = storageclass =
case fromJust $ M.lookup "storageclass" $ fromJust $ config r of case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY "REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
_ -> STANDARD _ -> STANDARD
getsize = fileSize <$> (liftIO $ getFileStatus file) getsize = fileSize <$> (liftIO $ getFileStatus file)
xheaders = filter isxheader $ M.assocs $ fromJust $ config r xheaders = filter isxheader $ M.assocs $ fromJust $ config r
isxheader (h, _) = "x-amz-" `isPrefixOf` h isxheader (h, _) = "x-amz-" `isPrefixOf` h
retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool
retrieve r k _f d = s3Action r False $ \(conn, bucket) -> do retrieve r k _f d = s3Action r False $ \(conn, bucket) -> do
@ -188,8 +185,8 @@ checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
Right _ -> return $ Right True Right _ -> return $ Right True
Left (AWSError _ _) -> return $ Right False Left (AWSError _ _) -> return $ Right False
Left e -> return $ Left (s3Error e) Left e -> return $ Left (s3Error e)
where where
noconn = Left $ error "S3 not configured" noconn = Left $ error "S3 not configured"
s3Warning :: ReqError -> Annex Bool s3Warning :: ReqError -> Annex Bool
s3Warning e = do s3Warning e = do
@ -215,12 +212,12 @@ s3Action r noconn action = do
bucketFile :: Remote -> Key -> FilePath bucketFile :: Remote -> Key -> FilePath
bucketFile r = munge . key2file bucketFile r = munge . key2file
where where
munge s = case M.lookup "mungekeys" c of munge s = case M.lookup "mungekeys" c of
Just "ia" -> iaMunge $ fileprefix ++ s Just "ia" -> iaMunge $ fileprefix ++ s
_ -> fileprefix ++ s _ -> fileprefix ++ s
fileprefix = M.findWithDefault "" "fileprefix" c fileprefix = M.findWithDefault "" "fileprefix" c
c = fromJust $ config r c = fromJust $ config r
bucketKey :: Remote -> String -> Key -> S3Object bucketKey :: Remote -> String -> Key -> S3Object
bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
@ -230,12 +227,12 @@ bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
- encoded. -} - encoded. -}
iaMunge :: String -> String iaMunge :: String -> String
iaMunge = (>>= munge) iaMunge = (>>= munge)
where where
munge c munge c
| isAsciiUpper c || isAsciiLower c || isNumber c = [c] | isAsciiUpper c || isAsciiLower c || isNumber c = [c]
| c `elem` "_-.\"" = [c] | c `elem` "_-.\"" = [c]
| isSpace c = [] | isSpace c = []
| otherwise = "&" ++ show (ord c) ++ ";" | otherwise = "&" ++ show (ord c) ++ ";"
genBucket :: RemoteConfig -> UUID -> Annex () genBucket :: RemoteConfig -> UUID -> Annex ()
genBucket c u = do genBucket c u = do
@ -251,9 +248,9 @@ genBucket c u = do
case res of case res of
Right _ -> noop Right _ -> noop
Left err -> s3Error err Left err -> s3Error err
where where
bucket = fromJust $ M.lookup "bucket" c bucket = fromJust $ M.lookup "bucket" c
datacenter = fromJust $ M.lookup "datacenter" c datacenter = fromJust $ M.lookup "datacenter" c
s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection
s3ConnectionRequired c u = s3ConnectionRequired c u =
@ -267,46 +264,46 @@ s3Connection c u = do
_ -> do _ -> do
warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey ++ " to use S3" warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey ++ " to use S3"
return Nothing return Nothing
where where
host = fromJust $ M.lookup "host" c host = fromJust $ M.lookup "host" c
port = let s = fromJust $ M.lookup "port" c in port = let s = fromJust $ M.lookup "port" c in
case reads s of case reads s of
[(p, _)] -> p [(p, _)] -> p
_ -> error $ "bad S3 port value: " ++ s _ -> error $ "bad S3 port value: " ++ s
{- S3 creds come from the environment if set, otherwise from the cache {- S3 creds come from the environment if set, otherwise from the cache
- in gitAnnexCredsDir, or failing that, might be stored encrypted in - in gitAnnexCredsDir, or failing that, might be stored encrypted in
- the remote's config. -} - the remote's config. -}
s3GetCreds :: RemoteConfig -> UUID -> Annex (Maybe (String, String)) s3GetCreds :: RemoteConfig -> UUID -> Annex (Maybe (String, String))
s3GetCreds c u = maybe fromcache (return . Just) =<< liftIO getenv s3GetCreds c u = maybe fromcache (return . Just) =<< liftIO getenv
where where
getenv = liftM2 (,) getenv = liftM2 (,)
<$> get s3AccessKey <$> get s3AccessKey
<*> get s3SecretKey <*> get s3SecretKey
where where
get = catchMaybeIO . getEnv get = catchMaybeIO . getEnv
fromcache = do fromcache = do
d <- fromRepo gitAnnexCredsDir d <- fromRepo gitAnnexCredsDir
let f = d </> fromUUID u let f = d </> fromUUID u
v <- liftIO $ catchMaybeIO $ readFile f v <- liftIO $ catchMaybeIO $ readFile f
case lines <$> v of case lines <$> v of
Just (ak:sk:[]) -> return $ Just (ak, sk) Just (ak:sk:[]) -> return $ Just (ak, sk)
_ -> fromconfig _ -> fromconfig
fromconfig = do fromconfig = do
mcipher <- remoteCipher c mcipher <- remoteCipher c
case (M.lookup "s3creds" c, mcipher) of case (M.lookup "s3creds" c, mcipher) of
(Just s3creds, Just cipher) -> do (Just s3creds, Just cipher) -> do
creds <- liftIO $ decrypt s3creds cipher creds <- liftIO $ decrypt s3creds cipher
case creds of case creds of
[ak, sk] -> do [ak, sk] -> do
s3CacheCreds (ak, sk) u s3CacheCreds (ak, sk) u
return $ Just (ak, sk) return $ Just (ak, sk)
_ -> do error "bad s3creds" _ -> do error "bad s3creds"
_ -> return Nothing _ -> return Nothing
decrypt s3creds cipher = lines <$> decrypt s3creds cipher = lines
withDecryptedContent cipher <$> withDecryptedContent cipher
(return $ L.pack $ fromB64 s3creds) (return $ L.pack $ fromB64 s3creds)
(return . L.unpack) (return . L.unpack)
{- Stores S3 creds encrypted in the remote's config if possible to do so {- Stores S3 creds encrypted in the remote's config if possible to do so
- securely, and otherwise locally in gitAnnexCredsDir. -} - securely, and otherwise locally in gitAnnexCredsDir. -}

View file

@ -55,13 +55,13 @@ gen r _ _ =
downloadKey :: Key -> AssociatedFile -> FilePath -> Annex Bool downloadKey :: Key -> AssociatedFile -> FilePath -> Annex Bool
downloadKey key _file dest = get =<< getUrls key downloadKey key _file dest = get =<< getUrls key
where where
get [] = do get [] = do
warning "no known url" warning "no known url"
return False return False
get urls = do get urls = do
showOutput -- make way for download progress bar showOutput -- make way for download progress bar
downloadUrl urls dest downloadUrl urls dest
downloadKeyCheap :: Key -> FilePath -> Annex Bool downloadKeyCheap :: Key -> FilePath -> Annex Bool
downloadKeyCheap _ _ = return False downloadKeyCheap _ _ = return False

43
Seek.hs
View file

@ -35,21 +35,21 @@ withFilesNotInGit a params = do
seekunless (null ps && not (null params)) ps seekunless (null ps && not (null params)) ps
dotfiles <- seekunless (null dotps) dotps dotfiles <- seekunless (null dotps) dotps
prepFiltered a $ return $ preserveOrder params (files++dotfiles) prepFiltered a $ return $ preserveOrder params (files++dotfiles)
where where
(dotps, ps) = partition dotfile params (dotps, ps) = partition dotfile params
seekunless True _ = return [] seekunless True _ = return []
seekunless _ l = do seekunless _ l = do
force <- Annex.getState Annex.force force <- Annex.getState Annex.force
g <- gitRepo g <- gitRepo
liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g
withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek
withPathContents a params = map a . concat <$> liftIO (mapM get params) withPathContents a params = map a . concat <$> liftIO (mapM get params)
where where
get p = ifM (isDirectory <$> getFileStatus p) get p = ifM (isDirectory <$> getFileStatus p)
( map (\f -> (f, makeRelative p f)) <$> dirContentsRecursive p ( map (\f -> (f, makeRelative p f)) <$> dirContentsRecursive p
, return [(p, takeFileName p)] , return [(p, takeFileName p)]
) )
withWords :: ([String] -> CommandStart) -> CommandSeek withWords :: ([String] -> CommandStart) -> CommandSeek
withWords a params = return [a params] withWords a params = return [a params]
@ -59,10 +59,10 @@ withStrings a params = return $ map a params
withPairs :: ((String, String) -> CommandStart) -> CommandSeek withPairs :: ((String, String) -> CommandStart) -> CommandSeek
withPairs a params = return $ map a $ pairs [] params withPairs a params = return $ map a $ pairs [] params
where where
pairs c [] = reverse c pairs c [] = reverse c
pairs c (x:y:xs) = pairs ((x,y):c) xs pairs c (x:y:xs) = pairs ((x,y):c) xs
pairs _ _ = error "expected pairs" pairs _ _ = error "expected pairs"
withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek
withFilesToBeCommitted a params = prepFiltered a $ withFilesToBeCommitted a params = prepFiltered a $
@ -83,8 +83,8 @@ withFilesUnlocked' typechanged a params = do
withKeys :: (Key -> CommandStart) -> CommandSeek withKeys :: (Key -> CommandStart) -> CommandSeek
withKeys a params = return $ map (a . parse) params withKeys a params = return $ map (a . parse) params
where where
parse p = fromMaybe (error "bad key") $ file2key p parse p = fromMaybe (error "bad key") $ file2key p
withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek
withValue v a params = do withValue v a params = do
@ -111,10 +111,9 @@ prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [Command
prepFiltered a fs = do prepFiltered a fs = do
matcher <- Limit.getMatcher matcher <- Limit.getMatcher
map (process matcher) <$> fs map (process matcher) <$> fs
where where
process matcher f = do process matcher f = ifM (matcher $ Annex.FileInfo f f)
ok <- matcher $ Annex.FileInfo f f ( a f , return Nothing )
if ok then a f else return Nothing
notSymlink :: FilePath -> IO Bool notSymlink :: FilePath -> IO Bool
notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f

View file

@ -30,16 +30,16 @@ myPostInst _ (InstallFlags { installVerbosity }) pkg lbi = do
installGitAnnexShell dest verbosity pkg lbi installGitAnnexShell dest verbosity pkg lbi
installManpages dest verbosity pkg lbi installManpages dest verbosity pkg lbi
installDesktopFile dest verbosity pkg lbi installDesktopFile dest verbosity pkg lbi
where where
dest = NoCopyDest dest = NoCopyDest
verbosity = fromFlag installVerbosity verbosity = fromFlag installVerbosity
installGitAnnexShell :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO () installGitAnnexShell :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
installGitAnnexShell copyDest verbosity pkg lbi = installGitAnnexShell copyDest verbosity pkg lbi =
rawSystemExit verbosity "ln" rawSystemExit verbosity "ln"
["-sf", "git-annex", dstBinDir </> "git-annex-shell"] ["-sf", "git-annex", dstBinDir </> "git-annex-shell"]
where where
dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest
{- See http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Installing_manpages {- See http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Installing_manpages
- -
@ -49,15 +49,15 @@ installGitAnnexShell copyDest verbosity pkg lbi =
installManpages :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO () installManpages :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
installManpages copyDest verbosity pkg lbi = installManpages copyDest verbosity pkg lbi =
installOrdinaryFiles verbosity dstManDir =<< srcManpages installOrdinaryFiles verbosity dstManDir =<< srcManpages
where where
dstManDir = mandir (absoluteInstallDirs pkg lbi copyDest) </> "man1" dstManDir = mandir (absoluteInstallDirs pkg lbi copyDest) </> "man1"
srcManpages = zip (repeat srcManDir) srcManpages = zip (repeat srcManDir)
<$> filterM doesFileExist manpages <$> filterM doesFileExist manpages
srcManDir = "" srcManDir = ""
manpages = ["git-annex.1", "git-annex-shell.1"] manpages = ["git-annex.1", "git-annex-shell.1"]
installDesktopFile :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO () installDesktopFile :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
installDesktopFile copyDest verbosity pkg lbi = installDesktopFile copyDest verbosity pkg lbi =
InstallDesktopFile.install $ dstBinDir </> "git-annex" InstallDesktopFile.install $ dstBinDir </> "git-annex"
where where
dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest

View file

@ -46,33 +46,33 @@ fieldSep = '-'
key2file :: Key -> FilePath key2file :: Key -> FilePath
key2file Key { keyBackendName = b, keySize = s, keyMtime = m, keyName = n } = key2file Key { keyBackendName = b, keySize = s, keyMtime = m, keyName = n } =
b +++ ('s' ?: s) +++ ('m' ?: m) +++ (fieldSep : n) b +++ ('s' ?: s) +++ ('m' ?: m) +++ (fieldSep : n)
where where
"" +++ y = y "" +++ y = y
x +++ "" = x x +++ "" = x
x +++ y = x ++ fieldSep:y x +++ y = x ++ fieldSep:y
c ?: (Just v) = c : show v c ?: (Just v) = c : show v
_ ?: _ = "" _ ?: _ = ""
file2key :: FilePath -> Maybe Key file2key :: FilePath -> Maybe Key
file2key s = if key == Just stubKey then Nothing else key file2key s = if key == Just stubKey then Nothing else key
where where
key = startbackend stubKey s key = startbackend stubKey s
startbackend k v = sepfield k v addbackend startbackend k v = sepfield k v addbackend
sepfield k v a = case span (/= fieldSep) v of sepfield k v a = case span (/= fieldSep) v of
(v', _:r) -> findfields r $ a k v' (v', _:r) -> findfields r $ a k v'
_ -> Nothing _ -> Nothing
findfields (c:v) (Just k) findfields (c:v) (Just k)
| c == fieldSep = Just $ k { keyName = v } | c == fieldSep = Just $ k { keyName = v }
| otherwise = sepfield k v $ addfield c | otherwise = sepfield k v $ addfield c
findfields _ v = v findfields _ v = v
addbackend k v = Just k { keyBackendName = v } addbackend k v = Just k { keyBackendName = v }
addfield 's' k v = Just k { keySize = readish v } addfield 's' k v = Just k { keySize = readish v }
addfield 'm' k v = Just k { keyMtime = readish v } addfield 'm' k v = Just k { keyMtime = readish v }
addfield _ _ _ = Nothing addfield _ _ _ = Nothing
prop_idempotent_key_encode :: Key -> Bool prop_idempotent_key_encode :: Key -> Bool
prop_idempotent_key_encode k = Just k == (file2key . key2file) k prop_idempotent_key_encode k = Just k == (file2key . key2file) k

View file

@ -15,8 +15,8 @@ import qualified Upgrade.V2
upgrade :: Annex Bool upgrade :: Annex Bool
upgrade = go =<< getVersion upgrade = go =<< getVersion
where where
go (Just "0") = Upgrade.V0.upgrade go (Just "0") = Upgrade.V0.upgrade
go (Just "1") = Upgrade.V1.upgrade go (Just "1") = Upgrade.V1.upgrade
go (Just "2") = Upgrade.V2.upgrade go (Just "2") = Upgrade.V2.upgrade
go _ = return True go _ = return True

View file

@ -40,10 +40,10 @@ getKeysPresent0 dir = ifM (liftIO $ doesDirectoryExist dir)
<$> (filterM present =<< getDirectoryContents dir) <$> (filterM present =<< getDirectoryContents dir)
, return [] , return []
) )
where where
present d = do present d = do
result <- tryIO $ result <- tryIO $
getFileStatus $ dir ++ "/" ++ takeFileName d getFileStatus $ dir ++ "/" ++ takeFileName d
case result of case result of
Right s -> return $ isRegularFile s Right s -> return $ isRegularFile s
Left _ -> return False Left _ -> return False

View file

@ -70,14 +70,14 @@ moveContent = do
showAction "moving content" showAction "moving content"
files <- getKeyFilesPresent1 files <- getKeyFilesPresent1
forM_ files move forM_ files move
where where
move f = do move f = do
let k = fileKey1 (takeFileName f) let k = fileKey1 (takeFileName f)
let d = parentDir f let d = parentDir f
liftIO $ allowWrite d liftIO $ allowWrite d
liftIO $ allowWrite f liftIO $ allowWrite f
moveAnnex k f moveAnnex k f
liftIO $ removeDirectory d liftIO $ removeDirectory d
updateSymlinks :: Annex () updateSymlinks :: Annex ()
updateSymlinks = do updateSymlinks = do
@ -86,54 +86,54 @@ updateSymlinks = do
(files, cleanup) <- inRepo $ LsFiles.inRepo [top] (files, cleanup) <- inRepo $ LsFiles.inRepo [top]
forM_ files fixlink forM_ files fixlink
void $ liftIO cleanup void $ liftIO cleanup
where where
fixlink f = do fixlink f = do
r <- lookupFile1 f r <- lookupFile1 f
case r of case r of
Nothing -> noop Nothing -> noop
Just (k, _) -> do Just (k, _) -> do
link <- calcGitLink f k link <- calcGitLink f k
liftIO $ removeFile f liftIO $ removeFile f
liftIO $ createSymbolicLink link f liftIO $ createSymbolicLink link f
Annex.Queue.addCommand "add" [Param "--"] [f] Annex.Queue.addCommand "add" [Param "--"] [f]
moveLocationLogs :: Annex () moveLocationLogs :: Annex ()
moveLocationLogs = do moveLocationLogs = do
showAction "moving location logs" showAction "moving location logs"
logkeys <- oldlocationlogs logkeys <- oldlocationlogs
forM_ logkeys move forM_ logkeys move
where where
oldlocationlogs = do oldlocationlogs = do
dir <- fromRepo Upgrade.V2.gitStateDir dir <- fromRepo Upgrade.V2.gitStateDir
ifM (liftIO $ doesDirectoryExist dir) ifM (liftIO $ doesDirectoryExist dir)
( mapMaybe oldlog2key ( mapMaybe oldlog2key
<$> (liftIO $ getDirectoryContents dir) <$> (liftIO $ getDirectoryContents dir)
, return [] , return []
) )
move (l, k) = do move (l, k) = do
dest <- fromRepo $ logFile2 k dest <- fromRepo $ logFile2 k
dir <- fromRepo Upgrade.V2.gitStateDir dir <- fromRepo Upgrade.V2.gitStateDir
let f = dir </> l let f = dir </> l
liftIO $ createDirectoryIfMissing True (parentDir dest) liftIO $ createDirectoryIfMissing True (parentDir dest)
-- could just git mv, but this way deals with -- could just git mv, but this way deals with
-- log files that are not checked into git, -- log files that are not checked into git,
-- as well as merging with already upgraded -- as well as merging with already upgraded
-- logs that have been pulled from elsewhere -- logs that have been pulled from elsewhere
old <- liftIO $ readLog1 f old <- liftIO $ readLog1 f
new <- liftIO $ readLog1 dest new <- liftIO $ readLog1 dest
liftIO $ writeLog1 dest (old++new) liftIO $ writeLog1 dest (old++new)
Annex.Queue.addCommand "add" [Param "--"] [dest] Annex.Queue.addCommand "add" [Param "--"] [dest]
Annex.Queue.addCommand "add" [Param "--"] [f] Annex.Queue.addCommand "add" [Param "--"] [f]
Annex.Queue.addCommand "rm" [Param "--quiet", Param "-f", Param "--"] [f] Annex.Queue.addCommand "rm" [Param "--quiet", Param "-f", Param "--"] [f]
oldlog2key :: FilePath -> Maybe (FilePath, Key) oldlog2key :: FilePath -> Maybe (FilePath, Key)
oldlog2key l oldlog2key l
| drop len l == ".log" && sane = Just (l, k) | drop len l == ".log" && sane = Just (l, k)
| otherwise = Nothing | otherwise = Nothing
where where
len = length l - 4 len = length l - 4
k = readKey1 (take len l) k = readKey1 (take len l)
sane = (not . null $ keyName k) && (not . null $ keyBackendName k) sane = (not . null $ keyName k) && (not . null $ keyBackendName k)
-- WORM backend keys: "WORM:mtime:size:filename" -- WORM backend keys: "WORM:mtime:size:filename"
-- all the rest: "backend:key" -- all the rest: "backend:key"
@ -150,25 +150,25 @@ readKey1 v
, keySize = s , keySize = s
, keyMtime = t , keyMtime = t
} }
where where
bits = split ":" v bits = split ":" v
b = Prelude.head bits b = Prelude.head bits
n = join ":" $ drop (if wormy then 3 else 1) bits n = join ":" $ drop (if wormy then 3 else 1) bits
t = if wormy t = if wormy
then Just (Prelude.read (bits !! 1) :: EpochTime) then Just (Prelude.read (bits !! 1) :: EpochTime)
else Nothing else Nothing
s = if wormy s = if wormy
then Just (Prelude.read (bits !! 2) :: Integer) then Just (Prelude.read (bits !! 2) :: Integer)
else Nothing else Nothing
wormy = Prelude.head bits == "WORM" wormy = Prelude.head bits == "WORM"
mixup = wormy && isUpper (Prelude.head $ bits !! 1) mixup = wormy && isUpper (Prelude.head $ bits !! 1)
showKey1 :: Key -> String showKey1 :: Key -> String
showKey1 Key { keyName = n , keyBackendName = b, keySize = s, keyMtime = t } = showKey1 Key { keyName = n , keyBackendName = b, keySize = s, keyMtime = t } =
join ":" $ filter (not . null) [b, showifhere t, showifhere s, n] join ":" $ filter (not . null) [b, showifhere t, showifhere s, n]
where where
showifhere Nothing = "" showifhere Nothing = ""
showifhere (Just v) = show v showifhere (Just v) = show v
keyFile1 :: Key -> FilePath keyFile1 :: Key -> FilePath
keyFile1 key = replace "/" "%" $ replace "%" "&s" $ replace "&" "&a" $ showKey1 key keyFile1 key = replace "/" "%" $ replace "%" "&s" $ replace "&" "&a" $ showKey1 key
@ -190,21 +190,21 @@ lookupFile1 file = do
case tl of case tl of
Left _ -> return Nothing Left _ -> return Nothing
Right l -> makekey l Right l -> makekey l
where where
getsymlink = takeFileName <$> readSymbolicLink file getsymlink = takeFileName <$> readSymbolicLink file
makekey l = case maybeLookupBackendName bname of makekey l = case maybeLookupBackendName bname of
Nothing -> do Nothing -> do
unless (null kname || null bname || unless (null kname || null bname ||
not (isLinkToAnnex l)) $ not (isLinkToAnnex l)) $
warning skip warning skip
return Nothing return Nothing
Just backend -> return $ Just (k, backend) Just backend -> return $ Just (k, backend)
where where
k = fileKey1 l k = fileKey1 l
bname = keyBackendName k bname = keyBackendName k
kname = keyName k kname = keyName k
skip = "skipping " ++ file ++ skip = "skipping " ++ file ++
" (unknown backend " ++ bname ++ ")" " (unknown backend " ++ bname ++ ")"
getKeyFilesPresent1 :: Annex [FilePath] getKeyFilesPresent1 :: Annex [FilePath]
getKeyFilesPresent1 = getKeyFilesPresent1' =<< fromRepo gitAnnexObjectDir getKeyFilesPresent1 = getKeyFilesPresent1' =<< fromRepo gitAnnexObjectDir
@ -217,12 +217,12 @@ getKeyFilesPresent1' dir =
liftIO $ filterM present files liftIO $ filterM present files
, return [] , return []
) )
where where
present f = do present f = do
result <- tryIO $ getFileStatus f result <- tryIO $ getFileStatus f
case result of case result of
Right s -> return $ isRegularFile s Right s -> return $ isRegularFile s
Left _ -> return False Left _ -> return False
logFile1 :: Git.Repo -> Key -> String logFile1 :: Git.Repo -> Key -> String
logFile1 repo key = Upgrade.V2.gitStateDir repo ++ keyFile1 key ++ ".log" logFile1 repo key = Upgrade.V2.gitStateDir repo ++ keyFile1 key ++ ".log"

View file

@ -70,10 +70,10 @@ locationLogs = do
levelb <- mapM tryDirContents levela levelb <- mapM tryDirContents levela
files <- mapM tryDirContents (concat levelb) files <- mapM tryDirContents (concat levelb)
return $ mapMaybe islogfile (concat files) return $ mapMaybe islogfile (concat files)
where where
tryDirContents d = catchDefaultIO [] $ dirContents d tryDirContents d = catchDefaultIO [] $ dirContents d
islogfile f = maybe Nothing (\k -> Just (k, f)) $ islogfile f = maybe Nothing (\k -> Just (k, f)) $
logFileKey $ takeFileName f logFileKey $ takeFileName f
inject :: FilePath -> FilePath -> Annex () inject :: FilePath -> FilePath -> Annex ()
inject source dest = do inject source dest = do

View file

@ -13,9 +13,9 @@ import qualified GitAnnexShell
main :: IO () main :: IO ()
main = run =<< getProgName main = run =<< getProgName
where where
run n run n
| isshell n = go GitAnnexShell.run | isshell n = go GitAnnexShell.run
| otherwise = go GitAnnex.run | otherwise = go GitAnnex.run
isshell n = takeFileName n == "git-annex-shell" isshell n = takeFileName n == "git-annex-shell"
go a = a =<< getArgs go a = a =<< getArgs

386
test.hs
View file

@ -133,45 +133,45 @@ blackbox = TestLabel "blackbox" $ TestList
test_init :: Test test_init :: Test
test_init = "git-annex init" ~: TestCase $ innewrepo $ do test_init = "git-annex init" ~: TestCase $ innewrepo $ do
git_annex "init" [reponame] @? "init failed" git_annex "init" [reponame] @? "init failed"
where where
reponame = "test repo" reponame = "test repo"
test_add :: Test test_add :: Test
test_add = "git-annex add" ~: TestList [basic, sha1dup, subdirs] test_add = "git-annex add" ~: TestList [basic, sha1dup, subdirs]
where where
-- this test case runs in the main repo, to set up a basic -- this test case runs in the main repo, to set up a basic
-- annexed file that later tests will use -- annexed file that later tests will use
basic = TestCase $ inmainrepo $ do basic = TestCase $ inmainrepo $ do
writeFile annexedfile $ content annexedfile writeFile annexedfile $ content annexedfile
git_annex "add" [annexedfile] @? "add failed" git_annex "add" [annexedfile] @? "add failed"
annexed_present annexedfile annexed_present annexedfile
writeFile sha1annexedfile $ content sha1annexedfile writeFile sha1annexedfile $ content sha1annexedfile
git_annex "add" [sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed" git_annex "add" [sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed"
annexed_present sha1annexedfile annexed_present sha1annexedfile
checkbackend sha1annexedfile backendSHA1 checkbackend sha1annexedfile backendSHA1
writeFile wormannexedfile $ content wormannexedfile writeFile wormannexedfile $ content wormannexedfile
git_annex "add" [wormannexedfile, "--backend=WORM"] @? "add with WORM failed" git_annex "add" [wormannexedfile, "--backend=WORM"] @? "add with WORM failed"
annexed_present wormannexedfile annexed_present wormannexedfile
checkbackend wormannexedfile backendWORM checkbackend wormannexedfile backendWORM
boolSystem "git" [Params "rm --force -q", File wormannexedfile] @? "git rm failed" boolSystem "git" [Params "rm --force -q", File wormannexedfile] @? "git rm failed"
writeFile ingitfile $ content ingitfile writeFile ingitfile $ content ingitfile
boolSystem "git" [Param "add", File ingitfile] @? "git add failed" boolSystem "git" [Param "add", File ingitfile] @? "git add failed"
boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed" boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed"
git_annex "add" [ingitfile] @? "add ingitfile should be no-op" git_annex "add" [ingitfile] @? "add ingitfile should be no-op"
unannexed ingitfile unannexed ingitfile
sha1dup = TestCase $ intmpclonerepo $ do sha1dup = TestCase $ intmpclonerepo $ do
writeFile sha1annexedfiledup $ content sha1annexedfiledup writeFile sha1annexedfiledup $ content sha1annexedfiledup
git_annex "add" [sha1annexedfiledup, "--backend=SHA1"] @? "add of second file with same SHA1 failed" git_annex "add" [sha1annexedfiledup, "--backend=SHA1"] @? "add of second file with same SHA1 failed"
annexed_present sha1annexedfiledup annexed_present sha1annexedfiledup
annexed_present sha1annexedfile annexed_present sha1annexedfile
subdirs = TestCase $ intmpclonerepo $ do subdirs = TestCase $ intmpclonerepo $ do
createDirectory "dir" createDirectory "dir"
writeFile "dir/foo" $ content annexedfile writeFile "dir/foo" $ content annexedfile
git_annex "add" ["dir"] @? "add of subdir failed" git_annex "add" ["dir"] @? "add of subdir failed"
createDirectory "dir2" createDirectory "dir2"
writeFile "dir2/foo" $ content annexedfile writeFile "dir2/foo" $ content annexedfile
changeWorkingDirectory "dir" changeWorkingDirectory "dir"
git_annex "add" ["../dir2"] @? "add of ../subdir failed" git_annex "add" ["../dir2"] @? "add of ../subdir failed"
test_reinject :: Test test_reinject :: Test
test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do
@ -183,53 +183,53 @@ test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do
git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed" git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed"
git_annex "fromkey" [key, sha1annexedfiledup] @? "fromkey failed" git_annex "fromkey" [key, sha1annexedfiledup] @? "fromkey failed"
annexed_present sha1annexedfiledup annexed_present sha1annexedfiledup
where where
tmp = "tmpfile" tmp = "tmpfile"
test_unannex :: Test test_unannex :: Test
test_unannex = "git-annex unannex" ~: TestList [nocopy, withcopy] test_unannex = "git-annex unannex" ~: TestList [nocopy, withcopy]
where where
nocopy = "no content" ~: intmpclonerepo $ do nocopy = "no content" ~: intmpclonerepo $ do
annexed_notpresent annexedfile annexed_notpresent annexedfile
git_annex "unannex" [annexedfile] @? "unannex failed with no copy" git_annex "unannex" [annexedfile] @? "unannex failed with no copy"
annexed_notpresent annexedfile annexed_notpresent annexedfile
withcopy = "with content" ~: intmpclonerepo $ do withcopy = "with content" ~: intmpclonerepo $ do
git_annex "get" [annexedfile] @? "get failed" git_annex "get" [annexedfile] @? "get failed"
annexed_present annexedfile annexed_present annexedfile
git_annex "unannex" [annexedfile, sha1annexedfile] @? "unannex failed" git_annex "unannex" [annexedfile, sha1annexedfile] @? "unannex failed"
unannexed annexedfile unannexed annexedfile
git_annex "unannex" [annexedfile] @? "unannex failed on non-annexed file" git_annex "unannex" [annexedfile] @? "unannex failed on non-annexed file"
unannexed annexedfile unannexed annexedfile
git_annex "unannex" [ingitfile] @? "unannex ingitfile should be no-op" git_annex "unannex" [ingitfile] @? "unannex ingitfile should be no-op"
unannexed ingitfile unannexed ingitfile
test_drop :: Test test_drop :: Test
test_drop = "git-annex drop" ~: TestList [noremote, withremote, untrustedremote] test_drop = "git-annex drop" ~: TestList [noremote, withremote, untrustedremote]
where where
noremote = "no remotes" ~: TestCase $ intmpclonerepo $ do noremote = "no remotes" ~: TestCase $ intmpclonerepo $ do
git_annex "get" [annexedfile] @? "get failed" git_annex "get" [annexedfile] @? "get failed"
boolSystem "git" [Params "remote rm origin"] boolSystem "git" [Params "remote rm origin"]
@? "git remote rm origin failed" @? "git remote rm origin failed"
not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of file" not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of file"
annexed_present annexedfile annexed_present annexedfile
git_annex "drop" ["--force", annexedfile] @? "drop --force failed" git_annex "drop" ["--force", annexedfile] @? "drop --force failed"
annexed_notpresent annexedfile annexed_notpresent annexedfile
git_annex "drop" [annexedfile] @? "drop of dropped file failed" git_annex "drop" [annexedfile] @? "drop of dropped file failed"
git_annex "drop" [ingitfile] @? "drop ingitfile should be no-op" git_annex "drop" [ingitfile] @? "drop ingitfile should be no-op"
unannexed ingitfile unannexed ingitfile
withremote = "with remote" ~: TestCase $ intmpclonerepo $ do withremote = "with remote" ~: TestCase $ intmpclonerepo $ do
git_annex "get" [annexedfile] @? "get failed" git_annex "get" [annexedfile] @? "get failed"
annexed_present annexedfile annexed_present annexedfile
git_annex "drop" [annexedfile] @? "drop failed though origin has copy" git_annex "drop" [annexedfile] @? "drop failed though origin has copy"
annexed_notpresent annexedfile annexed_notpresent annexedfile
inmainrepo $ annexed_present annexedfile inmainrepo $ annexed_present annexedfile
untrustedremote = "untrusted remote" ~: TestCase $ intmpclonerepo $ do untrustedremote = "untrusted remote" ~: TestCase $ intmpclonerepo $ do
git_annex "untrust" ["origin"] @? "untrust of origin failed" git_annex "untrust" ["origin"] @? "untrust of origin failed"
git_annex "get" [annexedfile] @? "get failed" git_annex "get" [annexedfile] @? "get failed"
annexed_present annexedfile annexed_present annexedfile
not <$> git_annex "drop" [annexedfile] @? "drop wrongly suceeded with only an untrusted copy of the file" not <$> git_annex "drop" [annexedfile] @? "drop wrongly suceeded with only an untrusted copy of the file"
annexed_present annexedfile annexed_present annexedfile
inmainrepo $ annexed_present annexedfile inmainrepo $ annexed_present annexedfile
test_get :: Test test_get :: Test
test_get = "git-annex get" ~: TestCase $ intmpclonerepo $ do test_get = "git-annex get" ~: TestCase $ intmpclonerepo $ do
@ -326,27 +326,27 @@ test_lock = "git-annex unlock/lock" ~: intmpclonerepo $ do
test_edit :: Test test_edit :: Test
test_edit = "git-annex edit/commit" ~: TestList [t False, t True] test_edit = "git-annex edit/commit" ~: TestList [t False, t True]
where t precommit = TestCase $ intmpclonerepo $ do where t precommit = TestCase $ intmpclonerepo $ do
git_annex "get" [annexedfile] @? "get of file failed" git_annex "get" [annexedfile] @? "get of file failed"
annexed_present annexedfile annexed_present annexedfile
git_annex "edit" [annexedfile] @? "edit failed" git_annex "edit" [annexedfile] @? "edit failed"
unannexed annexedfile unannexed annexedfile
changecontent annexedfile changecontent annexedfile
if precommit if precommit
then do then do
-- pre-commit depends on the file being -- pre-commit depends on the file being
-- staged, normally git commit does this -- staged, normally git commit does this
boolSystem "git" [Param "add", File annexedfile] boolSystem "git" [Param "add", File annexedfile]
@? "git add of edited file failed" @? "git add of edited file failed"
git_annex "pre-commit" [] git_annex "pre-commit" []
@? "pre-commit failed" @? "pre-commit failed"
else do else do
boolSystem "git" [Params "commit -q -a -m contentchanged"] boolSystem "git" [Params "commit -q -a -m contentchanged"]
@? "git commit of edited file failed" @? "git commit of edited file failed"
runchecks [checklink, checkunwritable] annexedfile runchecks [checklink, checkunwritable] annexedfile
c <- readFile annexedfile c <- readFile annexedfile
assertEqual "content of modified file" c (changedcontent annexedfile) assertEqual "content of modified file" c (changedcontent annexedfile)
not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of modified file" not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of modified file"
test_fix :: Test test_fix :: Test
test_fix = "git-annex fix" ~: intmpclonerepo $ do test_fix = "git-annex fix" ~: intmpclonerepo $ do
@ -364,9 +364,9 @@ test_fix = "git-annex fix" ~: intmpclonerepo $ do
runchecks [checklink, checkunwritable] newfile runchecks [checklink, checkunwritable] newfile
c <- readFile newfile c <- readFile newfile
assertEqual "content of moved file" c (content annexedfile) assertEqual "content of moved file" c (content annexedfile)
where where
subdir = "s" subdir = "s"
newfile = subdir ++ "/" ++ annexedfile newfile = subdir ++ "/" ++ annexedfile
test_trust :: Test test_trust :: Test
test_trust = "git-annex trust/untrust/semitrust/dead" ~: intmpclonerepo $ do test_trust = "git-annex trust/untrust/semitrust/dead" ~: intmpclonerepo $ do
@ -386,89 +386,89 @@ test_trust = "git-annex trust/untrust/semitrust/dead" ~: intmpclonerepo $ do
trustcheck Logs.Trust.SemiTrusted "semitrusted 1" trustcheck Logs.Trust.SemiTrusted "semitrusted 1"
git_annex "semitrust" [repo] @? "semitrust of semitrusted failed" git_annex "semitrust" [repo] @? "semitrust of semitrusted failed"
trustcheck Logs.Trust.SemiTrusted "semitrusted 2" trustcheck Logs.Trust.SemiTrusted "semitrusted 2"
where where
repo = "origin" repo = "origin"
trustcheck expected msg = do trustcheck expected msg = do
present <- annexeval $ do present <- annexeval $ do
l <- Logs.Trust.trustGet expected l <- Logs.Trust.trustGet expected
u <- Remote.nameToUUID repo u <- Remote.nameToUUID repo
return $ u `elem` l return $ u `elem` l
assertBool msg present assertBool msg present
test_fsck :: Test test_fsck :: Test
test_fsck = "git-annex fsck" ~: TestList [basicfsck, barefsck, withlocaluntrusted, withremoteuntrusted] test_fsck = "git-annex fsck" ~: TestList [basicfsck, barefsck, withlocaluntrusted, withremoteuntrusted]
where where
basicfsck = TestCase $ intmpclonerepo $ do basicfsck = TestCase $ intmpclonerepo $ do
git_annex "fsck" [] @? "fsck failed" git_annex "fsck" [] @? "fsck failed"
boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed" boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed"
fsck_should_fail "numcopies unsatisfied" fsck_should_fail "numcopies unsatisfied"
boolSystem "git" [Params "config annex.numcopies 1"] @? "git config failed" boolSystem "git" [Params "config annex.numcopies 1"] @? "git config failed"
corrupt annexedfile corrupt annexedfile
corrupt sha1annexedfile corrupt sha1annexedfile
barefsck = TestCase $ intmpbareclonerepo $ do barefsck = TestCase $ intmpbareclonerepo $ do
git_annex "fsck" [] @? "fsck failed" git_annex "fsck" [] @? "fsck failed"
withlocaluntrusted = TestCase $ intmpclonerepo $ do withlocaluntrusted = TestCase $ intmpclonerepo $ do
git_annex "get" [annexedfile] @? "get failed" git_annex "get" [annexedfile] @? "get failed"
git_annex "untrust" ["origin"] @? "untrust of origin repo failed" git_annex "untrust" ["origin"] @? "untrust of origin repo failed"
git_annex "untrust" ["."] @? "untrust of current repo failed" git_annex "untrust" ["."] @? "untrust of current repo failed"
fsck_should_fail "content only available in untrusted (current) repository" fsck_should_fail "content only available in untrusted (current) repository"
git_annex "trust" ["."] @? "trust of current repo failed" git_annex "trust" ["."] @? "trust of current repo failed"
git_annex "fsck" [annexedfile] @? "fsck failed on file present in trusted repo" git_annex "fsck" [annexedfile] @? "fsck failed on file present in trusted repo"
withremoteuntrusted = TestCase $ intmpclonerepo $ do withremoteuntrusted = TestCase $ intmpclonerepo $ do
boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed" boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed"
git_annex "get" [annexedfile] @? "get failed" git_annex "get" [annexedfile] @? "get failed"
git_annex "get" [sha1annexedfile] @? "get failed" git_annex "get" [sha1annexedfile] @? "get failed"
git_annex "fsck" [] @? "fsck failed with numcopies=2 and 2 copies" git_annex "fsck" [] @? "fsck failed with numcopies=2 and 2 copies"
git_annex "untrust" ["origin"] @? "untrust of origin failed" git_annex "untrust" ["origin"] @? "untrust of origin failed"
fsck_should_fail "content not replicated to enough non-untrusted repositories" fsck_should_fail "content not replicated to enough non-untrusted repositories"
corrupt f = do corrupt f = do
git_annex "get" [f] @? "get of file failed" git_annex "get" [f] @? "get of file failed"
Utility.FileMode.allowWrite f Utility.FileMode.allowWrite f
writeFile f (changedcontent f) writeFile f (changedcontent f)
not <$> git_annex "fsck" [] @? "fsck failed to fail with corrupted file content" not <$> git_annex "fsck" [] @? "fsck failed to fail with corrupted file content"
git_annex "fsck" [] @? "fsck unexpectedly failed again; previous one did not fix problem with " ++ f git_annex "fsck" [] @? "fsck unexpectedly failed again; previous one did not fix problem with " ++ f
fsck_should_fail m = do fsck_should_fail m = do
not <$> git_annex "fsck" [] @? "fsck failed to fail with " ++ m not <$> git_annex "fsck" [] @? "fsck failed to fail with " ++ m
test_migrate :: Test test_migrate :: Test
test_migrate = "git-annex migrate" ~: TestList [t False, t True] test_migrate = "git-annex migrate" ~: TestList [t False, t True]
where t usegitattributes = TestCase $ intmpclonerepo $ do where t usegitattributes = TestCase $ intmpclonerepo $ do
annexed_notpresent annexedfile annexed_notpresent annexedfile
annexed_notpresent sha1annexedfile annexed_notpresent sha1annexedfile
git_annex "migrate" [annexedfile] @? "migrate of not present failed" git_annex "migrate" [annexedfile] @? "migrate of not present failed"
git_annex "migrate" [sha1annexedfile] @? "migrate of not present failed" git_annex "migrate" [sha1annexedfile] @? "migrate of not present failed"
git_annex "get" [annexedfile] @? "get of file failed" git_annex "get" [annexedfile] @? "get of file failed"
git_annex "get" [sha1annexedfile] @? "get of file failed" git_annex "get" [sha1annexedfile] @? "get of file failed"
annexed_present annexedfile annexed_present annexedfile
annexed_present sha1annexedfile annexed_present sha1annexedfile
if usegitattributes if usegitattributes
then do then do
writeFile ".gitattributes" $ "* annex.backend=SHA1" writeFile ".gitattributes" $ "* annex.backend=SHA1"
git_annex "migrate" [sha1annexedfile] git_annex "migrate" [sha1annexedfile]
@? "migrate sha1annexedfile failed" @? "migrate sha1annexedfile failed"
git_annex "migrate" [annexedfile] git_annex "migrate" [annexedfile]
@? "migrate annexedfile failed" @? "migrate annexedfile failed"
else do else do
git_annex "migrate" [sha1annexedfile, "--backend", "SHA1"] git_annex "migrate" [sha1annexedfile, "--backend", "SHA1"]
@? "migrate sha1annexedfile failed" @? "migrate sha1annexedfile failed"
git_annex "migrate" [annexedfile, "--backend", "SHA1"] git_annex "migrate" [annexedfile, "--backend", "SHA1"]
@? "migrate annexedfile failed" @? "migrate annexedfile failed"
annexed_present annexedfile annexed_present annexedfile
annexed_present sha1annexedfile annexed_present sha1annexedfile
checkbackend annexedfile backendSHA1 checkbackend annexedfile backendSHA1
checkbackend sha1annexedfile backendSHA1 checkbackend sha1annexedfile backendSHA1
-- check that reversing a migration works -- check that reversing a migration works
writeFile ".gitattributes" $ "* annex.backend=SHA256" writeFile ".gitattributes" $ "* annex.backend=SHA256"
git_annex "migrate" [sha1annexedfile] git_annex "migrate" [sha1annexedfile]
@? "migrate sha1annexedfile failed" @? "migrate sha1annexedfile failed"
git_annex "migrate" [annexedfile] git_annex "migrate" [annexedfile]
@? "migrate annexedfile failed" @? "migrate annexedfile failed"
annexed_present annexedfile annexed_present annexedfile
annexed_present sha1annexedfile annexed_present sha1annexedfile
checkbackend annexedfile backendSHA256 checkbackend annexedfile backendSHA256
checkbackend sha1annexedfile backendSHA256 checkbackend sha1annexedfile backendSHA256
test_unused :: Test test_unused :: Test
test_unused = "git-annex unused/dropunused" ~: intmpclonerepo $ do test_unused = "git-annex unused/dropunused" ~: intmpclonerepo $ do
@ -498,16 +498,16 @@ test_unused = "git-annex unused/dropunused" ~: intmpclonerepo $ do
checkunused [] "after dropunused" checkunused [] "after dropunused"
git_annex "dropunused" ["10", "501"] @? "dropunused failed on bogus numbers" git_annex "dropunused" ["10", "501"] @? "dropunused failed on bogus numbers"
where where
checkunused expectedkeys desc = do checkunused expectedkeys desc = do
git_annex "unused" [] @? "unused failed" git_annex "unused" [] @? "unused failed"
unusedmap <- annexeval $ Logs.Unused.readUnusedLog "" unusedmap <- annexeval $ Logs.Unused.readUnusedLog ""
let unusedkeys = M.elems unusedmap let unusedkeys = M.elems unusedmap
assertEqual ("unused keys differ " ++ desc) assertEqual ("unused keys differ " ++ desc)
(sort expectedkeys) (sort unusedkeys) (sort expectedkeys) (sort unusedkeys)
findkey f = do findkey f = do
r <- Backend.lookupFile f r <- Backend.lookupFile f
return $ fst $ fromJust r return $ fst $ fromJust r
test_describe :: Test test_describe :: Test
test_describe = "git-annex describe" ~: intmpclonerepo $ do test_describe = "git-annex describe" ~: intmpclonerepo $ do
@ -604,11 +604,11 @@ test_hook_remote = "git-annex hook remote" ~: intmpclonerepo $ do
annexed_present annexedfile annexed_present annexedfile
not <$> git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail" not <$> git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"
annexed_present annexedfile annexed_present annexedfile
where where
dir = "dir" dir = "dir"
loc = dir ++ "/$ANNEX_KEY" loc = dir ++ "/$ANNEX_KEY"
git_config k v = boolSystem "git" [Param "config", Param k, Param v] git_config k v = boolSystem "git" [Param "config", Param k, Param v]
@? "git config failed" @? "git config failed"
test_directory_remote :: Test test_directory_remote :: Test
test_directory_remote = "git-annex directory remote" ~: intmpclonerepo $ do test_directory_remote = "git-annex directory remote" ~: intmpclonerepo $ do
@ -692,8 +692,8 @@ git_annex command params = do
case r of case r of
Right _ -> return True Right _ -> return True
Left _ -> return False Left _ -> return False
where where
run = GitAnnex.run (command:"-q":params) run = GitAnnex.run (command:"-q":params)
{- Runs git-annex and returns its output. -} {- Runs git-annex and returns its output. -}
git_annex_output :: String -> [String] -> IO String git_annex_output :: String -> [String] -> IO String