ipatch-0.1.1: src/IPatch/Split.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 #-}
module IPatch.Split where
import qualified Data.ByteString as B ( writeFile )
import Control.Applicative ( (<$>) )
import System.Directory ( createDirectory )
import Control.Monad ( when )
import Control.Monad.Fix ( fix )
import System.Exit ( exitWith, ExitCode(ExitSuccess) )
import System.FilePath ( (</>) )
import Darcs.Commands ( DarcsCommand(..) )
import Darcs.Arguments ( DarcsFlag(..), fixFilePathOrStd, listFiles )
import Darcs.Repository ( amNotInRepository, withRepoLock, RepoJob(..), Repository )
import Darcs.External ( execPipeIgnoreError )
import Darcs.Lock ( withTempDir )
import Darcs.Patch ( apply, PrimOf )
import Printer ( empty, renderPS )
import Workaround ( getCurrentDirectory )
import Darcs.Global ( debugMessage )
import Darcs.Utils ( askUser, promptYorn )
import Darcs.Utils ( withCurrentDirectory )
import Darcs.SelectChanges
( WhichChanges(First),
runSelection,
selectChanges,
selectionContextPrim )
import Darcs.Patch.Split ( primSplitter )
import Darcs.Witnesses.Ordered ( FL, (:>)(..), nullFL )
import Darcs.Witnesses.Sealed ( Sealed(Sealed), Sealed2(..) )
import IPatch.Common
( withTempRepository,
initializeBaseState,
diffToPrims,
stdindefault )
import IPatch.DiffFile ( readDiffFile, filesTouchedByDiff )
splitHelp :: String
splitHelp =
"The `ipatch split file.patch' lets the user select different parts (hunks) of the\n" ++
"given patch file. After making a choice for each hunk, the user has to provide a\n" ++
"file name where the selected changes are stored. This procedure is repeated until\n" ++
"each change in the original file has been selected for one output file.\n" ++
"\n"++
"No files are modified by this command. The output patch files are all written at the\n" ++
"of the process.\n"
splitDescription :: String
splitDescription = "Split a diff file interactively."
split :: DarcsCommand
split = DarcsCommand {commandProgramName = "ipatch",
commandName = "split",
commandHelp = splitHelp,
commandDescription = splitDescription,
commandExtraArgs = 1,
commandExtraArgHelp = ["<PATCHFILE>"],
commandCommand = splitCmd,
commandPrereq = amNotInRepository,
commandGetArgPossibilities = listFiles,
commandArgdefaults = const stdindefault,
commandAdvancedOptions = [],
commandBasicOptions = []}
splitCmd :: [DarcsFlag] -> [String] -> IO ()
splitCmd _ [""] = fail "Empty filename argument given to split!"
splitCmd opts [unfixed_patchesfile] = do
maindir <- getCurrentDirectory
patchesfile <- fixFilePathOrStd opts unfixed_patchesfile
diffPS <- readDiffFile patchesfile
files <- filesTouchedByDiff diffPS
if null files
then putStrLn "Patch seems to be empty"
else withTempRepository "work" $ \rdir -> withRepoLock [LookForAdds] $ RepoJob $ \(repo :: Repository p r u r) -> do
(init_ps, repo) <- initializeBaseState rdir maindir files repo
Sealed patch_ps <- diffToPrims diffPS repo
when (nullFL patch_ps) $ do
putStrLn "Patch seems to be empty"
exitWith ExitSuccess
let run :: (Sealed2 (FL (PrimOf p)) -> Int -> IO [(Sealed2 (FL (PrimOf p)),String)]) ->
(Sealed2 (FL (PrimOf p)) -> Int -> IO [(Sealed2 (FL (PrimOf p)),String)])
run repeat (Sealed2 remaining_ps) n = if nullFL remaining_ps then return [] else do
putStrLn $ "Please select the changes for the " ++ ordinal n ++ " patch"
let context = selectionContextPrim "split" [] (Just primSplitter) Nothing Nothing
let selector = selectChanges First remaining_ps
(chosen_ps :> remaining_ps') <- runSelection selector context
if (nullFL chosen_ps)
then do
yorn <- promptYorn "You selected nothing. Do you want to abort?"
when yorn $ do
exitWith ExitSuccess
repeat (Sealed2 remaining_ps) n
else do
filename <- askUser $ "Please enter filename for the " ++ ordinal n ++ " patch: "
((Sealed2 chosen_ps,filename) :) <$> repeat (Sealed2 remaining_ps') (succ n)
chunks <- fix run (Sealed2 patch_ps) 1
when (null chunks) $ do
putStrLn "No patched splitted, exiting."
exitWith ExitSuccess
withTempDir "ipatch-diff-area" $ \bpath -> do
debugMessage "Setting up old and new staging areas"
createDirectory "old" -- Find nicer names based on original directory name
createDirectory "new"
withCurrentDirectory "new" $ apply init_ps
let applyAndDiff (Sealed2 last) (Sealed2 next) name = do
withCurrentDirectory "old" $ apply last
withCurrentDirectory "new" $ apply next
output <- renderPS <$> execPipeIgnoreError "diff" ["-Nur","old","new"] empty
putStrLn $ "Writing File " ++ name ++ " .."
B.writeFile (maindir </> name) output
sequence_ $ zipWith3 applyAndDiff (Sealed2 init_ps : map fst chunks) (map fst chunks) (map snd chunks)
ordinal 1 = "first"
ordinal 2 = "second"
ordinal 3 = "third"
ordinal n = show n ++ "th"