1 % Copyright (C) 2003-2005 David Roundy
3 % This program is free software; you can redistribute it and/or modify
4 % it under the terms of the GNU General Public License as published by
5 % the Free Software Foundation; either version 2, or (at your option)
8 % This program is distributed in the hope that it will be useful,
9 % but WITHOUT ANY WARRANTY; without even the implied warranty of
10 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 % GNU General Public License for more details.
13 % You should have received a copy of the GNU General Public License
14 % along with this program; see the file COPYING. If not, write to
15 % the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 % Boston, MA 02110-1301, USA.
18 \subsection{darcs apply}
20 {-# OPTIONS_GHC -cpp #-}
23 module Darcs.Commands.Apply ( apply ) where
24 import System.Exit ( ExitCode(..), exitWith )
25 import Prelude hiding ( catch )
26 import System.IO ( hClose, stdin, stdout, stderr )
27 import Control.Exception ( catch, throw, Exception( ExitException ) )
28 import Control.Monad ( when )
30 import Darcs.Hopefully ( n2pia, conscientiously, info )
31 import Darcs.SignalHandler ( withSignalsBlocked )
32 import Darcs.Commands ( DarcsCommand(..) )
33 import Darcs.CommandsAux ( check_paths )
34 import Darcs.Arguments ( DarcsFlag( Reply, Interactive, All,
35 Verbose, HappyForwarding ),
37 get_cc, working_repo_dir,
38 notest, nocompress, apply_conflict_options,
40 ignoretimes, get_sendmail_cmd,
41 reply, verify, list_files,
42 fixFilePathOrStd, umask_option,
43 all_interactive, sendmail_cmd,
44 leave_test_dir, happy_forwarding,
45 dry_run, print_dry_run_message_and_exit,
46 set_scripts_executable
48 import qualified Darcs.Arguments as DarcsArguments ( cc )
49 import Darcs.RepoPath ( toFilePath, useAbsoluteOrStd )
50 import Darcs.Repository ( SealedPatchSet, withRepoLock, ($-), amInRepository,
51 tentativelyMergePatches, slurp_recorded,
53 finalizeRepositoryChanges,
56 import Darcs.Patch ( RepoPatch, description )
57 import Darcs.Patch.Info ( human_friendly )
58 import Darcs.Ordered ( (:\/:)(..), (:>)(..), unsafeUnRL,
59 mapFL, nullFL, mapFL_FL, mapRL, concatRL, reverseRL )
60 import Darcs.SlurpDirectory ( wait_a_moment )
62 import ByteStringUtils ( linesPS, unlinesPS )
63 import qualified Data.ByteString as B (ByteString, null, readFile, hGetContents, init, take, drop)
64 import qualified Data.ByteString.Char8 as BC (unpack, last, pack)
66 import Darcs.External ( sendEmail, sendEmailDoc, resendEmail,
68 import Darcs.Email ( read_email )
69 import Darcs.Lock ( withStdoutTemp, readBinFile )
70 import Darcs.Patch.Depends ( get_common_and_uncommon_or_missing )
71 import Darcs.SelectChanges ( with_selected_changes )
72 import Darcs.Patch.Bundle ( scan_bundle )
73 import Darcs.Sealed ( Sealed(Sealed) )
74 import Printer ( packedString, putDocLn, vcat, text, ($$), errorDoc, empty )
75 #include "impossible.h"
78 apply_description :: String
80 "Apply patches (from an email bundle) to the repository."
89 "Apply is used to apply a bundle of patches to this repository.\n"++
90 "Such a bundle may be created using send.\n"
93 stdindefault :: a -> [String] -> IO [String]
94 stdindefault _ [] = return ["-"]
95 stdindefault _ x = return x
97 apply = DarcsCommand {command_name = "apply",
98 command_help = apply_help,
99 command_description = apply_description,
100 command_extra_args = 1,
101 command_extra_arg_help = ["<PATCHFILE>"],
102 command_command = apply_cmd,
103 command_prereq = amInRepository,
104 command_get_arg_possibilities = list_files,
105 command_argdefaults = const stdindefault,
106 command_advanced_options = [reply, DarcsArguments.cc,
109 ignoretimes, nocompress,
110 set_scripts_executable, umask_option],
111 command_basic_options = [verify,
112 all_interactive]++dry_run++
113 [apply_conflict_options,
120 apply_cmd :: [DarcsFlag] -> [String] -> IO ()
121 apply_cmd _ [""] = fail "Empty filename argument given to apply!"
122 apply_cmd opts [unfixed_patchesfile] = withRepoLock opts $- \repository -> do
123 patchesfile <- fixFilePathOrStd opts unfixed_patchesfile
124 ps <- useAbsoluteOrStd (B.readFile . toFilePath) (B.hGetContents stdin) patchesfile
125 am_verbose <- return $ Verbose `elem` opts
126 let from_whom = get_from ps
127 us <- read_repo repository
128 either_them <- get_patch_bundle opts ps
129 them <- case either_them of
130 Right (Sealed t) -> return t
131 Left er -> do forwarded <- consider_forwarding opts ps
133 then exitWith ExitSuccess
135 (_, us':\/:them') <- case get_common_and_uncommon_or_missing (us, them) of
137 if pinfo `elem` mapRL info (concatRL us)
138 then cannotApplyPartialRepo pinfo ""
139 else cannotApplyMissing pinfo
141 when (null $ unsafeUnRL $ head $ unsafeUnRL them') $
142 do putStr $ "All these patches have already been applied. " ++
145 s <- slurp_recorded repository
146 let their_ps = mapFL_FL (n2pia . conscientiously (text ("We cannot apply this patch "
147 ++"bundle, since we're missing:") $$))
148 $ reverseRL $ head $ unsafeUnRL them'
149 with_selected_changes "apply" fixed_opts s their_ps $
150 \ (to_be_applied:>_) -> do
151 print_dry_run_message_and_exit "apply" opts to_be_applied
152 when (nullFL to_be_applied) $
153 do putStrLn "You don't want to apply any patches, so I'm exiting!"
155 check_paths opts to_be_applied
156 redirect_output opts from_whom $ do
157 when am_verbose $ putStrLn "We have the following extra patches:"
158 when am_verbose $ putDocLn $ vcat $ mapRL description $ head $ unsafeUnRL us'
159 when am_verbose $ putStrLn "Will apply the following patches:"
160 when am_verbose $ putDocLn $ vcat $ mapFL description to_be_applied
161 definePatches to_be_applied
162 Sealed pw <- tentativelyMergePatches repository "apply" opts
163 (reverseRL $ head $ unsafeUnRL us') to_be_applied
164 withSignalsBlocked $ do finalizeRepositoryChanges repository
165 wait_a_moment -- so work will be more recent than rec
166 applyToWorking repository opts pw `catch` \e ->
167 fail ("Error applying patch to working dir:\n" ++ show e)
169 putStrLn "Finished applying..."
171 where fixed_opts = if Interactive `elem` opts
174 cannotApplyMissing pinfo
175 = errorDoc $ text "Cannot apply this patch bundle, since we're missing:"
176 $$ human_friendly pinfo
177 cannotApplyPartialRepo pinfo e
178 = errorDoc $ text ("Cannot apply this patch bundle, "
179 ++ "this is a \"--partial repository")
180 $$ text "We don't have the following patch:"
181 $$ human_friendly pinfo $$ text e
182 apply_cmd _ _ = impossible
185 Darcs apply accepts a single argument, which is the name of the patch
186 file to be applied. If you omit this argument, the patch is read from
187 standard input. Darcs also interprets an argument of `\-' to mean it
188 should read the file from standard input. This allows you to use apply
189 with a pipe from your email program, for example.
195 If you specify the \verb!--verify PUBRING! option, darcs will check that
196 the patch was GPG-signed by a key which is in \verb!PUBRING! and will
197 refuse to apply the patch otherwise.
200 get_patch_bundle :: RepoPatch p => [DarcsFlag] -> B.ByteString
201 -> IO (Either String (SealedPatchSet p))
202 get_patch_bundle opts fps = do
203 mps <- verifyPS opts $ read_email fps
204 mops <- verifyPS opts fps
206 (Nothing, Nothing) ->
207 return $ Left "Patch bundle not properly signed, or gpg failed."
208 (Just ps, Nothing) -> return $ scan_bundle ps
209 (Nothing, Just ps) -> return $ scan_bundle ps
210 -- We use careful_scan_bundle only below because in either of the two
211 -- above case we know the patch was signed, so it really shouldn't
212 -- need stripping of CRs.
213 (Just ps1, Just ps2) -> case careful_scan_bundle ps1 of
214 Left _ -> return $ careful_scan_bundle ps2
215 Right x -> return $ Right x
216 where careful_scan_bundle ps =
217 case scan_bundle ps of
218 Left e -> case scan_bundle $ stripCrPS ps of
222 stripCrPS :: B.ByteString -> B.ByteString
223 stripCrPS ps = unlinesPS $ map stripline $ linesPS ps
224 stripline p | B.null p = p
225 | BC.last p == '\r' = B.init p
233 If you give the \verb!--reply FROM! option to \verb!darcs apply!, it will send the
234 results of the application to the sender of the patch. This only works if
235 the patch is in the form of email with its headers intact, so that darcs
236 can actually know the origin of the patch. The reply email will indicate
237 whether or not the patch was successfully applied. The \verb!FROM! flag is
238 the email address that will be used as the ``from'' address when replying.
239 If the darcs apply is being done automatically, it is important that this
240 address not be the same as the address at which the patch was received, in
241 order to avoid automatic email loops.
243 If you want to also send the apply email to another address (for example,
244 to create something like a ``commits'' mailing list), you can use the
245 \verb!--cc! option to specify additional recipients. Note that the
246 \verb!--cc! option \emph{requires} the \verb!--reply! option, which
247 provides the ``From'' address.
249 The \verb!--reply! feature of apply is intended primarily for two uses.
250 When used by itself, it is handy for when you want to apply patches sent to
251 you by other developers so that they will know when their patch has been
252 applied. For example, in my \verb!.muttrc! (the config file for my mailer)
255 macro pager A "<pipe-entry>darcs apply --verbose \
256 --reply droundy@abridgegame.org --repodir ~/darcs
258 which allows me to apply a patch to darcs directly from my mailer, with the
259 originator of that patch being sent a confirmation when the patch is
260 successfully applied. NOTE: In an attempt to make sure no one else
261 can read your email, mutt seems to set the umask
262 such that patches created with the above macro are not world-readable, so
265 When used in combination with the \verb!--verify! option, the
266 \verb!--reply! option allows for a nice pushable repository. When these
267 two options are used together, any patches that don't pass the verify will
268 be forwarded to the \verb!FROM! address of the \verb!--reply! option. This
269 allows you to set up a repository so that anyone who is authorized can push
270 to it and have it automatically applied, but if a stranger pushes to it,
271 the patch will be forwarded to you. Please (for your own sake!)\ be certain
272 that the \verb!--reply FROM! address is different from the one used to send
273 patches to a pushable repository, since otherwise an unsigned patch will be
274 forwarded to the repository in an infinite loop.
276 If you use \verb!darcs apply --verify PUBRING --reply! to create a
277 pushable repository by applying patches automatically as they are received by
278 email, you will also want to use the \verb!--dont-allow-conflicts! option.
281 --dont-allow-conflicts
283 The \verb!--dont-allow-conflicts! flag causes apply to fail when applying a
284 patch would cause conflicts. This flag is recommended on repositories
285 which will be pushed to or sent to.
291 \verb!--allow-conflicts! will allow conflicts, but will keep the local and
292 recorded versions in sync on the repository. This means the conflict will exist
293 in both locations until it is resolved.
299 \verb!--mark-conflicts! will add conflict markers to illustrate the the
306 You can use an external interactive merge tool to resolve conflicts with the
307 flag \verb!--external-merge!. For more details see
308 subsection~\ref{resolution}.
314 If you provide the \verb!--interactive! flag, darcs will
315 ask you for each change in the patch bundle whether or not you wish to
316 apply that change. The opposite is the \verb!--all! flag, which can be
317 used to override an \verb!interactive! which might be set in your
324 If you want to use a command different from the default one for sending mail,
325 you need to specify a command line with the \verb!--sendmail-command! option.
326 The command line can contain the format specifier \verb!%t! for to
327 and you can add \verb!%<! to the end of the command line if the command
328 expects the complete mail on standard input. For example, the command line for
329 msmtp looks like this:
337 get_from :: B.ByteString -> String
338 get_from ps = readFrom $ linesPS ps
339 where readFrom [] = ""
341 | B.take 5 x == from_start = BC.unpack $ B.drop 5 x
342 | otherwise = readFrom xs
344 redirect_output :: [DarcsFlag] -> String -> IO a -> IO a
345 redirect_output opts to doit = ro opts
350 withStdoutTemp $ \tempf-> do {a <- doit;
354 } `catch` (sendit tempf)
355 where sendit tempf e@(ExitException ExitSuccess) =
356 do sendSanitizedEmail opts f to "Patch applied" cc tempf
358 sendit tempf (ExitException _) =
359 do sendSanitizedEmail opts f to "Patch failed!" cc tempf
360 throwIO $ ExitException ExitSuccess
362 do sendSanitizedEmail opts f to "Darcs error applying patch!" cc $
363 tempf ++ "\n\nCaught exception:\n"++
365 throwIO $ ExitException ExitSuccess
368 -- |sendSanitizedEmail sends a sanitized email using the given sendmailcmd
369 -- It takes @DacrsFlag@ options a file with the mail contents,
370 -- To:, Subject:, CC:, and mail body
371 sendSanitizedEmail :: [DarcsFlag] -> String -> String -> String -> String -> String -> IO ()
372 sendSanitizedEmail opts file to subject cc mailtext =
373 do scmd <- get_sendmail_cmd opts
374 body <- sanitizeFile mailtext
375 sendEmail file to subject cc scmd body
377 -- sanitizeFile is used to clean up the stdout/stderr before sticking it in
380 sanitizeFile :: FilePath -> IO String
381 sanitizeFile f = sanitize `fmap` readBinFile f
382 where sanitize s = wash $ remove_backspaces "" s
383 wash ('\000':s) = "\\NUL" ++ wash s
384 wash ('\026':s) = "\\EOF" ++ wash s
385 wash (c:cs) = c : wash cs
387 remove_backspaces rev_sofar "" = reverse rev_sofar
388 remove_backspaces (_:rs) ('\008':s) = remove_backspaces rs s
389 remove_backspaces "" ('\008':s) = remove_backspaces "" s
390 remove_backspaces rs (s:ss) = remove_backspaces (s:rs) ss
392 throwIO :: Exception -> IO a
393 throwIO e = return $ throw e
397 forwarding_message :: B.ByteString
398 forwarding_message = BC.pack $
399 "The following patch was either unsigned, or signed by a non-allowed\n"++
400 "key, or there was a GPG failure.\n"
402 consider_forwarding :: [DarcsFlag] -> B.ByteString -> IO Bool
403 consider_forwarding opts m = cf opts (get_cc opts)
404 where cf [] _ = return False
406 case break is_from (linesPS m) of
408 let m_lines = forwarding_message:m1 ++ m2
409 m' = unlinesPS m_lines
410 f' = BC.unpack (B.drop 5 f) in
411 if t == f' || t == init f'
412 then return False -- Refuse possible email loop.
414 scmd <- get_sendmail_cmd opts
415 if HappyForwarding `elem` opts
416 then resendEmail t scmd m
417 else sendEmailDoc f' t "A forwarded darcs patch" cc
418 scmd (Just (empty,empty))
421 _ -> return False -- Don't forward emails lacking headers!
422 cf (_:fs) cc = cf fs cc
423 is_from l = B.take 5 l == from_start
425 from_start :: B.ByteString
426 from_start = BC.pack "From:"
433 If you specify the \verb!--test! option, apply will run the test (if a test
434 exists) prior to applying the patch. If the test fails, the patch is not
435 applied. In this case, if the \verb!--reply! option was used, the results
436 of the test are sent in the reply email. You can also specify the
437 \verb!--no-test! option, which will override the \verb!--test! option, and
438 prevent the test from being run. This is helpful when setting up a
439 pushable repository, to keep users from running code.