aboutsummaryrefslogtreecommitdiffstats
path: root/app/History/PartialCommitInfo.hs
diff options
context:
space:
mode:
Diffstat (limited to 'app/History/PartialCommitInfo.hs')
-rw-r--r--app/History/PartialCommitInfo.hs97
1 files changed, 97 insertions, 0 deletions
diff --git a/app/History/PartialCommitInfo.hs b/app/History/PartialCommitInfo.hs
new file mode 100644
index 0000000..fb53fbf
--- /dev/null
+++ b/app/History/PartialCommitInfo.hs
@@ -0,0 +1,97 @@
+module History.PartialCommitInfo
+ ( PartialCommitInfo (..),
+ getPartialCommitInfos,
+ )
+where
+
+import Control.Exception (catch)
+import Data.Binary (Binary)
+import Data.ByteString.Lazy.Char8 qualified as LB8
+import Data.Function ((&))
+import Data.Text qualified as T
+import Die (die)
+import Exception qualified as E
+import GHC.Generics (Generic)
+import Git qualified
+import History.Cache (cached)
+import History.CommitHash (CommitHash (..))
+import Issue (Issue, getIssuesPar)
+import Process (proc, sh)
+import System.Directory (getCurrentDirectory)
+import System.FilePath ((</>))
+import System.IO.Temp (withSystemTempDirectory)
+import System.Process.Typed (setWorkingDir)
+
+-- | `PartialCommitInfo` records the complete issues ONLY in files that have
+-- been changed in the commit.
+data PartialCommitInfo = PartialCommitInfo
+ { hash :: CommitHash,
+ filesChanged :: [FilePath],
+ issues :: [Issue]
+ }
+ deriving (Show, Binary, Generic)
+
+getPartialCommitInfos :: IO [PartialCommitInfo]
+getPartialCommitInfos = do
+ -- TODO Revise `getCommitHashes`
+ --
+ -- - Should throw if no commits.
+ -- - Should always be reversed?
+ commitHashes <- reverse <$> Git.getCommitHashes
+ mapM getCommitInfoOf (map Commit commitHashes ++ [WorkingTree])
+
+getCommitInfoOf :: CommitHash -> IO PartialCommitInfo
+getCommitInfoOf WorkingTree = do
+ (issuesWorkingTreeChanged, filesChanged) <- getIssuesAndFilesWorkingTreeChanged []
+ pure $
+ PartialCommitInfo
+ { hash = WorkingTree,
+ filesChanged = filesChanged,
+ issues = issuesWorkingTreeChanged
+ }
+getCommitInfoOf (Commit hash) = cached (hash <> (T.pack ".changed")) $ \_ -> do
+ (issuesCommitChanged, filesChanged) <- getIssuesAndFilesCommitChanged hash
+ pure $
+ PartialCommitInfo
+ { hash = Commit hash,
+ filesChanged = filesChanged,
+ issues = issuesCommitChanged
+ }
+
+-- | Given the hash of a commit, get all issues in the files which have
+-- been changed by this commit, as well as all changed files.
+getIssuesAndFilesCommitChanged :: T.Text -> IO ([Issue], [FilePath])
+getIssuesAndFilesCommitChanged hash = do
+ withSystemTempDirectory "history" $ \tmp -> do
+ let cwd = tmp </> T.unpack hash
+ Git.withWorkingTree cwd hash do
+ files <- gitShowChanged cwd
+ issues <- concat <$> catch (getIssuesPar cwd files) dieOfInvalidTreeGrepperResult
+ pure (issues, files)
+
+dieOfInvalidTreeGrepperResult :: E.InvalidTreeGrepperResult -> IO a
+dieOfInvalidTreeGrepperResult (E.InvalidTreeGrepperResult e) =
+ die e
+
+-- | Gets issues in all files which have been changed in your current
+-- [working
+-- - tree](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefworkingtreeaworkingtree)
+getIssuesAndFilesWorkingTreeChanged :: [FilePath] -> IO ([Issue], [FilePath])
+getIssuesAndFilesWorkingTreeChanged paths = do
+ cwd <- getCurrentDirectory
+ files <- gitLsFilesModifiedIn cwd paths
+ issues <- concat <$> catch (getIssuesPar cwd files) dieOfInvalidTreeGrepperResult
+ pure (issues, files)
+
+gitShowChanged :: FilePath -> IO [FilePath]
+gitShowChanged cwd =
+ Prelude.lines . LB8.unpack
+ <$> sh ("git show -p --name-only --format=''" & setWorkingDir cwd)
+
+gitLsFilesModifiedIn :: FilePath -> [FilePath] -> IO [FilePath]
+gitLsFilesModifiedIn cwd paths =
+ Prelude.lines . LB8.unpack
+ <$> sh
+ ( proc "git ls-files --modified %" ("--" : paths)
+ & setWorkingDir cwd
+ )