% 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; if not, write to the Free Software Foundation, % Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \section{darcs trackdown} \begin{code} module TrackDown ( trackdown ) where import Prelude hiding ( init ) import System ( system, ExitCode(..) ) import DarcsCommands ( DarcsCommand(..), nodefaults ) import DarcsArguments ( DarcsFlag, verbose ) import Repository ( slurp_recorded, read_repo, am_in_repo ) import Patch ( Patch, patch2patchinfo, apply_to_slurpy, invert ) import SlurpDirectory ( slurp, slurp_write, slurp_write_dirty ) import Test ( get_test ) import Lock ( withTempDir ) #include "impossible.h" \end{code} \options{trackdown} \begin{code} trackdown_description :: String trackdown_description = "Locate the most recent version lacking an error." \end{code} \haskell{trackdown_help} When given no options, trackdown tries to find the latest version that passes the test (i.e.\ the test that is run under 'darcs record'). If you give it a single argument, it is interpereted as a shell command to be run as a test. If you give it two arguments, the first is a shell command that is run only once (e.g.\ autoconf, perhaps) and the second is the ``test command''. \begin{code} trackdown_help :: String trackdown_help = "Trackdown tries to find the most recent version in the repository which\n"++ "passes a test. Given no arguments, it uses the default repository test.\n"++ "Given one argument, it treats it as a test command. Given two arguments,\n"++ "the first is an initialization command with is run only once, and the\n"++ "second is the test command.\n" \end{code} \begin{code} trackdown :: DarcsCommand trackdown = DarcsCommand {command_name = "trackdown", command_help = trackdown_help, command_description = trackdown_description, command_extra_args = -1, command_extra_arg_help = ["[INITIALIZATION]", "[COMMAND]"], command_command = trackdown_cmd, command_prereq = am_in_repo, command_get_arg_possibilities = return [], command_argdefaults = nodefaults, command_darcsoptions = [verbose]} \end{code} \begin{code} trackdown_cmd :: [DarcsFlag] -> [String] -> IO () trackdown_cmd opts args = do patches <- read_repo "." current <- slurp_recorded "." (init,test) <- case args of [] -> do t <- get_test opts return (return ExitSuccess, t) [cmd] -> do putStr $ "Tracking down command:\n"++cmd++"\n" return $ (return ExitSuccess, system cmd) [init,cmd] -> do putStr $ "Initializing with command:\n"++init++"\n" putStr $ "Tracking down command:\n"++cmd++"\n" return $ (system init, system cmd) _ -> fail "Trackdown expects zero to two arguments." withTempDir "trackingdown" $ \_ -> do slurp_write current init track_next test $ map (invert . fromJust . snd) $ concat patches \end{code} \begin{code} track_next :: (IO ExitCode) -> [Patch] -> IO () track_next test (p:ps) = do here <- slurp "." test_result <- test if test_result == ExitSuccess then putStr "Success!\n" else case apply_to_slurpy p here of Nothing -> putStr $ "Bad patch:\n"++show (fromJust $ patch2patchinfo p) Just here' -> do slurp_write_dirty here' putStr $ "Trying without the patch:\n"++ show (fromJust $ patch2patchinfo p)++"\n" track_next test ps track_next _ [] = putStr "Noone passed the test!\n" \end{code} Trackdown is helpful for locating when something was broken. FIXME: It is still rather primitive. Currently it just goes back over the history in reverse order trying each version. I'd like for it to explore different patch combinations, to try to find the minimum number of patches that you would need to unpull in order to make the test succeed. FIXME: I also would like to add an interface by which you can tell it which patches it should consider not including. Without such a feature, the following command: \begin{verbatim} % darcs trackdown 'make && false' \end{verbatim} would result in compiling every version in the repository--which is a rather tedious prospect. \paragraph{Example usage} If you want to find the last version of darcs that had a FIXME note in the file Record.lhs, you could run \begin{verbatim} % darcs trackdown 'grep FIXME Record.lhs' \end{verbatim} To find the latest version that compiles, you can run \begin{verbatim} % darcs trackdown 'autoconf' './configure && make' \end{verbatim}