packages feed

darcsden-1.1: src/DarcsDen/Handler/Repository/Forks.hs

{-# LANGUAGE ScopedTypeVariables, TypeFamilies, CPP #-}
module DarcsDen.Handler.Repository.Forks where

import Darcs.Patch.PatchInfoAnd (hopefully, info)
import Darcs.Patch.Depends (findUncommon)
import Darcs.Patch.Info (makeFilename)
import Darcs.Patch (getdeps)
import Darcs.Patch.Permutations
import Darcs.Repository
    ( RepoJob(..)
    , applyToWorking
    , finalizeRepositoryChanges
    , identifyRepositoryFor
    , invalidateIndex
    , readRepo
    , tentativelyMergePatches
#ifdef DARCS28
    , withGutsOf
#endif
    , withRepoLock
    )
import qualified Darcs.Repository as R (Repository)
#ifdef DARCS28
import Darcs.Utils (withCurrentDirectory)
import Darcs.CommandsAux (checkPaths)
import Darcs.Flags as DUF (DarcsFlag(SkipConflicts))
import Darcs.SelectChanges (filterOutConflicts)
import Darcs.Witnesses.Ordered
import Darcs.Witnesses.Sealed
#else
import Darcs.Util.File (withCurrentDirectory)
import Darcs.Patch.Witnesses.Ordered ((:\/:)(..), (:>)(..), lengthFL, reverseFL, mapRL)
import Darcs.Patch.Witnesses.Sealed
import Darcs.UI.CommandsAux (checkPaths)
import qualified Darcs.Repository.Flags as DRF
import Darcs.Repository.State (filterOutConflicts)
#endif

import DarcsDen.Handler.Repository.Changes
import DarcsDen.State.Repository
import DarcsDen.State.Session
import DarcsDen.State.Util

data Fork =
    Fork
        { fRepo :: Repository
        , fPatches :: [PatchLog]
        } deriving (Show)

getUpstreamChanges :: Repository -> IO (Maybe Fork)
getUpstreamChanges r = withParent (getForkChanges r) r

getDownstreamChanges :: Repository -> IO (Maybe Fork)
getDownstreamChanges r = withParent (flip getForkChanges r) r

-- Get patches that are in rOther but not r
getForkChanges :: Repository -> Repository -> IO Fork
getForkChanges rOther r = do
    let pdir = repoDir (rOwner rOther) (rName rOther)
        cdir = repoDir (rOwner r) (rName r)
#ifdef DARCS28
    withCurrentDirectory pdir $ withRepoLock [] $ RepoJob $ \(pr :: R.Repository p wR wU wR) -> do
      cr <- identifyRepositoryFor pr cdir
#else
    withCurrentDirectory pdir $ withRepoLock DRF.NoDryRun DRF.YesUseCache DRF.YesUpdateWorking DRF.NoUMask $ RepoJob $ \(pr :: R.Repository p wR wU wR) -> do
      cr <- identifyRepositoryFor pr DRF.YesUseCache cdir
#endif
      pps <- readRepo pr
      cps <- readRepo cr
      let cs = case findUncommon pps cps of
                 _ :\/: them ->
                   let depends = map (\(p, ds) -> (makeFilename p, ds)) (findAllDeps (reverseFL them))
                   in mapRL
                        (\p ->
                           let l = toLog (info p, getdeps (hopefully p))
                           in case lookup (makeFilename (info p)) depends of
                                Just ds -> l{pDepends = map (take 20 . makeFilename) ds}
                                Nothing -> l)
                        (reverseFL them)
      changes <- findUsers cs
      return $ Fork r changes

mergePatches :: Repository -> Repository -> [String] -> Session -> IO Bool
mergePatches rDest r ps s = do
#ifdef DARCS28
    withCurrentDirectory (origin rDest) $ withRepoLock [] $ RepoJob $ \(pr :: R.Repository p wR wU wR) -> do
        cr <- identifyRepositoryFor pr fork
#else
    withCurrentDirectory (origin rDest) $ withRepoLock DRF.NoDryRun DRF.YesUseCache DRF.YesUpdateWorking DRF.NoUMask $ RepoJob $ \(pr :: R.Repository p wR wU wR) -> do
        cr <- identifyRepositoryFor pr DRF.YesUseCache fork
#endif

        pps <- readRepo pr
        cps <- readRepo cr

        case findUncommon pps cps of
            us :\/: them ->
                case partitionFL ((`elem` ps) . take 20 . makeFilename . info) them of
                    chosen :> _ -> do
#ifdef DARCS28
                        (conflicts, Sealed merge) <- filterOutConflicts [DUF.SkipConflicts] (reverseFL us) pr chosen
#else
                        (conflicts, Sealed merge) <- filterOutConflicts (reverseFL us) pr chosen
#endif

                        if conflicts || lengthFL merge < length ps
                            then do
                                flip warn s . unwords $
                                    [ "Patches for fork"
                                    , "\"" ++ rOwner r ++ "/" ++ rName r ++ "\""
                                    , "could not be applied cleanly and have been skipped."
                                    ]
                                return False
                            else do

                        checkPaths [] merge

#ifdef DARCS28
                        Sealed pw <- tentativelyMergePatches pr "pull" [] us merge
#else
                        Sealed pw <- tentativelyMergePatches pr "pull" DRF.NoAllowConflicts DRF.YesUpdateWorking DRF.NoExternalMerge DRF.NoWantGuiPause DRF.GzipCompression DRF.NormalVerbosity (DRF.UseIndex, DRF.ScanKnown) us merge
#endif
                        invalidateIndex pr
#ifdef DARCS28
                        withGutsOf pr $ do
                            finalizeRepositoryChanges pr
                            applyToWorking pr [] pw
                            return ()
#else
                        finalizeRepositoryChanges pr DRF.YesUpdateWorking DRF.GzipCompression
                        applyToWorking pr DRF.NormalVerbosity pw
#endif
                        return True
  where
    origin p = repoDir (rOwner p) (rName p)
    fork = repoDir (rOwner r) (rName r)

canView :: Maybe String -> Repository -> Bool
canView _ Repository{rIsPrivate=False} = True
canView (Just user) r | user == rOwner r || user `elem` rMembers r = True
canView _ _ = False

canMerge :: Maybe String -> Repository -> Bool
canMerge Nothing _     = False
canMerge (Just user) r = user == rOwner r || user `elem` rMembers r