]> dgit.raspbian.org Git - git-annex.git/commitdiff
ByteString Ref continued
authorJoey Hess <joeyh@joeyh.name>
Tue, 7 Apr 2020 15:54:27 +0000 (11:54 -0400)
committerJoey Hess <joeyh@joeyh.name>
Tue, 7 Apr 2020 15:54:27 +0000 (11:54 -0400)
Attoparsec parser for diff-tree.

Changed fromRef back to producing a String, to avoid needing to convert
every use of it. However, this does mean I'm going to miss some
opportunities where fromRef is used and the result converted back to a
ByteString. Would be worth revisiting that at some point maybe.

12 files changed:
Git.hs
Git/DiffTree.hs
Git/DiffTreeItem.hs
Git/FilePath.hs
Git/LsTree.hs
Git/Objects.hs
Git/Ref.hs
Git/RefLog.hs
Git/Types.hs
Git/UpdateIndex.hs
Types/RefSpec.hs
Utility/Misc.hs

diff --git a/Git.hs b/Git.hs
index 87a8d1972011613e21d38f1dc1a4273532a3869d..d33345ed3f1129f034fbd1a77efebcc135492ca3 100644 (file)
--- a/Git.hs
+++ b/Git.hs
@@ -14,6 +14,7 @@ module Git (
        Repo(..),
        Ref(..),
        fromRef,
+       fromRef',
        Branch,
        Sha,
        Tag,
index bfd1a7a1bc8256ada61b3e8d6356932866e50837..6e69a91a703a1c3c3162cc585e21c6b7830221f6 100644 (file)
@@ -1,6 +1,6 @@
 {- git diff-tree interface
  -
- - Copyright 2012 Joey Hess <id@joeyh.name>
+ - Copyright 2012-2020 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -18,6 +18,9 @@ module Git.DiffTree (
 ) where
 
 import Numeric
+import qualified Data.ByteString.Lazy as L
+import qualified Data.Attoparsec.ByteString.Lazy as A
+import qualified Data.Attoparsec.ByteString.Char8 as A8
 
 import Common
 import Git
@@ -27,6 +30,7 @@ import Git.FilePath
 import Git.DiffTreeItem
 import qualified Git.Filename
 import qualified Git.Ref
+import Utility.Attoparsec
 
 {- Checks if the DiffTreeItem modifies a file with a given name
  - or under a directory by that name. -}
@@ -89,7 +93,7 @@ commitDiff ref = getdiff (Param "show")
 getdiff :: CommandParam -> [CommandParam] -> Repo -> IO ([DiffTreeItem], IO Bool)
 getdiff command params repo = do
        (diff, cleanup) <- pipeNullSplit ps repo
-       return (parseDiffRaw (map decodeBL diff), cleanup)
+       return (parseDiffRaw diff, cleanup)
   where
        ps = 
                command :
@@ -100,26 +104,28 @@ getdiff command params repo = do
                params
 
 {- Parses --raw output used by diff-tree and git-log. -}
-parseDiffRaw :: [String] -> [DiffTreeItem]
+parseDiffRaw :: [L.ByteString] -> [DiffTreeItem]
 parseDiffRaw l = go l
   where
        go [] = []
-       go (info:f:rest) = mk info f : go rest
-       go (s:[]) = error $ "diff-tree parse error near \"" ++ s ++ "\""
-
-       mk info f = DiffTreeItem
-               { srcmode = readmode srcm
-               , dstmode = readmode dstm
-               , srcsha = fromMaybe (error "bad srcsha") $ extractSha ssha
-               , dstsha = fromMaybe (error "bad dstsha") $ extractSha dsha
-               , status = s
-               , file = asTopFilePath $ fromInternalGitPath $ Git.Filename.decode $ toRawFilePath f
-               }
-         where
-               readmode = fst . Prelude.head . readOct
-
-               -- info = :<srcmode> SP <dstmode> SP <srcsha> SP <dstsha> SP <status>
-               (srcm, past_srcm) = splitAt 7 $ drop 1 info
-               (dstm, past_dstm) = splitAt 7 past_srcm
-               (ssha, past_ssha) = separate (== ' ') past_dstm
-               (dsha, s) = separate (== ' ') past_ssha
+       go (info:f:rest) = case A.parse (parserDiffRaw (L.toStrict f)) info of
+               A.Done _ r -> r : go rest
+               A.Fail _ _ err -> error $ "diff-tree parse error: " ++ err
+       go (s:[]) = error $ "diff-tree parse error near \"" ++ decodeBL' s ++ "\""
+
+-- :<srcmode> SP <dstmode> SP <srcsha> SP <dstsha> SP <status>
+parserDiffRaw :: RawFilePath -> A.Parser DiffTreeItem
+parserDiffRaw f = DiffTreeItem
+       <$ A8.char ':'
+       <*> octal
+       <* A8.char ' '
+       <*> octal
+       <* A8.char ' '
+       <*> (maybe (fail "bad srcsha") return . extractSha =<< nextword)
+       <* A8.char ' '
+       <*> (maybe (fail "bad dstsha") return . extractSha =<< nextword)
+       <* A8.char ' '
+       <*> A.takeByteString
+       <*> pure (asTopFilePath $ fromInternalGitPath $ Git.Filename.decode f)
+  where
+       nextword = A8.takeTill (== ' ')
index 4034e5ecfbdb93a41def49a5ff1836c6c8b8eaa8..090ad3e008944cf0140ae322a734031fced85dd7 100644 (file)
@@ -10,6 +10,7 @@ module Git.DiffTreeItem (
 ) where
 
 import System.Posix.Types
+import qualified Data.ByteString as S
 
 import Git.FilePath
 import Git.Types
@@ -19,6 +20,6 @@ data DiffTreeItem = DiffTreeItem
        , dstmode :: FileMode
        , srcsha :: Sha -- null sha if file was added
        , dstsha :: Sha -- null sha if file was deleted
-       , status :: String
+       , status :: S.ByteString
        , file :: TopFilePath
        } deriving Show
index ea9cceaa87d56df77a4c2e174fdb7278d27f4986..d31b421a5aafda41d854e16c94d40ce8ef88e4bf 100644 (file)
@@ -50,7 +50,7 @@ data BranchFilePath = BranchFilePath Ref TopFilePath
 {- Git uses the branch:file form to refer to a BranchFilePath -}
 descBranchFilePath :: BranchFilePath -> S.ByteString
 descBranchFilePath (BranchFilePath b f) =
-       fromRef b <> ":" <> getTopFilePath f
+       fromRef' b <> ":" <> getTopFilePath f
 
 {- Path to a TopFilePath, within the provided git repo. -}
 fromTopFilePath :: TopFilePath -> Git.Repo -> RawFilePath
index ac059bbdff9db1ee5505852420039bd3dca68adb..ead501f0dca4e214d87437b805c9c23fd9bfb2e7 100644 (file)
@@ -96,7 +96,7 @@ parserLsTree = TreeItem
        <*> A8.takeTill (== ' ')
        <* A8.char ' '
        -- sha
-       <*> (Ref . decodeBS' <$> A8.takeTill (== '\t'))
+       <*> (Ref <$> A8.takeTill (== '\t'))
        <* A8.char '\t'
        -- file
        <*> (asTopFilePath . Git.Filename.decode <$> A.takeByteString)
@@ -106,6 +106,6 @@ formatLsTree :: TreeItem -> String
 formatLsTree ti = unwords
        [ showOct (mode ti) ""
        , decodeBS (typeobj ti)
-       , decodeBS' (fromRef (sha ti))
+       , fromRef (sha ti)
        , fromRawFilePath (getTopFilePath (file ti))
        ]
index 6f76886f5f83145a4e8d1e7ee97f5fccf9478746..6a240875e09c7b67e010f784334a610452f58dd2 100644 (file)
@@ -32,7 +32,7 @@ listLooseObjectShas r = catchDefaultIO [] $
 looseObjectFile :: Repo -> Sha -> FilePath
 looseObjectFile r sha = objectsDir r </> prefix </> rest
   where
-       (prefix, rest) = splitAt 2 (decodeBS' (fromRef sha))
+       (prefix, rest) = splitAt 2 (fromRef sha)
 
 listAlternates :: Repo -> IO [FilePath]
 listAlternates r = catchDefaultIO [] (lines <$> readFile alternatesfile)
index 433f423b9c24eee5c6521ff07c9e95e481124c21..33922d1e301666547dd2b1248647ccbf38ef11cf 100644 (file)
@@ -17,6 +17,7 @@ import Git.Types
 
 import Data.Char (chr, ord)
 import qualified Data.ByteString as S
+import qualified Data.ByteString.Char8 as S8
 
 headRef :: Ref
 headRef = Ref "HEAD"
@@ -41,10 +42,11 @@ base = removeBase "refs/heads/" . removeBase "refs/remotes/"
 {- Removes a directory such as "refs/heads/master" from a
  - fully qualified ref. Any ref not starting with it is left as-is. -}
 removeBase :: String -> Ref -> Ref
-removeBase dir (Ref r)
-       | prefix `isPrefixOf` r = Ref (drop (length prefix) r)
-       | otherwise = Ref r
+removeBase dir r
+       | prefix `isPrefixOf` rs = Ref $ encodeBS $ drop (length prefix) rs
+       | otherwise = r
   where
+       rs = fromRef r
        prefix = case end dir of
                ['/'] -> dir
                _ -> dir ++ "/"
@@ -53,7 +55,7 @@ removeBase dir (Ref r)
  - refs/heads/master, yields a version of that ref under the directory,
  - such as refs/remotes/origin/master. -}
 underBase :: String -> Ref -> Ref
-underBase dir r = Ref $ dir ++ "/" ++ fromRef (base r)
+underBase dir r = Ref $ encodeBS' $ dir ++ "/" ++ fromRef (base r)
 
 {- Convert a branch such as "master" into a fully qualified ref. -}
 branchRef :: Branch -> Ref
@@ -66,21 +68,25 @@ branchRef = underBase "refs/heads"
  - of a repo.
  -}
 fileRef :: RawFilePath -> Ref
-fileRef f = Ref $ ":./" ++ fromRawFilePath f
+fileRef f = Ref $ ":./" <> f
 
 {- Converts a Ref to refer to the content of the Ref on a given date. -}
 dateRef :: Ref -> RefDate -> Ref
-dateRef (Ref r) (RefDate d) = Ref $ r ++ "@" ++ d
+dateRef r (RefDate d) = Ref $ fromRef' r <> "@" <> encodeBS' d
 
 {- A Ref that can be used to refer to a file in the repository as it
  - appears in a given Ref. -}
 fileFromRef :: Ref -> RawFilePath -> Ref
-fileFromRef (Ref r) f = let (Ref fr) = fileRef f in Ref (r ++ fr)
+fileFromRef r f = let (Ref fr) = fileRef f in Ref (fromRef' r <> fr)
 
 {- Checks if a ref exists. -}
 exists :: Ref -> Repo -> IO Bool
 exists ref = runBool
-       [Param "show-ref", Param "--verify", Param "-q", Param $ fromRef ref]
+       [ Param "show-ref"
+       , Param "--verify"
+       , Param "-q"
+       , Param $ fromRef ref
+       ]
 
 {- The file used to record a ref. (Git also stores some refs in a
  - packed-refs file.) -}
@@ -107,26 +113,26 @@ sha branch repo = process <$> showref repo
                ]
        process s
                | S.null s = Nothing
-               | otherwise = Just $ Ref $ decodeBS' $ firstLine' s
+               | otherwise = Just $ Ref $ firstLine' s
 
 headSha :: Repo -> IO (Maybe Sha)
 headSha = sha headRef
 
 {- List of (shas, branches) matching a given ref or refs. -}
 matching :: [Ref] -> Repo -> IO [(Sha, Branch)]
-matching refs repo =  matching' (map fromRef refs) repo
+matching = matching' []
 
 {- Includes HEAD in the output, if asked for it. -}
 matchingWithHEAD :: [Ref] -> Repo -> IO [(Sha, Branch)]
-matchingWithHEAD refs repo = matching' ("--head" : map fromRef refs) repo
+matchingWithHEAD = matching' [Param "--head"]
 
-{- List of (shas, branches) matching a given ref spec. -}
-matching' :: [String] -> Repo -> IO [(Sha, Branch)]
-matching' ps repo = map gen . lines . decodeBS' <$> 
-       pipeReadStrict (Param "show-ref" : map Param ps) repo
+matching' :: [CommandParam] -> [Ref] -> Repo -> IO [(Sha, Branch)]
+matching' ps rs repo = map gen . S8.lines <$> 
+       pipeReadStrict (Param "show-ref" : ps ++ rps) repo
   where
-       gen l = let (r, b) = separate (== ' ') l
+       gen l = let (r, b) = separate' (== fromIntegral (ord ' ')) l
                in (Ref r, Ref b)
+       rps = map (Param . fromRef) rs
 
 {- List of (shas, branches) matching a given ref.
  - Duplicate shas are filtered out. -}
@@ -137,7 +143,7 @@ matchingUniq refs repo = nubBy uniqref <$> matching refs repo
 
 {- List of all refs. -}
 list :: Repo -> IO [(Sha, Ref)]
-list = matching' []
+list = matching' [] []
 
 {- Deletes a ref. This can delete refs that are not branches, 
  - which git branch --delete refuses to delete. -}
@@ -145,8 +151,8 @@ delete :: Sha -> Ref -> Repo -> IO ()
 delete oldvalue ref = run
        [ Param "update-ref"
        , Param "-d"
-       , Param $ decodeBS' (fromRef ref)
-       , Param $ decodeBS' (fromRef oldvalue)
+       , Param $ fromRef ref
+       , Param $ fromRef oldvalue
        ]
 
 {- Gets the sha of the tree a ref uses. 
index e8fe6a2217e17ccc874be375209bb1ade4d43eb6..b98833c391fc611ea0fad43975946b8231884800 100644 (file)
@@ -21,7 +21,7 @@ get b = getMulti [b]
 
 {- Gets reflogs for multiple branches. -}
 getMulti :: [Branch] -> Repo -> IO [Sha]
-getMulti bs = get' (map (Param . decodeBS' . fromRef) bs)
+getMulti bs = get' (map (Param . fromRef) bs)
 
 get' :: [CommandParam] -> Repo -> IO [Sha]
 get' ps = mapMaybe (extractSha . S.copy) . S8.lines <$$> pipeReadStrict ps'
