packages feed

git-phoenix-0.0.1: src/Data/Git/Phoenix/Extraction.hs

module Data.Git.Phoenix.Extraction where

import Data.ByteString.Lazy.Char8 qualified as L8
import Data.Git.Phoenix.App
import Data.Git.Phoenix.CmdArgs
import Data.Git.Phoenix.Commit
import Data.Git.Phoenix.Io
import Data.Git.Phoenix.Object
import Data.Git.Phoenix.Prelude
import Data.Git.Phoenix.Repo
import Data.Git.Phoenix.Sha
import Data.Git.Phoenix.ShaCollision
import Data.Git.Phoenix.Tree


readCommitObject :: PhoenixExtractM m => GitPath Commit -> m (Maybe (GitPath Commit), GitPath Tree)
readCommitObject gop = go . (</> toFp gop) . untag =<< asks uberDir
  where
    goCommit bs =
      case extractTreeHash $ $(tr "eee/bs") bs of
        ("", _) -> fail $ show gop <> " does not have tree field"
        (treeComit, bs') -> do
          gitDir <- untag <$> asks destGitDir
          saveCompressedBs
            (gitDir </> ".git" </> "objects" </> toFp gop)
            bs
          case extractParent bs' of
            ("", _) -> pure (Nothing, shaToPath $ L8.unpack treeComit)
            (!ph, _) -> pure ( Just . shaToPath $ L8.unpack ph
                            , $(tr "/treeComit") . shaToPath $ L8.unpack treeComit
                            )
    go absGop = do
      lr <- withCompressedH absGop $ \cbs bs ->
        case classifyGitObject bs of
          Just BlobType -> fail $ show gop <> " is Git blob but expected Git commit"
          Just TreeType -> fail $ show gop <> " is Git tree but expected Git commit"
          Just CommitType -> Right <$> goCommit bs
          Just CollidedHash -> pure $ Left cbs
          Nothing -> fail $ show gop <> " is not a Git commit object"
      case lr of
        Right cmt -> pure cmt
        Left cbs -> do
            uniPath <- uniqBs gop cbs CommitType
            withCompressed uniPath $ \ubs ->
              case classifyGitObject ubs of
                Just CommitType -> goCommit ubs
                ops -> fail $ "Uniq BS of " <> show gop <> " is not commit but " <> show ops


extractCommit :: PhoenixExtractM m => GitPath Commit -> m ()
extractCommit ohp = do
  liftIO $(trIo "/ohp")
  (mParHash, treeHash) <- readCommitObject ohp
  extractTree $ $(tw "/") treeHash
  mapM_ extractCommit mParHash

extractCommitChainAsRepo :: PhoenixExtractM m => Tagged ShaPrefix String -> m ()
extractCommitChainAsRepo (Tagged rootCommit) = do
  (Tagged udr) <- asks uberDir
  completePath (udr </> (toFp $ shaToPath rootCommit)) >>= \case
    [up] -> do
      gitDir <- untag <$> asks destGitDir
      initGitRepo gitDir
      let uc = GitPath . $(tw "/udr up") $ makeRelative udr up
      extractCommit uc
      withBinaryFile
        (gitDir </> ".git" </> "refs" </> "heads" </> "master")
        WriteMode
        (`hPut` toCommitSha uc)
    [] -> fail $ "No commit matching prefix: " <> show rootCommit
    ambiP -> fail $ "Commit prefix is ambioguous:\n " <> intercalate "\n" ambiP

completePath :: MonadUnliftIO m => FilePath -> m [FilePath]
completePath fp = do
  ifM (doesFileExist fp) (pure [fp]) $ do
    ifM (doesDirectoryExist fp)
      (completeNonEmptyDir fp id) $ do
        case splitFileName fp of
          (dp, fpre) ->
            completeNonEmptyDir dp (filter (fpre `isPrefixOf`))
  where
    completeNonEmptyDir dp fnf =
      listDirectory dp >>= (\case [] -> pure [dp] ; o -> pure $ fmap (dp </>) o) . fnf