% 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 push} \begin{code} module Push ( push ) where import System ( exitWith, ExitCode( ExitSuccess ) ) import Monad ( when, unless ) import List ( elem ) import DarcsCommands ( DarcsCommand(..) ) import DarcsArguments ( DarcsFlag( ApplyAs, DryRun, Verbose, Quiet ), working_repo_dir, applyas, match_several, all_gui_interactive, dry_run, any_verbosity, set_default, ) import Repository ( read_repo, slurp_recorded, am_in_repo, is_repo ) import Patch ( Patch, patch2patchinfo ) import PatchInfo ( PatchInfo, human_friendly ) import RepoPrefs ( defaultrepo, set_defaultrepo, get_preflist ) import External ( execPipeIgnoreError, pipeSSH_IgnoreError, ) import DarcsURL ( is_ssh, is_file, ) import Lock ( withLock ) import SelectChanges ( with_selected_changes ) import Depends ( get_common_and_uncommon ) #include "impossible.h" \end{code} \begin{code} push_description :: String push_description = "Push patches into another repo.\n" \end{code} \options{push} \haskell{push_help} \begin{code} push_help :: String push_help = "Push is the opposite of pull. Push allows you to move changes from the\n"++ "current repository into another repository.\n" \end{code} \begin{code} push :: DarcsCommand push = DarcsCommand {command_name = "push", command_help = push_help, command_description = push_description, command_extra_args = 1, command_extra_arg_help = ["[REPOSITORY]"], command_command = push_cmd, command_prereq = am_in_repo, command_get_arg_possibilities = get_preflist "repos", command_argdefaults = defaultrepo, command_darcsoptions = [any_verbosity, match_several, all_gui_interactive, applyas, dry_run, working_repo_dir, set_default]} \end{code} \begin{code} push_cmd :: [DarcsFlag] -> [String] -> IO () push_cmd opts [repodir] = let am_verbose = Verbose `elem` opts am_quiet = Quiet `elem` opts putVerbose s = when am_verbose $ putStr s putInfo s = when (not am_quiet) $ putStr s in do bundle <- withLock "./_darcs/lock" $ do repovalid <- is_repo repodir unless repovalid $ fail $ "Bad repo directory: "++repodir old_default <- defaultrepo [] set_defaultrepo repodir opts when (old_default == [repodir]) $ putInfo $ "Pushing to "++repodir++"...\n" when (not (is_file repodir || is_ssh repodir)) $ if DryRun `elem` opts then putInfo $ "NOTE: you can only push to " ++ "local directories or scp-style URLs.\n\n" else fail "You can only push to local directories or scp-style URLs." them <- read_repo repodir us <- read_repo "." case get_common_and_uncommon (us, them) of (common, us', _) -> do putVerbose $ "We have the following patches to push:\n" ++ (unlines $ map (human_friendly.fst) $ head us') when (us' == [[]]) $ do putInfo "No recorded local changes to push!\n" exitWith ExitSuccess s <- slurp_recorded "." with_selected_changes "push" opts s (map (fromJust.snd) $ reverse $ head us') $ \ (_,to_be_pushed) -> do when (DryRun `elem` opts) $ do putStr $ "Would push the following changes:\n"++ (unlines $ map (human_friendly.fromJust.patch2patchinfo) to_be_pushed) putStr "Making no changes: this is a dry run.\n" exitWith ExitSuccess when (to_be_pushed == []) $ do putInfo "You don't want to push any patches, and that's fine with me!\n" exitWith ExitSuccess return $ make_patch_bundle common to_be_pushed case apply_as opts of Nothing -> do out <- if is_ssh repodir then apply_via_ssh repodir bundle else apply_via_local repodir bundle putStr out Just un -> do out <- if is_ssh repodir then apply_via_ssh_and_sudo repodir un bundle else apply_via_sudo un repodir bundle putStr out push_cmd _ _ = impossible apply_as :: [DarcsFlag] -> Maybe String apply_as (ApplyAs user:_) = Just user apply_as (_:fs) = apply_as fs apply_as [] = Nothing apply_via_sudo :: String -> String -> String -> IO String apply_via_sudo user repo bundle = execPipeIgnoreError "sudo" ["-u",user,"darcs","apply","--repodir", repo] bundle apply_via_local :: String -> String -> IO String apply_via_local repo bundle = execPipeIgnoreError "darcs" ["apply","--repodir",repo] bundle apply_via_ssh :: String -> String -> IO String apply_via_ssh repo bundle = pipeSSH_IgnoreError [addr,"cd '"++path++"' && darcs apply"] bundle where (addr,':':path) = break (==':') repo apply_via_ssh_and_sudo :: String -> String -> String -> IO String apply_via_ssh_and_sudo repo username bundle = pipeSSH_IgnoreError [addr,"sudo -u "++username++ " darcs apply --repodir '"++path++"'"] bundle where (addr,':':path) = break (==':') repo make_patch_bundle :: [PatchInfo] -> [Patch] -> String make_patch_bundle common to_be_pushed = "\nContext:\n\n"++ (unlines $ map show $ common) ++ "\nNew patches:\n\n"++ (unlines $ map show to_be_pushed) ++ "\n\n" {- "\nNew patches:\n\n"++ (unlines $ map show to_be_pushed) ++ "\n\n" ++ "\nContext:\n\n"++ (unlines $ map show $ common) -} \end{code} For obvious reasons, you can only push to repositories to which you have write access. In addition, you can only push to repos that you access either via the local file system or via ssh. In order to apply via ssh, darcs must also be installed on the remote computer. The command invoked to run ssh may be configured via the \verb!DARCS_SSH! environment variable (see section~\ref{darcsssh}. If you give the \verb!--apply-as! flag, darcs will use sudo to apply the changes as a different user. This can be useful if you want to set up a system where several users can modify the same repository, but you don't want to allow them full write access. This isn't secure against skilled malicious attackers, but at least can protect your repository from clumsy, inept or lazy users. The \verb!--patches!, \verb!--matches!, and \verb!--tags! options can be used to select which patches to push, as described in section~\ref{selecting}. darcs will silently push along any other patches upon which the selected patches depend.