index 84238c7c4e633b5677a09b53d117bd27b20746ab..6e4558d8fd58a792b442c0cf6caaa010ff7786c6 100644 (file)
@@ -84,8 +84,11 @@ type RemoteName = String
 newtype Ref = Ref S.ByteString
        deriving (Eq, Ord, Read, Show)
 
-fromRef :: Ref -> S.ByteString
-fromRef (Ref s) = s
+fromRef :: Ref -> String
+fromRef = decodeBS' . fromRef'
+
+fromRef' :: Ref -> S.ByteString
+fromRef' (Ref s) = s
 
 {- Aliases for Ref. -}
 type Branch = Ref
index 59d08437de5064f363bbbfb20abc4a2122c3f97c..f0331d5c1f0075bda0f32aeec2d9906d3f7c8411 100644 (file)
@@ -90,7 +90,7 @@ updateIndexLine :: Sha -> TreeItemType -> TopFilePath -> L.ByteString
 updateIndexLine sha treeitemtype file = L.fromStrict $
        fmtTreeItemType treeitemtype
        <> " blob "
-       <> fromRef sha
+       <> fromRef' sha
        <> "\t"
        <> indexPath file
 
@@ -108,7 +108,7 @@ unstageFile file repo = do
 unstageFile' :: TopFilePath -> Streamer
 unstageFile' p = pureStreamer $ L.fromStrict $
        "0 "
