Merge branch 'darcs' into master
[git-darcs-import.git] / src / Darcs / Commands / TrackDown.lhs
blob1995df675624a2602771d76a800953a77c8f5a42
1 % Copyright (C) 2002-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)
6 % any later version.
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 trackdown}
19 \begin{code}
20 module Darcs.Commands.TrackDown ( trackdown ) where
21 import Prelude hiding ( init )
22 import System.Exit ( ExitCode(..) )
23 import System.Cmd ( system )
24 import System.IO ( hFlush, stdout )
25 import Control.Monad( when )
27 import Darcs.Commands ( DarcsCommand(..), nodefaults )
28 import Darcs.Arguments ( DarcsFlag(SetScriptsExecutable), working_repo_dir,
29 set_scripts_executable )
30 import Darcs.Hopefully ( hopefully )
31 import Darcs.Repository ( amInRepository, read_repo, withRepoReadLock, ($-), withRecorded,
32 setScriptsExecutable )
33 import Darcs.Ordered ( unsafeUnRL, concatRL )
34 import Darcs.Patch ( RepoPatch, Named, description, apply, invert )
35 import Printer ( putDocLn )
36 import Darcs.Test ( get_test )
37 import Darcs.Lock ( withTempDir )
38 \end{code}
40 \options{trackdown}
42 \begin{code}
43 trackdown_description :: String
44 trackdown_description = "Locate the most recent version lacking an error."
45 \end{code}
47 \haskell{trackdown_help}
49 \begin{code}
50 trackdown_help :: String
51 trackdown_help =
52 "Trackdown tries to find the most recent version in the repository which\n"++
53 "passes a test. Given no arguments, it uses the default repository test.\n"++
54 "Given one argument, it treats it as a test command. Given two arguments,\n"++
55 "the first is an initialization command with is run only once, and the\n"++
56 "second is the test command.\n"
57 \end{code}
59 \begin{code}
60 trackdown :: DarcsCommand
61 trackdown = DarcsCommand {command_name = "trackdown",
62 command_help = trackdown_help,
63 command_description = trackdown_description,
64 command_extra_args = -1,
65 command_extra_arg_help = ["[[INITIALIZATION]",
66 "COMMAND]"],
67 command_command = trackdown_cmd,
68 command_prereq = amInRepository,
69 command_get_arg_possibilities = return [],
70 command_argdefaults = nodefaults,
71 command_advanced_options = [set_scripts_executable],
72 command_basic_options = [working_repo_dir]}
73 \end{code}
75 \begin{code}
76 trackdown_cmd :: [DarcsFlag] -> [String] -> IO ()
77 trackdown_cmd opts args = withRepoReadLock opts $- \repository -> do
78 patches <- read_repo repository
79 (init,test) <- case args of
80 [] ->
81 do t <- get_test opts
82 return (return ExitSuccess, t)
83 [cmd] ->
84 do putStrLn $ "Tracking down command:\n"++cmd
85 return $ (return ExitSuccess, system cmd)
86 [init,cmd] ->
87 do putStrLn $ "Initializing with command:\n"++init
88 putStrLn $ "Tracking down command:\n"++cmd
89 return $ (system init, system cmd)
90 _ -> fail "Trackdown expects zero to two arguments."
91 withRecorded repository (withTempDir "trackingdown") $ \_ -> do
92 when (SetScriptsExecutable `elem` opts) setScriptsExecutable
93 init
94 track_next opts test $ map (invert . hopefully) $ unsafeUnRL $ concatRL patches
95 \end{code}
97 \begin{code}
98 track_next :: RepoPatch p => [DarcsFlag] -> (IO ExitCode) -> [Named p] -> IO ()
99 track_next opts test (p:ps) = do
100 test_result <- test
101 if test_result == ExitSuccess
102 then putStrLn "Success!"
103 else do apply opts p `catch` \e -> fail ("Bad patch:\n" ++ show e)
104 putStrLn "Trying without the patch:"
105 putDocLn $ description $ invert p
106 hFlush stdout
107 track_next opts test ps
108 track_next _ _ [] = putStrLn "Noone passed the test!"
109 \end{code}
111 Trackdown is helpful for locating when something was broken. It creates
112 a temporary directory with the latest repository content in it and cd to it.
113 First, and only once, it runs the initialization command if any,
114 for example
115 \begin{verbatim}
116 'autoconf; ./configure >/dev/null'
117 \end{verbatim}
118 Then it runs the test command, for example
119 \begin{verbatim}
120 'make && cd tests && sh /tmp/test.sh'
121 \end{verbatim}
122 While the test command exits with an error return code, darcs
123 ``unapplies'' one patch from the version controlled files to retrieve
124 an earlier version, and repeats the test command. If the test command
125 finally succeeds, the name of the hunted down patch is found in the
126 output before the last test run.
128 FIXME: It is
129 still rather primitive. Currently it just goes back over the history in
130 reverse order trying each version. I'd like for it to explore different
131 patch combinations, to try to find the minimum number of patches that you
132 would need to obliterate in order to make the test succeed.
134 FIXME: I also would like to add an interface by which you can tell it which
135 patches it should consider not including. Without such a feature, the
136 following command:
137 \begin{verbatim}
138 % darcs trackdown 'make && false'
139 \end{verbatim}
140 would result in compiling every version in the repository--which is a
141 rather tedious prospect.
143 \subsubsection{Example usage}
144 If you want to find the last version of darcs that had a FIXME note in the
145 file Record.lhs, you could run
146 \begin{verbatim}
147 % darcs trackdown 'grep FIXME Record.lhs'
148 \end{verbatim}
150 To find the latest version that compiles, you can run
151 \begin{verbatim}
152 % darcs trackdown 'autoconf' './configure && make'
153 \end{verbatim}
155 Trackdown can also be used to see how other features of the code changed
156 with time. For example
157 \begin{verbatim}
158 % darcs trackdown 'autoconf; ./configure' \
159 "make darcs > /dev/null && cd ~/darcs && time darcs check && false"
160 \end{verbatim}
161 would let you see how long `darcs check' takes to run on each previous
162 version of darcs that will actually compile. The ``\verb!&& false!''
163 ensures that trackdown keeps going.