diff --git a/Annex/MetaData.hs b/Annex/MetaData.hs index 4e0a541af9..e821101c0a 100644 --- a/Annex/MetaData.hs +++ b/Annex/MetaData.hs @@ -107,7 +107,7 @@ parseMetaDataMatcher p = (,) ('>':v) -> checkcmp (>) v _ -> checkglob "" checkglob v = - let cglob = compileGlob v CaseInsensative + let cglob = compileGlob v CaseInsensative (GlobFilePath False) in matchGlob cglob . decodeBS . fromMetaValue checkcmp cmp v v' = case (doubleval v, doubleval (decodeBS (fromMetaValue v'))) of (Just d, Just d') -> d' `cmp` d diff --git a/Annex/View.hs b/Annex/View.hs index f13c92909f..9cbf9110fc 100644 --- a/Annex/View.hs +++ b/Annex/View.hs @@ -163,11 +163,11 @@ combineViewFilter old@(ExcludeValues olds) (ExcludeValues news) combineViewFilter (FilterValues _) newglob@(FilterGlob _) = (newglob, Widening) combineViewFilter (FilterGlob oldglob) new@(FilterValues s) - | all (matchGlob (compileGlob oldglob CaseInsensative) . decodeBS . fromMetaValue) (S.toList s) = (new, Narrowing) + | all (matchGlob (compileGlob oldglob CaseInsensative (GlobFilePath False)) . decodeBS . fromMetaValue) (S.toList s) = (new, Narrowing) | otherwise = (new, Widening) combineViewFilter (FilterGlob old) newglob@(FilterGlob new) | old == new = (newglob, Unchanged) - | matchGlob (compileGlob old CaseInsensative) new = (newglob, Narrowing) + | matchGlob (compileGlob old CaseInsensative (GlobFilePath False)) new = (newglob, Narrowing) | otherwise = (newglob, Widening) combineViewFilter (FilterGlob _) new@(ExcludeValues _) = (new, Narrowing) combineViewFilter (ExcludeValues _) new@(FilterGlob _) = (new, Widening) @@ -216,7 +216,7 @@ viewComponentMatcher viewcomponent = \metadata -> FilterValues s -> \values -> setmatches $ S.intersection s values FilterGlob glob -> - let cglob = compileGlob glob CaseInsensative + let cglob = compileGlob glob CaseInsensative (GlobFilePath False) in \values -> setmatches $ S.filter (matchGlob cglob . decodeBS . fromMetaValue) values ExcludeValues excludes -> \values -> diff --git a/CHANGELOG b/CHANGELOG index aaaa508b71..2da82ed5b9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,9 @@ git-annex (8.20201128) UNRELEASED; urgency=medium enclosure, but only a link to an url which youtube-dl does not support. * initremote: Prevent enabling encryption with exporttree=yes or importtree=yes. + * Windows: include= and exclude= containing '/' will also match filenames + that are written using '\'. (And vice-versa, but it's better to use '/' + for portability.) -- Joey Hess Mon, 30 Nov 2020 12:55:49 -0400 diff --git a/Limit.hs b/Limit.hs index 2e03e86a99..6da8cd0c49 100644 --- a/Limit.hs +++ b/Limit.hs @@ -115,7 +115,7 @@ limitExclude glob = Right $ MatchFiles matchGlobFile :: String -> MatchInfo -> Annex Bool matchGlobFile glob = go where - cglob = compileGlob glob CaseSensative -- memoized + cglob = compileGlob glob CaseSensative (GlobFilePath True) -- memoized go (MatchingFile fi) = pure $ matchGlob cglob (fromRawFilePath (matchFile fi)) go (MatchingInfo p) = pure $ matchGlob cglob (fromRawFilePath (providedFilePath p)) go (MatchingUserInfo p) = matchGlob cglob <$> getUserInfo (userProvidedFilePath p) @@ -166,7 +166,7 @@ matchMagic _limitname querymagic selectprovidedinfo selectuserprovidedinfo (Just , matchNeedsLocationLog = False } where - cglob = compileGlob glob CaseSensative -- memoized + cglob = compileGlob glob CaseSensative (GlobFilePath False) -- memoized go (MatchingKey _ _) = pure False go (MatchingFile fi) = case contentFile fi of Just f -> catchBoolIO $ diff --git a/Types/RefSpec.hs b/Types/RefSpec.hs index 0f3dded9d9..0567622319 100644 --- a/Types/RefSpec.hs +++ b/Types/RefSpec.hs @@ -22,7 +22,7 @@ data RefSpecPart | RemoveMatching Glob allRefSpec :: RefSpec -allRefSpec = [AddMatching $ compileGlob "*" CaseSensative] +allRefSpec = [AddMatching $ compileGlob "*" CaseSensative (GlobFilePath False)] parseRefSpec :: String -> Either String RefSpec parseRefSpec v = case partitionEithers (map mk $ splitc ':' v) of @@ -31,9 +31,9 @@ parseRefSpec v = case partitionEithers (map mk $ splitc ':' v) of where mk ('+':s) | any (`elem` s) "*?" = - Right $ AddMatching $ compileGlob s CaseSensative + Right $ AddMatching $ compileGlob s CaseSensative (GlobFilePath False) | otherwise = Right $ AddRef $ Ref $ encodeBS s - mk ('-':s) = Right $ RemoveMatching $ compileGlob s CaseSensative + mk ('-':s) = Right $ RemoveMatching $ compileGlob s CaseSensative (GlobFilePath False) mk "reflog" = Right AddRefLog mk s = Left $ "bad refspec item \"" ++ s ++ "\" (expected + or - prefix)" diff --git a/Utility/Glob.hs b/Utility/Glob.hs index c7d535933d..9f2d147b4c 100644 --- a/Utility/Glob.hs +++ b/Utility/Glob.hs @@ -1,15 +1,17 @@ -{-# LANGUAGE PackageImports #-} - {- file globbing - - - Copyright 2014 Joey Hess + - Copyright 2014-2020 Joey Hess - - License: BSD-2-clause -} +{-# LANGUAGE CPP #-} +{-# LANGUAGE PackageImports #-} + module Utility.Glob ( Glob, GlobCase(..), + GlobFilePath(..), compileGlob, matchGlob ) where @@ -24,26 +26,42 @@ newtype Glob = Glob Regex data GlobCase = CaseSensative | CaseInsensative +-- Is the glob being used to match filenames? +-- +-- When matching filenames, +-- a single path separator (eg /) in the glob will match any +-- number of path separators in the filename. +-- And on Windows, both / and \ are used as path separators, so compile +-- the glob to a regexp that matches either path separator. +newtype GlobFilePath = GlobFilePath Bool + {- Compiles a glob to a regex, that can be repeatedly used. -} -compileGlob :: String -> GlobCase -> Glob -compileGlob glob globcase = Glob $ +compileGlob :: String -> GlobCase -> GlobFilePath -> Glob +compileGlob glob globcase globfilepath = Glob $ case compile (defaultCompOpt {caseSensitive = casesentitive}) defaultExecOpt regex of Right r -> r Left _ -> giveup $ "failed to compile regex: " ++ regex where - regex = '^' : wildToRegex glob ++ "$" + regex = '^' : wildToRegex globfilepath glob ++ "$" casesentitive = case globcase of CaseSensative -> True CaseInsensative -> False -wildToRegex :: String -> String -wildToRegex = concat . go +wildToRegex :: GlobFilePath -> String -> String +wildToRegex (GlobFilePath globfile) = concat . go where go [] = [] go ('*':xs) = ".*" : go xs go ('?':xs) = "." : go xs go ('[':'!':xs) = "[^" : inpat xs go ('[':xs) = "[" : inpat xs +#ifdef mingw32_HOST_OS + go ('/':xs) | globfile = "[/\\]+" : go xs + go ('\\':xs) | globfile = "[/\\]+" : go xs +#else + go ('/':xs) | globfile = "[/]+" : go xs + go ('\\':xs) | globfile = "[\\]+" : go xs +#endif go (x:xs) | isDigit x || isAlpha x = [x] : go xs | otherwise = esc x : go xs diff --git a/doc/bugs/drop_claims_that_content_is_required___40__8.20201127__41__/comment_3_9f0415b3945ee80698b5ee50351f1a5c._comment b/doc/bugs/drop_claims_that_content_is_required___40__8.20201127__41__/comment_3_9f0415b3945ee80698b5ee50351f1a5c._comment new file mode 100644 index 0000000000..b0e38eb212 --- /dev/null +++ b/doc/bugs/drop_claims_that_content_is_required___40__8.20201127__41__/comment_3_9f0415b3945ee80698b5ee50351f1a5c._comment @@ -0,0 +1,11 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 3""" + date="2020-12-15T16:16:21Z" + content=""" +Hmm, yes preferred content expressions on windows ought +to let `/` be used and still match on `\`. (And vice-versa, although then +the preferred content expression is windows-specific.) + +I've implemented that now. +"""]]