# Copyright 2007 Matthew Elder # based on work by Tobias Luetke require 'capistrano/recipes/deploy/scm/base' module Capistrano module Deploy module SCM # Implements the Capistrano SCM interface for the Mercurial revision # control system (http://www.selenic.com/mercurial/). # Latest updates at http://tackletechnology.org/oss/cap2-mercurial class Mercurial < Base # Sets the default command name for this SCM. Users may override this # by setting the :scm_command variable. default_command "hg" # For mercurial HEAD == tip except that it bases this assumption on what # tip is in the current repository (so push before you deploy) def head "tip" end # Clone the repository and update to the specified changeset. def checkout(changeset, destination) clone(destination) + " && " + update(changeset, destination) end # Pull from the repository and update to the specified changeset. def sync(changeset, destination) pull(destination) + " && " + update(changeset, destination) end # One day we will have hg archive, although i think its not needed def export(revision, destination) raise NotImplementedError, "`diff' is not implemented by #{self.class.name}" + "use checkout strategy" end # Compute the difference between the two changesets +from+ and +to+ # as a unified diff. def diff(from, to=nil) scm :diff, "--rev #{from}", (to ? "--rev #{to}" : nil) end # Return a log of all changes between the two specified changesets, # +from+ and +to+, inclusive or the log for +from+ if +to+ is omitted. def log(from, to=nil) scm :log, verbose, "--rev #{from}" + (to ? ":#{to}" : "") end # Translates a tag to a changeset if needed or just returns changeset. def query_revision(changeset) cmd = scm :log, verbose, "-r #{changeset}", "--template '{node|short}'" yield cmd end # Determine response for SCM prompts # user/pass can come from ssh and http distribution methods # yes/no is for when ssh asks you about fingerprints def handle_data(state, stream, text) host = state[:channel][:host] logger.info "[#{host} :: #{stream}] #{text}" case text when /^user:/mi # support :scm_user for backwards compatibility of this module if user = variable(:scm_username) || variable(:scm_user) "#{user}\n" else raise "No variable :scm_username specified and Mercurial asked!\n" + "Prompt was: #{text}" end when /\bpassword:/mi unless pass = scm_password_or_prompt # fall back on old behavior of erroring out with msg raise "No variable :scm_password specified and Mercurial asked!\n" + "Prompt was: #{text}" end "#{pass}\n" when /yes\/no/i "yes\n" end end private # Fine grained mercurial commands def clone(destination) scm :clone, verbose, "--noupdate", # do not update to tip when cloning is done repository, # clone which repository? destination # and put the clone where? end def pull(destination) scm :pull, verbose, "--repository #{destination}", # pull changes into what? repository # and pull the changes from? end def update(changeset, destination) scm :update, verbose, "--repository #{destination}", # update what? "--clean", # ignore untracked changes changeset # update to this changeset end # verbosity configuration grokking :) def verbose case variable(:scm_verbose) when nil then nil when false then "--quiet" else "--verbose" end end # honor Cap 2.1+'s :scm_prefer_prompt if present def scm_password_or_prompt @scm_password_or_prompt ||= variable(:scm_password) || (Capistrano::CLI.password_prompt("hg password: ") if variable(:scm_prefer_prompt)) end end end end end