ipatch-0.1.1: src/IPatch/Common.hs
{-
- Copyright (C) 2010 Joachim Breitner
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-}
{-# LANGUAGE Rank2Types, ScopedTypeVariables, TypeFamilies, GADTs #-}
module IPatch.Common where
import Control.Applicative ( (<$>) )
import Control.Monad ( when, unless )
import System.Posix.Files ( getSymbolicLinkStatus, isRegularFile, isDirectory )
import System.Directory ( createDirectoryIfMissing, doesFileExist, removeFile )
import System.FilePath.Posix ( (</>), takeDirectory, normalise )
import System.Exit ( exitWith, ExitCode(..) )
import Darcs.Arguments ( DarcsFlag(LookForAdds) )
import Darcs.Repository
( RepoJob(..),
Repository,
createRepository,
applyToWorking,
finalizeRepositoryChanges,
tentativelyAddPatch,
withGutsOf,
withRepoLock,
invalidateIndex,
unrecordedChanges )
import Darcs.Flags ( Compression(..), UseIndex(..), ScanKnown(..) )
import Darcs.RepoPath ( AbsolutePath, FilePathLike(..) )
import Darcs.External ( cloneFile )
import Darcs.Lock ( withTempDir )
import Darcs.Patch ( invert, fromPrims, namepatch, PrimOf, RepoPatch )
import Darcs.Global ( debugMessage )
import Darcs.Patch.PatchInfoAnd ( n2pia )
import Darcs.Utils ( clarifyErrors, promptYorn )
import Darcs.Witnesses.Ordered ( nullFL, FL(..) )
import Darcs.Witnesses.Sealed ( Sealed(Sealed), Sealed2(..) )
import Darcs.Witnesses.Sealed ( Sealed(Sealed), Sealed2(..), unsafeUnseal2 )
import Darcs.Patch.Apply ( ApplyState )
import Storage.Hashed.Tree ( Tree )
import IPatch.DiffFile ( applyDiff, DiffFile )
clonePathWithDeletion :: FilePath -> FilePath -> FilePath -> IO ()
clonePathWithDeletion source dest path = do
let source' = source </> path
dest' = dest </> path
ex <- doesFileExist source'
if ex
then do
fs <- getSymbolicLinkStatus source'
if isDirectory fs
then do
createDirectoryIfMissing True dest'
else
if isRegularFile fs
then do
createDirectoryIfMissing True (dest </> takeDirectory path)
cloneFile source' dest'
else
fail ("clonePathWithDeletion: Bad file " ++ source')
else do
exT <- doesFileExist dest'
when exT $ removeFile dest'
`catch` fail ("clonePathWithDeletion: Bad file " ++ source </> path)
clonePathsWithDeletion source dest = mapM_ (clonePathWithDeletion source dest)
withTempRepository :: String -> (AbsolutePath -> IO a) -> IO a
withTempRepository name job =
withTempDir ("ipatch-repo-" ++ name) $ \rdir -> do
debugMessage $ "Creating temporary repository in directory " ++ show rdir
createRepository []
job rdir
initializeBaseState
:: (FilePathLike a, RepoPatch p, ApplyState p ~ Tree) =>
a -> FilePath -> [FilePath] -> Repository p r u r ->
IO (FL (PrimOf p) r r', Repository p r r' r')
initializeBaseState rdir sdir files repo = do
-- There should be no local changes:
pend <- unrecordedChanges (IgnoreIndex, ScanAll) repo Nothing
(repo :: Repository p r r r) <- case pend of
NilFL -> return repo
_ -> fail $ "Repo passed to initializeBaseState has unrecord changes"
debugMessage $ "Copying " ++ show (length files) ++ " files to temporary repository."
clonePathsWithDeletion sdir (toFilePath rdir) files
-- Change the phantom type here.
(repo :: Repository p r r' r) <- return $ unsafeUnseal2 (Sealed2 repo)
-- Create a patch from the newly added files
debugMessage $ "Creating initial check in patch"
init_ps <- unrecordedChanges (IgnoreIndex, ScanAll) repo Nothing -- Correct flags?
init_patch <- n2pia <$> namepatch "NODATE" "Initial state" "NOAUTHOR" [] (fromPrims init_ps)
repo <- tentativelyAddPatch repo NoCompression init_patch
invalidateIndex repo
withGutsOf repo (finalizeRepositoryChanges repo)
`clarifyErrors` "Failed to apply inital patch"
return (init_ps, repo)
diffToPrims
:: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) =>
DiffFile -> Repository p r t t -> IO (Sealed (FL (PrimOf p) t))
diffToPrims diff repo = do
debugMessage $ "Applying the user provided diff"
-- Now apply the patch
ok <- applyDiff diff
-- Change the phantom type here.
(repo :: Repository p r u' t) <- return $ unsafeUnseal2 (Sealed2 repo)
debugMessage $ "Creating a patch from the user changes"
-- Create another patch from the changed files
patch_ps <- unrecordedChanges (IgnoreIndex, ScanAll) repo Nothing
unless ok $
if nullFL patch_ps
then do
putStrLn $ "The patch did not apply cleanly. Aborting."
exitWith (ExitFailure 1)
else do
putStrLn $ "The patch did fully apply cleanly. "
yorn <- promptYorn "Do you want to continue with the partially applied patch?"
unless yorn $ do
exitWith $ ExitFailure 1
-- Now we obliterate the patch, undoing its effects
applyToWorking repo [] (invert patch_ps) `catch` \e ->
fail ("Couldn't undo diff effect in working dir.\n" ++ show e)
return (Sealed patch_ps)
stdindefault :: a -> [String] -> IO [String]
stdindefault _ [] = return ["-"]
stdindefault _ x = return x