Index: drush.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/drush.php,v
retrieving revision 1.66
diff -u -p -r1.66 drush.php
--- drush.php   26 May 2009 06:50:41 -0000      1.66
+++ drush.php   29 May 2009 21:49:35 -0000
@@ -63,6 +63,33 @@ function drush_main() {
   foreach ($phases as $phase) {
     if (drush_bootstrap($phase)) {
       $command = drush_parse_command();
+
+      // Process a remote command if 'remote_host' option is set.
+      $remote_host = drush_get_option('remote_host');
+      if(isset($remote_host)) {         
+        
+       $args = drush_get_arguments();
+        $command = array_shift($args);
+        $data = drush_get_context('options');
+        foreach (array('r', 'root', 'l', 'uri', 'v', 'd', 'i') as $key) {
+         unset($data[$key]);
+       }
+        foreach (array('root', 'uri') as $key) {
+         $data[$key] = drush_get_option($key);
+       }
+       
+        $drush_path = drush_get_option('drush');
+        $remote_user = drush_get_option('remote_user');
+        $output = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
+       
+        // If we set INTEGRATE to FALSE, we can print the output as shown below:
+       // using 'drush_print' introduces an extra blank line at
+       // the end of the input.
+        //drush_print($output['output']);
+       
+        return;
+      }
+
       if (is_array($command)) {
         if ($command['bootstrap'] == $phase && empty($command['bootstrap_errors'])) {
           drush_log(dt("Found command: !command", array('!command' => $command['command'])), 'bootstrap');
Index: example.drushrc.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/example.drushrc.php,v
retrieving revision 1.11
diff -u -p -r1.11 example.drushrc.php
--- example.drushrc.php 19 Apr 2009 03:13:09 -0000      1.11
+++ example.drushrc.php 29 May 2009 21:49:35 -0000
@@ -78,3 +78,52 @@ $options['structure-tables'] = array(
 #   'theme_default' => 'minnelli',
 #   'anonymous' => 'Visitor',
 # );
+
+/**
+ * Site aliases:
+ *
+ * To create aliases to remote Drupal installations, add entries
+ * to the site-aliases option array here.  These settings are
+ * only respected by "drush sql load" and "drush sync".
+ *
+ * Each entry in the 'site-aliases' array is accessed by its
+ * site alias (e.g. 'stage' or 'dev').  The following settings
+ * are stored in the site alias record:
+ *
+ * - 'db-url': The database connection string from settings.php.
+ * For remote databases accessed via an ssh tunnel, set the port
+ * number to the tunneled port as it is accessed on the local machine.
+ * - 'remote-port': If the database is remote and 'db-url' contains
+ * a tunneled port number, put the actual database port number
+ * used on the remote machine in the 'remote-port' setting.
+ * - 'uri': This should always be the same as the site's folder name
+ * in the 'sites' folder.
+ * - 'remote-host': The fully-qualified domain name of the remote system
+ * hosting the Drupal instance.
+ * - 'remote-user': The username to log in as when using ssh or rsync.
+ * - 'remote-pass': The password to use with rsync.
+ * - 'path-aliases': An array of aliases for common rsync targets.
+ * Relative aliases are always taken from the Drupal root.  The
+ * '!root' alias must not be specified as a relative path.
+ *
+ * Remove the leading hash signs to enable.
+ */
+#$options['site-aliases']['stage'] = array(
+#    'db-url' => 'pgsql://username:password@dbhost.com:port/databasename',
+#    'uri' => 'stage.mydrupalsite.com',
+#    'ssh-user' => 'publisher',
+#    'ssh-password' => 'secretsecret',
+#    'path-aliases' => array(
+#      '!root' => '/path/to/remote/drupal/root',
+#      '!dump' => '/path/to/live/sql_dump.sql',
+#      '!files' => 'sites/mydrupalsite.com/files',
+#      '!data' => '/my/arbitrary/path',    )
+#  );
+#$options['site-aliases']['dev'] = array(
+#    'db-url' => 'pgsql://username:password@localhost/databasename',
+#    'uri' => 'dev.mydrupalsite.com',
+#    'path-aliases' => array(
+#      '!dump' => '/path/to/dev/sql_dump.sql',
+#      '!data' => '/my/other/path',    )
+#  );
+
Index: commands/core/core.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v
retrieving revision 1.29
diff -u -p -r1.29 core.drush.inc
--- commands/core/core.drush.inc        27 May 2009 01:01:17 -0000      1.29
+++ commands/core/core.drush.inc        29 May 2009 21:49:35 -0000
@@ -82,13 +82,14 @@ function core_drush_command() {
     
   );
   $items['sync'] = array(
-    'description' => 'Rsync the Drupal tree to/from another server using ssh.',
+    'description' => 'Rsync the Drupal tree to/from another server using ssh.  Relative paths start from the Drupal root folder if a site alias is used; otherwise they start from the current working directory.',
     'arguments' => array(
-      'source' => 'See rsync documentation.',
-      'destination' => 'See rsync documentation.',
+      'source' => 'See rsync documentation and example.drushrc.php.',
+      'destination' => 'See rsync documentation and example.drushrc.php.',
     ),
     'examples' => array(
-      'sync dev:/var/www/files/ stage:/var/www/files/' => 'Rsync \'files\' dir from dev to stage',
+      'sync dev:. stage:.' => 'Rsync entire Drupal tree from dev to stage (one of which must be local).',
+      'sync ./ stage:!files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on stage.',
     ),
   );
   $items['eval'] = array(
@@ -328,15 +329,26 @@ function drush_core_cache_clear() {
  * @return void
  **/
 function drush_core_sync($source, $destination) {
-  // Local paths are relative to Drupal root
-  $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
+  $options = '-az';
+  $remote_user = '';
+  $pwfile = '';
   
-  if (!strstr($source, ':')) {
-    $source = $drupal_root. "/$source";
-  }
-  if (!strstr($destination, ':')) {
-    $destination = $drupal_root . "/$destination";
+  // One of $souruce or $destination (at most) will be remote;
+  // get the site alias settings for the remote component.
+  $remote_alias_settings = _drush_core_get_site_alias_settings_for_remote($source, $destination);
+  if (!empty($remote_alias_settings)) {
+    if (array_key_exists('ssh-user', $remote_alias_settings)) {
+      $remote_user = $remote_alias_settings['ssh-user'] . '@';
+    }
+    if (array_key_exists('ssh-password', $remote_alias_settings)) {
+      $pwfile = drush_save_data_to_temp_file($remote_alias_settings['ssh-password']);
+      $options = "$options --password-file $pwfile";
+    }    
   }
+  
+  // expand file paths
+  $source = _drush_core_expand_path($source, $remote_user);
+  $destination = _drush_core_expand_path($destination, $remote_user);
 
   // Prompt for confirmation. This is destructive.
   if (!drush_get_context('DRUSH_SIMULATE')) {
@@ -346,14 +358,122 @@ function drush_core_sync($source, $desti
     }
   }
 
-  $options = '-az';
   $exec = "rsync -e ssh $options --exclude \"*.svn*\" $source $destination";
   if (drush_get_context('drush_log')) {
     // the drush_op() will be verbose about the command that gets executed.
     $options .= 'v';
   }
 
-  return drush_op('system', $exec) !== FALSE;
+  $exec_result=drush_op('system', $exec) !== FALSE;
+  if (!empty($pwfile)) {
+    drush_op('unlink', $pwfile);
+  }
+  
+  return $exec_result;
+}
+
+// a path is "machine:/path" or "machine:path" or "/path" or "path"
+function _drush_core_expand_path($path, $remote_user) {
+  $machine = '';
+  $path_aliases = array();
+  // Parse site aliases if there is a colon in the path
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+    $machine = $alias;
+    $path = substr($path, $colon_pos + 1);
+    $site_alias_settings=_drush_core_get_site_alias_settings($alias);
+    if (!empty($site_alias_settings)) {
+      if (array_key_exists('path-aliases', $site_alias_settings)) {
+        $path_aliases = $site_alias_settings['path-aliases'];
+      }
+      // Use 'uri' from settings if available; otherwise use alias name itself 
+      if (array_key_exists('uri', $site_alias_settings)) {
+        $machine = $site_alias_settings['uri'];
+      }
+    }
+    // Strip the machine portion of the path if the
+    // alias points to the local machine.
+    if (_drush_core_is_local_machine($machine)) {
+      $machine = '';
+    }
+    else {
+      $machine = "$remote_user$machine:";
+    }
+  }
+  // If path aliases does not contain an alias for '!root',
+  // then assume that the remote Drupal root is the same
+  // as the local Drupal root.
+  if (!array_key_exists('!root', $path_aliases)) {
+    $path_aliases['!root'] = drush_get_context('DRUSH_DRUPAL_ROOT');
+  }
+  // If path aliases does not contain an alias for '!files',
+  // and no machine alias was specified, then fill in file_directory_path().
+  if ((!array_key_exists('!files', $path_aliases)) && (empty($alias))) {
+    $path_aliases['!files'] = file_directory_path();
+  }
+  // Expand all relative path aliases to be based off of the Drupal root
+  foreach ($path_aliases as $key => $value) {
+    if (($value[0] != '/') && ($key != 'root')) {
+      $path_aliases[$key] = $path_aliases['!root'] . '/' . $path_aliases[$key];
+    }
+  }
+  // Check for path aliases if the path does not start with a '/'.
+  if ($path[0] != '/') {
+    $path = dt($path, $path_aliases);
+  }
+  // If we have a relative path that was used with an alias, then make
+  // it relative to the Drupal root.
+  if (($path[0] != '/') && (!empty($alias))) {
+      $path = $path_aliases['!root'] . '/' . $path;
+  }
+  return "$machine$path";
+}
+
+function _drush_core_is_local_machine($alias) {
+  // To do:  the directory in 'sites' might contain
+  // a component for the port or local path, so it
+  // would be better to do a 'contains' test on all
+  // files in 'sites' to determine if 'alias' is local.
+  // This might give a false positive, though: a remote
+  // 'mysite.com' would look local if there was a local
+  // 'dev.mysite.com'.
+  return ($alias == 'localhost') || ($alias == '127.0.0.1') || (file_exists(drush_get_context('DRUSH_DRUPAL_ROOT') . "/sites/$alias"));
+}
+
+function _drush_core_get_site_alias_settings($alias) {
+  $site_alias_settings = array();
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $site_alias_settings = $all_site_aliases[$alias];
+  }
+  return $site_alias_settings;
+}
+
+function _drush_core_get_site_alias_settings_if_remote($path) {
+  $site_alias_settings = array();
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+    if (!_drush_core_is_local_machine($alias)) {
+      $site_alias_settings = _drush_core_get_site_alias_settings($alias);
+    }
+  }
+  return $site_alias_settings;
+}
+
+function _drush_core_get_site_alias_settings_for_remote($source, $destination) {
+  $source_settings = _drush_core_get_site_alias_settings_if_remote($source);
+  $destination_settings = _drush_core_get_site_alias_settings_if_remote($destination);
+  /*if ((!empty($source_settings)) && (!empty($destination_settings))) {
+    return FALSE;
+  }
+  else*/if (!empty($source_settings)) {
+    return $source_settings;
+  }
+  else {
+    return $destination_settings;
+  }
 }
 
 /**
Index: commands/sql/sql.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/sql/sql.drush.inc,v
retrieving revision 1.17
diff -u -p -r1.17 sql.drush.inc
--- commands/sql/sql.drush.inc  11 May 2009 20:53:10 -0000      1.17
+++ commands/sql/sql.drush.inc  29 May 2009 21:49:35 -0000
@@ -22,6 +22,8 @@ function sql_drush_help($section) {
       return dt("Usage: drush [options] sql query <query>...\n<query> is a SQL statement, which can alternatively be passed via STDIN. Any additional arguments are passed to the mysql command directly.");
     case 'drush:sql load':
       return dt("Usage: drush [options] sql load <source_dir> <target_dir>.\n <source_dir> and <target_dir> are names of directories under \'sites\'. These determine from where and to where you want your database copied. Optional: specify \'common\' for --skip if you wish to omit disposable tables like cache*, search*, etc. Your skip lists are specified in your drushrc.php file. Any additional arguments are passed to the mysqldump command directly.");
+    case 'drush:sql sync':
+      return dt("Usage: drush [options] sql sync <source_alias> <target_alias>.\n <source_alias> and <target_alias> are site aliases, or names of directories under \'sites\'. These determine from where and to where you want your database copied.");
   }
 }
 
@@ -54,6 +56,7 @@ function sql_drush_command() {
       'drush sql dump --result-file=../18.sql' => 'Save SQL dump to the directory above Drupal root.',
       'drush sql dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.com',
     ),
+    //  '--cache' => 'Skip dump is result file exists and is less than "cache" hours old. Optional.',
     'options' => array(
       '--result-file' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
       '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
@@ -83,6 +86,25 @@ function sql_drush_command() {
       'from' => 'Name of subdirectory within /sites. Its settings.php defines where to dump from.',
       'to' => 'Name of subdirectory within /sites. Its settings.php defines the destination for the dump.',
     ),
+    'optional-arguments' => TRUE,
+    'options' => array(
+      '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
+      '--structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
+      '--source_database' => 'The SOURCE DB connection key if using multiple connections in settings.php.',
+      '--destination_database' => 'The DESTINATION DB connection key if using multiple DB connections',
+    ),
+  );
+  $items['sql sync'] = array(
+    'callback' => 'drush_sql_sync',
+    'description' => 'Copy source database to target database using rsync.',
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
+    'examples' => array(
+      'drush sql sync prod dev' => 'Copy the DB defined in sites/prod to the DB in sites/dev.',
+    ),
+    'arguments' => array(
+      'from' => 'Name of subdirectory within /sites or a site alias.',
+      'to' => 'Name of subdirectory within /sites or a site alias.',
+    ),
     'options' => array(
       '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
       '--structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
@@ -115,7 +137,7 @@ function drush_sql_conf() {
 /**
  * Command callback. Emits a connect string for mysql or pgsql.
  */
-function drush_sql_connect() {
+function _drush_sql_connect() {
   switch (_drush_sql_get_scheme()) {
     case 'mysql':
       $command = 'mysql ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
@@ -126,7 +148,11 @@ function drush_sql_connect() {
       $command .= _drush_sql_get_credentials();
       break;
   }
-  drush_print($command);
+  return $command;
+}
+
+function drush_sql_connect() {
+  drush_print(_drush_sql_connect());
 }
 
 /**
@@ -254,6 +280,121 @@ function drush_sql_query($query) {
   }
 }
 
+function drush_sql_sync($source, $destination) {
+  // Expand the source and destination specifications into options
+  drush_expand_site_specification($source, 'source-');
+  drush_expand_site_specification($destination, 'target-');
+
+  // Get the options for the source and target databases
+  $source = _drush_sql_get_spec_from_options('source-');
+  $source_remote_host = drush_get_option('source-remote-host');
+  $source_remote_port = drush_get_option('source-remote-port');
+
+  $target = _drush_sql_get_spec_from_options('target-');
+  $target_remote_host = drush_get_option('target-remote-host');
+  $target_remote_port = drush_get_option('target-remote-port');
+    
+  // Prompt for confirmation. This is destructive.
+  if (!drush_get_context('DRUSH_SIMULATE')) {
+    $txt_source = (isset($source_remote_host) ? $source_remote_host . '/' : '') . $source['database'];
+    $txt_destination = (isset($target_remote_host) ? $target_remote_host . '/' : '') . $target['database'];
+    drush_print(dt("You will destroy data from !target and replace with data from !source.", array('!source' => $txt_source, '!target' => $txt_destination)));
+    drush_print();
+    // TODO: actually make the backup if desired.
+    drush_print(dt("You might want to make a backup first, using sql_dump command.\n"));
+    if (!drush_confirm(dt('Do you really want to continue?'))) {
+      drush_die('Aborting.');
+    }
+  }
+
+  // Set up the result file and the remote file.
+  // If the result file is not set, then create a temporary file.
+  // If the remote file is not set, use the same name for the remote
+  // and local files and hope for the best.
+  $source_dump = drush_get_option('source-dump');
+  if (!isset($source_dump)) {
+    $source_dump = tempnam(sys_get_temp_dir(), 'sql');
+  }
+  $target_dump = drush_get_option('target-dump');
+  if (!isset($target_dump)) {
+    $target_dump = tempnam(sys_get_temp_dir(), 'sql');
+  }    
+
+  if (isset($source_remote_host) && isset($target_remote_host)) {
+    $local_file = tempnam(sys_get_temp_dir(), 'sql');
+  }
+  elseif (!isset($source_remote_host) && !isset($target_remote_host)) {
+    $source_dump = $target_dump;
+    $local_file = $source_dump;
+    
+  }
+  elseif (!isset($source_remote_host)) {
+    $local_file = $source_dump;
+  }
+  else {
+    $local_file = $target_dump;
+  }
+
+  //
+  // If source is remote, then use ssh to dump the database and then rsync to local machine
+  // If source is local, call drush_sql_dump to dump the database to local machine
+  // In either case, the '--no-dump' option will cause the sql dump step to be skipped, and
+  // we will import from the existing local file (first using rsync to fetch it if it does not exist)
+  //  
+  $no_dump = drush_get_option(array('no-dump', 'source-no-dump'));
+  if (isset($source_remote_host)) {
+    if (isset($source_remote_port)) {
+      $source['port'] = $source_remote_port;
+    }
+    $source_remote_user = drush_get_option('source-remote-user'); 
+    if (isset($source_remote_user)) {
+      $source_at ='@';
+      $source_remote_pass = drush_get_option('source-remote-pass') ? ':' . drush_get_option('source-remote-pass') : '';
+    }
+    
+    if (!isset($no_dump)) {
+      drush_set_option('result-file', $source_dump);
+      $dump_exec = drush_sql_dump($source);
+      $dump_exec = 'ssh ' . $source_remote_user . $source_at . $source_remote_host . ' ' . escapeshellarg($dump_exec) . ' && ';
+    }
+    $dump_exec = $dump_exec . 'rsync ' . $source_remote_user . $source_remote_pass . $source_at . $source_remote_host . ':' . $source_dump . ' ' . $local_file;
+  }
+  else {
+    if (!isset($no_dump)) {
+      drush_op('unlink', $local_file);
+      drush_set_option('result-file', $local_file);
+      $dump_exec = drush_sql_dump($dump_exec);
+    }
+  }
+  
+  drush_op('system', $dump_exec);
+  
+  // IF FAILURE THEN ABORT
+  
+  // If destination is remote, then use rsync to push the database, then use ssh to import the database
+  // If destination is local, then just import the database locally
+    
+  if (isset($target_remote_host)) {
+    if (isset($target_remote_port)) {
+      $target['port'] = $target_remote_port;
+    }
+    $target_remote_user = drush_get_option('target-remote-user'); 
+    if (isset($target_remote_user)) {
+      $target_at ='@';
+      $target_remote_pass = drush_get_option('target-remote-pass') ? ':' . drush_get_option('target-remote-pass') : '';
+    }
+    
+    $connect_exec = _drush_sql_connect($target) . ' < ' . $target_dump;
+    $import_exec = 'rsync ' . $local_file . ' ' . $target_remote_user . $target_remote_pass . $target_at . $target_remote_host . ':' . $target_dump;
+    $import_exec = $import_exec . '; ssh ' . $target_remote_user . $target_at . $target_remote_host . ' ' . escapeshellarg($connect_exec);
+  }
+  else {
+    $import_exec = _drush_sql_connect($target) . ' < ' . $local_file;
+  }
+  
+  drush_op('system', $import_exec);
+}
+
 /**
  * Copy an entire database to another database. For example, migrate from production to dev
  * or dev to staging.
@@ -267,8 +408,10 @@ function drush_sql_query($query) {
  *    be replaced with the contents of `source`. *
  **/
 function drush_sql_load($source, $destination) {
-  $source = _drush_sql_get_spec_from_settings("./sites/$source/settings.php", 'source');
-  $destination = _drush_sql_get_spec_from_settings("./sites/$destination/settings.php", 'target');
+  # $source = _drush_sql_get_spec_from_settings("./sites/$source/settings.php", 'source');
+  # $destination = _drush_sql_get_spec_from_settings("./sites/$destination/settings.php", 'target');
+  $source = _drush_sql_get_spec_from_site_alias($source, 'source');
+  $destination = _drush_sql_get_spec_from_site_alias($destination, 'target');
   
   // Prompt for confirmation. This is destructive.
   if (!drush_get_context('DRUSH_SIMULATE')) {
@@ -367,6 +510,18 @@ function _drush_sql_get_db_spec() {
   }
 }
 
+// Build a $db_spec from a given site alias
+function _drush_sql_get_spec_from_site_alias($alias, $prefix) {
+  global $db_url;
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $site_alias_settings = $all_site_aliases[$alias];
+    $db_url = $site_alias_settings['db-url'];
+    return _drush_sql_get_db_spec();
+  }
+  return _drush_sql_get_spec_from_settings("./sites/$alias/settings.php", $prefix);
+}
+
 // Build a $db_spec from a given settings.php.
 function _drush_sql_get_spec_from_settings($file, $prefix) {
   // Reset global databases to whatever is defined in $file
@@ -384,6 +539,22 @@ function _drush_sql_get_spec_from_settin
   return _drush_sql_get_db_spec();
 }
 
+function _drush_sql_get_spec_from_options($prefix) {
+  global $db_url;
+  
+  $db_url = drush_get_option($prefix . 'db-url');
+    
+  // Translate the custom options for sql sync into ones usually recognized (i.e. no prefix).
+  if ($database = drush_get_option($prefix . 'database')) {
+    drush_set_option('database', $value);
+  }
+  if ($target = drush_get_option($prefix . 'target')) {
+    drush_set_option('target', $target);
+  }
+
+  return _drush_sql_get_db_spec();
+}
+
 function _drush_sql_get_scheme($db_spec = NULL) {
   if (is_null($db_spec)) {
     $db_spec = _drush_sql_get_db_spec();
@@ -415,14 +586,14 @@ function _drush_sql_get_credentials($db_
       }
 
       $cred = $db_spec['database'] . ' ' .
-         (empty($db_spec['host']) || ($db_spec['host'] == "localhost") ? '' : '-h ' . $db_spec['host']) . ' ' .
-         (empty($db_spec['port']) ? '' : '-p' . $db_spec['port']) . ' ';
+         (empty($db_spec['host'])  ? '-h localhost ' : '-h ' . $db_spec['host']) . ' ' .
+         (empty($db_spec['port']) ? '-p 5432 ' : '-p ' . $db_spec['port']) . ' ';
       // Adding '-U' will cause Postgres to prompt for a password, so disable this for now.
       // Use "sudo -u postgres drush sql ..." to access the database without a password,
       // presuming that "postgres" is the database superuser and you have
       // "local all all ident sameuser" in your Postgres pg_hba.conf file.
       // See: http://drupal.org/node/438828
-      // $cred .= '-U ' . $db_spec['username'] . ' ';
+      $cred .= '-U ' . $db_spec['username'] . ' ';
       break;
   }
   return escapeshellcmd($cred);
Index: includes/backend.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/backend.inc,v
retrieving revision 1.15
diff -u -p -r1.15 backend.inc
--- includes/backend.inc        20 May 2009 21:26:55 -0000      1.15
+++ includes/backend.inc        29 May 2009 21:49:35 -0000
@@ -233,7 +233,13 @@ function _drush_proc_open($cmd, $data = 
  *   If the command was completed, this will return an associative array containing the data from drush_backend_output().
  */
 function drush_backend_invoke($command, $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL) {
-  $cmd = _drush_backend_generate_command($command, $data, $method, $drush_path, $hostname, $username);
+  $args = explode(" ", $command);
+  $command = array_shift($args);
+  return drush_backend_invoke_args($command, $args, $data, $method, $integrate, $drush_path, $hostname, $username);
+}
+
+function drush_backend_invoke_args($command, $args, $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL) {
+  $cmd = _drush_backend_generate_command($command, $args, $data, $method, $drush_path, $hostname, $username);
   drush_log(dt('Running: !cmd', array('!cmd' => $cmd)), 'command');
   return _drush_backend_invoke($cmd, $data, $integrate);
 }
@@ -280,9 +286,11 @@ function _drush_backend_invoke($cmd, $da
 
 /**
  * Generate a command to execute.
-
+ *
  * @param command
  *    A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'.
+ * @param args
+ *    An array or arguments for the command.
  * @param data
  *    Optional. An array containing options to pass to the remote script.
  *    Array items with a numeric key are treated as optional arguments to the command.
@@ -308,20 +316,18 @@ function _drush_backend_invoke($cmd, $da
  * @return
  *   A text string representing a fully escaped command.
  */
-function _drush_backend_generate_command($command, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null) {
+function _drush_backend_generate_command($command, $args, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null) {
   $drush_path = !is_null($drush_path) ? $drush_path : DRUSH_COMMAND; // Call own drush.php file.
   $data['root'] = ($data['root']) ? $data['root'] : drush_get_context('DRUSH_DRUPAL_ROOT');
   $data['uri'] = array_key_exists('uri', $data) ? $data['uri'] : drush_get_context('DRUSH_URI');
 
   $option_str = _drush_backend_argument_string($data, $method);
-  $args = explode(" ", $command);
   foreach ($data as $key => $arg) {
     if (is_numeric($key)) {
       $args[] = $arg;
       unset($data[$key]);
     }
   }
-  $command = '';
   foreach ($args as $arg) {
     $command .= ' ' . escapeshellarg($arg);
   }
@@ -347,7 +353,9 @@ function _drush_backend_generate_command
  */
 function drush_backend_fork($command, $data, $drush_path = null, $hostname = null, $username = null) {
   $data['quiet'] = TRUE;
-  $cmd = "(" . _drush_backend_generate_command($command, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null';
+  $args = explode(" ", $command);
+  $command = array_shift($args);
+  $cmd = "(" . _drush_backend_generate_command($command, $args, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null';
   drush_log(dt("Forking : !cmd", array('!cmd' => $cmd)));
   system($cmd);
 }
Index: includes/command.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/command.inc,v
retrieving revision 1.36
diff -u -p -r1.36 command.inc
--- includes/command.inc        27 May 2009 19:38:12 -0000      1.36
+++ includes/command.inc        29 May 2009 21:49:35 -0000
@@ -147,6 +147,16 @@ function drush_get_commands() {
  */
 function drush_parse_command() {
   $args = drush_get_arguments();
+  
+  // Test to see if the first arg is a site specification
+  if (drush_expand_site_specification($args[0])) {
+    array_shift($args);
+    // We only need to expand the site specification
+    // once, then we are done.  (Should we set a flag indicating
+    // that we already did this?  Otherwise 'drush alias1 alias2 command'
+    // would behave strangely.)
+    drush_set_arguments($args);
+  }
 
   // Get a list of all implemented commands.
   $implemented = drush_get_commands();
@@ -156,6 +166,7 @@ function drush_parse_command() {
   // Try to determine the handler for the current command.
   while (!$command && count($args)) {
     $part = implode(" ", $args);
+    drush_log(dt("part is !part", array('!part' => $part)));
     if (isset($implemented[$part])) {
       $command = $implemented[$part];
     }
@@ -167,7 +178,15 @@ function drush_parse_command() {
   // We have found a command that matches. Set the appropriate values.
   if ($command) {
     $arguments = array_reverse($arguments);
-
+    // If the command has the 'optional-arguments' flag set, then
+    // insure that there are enough arguments provided on the command
+    // line.  Keep adding null parameters until there are enough.
+    if (isset($command['optional-arguments']) && isset($command['arguments'])) {
+      while (count($arguments) < count($command['arguments'])) {
+        $arguments[] = null;
+      }
+    }
+    
     // Merge specified callback arguments, which precede the arguments passed on the command line.
     if (isset($command['callback arguments']) && is_array($command['callback arguments'])) {
       $arguments = array_merge($command['callback arguments'], $arguments);
Index: includes/context.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/context.inc,v
retrieving revision 1.8
diff -u -p -r1.8 context.inc
--- includes/context.inc        24 May 2009 18:42:09 -0000      1.8
+++ includes/context.inc        29 May 2009 21:49:35 -0000
@@ -265,7 +265,7 @@ function drush_get_option($option, $defa
   else {
     // We are not checking a specific context, so check them in a predefined order of precedence.
     static $contexts = array(
-      'process', 'options', 'stdin', 
+      'process', 'options', 'stdin', 'alias',
       'custom', 'site', 'drupal', 'user', 'system', 
       'drush', 'default');
 
@@ -290,7 +290,7 @@ function drush_get_option($option, $defa
  */
 function drush_get_merged_options() {
   static $contexts = array(
-      'process', 'options', 'stdin', 
+      'process', 'options', 'stdin', 'alias', 
       'custom', 'site', 'drupal', 'user', 'system',
       'drush', 'default');
   $cache = drush_get_context();
Index: includes/environment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/environment.inc,v
retrieving revision 1.34
diff -u -p -r1.34 environment.inc
--- includes/environment.inc    27 May 2009 00:40:59 -0000      1.34
+++ includes/environment.inc    29 May 2009 21:49:35 -0000
@@ -375,6 +375,9 @@ function _drush_bootstrap_drush() {
   // Load a drushrc.php file in the ~/.drush directory.
   drush_load_config('user');
 
+  // If the '-l' / '--uri' option is a site alias, then process it
+  _drush_process_site_alias();
+  
   $backend = drush_set_context('DRUSH_BACKEND', drush_get_option(array('b', 'backend')));
 
   if ($backend) {
@@ -409,6 +412,87 @@ function _drush_bootstrap_drush() {
   _drush_find_commandfiles(DRUSH_BOOTSTRAP_DRUSH);
 }
 
+function _drush_process_site_alias() {
+  return drush_expand_site_alias(drush_get_option(array('l', 'uri')));
+}
+
+function drush_expand_site_alias($alias, $prefix = '') {
+  if(isset($alias)) {
+    // Test to see if uri is a site alias
+    $all_site_aliases = drush_get_option('site-aliases', array());
+    if (array_key_exists($alias, $all_site_aliases)) {
+      $site_alias_settings = $all_site_aliases[$alias];
+      
+      // Transfer all non-array options from the site alias to the drush options
+      // in the 'alias' context.
+      foreach ($site_alias_settings as $key => $value) {
+        if (!is_array($value)) {
+         drush_set_option($prefix . $key, $value, 'alias');
+       }
+      }
+      // Transfer selected path aliases to the drush options.
+      if (array_key_exists('path-aliases', $site_alias_settings)) {
+       foreach (array('!root', '!drush', '!dump') as $key) {
+         if (array_key_exists($key, $site_alias_settings['path-aliases'])) {
+           drush_set_option($prefix . substr($key, 1), $site_alias_settings['path-aliases'][$key], 'alias');
+         }
+       }
+      }
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+// Site specifications come in several forms:
+// 1.) /path/to/drupal#sitename
+// 2.) user@server/path/to/drupal#sitename
+// 3.) user@server/path/to/drupal            (sitename == server)
+// 4.) user@server#sitename                  (only if $option['r'] already set)
+// 5.) sitename                              (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)      
+function drush_expand_site_specification($alias, $prefix = '') {
+  // First test to see if it is a site alias.
+  $result = drush_expand_site_alias($alias, $prefix);
+  if (isset($alias)) {
+    // If it is not a site alias, perhaps is is a site specification
+    if ($result == FALSE) {
+      // Cases 1.) - 4.):
+      // We will check for a site specification if the alias has at least
+      // two characters from the set '@', '/', '#'.
+      if ((strpos($alias, '@') == FALSE ? 0 : 1) + (strpos($alias, '/') == FALSE ? 0 : 1) + (strpos($alias, '#') == FALSE ? 0 : 1) >= 2) {
+        // Add on a scheme so that "user:pass@server" will always parse correctly
+        $parsed = parse_url('site://' . $alias);
+        foreach (array('host' => 'remote_host', 'user' => 'remote_user', 'pass' => 'remote_pass', 'path' => 'root', 'fragment' => 'uri') as $url_key => $option_key) {
+          if (array_key_exists($url_key, $parsed)) {
+            drush_set_option($option_key, $parsed[$url_key]);
+         }
+        }
+        // Case 3.): If the URL contains a 'host' portion but no fragment, then set the uri to the host
+        // Note: We presume that 'server' is the best default for case 3; without this code, the default would
+        // be whatever is set in $options['l'] on the target machine's drushrc.php settings file.
+        if (array_key_exists('host', $parsed) && !array_key_exists('fragment', $parsed)) {
+          drush_set_option('uri', $parsed['host']);
+        }
+        $result = TRUE;
+      }
+      // Case 5.):
+      // If the alias is the name of a folder in the 'sites' directory, 
+      // then use it as a local site specification.
+      // BUG WORKAROUND:  if we use 'drush_locate_root' we will set 'uri' after 
+      // drush_bootstrap_drush, which works.  If we use drush_get_context('DRUSH_DRUPAL_ROOT'),
+      // then we set 'uri' after drush_bootstrap_drupal_root, which seems like it should work
+      // but doesn't.
+      $drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
+      // drush_get_context('DRUSH_DRUPAL_ROOT');
+      if (isset($drupal_root) && file_exists($drupal_root . '/sites/' . $alias . '/settings.php')) {
+        drush_set_option('uri', $alias);
+        $result = TRUE;
+      }
+    }
+  }
+  return $result;
+}
+
 /**
  * Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
  *
