% Copyright (C) 2003-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 diff} \begin{code} module DiffCommand ( diff_command ) where import Workaround ( getCurrentDirectory ) import DarcsUtils ( withCurrentDirectory ) import Monad ( liftM ) import List ( (\\) ) import Autoconf ( diff_program ) import DarcsCommands ( DarcsCommand(..), nodefaults ) import DarcsArguments ( DarcsFlag(DiffFlags, Unified), match_range, diffflags, unidiff, working_repo_dir, fix_filepath, ) import Match ( get_first_match, get_second_match, first_match, second_match, match_first_patchset, match_second_patchset, ) import Repository ( PatchSet, read_repo, am_in_repo, slurp_recorded, slurp_pending ) import PatchInfo ( PatchInfo, human_friendly ) import SlurpDirectory ( co_slurp, slurp_write ) import External ( execPipeIgnoreError ) import Lock ( withTempDir ) \end{code} \options{diff} \begin{code} diff_description :: String diff_description = "Create a diff between two versions of the repository.\n" \end{code} \haskell{diff_help} \begin{code} diff_help :: String diff_help = "Diff can be used to create a diff between two versions which\n"++ "are in your repository. Specifying just --from-patch will get\n"++ "you a diff against the version in your working directory. If you give\n"++ "diff no version arguments, it gives you the same information as\n"++ "whatsnew except that the patch is formatted as the output of a\n"++ "diff command.\n" diff_command :: DarcsCommand diff_command = DarcsCommand {command_name = "diff", command_help = diff_help, command_description = diff_description, command_extra_args = -1, command_extra_arg_help = ["[FILE or DIRECTORY]..."], command_command = diff_cmd, command_prereq = am_in_repo, command_get_arg_possibilities = return [], command_argdefaults = nodefaults, command_darcsoptions = [match_range, diffflags, unidiff, working_repo_dir]} \end{code} Diff calls an external ``diff'' command to do the actual work, and passes any unrecognized flags to this diff command. Thus you can call \begin{verbatim} % darcs diff -t 0.9.8 -t 0.9.10 -- -u \end{verbatim} to get a diff in the unified format. Actually, thanks to the wonders of getopt you need the ``\verb!--!'' shown above before any arguments to diff. You can also specify additional arguments to diff using the \verb!--diff-opts! flag. The above command would look like this: \begin{verbatim} % darcs diff --diff-opts -u -t 0.9.8 -t 0.9.10 \end{verbatim} This may not seem like an improvement, but it really pays off when you want to always give diff the same options. You can do this by adding \begin{verbatim} % diff diff-opts -udp \end{verbatim} to your \verb!_darcs/prefs/defaults! file. \begin{code} get_diff_opts :: [DarcsFlag] -> [String] get_diff_opts [] = [] get_diff_opts (Unified:fs) = "-u" : get_diff_opts fs get_diff_opts (DiffFlags f:fs) = f : get_diff_opts fs get_diff_opts (_:fs) = get_diff_opts fs \end{code} If you want to view only the differences to one or more files, you can do so with a command such as \begin{verbatim} % darcs diff foo.c bar.c baz/ \end{verbatim} FIXME: I should allow the user to specify the external diff command. Currently it is hardwired to "diff". \begin{code} diff_cmd :: [DarcsFlag] -> [String] -> IO () diff_cmd opts args = do formerdir <- getCurrentDirectory thename <- return $ reverse (takeWhile (/='/') $ reverse formerdir) withTempDir (thename++"-old") $ \odir -> withTempDir (thename++"-new") $ \ndir -> do withCurrentDirectory odir $ do if first_match opts then get_first_match formerdir opts else do recorded <- slurp_recorded formerdir slurp_write recorded withCurrentDirectory ndir $ do if second_match opts then get_second_match formerdir opts else do recorded_with_pending <- slurp_pending formerdir latest <- co_slurp recorded_with_pending formerdir slurp_write latest thediff <- withCurrentDirectory (odir ++ "/..") $ case map (fix_filepath opts) args of [] -> rundiff (just_dir odir) (just_dir ndir) fs -> concat `liftM` mapM (\f -> rundiff (just_dir odir ++ "/" ++ f) (just_dir ndir ++ "/" ++ f)) fs morepatches <- read_repo formerdir putStr $ changelog $ get_diff_info opts morepatches putStr thediff where just_dir d = reverse $ takeWhile (/='/') $ reverse d rundiff :: String -> String -> IO String rundiff f1 f2 = execPipeIgnoreError diff_program ("-rN": get_diff_opts opts++[f1, f2]) "" \end{code} \begin{code} get_diff_info :: [DarcsFlag] -> PatchSet -> [PatchInfo] get_diff_info opts ps = let pi1s = map fst $ concat $ if first_match opts then match_first_patchset opts ps else ps pi2s = map fst $ concat $ if second_match opts then match_second_patchset opts ps else ps in pi2s \\ pi1s changelog :: [PatchInfo] -> String changelog pis = unlines $ map human_friendly pis \end{code}