packages feed

fbrnch-1.3: src/Cmd/Diff.hs

module Cmd.Diff (
  diffCmd,
  DiffFilter(..),
  DiffFormat(..),
  DiffWork(..)
  ) where

import Branches
import Common
import Common.System
import Git
import Package
import Patch

data DiffFormat =
  DiffDefault | DiffContext Int | DiffMinimal | DiffStatus | DiffStats |
  DiffQuiet
  deriving Eq

data DiffWork =
  DiffWorkAll | DiffWorkUnstage | DiffWorkStaged
  deriving Eq

data DiffFilter =
  DiffMatch String | DiffNotMatch String
  -- | DiffRegex String | DiffNotRegex String
  deriving Eq

-- FIXME diff other branches without switching
-- FIXME --older/--newer branch
diffCmd :: Bool -> Bool -> DiffWork -> DiffFormat -> Bool -> Maybe DiffFilter
        -> Maybe AnyBranch -> (Maybe Branch,[String]) -> IO ()
diffCmd debug speconly work fmt ignorebumps mpatt mwbr =
  withPackagesMaybeBranch (if debug then HeaderMust else HeaderNone) False dirtyGit diffPkg
  where
    diffPkg :: Package -> AnyBranch -> IO ()
    diffPkg pkg br = do
      ok <-
        case br of
          OtherBranch _ -> gitSwitchBranch br >> return True
          RelBranch rbr -> gitSwitchBranch' False rbr
      when ok $ do
        speconlyNone <-
          if speconly
          then notM $ doesFileExist $ packageSpec pkg
          else return False
        if speconlyNone
          then do
          dead <- doesFileExist "dead.package"
          unless dead $ putStrLn $ "no " ++ packageSpec pkg
          else do
          let contxt = case fmt of
                         DiffContext n -> ["--unified=" ++ show n]
                         DiffMinimal -> ["--unified=0"]
                         DiffStatus -> ["--name-status"]
                         -- FIXME hide "files changed" and "insertions" summary
                         DiffStats -> ["--compact-summary"]
                         _ -> []
              (workOpts,workArgs) =
                case work of
                  DiffWorkAll -> ([],["HEAD" | isNothing mwbr])
                  DiffWorkUnstage -> ([],[])
                  DiffWorkStaged -> (["--cached"],[])
              file = [packageSpec pkg | speconly]
          mwithBranch <-
            case mwbr of
              Nothing -> return $ Just []
              Just wbr ->
                let brn = show wbr in
                  if '/' `elem` brn
                  then return $ Just [brn]
                  else do
                    localbrs <- gitLines "branch" ["--format=%(refname:short)"]
                    if brn `elem` localbrs
                      then return $ Just [brn]
                      else do
                      wbexists <- checkIfRemoteBranchExists wbr
                      if wbexists
                        then return $ Just ["origin/" ++ brn]
                        else do
                          putStrLn $ "no" +-+ show wbr +-+ "for" +-+ unPackage pkg
                          return Nothing
          whenJust mwithBranch $ \withBranch -> do
            let revdiff =
                  case mwbr of
                    Nothing -> []
                    Just wbr -> case (wbr,br) of
                      (RelBranch rwbr, RelBranch rbr) -> ["-R" | rwbr > rbr]
                      _ -> []
            diff <- gitLines "diff" $ contxt ++ workOpts ++ revdiff ++ withBranch ++ workArgs ++ file
            let diffout = (maybe id filterPattern mpatt . simplifyDiff fmt) diff
            -- FIXME: sometimes we may want to list even if diff but no diffout
            unless (null diffout) $
              unless (ignorebumps && isTrivialRebuildCommit diffout) $
              if fmt == DiffQuiet
              then putStrLn $ unPackage pkg
              else do
                putPkgAnyBrnchHdr pkg br
                mapM_ putStrLn diffout
        where
          simplifyDiff :: DiffFormat -> [String] -> [String]
          simplifyDiff DiffMinimal ds = simplifyMinimalDiff ds
          -- drop "2 files changed, 113 insertions(+)"
          simplifyDiff DiffStats ds = if null ds then ds else init ds
          simplifyDiff _ ds = ds

          filterPattern :: DiffFilter -> [String] -> [String]
          filterPattern (DiffMatch patt) = filter (patt `isInfixOf`)
          filterPattern (DiffNotMatch patt) = filter (not . (patt `isInfixOf`))
            --  void $ runProcess $ setStderr nullStream $ proc "grep" [pat, file]