--explain for preferred/required content matching
And annex.largefiles and annex.addunlocked. Also git-annex matchexpression --explain explains why its input expression matches or fails to match. When there is no limit, avoid explaining why the lack of limit matches. This is also done when no preferred content expression is set, although in a few cases it defaults to a non-empty matcher, which will be explained. Sponsored-by: Dartmouth College's DANDI project
This commit is contained in:
parent
ba1c222912
commit
518a51a8a0
12 changed files with 89 additions and 60 deletions
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex file matching
|
{- git-annex file matching
|
||||||
-
|
-
|
||||||
- Copyright 2012-2019 Joey Hess <id@joeyh.name>
|
- Copyright 2012-2023 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -50,6 +50,7 @@ import Annex.Magic
|
||||||
|
|
||||||
import Data.Either
|
import Data.Either
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
import Control.Monad.Writer
|
||||||
|
|
||||||
type GetFileMatcher = RawFilePath -> Annex (FileMatcher Annex)
|
type GetFileMatcher = RawFilePath -> Annex (FileMatcher Annex)
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ checkFileMatcher' getmatcher file notconfigured = do
|
||||||
|
|
||||||
checkMatcher :: FileMatcher Annex -> Maybe Key -> AssociatedFile -> AssumeNotPresent -> Annex Bool -> Annex Bool -> Annex Bool
|
checkMatcher :: FileMatcher Annex -> Maybe Key -> AssociatedFile -> AssumeNotPresent -> Annex Bool -> Annex Bool -> Annex Bool
|
||||||
checkMatcher matcher mkey afile notpresent notconfigured d
|
checkMatcher matcher mkey afile notpresent notconfigured d
|
||||||
| isEmpty matcher = notconfigured
|
| isEmpty (fst matcher) = notconfigured
|
||||||
| otherwise = case (mkey, afile) of
|
| otherwise = case (mkey, afile) of
|
||||||
(_, AssociatedFile (Just file)) ->
|
(_, AssociatedFile (Just file)) ->
|
||||||
go =<< fileMatchInfo file mkey
|
go =<< fileMatchInfo file mkey
|
||||||
|
@ -88,8 +89,13 @@ checkMatcher matcher mkey afile notpresent notconfigured d
|
||||||
go mi = checkMatcher' matcher mi notpresent
|
go mi = checkMatcher' matcher mi notpresent
|
||||||
|
|
||||||
checkMatcher' :: FileMatcher Annex -> MatchInfo -> AssumeNotPresent -> Annex Bool
|
checkMatcher' :: FileMatcher Annex -> MatchInfo -> AssumeNotPresent -> Annex Bool
|
||||||
checkMatcher' matcher mi notpresent =
|
checkMatcher' (matcher, (MatcherDesc matcherdesc)) mi notpresent = do
|
||||||
matchMrun matcher $ \o -> matchAction o notpresent mi
|
(matches, desc) <- runWriterT $ matchMrun' matcher $ \op ->
|
||||||
|
matchAction op notpresent mi
|
||||||
|
explain (mkActionItem mi) $ UnquotedString <$>
|
||||||
|
describeMatchResult matchDesc desc
|
||||||
|
((if matches then "matches " else "does not match ") ++ matcherdesc ++ ": ")
|
||||||
|
return matches
|
||||||
|
|
||||||
fileMatchInfo :: RawFilePath -> Maybe Key -> Annex MatchInfo
|
fileMatchInfo :: RawFilePath -> Maybe Key -> Annex MatchInfo
|
||||||
fileMatchInfo file mkey = do
|
fileMatchInfo file mkey = do
|
||||||
|
@ -100,12 +106,12 @@ fileMatchInfo file mkey = do
|
||||||
, matchKey = mkey
|
, matchKey = mkey
|
||||||
}
|
}
|
||||||
|
|
||||||
matchAll :: FileMatcher Annex
|
matchAll :: Matcher (MatchFiles Annex)
|
||||||
matchAll = generate []
|
matchAll = generate []
|
||||||
|
|
||||||
parsedToMatcher :: [ParseResult (MatchFiles Annex)] -> Either String (FileMatcher Annex)
|
parsedToMatcher :: MatcherDesc -> [ParseResult (MatchFiles Annex)] -> Either String (FileMatcher Annex)
|
||||||
parsedToMatcher parsed = case partitionEithers parsed of
|
parsedToMatcher matcherdesc parsed = case partitionEithers parsed of
|
||||||
([], vs) -> Right $ generate vs
|
([], vs) -> Right (generate vs, matcherdesc)
|
||||||
(es, _) -> Left $ unwords $ map ("Parse failure: " ++) es
|
(es, _) -> Left $ unwords $ map ("Parse failure: " ++) es
|
||||||
|
|
||||||
data ParseToken t
|
data ParseToken t
|
||||||
|
@ -149,8 +155,8 @@ commonKeyedTokens =
|
||||||
]
|
]
|
||||||
|
|
||||||
data PreferredContentData = PCD
|
data PreferredContentData = PCD
|
||||||
{ matchStandard :: Either String (FileMatcher Annex)
|
{ matchStandard :: Either String (Matcher (MatchFiles Annex))
|
||||||
, matchGroupWanted :: Either String (FileMatcher Annex)
|
, matchGroupWanted :: Either String (Matcher (MatchFiles Annex))
|
||||||
, getGroupMap :: Annex GroupMap
|
, getGroupMap :: Annex GroupMap
|
||||||
, configMap :: M.Map UUID RemoteConfig
|
, configMap :: M.Map UUID RemoteConfig
|
||||||
, repoUUID :: Maybe UUID
|
, repoUUID :: Maybe UUID
|
||||||
|
@ -227,6 +233,7 @@ mkMatchExpressionParser = do
|
||||||
largeFilesMatcher :: Annex GetFileMatcher
|
largeFilesMatcher :: Annex GetFileMatcher
|
||||||
largeFilesMatcher = go =<< getGitConfigVal' annexLargeFiles
|
largeFilesMatcher = go =<< getGitConfigVal' annexLargeFiles
|
||||||
where
|
where
|
||||||
|
matcherdesc = MatcherDesc "annex.largefiles"
|
||||||
go (HasGitConfig (Just expr)) = do
|
go (HasGitConfig (Just expr)) = do
|
||||||
matcher <- mkmatcher expr "git config"
|
matcher <- mkmatcher expr "git config"
|
||||||
return $ const $ return matcher
|
return $ const $ return matcher
|
||||||
|
@ -236,12 +243,13 @@ largeFilesMatcher = go =<< getGitConfigVal' annexLargeFiles
|
||||||
then case v of
|
then case v of
|
||||||
HasGlobalConfig (Just expr') ->
|
HasGlobalConfig (Just expr') ->
|
||||||
mkmatcher expr' "git-annex config"
|
mkmatcher expr' "git-annex config"
|
||||||
_ -> return matchAll
|
_ -> return (matchAll, matcherdesc)
|
||||||
else mkmatcher expr "gitattributes"
|
else mkmatcher expr "gitattributes"
|
||||||
|
|
||||||
mkmatcher expr cfgfrom = do
|
mkmatcher expr cfgfrom = do
|
||||||
parser <- mkMatchExpressionParser
|
parser <- mkMatchExpressionParser
|
||||||
either (badexpr cfgfrom) return $ parsedToMatcher $ parser expr
|
either (badexpr cfgfrom) return $ parsedToMatcher matcherdesc $ parser expr
|
||||||
|
|
||||||
badexpr cfgfrom e = giveup $ "bad annex.largefiles configuration in " ++ cfgfrom ++ ": " ++ e
|
badexpr cfgfrom e = giveup $ "bad annex.largefiles configuration in " ++ cfgfrom ++ ": " ++ e
|
||||||
|
|
||||||
newtype AddUnlockedMatcher = AddUnlockedMatcher (FileMatcher Annex)
|
newtype AddUnlockedMatcher = AddUnlockedMatcher (FileMatcher Annex)
|
||||||
|
@ -254,16 +262,19 @@ addUnlockedMatcher = AddUnlockedMatcher <$>
|
||||||
go (HasGlobalConfig (Just expr)) = mkmatcher expr "git annex config"
|
go (HasGlobalConfig (Just expr)) = mkmatcher expr "git annex config"
|
||||||
go _ = matchalways False
|
go _ = matchalways False
|
||||||
|
|
||||||
|
matcherdesc = MatcherDesc "annex.addunlocked"
|
||||||
|
|
||||||
mkmatcher :: String -> String -> Annex (FileMatcher Annex)
|
mkmatcher :: String -> String -> Annex (FileMatcher Annex)
|
||||||
mkmatcher expr cfgfrom = case Git.Config.isTrueFalse expr of
|
mkmatcher expr cfgfrom = case Git.Config.isTrueFalse expr of
|
||||||
Just b -> matchalways b
|
Just b -> matchalways b
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
parser <- mkMatchExpressionParser
|
parser <- mkMatchExpressionParser
|
||||||
either (badexpr cfgfrom) return $ parsedToMatcher $ parser expr
|
either (badexpr cfgfrom) return $ parsedToMatcher matcherdesc $ parser expr
|
||||||
|
|
||||||
badexpr cfgfrom e = giveup $ "bad annex.addunlocked configuration in " ++ cfgfrom ++ ": " ++ e
|
badexpr cfgfrom e = giveup $ "bad annex.addunlocked configuration in " ++ cfgfrom ++ ": " ++ e
|
||||||
|
|
||||||
matchalways True = return $ MOp limitAnything
|
matchalways True = return (MOp limitAnything, matcherdesc)
|
||||||
matchalways False = return $ MOp limitNothing
|
matchalways False = return (MOp limitNothing, matcherdesc)
|
||||||
|
|
||||||
checkAddUnlockedMatcher :: AddUnlockedMatcher -> MatchInfo -> Annex Bool
|
checkAddUnlockedMatcher :: AddUnlockedMatcher -> MatchInfo -> Annex Bool
|
||||||
checkAddUnlockedMatcher (AddUnlockedMatcher matcher) mi =
|
checkAddUnlockedMatcher (AddUnlockedMatcher matcher) mi =
|
||||||
|
@ -275,7 +286,7 @@ simply = Right . Operation
|
||||||
usev :: MkLimit Annex -> String -> ParseResult (MatchFiles Annex)
|
usev :: MkLimit Annex -> String -> ParseResult (MatchFiles Annex)
|
||||||
usev a v = Operation <$> a v
|
usev a v = Operation <$> a v
|
||||||
|
|
||||||
call :: String -> Either String (FileMatcher Annex) -> ParseResult (MatchFiles Annex)
|
call :: String -> Either String (Matcher (MatchFiles Annex)) -> ParseResult (MatchFiles Annex)
|
||||||
call desc (Right sub) = Right $ Operation $ MatchFiles
|
call desc (Right sub) = Right $ Operation $ MatchFiles
|
||||||
{ matchAction = \notpresent mi ->
|
{ matchAction = \notpresent mi ->
|
||||||
matchMrun sub $ \o -> matchAction o notpresent mi
|
matchMrun sub $ \o -> matchAction o notpresent mi
|
||||||
|
|
|
@ -771,7 +771,7 @@ importKeys remote importtreeconfig importcontent thirdpartypopulated importablec
|
||||||
let act = if importcontent
|
let act = if importcontent
|
||||||
then case Remote.importKey ia of
|
then case Remote.importKey ia of
|
||||||
Nothing -> dodownload
|
Nothing -> dodownload
|
||||||
Just _ -> if Utility.Matcher.introspect matchNeedsFileContent matcher
|
Just _ -> if Utility.Matcher.introspect matchNeedsFileContent (fst matcher)
|
||||||
then dodownload
|
then dodownload
|
||||||
else doimport
|
else doimport
|
||||||
else doimport
|
else doimport
|
||||||
|
@ -781,7 +781,7 @@ importKeys remote importtreeconfig importcontent thirdpartypopulated importablec
|
||||||
case Remote.importKey ia of
|
case Remote.importKey ia of
|
||||||
Nothing -> error "internal" -- checked earlier
|
Nothing -> error "internal" -- checked earlier
|
||||||
Just importkey -> do
|
Just importkey -> do
|
||||||
when (Utility.Matcher.introspect matchNeedsFileContent matcher) $
|
when (Utility.Matcher.introspect matchNeedsFileContent (fst matcher)) $
|
||||||
giveup "annex.largefiles configuration examines file contents, so cannot import without content."
|
giveup "annex.largefiles configuration examines file contents, so cannot import without content."
|
||||||
let mi = MatchingInfo ProvidedInfo
|
let mi = MatchingInfo ProvidedInfo
|
||||||
{ providedFilePath = Just f
|
{ providedFilePath = Just f
|
||||||
|
@ -994,14 +994,15 @@ addBackExportExcluded remote importtree =
|
||||||
-}
|
-}
|
||||||
makeImportMatcher :: Remote -> Annex (Either String (FileMatcher Annex))
|
makeImportMatcher :: Remote -> Annex (Either String (FileMatcher Annex))
|
||||||
makeImportMatcher r = load preferredContentKeylessTokens >>= \case
|
makeImportMatcher r = load preferredContentKeylessTokens >>= \case
|
||||||
Nothing -> return $ Right matchAll
|
Nothing -> return $ Right (matchAll, matcherdesc)
|
||||||
Just (Right v) -> return $ Right v
|
Just (Right v) -> return $ Right (v, matcherdesc)
|
||||||
Just (Left err) -> load preferredContentTokens >>= \case
|
Just (Left err) -> load preferredContentTokens >>= \case
|
||||||
Just (Left err') -> return $ Left err'
|
Just (Left err') -> return $ Left err'
|
||||||
_ -> return $ Left $
|
_ -> return $ Left $
|
||||||
"The preferred content expression contains terms that cannot be checked when importing: " ++ err
|
"The preferred content expression contains terms that cannot be checked when importing: " ++ err
|
||||||
where
|
where
|
||||||
load t = M.lookup (Remote.uuid r) . fst <$> preferredRequiredMapsLoad' t
|
load t = M.lookup (Remote.uuid r) . fst <$> preferredRequiredMapsLoad' t
|
||||||
|
matcherdesc = MatcherDesc "preferred content"
|
||||||
|
|
||||||
{- Gets the ImportableContents from the remote.
|
{- Gets the ImportableContents from the remote.
|
||||||
-
|
-
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
git-annex (10.20230627) UNRELEASED; urgency=medium
|
git-annex (10.20230627) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* --explain: New option to display explanations of what git-annex
|
* --explain: New option to display explanations of what git-annex
|
||||||
takes into account when deciding what to do.
|
takes into account when deciding what to do. Including explaining
|
||||||
|
matching of preferred content expressions, annex.largefiles, and
|
||||||
|
annex.addunlocked.
|
||||||
* satisfy: New command that gets/sends/drops content to satisfy
|
* satisfy: New command that gets/sends/drops content to satisfy
|
||||||
preferred content settings. This is like to the --content
|
preferred content settings. This is like to the --content
|
||||||
part of git-annex sync.
|
part of git-annex sync.
|
||||||
|
|
|
@ -491,8 +491,8 @@ filterExport :: Remote -> Git.Ref -> Annex (ExportFiltered Git.Ref)
|
||||||
filterExport r tree = logExportExcluded (uuid r) $ \logwriter -> do
|
filterExport r tree = logExportExcluded (uuid r) $ \logwriter -> do
|
||||||
m <- preferredContentMap
|
m <- preferredContentMap
|
||||||
case M.lookup (uuid r) m of
|
case M.lookup (uuid r) m of
|
||||||
Just matcher | not (isEmpty matcher) ->
|
Just (matcher, matcherdesc) | not (isEmpty matcher) ->
|
||||||
ExportFiltered <$> go (Just matcher) logwriter
|
ExportFiltered <$> go (Just (matcher, matcherdesc)) logwriter
|
||||||
_ -> ExportFiltered <$> go Nothing logwriter
|
_ -> ExportFiltered <$> go Nothing logwriter
|
||||||
where
|
where
|
||||||
go mmatcher logwriter = do
|
go mmatcher logwriter = do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex command
|
{- git-annex command
|
||||||
-
|
-
|
||||||
- Copyright 2016 Joey Hess <id@joeyh.name>
|
- Copyright 2016-2023 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -10,7 +10,6 @@ module Command.MatchExpression where
|
||||||
import Command
|
import Command
|
||||||
import Annex.FileMatcher
|
import Annex.FileMatcher
|
||||||
import Utility.DataUnits
|
import Utility.DataUnits
|
||||||
import Utility.Matcher
|
|
||||||
import Annex.UUID
|
import Annex.UUID
|
||||||
import Logs.Group
|
import Logs.Group
|
||||||
|
|
||||||
|
@ -84,15 +83,14 @@ seek o = do
|
||||||
, configMap = M.empty
|
, configMap = M.empty
|
||||||
, repoUUID = Just u
|
, repoUUID = Just u
|
||||||
}
|
}
|
||||||
case parsedToMatcher $ parser ((matchexpr o)) of
|
case parsedToMatcher (MatcherDesc "provided expression") $ parser ((matchexpr o)) of
|
||||||
Left e -> liftIO $ bail $ "bad expression: " ++ e
|
Left e -> liftIO $ bail $ "bad expression: " ++ e
|
||||||
Right matcher -> ifM (checkmatcher matcher)
|
Right matcher -> ifM (checkmatcher matcher)
|
||||||
( liftIO exitSuccess
|
( liftIO exitSuccess
|
||||||
, liftIO exitFailure
|
, liftIO exitFailure
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
checkmatcher matcher = matchMrun matcher $ \op ->
|
checkmatcher matcher = checkMatcher' matcher (matchinfo o) S.empty
|
||||||
matchAction op S.empty (matchinfo o)
|
|
||||||
|
|
||||||
bail :: String -> IO a
|
bail :: String -> IO a
|
||||||
bail s = do
|
bail s = do
|
||||||
|
|
11
Limit.hs
11
Limit.hs
|
@ -65,16 +65,11 @@ getMatcher = run <$> getMatcher'
|
||||||
(match, desc) <- runWriterT $
|
(match, desc) <- runWriterT $
|
||||||
Utility.Matcher.matchMrun' matcher $ \o ->
|
Utility.Matcher.matchMrun' matcher $ \o ->
|
||||||
matchAction o S.empty i
|
matchAction o S.empty i
|
||||||
explain (getfile i) $ UnquotedString $ unwords
|
explain (mkActionItem i) $ UnquotedString <$>
|
||||||
[ if match then "matches:" else "does not match:"
|
Utility.Matcher.describeMatchResult matchDesc desc
|
||||||
, Utility.Matcher.describeMatchResult matchDesc desc
|
(if match then "matches:" else "does not match:")
|
||||||
]
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
getfile (MatchingFile f) = Just (matchFile f)
|
|
||||||
getfile (MatchingInfo p) = providedFilePath p
|
|
||||||
getfile (MatchingUserInfo _) = Nothing
|
|
||||||
|
|
||||||
getMatcher' :: Annex (Utility.Matcher.Matcher (MatchFiles Annex))
|
getMatcher' :: Annex (Utility.Matcher.Matcher (MatchFiles Annex))
|
||||||
getMatcher' = go =<< Annex.getState Annex.limit
|
getMatcher' = go =<< Annex.getState Annex.limit
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex preferred content matcher configuration
|
{- git-annex preferred content matcher configuration
|
||||||
-
|
-
|
||||||
- Copyright 2012-2020 Joey Hess <id@joeyh.name>
|
- Copyright 2012-2023 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -70,7 +70,7 @@ introspectPreferredRequiredContent c mu = do
|
||||||
u <- maybe getUUID return mu
|
u <- maybe getUUID return mu
|
||||||
check u preferredContentMap <||> check u requiredContentMap
|
check u preferredContentMap <||> check u requiredContentMap
|
||||||
where
|
where
|
||||||
check u mk = mk >>= return . maybe False (any c) . M.lookup u
|
check u mk = mk >>= return . maybe False (any c . fst) . M.lookup u
|
||||||
|
|
||||||
preferredContentMap :: Annex (FileMatcherMap Annex)
|
preferredContentMap :: Annex (FileMatcherMap Annex)
|
||||||
preferredContentMap = maybe (fst <$> preferredRequiredMapsLoad preferredContentTokens) return
|
preferredContentMap = maybe (fst <$> preferredRequiredMapsLoad preferredContentTokens) return
|
||||||
|
@ -83,18 +83,18 @@ requiredContentMap = maybe (snd <$> preferredRequiredMapsLoad preferredContentTo
|
||||||
preferredRequiredMapsLoad :: (PreferredContentData -> [ParseToken (MatchFiles Annex)]) -> Annex (FileMatcherMap Annex, FileMatcherMap Annex)
|
preferredRequiredMapsLoad :: (PreferredContentData -> [ParseToken (MatchFiles Annex)]) -> Annex (FileMatcherMap Annex, FileMatcherMap Annex)
|
||||||
preferredRequiredMapsLoad mktokens = do
|
preferredRequiredMapsLoad mktokens = do
|
||||||
(pc, rc) <- preferredRequiredMapsLoad' mktokens
|
(pc, rc) <- preferredRequiredMapsLoad' mktokens
|
||||||
let pc' = handleunknown pc
|
let pc' = handleunknown (MatcherDesc "preferred content") pc
|
||||||
let rc' = handleunknown rc
|
let rc' = handleunknown (MatcherDesc "required content") rc
|
||||||
Annex.changeState $ \s -> s
|
Annex.changeState $ \s -> s
|
||||||
{ Annex.preferredcontentmap = Just pc'
|
{ Annex.preferredcontentmap = Just pc'
|
||||||
, Annex.requiredcontentmap = Just rc'
|
, Annex.requiredcontentmap = Just rc'
|
||||||
}
|
}
|
||||||
return (pc', rc')
|
return (pc', rc')
|
||||||
where
|
where
|
||||||
handleunknown = M.mapWithKey $ \u ->
|
handleunknown matcherdesc = M.mapWithKey $ \u v ->
|
||||||
either (const $ unknownMatcher u) id
|
(either (const $ unknownMatcher u) id v, matcherdesc)
|
||||||
|
|
||||||
preferredRequiredMapsLoad' :: (PreferredContentData -> [ParseToken (MatchFiles Annex)]) -> Annex (M.Map UUID (Either String (FileMatcher Annex)), M.Map UUID (Either String (FileMatcher Annex)))
|
preferredRequiredMapsLoad' :: (PreferredContentData -> [ParseToken (MatchFiles Annex)]) -> Annex (M.Map UUID (Either String (Matcher (MatchFiles Annex))), M.Map UUID (Either String (Matcher (MatchFiles Annex))))
|
||||||
preferredRequiredMapsLoad' mktokens = do
|
preferredRequiredMapsLoad' mktokens = do
|
||||||
groupmap <- groupMap
|
groupmap <- groupMap
|
||||||
configmap <- remoteConfigMap
|
configmap <- remoteConfigMap
|
||||||
|
@ -125,12 +125,12 @@ makeMatcher
|
||||||
-> UUID
|
-> UUID
|
||||||
-> (PreferredContentData -> [ParseToken (MatchFiles Annex)])
|
-> (PreferredContentData -> [ParseToken (MatchFiles Annex)])
|
||||||
-> PreferredContentExpression
|
-> PreferredContentExpression
|
||||||
-> Either String (FileMatcher Annex)
|
-> Either String (Matcher (MatchFiles Annex))
|
||||||
makeMatcher groupmap configmap groupwantedmap u mktokens = go True True
|
makeMatcher groupmap configmap groupwantedmap u mktokens = go True True
|
||||||
where
|
where
|
||||||
go expandstandard expandgroupwanted expr
|
go expandstandard expandgroupwanted expr
|
||||||
| null (lefts tokens) = Right $ generate $ rights tokens
|
| null (lefts tokens) = Right $ generate $ rights tokens
|
||||||
| otherwise = Left (unwords (lefts tokens))
|
| otherwise = Left $ unwords $ lefts tokens
|
||||||
where
|
where
|
||||||
tokens = preferredContentParser (mktokens pcd) expr
|
tokens = preferredContentParser (mktokens pcd) expr
|
||||||
pcd = PCD
|
pcd = PCD
|
||||||
|
@ -159,14 +159,15 @@ makeMatcher groupmap configmap groupwantedmap u mktokens = go True True
|
||||||
-
|
-
|
||||||
- This avoid unwanted/expensive changes to the content, until the problem
|
- This avoid unwanted/expensive changes to the content, until the problem
|
||||||
- is resolved. -}
|
- is resolved. -}
|
||||||
unknownMatcher :: UUID -> FileMatcher Annex
|
unknownMatcher :: UUID -> Matcher (MatchFiles Annex)
|
||||||
unknownMatcher u = generate [present]
|
unknownMatcher u = generate [present]
|
||||||
where
|
where
|
||||||
present = Operation $ limitPresent (Just u)
|
present = Operation $ limitPresent (Just u)
|
||||||
|
|
||||||
{- Checks if an expression can be parsed, if not returns Just error -}
|
{- Checks if an expression can be parsed, if not returns Just error -}
|
||||||
checkPreferredContentExpression :: PreferredContentExpression -> Maybe String
|
checkPreferredContentExpression :: PreferredContentExpression -> Maybe String
|
||||||
checkPreferredContentExpression expr = case parsedToMatcher tokens of
|
checkPreferredContentExpression expr =
|
||||||
|
case parsedToMatcher (MatcherDesc mempty) tokens of
|
||||||
Left e -> Just e
|
Left e -> Just e
|
||||||
Right _ -> Nothing
|
Right _ -> Nothing
|
||||||
where
|
where
|
||||||
|
|
11
Messages.hs
11
Messages.hs
|
@ -300,13 +300,14 @@ jsonOutputEnabled = withMessageState $ \s -> return $
|
||||||
JSONOutput _ -> True
|
JSONOutput _ -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
explain :: Maybe RawFilePath -> StringContainingQuotedPath -> Annex ()
|
explain :: ActionItem -> Maybe StringContainingQuotedPath -> Annex ()
|
||||||
explain Nothing _ = return ()
|
explain ai (Just msg) = do
|
||||||
explain (Just f) msg = do
|
|
||||||
rd <- Annex.getRead id
|
rd <- Annex.getRead id
|
||||||
when (Annex.explainenabled rd) $
|
when (Annex.explainenabled rd) $
|
||||||
outputMessage JSON.none id $
|
let d = actionItemDesc ai
|
||||||
"[" <> QuotedPath f <> " " <> msg <> "]\n"
|
in outputMessage JSON.none id $
|
||||||
|
"[" <> (if d == mempty then "" else (d <> " ")) <> msg <> "]\n"
|
||||||
|
explain _ _ = return ()
|
||||||
|
|
||||||
{- Prevents any concurrent console access while running an action, so
|
{- Prevents any concurrent console access while running an action, so
|
||||||
- that the action is the only thing using the console, and can eg prompt
|
- that the action is the only thing using the console, and can eg prompt
|
||||||
|
|
|
@ -15,6 +15,7 @@ module Types.ActionItem (
|
||||||
import Key
|
import Key
|
||||||
import Types.Transfer
|
import Types.Transfer
|
||||||
import Types.UUID
|
import Types.UUID
|
||||||
|
import Types.FileMatcher
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
import Git.Quote (StringContainingQuotedPath(..))
|
import Git.Quote (StringContainingQuotedPath(..))
|
||||||
import Utility.FileSystemEncoding
|
import Utility.FileSystemEncoding
|
||||||
|
@ -60,6 +61,15 @@ instance MkActionItem (BranchFilePath, Key) where
|
||||||
instance MkActionItem (Transfer, TransferInfo) where
|
instance MkActionItem (Transfer, TransferInfo) where
|
||||||
mkActionItem = uncurry ActionItemFailedTransfer
|
mkActionItem = uncurry ActionItemFailedTransfer
|
||||||
|
|
||||||
|
instance MkActionItem MatchInfo where
|
||||||
|
mkActionItem (MatchingFile i) = ActionItemTreeFile (matchFile i)
|
||||||
|
mkActionItem (MatchingInfo i) = case providedFilePath i of
|
||||||
|
Just f -> ActionItemTreeFile f
|
||||||
|
Nothing -> case providedKey i of
|
||||||
|
Just k -> ActionItemKey k
|
||||||
|
Nothing -> ActionItemOther Nothing
|
||||||
|
mkActionItem (MatchingUserInfo _) = ActionItemOther Nothing
|
||||||
|
|
||||||
actionItemDesc :: ActionItem -> StringContainingQuotedPath
|
actionItemDesc :: ActionItem -> StringContainingQuotedPath
|
||||||
actionItemDesc (ActionItemAssociatedFile (AssociatedFile (Just f)) _) =
|
actionItemDesc (ActionItemAssociatedFile (AssociatedFile (Just f)) _) =
|
||||||
QuotedPath f
|
QuotedPath f
|
||||||
|
|
|
@ -76,6 +76,8 @@ getUserInfo :: MonadIO m => UserInfo a -> m a
|
||||||
getUserInfo (Right i) = return i
|
getUserInfo (Right i) = return i
|
||||||
getUserInfo (Left e) = liftIO e
|
getUserInfo (Left e) = liftIO e
|
||||||
|
|
||||||
|
newtype MatcherDesc = MatcherDesc String
|
||||||
|
|
||||||
type FileMatcherMap a = M.Map UUID (FileMatcher a)
|
type FileMatcherMap a = M.Map UUID (FileMatcher a)
|
||||||
|
|
||||||
type MkLimit a = String -> Either String (MatchFiles a)
|
type MkLimit a = String -> Either String (MatchFiles a)
|
||||||
|
@ -97,7 +99,7 @@ data MatchFiles a = MatchFiles
|
||||||
-- ^ displayed to the user to describe whether it matched or not
|
-- ^ displayed to the user to describe whether it matched or not
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileMatcher a = Matcher (MatchFiles a)
|
type FileMatcher a = (Matcher (MatchFiles a), MatcherDesc)
|
||||||
|
|
||||||
-- This is a matcher that can have tokens added to it while it's being
|
-- This is a matcher that can have tokens added to it while it's being
|
||||||
-- built, and once complete is compiled to an unchangeable matcher.
|
-- built, and once complete is compiled to an unchangeable matcher.
|
||||||
|
|
|
@ -215,9 +215,12 @@ introspect :: (a -> Bool) -> Matcher a -> Bool
|
||||||
introspect = any
|
introspect = any
|
||||||
|
|
||||||
{- Converts a [MatchResult] into a description of what matched and didn't
|
{- Converts a [MatchResult] into a description of what matched and didn't
|
||||||
- match. -}
|
- match. Returns Nothing when the matcher didn't contain any operations
|
||||||
describeMatchResult :: (op -> Bool -> MatchDesc) -> [MatchResult op] -> String
|
- and so matched by default. -}
|
||||||
describeMatchResult descop = unwords . go . simplify True
|
describeMatchResult :: (op -> Bool -> MatchDesc) -> [MatchResult op] -> String -> Maybe String
|
||||||
|
describeMatchResult _ [] _ = Nothing
|
||||||
|
describeMatchResult descop l prefix = Just $
|
||||||
|
prefix ++ unwords (go $ simplify True l)
|
||||||
where
|
where
|
||||||
go [] = []
|
go [] = []
|
||||||
go (MatchedOperation b op:rest) =
|
go (MatchedOperation b op:rest) =
|
||||||
|
|
|
@ -53,6 +53,11 @@ For example, this will exit 0:
|
||||||
Tell what the mime encoding of the file is. Only needed when using
|
Tell what the mime encoding of the file is. Only needed when using
|
||||||
--largefiles with a mimeencoding= expression.
|
--largefiles with a mimeencoding= expression.
|
||||||
|
|
||||||
|
* `--explain`
|
||||||
|
|
||||||
|
Display explanation of what parts of the preferred content expression
|
||||||
|
match, and which parts don't match.
|
||||||
|
|
||||||
* Also the [[git-annex-common-options]](1) can be used.
|
* Also the [[git-annex-common-options]](1) can be used.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
Loading…
Add table
Reference in a new issue