diff --git a/Config.hs b/Config.hs
new file mode 100644
--- /dev/null
+++ b/Config.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances,
+             UndecidableInstances #-}
+{-  darcs-monitor - Darcs repository monitor
+    Copyright © 2007 Antti-Juhani Kaijanaho
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+module Config (module Control.Monad.Reader
+              ,Config (..)
+              ,MonadConfig
+              ) where
+
+import Control.Monad.Reader
+
+data Config = Config { confDarcsPath :: FilePath
+                     , confDarcsArgs :: [String]
+                     , confProgName :: String
+                     , confSendmailPath :: FilePath
+                     , confEmailTemplate :: Maybe FilePath
+                     , confCharset :: Maybe String
+                     }
+              deriving Show
+
+
+class MonadReader Config m => MonadConfig m where
+
+instance MonadReader Config m => MonadConfig m where
+
diff --git a/Darcs.hs b/Darcs.hs
new file mode 100644
--- /dev/null
+++ b/Darcs.hs
@@ -0,0 +1,44 @@
+{-  darcs-monitor - Darcs repository monitor
+    Copyright © 2007 Antti-Juhani Kaijanaho
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+module Darcs where
+
+import Control.Monad.Trans
+import System.Exit
+import System.IO
+import System.Process
+
+import Config
+
+-- runs darcs with args and returns what it outputs
+invokeDarcs :: (MonadConfig m, MonadIO m) =>
+               [String] -> m String
+invokeDarcs args = do
+  darcs <- asks confDarcsPath
+  cargs <- asks confDarcsArgs
+  let as' = cargs ++ args
+  liftIO $ do (inh, outh, errh, ph) <-
+                  runInteractiveProcess darcs as' Nothing Nothing
+              hClose inh
+              rv <- hGetContents outh
+              err <- hGetContents errh
+              length rv `seq` hClose outh
+              length err `seq` hClose errh
+              hPutStr stderr err
+              ec <- waitForProcess ph
+              when (ec /= ExitSuccess) $ ioError $ userError "darcs failed"
+              return rv
diff --git a/EmailPatch.hs b/EmailPatch.hs
new file mode 100644
--- /dev/null
+++ b/EmailPatch.hs
@@ -0,0 +1,153 @@
+{-  darcs-monitor - Darcs repository monitor
+    Copyright © 2007 Antti-Juhani Kaijanaho
+    Copyright © 2007 Benjamin Franksen
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+module EmailPatch (emailPatch,defaultSendmail) where
+
+import Control.Monad.Trans
+import System.Directory
+import System.Exit
+import System.IO
+import System.Process
+
+import Config
+import Darcs
+import PatchData
+
+import Paths_darcs_monitor
+
+defaultSendmail = "/usr/sbin/sendmail"
+
+sendMail :: (MonadConfig m, MonadIO m) =>
+            String -> String -> m Bool
+sendMail recipient text = do
+  sendmail <- asks confSendmailPath
+  liftIO $ do (inh, outh, errh, ph) <-
+                  runInteractiveProcess sendmail [recipient] Nothing Nothing
+              hClose outh
+              hPutStr inh text
+              hClose inh
+              err <- hGetContents errh
+              hPutStr stderr err
+              ec <- waitForProcess ph
+              return (ec == ExitSuccess)
+
+emailPatch :: (MonadConfig m, MonadIO m) =>
+              String -> PatchData -> m Bool
+emailPatch rec pd = do
+  diff <- invokeDarcs ["diff"
+                      ,"--match=hash " ++ patchHash pd
+                      ,"--repo=" ++ patchRepo pd
+                      ,"-u"]
+  changes <- invokeDarcs ["changes"
+                      ,"--match=hash " ++ patchHash pd
+                      ,"--repo=" ++ patchRepo pd
+                      ,"-s"]
+  menc <- asks confCharset
+  met <- asks confEmailTemplate
+  et <- liftIO $ case met of
+          Just s  -> return s
+          Nothing -> do let s = patchRepoDir pd ++ "email-template"
+                        ex <- doesFileExist s
+                        case ex of
+                          True  -> return s
+                          False -> getDataFileName "default-template"
+  tmpl <- liftIO $ readFile et
+  let f hdr ('%':'%':r) = let r' = dropWhile (/= '\n') r
+                              r'' = case r' of ('\n':r'') -> r''
+                                               _          -> r'
+                          in f hdr r''
+      f hdr ('%':r) = let (k,('%':r')) = span (/= '%') r
+                          he s = if hdr
+                                 then headerEncode menc s 
+                                 else nhe s
+                          nhe = showString
+                          repl = case k of
+                                   "RECIPIENT" -> nhe rec
+                                   "DIFF"      -> he diff
+                                   "CHANGES"   -> he changes
+                                   "REPO"      -> he (patchRepo pd)
+                                   "SHORTREPO" -> he (shortRepo pd)
+                                   "AUTHOR"    -> he (patchAuthor pd ++ " ") .
+                                                  showChar '<' .
+                                                  nhe (patchAuthorEmail pd) .
+                                                  showChar '>'
+                                   "DATE"      -> nhe (patchDate pd)
+                                   "HASH"      -> nhe (patchHash pd)
+                                   "TITLE"     -> he (patchTitle pd)
+                                   "COMMENT"   -> he (patchComment pd)
+                                   _           -> nhe ('%' : k ++ "%")
+                      in repl . f hdr r'
+      f True ('\n':'\n':r) = showString "\n\n" . f False r
+      f hdr (c:r) = showChar c . f hdr r
+      f _ [] = id
+      text' = f True tmpl
+  let text = case menc of
+               Just enc -> showString "MIME-Version: 1.0\n\
+                                      \Content-Transfer-Encoding: 8bit\n\
+                                      \Content-Type: text/plain; charset=" .
+                           showString enc .
+                           showChar '\n' .
+                           text'
+               Nothing -> text'
+  liftIO $ putStrLn $ "Sending mail to " ++ rec ++ " about " ++ patchTitle pd
+  sendMail rec (text "")
+
+
+headerEncode :: Maybe String -> String -> ShowS
+headerEncode menc str = do 
+  case menc of
+    Nothing  -> showString str
+    Just enc -> let ws = words str
+                    ws' = map (\s ->
+                               if areSafe s
+                               then s
+                               else "=?" ++
+                                    enc ++
+                                    "?Q?" ++
+                                    qEnc s "" ++
+                                    "?="
+                              ) ws
+                in showString (unwords ws')
+        where qEnc [] = id
+              qEnc (' ':r) = showChar '_' . qEnc r
+              qEnc ('_':r) = showString "=5F" . qEnc r
+              qEnc (c:r)
+                  | c `elem` safe = showChar c . qEnc r
+                  | otherwise            = showChar '=' .
+                                           showChar c1 .
+                                           showChar c2 .
+                                           qEnc r
+                  where a = fromEnum c
+                        a1 = a `div` 16
+                        a2 = a `mod` 16
+                        c1 = hds !! a1
+                        c2 = hds !! a2
+                        hds = "0123456789ABCDEF"
+
+
+safe = "<>@-+.abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXY0123456789\
+       \!\"#$%&\'()*,:;[]\\^_`{}|~/"
+
+areSafe = all (`elem` safe)
+
+shortRepo he 
+    | last repo == '/' = reverse $ takeWhile (/= '/') $ tail $ reverse repo
+    | '/' `elem` repo  = reverse $ takeWhile (/= '/') $ reverse repo
+    | otherwise        = repo
+      where repo = patchRepo he
+
diff --git a/GPL.txt b/GPL.txt
new file mode 100644
--- /dev/null
+++ b/GPL.txt
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/PatchData.hs b/PatchData.hs
new file mode 100644
--- /dev/null
+++ b/PatchData.hs
@@ -0,0 +1,30 @@
+{-  darcs-monitor - Darcs repository monitor
+    Copyright © 2007 Antti-Juhani Kaijanaho
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+module PatchData where
+
+data PatchData = PatchData { patchRepo :: FilePath
+                           , patchAuthor :: String
+                           , patchAuthorEmail :: String
+                           , patchDate :: String
+                           , patchHash :: String
+                           , patchTitle :: String
+                           , patchComment :: String
+                           , patchRepoDir :: String
+                           }
+                 deriving Show
+
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,104 @@
+                            darcs-monitor
+                            -------------
+
+Darcs-monitor sends email to a specified recipient about every patch
+in the repository that it has not sent mail about yet.  It can be run from
+Cron or as a darcs apply posthook, whichever fits best your needs.
+
+It ought not be too hard to extend it for other kinds of notification than
+just emails; but no others are implemented at this time.
+
+There is a DarcsWiki page on darcs-monitor at
+  http://wiki.darcs.net/DarcsWiki/DarcsMonitor
+
+
+                             Installation
+
+Darcs-monitor depends on mtl and HaXml.  First, make sure they are
+installed.
+
+Darcs-monitor is Cabalised.  The standard incantations
+
+   runhaskell Setup.hs configure
+   runhaskell Setup.hs build
+   runhaskell Setup.hs install
+
+ought to work for most people.
+
+Darcs-monitor has been tested using GHC 6.8.1.  It might not work with
+older compilers; though patches are accepted for fixing any
+unportabilities.
+
+
+                                Usage
+
+In the following, I assume that the repository you want to monitor is
+in directory $REPO.
+
+To have darcs-monitor run every time someone pushes to the repository,
+create the file $REPO/_darcs/prefs/defaults with the following content:
+
+8<-- cut here --
+apply posthook darcs-monitor --charset=CHARSET email RECIPIENT
+apply run-posthook
+8<-- cut here
+
+In the above, CHARSET should be the MIME charset that patches
+(including their metadata) are in, and RECIPIENT should be the email
+address where emails should be sent.  Also, if darcs-monitor is not
+installed in the default $PATH, you should specify th full path to the
+darcs-monitor binary.
+
+You can also run darcs-monitor from Cron or from the command line.  In
+those cases, it is easiest to specify the path to the repository on
+the command line after other arguments.  In fact, you can specify more
+than one.
+
+By default, mail is sent by calling /usr/sbin/sendmail.  Another sendmail
+binary can be specified by using a command-line switch.
+
+You can customise the email by providing a template.  The default one
+is installed in the Cabal-specified datadir as "default-template".
+You should put your own template in the file
+$REPO/_darcs/third-party/darcs-monitor/email-template
+(you can use other files, but then you have to specify where it is).
+
+In the template you can use the following substitutions:
+  %RECIPIENT%   -- the email address to which email is sent
+  %DIFF%        -- the Darcs-provided diff -u of the patch
+  %CHANGES%     -- a Darcs-provided summary of changes
+  %REPO%        -- the path to the repository
+  %SHORTREPO%   -- the final component of the path to the repository
+  %AUTHOR%      -- the patch author
+  %DATE%        -- the patch date
+  %HASH%        -- the patch Darcs hash
+  %TITLE%       -- the patch name
+  %COMMENT%     -- the patch comment
+
+Also, in the template, two percent signs in sequence (%%) start a comment
+that extends to the end of the current line.
+
+
+                                Thanks
+
+The following people, in addition to myself, have contributed code to
+this program:
+
+  Benja Fallenstein
+  Benjamin Franksen
+
+My thanks to them.
+
+
+                               Feedback
+
+Please send comments and bug reports to antti-juhani@kaijanaho.fi.
+
+Any patches should be prepared with Darcs against the repository
+  http://antti-juhani.kaijanaho.fi/darcs/darcs-monitor/
+and sent using "darcs send".
+
+Have fun,
+
+
+Antti-Juhani Kaijanaho
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,82 @@
+{-  darcs-monitor - Darcs repository monitor
+    Copyright © 2007, 2008 Antti-Juhani Kaijanaho
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+import Control.Monad
+import Data.Version
+import Distribution.PackageDescription
+import Distribution.Simple
+import Distribution.Simple.Configure
+import Distribution.Simple.LocalBuildInfo
+import Distribution.Simple.Setup
+import System.Directory
+import System.Exit
+import System.IO.Error
+import System.Time
+
+main = defaultMainWithHooks defaultUserHooks { confHook = conf
+                                             , postClean = pclean
+                                             , postBuild = pbuild
+                                               }
+
+conf gpd cf = do
+  let PackageDescription { package = PackageIdentifier 
+                                     { pkgVersion = ver
+                                     , pkgName = name 
+                                     }
+                         }
+          = case gpd of
+              (Left GenericPackageDescription { packageDescription = pd }, _) 
+                  -> pd
+              (Right pd, _)
+                  -> pd
+  writeFile "Version.hs" $ "-- GENERATED FILE -- DO NOT EDIT\n\
+                           \module Version where\n\
+                           \packageVersion = \"" ++
+                           name ++ " " ++
+                           showVersion ver ++ "\""
+  configure gpd cf
+
+substFiles = ["darcs-monitor.1"]
+
+pclean _ _ _ _ = do flip catch ct $ do
+                      removeDirectory "dist"
+                      removeFile "Version.hs"
+                      mapM_ removeFile substFiles
+    where ct ioe | isDoesNotExistError ioe = return ()
+                 | otherwise               = ioError ioe
+
+data Repl = Repl { replDataDir :: FilePath }
+
+pbuild _ _ pd lbi = do
+  mapM_ (substFile pd lbi) substFiles
+
+substFile pd lbi outf = do
+  let inf = outf ++ ".in"
+  imd <- getModificationTime inf
+  omd <- catch (getModificationTime outf) $ \ioe ->
+         if isDoesNotExistError ioe
+         then return (TOD 0 0)
+         else ioError ioe
+  when (imd > omd) $ do
+    putStrLn ("Generating " ++ outf ++ " ...")
+    is <- readFile inf
+    let f ('@':'D':'A':'T':'A':'D':'I':'R':'@':r) = 
+            (datadir (absoluteInstallDirs pd lbi NoCopyDest)) ++ f r
+        f (c:r) = c : f r
+        f []    = []
+    writeFile outf (f is)
+
diff --git a/darcs-monitor.1.in b/darcs-monitor.1.in
new file mode 100644
--- /dev/null
+++ b/darcs-monitor.1.in
@@ -0,0 +1,169 @@
+.\"     Copyright © 2007 Antti-Juhani Kaijanaho
+.\"
+.\"     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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+.\"     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.TH darcs-monitor 1
+.SH NAME
+darcs\-monitor \- Darcs add-on that sends mail about newly pushed changes
+.SH SYNOPSIS
+.B darcs-monitor
+[
+.I option
+...
+]
+.B email
+.I recipient
+[
+.I repository
+...
+]
+.sp
+.B darcs\-monitor
+.B \-h
+|
+.B \-\-help
+.sp
+.B darcs\-monitor
+.B \-\-version
+.SH DESCRIPTION
+.B darcs\-monitor
+reads one or more
+.BR darcs (1)
+repositories and sends email about every change it has not previously
+sent an email about to the same address.
+.
+To have darcs-monitor run every time someone pushes to a repository,
+add
+the following lines to 
+.IR _darcs/prefs/defaults .
+.IP
+.B apply
+.B posthook
+.B darcs\-monitor 
+.BI \-\-charset=  charset
+.B email
+.I recipient
+.br
+.B apply
+.B run\-posthook
+.br
+.PP
+In the above,
+.I charset
+should be the MIME charset that patches
+(including their metadata) are in, and
+.I recipient
+should be the email
+address where emails should be sent. 
+.PP
+You can also run
+.B darcs\-monitor
+from
+.BR crontab (1)
+or from the command line.
+.
+In those cases, it is easiest to specify the path to the repository on
+the command line after other arguments.
+.
+In fact, you can specify more than one.
+.PP
+By default, mail is sent by calling
+.IR /usr/sbin/sendmail .
+Another
+.B sendmail (1)
+binary can be specified by using a command-line switch.
+.PP
+You can customise the email by providing a template.
+.
+You should put your own template in the file
+.I _darcs/third-party/darcs-monitor/email-template
+(you can use other files, but then you have to specify where it is).
+.
+In the template you can use the following substitutions:
+.TP
+.B %RECIPIENT%
+the email address to which email is sent
+.TP
+.B %DIFF%
+the Darcs-provided diff -u of the patch
+.TP
+.B %CHANGES%
+a Darcs-provided summary of changes
+.TP
+.B %REPO%
+the path to the repository
+.TP
+.B %SHORTREPO%
+the final component of the path to the repository
+.TP
+.B %AUTHOR%
+the patch author
+.TP
+.B %DATE%
+the patch date
+.TP
+.B %HASH%
+the patch Darcs hash
+.TP
+.B %TITLE%
+the patch name
+.TP
+.B %COMMENT%
+the patch comment
+.PP
+Also, in the template, two percent signs in sequence
+.BR "" ( %% )
+start a comment that extends to the end of the current line.
+.SH OPTIONS
+.TP
+\-n, \-\-dry-run
+Do not actually do anything, but do mark it done.
+.TP
+\-\-darcs\-path=FILE
+Provide the location of the
+.BR darcs (1)
+executable.
+.TP
+\-\-darcs\-args=ARGS
+Provide additional arguments to
+.BR darcs (1),
+separating arguments with commas.
+.TP
+\-\-use\-sendmail[=FILE]
+Use
+.BR sendmail (1)
+(either at
+.I /usr/sbin/sendmail
+or at
+.IR FILE ).
+.TP
+\-\-charset=CHARSET
+Specify the MIME name of the repository's character set.
+.TP
+\-h, \-\-help
+Show usage.
+.TP
+\-\-version
+Show version.
+.SH "EXIT STATUS"
+Success is indicated by 0, failure by any other exit status.
+.SH FILES
+.TP
+.I @DATADIR@/default-template
+The default email template.  See above for the format.
+.SH AUTHOR
+.B darcs\-monitor
+was written by Antti-Juhani Kaijanaho.
+.SH "SEE ALSO"
+.BR darcs (1)
diff --git a/darcs-monitor.cabal b/darcs-monitor.cabal
new file mode 100644
--- /dev/null
+++ b/darcs-monitor.cabal
@@ -0,0 +1,29 @@
+Cabal-Version: >= 1.2
+Name: darcs-monitor
+Version: 0.3.4
+License: GPL
+License-File: GPL.txt
+Author: Antti-Juhani Kaijanaho
+Maintainer: antti-juhani@kaijanaho.fi
+Stability: experimental
+Synopsis: Darcs repository monitor (sends email)
+Description: Darcs-monitor will send email to a specified recipient
+ about new changes added to a specific darcs repository. It can be run
+ as an apply posthook (resulting in near-instantaneous "push"
+ emails), or periodically from Cron, or occasionally by hand, whatever
+ seems most convenient.
+Category: Distribution
+Build-Type: Custom
+Extra-Source-Files: README darcs-monitor.1.in
+Data-Files: default-template
+
+flag splitBase
+  description: Choose the new smaller, split-up base package.
+
+Executable darcs-monitor
+  if flag(splitBase)
+    Build-Depends: base >=3, containers, directory, mtl, process, HaXml
+  else
+    Build-Depends: base < 3, mtl, HaXml
+  Main-Is: darcs-monitor.hs
+  Other-Modules: Config Darcs EmailPatch PatchData
diff --git a/darcs-monitor.hs b/darcs-monitor.hs
new file mode 100644
--- /dev/null
+++ b/darcs-monitor.hs
@@ -0,0 +1,246 @@
+{-  darcs-monitor - Darcs repository monitor
+    Copyright © 2007 Antti-Juhani Kaijanaho
+    Copyright © 2007 Benja Fallenstein
+    Copyright © 2007 Benjamin Franksen
+
+    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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+module Main where
+
+import Control.Monad
+import Control.Monad.Trans
+import Data.Char
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Set (Set)
+import qualified Data.Set as Set
+import System.Console.GetOpt
+import System.Directory
+import System.Environment
+import System.Exit
+import System.IO
+import Text.XML.HaXml hiding (when)
+
+import Config
+import Darcs
+import EmailPatch
+import PatchData
+import Version
+
+data Opt = HelpOpt
+         | VersionOpt
+         | DarcsPathOpt String
+         | DarcsArgsOpt [String]
+         | SendmailOpt String
+         | EmailTemplateOpt String
+         | CharsetOpt String
+         | DryRunOpt
+
+options :: [OptDescr Opt]
+options = 
+    [Option ['n'] ["dry-run"] (NoArg DryRunOpt)
+                "Do not actually do anything, but do mark it done"
+    ,Option [] ["darcs-path"] (ReqArg DarcsPathOpt "FILE")
+                "Provide location of darcs"
+    ,Option [] ["darcs-args"] (ReqArg (DarcsArgsOpt . splitArgs) "ARGS")
+                "Provide additional arguments to darcs\n\
+                \(separate arguments with commas)"
+    ,Option [] ["use-sendmail"] (OptArg (\m -> SendmailOpt $
+                                               case m of
+                                                Nothing -> defaultSendmail
+                                                Just s  -> s)
+                                 "FILE")
+     ("Use sendmail (either " ++ defaultSendmail ++ " or FILE")
+    ,Option [] ["email-template"] (ReqArg EmailTemplateOpt "FILE")
+                "Provide a template for emails"
+    ,Option [] ["charset"] (ReqArg CharsetOpt "CHARSET")
+              "Specify character set"
+    ,Option ['h'] ["help"] (NoArg HelpOpt) "Show usage"
+    ,Option [] ["version"] (NoArg VersionOpt) "Show version"
+    ]
+
+findOpt :: [a] -> b -> (a -> Maybe b) -> b
+findOpt [] b _ = b
+findOpt (x:xs) b f = case f x of
+                       Nothing -> findOpt xs b f
+                       Just b' -> b'
+
+splitArgs :: String -> [String]
+splitArgs str = let (a,b) = span (/=',') str
+                in case b of ',':r -> a : splitArgs r
+                             [] -> [a]
+
+main :: IO ()
+main = do args <- getArgs
+          let (oa, args', errs) = getOpt Permute options args
+          when (errs /= []) $ do mapM_ putStr errs
+                                 exitFailure
+          pn <- getProgName
+          forM_ oa $ \ opt ->
+              case opt of
+                HelpOpt -> do putStr (usageInfo (usage pn) options)
+                              exitWith ExitSuccess
+                    where usage pn = "Usage: " ++ pn ++
+                                     " [OPTIONS] email RECIPIENT [REPO ...]"
+                VersionOpt -> do putStrLn packageVersion
+                                 exitWith ExitSuccess
+                _ -> return ()
+          let dryRun = findOpt roa False $ \opt ->
+                       case opt of DryRunOpt -> Just True
+                                   _         -> Nothing
+              roa = reverse oa
+              conf = Config { confDarcsPath = findOpt roa "darcs" $ \opt ->
+                                              case opt of
+                                                DarcsPathOpt s -> Just s
+                                                _              -> Nothing
+                            , confDarcsArgs = findOpt roa [] $ \opt ->
+                                              case opt of
+                                                DarcsArgsOpt s -> Just s
+                                                _              -> Nothing
+                            , confProgName = pn
+                            , confSendmailPath = findOpt roa defaultSendmail $
+                                                 \opt ->
+                                                 case opt of
+                                                   SendmailOpt s -> Just s
+                                                   _             -> Nothing
+                            , confEmailTemplate = findOpt roa Nothing $ \opt ->
+                                                  case opt of
+                                                    EmailTemplateOpt s ->
+                                                         Just (Just s)
+                                                    _ -> Nothing
+                            , confCharset = findOpt roa Nothing $ \opt ->
+                                            case opt of 
+                                              CharsetOpt s -> Just (Just s)
+                                              _            -> Nothing
+                            }
+          flip runReaderT conf $ do
+            (cmd,args'') <- case args' of
+                              ["email"] -> err "email requires an argument"
+                              ("email":addr:rest) ->
+                                  return (("emailPatch " ++ addr,
+                                           emailPatch addr),
+                                          rest)
+                              ("print":rest) ->
+                                  return (("print",
+                                           \p -> liftIO $ print p >> 
+                                                 return True),
+                                          rest)
+                              s:_ -> err ("unknown command " ++ s)
+                              _ -> err "missing command"
+            repos <- case args'' of [] -> do wd <- liftIO $ getCurrentDirectory
+                                             return [wd]
+                                    _  -> return args''
+            mapM_ (processRepo dryRun cmd) repos
+
+err msg = do pn <- asks confProgName
+             liftIO $ do hPutStrLn stderr (pn ++ ": " ++ msg)
+                         exitFailure
+
+type Command m = (String, PatchData -> m Bool)
+
+dir = "_darcs/third-party/darcs-monitor/"
+seenPatchesFileName = dir ++ "seen-patches"
+
+type SeenPatches = Map String (Set String)
+
+readSeenPatches :: MonadIO m => String -> m SeenPatches
+readSeenPatches repo = do
+  liftIO $ catch (do fc <- readFile (repo ++ seenPatchesFileName)
+                     return (read fc)
+                 ) $ \_ -> return Map.empty
+
+
+processRepo :: (MonadConfig m, MonadIO m) => Bool -> Command m -> FilePath -> m ()
+processRepo dryRun (tag,cmd) repo' = do
+  let repo = case last repo' of '/' -> repo'
+                                _   -> repo' ++ "/"
+  liftIO $ createDirectoryIfMissing True (repo++dir)
+  seenPatches <- readSeenPatches repo
+  xml <- invokeDarcs ["changes", "--reverse", "--repo="++repo,"--xml-output"]
+  let Document _ _ (Elem "changelog" _ content) _ = xmlParse repo xml 
+  let patches = filter (\c -> case c of CElem e -> True ; _ -> False) content
+  spl <- forM patches $ \ (CElem (Elem "patch" attrs content)) -> 
+      do let author = getAttr "author" attrs
+             localDate = getAttr "local_date" attrs
+             hash = getAttr "hash" attrs
+             name = getElem "name" content
+             comment = getElem "comment" content
+             (authorName, authorEmail) = parseAuthor author
+             dt = PatchData { patchRepo = repo
+                            , patchAuthor = authorName
+                            , patchAuthorEmail = authorEmail
+                            , patchDate = localDate
+                            , patchHash = hash
+                            , patchTitle = name
+                            , patchComment = comment
+                            , patchRepoDir = (repo++dir)
+                            }
+         res <- let f set | dryRun = return (Set.insert tag set)
+                    f set = do ok <- cmd dt
+                               case ok of True  -> return $
+                                                   Set.insert tag set
+                                          False -> return set
+                in case Map.lookup hash seenPatches of
+                     Just set -> case Set.member tag set of
+                                   True  -> return set
+                                   False -> f set
+                     Nothing  -> f (Set.empty)
+         return (hash,res)
+  let seenPatches' :: SeenPatches
+      seenPatches' = Map.fromList spl
+  liftIO $ writeFile (repo ++ seenPatchesFileName) (show seenPatches')
+
+parseAuthor str
+    | '<' `elem` str = let (name, '<':rest) = span (/= '<') str
+                           (addr, _) = span (/= '>') rest
+                       in (trim name, trim addr)
+    | '(' `elem` str = let (addr, '(':rest) = span (/= '(') str
+                           (name, _) = span (/= ')') rest
+                       in (trim name, trim addr)
+    | otherwise      = ("", trim str)
+
+ltrim str = dropWhile isSpace str
+
+trim = reverse . ltrim . reverse . ltrim
+
+getAttr name attrs = case lookup name attrs of
+                       Nothing -> ""
+                       Just (AttValue x) -> massage x ""
+    where massage (Left s : ss) = showString s .
+                                  massage ss
+          massage (Right ref : ss) = getRef ref .
+                                     massage ss
+          massage [] = id
+
+getElem name (CElem (Elem name' _ content) : rest)
+    | name == name'     = getContent content ""
+getElem name (_ : rest) = getElem name rest
+
+getContent (CElem (Elem _ _ content) : rest) = getContent content .
+                                               getContent rest
+getContent (CString _ cdata : rest) = showString cdata .
+                                      getContent rest
+getContent (CRef ref : rest) = getRef ref .
+                               getContent rest
+getContent (CMisc _ : rest) = getContent rest
+getContent [] = id
+
+getRef (RefEntity "lt") = showChar '<' 
+getRef (RefEntity "gt") = showChar '>' 
+getRef (RefEntity "amp") = showChar '&' 
+getRef (RefEntity "apos") = showChar '\''
+getRef (RefEntity "quot") = showChar '"'
+getRef (RefEntity s) = error ("unsupported entity reference &" ++ s ++ ";")
+getRef (RefChar i) = showChar (toEnum i) 
diff --git a/default-template b/default-template
new file mode 100644
--- /dev/null
+++ b/default-template
@@ -0,0 +1,22 @@
+%% darcs-monitor - Darcs repository monitor
+%% Copyright © 2007 Antti-Juhani Kaijanaho
+%%
+%% 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 of the License, 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; if not, write to the Free Software Foundation, Inc.,
+%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+From: %AUTHOR%
+To: %RECIPIENT%
+Subject: %SHORTREPO%: %TITLE%
+
+%CHANGES%
+%DIFF%
