git-annex/Git/History.hs

113 lines
3 KiB
Haskell
Raw Normal View History

2019-04-24 18:55:49 +00:00
{-# LANGUAGE BangPatterns #-}
{- git commit history interface
-
- Copyright 2019 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
module Git.History where
import Common
import Git
import Git.Command
import Git.Sha
import qualified Data.Set as S
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy.Char8 as L8
2019-04-24 18:55:49 +00:00
data History t = History t (S.Set (History t))
deriving (Show, Eq, Ord)
mapHistory :: (Ord a, Ord b) => (a -> b) -> History a -> History b
mapHistory f (History t s) = History (f t) (S.map (mapHistory f) s)
historyDepth :: History t -> Integer
historyDepth (History _ s)
| S.null s = 1
| otherwise = 1 + maximum (map historyDepth (S.toList s))
truncateHistoryToDepth :: Ord t => Integer -> History t -> History t
truncateHistoryToDepth n (History t ps) = History t (go 1 ps)
where
go depth s
| depth >= n = S.empty
| otherwise =
let depth' = succ depth
in flip S.map s $ \(History t' s') ->
History t' (go depth' s')
2019-04-30 17:52:55 +00:00
data HistoryCommit = HistoryCommit
{ historyCommit :: Sha
, historyCommitTree :: Sha
, historyCommitParents :: [Sha]
} deriving (Show, Eq, Ord)
{- Gets a History starting with the provided commit, and down to the
- requested depth. -}
getHistoryToDepth :: Integer -> Ref -> Repo -> IO (Maybe (History HistoryCommit))
getHistoryToDepth n commit r = withCreateProcess p go
2019-04-24 18:55:49 +00:00
where
p = (gitCreateProcess params r)
{ std_out = CreatePipe }
go _ (Just inh) _ pid = do
!h <- fmap (truncateHistoryToDepth n)
. build Nothing
. map parsehistorycommit
. map L.toStrict
. L8.lines
<$> L.hGetContents inh
hClose inh
void $ waitForProcess pid
return h
go _ _ _ _ = error "internal"
2019-05-01 17:38:40 +00:00
build h [] = fmap (mapHistory fst) h
2019-04-24 18:55:49 +00:00
build _ (Nothing:_) = Nothing
build Nothing (Just v:rest) =
build (Just (History v S.empty)) rest
build (Just h) (Just v:rest) =
let h' = traverseadd v h
in build (Just h') $
-- detect when all parents down to desired depth
-- have been found, and avoid processing any more,
-- this makes it much faster when there is a lot of
-- history.
2019-04-30 17:52:55 +00:00
if parentsfound h' then [] else rest
2019-04-24 18:55:49 +00:00
2019-05-01 17:38:40 +00:00
traverseadd v@(hc, _ps) (History v'@(hc', ps') s)
| historyCommit hc `elem` ps' =
let ps'' = filter (/= historyCommit hc) ps'
in History (hc', ps'') (S.insert (History v S.empty) s)
| otherwise = History v' (S.map (traverseadd v) s)
2019-04-24 18:55:49 +00:00
2019-04-30 17:52:55 +00:00
parentsfound = parentsfound' 1
2019-05-01 17:38:40 +00:00
parentsfound' depth (History (_hc, ps) s)
| not (null ps) = False
| null ps && depth == n = True
2019-04-24 18:55:49 +00:00
| depth >= n = True
2019-04-30 17:52:55 +00:00
| otherwise = all (parentsfound' (succ depth)) (S.toList s)
2019-04-24 18:55:49 +00:00
params =
[ Param "log"
, Param (fromRef commit)
, Param "--full-history"
, Param "--no-abbrev"
, Param "--format=%T %H %P"
]
parsehistorycommit l = case map extractSha (B8.split ' ' l) of
2019-05-01 17:38:40 +00:00
(Just t:Just c:ps) -> Just $
( HistoryCommit
{ historyCommit = c
, historyCommitTree = t
, historyCommitParents = catMaybes ps
}
, catMaybes ps
)
2019-04-24 18:55:49 +00:00
_ -> Nothing