packages feed

mit-3qvpPyAi6mH-12: src/Mit/Merge.hs

-- | @git merge@ utilities.
module Mit.Merge
  ( performMerge,
    MergeResult (..),
    mergeResultConflicts,
  )
where

import Mit.Git (GitCommitInfo, GitConflict, git, gitCommitsBetween, gitConflicts, gitMergeInProgress)
import Mit.Logger (Logger)
import Mit.Prelude
import Mit.ProcessInfo (ProcessInfo)
import Mit.Seq1 qualified as Seq1

-- | @performMerge logger message commit@ merges @commit@ into the current branch, preferring a fast-forward. If a merge
-- commit is created, its commit message is @message@.
performMerge :: Logger ProcessInfo -> Text -> Text -> Text -> IO MergeResult
performMerge logger gitdir commit message = do
  head <- git logger ["rev-parse", "HEAD"]
  commits0 <- gitCommitsBetween logger (Just head) commit
  case Seq1.fromSeq commits0 of
    Nothing -> pure NothingToMerge
    Just commits -> do
      git logger ["merge", "--ff", "--no-commit", commit] >>= \case
        False -> do
          conflicts <- gitConflicts logger
          pure (TriedToMerge commits (Seq1.unsafeFromList conflicts))
        True -> do
          -- If this was a fast-forward, a merge would not be in progress at this point.
          whenM (gitMergeInProgress gitdir) (git @() logger ["commit", "--message", message])
          pure (Merged commits)

-- | The result of a 'performMerge'. Lists of commits do not contain the merge commit itself.
data MergeResult
  = -- | There was nothing to merge.
    NothingToMerge
  | -- | We tried to merge these commits, but observed these conflicts.
    TriedToMerge !(Seq1 GitCommitInfo) !(Seq1 GitConflict)
  | -- | We successfully merged these commits.
    Merged !(Seq1 GitCommitInfo) -- note: doesn't distinguish between FF and non-FF

mergeResultConflicts :: MergeResult -> Maybe (Seq1 GitConflict)
mergeResultConflicts = \case
  NothingToMerge -> Nothing
  TriedToMerge _commits conflicts -> Just conflicts
  Merged _commits -> Nothing