where indentation
This commit is contained in:
parent
f0dd6d00d1
commit
ebd576ebcb
30 changed files with 804 additions and 812 deletions
|
@ -32,20 +32,20 @@ seek = [withFilesNotInGit start, withFilesUnlocked start]
|
||||||
- to its content. -}
|
- to its content. -}
|
||||||
start :: FilePath -> CommandStart
|
start :: FilePath -> CommandStart
|
||||||
start file = notBareRepo $ ifAnnexed file fixup add
|
start file = notBareRepo $ ifAnnexed file fixup add
|
||||||
where
|
where
|
||||||
add = do
|
add = do
|
||||||
s <- liftIO $ getSymbolicLinkStatus file
|
s <- liftIO $ getSymbolicLinkStatus file
|
||||||
if isSymbolicLink s || not (isRegularFile s)
|
if isSymbolicLink s || not (isRegularFile s)
|
||||||
then stop
|
then stop
|
||||||
else do
|
else do
|
||||||
showStart "add" file
|
showStart "add" file
|
||||||
next $ perform file
|
next $ perform file
|
||||||
fixup (key, _) = do
|
fixup (key, _) = do
|
||||||
-- fixup from an interrupted add; the symlink
|
-- fixup from an interrupted add; the symlink
|
||||||
-- is present but not yet added to git
|
-- is present but not yet added to git
|
||||||
showStart "add" file
|
showStart "add" file
|
||||||
liftIO $ removeFile file
|
liftIO $ removeFile file
|
||||||
next $ next $ cleanup file key =<< inAnnex key
|
next $ next $ cleanup file key =<< inAnnex key
|
||||||
|
|
||||||
{- The file that's being added is locked down before a key is generated,
|
{- The file that's being added is locked down before a key is generated,
|
||||||
- to prevent it from being modified in between. It's hard linked into a
|
- to prevent it from being modified in between. It's hard linked into a
|
||||||
|
@ -67,15 +67,15 @@ ingest :: KeySource -> Annex (Maybe Key)
|
||||||
ingest source = do
|
ingest source = do
|
||||||
backend <- chooseBackend $ keyFilename source
|
backend <- chooseBackend $ keyFilename source
|
||||||
genKey source backend >>= go
|
genKey source backend >>= go
|
||||||
where
|
where
|
||||||
go Nothing = do
|
go Nothing = do
|
||||||
liftIO $ nukeFile $ contentLocation source
|
liftIO $ nukeFile $ contentLocation source
|
||||||
return Nothing
|
return Nothing
|
||||||
go (Just (key, _)) = do
|
go (Just (key, _)) = do
|
||||||
handle (undo (keyFilename source) key) $
|
handle (undo (keyFilename source) key) $
|
||||||
moveAnnex key $ contentLocation source
|
moveAnnex key $ contentLocation source
|
||||||
liftIO $ nukeFile $ keyFilename source
|
liftIO $ nukeFile $ keyFilename source
|
||||||
return $ Just key
|
return $ Just key
|
||||||
|
|
||||||
perform :: FilePath -> CommandPerform
|
perform :: FilePath -> CommandPerform
|
||||||
perform file =
|
perform file =
|
||||||
|
@ -91,12 +91,12 @@ undo file key e = do
|
||||||
handle tryharder $ fromAnnex key file
|
handle tryharder $ fromAnnex key file
|
||||||
logStatus key InfoMissing
|
logStatus key InfoMissing
|
||||||
throw e
|
throw e
|
||||||
where
|
where
|
||||||
-- fromAnnex could fail if the file ownership is weird
|
-- fromAnnex could fail if the file ownership is weird
|
||||||
tryharder :: IOException -> Annex ()
|
tryharder :: IOException -> Annex ()
|
||||||
tryharder _ = do
|
tryharder _ = do
|
||||||
src <- inRepo $ gitAnnexLocation key
|
src <- inRepo $ gitAnnexLocation key
|
||||||
liftIO $ moveFile src file
|
liftIO $ moveFile src file
|
||||||
|
|
||||||
{- Creates the symlink to the annexed content, returns the link target. -}
|
{- Creates the symlink to the annexed content, returns the link target. -}
|
||||||
link :: FilePath -> Key -> Bool -> Annex String
|
link :: FilePath -> Key -> Bool -> Annex String
|
||||||
|
|
|
@ -25,8 +25,8 @@ start = startUnused "addunused" perform (performOther "bad") (performOther "tmp"
|
||||||
|
|
||||||
perform :: Key -> CommandPerform
|
perform :: Key -> CommandPerform
|
||||||
perform key = next $ Command.Add.cleanup file key True
|
perform key = next $ Command.Add.cleanup file key True
|
||||||
where
|
where
|
||||||
file = "unused." ++ key2file key
|
file = "unused." ++ key2file key
|
||||||
|
|
||||||
{- The content is not in the annex, but in another directory, and
|
{- The content is not in the annex, but in another directory, and
|
||||||
- it seems better to error out, rather than moving bad/tmp content into
|
- it seems better to error out, rather than moving bad/tmp content into
|
||||||
|
|
|
@ -40,31 +40,31 @@ seek = [withField fileOption return $ \f ->
|
||||||
|
|
||||||
start :: Maybe FilePath -> Maybe Int -> String -> CommandStart
|
start :: Maybe FilePath -> Maybe Int -> String -> CommandStart
|
||||||
start optfile pathdepth s = notBareRepo $ go $ fromMaybe bad $ parseURI s
|
start optfile pathdepth s = notBareRepo $ go $ fromMaybe bad $ parseURI s
|
||||||
where
|
where
|
||||||
bad = fromMaybe (error $ "bad url " ++ s) $
|
bad = fromMaybe (error $ "bad url " ++ s) $
|
||||||
parseURI $ escapeURIString isUnescapedInURI s
|
parseURI $ escapeURIString isUnescapedInURI s
|
||||||
go url = do
|
go url = do
|
||||||
let file = fromMaybe (url2file url pathdepth) optfile
|
let file = fromMaybe (url2file url pathdepth) optfile
|
||||||
showStart "addurl" file
|
showStart "addurl" file
|
||||||
next $ perform s file
|
next $ perform s file
|
||||||
|
|
||||||
perform :: String -> FilePath -> CommandPerform
|
perform :: String -> FilePath -> CommandPerform
|
||||||
perform url file = ifAnnexed file addurl geturl
|
perform url file = ifAnnexed file addurl geturl
|
||||||
where
|
where
|
||||||
geturl = do
|
geturl = do
|
||||||
liftIO $ createDirectoryIfMissing True (parentDir file)
|
liftIO $ createDirectoryIfMissing True (parentDir file)
|
||||||
ifM (Annex.getState Annex.fast)
|
ifM (Annex.getState Annex.fast)
|
||||||
( nodownload url file , download url file )
|
( nodownload url file , download url file )
|
||||||
addurl (key, _backend) = do
|
addurl (key, _backend) = do
|
||||||
headers <- getHttpHeaders
|
headers <- getHttpHeaders
|
||||||
ifM (liftIO $ Url.check url headers $ keySize key)
|
ifM (liftIO $ Url.check url headers $ keySize key)
|
||||||
( do
|
( do
|
||||||
setUrlPresent key url
|
setUrlPresent key url
|
||||||
next $ return True
|
next $ return True
|
||||||
, do
|
, do
|
||||||
warning $ "failed to verify url: " ++ url
|
warning $ "failed to verify url: " ++ url
|
||||||
stop
|
stop
|
||||||
)
|
)
|
||||||
|
|
||||||
download :: String -> FilePath -> CommandPerform
|
download :: String -> FilePath -> CommandPerform
|
||||||
download url file = do
|
download url file = do
|
||||||
|
@ -103,10 +103,10 @@ url2file url pathdepth = case pathdepth of
|
||||||
| depth > 0 -> frombits $ drop depth
|
| depth > 0 -> frombits $ drop depth
|
||||||
| depth < 0 -> frombits $ reverse . take (negate depth) . reverse
|
| depth < 0 -> frombits $ reverse . take (negate depth) . reverse
|
||||||
| otherwise -> error "bad --pathdepth"
|
| otherwise -> error "bad --pathdepth"
|
||||||
where
|
where
|
||||||
fullurl = uriRegName auth ++ uriPath url ++ uriQuery url
|
fullurl = uriRegName auth ++ uriPath url ++ uriQuery url
|
||||||
frombits a = join "/" $ a urlbits
|
frombits a = join "/" $ a urlbits
|
||||||
urlbits = map (filesize . escape) $ filter (not . null) $ split "/" fullurl
|
urlbits = map (filesize . escape) $ filter (not . null) $ split "/" fullurl
|
||||||
auth = fromMaybe (error $ "bad url " ++ show url) $ uriAuthority url
|
auth = fromMaybe (error $ "bad url " ++ show url) $ uriAuthority url
|
||||||
filesize = take 255
|
filesize = take 255
|
||||||
escape = replace "/" "_" . replace "?" "_"
|
escape = replace "/" "_" . replace "?" "_"
|
||||||
|
|
|
@ -65,7 +65,7 @@ autoStart = do
|
||||||
)
|
)
|
||||||
, nothing
|
, nothing
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
go program dir = do
|
go program dir = do
|
||||||
changeWorkingDirectory dir
|
changeWorkingDirectory dir
|
||||||
boolSystem program [Param "assistant"]
|
boolSystem program [Param "assistant"]
|
||||||
|
|
|
@ -24,6 +24,6 @@ start = next $ next $ do
|
||||||
Annex.Branch.commit "update"
|
Annex.Branch.commit "update"
|
||||||
_ <- runhook <=< inRepo $ Git.hookPath "annex-content"
|
_ <- runhook <=< inRepo $ Git.hookPath "annex-content"
|
||||||
return True
|
return True
|
||||||
where
|
where
|
||||||
runhook (Just hook) = liftIO $ boolSystem hook []
|
runhook (Just hook) = liftIO $ boolSystem hook []
|
||||||
runhook Nothing = return True
|
runhook Nothing = return True
|
||||||
|
|
|
@ -29,7 +29,7 @@ start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandSt
|
||||||
start to from file (key, backend) = autoCopies file key (<) $
|
start to from file (key, backend) = autoCopies file key (<) $
|
||||||
stopUnless shouldCopy $
|
stopUnless shouldCopy $
|
||||||
Command.Move.start to from False file (key, backend)
|
Command.Move.start to from False file (key, backend)
|
||||||
where
|
where
|
||||||
shouldCopy = case to of
|
shouldCopy = case to of
|
||||||
Nothing -> checkAuto $ wantGet (Just file)
|
Nothing -> checkAuto $ wantGet (Just file)
|
||||||
Just r -> checkAuto $ wantSend (Just file) (Remote.uuid r)
|
Just r -> checkAuto $ wantSend (Just file) (Remote.uuid r)
|
||||||
|
|
|
@ -76,8 +76,8 @@ performRemote key numcopies remote = lockContent key $ do
|
||||||
stopUnless (canDropKey key numcopies have tocheck [uuid]) $ do
|
stopUnless (canDropKey key numcopies have tocheck [uuid]) $ do
|
||||||
ok <- Remote.removeKey remote key
|
ok <- Remote.removeKey remote key
|
||||||
next $ cleanupRemote key remote ok
|
next $ cleanupRemote key remote ok
|
||||||
where
|
where
|
||||||
uuid = Remote.uuid remote
|
uuid = Remote.uuid remote
|
||||||
|
|
||||||
cleanupLocal :: Key -> CommandCleanup
|
cleanupLocal :: Key -> CommandCleanup
|
||||||
cleanupLocal key = do
|
cleanupLocal key = do
|
||||||
|
@ -106,20 +106,20 @@ canDropKey key numcopiesM have check skip = do
|
||||||
|
|
||||||
findCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
|
findCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
|
||||||
findCopies key need skip = helper []
|
findCopies key need skip = helper []
|
||||||
where
|
where
|
||||||
helper bad have []
|
helper bad have []
|
||||||
| length have >= need = return True
|
| length have >= need = return True
|
||||||
| otherwise = notEnoughCopies key need have skip bad
|
| otherwise = notEnoughCopies key need have skip bad
|
||||||
helper bad have (r:rs)
|
helper bad have (r:rs)
|
||||||
| length have >= need = return True
|
| length have >= need = return True
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
let u = Remote.uuid r
|
let u = Remote.uuid r
|
||||||
let duplicate = u `elem` have
|
let duplicate = u `elem` have
|
||||||
haskey <- Remote.hasKey r key
|
haskey <- Remote.hasKey r key
|
||||||
case (duplicate, haskey) of
|
case (duplicate, haskey) of
|
||||||
(False, Right True) -> helper bad (u:have) rs
|
(False, Right True) -> helper bad (u:have) rs
|
||||||
(False, Left _) -> helper (r:bad) have rs
|
(False, Left _) -> helper (r:bad) have rs
|
||||||
_ -> helper bad have rs
|
_ -> helper bad have rs
|
||||||
|
|
||||||
notEnoughCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
|
notEnoughCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
|
||||||
notEnoughCopies key need have skip bad = do
|
notEnoughCopies key need have skip bad = do
|
||||||
|
@ -132,6 +132,6 @@ notEnoughCopies key need have skip bad = do
|
||||||
Remote.showLocations key (have++skip)
|
Remote.showLocations key (have++skip)
|
||||||
hint
|
hint
|
||||||
return False
|
return False
|
||||||
where
|
where
|
||||||
unsafe = showNote "unsafe"
|
unsafe = showNote "unsafe"
|
||||||
hint = showLongNote "(Use --force to override this check, or adjust annex.numcopies.)"
|
hint = showLongNote "(Use --force to override this check, or adjust annex.numcopies.)"
|
||||||
|
|
|
@ -29,13 +29,13 @@ start = startUnused "dropunused" perform (performOther gitAnnexBadLocation) (per
|
||||||
|
|
||||||
perform :: Key -> CommandPerform
|
perform :: Key -> CommandPerform
|
||||||
perform key = maybe droplocal dropremote =<< Remote.byName =<< from
|
perform key = maybe droplocal dropremote =<< Remote.byName =<< from
|
||||||
where
|
where
|
||||||
dropremote r = do
|
dropremote r = do
|
||||||
showAction $ "from " ++ Remote.name r
|
showAction $ "from " ++ Remote.name r
|
||||||
ok <- Remote.removeKey r key
|
ok <- Remote.removeKey r key
|
||||||
next $ Command.Drop.cleanupRemote key r ok
|
next $ Command.Drop.cleanupRemote key r ok
|
||||||
droplocal = Command.Drop.performLocal key (Just 0) -- force drop
|
droplocal = Command.Drop.performLocal key (Just 0) -- force drop
|
||||||
from = Annex.getField $ Option.name Command.Drop.fromOption
|
from = Annex.getField $ Option.name Command.Drop.fromOption
|
||||||
|
|
||||||
performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform
|
performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform
|
||||||
performOther filespec key = do
|
performOther filespec key = do
|
||||||
|
|
|
@ -29,14 +29,14 @@ formatOption = Option.field [] "format" paramFormat "control format of output"
|
||||||
print0Option :: Option
|
print0Option :: Option
|
||||||
print0Option = Option.Option [] ["print0"] (Option.NoArg set)
|
print0Option = Option.Option [] ["print0"] (Option.NoArg set)
|
||||||
"terminate output with null"
|
"terminate output with null"
|
||||||
where
|
where
|
||||||
set = Annex.setField (Option.name formatOption) "${file}\0"
|
set = Annex.setField (Option.name formatOption) "${file}\0"
|
||||||
|
|
||||||
seek :: [CommandSeek]
|
seek :: [CommandSeek]
|
||||||
seek = [withField formatOption formatconverter $ \f ->
|
seek = [withField formatOption formatconverter $ \f ->
|
||||||
withFilesInGit $ whenAnnexed $ start f]
|
withFilesInGit $ whenAnnexed $ start f]
|
||||||
where
|
where
|
||||||
formatconverter = return . fmap Utility.Format.gen
|
formatconverter = return . fmap Utility.Format.gen
|
||||||
|
|
||||||
start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart
|
start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart
|
||||||
start format file (key, _) = do
|
start format file (key, _) = do
|
||||||
|
@ -50,12 +50,12 @@ start format file (key, _) = do
|
||||||
Utility.Format.format formatter $
|
Utility.Format.format formatter $
|
||||||
M.fromList vars
|
M.fromList vars
|
||||||
stop
|
stop
|
||||||
where
|
where
|
||||||
vars =
|
vars =
|
||||||
[ ("file", file)
|
[ ("file", file)
|
||||||
, ("key", key2file key)
|
, ("key", key2file key)
|
||||||
, ("backend", keyBackendName key)
|
, ("backend", keyBackendName key)
|
||||||
, ("bytesize", size show)
|
, ("bytesize", size show)
|
||||||
, ("humansize", size $ roughSize storageUnits True)
|
, ("humansize", size $ roughSize storageUnits True)
|
||||||
]
|
]
|
||||||
size c = maybe "unknown" c $ keySize key
|
size c = maybe "unknown" c $ keySize key
|
||||||
|
|
160
Command/Fsck.hs
160
Command/Fsck.hs
|
@ -78,22 +78,22 @@ withIncremental = withValue $ do
|
||||||
(True, _, _) ->
|
(True, _, _) ->
|
||||||
maybe startIncremental (return . ContIncremental . Just)
|
maybe startIncremental (return . ContIncremental . Just)
|
||||||
=<< getStartTime
|
=<< getStartTime
|
||||||
where
|
where
|
||||||
startIncremental = do
|
startIncremental = do
|
||||||
recordStartTime
|
recordStartTime
|
||||||
return StartIncremental
|
return StartIncremental
|
||||||
|
|
||||||
checkschedule Nothing = error "bad --incremental-schedule value"
|
checkschedule Nothing = error "bad --incremental-schedule value"
|
||||||
checkschedule (Just delta) = do
|
checkschedule (Just delta) = do
|
||||||
Annex.addCleanup "" $ do
|
Annex.addCleanup "" $ do
|
||||||
v <- getStartTime
|
v <- getStartTime
|
||||||
case v of
|
case v of
|
||||||
Nothing -> noop
|
Nothing -> noop
|
||||||
Just started -> do
|
Just started -> do
|
||||||
now <- liftIO getPOSIXTime
|
now <- liftIO getPOSIXTime
|
||||||
when (now - realToFrac started >= delta) $
|
when (now - realToFrac started >= delta) $
|
||||||
resetStartTime
|
resetStartTime
|
||||||
return True
|
return True
|
||||||
|
|
||||||
start :: Maybe Remote -> Incremental -> FilePath -> (Key, Backend) -> CommandStart
|
start :: Maybe Remote -> Incremental -> FilePath -> (Key, Backend) -> CommandStart
|
||||||
start from inc file (key, backend) = do
|
start from inc file (key, backend) = do
|
||||||
|
@ -101,8 +101,8 @@ start from inc file (key, backend) = do
|
||||||
case from of
|
case from of
|
||||||
Nothing -> go $ perform key file backend numcopies
|
Nothing -> go $ perform key file backend numcopies
|
||||||
Just r -> go $ performRemote key file backend numcopies r
|
Just r -> go $ performRemote key file backend numcopies r
|
||||||
where
|
where
|
||||||
go = runFsck inc file key
|
go = runFsck inc file key
|
||||||
|
|
||||||
perform :: Key -> FilePath -> Backend -> Maybe Int -> Annex Bool
|
perform :: Key -> FilePath -> Backend -> Maybe Int -> Annex Bool
|
||||||
perform key file backend numcopies = check
|
perform key file backend numcopies = check
|
||||||
|
@ -119,48 +119,48 @@ perform key file backend numcopies = check
|
||||||
performRemote :: Key -> FilePath -> Backend -> Maybe Int -> Remote -> Annex Bool
|
performRemote :: Key -> FilePath -> Backend -> Maybe Int -> Remote -> Annex Bool
|
||||||
performRemote key file backend numcopies remote =
|
performRemote key file backend numcopies remote =
|
||||||
dispatch =<< Remote.hasKey remote key
|
dispatch =<< Remote.hasKey remote key
|
||||||
where
|
where
|
||||||
dispatch (Left err) = do
|
dispatch (Left err) = do
|
||||||
showNote err
|
showNote err
|
||||||
return False
|
return False
|
||||||
dispatch (Right True) = withtmp $ \tmpfile ->
|
dispatch (Right True) = withtmp $ \tmpfile ->
|
||||||
ifM (getfile tmpfile)
|
ifM (getfile tmpfile)
|
||||||
( go True (Just tmpfile)
|
( go True (Just tmpfile)
|
||||||
, go True Nothing
|
, go True Nothing
|
||||||
)
|
)
|
||||||
dispatch (Right False) = go False Nothing
|
dispatch (Right False) = go False Nothing
|
||||||
go present localcopy = check
|
go present localcopy = check
|
||||||
[ verifyLocationLogRemote key file remote present
|
[ verifyLocationLogRemote key file remote present
|
||||||
, checkKeySizeRemote key remote localcopy
|
, checkKeySizeRemote key remote localcopy
|
||||||
, checkBackendRemote backend key remote localcopy
|
, checkBackendRemote backend key remote localcopy
|
||||||
, checkKeyNumCopies key file numcopies
|
, checkKeyNumCopies key file numcopies
|
||||||
]
|
]
|
||||||
withtmp a = do
|
withtmp a = do
|
||||||
pid <- liftIO getProcessID
|
pid <- liftIO getProcessID
|
||||||
t <- fromRepo gitAnnexTmpDir
|
t <- fromRepo gitAnnexTmpDir
|
||||||
createAnnexDirectory t
|
createAnnexDirectory t
|
||||||
let tmp = t </> "fsck" ++ show pid ++ "." ++ keyFile key
|
let tmp = t </> "fsck" ++ show pid ++ "." ++ keyFile key
|
||||||
let cleanup = liftIO $ catchIO (removeFile tmp) (const noop)
|
let cleanup = liftIO $ catchIO (removeFile tmp) (const noop)
|
||||||
cleanup
|
cleanup
|
||||||
cleanup `after` a tmp
|
cleanup `after` a tmp
|
||||||
getfile tmp =
|
getfile tmp =
|
||||||
ifM (Remote.retrieveKeyFileCheap remote key tmp)
|
ifM (Remote.retrieveKeyFileCheap remote key tmp)
|
||||||
( return True
|
( return True
|
||||||
, ifM (Annex.getState Annex.fast)
|
, ifM (Annex.getState Annex.fast)
|
||||||
( return False
|
( return False
|
||||||
, Remote.retrieveKeyFile remote key Nothing tmp
|
, Remote.retrieveKeyFile remote key Nothing tmp
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
{- To fsck a bare repository, fsck each key in the location log. -}
|
{- To fsck a bare repository, fsck each key in the location log. -}
|
||||||
withBarePresentKeys :: (Key -> CommandStart) -> CommandSeek
|
withBarePresentKeys :: (Key -> CommandStart) -> CommandSeek
|
||||||
withBarePresentKeys a params = isBareRepo >>= go
|
withBarePresentKeys a params = isBareRepo >>= go
|
||||||
where
|
where
|
||||||
go False = return []
|
go False = return []
|
||||||
go True = do
|
go True = do
|
||||||
unless (null params) $
|
unless (null params) $
|
||||||
error "fsck should be run without parameters in a bare repository"
|
error "fsck should be run without parameters in a bare repository"
|
||||||
map a <$> loggedKeys
|
map a <$> loggedKeys
|
||||||
|
|
||||||
startBare :: Incremental -> Key -> CommandStart
|
startBare :: Incremental -> Key -> CommandStart
|
||||||
startBare inc key = case Backend.maybeLookupBackendName (Types.Key.keyBackendName key) of
|
startBare inc key = case Backend.maybeLookupBackendName (Types.Key.keyBackendName key) of
|
||||||
|
@ -242,10 +242,10 @@ verifyLocationLog' key desc present u bad = do
|
||||||
"but its content is missing."
|
"but its content is missing."
|
||||||
return False
|
return False
|
||||||
_ -> return True
|
_ -> return True
|
||||||
where
|
where
|
||||||
fix s = do
|
fix s = do
|
||||||
showNote "fixing location log"
|
showNote "fixing location log"
|
||||||
bad s
|
bad s
|
||||||
|
|
||||||
{- The size of the data for a key is checked against the size encoded in
|
{- The size of the data for a key is checked against the size encoded in
|
||||||
- the key's metadata, if available. -}
|
- the key's metadata, if available. -}
|
||||||
|
@ -269,19 +269,19 @@ checkKeySizeOr bad key file = case Types.Key.keySize key of
|
||||||
size' <- fromIntegral . fileSize
|
size' <- fromIntegral . fileSize
|
||||||
<$> liftIO (getFileStatus file)
|
<$> liftIO (getFileStatus file)
|
||||||
comparesizes size size'
|
comparesizes size size'
|
||||||
where
|
where
|
||||||
comparesizes a b = do
|
comparesizes a b = do
|
||||||
let same = a == b
|
let same = a == b
|
||||||
unless same $ badsize a b
|
unless same $ badsize a b
|
||||||
return same
|
return same
|
||||||
badsize a b = do
|
badsize a b = do
|
||||||
msg <- bad key
|
msg <- bad key
|
||||||
warning $ concat
|
warning $ concat
|
||||||
[ "Bad file size ("
|
[ "Bad file size ("
|
||||||
, compareSizes storageUnits True a b
|
, compareSizes storageUnits True a b
|
||||||
, "); "
|
, "); "
|
||||||
, msg
|
, msg
|
||||||
]
|
]
|
||||||
|
|
||||||
checkBackend :: Backend -> Key -> Annex Bool
|
checkBackend :: Backend -> Key -> Annex Bool
|
||||||
checkBackend backend key = do
|
checkBackend backend key = do
|
||||||
|
@ -290,8 +290,8 @@ checkBackend backend key = do
|
||||||
|
|
||||||
checkBackendRemote :: Backend -> Key -> Remote -> Maybe FilePath -> Annex Bool
|
checkBackendRemote :: Backend -> Key -> Remote -> Maybe FilePath -> Annex Bool
|
||||||
checkBackendRemote backend key remote = maybe (return True) go
|
checkBackendRemote backend key remote = maybe (return True) go
|
||||||
where
|
where
|
||||||
go = checkBackendOr (badContentRemote remote) backend key
|
go = checkBackendOr (badContentRemote remote) backend key
|
||||||
|
|
||||||
checkBackendOr :: (Key -> Annex String) -> Backend -> Key -> FilePath -> Annex Bool
|
checkBackendOr :: (Key -> Annex String) -> Backend -> Key -> FilePath -> Annex Bool
|
||||||
checkBackendOr bad backend key file =
|
checkBackendOr bad backend key file =
|
||||||
|
@ -414,9 +414,9 @@ recordStartTime = do
|
||||||
t <- modificationTime <$> getFileStatus f
|
t <- modificationTime <$> getFileStatus f
|
||||||
hPutStr h $ showTime $ realToFrac t
|
hPutStr h $ showTime $ realToFrac t
|
||||||
hClose h
|
hClose h
|
||||||
where
|
where
|
||||||
showTime :: POSIXTime -> String
|
showTime :: POSIXTime -> String
|
||||||
showTime = show
|
showTime = show
|
||||||
|
|
||||||
resetStartTime :: Annex ()
|
resetStartTime :: Annex ()
|
||||||
resetStartTime = liftIO . nukeFile =<< fromRepo gitAnnexFsckState
|
resetStartTime = liftIO . nukeFile =<< fromRepo gitAnnexFsckState
|
||||||
|
@ -431,7 +431,7 @@ getStartTime = do
|
||||||
return $ if Just (realToFrac timestamp) == t
|
return $ if Just (realToFrac timestamp) == t
|
||||||
then Just timestamp
|
then Just timestamp
|
||||||
else Nothing
|
else Nothing
|
||||||
where
|
where
|
||||||
readishTime :: String -> Maybe POSIXTime
|
readishTime :: String -> Maybe POSIXTime
|
||||||
readishTime s = utcTimeToPOSIXSeconds <$>
|
readishTime s = utcTimeToPOSIXSeconds <$>
|
||||||
parseTime defaultTimeLocale "%s%Qs" s
|
parseTime defaultTimeLocale "%s%Qs" s
|
||||||
|
|
|
@ -32,10 +32,10 @@ start from file (key, _) = stopUnless ((not <$> inAnnex key) <&&> checkAuto (wan
|
||||||
-- get --from = copy --from
|
-- get --from = copy --from
|
||||||
stopUnless (Command.Move.fromOk src key) $
|
stopUnless (Command.Move.fromOk src key) $
|
||||||
go $ Command.Move.fromPerform src False key file
|
go $ Command.Move.fromPerform src False key file
|
||||||
where
|
where
|
||||||
go a = do
|
go a = do
|
||||||
showStart "get" file
|
showStart "get" file
|
||||||
next a
|
next a
|
||||||
|
|
||||||
perform :: Key -> FilePath -> CommandPerform
|
perform :: Key -> FilePath -> CommandPerform
|
||||||
perform key file = stopUnless (getViaTmp key $ getKeyFile key file) $
|
perform key file = stopUnless (getViaTmp key $ getKeyFile key file) $
|
||||||
|
@ -45,29 +45,29 @@ perform key file = stopUnless (getViaTmp key $ getKeyFile key file) $
|
||||||
- and copy it to here. -}
|
- and copy it to here. -}
|
||||||
getKeyFile :: Key -> FilePath -> FilePath -> Annex Bool
|
getKeyFile :: Key -> FilePath -> FilePath -> Annex Bool
|
||||||
getKeyFile key file dest = dispatch =<< Remote.keyPossibilities key
|
getKeyFile key file dest = dispatch =<< Remote.keyPossibilities key
|
||||||
where
|
where
|
||||||
dispatch [] = do
|
dispatch [] = do
|
||||||
showNote "not available"
|
showNote "not available"
|
||||||
Remote.showLocations key []
|
Remote.showLocations key []
|
||||||
return False
|
return False
|
||||||
dispatch remotes = trycopy remotes remotes
|
dispatch remotes = trycopy remotes remotes
|
||||||
trycopy full [] = do
|
trycopy full [] = do
|
||||||
Remote.showTriedRemotes full
|
Remote.showTriedRemotes full
|
||||||
Remote.showLocations key []
|
Remote.showLocations key []
|
||||||
return False
|
return False
|
||||||
trycopy full (r:rs) =
|
trycopy full (r:rs) =
|
||||||
ifM (probablyPresent r)
|
ifM (probablyPresent r)
|
||||||
( docopy r (trycopy full rs)
|
( docopy r (trycopy full rs)
|
||||||
, trycopy full rs
|
, trycopy full rs
|
||||||
)
|
)
|
||||||
-- This check is to avoid an ugly message if a remote is a
|
-- This check is to avoid an ugly message if a remote is a
|
||||||
-- drive that is not mounted.
|
-- drive that is not mounted.
|
||||||
probablyPresent r
|
probablyPresent r
|
||||||
| Remote.hasKeyCheap r =
|
| Remote.hasKeyCheap r =
|
||||||
either (const False) id <$> Remote.hasKey r key
|
either (const False) id <$> Remote.hasKey r key
|
||||||
| otherwise = return True
|
| otherwise = return True
|
||||||
docopy r continue = do
|
docopy r continue = do
|
||||||
ok <- download (Remote.uuid r) key (Just file) noRetry $ do
|
ok <- download (Remote.uuid r) key (Just file) noRetry $ do
|
||||||
showAction $ "from " ++ Remote.name r
|
showAction $ "from " ++ Remote.name r
|
||||||
Remote.retrieveKeyFile r key (Just file) dest
|
Remote.retrieveKeyFile r key (Just file) dest
|
||||||
if ok then return ok else continue
|
if ok then return ok else continue
|
||||||
|
|
|
@ -47,5 +47,5 @@ showHelp = liftIO $ putStrLn $ unlines
|
||||||
]
|
]
|
||||||
, "Run git-annex without any options for a complete command and option list."
|
, "Run git-annex without any options for a complete command and option list."
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
cmdline c = "\t" ++ cmdname c ++ "\t" ++ cmddesc c
|
cmdline c = "\t" ++ cmdname c ++ "\t" ++ cmddesc c
|
||||||
|
|
|
@ -20,8 +20,8 @@ seek = [withKeys start]
|
||||||
|
|
||||||
start :: Key -> CommandStart
|
start :: Key -> CommandStart
|
||||||
start key = inAnnexSafe key >>= dispatch
|
start key = inAnnexSafe key >>= dispatch
|
||||||
where
|
where
|
||||||
dispatch (Just True) = stop
|
dispatch (Just True) = stop
|
||||||
dispatch (Just False) = exit 1
|
dispatch (Just False) = exit 1
|
||||||
dispatch Nothing = exit 100
|
dispatch Nothing = exit 100
|
||||||
exit n = liftIO $ exitWith $ ExitFailure n
|
exit n = liftIO $ exitWith $ ExitFailure n
|
||||||
|
|
|
@ -22,8 +22,8 @@ start :: [String] -> CommandStart
|
||||||
start ws = do
|
start ws = do
|
||||||
showStart "init" description
|
showStart "init" description
|
||||||
next $ perform description
|
next $ perform description
|
||||||
where
|
where
|
||||||
description = unwords ws
|
description = unwords ws
|
||||||
|
|
||||||
perform :: String -> CommandPerform
|
perform :: String -> CommandPerform
|
||||||
perform description = do
|
perform description = do
|
||||||
|
|
|
@ -40,8 +40,8 @@ start (name:ws) = do
|
||||||
showStart "initremote" name
|
showStart "initremote" name
|
||||||
next $ perform t u name $ M.union config c
|
next $ perform t u name $ M.union config c
|
||||||
|
|
||||||
where
|
where
|
||||||
config = Logs.Remote.keyValToConfig ws
|
config = Logs.Remote.keyValToConfig ws
|
||||||
|
|
||||||
perform :: RemoteType -> UUID -> String -> R.RemoteConfig -> CommandPerform
|
perform :: RemoteType -> UUID -> String -> R.RemoteConfig -> CommandPerform
|
||||||
perform t u name c = do
|
perform t u name c = do
|
||||||
|
@ -59,19 +59,19 @@ findByName :: String -> Annex (UUID, R.RemoteConfig)
|
||||||
findByName name = do
|
findByName name = do
|
||||||
m <- Logs.Remote.readRemoteLog
|
m <- Logs.Remote.readRemoteLog
|
||||||
maybe generate return $ findByName' name m
|
maybe generate return $ findByName' name m
|
||||||
where
|
where
|
||||||
generate = do
|
generate = do
|
||||||
uuid <- liftIO genUUID
|
uuid <- liftIO genUUID
|
||||||
return (uuid, M.insert nameKey name M.empty)
|
return (uuid, M.insert nameKey name M.empty)
|
||||||
|
|
||||||
findByName' :: String -> M.Map UUID R.RemoteConfig -> Maybe (UUID, R.RemoteConfig)
|
findByName' :: String -> M.Map UUID R.RemoteConfig -> Maybe (UUID, R.RemoteConfig)
|
||||||
findByName' n = headMaybe . filter (matching . snd) . M.toList
|
findByName' n = headMaybe . filter (matching . snd) . M.toList
|
||||||
where
|
where
|
||||||
matching c = case M.lookup nameKey c of
|
matching c = case M.lookup nameKey c of
|
||||||
Nothing -> False
|
Nothing -> False
|
||||||
Just n'
|
Just n'
|
||||||
| n' == n -> True
|
| n' == n -> True
|
||||||
| otherwise -> False
|
| otherwise -> False
|
||||||
|
|
||||||
remoteNames :: Annex [String]
|
remoteNames :: Annex [String]
|
||||||
remoteNames = do
|
remoteNames = do
|
||||||
|
@ -81,12 +81,12 @@ remoteNames = do
|
||||||
{- find the specified remote type -}
|
{- find the specified remote type -}
|
||||||
findType :: R.RemoteConfig -> Annex RemoteType
|
findType :: R.RemoteConfig -> Annex RemoteType
|
||||||
findType config = maybe unspecified specified $ M.lookup typeKey config
|
findType config = maybe unspecified specified $ M.lookup typeKey config
|
||||||
where
|
where
|
||||||
unspecified = error "Specify the type of remote with type="
|
unspecified = error "Specify the type of remote with type="
|
||||||
specified s = case filter (findtype s) Remote.remoteTypes of
|
specified s = case filter (findtype s) Remote.remoteTypes of
|
||||||
[] -> error $ "Unknown remote type " ++ s
|
[] -> error $ "Unknown remote type " ++ s
|
||||||
(t:_) -> return t
|
(t:_) -> return t
|
||||||
findtype s i = R.typename i == s
|
findtype s i = R.typename i == s
|
||||||
|
|
||||||
{- The name of a configured remote is stored in its config using this key. -}
|
{- The name of a configured remote is stored in its config using this key. -}
|
||||||
nameKey :: String
|
nameKey :: String
|
||||||
|
|
|
@ -47,9 +47,8 @@ passthruOptions = map odate ["since", "after", "until", "before"] ++
|
||||||
[ Option.field ['n'] "max-count" paramNumber
|
[ Option.field ['n'] "max-count" paramNumber
|
||||||
"limit number of logs displayed"
|
"limit number of logs displayed"
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
odate n = Option.field [] n paramDate $
|
odate n = Option.field [] n paramDate $ "show log " ++ n ++ " date"
|
||||||
"show log " ++ n ++ " date"
|
|
||||||
|
|
||||||
gourceOption :: Option
|
gourceOption :: Option
|
||||||
gourceOption = Option.flag [] "gource" "format output for gource"
|
gourceOption = Option.flag [] "gource" "format output for gource"
|
||||||
|
@ -60,10 +59,10 @@ seek = [withValue Remote.uuidDescriptions $ \m ->
|
||||||
withValue (concat <$> mapM getoption passthruOptions) $ \os ->
|
withValue (concat <$> mapM getoption passthruOptions) $ \os ->
|
||||||
withFlag gourceOption $ \gource ->
|
withFlag gourceOption $ \gource ->
|
||||||
withFilesInGit $ whenAnnexed $ start m zone os gource]
|
withFilesInGit $ whenAnnexed $ start m zone os gource]
|
||||||
where
|
where
|
||||||
getoption o = maybe [] (use o) <$>
|
getoption o = maybe [] (use o) <$>
|
||||||
Annex.getField (Option.name o)
|
Annex.getField (Option.name o)
|
||||||
use o v = [Param ("--" ++ Option.name o), Param v]
|
use o v = [Param ("--" ++ Option.name o), Param v]
|
||||||
|
|
||||||
start :: M.Map UUID String -> TimeZone -> [CommandParam] -> Bool ->
|
start :: M.Map UUID String -> TimeZone -> [CommandParam] -> Bool ->
|
||||||
FilePath -> (Key, Backend) -> CommandStart
|
FilePath -> (Key, Backend) -> CommandStart
|
||||||
|
@ -72,41 +71,41 @@ start m zone os gource file (key, _) = do
|
||||||
-- getLog produces a zombie; reap it
|
-- getLog produces a zombie; reap it
|
||||||
liftIO reapZombies
|
liftIO reapZombies
|
||||||
stop
|
stop
|
||||||
where
|
where
|
||||||
output
|
output
|
||||||
| gource = gourceOutput lookupdescription file
|
| gource = gourceOutput lookupdescription file
|
||||||
| otherwise = normalOutput lookupdescription file zone
|
| otherwise = normalOutput lookupdescription file zone
|
||||||
lookupdescription u = fromMaybe (fromUUID u) $ M.lookup u m
|
lookupdescription u = fromMaybe (fromUUID u) $ M.lookup u m
|
||||||
|
|
||||||
showLog :: Outputter -> [RefChange] -> Annex ()
|
showLog :: Outputter -> [RefChange] -> Annex ()
|
||||||
showLog outputter ps = do
|
showLog outputter ps = do
|
||||||
sets <- mapM (getset newref) ps
|
sets <- mapM (getset newref) ps
|
||||||
previous <- maybe (return genesis) (getset oldref) (lastMaybe ps)
|
previous <- maybe (return genesis) (getset oldref) (lastMaybe ps)
|
||||||
sequence_ $ compareChanges outputter $ sets ++ [previous]
|
sequence_ $ compareChanges outputter $ sets ++ [previous]
|
||||||
where
|
where
|
||||||
genesis = (0, S.empty)
|
genesis = (0, S.empty)
|
||||||
getset select change = do
|
getset select change = do
|
||||||
s <- S.fromList <$> get (select change)
|
s <- S.fromList <$> get (select change)
|
||||||
return (changetime change, s)
|
return (changetime change, s)
|
||||||
get ref = map toUUID . Logs.Presence.getLog . L.unpack <$>
|
get ref = map toUUID . Logs.Presence.getLog . L.unpack <$>
|
||||||
catObject ref
|
catObject ref
|
||||||
|
|
||||||
normalOutput :: (UUID -> String) -> FilePath -> TimeZone -> Outputter
|
normalOutput :: (UUID -> String) -> FilePath -> TimeZone -> Outputter
|
||||||
normalOutput lookupdescription file zone present ts us =
|
normalOutput lookupdescription file zone present ts us =
|
||||||
liftIO $ mapM_ (putStrLn . format) us
|
liftIO $ mapM_ (putStrLn . format) us
|
||||||
where
|
where
|
||||||
time = showTimeStamp zone ts
|
time = showTimeStamp zone ts
|
||||||
addel = if present then "+" else "-"
|
addel = if present then "+" else "-"
|
||||||
format u = unwords [ addel, time, file, "|",
|
format u = unwords [ addel, time, file, "|",
|
||||||
fromUUID u ++ " -- " ++ lookupdescription u ]
|
fromUUID u ++ " -- " ++ lookupdescription u ]
|
||||||
|
|
||||||
gourceOutput :: (UUID -> String) -> FilePath -> Outputter
|
gourceOutput :: (UUID -> String) -> FilePath -> Outputter
|
||||||
gourceOutput lookupdescription file present ts us =
|
gourceOutput lookupdescription file present ts us =
|
||||||
liftIO $ mapM_ (putStrLn . intercalate "|" . format) us
|
liftIO $ mapM_ (putStrLn . intercalate "|" . format) us
|
||||||
where
|
where
|
||||||
time = takeWhile isDigit $ show ts
|
time = takeWhile isDigit $ show ts
|
||||||
addel = if present then "A" else "M"
|
addel = if present then "A" else "M"
|
||||||
format u = [ time, lookupdescription u, addel, file ]
|
format u = [ time, lookupdescription u, addel, file ]
|
||||||
|
|
||||||
{- Generates a display of the changes (which are ordered with newest first),
|
{- Generates a display of the changes (which are ordered with newest first),
|
||||||
- by comparing each change with the previous change.
|
- by comparing each change with the previous change.
|
||||||
|
@ -114,12 +113,12 @@ gourceOutput lookupdescription file present ts us =
|
||||||
- removed. -}
|
- removed. -}
|
||||||
compareChanges :: Ord a => (Bool -> POSIXTime -> [a] -> b) -> [(POSIXTime, S.Set a)] -> [b]
|
compareChanges :: Ord a => (Bool -> POSIXTime -> [a] -> b) -> [(POSIXTime, S.Set a)] -> [b]
|
||||||
compareChanges format changes = concatMap diff $ zip changes (drop 1 changes)
|
compareChanges format changes = concatMap diff $ zip changes (drop 1 changes)
|
||||||
where
|
where
|
||||||
diff ((ts, new), (_, old)) =
|
diff ((ts, new), (_, old)) =
|
||||||
[format True ts added, format False ts removed]
|
[format True ts added, format False ts removed]
|
||||||
where
|
where
|
||||||
added = S.toList $ S.difference new old
|
added = S.toList $ S.difference new old
|
||||||
removed = S.toList $ S.difference old new
|
removed = S.toList $ S.difference old new
|
||||||
|
|
||||||
{- Gets the git log for a given location log file.
|
{- Gets the git log for a given location log file.
|
||||||
-
|
-
|
||||||
|
@ -148,21 +147,21 @@ getLog key os = do
|
||||||
|
|
||||||
readLog :: [String] -> [RefChange]
|
readLog :: [String] -> [RefChange]
|
||||||
readLog = mapMaybe (parse . lines)
|
readLog = mapMaybe (parse . lines)
|
||||||
where
|
where
|
||||||
parse (ts:raw:[]) = let (old, new) = parseRaw raw in
|
parse (ts:raw:[]) = let (old, new) = parseRaw raw in
|
||||||
Just RefChange
|
Just RefChange
|
||||||
{ changetime = parseTimeStamp ts
|
{ changetime = parseTimeStamp ts
|
||||||
, oldref = old
|
, oldref = old
|
||||||
, newref = new
|
, newref = new
|
||||||
}
|
}
|
||||||
parse _ = Nothing
|
parse _ = Nothing
|
||||||
|
|
||||||
-- Parses something like ":100644 100644 oldsha newsha M"
|
-- Parses something like ":100644 100644 oldsha newsha M"
|
||||||
parseRaw :: String -> (Git.Ref, Git.Ref)
|
parseRaw :: String -> (Git.Ref, Git.Ref)
|
||||||
parseRaw l = go $ words l
|
parseRaw l = go $ words l
|
||||||
where
|
where
|
||||||
go (_:_:oldsha:newsha:_) = (Git.Ref oldsha, Git.Ref newsha)
|
go (_:_:oldsha:newsha:_) = (Git.Ref oldsha, Git.Ref newsha)
|
||||||
go _ = error $ "unable to parse git log output: " ++ l
|
go _ = error $ "unable to parse git log output: " ++ l
|
||||||
|
|
||||||
parseTimeStamp :: String -> POSIXTime
|
parseTimeStamp :: String -> POSIXTime
|
||||||
parseTimeStamp = utcTimeToPOSIXSeconds . fromMaybe (error "bad timestamp") .
|
parseTimeStamp = utcTimeToPOSIXSeconds . fromMaybe (error "bad timestamp") .
|
||||||
|
|
159
Command/Map.hs
159
Command/Map.hs
|
@ -63,14 +63,13 @@ start = do
|
||||||
-}
|
-}
|
||||||
drawMap :: [Git.Repo] -> M.Map UUID String -> [UUID] -> String
|
drawMap :: [Git.Repo] -> M.Map UUID String -> [UUID] -> String
|
||||||
drawMap rs umap ts = Dot.graph $ repos ++ trusted ++ others
|
drawMap rs umap ts = Dot.graph $ repos ++ trusted ++ others
|
||||||
where
|
where
|
||||||
repos = map (node umap rs) rs
|
repos = map (node umap rs) rs
|
||||||
ruuids = ts ++ map getUncachedUUID rs
|
ruuids = ts ++ map getUncachedUUID rs
|
||||||
others = map (unreachable . uuidnode) $
|
others = map (unreachable . uuidnode) $
|
||||||
filter (`notElem` ruuids) (M.keys umap)
|
filter (`notElem` ruuids) (M.keys umap)
|
||||||
trusted = map (trustworthy . uuidnode) ts
|
trusted = map (trustworthy . uuidnode) ts
|
||||||
uuidnode u = Dot.graphNode (fromUUID u) $
|
uuidnode u = Dot.graphNode (fromUUID u) $ M.findWithDefault "" u umap
|
||||||
M.findWithDefault "" u umap
|
|
||||||
|
|
||||||
hostname :: Git.Repo -> String
|
hostname :: Git.Repo -> String
|
||||||
hostname r
|
hostname r
|
||||||
|
@ -86,9 +85,9 @@ repoName :: M.Map UUID String -> Git.Repo -> String
|
||||||
repoName umap r
|
repoName umap r
|
||||||
| repouuid == NoUUID = fallback
|
| repouuid == NoUUID = fallback
|
||||||
| otherwise = M.findWithDefault fallback repouuid umap
|
| otherwise = M.findWithDefault fallback repouuid umap
|
||||||
where
|
where
|
||||||
repouuid = getUncachedUUID r
|
repouuid = getUncachedUUID r
|
||||||
fallback = fromMaybe "unknown" $ Git.remoteName r
|
fallback = fromMaybe "unknown" $ Git.remoteName r
|
||||||
|
|
||||||
{- A unique id for the node for a repo. Uses the annex.uuid if available. -}
|
{- A unique id for the node for a repo. Uses the annex.uuid if available. -}
|
||||||
nodeId :: Git.Repo -> String
|
nodeId :: Git.Repo -> String
|
||||||
|
@ -100,32 +99,32 @@ nodeId r =
|
||||||
{- A node representing a repo. -}
|
{- A node representing a repo. -}
|
||||||
node :: M.Map UUID String -> [Git.Repo] -> Git.Repo -> String
|
node :: M.Map UUID String -> [Git.Repo] -> Git.Repo -> String
|
||||||
node umap fullinfo r = unlines $ n:edges
|
node umap fullinfo r = unlines $ n:edges
|
||||||
where
|
where
|
||||||
n = Dot.subGraph (hostname r) (basehostname r) "lightblue" $
|
n = Dot.subGraph (hostname r) (basehostname r) "lightblue" $
|
||||||
decorate $ Dot.graphNode (nodeId r) (repoName umap r)
|
decorate $ Dot.graphNode (nodeId r) (repoName umap r)
|
||||||
edges = map (edge umap fullinfo r) (Git.remotes r)
|
edges = map (edge umap fullinfo r) (Git.remotes r)
|
||||||
decorate
|
decorate
|
||||||
| Git.config r == M.empty = unreachable
|
| Git.config r == M.empty = unreachable
|
||||||
| otherwise = reachable
|
| otherwise = reachable
|
||||||
|
|
||||||
{- An edge between two repos. The second repo is a remote of the first. -}
|
{- An edge between two repos. The second repo is a remote of the first. -}
|
||||||
edge :: M.Map UUID String -> [Git.Repo] -> Git.Repo -> Git.Repo -> String
|
edge :: M.Map UUID String -> [Git.Repo] -> Git.Repo -> Git.Repo -> String
|
||||||
edge umap fullinfo from to =
|
edge umap fullinfo from to =
|
||||||
Dot.graphEdge (nodeId from) (nodeId fullto) edgename
|
Dot.graphEdge (nodeId from) (nodeId fullto) edgename
|
||||||
where
|
where
|
||||||
-- get the full info for the remote, to get its UUID
|
-- get the full info for the remote, to get its UUID
|
||||||
fullto = findfullinfo to
|
fullto = findfullinfo to
|
||||||
findfullinfo n =
|
findfullinfo n =
|
||||||
case filter (same n) fullinfo of
|
case filter (same n) fullinfo of
|
||||||
[] -> n
|
[] -> n
|
||||||
(n':_) -> n'
|
(n':_) -> n'
|
||||||
{- Only name an edge if the name is different than the name
|
{- Only name an edge if the name is different than the name
|
||||||
- that will be used for the destination node, and is
|
- that will be used for the destination node, and is
|
||||||
- different from its hostname. (This reduces visual clutter.) -}
|
- different from its hostname. (This reduces visual clutter.) -}
|
||||||
edgename = maybe Nothing calcname $ Git.remoteName to
|
edgename = maybe Nothing calcname $ Git.remoteName to
|
||||||
calcname n
|
calcname n
|
||||||
| n `elem` [repoName umap fullto, hostname fullto] = Nothing
|
| n `elem` [repoName umap fullto, hostname fullto] = Nothing
|
||||||
| otherwise = Just n
|
| otherwise = Just n
|
||||||
|
|
||||||
unreachable :: String -> String
|
unreachable :: String -> String
|
||||||
unreachable = Dot.fillColor "red"
|
unreachable = Dot.fillColor "red"
|
||||||
|
@ -165,11 +164,10 @@ same a b
|
||||||
| both Git.repoIsUrl && neither Git.repoIsSsh = matching show
|
| both Git.repoIsUrl && neither Git.repoIsSsh = matching show
|
||||||
| neither Git.repoIsSsh = matching Git.repoPath
|
| neither Git.repoIsSsh = matching Git.repoPath
|
||||||
| otherwise = False
|
| otherwise = False
|
||||||
|
where
|
||||||
where
|
matching t = t a == t b
|
||||||
matching t = t a == t b
|
both t = t a && t b
|
||||||
both t = t a && t b
|
neither t = not (t a) && not (t b)
|
||||||
neither t = not (t a) && not (t b)
|
|
||||||
|
|
||||||
{- reads the config of a remote, with progress display -}
|
{- reads the config of a remote, with progress display -}
|
||||||
scan :: Git.Repo -> Annex Git.Repo
|
scan :: Git.Repo -> Annex Git.Repo
|
||||||
|
@ -192,50 +190,49 @@ tryScan r
|
||||||
| Git.repoIsSsh r = sshscan
|
| Git.repoIsSsh r = sshscan
|
||||||
| Git.repoIsUrl r = return Nothing
|
| Git.repoIsUrl r = return Nothing
|
||||||
| otherwise = safely $ Git.Config.read r
|
| otherwise = safely $ Git.Config.read r
|
||||||
where
|
where
|
||||||
safely a = do
|
safely a = do
|
||||||
result <- liftIO (try a :: IO (Either SomeException Git.Repo))
|
result <- liftIO (try a :: IO (Either SomeException Git.Repo))
|
||||||
case result of
|
case result of
|
||||||
Left _ -> return Nothing
|
Left _ -> return Nothing
|
||||||
Right r' -> return $ Just r'
|
Right r' -> return $ Just r'
|
||||||
pipedconfig cmd params = safely $
|
pipedconfig cmd params = safely $
|
||||||
withHandle StdoutHandle createProcessSuccess p $
|
withHandle StdoutHandle createProcessSuccess p $
|
||||||
Git.Config.hRead r
|
Git.Config.hRead r
|
||||||
where
|
where
|
||||||
p = proc cmd $ toCommand params
|
p = proc cmd $ toCommand params
|
||||||
|
|
||||||
configlist =
|
configlist = onRemote r (pipedconfig, Nothing) "configlist" [] []
|
||||||
onRemote r (pipedconfig, Nothing) "configlist" [] []
|
manualconfiglist = do
|
||||||
manualconfiglist = do
|
sshparams <- sshToRepo r [Param sshcmd]
|
||||||
sshparams <- sshToRepo r [Param sshcmd]
|
liftIO $ pipedconfig "ssh" sshparams
|
||||||
liftIO $ pipedconfig "ssh" sshparams
|
where
|
||||||
where
|
sshcmd = cddir ++ " && " ++
|
||||||
sshcmd = cddir ++ " && " ++
|
"git config --null --list"
|
||||||
"git config --null --list"
|
dir = Git.repoPath r
|
||||||
dir = Git.repoPath r
|
cddir
|
||||||
cddir
|
| "/~" `isPrefixOf` dir =
|
||||||
| "/~" `isPrefixOf` dir =
|
let (userhome, reldir) = span (/= '/') (drop 1 dir)
|
||||||
let (userhome, reldir) = span (/= '/') (drop 1 dir)
|
in "cd " ++ userhome ++ " && cd " ++ shellEscape (drop 1 reldir)
|
||||||
in "cd " ++ userhome ++ " && cd " ++ shellEscape (drop 1 reldir)
|
| otherwise = "cd " ++ shellEscape dir
|
||||||
| otherwise = "cd " ++ shellEscape dir
|
|
||||||
|
|
||||||
-- First, try sshing and running git config manually,
|
-- First, try sshing and running git config manually,
|
||||||
-- only fall back to git-annex-shell configlist if that
|
-- only fall back to git-annex-shell configlist if that
|
||||||
-- fails.
|
-- fails.
|
||||||
--
|
--
|
||||||
-- This is done for two reasons, first I'd like this
|
-- This is done for two reasons, first I'd like this
|
||||||
-- subcommand to be usable on non-git-annex repos.
|
-- subcommand to be usable on non-git-annex repos.
|
||||||
-- Secondly, configlist doesn't include information about
|
-- Secondly, configlist doesn't include information about
|
||||||
-- the remote's remotes.
|
-- the remote's remotes.
|
||||||
sshscan = do
|
sshscan = do
|
||||||
sshnote
|
sshnote
|
||||||
v <- manualconfiglist
|
v <- manualconfiglist
|
||||||
case v of
|
case v of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
sshnote
|
sshnote
|
||||||
configlist
|
configlist
|
||||||
ok -> return ok
|
ok -> return ok
|
||||||
|
|
||||||
sshnote = do
|
sshnote = do
|
||||||
showAction "sshing"
|
showAction "sshing"
|
||||||
showOutput
|
showOutput
|
||||||
|
|
|
@ -31,9 +31,9 @@ start file (key, oldbackend) = do
|
||||||
showStart "migrate" file
|
showStart "migrate" file
|
||||||
next $ perform file key oldbackend newbackend
|
next $ perform file key oldbackend newbackend
|
||||||
else stop
|
else stop
|
||||||
where
|
where
|
||||||
choosebackend Nothing = Prelude.head <$> orderedList
|
choosebackend Nothing = Prelude.head <$> orderedList
|
||||||
choosebackend (Just backend) = return backend
|
choosebackend (Just backend) = return backend
|
||||||
|
|
||||||
{- Checks if a key is upgradable to a newer representation. -}
|
{- Checks if a key is upgradable to a newer representation. -}
|
||||||
{- Ideally, all keys have file size metadata. Old keys may not. -}
|
{- Ideally, all keys have file size metadata. Old keys may not. -}
|
||||||
|
@ -49,10 +49,10 @@ perform file oldkey oldbackend newbackend = do
|
||||||
( maybe stop go =<< genkey
|
( maybe stop go =<< genkey
|
||||||
, stop
|
, stop
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
go newkey = stopUnless (Command.ReKey.linkKey oldkey newkey) $
|
go newkey = stopUnless (Command.ReKey.linkKey oldkey newkey) $
|
||||||
next $ Command.ReKey.cleanup file oldkey newkey
|
next $ Command.ReKey.cleanup file oldkey newkey
|
||||||
genkey = do
|
genkey = do
|
||||||
content <- inRepo $ gitAnnexLocation oldkey
|
content <- inRepo $ gitAnnexLocation oldkey
|
||||||
let source = KeySource { keyFilename = file, contentLocation = content }
|
let source = KeySource { keyFilename = file, contentLocation = content }
|
||||||
liftM fst <$> genKey source (Just newbackend)
|
liftM fst <$> genKey source (Just newbackend)
|
||||||
|
|
|
@ -44,9 +44,9 @@ start to from move file (key, _) = do
|
||||||
(Nothing, Just dest) -> toStart dest move file key
|
(Nothing, Just dest) -> toStart dest move file key
|
||||||
(Just src, Nothing) -> fromStart src move file key
|
(Just src, Nothing) -> fromStart src move file key
|
||||||
(_ , _) -> error "only one of --from or --to can be specified"
|
(_ , _) -> error "only one of --from or --to can be specified"
|
||||||
where
|
where
|
||||||
noAuto = when move $ whenM (Annex.getState Annex.auto) $ error
|
noAuto = when move $ whenM (Annex.getState Annex.auto) $ error
|
||||||
"--auto is not supported for move"
|
"--auto is not supported for move"
|
||||||
|
|
||||||
showMoveAction :: Bool -> FilePath -> Annex ()
|
showMoveAction :: Bool -> FilePath -> Annex ()
|
||||||
showMoveAction True file = showStart "move" file
|
showMoveAction True file = showStart "move" file
|
||||||
|
@ -98,15 +98,15 @@ toPerform dest move key file = moveLock move key $ do
|
||||||
warning "This could have failed because --fast is enabled."
|
warning "This could have failed because --fast is enabled."
|
||||||
stop
|
stop
|
||||||
Right True -> finish False
|
Right True -> finish False
|
||||||
where
|
where
|
||||||
finish remotechanged = do
|
finish remotechanged = do
|
||||||
when remotechanged $
|
when remotechanged $
|
||||||
Remote.logStatus dest key InfoPresent
|
Remote.logStatus dest key InfoPresent
|
||||||
if move
|
if move
|
||||||
then do
|
then do
|
||||||
whenM (inAnnex key) $ removeAnnex key
|
whenM (inAnnex key) $ removeAnnex key
|
||||||
next $ Command.Drop.cleanupLocal key
|
next $ Command.Drop.cleanupLocal key
|
||||||
else next $ return True
|
else next $ return True
|
||||||
|
|
||||||
{- Moves (or copies) the content of an annexed file from a remote
|
{- Moves (or copies) the content of an annexed file from a remote
|
||||||
- to the current repository.
|
- to the current repository.
|
||||||
|
@ -118,35 +118,37 @@ fromStart :: Remote -> Bool -> FilePath -> Key -> CommandStart
|
||||||
fromStart src move file key
|
fromStart src move file key
|
||||||
| move = go
|
| move = go
|
||||||
| otherwise = stopUnless (not <$> inAnnex key) go
|
| otherwise = stopUnless (not <$> inAnnex key) go
|
||||||
where
|
where
|
||||||
go = stopUnless (fromOk src key) $ do
|
go = stopUnless (fromOk src key) $ do
|
||||||
showMoveAction move file
|
showMoveAction move file
|
||||||
next $ fromPerform src move key file
|
next $ fromPerform src move key file
|
||||||
|
|
||||||
fromOk :: Remote -> Key -> Annex Bool
|
fromOk :: Remote -> Key -> Annex Bool
|
||||||
fromOk src key
|
fromOk src key
|
||||||
| Remote.hasKeyCheap src =
|
| Remote.hasKeyCheap src =
|
||||||
either (const expensive) return =<< Remote.hasKey src key
|
either (const expensive) return =<< Remote.hasKey src key
|
||||||
| otherwise = expensive
|
| otherwise = expensive
|
||||||
where
|
where
|
||||||
expensive = do
|
expensive = do
|
||||||
u <- getUUID
|
u <- getUUID
|
||||||
remotes <- Remote.keyPossibilities key
|
remotes <- Remote.keyPossibilities key
|
||||||
return $ u /= Remote.uuid src && elem src remotes
|
return $ u /= Remote.uuid src && elem src remotes
|
||||||
|
|
||||||
fromPerform :: Remote -> Bool -> Key -> FilePath -> CommandPerform
|
fromPerform :: Remote -> Bool -> Key -> FilePath -> CommandPerform
|
||||||
fromPerform src move key file = moveLock move key $
|
fromPerform src move key file = moveLock move key $
|
||||||
ifM (inAnnex key)
|
ifM (inAnnex key)
|
||||||
( handle move True
|
( handle move True
|
||||||
, handle move =<< go
|
, handle move =<< go
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
go = download (Remote.uuid src) key (Just file) noRetry $ do
|
go = download (Remote.uuid src) key (Just file) noRetry $ do
|
||||||
showAction $ "from " ++ Remote.name src
|
showAction $ "from " ++ Remote.name src
|
||||||
getViaTmp key $ Remote.retrieveKeyFile src key (Just file)
|
getViaTmp key $ Remote.retrieveKeyFile src key (Just file)
|
||||||
handle _ False = stop -- failed
|
handle _ False = stop -- failed
|
||||||
handle False True = next $ return True -- copy complete
|
handle False True = next $ return True -- copy complete
|
||||||
handle True True = do -- finish moving
|
handle True True = do -- finish moving
|
||||||
ok <- Remote.removeKey src key
|
ok <- Remote.removeKey src key
|
||||||
next $ Command.Drop.cleanupRemote key src ok
|
next $ Command.Drop.cleanupRemote key src ok
|
||||||
|
|
||||||
{- Locks a key in order for it to be moved.
|
{- Locks a key in order for it to be moved.
|
||||||
- No lock is needed when a key is being copied. -}
|
- No lock is needed when a key is being copied. -}
|
||||||
|
|
|
@ -25,13 +25,13 @@ seek = [withPairs start]
|
||||||
|
|
||||||
start :: (FilePath, String) -> CommandStart
|
start :: (FilePath, String) -> CommandStart
|
||||||
start (file, keyname) = ifAnnexed file go stop
|
start (file, keyname) = ifAnnexed file go stop
|
||||||
where
|
where
|
||||||
newkey = fromMaybe (error "bad key") $ file2key keyname
|
newkey = fromMaybe (error "bad key") $ file2key keyname
|
||||||
go (oldkey, _)
|
go (oldkey, _)
|
||||||
| oldkey == newkey = stop
|
| oldkey == newkey = stop
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
showStart "rekey" file
|
showStart "rekey" file
|
||||||
next $ perform file oldkey newkey
|
next $ perform file oldkey newkey
|
||||||
|
|
||||||
perform :: FilePath -> Key -> Key -> CommandPerform
|
perform :: FilePath -> Key -> Key -> CommandPerform
|
||||||
perform file oldkey newkey = do
|
perform file oldkey newkey = do
|
||||||
|
|
|
@ -27,10 +27,10 @@ start (src:dest:[])
|
||||||
ifAnnexed src
|
ifAnnexed src
|
||||||
(error $ "cannot used annexed file as src: " ++ src)
|
(error $ "cannot used annexed file as src: " ++ src)
|
||||||
go
|
go
|
||||||
where
|
where
|
||||||
go = do
|
go = do
|
||||||
showStart "reinject" dest
|
showStart "reinject" dest
|
||||||
next $ whenAnnexed (perform src) dest
|
next $ whenAnnexed (perform src) dest
|
||||||
start _ = error "specify a src file and a dest file"
|
start _ = error "specify a src file and a dest file"
|
||||||
|
|
||||||
perform :: FilePath -> FilePath -> (Key, Backend) -> CommandPerform
|
perform :: FilePath -> FilePath -> (Key, Backend) -> CommandPerform
|
||||||
|
@ -43,14 +43,14 @@ perform src _dest (key, backend) = do
|
||||||
next $ cleanup key
|
next $ cleanup key
|
||||||
, error "not reinjecting"
|
, error "not reinjecting"
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
-- the file might be on a different filesystem,
|
-- the file might be on a different filesystem,
|
||||||
-- so mv is used rather than simply calling
|
-- so mv is used rather than simply calling
|
||||||
-- moveToObjectDir; disk space is also
|
-- moveToObjectDir; disk space is also
|
||||||
-- checked this way.
|
-- checked this way.
|
||||||
move = getViaTmp key $ \tmp ->
|
move = getViaTmp key $ \tmp ->
|
||||||
liftIO $ boolSystem "mv" [File src, File tmp]
|
liftIO $ boolSystem "mv" [File src, File tmp]
|
||||||
reject = const $ return "wrong file?"
|
reject = const $ return "wrong file?"
|
||||||
|
|
||||||
cleanup :: Key -> CommandCleanup
|
cleanup :: Key -> CommandCleanup
|
||||||
cleanup key = do
|
cleanup key = do
|
||||||
|
|
|
@ -114,10 +114,10 @@ nojson a _ = a
|
||||||
|
|
||||||
showStat :: Stat -> StatState ()
|
showStat :: Stat -> StatState ()
|
||||||
showStat s = maybe noop calc =<< s
|
showStat s = maybe noop calc =<< s
|
||||||
where
|
where
|
||||||
calc (desc, a) = do
|
calc (desc, a) = do
|
||||||
(lift . showHeader) desc
|
(lift . showHeader) desc
|
||||||
lift . showRaw =<< a
|
lift . showRaw =<< a
|
||||||
|
|
||||||
supported_backends :: Stat
|
supported_backends :: Stat
|
||||||
supported_backends = stat "supported backends" $ json unwords $
|
supported_backends = stat "supported backends" $ json unwords $
|
||||||
|
@ -133,8 +133,8 @@ remote_list level = stat n $ nojson $ lift $ do
|
||||||
rs <- fst <$> trustPartition level us
|
rs <- fst <$> trustPartition level us
|
||||||
s <- prettyPrintUUIDs n rs
|
s <- prettyPrintUUIDs n rs
|
||||||
return $ if null s then "0" else show (length rs) ++ "\n" ++ beginning s
|
return $ if null s then "0" else show (length rs) ++ "\n" ++ beginning s
|
||||||
where
|
where
|
||||||
n = showTrustLevel level ++ " repositories"
|
n = showTrustLevel level ++ " repositories"
|
||||||
|
|
||||||
local_annex_size :: Stat
|
local_annex_size :: Stat
|
||||||
local_annex_size = stat "local annex size" $ json id $
|
local_annex_size = stat "local annex size" $ json id $
|
||||||
|
@ -182,42 +182,42 @@ transfer_list = stat "transfers in progress" $ nojson $ lift $ do
|
||||||
then return "none"
|
then return "none"
|
||||||
else return $ multiLine $
|
else return $ multiLine $
|
||||||
map (\(t, i) -> line uuidmap t i) $ sort ts
|
map (\(t, i) -> line uuidmap t i) $ sort ts
|
||||||
where
|
where
|
||||||
line uuidmap t i = unwords
|
line uuidmap t i = unwords
|
||||||
[ showLcDirection (transferDirection t) ++ "ing"
|
[ showLcDirection (transferDirection t) ++ "ing"
|
||||||
, fromMaybe (key2file $ transferKey t) (associatedFile i)
|
, fromMaybe (key2file $ transferKey t) (associatedFile i)
|
||||||
, if transferDirection t == Upload then "to" else "from"
|
, if transferDirection t == Upload then "to" else "from"
|
||||||
, maybe (fromUUID $ transferUUID t) Remote.name $
|
, maybe (fromUUID $ transferUUID t) Remote.name $
|
||||||
M.lookup (transferUUID t) uuidmap
|
M.lookup (transferUUID t) uuidmap
|
||||||
]
|
]
|
||||||
|
|
||||||
disk_size :: Stat
|
disk_size :: Stat
|
||||||
disk_size = stat "available local disk space" $ json id $ lift $
|
disk_size = stat "available local disk space" $ json id $ lift $
|
||||||
calcfree
|
calcfree
|
||||||
<$> getDiskReserve
|
<$> getDiskReserve
|
||||||
<*> inRepo (getDiskFree . gitAnnexDir)
|
<*> inRepo (getDiskFree . gitAnnexDir)
|
||||||
where
|
where
|
||||||
calcfree reserve (Just have) = unwords
|
calcfree reserve (Just have) = unwords
|
||||||
[ roughSize storageUnits False $ nonneg $ have - reserve
|
[ roughSize storageUnits False $ nonneg $ have - reserve
|
||||||
, "(+" ++ roughSize storageUnits False reserve
|
, "(+" ++ roughSize storageUnits False reserve
|
||||||
, "reserved)"
|
, "reserved)"
|
||||||
]
|
]
|
||||||
|
calcfree _ _ = "unknown"
|
||||||
calcfree _ _ = "unknown"
|
|
||||||
nonneg x
|
nonneg x
|
||||||
| x >= 0 = x
|
| x >= 0 = x
|
||||||
| otherwise = 0
|
| otherwise = 0
|
||||||
|
|
||||||
backend_usage :: Stat
|
backend_usage :: Stat
|
||||||
backend_usage = stat "backend usage" $ nojson $
|
backend_usage = stat "backend usage" $ nojson $
|
||||||
calc
|
calc
|
||||||
<$> (backendsKeys <$> cachedReferencedData)
|
<$> (backendsKeys <$> cachedReferencedData)
|
||||||
<*> (backendsKeys <$> cachedPresentData)
|
<*> (backendsKeys <$> cachedPresentData)
|
||||||
where
|
where
|
||||||
calc x y = multiLine $
|
calc x y = multiLine $
|
||||||
map (\(n, b) -> b ++ ": " ++ show n) $
|
map (\(n, b) -> b ++ ": " ++ show n) $
|
||||||
reverse $ sort $ map swap $ M.toList $
|
reverse $ sort $ map swap $ M.toList $
|
||||||
M.unionWith (+) x y
|
M.unionWith (+) x y
|
||||||
|
|
||||||
cachedPresentData :: StatState KeyData
|
cachedPresentData :: StatState KeyData
|
||||||
cachedPresentData = do
|
cachedPresentData = do
|
||||||
|
@ -249,39 +249,38 @@ foldKeys = foldl' (flip addKey) emptyKeyData
|
||||||
addKey :: Key -> KeyData -> KeyData
|
addKey :: Key -> KeyData -> KeyData
|
||||||
addKey key (KeyData count size unknownsize backends) =
|
addKey key (KeyData count size unknownsize backends) =
|
||||||
KeyData count' size' unknownsize' backends'
|
KeyData count' size' unknownsize' backends'
|
||||||
where
|
where
|
||||||
{- All calculations strict to avoid thunks when repeatedly
|
{- All calculations strict to avoid thunks when repeatedly
|
||||||
- applied to many keys. -}
|
- applied to many keys. -}
|
||||||
!count' = count + 1
|
!count' = count + 1
|
||||||
!backends' = M.insertWith' (+) (keyBackendName key) 1 backends
|
!backends' = M.insertWith' (+) (keyBackendName key) 1 backends
|
||||||
!size' = maybe size (+ size) ks
|
!size' = maybe size (+ size) ks
|
||||||
!unknownsize' = maybe (unknownsize + 1) (const unknownsize) ks
|
!unknownsize' = maybe (unknownsize + 1) (const unknownsize) ks
|
||||||
ks = keySize key
|
ks = keySize key
|
||||||
|
|
||||||
showSizeKeys :: KeyData -> String
|
showSizeKeys :: KeyData -> String
|
||||||
showSizeKeys d = total ++ missingnote
|
showSizeKeys d = total ++ missingnote
|
||||||
where
|
where
|
||||||
total = roughSize storageUnits False $ sizeKeys d
|
total = roughSize storageUnits False $ sizeKeys d
|
||||||
missingnote
|
missingnote
|
||||||
| unknownSizeKeys d == 0 = ""
|
| unknownSizeKeys d == 0 = ""
|
||||||
| otherwise = aside $
|
| otherwise = aside $
|
||||||
"+ " ++ show (unknownSizeKeys d) ++
|
"+ " ++ show (unknownSizeKeys d) ++
|
||||||
" keys of unknown size"
|
" keys of unknown size"
|
||||||
|
|
||||||
staleSize :: String -> (Git.Repo -> FilePath) -> Stat
|
staleSize :: String -> (Git.Repo -> FilePath) -> Stat
|
||||||
staleSize label dirspec = go =<< lift (Command.Unused.staleKeys dirspec)
|
staleSize label dirspec = go =<< lift (Command.Unused.staleKeys dirspec)
|
||||||
where
|
where
|
||||||
go [] = nostat
|
go [] = nostat
|
||||||
go keys = onsize =<< sum <$> keysizes keys
|
go keys = onsize =<< sum <$> keysizes keys
|
||||||
onsize 0 = nostat
|
onsize 0 = nostat
|
||||||
onsize size = stat label $
|
onsize size = stat label $
|
||||||
json (++ aside "clean up with git-annex unused") $
|
json (++ aside "clean up with git-annex unused") $
|
||||||
return $ roughSize storageUnits False size
|
return $ roughSize storageUnits False size
|
||||||
keysizes keys = map (fromIntegral . fileSize) <$> stats keys
|
keysizes keys = map (fromIntegral . fileSize) <$> stats keys
|
||||||
stats keys = do
|
stats keys = do
|
||||||
dir <- lift $ fromRepo dirspec
|
dir <- lift $ fromRepo dirspec
|
||||||
liftIO $ forM keys $ \k ->
|
liftIO $ forM keys $ \k -> getFileStatus (dir </> keyFile k)
|
||||||
getFileStatus (dir </> keyFile k)
|
|
||||||
|
|
||||||
aside :: String -> String
|
aside :: String -> String
|
||||||
aside s = " (" ++ s ++ ")"
|
aside s = " (" ++ s ++ ")"
|
||||||
|
|
188
Command/Sync.hs
188
Command/Sync.hs
|
@ -48,8 +48,8 @@ seek rs = do
|
||||||
, [ pushLocal branch ]
|
, [ pushLocal branch ]
|
||||||
, [ pushRemote remote branch | remote <- remotes ]
|
, [ pushRemote remote branch | remote <- remotes ]
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
nobranch = error "no branch is checked out"
|
nobranch = error "no branch is checked out"
|
||||||
|
|
||||||
syncBranch :: Git.Ref -> Git.Ref
|
syncBranch :: Git.Ref -> Git.Ref
|
||||||
syncBranch = Git.Ref.under "refs/heads/synced/"
|
syncBranch = Git.Ref.under "refs/heads/synced/"
|
||||||
|
@ -59,23 +59,23 @@ remoteBranch remote = Git.Ref.under $ "refs/remotes/" ++ Remote.name remote
|
||||||
|
|
||||||
syncRemotes :: [String] -> Annex [Remote]
|
syncRemotes :: [String] -> Annex [Remote]
|
||||||
syncRemotes rs = ifM (Annex.getState Annex.fast) ( nub <$> pickfast , wanted )
|
syncRemotes rs = ifM (Annex.getState Annex.fast) ( nub <$> pickfast , wanted )
|
||||||
where
|
where
|
||||||
pickfast = (++) <$> listed <*> (good =<< fastest <$> available)
|
pickfast = (++) <$> listed <*> (good =<< fastest <$> available)
|
||||||
wanted
|
wanted
|
||||||
| null rs = good =<< concat . Remote.byCost <$> available
|
| null rs = good =<< concat . Remote.byCost <$> available
|
||||||
| otherwise = listed
|
| otherwise = listed
|
||||||
listed = do
|
listed = do
|
||||||
l <- catMaybes <$> mapM (Remote.byName . Just) rs
|
l <- catMaybes <$> mapM (Remote.byName . Just) rs
|
||||||
let s = filter Remote.specialRemote l
|
let s = filter Remote.specialRemote l
|
||||||
unless (null s) $
|
unless (null s) $
|
||||||
error $ "cannot sync special remotes: " ++
|
error $ "cannot sync special remotes: " ++
|
||||||
unwords (map Types.Remote.name s)
|
unwords (map Types.Remote.name s)
|
||||||
return l
|
return l
|
||||||
available = filter (not . Remote.specialRemote)
|
available = filter (not . Remote.specialRemote)
|
||||||
<$> (filterM (repoSyncable . Types.Remote.repo)
|
<$> (filterM (repoSyncable . Types.Remote.repo)
|
||||||
=<< Remote.enabledRemoteList)
|
=<< Remote.enabledRemoteList)
|
||||||
good = filterM $ Remote.Git.repoAvail . Types.Remote.repo
|
good = filterM $ Remote.Git.repoAvail . Types.Remote.repo
|
||||||
fastest = fromMaybe [] . headMaybe . Remote.byCost
|
fastest = fromMaybe [] . headMaybe . Remote.byCost
|
||||||
|
|
||||||
commit :: CommandStart
|
commit :: CommandStart
|
||||||
commit = do
|
commit = do
|
||||||
|
@ -90,16 +90,16 @@ commit = do
|
||||||
|
|
||||||
mergeLocal :: Git.Ref -> CommandStart
|
mergeLocal :: Git.Ref -> CommandStart
|
||||||
mergeLocal branch = go =<< needmerge
|
mergeLocal branch = go =<< needmerge
|
||||||
where
|
where
|
||||||
syncbranch = syncBranch branch
|
syncbranch = syncBranch branch
|
||||||
needmerge = do
|
needmerge = do
|
||||||
unlessM (inRepo $ Git.Ref.exists syncbranch) $
|
unlessM (inRepo $ Git.Ref.exists syncbranch) $
|
||||||
inRepo $ updateBranch syncbranch
|
inRepo $ updateBranch syncbranch
|
||||||
inRepo $ Git.Branch.changed branch syncbranch
|
inRepo $ Git.Branch.changed branch syncbranch
|
||||||
go False = stop
|
go False = stop
|
||||||
go True = do
|
go True = do
|
||||||
showStart "merge" $ Git.Ref.describe syncbranch
|
showStart "merge" $ Git.Ref.describe syncbranch
|
||||||
next $ next $ mergeFrom syncbranch
|
next $ next $ mergeFrom syncbranch
|
||||||
|
|
||||||
pushLocal :: Git.Ref -> CommandStart
|
pushLocal :: Git.Ref -> CommandStart
|
||||||
pushLocal branch = do
|
pushLocal branch = do
|
||||||
|
@ -109,11 +109,11 @@ pushLocal branch = do
|
||||||
updateBranch :: Git.Ref -> Git.Repo -> IO ()
|
updateBranch :: Git.Ref -> Git.Repo -> IO ()
|
||||||
updateBranch syncbranch g =
|
updateBranch syncbranch g =
|
||||||
unlessM go $ error $ "failed to update " ++ show syncbranch
|
unlessM go $ error $ "failed to update " ++ show syncbranch
|
||||||
where
|
where
|
||||||
go = Git.Command.runBool "branch"
|
go = Git.Command.runBool "branch"
|
||||||
[ Param "-f"
|
[ Param "-f"
|
||||||
, Param $ show $ Git.Ref.base syncbranch
|
, Param $ show $ Git.Ref.base syncbranch
|
||||||
] g
|
] g
|
||||||
|
|
||||||
pullRemote :: Remote -> Git.Ref -> CommandStart
|
pullRemote :: Remote -> Git.Ref -> CommandStart
|
||||||
pullRemote remote branch = do
|
pullRemote remote branch = do
|
||||||
|
@ -122,9 +122,9 @@ pullRemote remote branch = do
|
||||||
showOutput
|
showOutput
|
||||||
stopUnless fetch $
|
stopUnless fetch $
|
||||||
next $ mergeRemote remote (Just branch)
|
next $ mergeRemote remote (Just branch)
|
||||||
where
|
where
|
||||||
fetch = inRepo $ Git.Command.runBool "fetch"
|
fetch = inRepo $ Git.Command.runBool "fetch"
|
||||||
[Param $ Remote.name remote]
|
[Param $ Remote.name remote]
|
||||||
|
|
||||||
{- The remote probably has both a master and a synced/master branch.
|
{- The remote probably has both a master and a synced/master branch.
|
||||||
- Which to merge from? Well, the master has whatever latest changes
|
- Which to merge from? Well, the master has whatever latest changes
|
||||||
|
@ -136,22 +136,22 @@ mergeRemote remote b = case b of
|
||||||
branch <- inRepo Git.Branch.currentUnsafe
|
branch <- inRepo Git.Branch.currentUnsafe
|
||||||
all id <$> (mapM merge $ branchlist branch)
|
all id <$> (mapM merge $ branchlist branch)
|
||||||
Just _ -> all id <$> (mapM merge =<< tomerge (branchlist b))
|
Just _ -> all id <$> (mapM merge =<< tomerge (branchlist b))
|
||||||
where
|
where
|
||||||
merge = mergeFrom . remoteBranch remote
|
merge = mergeFrom . remoteBranch remote
|
||||||
tomerge branches = filterM (changed remote) branches
|
tomerge branches = filterM (changed remote) branches
|
||||||
branchlist Nothing = []
|
branchlist Nothing = []
|
||||||
branchlist (Just branch) = [branch, syncBranch branch]
|
branchlist (Just branch) = [branch, syncBranch branch]
|
||||||
|
|
||||||
pushRemote :: Remote -> Git.Ref -> CommandStart
|
pushRemote :: Remote -> Git.Ref -> CommandStart
|
||||||
pushRemote remote branch = go =<< needpush
|
pushRemote remote branch = go =<< needpush
|
||||||
where
|
where
|
||||||
needpush = anyM (newer remote) [syncBranch branch, Annex.Branch.name]
|
needpush = anyM (newer remote) [syncBranch branch, Annex.Branch.name]
|
||||||
go False = stop
|
go False = stop
|
||||||
go True = do
|
go True = do
|
||||||
showStart "push" (Remote.name remote)
|
showStart "push" (Remote.name remote)
|
||||||
next $ next $ do
|
next $ next $ do
|
||||||
showOutput
|
showOutput
|
||||||
inRepo $ pushBranch remote branch
|
inRepo $ pushBranch remote branch
|
||||||
|
|
||||||
pushBranch :: Remote -> Git.Ref -> Git.Repo -> IO Bool
|
pushBranch :: Remote -> Git.Ref -> Git.Repo -> IO Bool
|
||||||
pushBranch remote branch g =
|
pushBranch remote branch g =
|
||||||
|
@ -160,12 +160,12 @@ pushBranch remote branch g =
|
||||||
, Param $ refspec Annex.Branch.name
|
, Param $ refspec Annex.Branch.name
|
||||||
, Param $ refspec branch
|
, Param $ refspec branch
|
||||||
] g
|
] g
|
||||||
where
|
where
|
||||||
refspec b = concat
|
refspec b = concat
|
||||||
[ show $ Git.Ref.base b
|
[ show $ Git.Ref.base b
|
||||||
, ":"
|
, ":"
|
||||||
, show $ Git.Ref.base $ syncBranch b
|
, show $ Git.Ref.base $ syncBranch b
|
||||||
]
|
]
|
||||||
|
|
||||||
mergeAnnex :: CommandStart
|
mergeAnnex :: CommandStart
|
||||||
mergeAnnex = do
|
mergeAnnex = do
|
||||||
|
@ -213,37 +213,37 @@ resolveMerge' u
|
||||||
withKey LsFiles.valUs $ \keyUs ->
|
withKey LsFiles.valUs $ \keyUs ->
|
||||||
withKey LsFiles.valThem $ \keyThem -> go keyUs keyThem
|
withKey LsFiles.valThem $ \keyThem -> go keyUs keyThem
|
||||||
| otherwise = return False
|
| otherwise = return False
|
||||||
where
|
where
|
||||||
go keyUs keyThem
|
go keyUs keyThem
|
||||||
| keyUs == keyThem = do
|
| keyUs == keyThem = do
|
||||||
makelink keyUs
|
makelink keyUs
|
||||||
return True
|
return True
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
liftIO $ nukeFile file
|
liftIO $ nukeFile file
|
||||||
Annex.Queue.addCommand "rm" [Params "--quiet -f --"] [file]
|
Annex.Queue.addCommand "rm" [Params "--quiet -f --"] [file]
|
||||||
makelink keyUs
|
makelink keyUs
|
||||||
makelink keyThem
|
makelink keyThem
|
||||||
return True
|
return True
|
||||||
file = LsFiles.unmergedFile u
|
file = LsFiles.unmergedFile u
|
||||||
issymlink select = any (select (LsFiles.unmergedBlobType u) ==)
|
issymlink select = any (select (LsFiles.unmergedBlobType u) ==)
|
||||||
[Just SymlinkBlob, Nothing]
|
[Just SymlinkBlob, Nothing]
|
||||||
makelink (Just key) = do
|
makelink (Just key) = do
|
||||||
let dest = mergeFile file key
|
let dest = mergeFile file key
|
||||||
l <- calcGitLink dest key
|
l <- calcGitLink dest key
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
nukeFile dest
|
nukeFile dest
|
||||||
createSymbolicLink l dest
|
createSymbolicLink l dest
|
||||||
Annex.Queue.addCommand "add" [Param "--force", Param "--"] [dest]
|
Annex.Queue.addCommand "add" [Param "--force", Param "--"] [dest]
|
||||||
makelink _ = noop
|
makelink _ = noop
|
||||||
withKey select a = do
|
withKey select a = do
|
||||||
let msha = select $ LsFiles.unmergedSha u
|
let msha = select $ LsFiles.unmergedSha u
|
||||||
case msha of
|
case msha of
|
||||||
Nothing -> a Nothing
|
Nothing -> a Nothing
|
||||||
Just sha -> do
|
Just sha -> do
|
||||||
key <- fileKey . takeFileName
|
key <- fileKey . takeFileName
|
||||||
. encodeW8 . L.unpack
|
. encodeW8 . L.unpack
|
||||||
<$> catObject sha
|
<$> catObject sha
|
||||||
maybe (return False) (a . Just) key
|
maybe (return False) (a . Just) key
|
||||||
|
|
||||||
{- The filename to use when resolving a conflicted merge of a file,
|
{- The filename to use when resolving a conflicted merge of a file,
|
||||||
- that points to a key.
|
- that points to a key.
|
||||||
|
@ -262,13 +262,13 @@ mergeFile :: FilePath -> Key -> FilePath
|
||||||
mergeFile file key
|
mergeFile file key
|
||||||
| doubleconflict = go $ key2file key
|
| doubleconflict = go $ key2file key
|
||||||
| otherwise = go $ shortHash $ key2file key
|
| otherwise = go $ shortHash $ key2file key
|
||||||
where
|
where
|
||||||
varmarker = ".variant-"
|
varmarker = ".variant-"
|
||||||
doubleconflict = varmarker `isSuffixOf` (dropExtension file)
|
doubleconflict = varmarker `isSuffixOf` (dropExtension file)
|
||||||
go v = takeDirectory file
|
go v = takeDirectory file
|
||||||
</> dropExtension (takeFileName file)
|
</> dropExtension (takeFileName file)
|
||||||
++ varmarker ++ v
|
++ varmarker ++ v
|
||||||
++ takeExtension file
|
++ takeExtension file
|
||||||
|
|
||||||
shortHash :: String -> String
|
shortHash :: String -> String
|
||||||
shortHash = take 4 . md5s . md5FilePath
|
shortHash = take 4 . md5s . md5FilePath
|
||||||
|
|
|
@ -30,10 +30,10 @@ check = do
|
||||||
cwd <- liftIO getCurrentDirectory
|
cwd <- liftIO getCurrentDirectory
|
||||||
whenM ((/=) <$> liftIO (absPath top) <*> liftIO (absPath cwd)) $
|
whenM ((/=) <$> liftIO (absPath top) <*> liftIO (absPath cwd)) $
|
||||||
error "can only run uninit from the top of the git repository"
|
error "can only run uninit from the top of the git repository"
|
||||||
where
|
where
|
||||||
current_branch = Git.Ref . Prelude.head . lines <$> revhead
|
current_branch = Git.Ref . Prelude.head . lines <$> revhead
|
||||||
revhead = inRepo $ Git.Command.pipeReadStrict
|
revhead = inRepo $ Git.Command.pipeReadStrict
|
||||||
[Params "rev-parse --abbrev-ref HEAD"]
|
[Params "rev-parse --abbrev-ref HEAD"]
|
||||||
|
|
||||||
seek :: [CommandSeek]
|
seek :: [CommandSeek]
|
||||||
seek = [
|
seek = [
|
||||||
|
|
|
@ -17,8 +17,8 @@ def =
|
||||||
[ c "unlock" "unlock files for modification"
|
[ c "unlock" "unlock files for modification"
|
||||||
, c "edit" "same as unlock"
|
, c "edit" "same as unlock"
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
c n = command n paramPaths seek
|
c n = command n paramPaths seek
|
||||||
|
|
||||||
seek :: [CommandSeek]
|
seek :: [CommandSeek]
|
||||||
seek = [withFilesInGit $ whenAnnexed start]
|
seek = [withFilesInGit $ whenAnnexed start]
|
||||||
|
|
|
@ -64,27 +64,26 @@ checkUnused = chain 0
|
||||||
, check "bad" staleBadMsg $ staleKeysPrune gitAnnexBadDir
|
, check "bad" staleBadMsg $ staleKeysPrune gitAnnexBadDir
|
||||||
, check "tmp" staleTmpMsg $ staleKeysPrune gitAnnexTmpDir
|
, check "tmp" staleTmpMsg $ staleKeysPrune gitAnnexTmpDir
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
findunused True = do
|
findunused True = do
|
||||||
showNote "fast mode enabled; only finding stale files"
|
showNote "fast mode enabled; only finding stale files"
|
||||||
return []
|
return []
|
||||||
findunused False = do
|
findunused False = do
|
||||||
showAction "checking for unused data"
|
showAction "checking for unused data"
|
||||||
excludeReferenced =<< getKeysPresent
|
excludeReferenced =<< getKeysPresent
|
||||||
chain _ [] = next $ return True
|
chain _ [] = next $ return True
|
||||||
chain v (a:as) = do
|
chain v (a:as) = do
|
||||||
v' <- a v
|
v' <- a v
|
||||||
chain v' as
|
chain v' as
|
||||||
|
|
||||||
checkRemoteUnused :: String -> CommandPerform
|
checkRemoteUnused :: String -> CommandPerform
|
||||||
checkRemoteUnused name = go =<< fromJust <$> Remote.byName (Just name)
|
checkRemoteUnused name = go =<< fromJust <$> Remote.byName (Just name)
|
||||||
where
|
where
|
||||||
go r = do
|
go r = do
|
||||||
showAction "checking for unused data"
|
showAction "checking for unused data"
|
||||||
_ <- check "" (remoteUnusedMsg r) (remoteunused r) 0
|
_ <- check "" (remoteUnusedMsg r) (remoteunused r) 0
|
||||||
next $ return True
|
next $ return True
|
||||||
remoteunused r =
|
remoteunused r = excludeReferenced <=< loggedKeysFor $ Remote.uuid r
|
||||||
excludeReferenced <=< loggedKeysFor $ Remote.uuid r
|
|
||||||
|
|
||||||
check :: FilePath -> ([(Int, Key)] -> String) -> Annex [Key] -> Int -> Annex Int
|
check :: FilePath -> ([(Int, Key)] -> String) -> Annex [Key] -> Int -> Annex Int
|
||||||
check file msg a c = do
|
check file msg a c = do
|
||||||
|
@ -100,9 +99,9 @@ number n (x:xs) = (n+1, x) : number (n+1) xs
|
||||||
|
|
||||||
table :: [(Int, Key)] -> [String]
|
table :: [(Int, Key)] -> [String]
|
||||||
table l = " NUMBER KEY" : map cols l
|
table l = " NUMBER KEY" : map cols l
|
||||||
where
|
where
|
||||||
cols (n,k) = " " ++ pad 6 (show n) ++ " " ++ key2file k
|
cols (n,k) = " " ++ pad 6 (show n) ++ " " ++ key2file k
|
||||||
pad n s = s ++ replicate (n - length s) ' '
|
pad n s = s ++ replicate (n - length s) ' '
|
||||||
|
|
||||||
staleTmpMsg :: [(Int, Key)] -> String
|
staleTmpMsg :: [(Int, Key)] -> String
|
||||||
staleTmpMsg t = unlines $
|
staleTmpMsg t = unlines $
|
||||||
|
@ -129,8 +128,8 @@ remoteUnusedMsg :: Remote -> [(Int, Key)] -> String
|
||||||
remoteUnusedMsg r u = unusedMsg' u
|
remoteUnusedMsg r u = unusedMsg' u
|
||||||
["Some annexed data on " ++ name ++ " is not used by any files:"]
|
["Some annexed data on " ++ name ++ " is not used by any files:"]
|
||||||
[dropMsg $ Just r]
|
[dropMsg $ Just r]
|
||||||
where
|
where
|
||||||
name = Remote.name r
|
name = Remote.name r
|
||||||
|
|
||||||
dropMsg :: Maybe Remote -> String
|
dropMsg :: Maybe Remote -> String
|
||||||
dropMsg Nothing = dropMsg' ""
|
dropMsg Nothing = dropMsg' ""
|
||||||
|
@ -159,11 +158,11 @@ dropMsg' s = "\nTo remove unwanted data: git-annex dropunused" ++ s ++ " NUMBER\
|
||||||
-}
|
-}
|
||||||
excludeReferenced :: [Key] -> Annex [Key]
|
excludeReferenced :: [Key] -> Annex [Key]
|
||||||
excludeReferenced ks = runfilter firstlevel ks >>= runfilter secondlevel
|
excludeReferenced ks = runfilter firstlevel ks >>= runfilter secondlevel
|
||||||
where
|
where
|
||||||
runfilter _ [] = return [] -- optimisation
|
runfilter _ [] = return [] -- optimisation
|
||||||
runfilter a l = bloomFilter show l <$> genBloomFilter show a
|
runfilter a l = bloomFilter show l <$> genBloomFilter show a
|
||||||
firstlevel = withKeysReferencedM
|
firstlevel = withKeysReferencedM
|
||||||
secondlevel = withKeysReferencedInGit
|
secondlevel = withKeysReferencedInGit
|
||||||
|
|
||||||
{- Finds items in the first, smaller list, that are not
|
{- Finds items in the first, smaller list, that are not
|
||||||
- present in the second, larger list.
|
- present in the second, larger list.
|
||||||
|
@ -174,8 +173,8 @@ excludeReferenced ks = runfilter firstlevel ks >>= runfilter secondlevel
|
||||||
exclude :: Ord a => [a] -> [a] -> [a]
|
exclude :: Ord a => [a] -> [a] -> [a]
|
||||||
exclude [] _ = [] -- optimisation
|
exclude [] _ = [] -- optimisation
|
||||||
exclude smaller larger = S.toList $ remove larger $ S.fromList smaller
|
exclude smaller larger = S.toList $ remove larger $ S.fromList smaller
|
||||||
where
|
where
|
||||||
remove a b = foldl (flip S.delete) b a
|
remove a b = foldl (flip S.delete) b a
|
||||||
|
|
||||||
{- A bloom filter capable of holding half a million keys with a
|
{- A bloom filter capable of holding half a million keys with a
|
||||||
- false positive rate of 1 in 1000 uses around 8 mb of memory,
|
- false positive rate of 1 in 1000 uses around 8 mb of memory,
|
||||||
|
@ -208,8 +207,8 @@ genBloomFilter convert populate = do
|
||||||
bloom <- lift $ newMB (cheapHashes numhashes) numbits
|
bloom <- lift $ newMB (cheapHashes numhashes) numbits
|
||||||
_ <- populate $ \v -> lift $ insertMB bloom (convert v)
|
_ <- populate $ \v -> lift $ insertMB bloom (convert v)
|
||||||
lift $ unsafeFreezeMB bloom
|
lift $ unsafeFreezeMB bloom
|
||||||
where
|
where
|
||||||
lift = liftIO . stToIO
|
lift = liftIO . stToIO
|
||||||
|
|
||||||
bloomFilter :: Hashable t => (v -> t) -> [v] -> Bloom t -> [v]
|
bloomFilter :: Hashable t => (v -> t) -> [v] -> Bloom t -> [v]
|
||||||
bloomFilter convert l bloom = filter (\k -> convert k `notElemB` bloom) l
|
bloomFilter convert l bloom = filter (\k -> convert k `notElemB` bloom) l
|
||||||
|
@ -218,14 +217,14 @@ bloomFilter convert l bloom = filter (\k -> convert k `notElemB` bloom) l
|
||||||
- symlinks in the git repo. -}
|
- symlinks in the git repo. -}
|
||||||
withKeysReferenced :: v -> (Key -> v -> v) -> Annex v
|
withKeysReferenced :: v -> (Key -> v -> v) -> Annex v
|
||||||
withKeysReferenced initial a = withKeysReferenced' initial folda
|
withKeysReferenced initial a = withKeysReferenced' initial folda
|
||||||
where
|
where
|
||||||
folda k v = return $ a k v
|
folda k v = return $ a k v
|
||||||
|
|
||||||
{- Runs an action on each referenced key in the git repo. -}
|
{- Runs an action on each referenced key in the git repo. -}
|
||||||
withKeysReferencedM :: (Key -> Annex ()) -> Annex ()
|
withKeysReferencedM :: (Key -> Annex ()) -> Annex ()
|
||||||
withKeysReferencedM a = withKeysReferenced' () calla
|
withKeysReferencedM a = withKeysReferenced' () calla
|
||||||
where
|
where
|
||||||
calla k _ = a k
|
calla k _ = a k
|
||||||
|
|
||||||
withKeysReferenced' :: v -> (Key -> v -> Annex v) -> Annex v
|
withKeysReferenced' :: v -> (Key -> v -> Annex v) -> Annex v
|
||||||
withKeysReferenced' initial a = do
|
withKeysReferenced' initial a = do
|
||||||
|
@ -233,54 +232,53 @@ withKeysReferenced' initial a = do
|
||||||
r <- go initial files
|
r <- go initial files
|
||||||
liftIO $ void clean
|
liftIO $ void clean
|
||||||
return r
|
return r
|
||||||
where
|
where
|
||||||
getfiles = ifM isBareRepo
|
getfiles = ifM isBareRepo
|
||||||
( return ([], return True)
|
( return ([], return True)
|
||||||
, do
|
, do
|
||||||
top <- fromRepo Git.repoPath
|
top <- fromRepo Git.repoPath
|
||||||
inRepo $ LsFiles.inRepo [top]
|
inRepo $ LsFiles.inRepo [top]
|
||||||
)
|
)
|
||||||
go v [] = return v
|
go v [] = return v
|
||||||
go v (f:fs) = do
|
go v (f:fs) = do
|
||||||
x <- Backend.lookupFile f
|
x <- Backend.lookupFile f
|
||||||
case x of
|
case x of
|
||||||
Nothing -> go v fs
|
Nothing -> go v fs
|
||||||
Just (k, _) -> do
|
Just (k, _) -> do
|
||||||
!v' <- a k v
|
!v' <- a k v
|
||||||
go v' fs
|
go v' fs
|
||||||
|
|
||||||
|
|
||||||
withKeysReferencedInGit :: (Key -> Annex ()) -> Annex ()
|
withKeysReferencedInGit :: (Key -> Annex ()) -> Annex ()
|
||||||
withKeysReferencedInGit a = do
|
withKeysReferencedInGit a = do
|
||||||
rs <- relevantrefs <$> showref
|
rs <- relevantrefs <$> showref
|
||||||
forM_ rs (withKeysReferencedInGitRef a)
|
forM_ rs (withKeysReferencedInGitRef a)
|
||||||
where
|
where
|
||||||
showref = inRepo $ Git.Command.pipeReadStrict [Param "show-ref"]
|
showref = inRepo $ Git.Command.pipeReadStrict [Param "show-ref"]
|
||||||
relevantrefs = map (Git.Ref . snd) .
|
relevantrefs = map (Git.Ref . snd) .
|
||||||
nubBy uniqref .
|
nubBy uniqref .
|
||||||
filter ourbranches .
|
filter ourbranches .
|
||||||
map (separate (== ' ')) . lines
|
map (separate (== ' ')) . lines
|
||||||
uniqref (x, _) (y, _) = x == y
|
uniqref (x, _) (y, _) = x == y
|
||||||
ourbranchend = '/' : show Annex.Branch.name
|
ourbranchend = '/' : show Annex.Branch.name
|
||||||
ourbranches (_, b) = not (ourbranchend `isSuffixOf` b)
|
ourbranches (_, b) = not (ourbranchend `isSuffixOf` b)
|
||||||
&& not ("refs/synced/" `isPrefixOf` b)
|
&& not ("refs/synced/" `isPrefixOf` b)
|
||||||
|
|
||||||
withKeysReferencedInGitRef :: (Key -> Annex ()) -> Git.Ref -> Annex ()
|
withKeysReferencedInGitRef :: (Key -> Annex ()) -> Git.Ref -> Annex ()
|
||||||
withKeysReferencedInGitRef a ref = do
|
withKeysReferencedInGitRef a ref = do
|
||||||
showAction $ "checking " ++ Git.Ref.describe ref
|
showAction $ "checking " ++ Git.Ref.describe ref
|
||||||
go <=< inRepo $ LsTree.lsTree ref
|
go <=< inRepo $ LsTree.lsTree ref
|
||||||
where
|
where
|
||||||
go [] = noop
|
go [] = noop
|
||||||
go (l:ls)
|
go (l:ls)
|
||||||
| isSymLink (LsTree.mode l) = do
|
| isSymLink (LsTree.mode l) = do
|
||||||
content <- encodeW8 . L.unpack
|
content <- encodeW8 . L.unpack
|
||||||
<$> catFile ref (LsTree.file l)
|
<$> catFile ref (LsTree.file l)
|
||||||
case fileKey (takeFileName content) of
|
case fileKey (takeFileName content) of
|
||||||
Nothing -> go ls
|
Nothing -> go ls
|
||||||
Just k -> do
|
Just k -> do
|
||||||
a k
|
a k
|
||||||
go ls
|
go ls
|
||||||
| otherwise = go ls
|
| otherwise = go ls
|
||||||
|
|
||||||
{- Looks in the specified directory for bad/tmp keys, and returns a list
|
{- Looks in the specified directory for bad/tmp keys, and returns a list
|
||||||
- of those that might still have value, or might be stale and removable.
|
- of those that might still have value, or might be stale and removable.
|
||||||
|
|
|
@ -29,8 +29,8 @@ start = do
|
||||||
putStrLn $ "supported repository versions: " ++ vs supportedVersions
|
putStrLn $ "supported repository versions: " ++ vs supportedVersions
|
||||||
putStrLn $ "upgrade supported from repository versions: " ++ vs upgradableVersions
|
putStrLn $ "upgrade supported from repository versions: " ++ vs upgradableVersions
|
||||||
stop
|
stop
|
||||||
where
|
where
|
||||||
vs = join " "
|
vs = join " "
|
||||||
|
|
||||||
showPackageVersion :: IO ()
|
showPackageVersion :: IO ()
|
||||||
showPackageVersion = putStrLn $ "git-annex version: " ++ SysConfig.packageversion
|
showPackageVersion = putStrLn $ "git-annex version: " ++ SysConfig.packageversion
|
||||||
|
|
189
Command/Vicfg.hs
189
Command/Vicfg.hs
|
@ -75,119 +75,116 @@ setCfg curcfg newcfg = do
|
||||||
|
|
||||||
diffCfg :: Cfg -> Cfg -> (TrustMap, M.Map UUID (S.Set Group), M.Map UUID String)
|
diffCfg :: Cfg -> Cfg -> (TrustMap, M.Map UUID (S.Set Group), M.Map UUID String)
|
||||||
diffCfg curcfg newcfg = (diff cfgTrustMap, diff cfgGroupMap, diff cfgPreferredContentMap)
|
diffCfg curcfg newcfg = (diff cfgTrustMap, diff cfgGroupMap, diff cfgPreferredContentMap)
|
||||||
where
|
where
|
||||||
diff f = M.differenceWith (\x y -> if x == y then Nothing else Just x)
|
diff f = M.differenceWith (\x y -> if x == y then Nothing else Just x)
|
||||||
(f newcfg) (f curcfg)
|
(f newcfg) (f curcfg)
|
||||||
|
|
||||||
genCfg :: Cfg -> M.Map UUID String -> String
|
genCfg :: Cfg -> M.Map UUID String -> String
|
||||||
genCfg cfg descs = unlines $ concat [intro, trust, groups, preferredcontent]
|
genCfg cfg descs = unlines $ concat [intro, trust, groups, preferredcontent]
|
||||||
where
|
where
|
||||||
intro =
|
intro =
|
||||||
[ com "git-annex configuration"
|
[ com "git-annex configuration"
|
||||||
, com ""
|
, com ""
|
||||||
, com "Changes saved to this file will be recorded in the git-annex branch."
|
, com "Changes saved to this file will be recorded in the git-annex branch."
|
||||||
, com ""
|
, com ""
|
||||||
, com "Lines in this file have the format:"
|
, com "Lines in this file have the format:"
|
||||||
, com " setting uuid = value"
|
, com " setting uuid = value"
|
||||||
]
|
]
|
||||||
|
|
||||||
trust = settings cfgTrustMap
|
trust = settings cfgTrustMap
|
||||||
[ ""
|
[ ""
|
||||||
, com "Repository trust configuration"
|
, com "Repository trust configuration"
|
||||||
, com "(Valid trust levels: " ++
|
, com "(Valid trust levels: " ++
|
||||||
unwords (map showTrustLevel [Trusted .. DeadTrusted]) ++
|
unwords (map showTrustLevel [Trusted .. DeadTrusted]) ++
|
||||||
")"
|
")"
|
||||||
]
|
]
|
||||||
(\(t, u) -> line "trust" u $ showTrustLevel t)
|
(\(t, u) -> line "trust" u $ showTrustLevel t)
|
||||||
(\u -> lcom $ line "trust" u $ showTrustLevel SemiTrusted)
|
(\u -> lcom $ line "trust" u $ showTrustLevel SemiTrusted)
|
||||||
|
|
||||||
groups = settings cfgGroupMap
|
groups = settings cfgGroupMap
|
||||||
[ ""
|
[ ""
|
||||||
, com "Repository groups"
|
, com "Repository groups"
|
||||||
, com "(Separate group names with spaces)"
|
, com "(Separate group names with spaces)"
|
||||||
]
|
]
|
||||||
(\(s, u) -> line "group" u $ unwords $ S.toList s)
|
(\(s, u) -> line "group" u $ unwords $ S.toList s)
|
||||||
(\u -> lcom $ line "group" u "")
|
(\u -> lcom $ line "group" u "")
|
||||||
|
|
||||||
preferredcontent = settings cfgPreferredContentMap
|
preferredcontent = settings cfgPreferredContentMap
|
||||||
[ ""
|
[ ""
|
||||||
, com "Repository preferred contents"
|
, com "Repository preferred contents"
|
||||||
]
|
]
|
||||||
(\(s, u) -> line "preferred-content" u s)
|
(\(s, u) -> line "preferred-content" u s)
|
||||||
(\u -> line "preferred-content" u "")
|
(\u -> line "preferred-content" u "")
|
||||||
|
|
||||||
settings field desc showvals showdefaults = concat
|
settings field desc showvals showdefaults = concat
|
||||||
[ desc
|
[ desc
|
||||||
, concatMap showvals $
|
, concatMap showvals $ sort $ map swap $ M.toList $ field cfg
|
||||||
sort $ map swap $ M.toList $ field cfg
|
, concatMap (\u -> lcom $ showdefaults u) $ missing field
|
||||||
, concatMap (\u -> lcom $ showdefaults u) $
|
]
|
||||||
missing field
|
|
||||||
]
|
|
||||||
|
|
||||||
line setting u value =
|
line setting u value =
|
||||||
[ com $ "(for " ++ (fromMaybe "" $ M.lookup u descs) ++ ")"
|
[ com $ "(for " ++ (fromMaybe "" $ M.lookup u descs) ++ ")"
|
||||||
, unwords [setting, fromUUID u, "=", value]
|
, unwords [setting, fromUUID u, "=", value]
|
||||||
]
|
]
|
||||||
lcom = map (\l -> if "#" `isPrefixOf` l then l else "#" ++ l)
|
lcom = map (\l -> if "#" `isPrefixOf` l then l else "#" ++ l)
|
||||||
missing field = S.toList $ M.keysSet descs `S.difference` M.keysSet (field cfg)
|
missing field = S.toList $ M.keysSet descs `S.difference` M.keysSet (field cfg)
|
||||||
|
|
||||||
{- If there's a parse error, returns a new version of the file,
|
{- If there's a parse error, returns a new version of the file,
|
||||||
- with the problem lines noted. -}
|
- with the problem lines noted. -}
|
||||||
parseCfg :: Cfg -> String -> Either String Cfg
|
parseCfg :: Cfg -> String -> Either String Cfg
|
||||||
parseCfg curcfg = go [] curcfg . lines
|
parseCfg curcfg = go [] curcfg . lines
|
||||||
where
|
where
|
||||||
go c cfg []
|
go c cfg []
|
||||||
| null (catMaybes $ map fst c) = Right cfg
|
| null (catMaybes $ map fst c) = Right cfg
|
||||||
| otherwise = Left $ unlines $
|
| otherwise = Left $ unlines $
|
||||||
badheader ++ concatMap showerr (reverse c)
|
badheader ++ concatMap showerr (reverse c)
|
||||||
go c cfg (l:ls) = case parse (dropWhile isSpace l) cfg of
|
go c cfg (l:ls) = case parse (dropWhile isSpace l) cfg of
|
||||||
Left msg -> go ((Just msg, l):c) cfg ls
|
Left msg -> go ((Just msg, l):c) cfg ls
|
||||||
Right cfg' -> go ((Nothing, l):c) cfg' ls
|
Right cfg' -> go ((Nothing, l):c) cfg' ls
|
||||||
|
|
||||||
parse l cfg
|
parse l cfg
|
||||||
| null l = Right cfg
|
| null l = Right cfg
|
||||||
| "#" `isPrefixOf` l = Right cfg
|
| "#" `isPrefixOf` l = Right cfg
|
||||||
| null setting || null u = Left "missing repository uuid"
|
| null setting || null u = Left "missing repository uuid"
|
||||||
| otherwise = handle cfg (toUUID u) setting value'
|
| otherwise = handle cfg (toUUID u) setting value'
|
||||||
where
|
where
|
||||||
(setting, rest) = separate isSpace l
|
(setting, rest) = separate isSpace l
|
||||||
(r, value) = separate (== '=') rest
|
(r, value) = separate (== '=') rest
|
||||||
value' = trimspace value
|
value' = trimspace value
|
||||||
u = reverse $ trimspace $
|
u = reverse $ trimspace $ reverse $ trimspace r
|
||||||
reverse $ trimspace r
|
trimspace = dropWhile isSpace
|
||||||
trimspace = dropWhile isSpace
|
|
||||||
|
|
||||||
handle cfg u setting value
|
handle cfg u setting value
|
||||||
| setting == "trust" = case readTrustLevel value of
|
| setting == "trust" = case readTrustLevel value of
|
||||||
Nothing -> badval "trust value" value
|
Nothing -> badval "trust value" value
|
||||||
Just t ->
|
Just t ->
|
||||||
let m = M.insert u t (cfgTrustMap cfg)
|
let m = M.insert u t (cfgTrustMap cfg)
|
||||||
in Right $ cfg { cfgTrustMap = m }
|
in Right $ cfg { cfgTrustMap = m }
|
||||||
| setting == "group" =
|
| setting == "group" =
|
||||||
let m = M.insert u (S.fromList $ words value) (cfgGroupMap cfg)
|
let m = M.insert u (S.fromList $ words value) (cfgGroupMap cfg)
|
||||||
in Right $ cfg { cfgGroupMap = m }
|
in Right $ cfg { cfgGroupMap = m }
|
||||||
| setting == "preferred-content" =
|
| setting == "preferred-content" =
|
||||||
case checkPreferredContentExpression value of
|
case checkPreferredContentExpression value of
|
||||||
Just e -> Left e
|
Just e -> Left e
|
||||||
Nothing ->
|
Nothing ->
|
||||||
let m = M.insert u value (cfgPreferredContentMap cfg)
|
let m = M.insert u value (cfgPreferredContentMap cfg)
|
||||||
in Right $ cfg { cfgPreferredContentMap = m }
|
in Right $ cfg { cfgPreferredContentMap = m }
|
||||||
| otherwise = badval "setting" setting
|
| otherwise = badval "setting" setting
|
||||||
|
|
||||||
showerr (Just msg, l) = [parseerr ++ msg, l]
|
showerr (Just msg, l) = [parseerr ++ msg, l]
|
||||||
showerr (Nothing, l)
|
showerr (Nothing, l)
|
||||||
-- filter out the header and parse error lines
|
-- filter out the header and parse error lines
|
||||||
-- from any previous parse failure
|
-- from any previous parse failure
|
||||||
| any (`isPrefixOf` l) (parseerr:badheader) = []
|
| any (`isPrefixOf` l) (parseerr:badheader) = []
|
||||||
| otherwise = [l]
|
| otherwise = [l]
|
||||||
|
|
||||||
badval desc val = Left $ "unknown " ++ desc ++ " \"" ++ val ++ "\""
|
badval desc val = Left $ "unknown " ++ desc ++ " \"" ++ val ++ "\""
|
||||||
badheader =
|
badheader =
|
||||||
[ com "There was a problem parsing your input."
|
[ com "There was a problem parsing your input."
|
||||||
, com "Search for \"Parse error\" to find the bad lines."
|
, com "Search for \"Parse error\" to find the bad lines."
|
||||||
, com "Either fix the bad lines, or delete them (to discard your changes)."
|
, com "Either fix the bad lines, or delete them (to discard your changes)."
|
||||||
]
|
]
|
||||||
parseerr = com "Parse error in next line: "
|
parseerr = com "Parse error in next line: "
|
||||||
|
|
||||||
com :: String -> String
|
com :: String -> String
|
||||||
com s = "# " ++ s
|
com s = "# " ++ s
|
||||||
|
|
|
@ -43,24 +43,24 @@ start' allowauto = notBareRepo $ do
|
||||||
liftIO $ ensureInstalled
|
liftIO $ ensureInstalled
|
||||||
ifM isInitialized ( go , auto )
|
ifM isInitialized ( go , auto )
|
||||||
stop
|
stop
|
||||||
where
|
where
|
||||||
go = do
|
go = do
|
||||||
browser <- fromRepo webBrowser
|
browser <- fromRepo webBrowser
|
||||||
f <- liftIO . absPath =<< fromRepo gitAnnexHtmlShim
|
f <- liftIO . absPath =<< fromRepo gitAnnexHtmlShim
|
||||||
ifM (checkpid <&&> checkshim f)
|
ifM (checkpid <&&> checkshim f)
|
||||||
( liftIO $ openBrowser browser f
|
( liftIO $ openBrowser browser f
|
||||||
, startDaemon True True $ Just $
|
, startDaemon True True $ Just $
|
||||||
const $ openBrowser browser
|
const $ openBrowser browser
|
||||||
)
|
)
|
||||||
auto
|
auto
|
||||||
| allowauto = liftIO startNoRepo
|
| allowauto = liftIO startNoRepo
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
d <- liftIO getCurrentDirectory
|
d <- liftIO getCurrentDirectory
|
||||||
error $ "no git repository in " ++ d
|
error $ "no git repository in " ++ d
|
||||||
checkpid = do
|
checkpid = do
|
||||||
pidfile <- fromRepo gitAnnexPidFile
|
pidfile <- fromRepo gitAnnexPidFile
|
||||||
liftIO $ isJust <$> checkDaemon pidfile
|
liftIO $ isJust <$> checkDaemon pidfile
|
||||||
checkshim f = liftIO $ doesFileExist f
|
checkshim f = liftIO $ doesFileExist f
|
||||||
|
|
||||||
{- When run without a repo, see if there is an autoStartFile,
|
{- When run without a repo, see if there is an autoStartFile,
|
||||||
- and if so, start the first available listed repository.
|
- and if so, start the first available listed repository.
|
||||||
|
@ -111,35 +111,35 @@ firstRun = do
|
||||||
webAppThread d urlrenderer True
|
webAppThread d urlrenderer True
|
||||||
(callback signaler)
|
(callback signaler)
|
||||||
(callback mainthread)
|
(callback mainthread)
|
||||||
where
|
where
|
||||||
signaler v = do
|
signaler v = do
|
||||||
putMVar v ""
|
putMVar v ""
|
||||||
takeMVar v
|
takeMVar v
|
||||||
mainthread v _url htmlshim = do
|
mainthread v _url htmlshim = do
|
||||||
browser <- maybe Nothing webBrowser <$> Git.Config.global
|
browser <- maybe Nothing webBrowser <$> Git.Config.global
|
||||||
openBrowser browser htmlshim
|
openBrowser browser htmlshim
|
||||||
|
|
||||||
_wait <- takeMVar v
|
_wait <- takeMVar v
|
||||||
|
|
||||||
state <- Annex.new =<< Git.CurrentRepo.get
|
state <- Annex.new =<< Git.CurrentRepo.get
|
||||||
Annex.eval state $ do
|
Annex.eval state $ do
|
||||||
dummydaemonize
|
dummydaemonize
|
||||||
startAssistant True id $ Just $ sendurlback v
|
startAssistant True id $ Just $ sendurlback v
|
||||||
sendurlback v url _htmlshim = putMVar v url
|
sendurlback v url _htmlshim = putMVar v url
|
||||||
{- Set up the pid file in the new repo. -}
|
|
||||||
dummydaemonize =
|
{- Set up the pid file in the new repo. -}
|
||||||
liftIO . lockPidFile =<< fromRepo gitAnnexPidFile
|
dummydaemonize = liftIO . lockPidFile =<< fromRepo gitAnnexPidFile
|
||||||
|
|
||||||
openBrowser :: Maybe FilePath -> FilePath -> IO ()
|
openBrowser :: Maybe FilePath -> FilePath -> IO ()
|
||||||
openBrowser cmd htmlshim = go $ maybe runBrowser runCustomBrowser cmd
|
openBrowser cmd htmlshim = go $ maybe runBrowser runCustomBrowser cmd
|
||||||
where
|
where
|
||||||
url = fileUrl htmlshim
|
url = fileUrl htmlshim
|
||||||
go a = do
|
go a = do
|
||||||
putStrLn ""
|
putStrLn ""
|
||||||
putStrLn $ "Launching web browser on " ++ url
|
putStrLn $ "Launching web browser on " ++ url
|
||||||
unlessM (a url) $
|
unlessM (a url) $
|
||||||
error $ "failed to start web browser"
|
error $ "failed to start web browser"
|
||||||
runCustomBrowser c u = boolSystem c [Param u]
|
runCustomBrowser c u = boolSystem c [Param u]
|
||||||
|
|
||||||
{- web.browser is a generic git config setting for a web browser program -}
|
{- web.browser is a generic git config setting for a web browser program -}
|
||||||
webBrowser :: Git.Repo -> Maybe FilePath
|
webBrowser :: Git.Repo -> Maybe FilePath
|
||||||
|
|
|
@ -40,15 +40,15 @@ perform remotemap key = do
|
||||||
forM_ (mapMaybe (`M.lookup` remotemap) locations) $
|
forM_ (mapMaybe (`M.lookup` remotemap) locations) $
|
||||||
performRemote key
|
performRemote key
|
||||||
if null safelocations then stop else next $ return True
|
if null safelocations then stop else next $ return True
|
||||||
where
|
where
|
||||||
copiesplural 1 = "copy"
|
copiesplural 1 = "copy"
|
||||||
copiesplural _ = "copies"
|
copiesplural _ = "copies"
|
||||||
untrustedheader = "The following untrusted locations may also have copies:\n"
|
untrustedheader = "The following untrusted locations may also have copies:\n"
|
||||||
|
|
||||||
performRemote :: Key -> Remote -> Annex ()
|
performRemote :: Key -> Remote -> Annex ()
|
||||||
performRemote key remote = maybe noop go $ whereisKey remote
|
performRemote key remote = maybe noop go $ whereisKey remote
|
||||||
where
|
where
|
||||||
go a = do
|
go a = do
|
||||||
ls <- a key
|
ls <- a key
|
||||||
unless (null ls) $ showLongNote $ unlines $
|
unless (null ls) $ showLongNote $ unlines $
|
||||||
map (\l -> name remote ++ ": " ++ l) ls
|
map (\l -> name remote ++ ": " ++ l) ls
|
||||||
|
|
Loading…
Reference in a new issue