-       <> fromRef deleteSha
+       <> fromRef' deleteSha
        <> "\t"
        <> indexPath p
 
index 1028ed5233089a86e1546dda6392f6723d3abe00..0f3dded9d937376835b3dfe56504d4abbcfe0102 100644 (file)
@@ -43,10 +43,10 @@ applyRefSpec refspec rs getreflog = go [] refspec
        go c [] = return (reverse c)
        go c (AddRef r : rest) = go (r:c) rest
        go c (AddMatching g : rest) =
-               let add = filter (matchGlob g . decodeBS' . fromRef) rs
+               let add = filter (matchGlob g . fromRef) rs
                in go (add ++ c) rest
        go c (AddRefLog : rest) = do
                reflog <- getreflog
                go (reflog ++ c) rest
        go c (RemoveMatching g : rest) = 
-               go (filter (not . matchGlob g . decodeBS' . fromRef) c) rest
+               go (filter (not . matchGlob g . fromRef) c) rest
index 2f1766ec23bb2b57552415f6acb803b325fca6f5..01ae178d85feaf152f4da3ef1dc3d30b6b834911 100644 (file)
@@ -11,6 +11,7 @@ module Utility.Misc (
        hGetContentsStrict,
        readFileStrict,
        separate,
+       separate',
        firstLine,
        firstLine',
        segment,
@@ -54,6 +55,13 @@ separate c l = unbreak $ break c l
                | null b = r
                | otherwise = (a, tail b)
 
+separate' :: (Word8 -> Bool) -> S.ByteString -> (S.ByteString, S.ByteString)
+separate' c l = unbreak $ S.break c l
+  where
+       unbreak r@(a, b)
+               | S.null b = r
+               | otherwise = (a, S.tail b)
+
 {- Breaks out the first line. -}
 firstLine :: String -> String
 firstLine = takeWhile (/= '\n')