%  Copyright (C) 2002-2004 David Roundy
%
%  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.

\subsection{darcs rollback}
\begin{code}
module Darcs.Commands.Rollback ( rollback ) where

import Darcs.SignalHandler ( withSignalsBlocked )
import Darcs.Commands ( DarcsCommand(..), nodefaults )
import Darcs.Arguments ( DarcsFlag,
                        verbose, working_repo_dir, nocompress,
                        match_one_nontag, umask_option,
                      )
import Darcs.Repository ( amInRepository, withRepoLock,
                    tentativelyAddPatch, finalizeRepositoryChanges,
                  )
import Darcs.Patch ( invert, patch2patchinfo, )
import Darcs.Patch.Info ( is_inverted )
import Darcs.Patch.Depends ( is_tag )
import Darcs.SelectChanges ( with_selected_patch_from_repo )
#include "impossible.h"
\end{code}

\begin{code}
rollback_description :: String
rollback_description =
 "Record an inverse patch without changing the working directory."
\end{code}

\options{rollback}

\haskell{rollback_help} If you decide you didn't want to roll back a patch
after all, you probably should use unrecord to undo the rollback, since
like rollback, unrecord doesn't affect the working directory.

\begin{code}
rollback_help :: String
rollback_help =
 "Rollback is used to undo the effects of a single patch without actually\n"++
 "deleting that patch.  Instead, it applies the inverse patch as a new patch.\n"++
 "Unlike unpull and unrecord (which accomplish a similar goal) rollback is\n"++
 "perfectly safe, since it leaves in the repository a record of the patch it\n"++
 "is removing.\n"
\end{code}
\begin{code}
rollback :: DarcsCommand
rollback = DarcsCommand {command_name = "rollback",
                         command_help = rollback_help,
                         command_description = rollback_description,
                         command_extra_args = 0,
                         command_extra_arg_help = [],
                         command_command = rollback_cmd,
                         command_prereq = amInRepository,
                         command_get_arg_possibilities = return [],
                         command_argdefaults = nodefaults,
                         command_darcsoptions = [match_one_nontag,
                                                 nocompress,verbose,
                                                 working_repo_dir,
                                                 umask_option]}
\end{code}
\begin{code}
rollback_cmd :: [DarcsFlag] -> [String] -> IO ()
rollback_cmd opts _ = withRepoLock opts $ \repository ->
  with_selected_patch_from_repo "rollback" opts True $
    \ (p, _) ->
      case patch2patchinfo (invert p) of
      Nothing -> impossible
      Just pinfo | is_tag pinfo ->
          fail "cannot roll back a 'tag' patch."
      Just pinfo | not (is_inverted pinfo) ->
          fail "cannot roll back a 'rollback' patch."
      Just _ ->
          do tentativelyAddPatch repository opts $ invert p
             withSignalsBlocked $ finalizeRepositoryChanges repository
             putStrLn "Finished rolling back."
\end{code}

