2012-12-10 18:36:57 +00:00
|
|
|
{- git diff-tree interface
|
|
|
|
-
|
2020-04-07 15:54:27 +00:00
|
|
|
- Copyright 2012-2020 Joey Hess <id@joeyh.name>
|
2012-12-10 18:36:57 +00:00
|
|
|
-
|
2019-03-13 19:48:14 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2012-12-10 18:36:57 +00:00
|
|
|
-}
|
|
|
|
|
|
|
|
module Git.DiffTree (
|
|
|
|
DiffTreeItem(..),
|
2014-11-13 20:41:21 +00:00
|
|
|
isDiffOf,
|
2012-12-10 18:36:57 +00:00
|
|
|
diffTree,
|
2012-12-18 19:04:44 +00:00
|
|
|
diffTreeRecursive,
|
2013-02-06 16:40:59 +00:00
|
|
|
diffIndex,
|
2013-08-26 17:01:48 +00:00
|
|
|
diffWorkTree,
|
2016-01-07 00:38:02 +00:00
|
|
|
diffFiles,
|
2014-11-13 20:41:21 +00:00
|
|
|
diffLog,
|
2016-03-11 17:15:49 +00:00
|
|
|
commitDiff,
|
2012-12-10 18:36:57 +00:00
|
|
|
) where
|
|
|
|
|
2020-10-28 19:40:50 +00:00
|
|
|
import qualified Data.ByteString as B
|
2020-04-07 15:54:27 +00:00
|
|
|
import qualified Data.ByteString.Lazy as L
|
|
|
|
import qualified Data.Attoparsec.ByteString.Lazy as A
|
|
|
|
import qualified Data.Attoparsec.ByteString.Char8 as A8
|
2012-12-10 18:36:57 +00:00
|
|
|
|
|
|
|
import Common
|
|
|
|
import Git
|
|
|
|
import Git.Sha
|
|
|
|
import Git.Command
|
2013-10-17 18:51:19 +00:00
|
|
|
import Git.FilePath
|
2014-12-22 19:32:51 +00:00
|
|
|
import Git.DiffTreeItem
|
2012-12-10 18:36:57 +00:00
|
|
|
import qualified Git.Filename
|
2013-02-06 16:40:59 +00:00
|
|
|
import qualified Git.Ref
|
2020-04-07 15:54:27 +00:00
|
|
|
import Utility.Attoparsec
|
2012-12-10 18:36:57 +00:00
|
|
|
|
2014-11-13 20:41:21 +00:00
|
|
|
{- Checks if the DiffTreeItem modifies a file with a given name
|
|
|
|
- or under a directory by that name. -}
|
|
|
|
isDiffOf :: DiffTreeItem -> TopFilePath -> Bool
|
2020-10-28 19:40:50 +00:00
|
|
|
isDiffOf diff f =
|
|
|
|
let f' = getTopFilePath f
|
|
|
|
in if B.null f'
|
|
|
|
then True -- top of repo contains all
|
|
|
|
else f' `dirContains` getTopFilePath (file diff)
|
2014-11-13 20:41:21 +00:00
|
|
|
|
2012-12-10 18:36:57 +00:00
|
|
|
{- Diffs two tree Refs. -}
|
|
|
|
diffTree :: Ref -> Ref -> Repo -> IO ([DiffTreeItem], IO Bool)
|
2013-02-06 16:40:59 +00:00
|
|
|
diffTree src dst = getdiff (Param "diff-tree")
|
2015-04-10 01:21:35 +00:00
|
|
|
[Param (fromRef src), Param (fromRef dst), Param "--"]
|
2012-12-18 19:04:44 +00:00
|
|
|
|
|
|
|
{- Diffs two tree Refs, recursing into sub-trees -}
|
|
|
|
diffTreeRecursive :: Ref -> Ref -> Repo -> IO ([DiffTreeItem], IO Bool)
|
2013-02-06 16:40:59 +00:00
|
|
|
diffTreeRecursive src dst = getdiff (Param "diff-tree")
|
2015-04-10 01:21:35 +00:00
|
|
|
[Param "-r", Param (fromRef src), Param (fromRef dst), Param "--"]
|
2012-12-18 19:04:44 +00:00
|
|
|
|
2013-08-26 00:47:49 +00:00
|
|
|
{- Diffs between a tree and the index. Does nothing if there is not yet a
|
|
|
|
- commit in the repository. -}
|
|
|
|
diffIndex :: Ref -> Repo -> IO ([DiffTreeItem], IO Bool)
|
2013-08-26 17:01:48 +00:00
|
|
|
diffIndex ref = diffIndex' ref [Param "--cached"]
|
|
|
|
|
|
|
|
{- Diffs between a tree and the working tree. Does nothing if there is not
|
2014-06-09 22:01:30 +00:00
|
|
|
- yet a commit in the repository, or if the repository is bare. -}
|
2013-08-26 17:01:48 +00:00
|
|
|
diffWorkTree :: Ref -> Repo -> IO ([DiffTreeItem], IO Bool)
|
|
|
|
diffWorkTree ref repo =
|
|
|
|
ifM (Git.Ref.headExists repo)
|
2014-10-09 19:09:26 +00:00
|
|
|
( diffIndex' ref [] repo
|
2013-08-26 17:01:48 +00:00
|
|
|
, return ([], return True)
|
|
|
|
)
|
|
|
|
|
|
|
|
diffIndex' :: Ref -> [CommandParam] -> Repo -> IO ([DiffTreeItem], IO Bool)
|
|
|
|
diffIndex' ref params repo =
|
2013-02-06 16:40:59 +00:00
|
|
|
ifM (Git.Ref.headExists repo)
|
2013-05-21 22:24:29 +00:00
|
|
|
( getdiff (Param "diff-index")
|
2015-01-07 01:41:21 +00:00
|
|
|
( params ++ [Param $ fromRef ref] ++ [Param "--"] )
|
2013-08-26 17:01:48 +00:00
|
|
|
repo
|
2013-02-06 16:40:59 +00:00
|
|
|
, return ([], return True)
|
|
|
|
)
|
|
|
|
|
2016-01-07 00:38:02 +00:00
|
|
|
{- Diff between the index and work tree. -}
|
|
|
|
diffFiles :: [CommandParam] -> Repo -> IO ([DiffTreeItem], IO Bool)
|
|
|
|
diffFiles = getdiff (Param "diff-files")
|
|
|
|
|
2014-11-13 20:41:21 +00:00
|
|
|
{- Runs git log in --raw mode to get the changes that were made in
|
2016-03-11 17:15:49 +00:00
|
|
|
- a particular commit to particular files. The output format
|
|
|
|
- is adjusted to be the same as diff-tree --raw._-}
|
2014-11-13 20:41:21 +00:00
|
|
|
diffLog :: [CommandParam] -> Repo -> IO ([DiffTreeItem], IO Bool)
|
|
|
|
diffLog params = getdiff (Param "log")
|
2020-01-07 16:29:37 +00:00
|
|
|
(Param "-n1" : Param "--no-abbrev" : Param "--pretty=format:" : params)
|
2014-11-13 20:41:21 +00:00
|
|
|
|
2016-03-11 17:15:49 +00:00
|
|
|
{- Uses git show to get the changes made by a commit.
|
|
|
|
-
|
|
|
|
- Does not support merge commits, and will fail on them. -}
|
|
|
|
commitDiff :: Sha -> Repo -> IO ([DiffTreeItem], IO Bool)
|
|
|
|
commitDiff ref = getdiff (Param "show")
|
2020-01-07 16:29:37 +00:00
|
|
|
[ Param "--no-abbrev", Param "--pretty=", Param "--raw", Param (fromRef ref) ]
|
2016-03-11 17:15:49 +00:00
|
|
|
|
2013-02-06 16:40:59 +00:00
|
|
|
getdiff :: CommandParam -> [CommandParam] -> Repo -> IO ([DiffTreeItem], IO Bool)
|
|
|
|
getdiff command params repo = do
|
2012-12-18 19:04:44 +00:00
|
|
|
(diff, cleanup) <- pipeNullSplit ps repo
|
2020-04-07 15:54:27 +00:00
|
|
|
return (parseDiffRaw diff, cleanup)
|
2012-12-18 19:04:44 +00:00
|
|
|
where
|
2015-06-01 17:52:23 +00:00
|
|
|
ps =
|
|
|
|
command :
|
|
|
|
Param "-z" :
|
|
|
|
Param "--raw" :
|
|
|
|
Param "--no-renames" :
|
|
|
|
Param "-l0" :
|
|
|
|
params
|
2012-12-10 18:36:57 +00:00
|
|
|
|
2014-11-13 20:41:21 +00:00
|
|
|
{- Parses --raw output used by diff-tree and git-log. -}
|
2020-04-07 15:54:27 +00:00
|
|
|
parseDiffRaw :: [L.ByteString] -> [DiffTreeItem]
|
2017-01-31 23:42:00 +00:00
|
|
|
parseDiffRaw l = go l
|
2012-12-10 18:36:57 +00:00
|
|
|
where
|
2017-01-31 23:42:00 +00:00
|
|
|
go [] = []
|
2020-04-07 15:54:27 +00:00
|
|
|
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 (== ' ')
|