? drush_pm
? drush_pm_cvs
? drush_pm_svn
? drush_pm_wget
? drush_simpletest
? drush_sql
? drush_tools
? drushrc.php
? drushrc.phpe
? example.drushrc.phpe
? services
? commands/alias
? commands/pm/handler
? includes/table.inc
Index: drush.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/drush.php,v
retrieving revision 1.69
diff -u -p -r1.69 drush.php
--- drush.php	30 Jun 2009 01:01:59 -0000	1.69
+++ drush.php	31 Jul 2009 00:13:21 -0000
@@ -71,6 +71,35 @@ 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');
+        drush_log(dt('Begin drush_backend_invoke'));
+        $output = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
+        drush_log(dt('drush_backend_invoke is complete'));
+       
+        // 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']);
+        drush_set_context("DRUSH_EXECUTION_COMPLETED", TRUE);
+        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	31 Jul 2009 00:13:21 -0000
@@ -78,3 +78,54 @@ $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 can be
+ * used in place of a site specification on the command line, and
+ * mya also be used in arguments to certain commands such as
+ * "drush sync", "drush sql sync" and "drush sql load".
+ *
+ * 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.  Behaves like the "-l" command-line flag.
+ * - 'remote-host': The fully-qualified domain name of the remote system
+ * hosting the Drupal instance.  The remote-host option must be
+ * omitted for local sites, as this option controls whether or not
+ * rsync parameters are for local or remote machines.
+ * - 'remote-user': The username to log in as when using ssh or 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',
+#    'remote-user' => 'publisher',
+#    '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.30
diff -u -p -r1.30 core.drush.inc
--- commands/core/core.drush.inc	2 Jun 2009 23:48:31 -0000	1.30
+++ commands/core/core.drush.inc	31 Jul 2009 00:13:21 -0000
@@ -82,13 +82,23 @@ function core_drush_command() {
     
   );
   $items['sync'] = array(
-    'description' => 'Rsync the Drupal tree to/from another server using ssh.',
+    'callback' => 'drush_core_sync',
+    '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' => 'May be rsync path or site alias.  See rsync documentation and example.drushrc.php.',
+      'destination' => 'May be rsync path or site alias.  See rsync documentation and example.drushrc.php.',
+    ),
+    'options' => array(
+      '--mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz.  Default is -az.',
+      '--RSYNC-FLAG' => 'Most rsync flags passed to drush sync will be passed on to rsync.  See rsync documentation.',
+      '--all-paths' => 'If <source> and <destination> are site aliases, and neither <source> nor <destinatiln> specifies a path component, then rsync all path aliases that are defined in both the source and destination alias.',
+      '--exclude-conf' => 'Implies rsync --exclude="settings.php" --exclude="robots.txt" --exclude=".htaccess"',
+      '--exclude-sites' => 'Exclude all directories in "sites/" except for "sites/all".',
     ),
     'examples' => array(
-      'sync dev:/var/www/files/ stage:/var/www/files/' => 'Rsync \'files\' dir from dev to stage',
+      'sync dev stage' => 'Rsync Drupal root from dev to stage (one of which must be local).',
+      'sync --all-paths --exclude-conf dev stage' => 'Rsync Drupal root and all other paths from dev to stage for every path alias defined in both dev and stage.',
+      '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(
@@ -331,16 +341,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');
-  
-  if (!strstr($source, ':')) {
-    $source = $drupal_root. "/$source";
-  }
-  if (!strstr($destination, ':')) {
-    $destination = $drupal_root . "/$destination";
+function _drush_core_sync($source, $destination, $parametric_options = array()) {
+  $remote_user = '';
+  $password = '';
+   
+  // One of $souruce or $destination (at most) will be remote;
+  // get the site alias settings for the remote component.
+  $remote_alias_settings = _drush_get_alias_record_for_remote($source, $destination);
+  if (!empty($remote_alias_settings)) {
+    if (array_key_exists('remote-user', $remote_alias_settings)) {
+      $remote_user = $remote_alias_settings['remote-user'] . '@';
+    }
+    // TODO: password option does not work with rsync; use key files
+    if (array_key_exists('remote-pass', $remote_alias_settings)) {
+      $password = $remote_alias_settings['remote-pass'];
+    }
   }
+  
+  // 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')) {
@@ -349,15 +369,306 @@ function drush_core_sync($source, $desti
       return drush_set_error('CORE_SYNC_ABORT', 'Aborting.');
     }
   }
+  
+  return drush_core_call_rsync($source, $destination, $remote_user, $password, $parametric_options);
+}
 
-  $options = '-az';
-  $exec = "rsync -e ssh $options --exclude \"*.svn*\" $source $destination";
+function drush_core_call_rsync($source, $destination, $remote_user, $password, $parametric_options = array()) {
+  // merge the options specified by the caller with the options specified on the command line
+  $all_specified_options = array_merge(drush_get_merged_options(), $parametric_options);
+  $pwfile = '';
+  $options = ' --exclude="*.svn*"';
+  $mode = '-az';
+  if (array_key_exists('exclude-conf', $all_specified_options)) {
+    $options .= ' --exclude="settings.php" --exclude="robots.txt" --exclude=".htaccess"';
+  }
+  if (array_key_exists('exclude-sites', $all_specified_options)) {
+    $options .= ' --include="sites/all" --exclude="sites/*"';
+  }
+  if (array_key_exists('mode', $all_specified_options)) {
+    $mode = "-" . $all_specified_options['mode'];
+  }
   if (drush_get_context('drush_log')) {
     // the drush_op() will be verbose about the command that gets executed.
-    $options .= 'v';
+    $mode .= 'v';
+    $options .= ' --stats --progress';
   }
+  if (!empty($password)) {
+    $pwfile = drush_save_data_to_temp_file($password);
+    $options .= " --password-file $pwfile";
+  }
+  $rsync_available_options = array(
+    // unary options
+    'archive', // -a
+    'recursive', // -r
+    'relative', // -R
+    'backup', // -b
+    'update', // -u
+    'checksum', // -c
+    'dirs', // -d
+    'links', // -l
+    'copy-links', // -L
+    'copy-dirlinks', // -k
+    'keep-dirlinks', // -K
+    'hard-links', // -H
+    'perms', // -p
+    'executability', // -E
+    'acls', // -A
+    'xattrs', // -X
+    'owner', // -o
+    'group', // -g
+    'times', // -t
+    'omit-dir-times', // -O
+    'sparse', // -S
+    'dry-run', // -n
+    'whole-file', // -W
+    'one-file-system', // -x
+    'prune-empty-dirs', // -m
+    'ignore-times', // -I
+    'fuzzy', // -y
+    'cvs-exclude', // -C
+    'compress', // -Z
+    'protect-args', // -s
+    '8-bit-output', // -8
+    'human-readable', // -h
+    'itemize-changes', // -i
+    'copy-unsafe-links',
+    'safe-links',
+    'no-implied-dirs',
+    'inplace',
+    'append',
+    'append-verify',
+    'existing',
+    'remove-source-files',
+    'delete',
+    'delete-before',
+    'delete-during',
+    'delete-delay',
+    'delete-after',
+    'delete-excluded',
+    'ignore-errors',
+    'force',
+    'ignore-existing',
+    'partial',
+    'delay-updates',
+    'numeric-ids',
+    'size-only',
+    'blocking-io',
+    'stats',
+    'progress',
+    'list-only',
+    // options with values
+    'block-size',
+    'backup-dir',
+    'suffix',
+    'chmod', 
+    'rsync-path', 
+    'modify-window', 
+    'compare-dest', 
+    'copy-dest', 
+    'link-dest', 
+    'skip-compress', 
+    'filter', 
+    'exclude', 
+    'include', 
+    'address', 
+    'port', 
+    'sockopts', 
+    'out-format', 
+    'bwlimit', 
+    'iconv', 
+    'checksum-seed',
+    'max-delete',
+    'max-size',
+    'min-size',
+    'partial-dir',
+    'timeout',
+    'temp-dir',
+    'compress-level',
+    'out-format',
+    'protocol', 
+    );
+  foreach ($rsync_available_options as $test_option) {
+    if (array_key_exists($test_option, $all_specified_options)) {
+      $value = $all_specified_options[$test_option];
+      if ($value === TRUE) {
+        $options .= " --$test_option";
+      } 
+      elseif (isset($value)) {
+        $options .= " --$test_option=" . escapeshellarg($value);
+      }
+    }
+  }
+  $exec = "rsync -e ssh $mode$options $source $destination";
+ 
+  $exec_result = drush_op('system', $exec) !== FALSE;
+  if (!empty($pwfile)) {
+    drush_op('unlink', $pwfile);
+  }
+  
+  return $exec_result;
+}
+
+// TODO:  This function, when called with --all-paths, will call _drush_core_sync
+// repeatedly, which will prompt for a y/n confirmation for every path (unless
+// 'drush -y' is used).  It would be better if it could prompt just once, but if
+// we did that, then the prompt should contain all of the paths that are going to
+// be copied.
+function drush_core_sync($source, $destination) {
+  $sync_all_paths = drush_get_option('all-paths');
+  if ((isset($sync_all_paths)) && ($result !== FALSE)) {
+    // If the source and destination paths are both aliases that contain
+    // no path component (that is, 'alias', not 'alias:/path'), then we will
+    // iterate over all of the path aliases of both, and sync all that are
+    // in common to both alias records.
+    $source_settings = drush_get_alias_record($source);
+    $destination_settings = drush_get_alias_record($destination);
+    if ((!empty($source_settings)) && (!empty($destination_settings)) && array_key_exists('path-aliases',$source_settings) && array_key_exists('path-aliases', $destination_settings)) {
+      $source_path_aliases = $source_settings['path-aliases'];
+      $destination_path_aliases = $destination_settings['path-aliases'];
+      $source_root = $source_path_aliases['!root'];
+      $destination_root = $destination_path_aliases['!root'];
+      // First call drush sync to rsync the source and destination paths as provided
+      $result = _drush_core_sync($source, $destination, array('exclude-sites' => TRUE));
+      // After sync-ing with 'exclude-sites', go back and sync the
+      // site directory for this installation.
+      if ($result !== FALSE) {
+        // TODO:  If the destination already exists, then it will probably have
+        // its own 'settings.php', and we almost certainly will want to call with
+        // --exclude-conf.  However, if the destination does not yet exist, then
+        // it would be confusing to include --exclude-conf.  Which option is better?
+        $result = _drush_core_sync($source . ':!root/sites/' . $source_settings['uri'] . '/', $destination . ':!root/sites/' . $destination_settings['uri'] . '/');  
+      }
+      // Iterate over all of the other path aliases
+      foreach ($source_path_aliases as $key => $value) {
+        // Skip this entry if its value is a path that is somewhere inside of the root,
+        // which we already rsync'ed at the top of this function.  (This also skips '!root').
+        if (($result !== FALSE) && (strncmp($source_root, $value, strlen($value)) != 0) && array_key_exists($key, $destination_path_aliases)) {
+          $result = _drush_core_sync($source . ':' . $key, $destination . ':' . $key);  
+        }
+      }
+    }
+  }
+  // If we didn't do a special-case rsync (alias-to-alias),
+  // then call sync with the original parameters nere
+  if (!isset($result)) {
+    $result = _drush_core_sync($source, $destination);
+  }
+}
 
-  return drush_op('system', $exec) !== FALSE;
+// a path is "machine:/path" or "machine:path" or "/path" or "path"
+function _drush_core_expand_path($path, $remote_user) {
+  $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);
+    $path = substr($path, $colon_pos + 1);
+    $site_alias_settings = drush_get_alias_record($alias);
+    $machine = $alias;
+  }
+  else {
+    // if the path is a site alias or a local site...
+    $site_alias_settings = drush_get_alias_record($path);
+    if (!empty($site_alias_settings) || _drush_core_is_local_machine($path)) {
+      $alias = $path;
+      $path = '';
+    }
+  }
+  
+  if (!empty($site_alias_settings)) {
+    if (array_key_exists('path-aliases', $site_alias_settings)) {
+      $path_aliases = $site_alias_settings['path-aliases'];
+    }
+    // Use 'remote-host' from settings if available; otherwise site is local 
+    if (array_key_exists('remote-host', $site_alias_settings)) {
+      $machine = $site_alias_settings['remote-host'];
+    }
+    else {
+      $machine = '';
+    }
+  }
+  
+  // 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');
+  }
+  foreach ($path_aliases as $key => $value) {
+    // Expand all relative path aliases to be based off of the Drupal root
+    if (($value[0] != '/') && ($key != 'root')) {
+      $path_aliases[$key] = $path_aliases['!root'] . '/' . $value;
+    }
+    // Rsync is very particular about the meaning of paths that
+    // end with a '/' compared to those that do not.  In order to
+    // copy from !path to !path without creating an extra spurrious
+    // directory, the path alias must end with a '/'.  Therefore
+    // we will add a slash to the end of any alias that does not
+    // already have one.
+    if ($value[strlen($value)-1] != '/') {
+      $path_aliases[$key] = $path_aliases[$key] . '/';
+    }
+  }
+  
+  // Fill in path aliases in the path.
+  $path = dt($path, $path_aliases);
+  
+  // If the path is empty or if it does not exist, then 
+  // make it relative to the Drupal root.
+  if (empty($path) || ($path[0] != '/')) {
+      $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_get_alias_record_if_remote($path) {
+  $site_alias_settings = array();
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+  }
+  else {
+    $alias = $path;
+  }
+  if (!_drush_core_is_local_machine($alias)) {
+    $site_alias_settings = drush_get_alias_record($alias);
+  }
+  return $site_alias_settings;
+}
+
+function _drush_get_alias_record_for_remote($source, $destination) {
+  $source_settings = _drush_get_alias_record_if_remote($source);
+  $destination_settings = _drush_get_alias_record_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	31 Jul 2009 00:13:21 -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,7 @@ 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.',
@@ -90,6 +94,40 @@ function sql_drush_command() {
       '--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.',
+    ),
+    'optional-arguments' => TRUE,
+    'arguments' => array(
+      'from' => 'Name of subdirectory within /sites or a site alias.',
+      'to' => 'Name of subdirectory within /sites or a site alias.',
+    ),
+    // TODO:  password does not work with rsync; use key files
+    //  '--source-remote-pass' => 'Password for remote machine; used only with rsync commands, not ssh commands (not supported by ssh).',
+    //  '--target-remote-pass' => 'Password for remote machine; used only with rsync commands, not ssh commands (not supported by ssh).',
+    '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.',
+      '--cache' => 'Skip dump if result file exists and is less than "cache" hours old. Optional; default is 24 hours.',
+      '--no-cache' => 'Do not cache the sql dump file.',
+      '--no-dump' => 'Do not dump the sql database; always use an existing dump file.',
+      '--source-db-url' => 'Database specification for source system to dump from.',
+      '--source-remote-port' => 'Override sql database port number in source-db-url. Optional.',
+      '--source-remote-host' => 'Remote machine to run sql dump file on. Optional; default is local machine.',
+      '--source-dump' => 'Path to dump file. Optional; default is to create a temporary file.',
+      '--target-db-url' => 'Database specification for target system to import into.',
+      '--target-remote-port' => 'Override sql database port number in target-db-url. Optional.',
+      '--target-remote-host' => 'Remote machine to import sql dump file on. Optional; default is local machine.',
+      '--target-dump' => 'Path to dump file. Optional; default is to create a temporary file.',
+      '--create-db' => 'Create a new database before importing the database dump on the target machine.',
+      '--db-su' => 'Remote account to use when creating a new database. Optional.', 
+      '--fix-paths' => 'Fix up paths to drupal root, etc. before importing database on target machine.',
+    ),
+  );
   if (drush_drupal_major_version() >=7) {
     $items['sql load']['options'] += array(
       '--source_target' => 'The name of a target within the SOURCE database.',
@@ -115,18 +153,22 @@ function drush_sql_conf() {
 /**
  * Command callback. Emits a connect string for mysql or pgsql.
  */
-function drush_sql_connect() {
+function _drush_sql_connect($db_spec = NULL) {
   switch (_drush_sql_get_scheme()) {
     case 'mysql':
       $command = 'mysql ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
-      $command .= _drush_sql_get_credentials();
+      $command .= _drush_sql_get_credentials($db_spec);
       break;
     case 'pgsql':
-      $command = 'psql -d ';
-      $command .= _drush_sql_get_credentials();
+      $command = 'psql';
+      $command .= _drush_sql_get_credentials($db_spec);
       break;
   }
-  drush_print($command);
+  return $command;
+}
+
+function drush_sql_connect() {
+  drush_print(_drush_sql_connect());
 }
 
 /**
@@ -166,57 +208,73 @@ function drush_sql_dump($db_spec = NULL)
   }
   $database = $db_spec['database'];
 
+  if (is_null($db_spec)) {
+    $db_spec = _drush_sql_get_db_spec();
+  }
+  
+  $single_table = drush_get_option('table');
   switch (_drush_sql_get_scheme()) {
     case 'mysql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
       $exec = 'mysqldump' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
       if ($file = drush_get_option('result-file')) {
         $exec .= ' --result-file '. $file;
       }
       $extra = ' --single-transaction --opt -Q' . _drush_sql_get_credentials($db_spec);
       $exec .= $extra;
-
-      // Append the ignore-table options.
-      foreach ($skip_tables as $table) {
-        $ignores[] = "--ignore-table=$database.$table";
+      
+      if ($single_table) {
+        $exec .= ' ' . $single_table;
       }
-      $exec .= ' '. implode(' ', $ignores);
+      else {
+        // Append the ignore-table options.
+        foreach ($skip_tables as $table) {
+          $ignores[] = "--ignore-table=$database.$table";
+        }
+        $exec .= ' '. implode(' ', $ignores);
 
-      // Run mysqldump again and append output if we need some structure only tables.
-      if (!empty($structure_tables)) {
-        $exec .= "; mysqldump --no-data $extra " . implode(' ', $structure_tables);
-        if ($file) {
-          $exec .= " >> $file";
+        // Run mysqldump again and append output if we need some structure only tables.
+        if (!empty($structure_tables)) {
+          $exec .= "; mysqldump --no-data $extra " . implode(' ', $structure_tables);
+          if ($file) {
+            $exec .= " >> $file";
+          }
         }
       }
       break;
     case 'pgsql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
-      $exec = 'pg_dump --clean ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
+      $create_db = drush_get_option('create-db');
+      $exec = 'pg_dump ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
       if ($file = drush_get_option('result-file')) {
         $exec .= ' --file '. $file;
       }
-      $extra = ' ' . _drush_sql_get_credentials($db_spec);
+      // Unlike psql, pg_dump does not take a '-d' flag before the database name.
+      // We'll put the database name in 'by hand' and then clear the name from
+      // the record to prevent _drush_sql_get_credentials from inserting a -d term
+      // that pg_dump does not understand.
+      $extra = ' ' . $db_spec['database'];
+      $db_spec['database'] = null;
+      $extra .= _drush_sql_get_credentials($db_spec);
       $exec .= $extra;
-      foreach ($skip_tables as $table) {
-        $ignores[] = "--exclude-table=$table";
+      
+      if (isset($single_table)) {
+        $exec .= ' --data-only -t ' . $single_table;
       }
-      $exec .= ' '. implode(' ', $ignores);
-      // Run pg_dump again and append output if we need some structure only tables.
-      if (!empty($structure_tables)) {
-        $schemaonlies = array();
-	foreach ($structure_tables as $table) {
-          $schemaonlies[] = "--table=$table";
+      else {
+        $exec .= (!isset($create_db) ? '--clean ' : '');
+        foreach ($skip_tables as $table) {
+          $ignores[] = "--exclude-table=$table";
         }
-        $exec .= "; pg_dump --schema-only " . implode(' ', $schemaonlies) . $extra;
-        if ($file) {
-          $exec .= " >> $file";
+        $exec .= ' '. implode(' ', $ignores);
+        // Run pg_dump again and append output if we need some structure only tables.
+        if (!empty($structure_tables)) {
+          $schemaonlies = array();
+          foreach ($structure_tables as $table) {
+            $schemaonlies[] = "--table=$table";
+          }
+          $exec .= "; pg_dump --schema-only " . implode(' ', $schemaonlies) . $extra;
+          if ($file) {
+            $exec .= " >> $file";
+          }
         }
       }
       break;
@@ -229,22 +287,25 @@ function drush_sql_dump($db_spec = NULL)
  * Command callback. Executes the given SQL query on the Drupal database.
  */
 function drush_sql_query($query) {
+  return _drush_sql_query($query);
+}
+
+function _drush_sql_query($query, $db_spec = NULL) {
   // Save $query to a tmp file. We will redirect it in.
   if ($file = drush_save_data_to_temp_file($query)) {
     switch (_drush_sql_get_scheme()) {
       case 'mysql':
         $exec = 'mysql' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
-        $exec .= _drush_sql_get_credentials();
+        $exec .= _drush_sql_get_credentials($db_spec);
         $exec .= drush_get_option('extra');
         $exec .= " < $file";
 
         break;
       case 'pgsql':
-        $exec = 'psql -d ';
-        $exec .= _drush_sql_get_credentials();
+        $exec = 'psql';
+        $exec .= _drush_sql_get_credentials($db_spec);
         $exec .= (drush_get_context('DRUSH_VERBOSE') ? '' : ' -q');
-        $exec .= ' --file -';
-        $exec .= " < $file";
+        $exec .= ' --file $file';
         break;
     }
     
@@ -254,6 +315,255 @@ function drush_sql_query($query) {
   }
 }
 
+function drush_sql_sync($source, $destination) {
+  drush_log("enter drush sql sync");
+  
+  // Expand the source and destination specifications into options.
+  // The options from the 'source-*' and 'target-*' aliases are set
+  // in a drush context that has a lower priority than the command-line
+  // options; this allows command-line options to override the default
+  // values specified in a site alias.
+  drush_expand_site_specification($source, 'source-');
+  drush_expand_site_specification($destination, 'target-');
+
+  // Get the options for the source and target databases
+  $source_db_url = _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');
+  $source_rsync_options = array();
+  
+  $target_db_url = _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');
+  $target_rsync_options = array();
+  
+  // Prompt for confirmation. This is destructive.
+  if (!drush_get_context('DRUSH_SIMULATE')) {
+    $txt_source = (isset($source_remote_host) ? $source_remote_host . '/' : '') . $source_db_url['database'];
+    $txt_destination = (isset($target_remote_host) ? $target_remote_host . '/' : '') . $target_db_url['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(), $source_db_url['database'] . '.sql.');
+    $source_is_tmp = TRUE;
+    $source_rsync_options['remove-source-files'] = TRUE;
+  }
+  $target_dump = drush_get_option('target-dump');
+  if (!isset($target_dump)) {
+    $target_dump = tempnam(sys_get_temp_dir(), $target_db_url['database'] . '.sql.');
+    $target_is_tmp = TRUE;
+  }
+  
+  if (isset($source_remote_host) && isset($target_remote_host)) {
+    $local_file = tempnam(sys_get_temp_dir(), $source_db_url['database'] . ($source_db_url['database'] == $target_db_url['database'] ? '' : '-to-' . $target_db_url['database']) . '.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 affects both local and remote sql dumps; it prevents drush sql sync
+  // from calling sql dump when the local cache file is newer than the cache threshhold
+  // No sync affects the remote sql dump; it will prevent drush sql sync from
+  // rsyncing the local sql dump file with the remote sql dump file.
+  $no_sync = drush_get_option(array('no-sync', 'source-no-sync'));
+  $no_dump = drush_get_option(array('no-dump', 'source-no-dump'));
+  $no_cache = drush_get_option('no-cache');
+  if (!isset($no_cache)) {
+    $cache = drush_get_option(array('cache', 'source-cache'));
+    if (!isset($cache)) {
+      $cache = 24; // default cache is 24 hours if nothing else is specified.
+    }
+  }
+  // If the 'cache' option is set, then we will set the no-dump option iff the
+  // target file exists and its modification date is less than "cache" hours.
+  if (isset($cache)) {
+    if (file_exists($local_file) && (filesize($local_file) > 0)) {
+      //drush_print('file ' . $local_file . ' exists; checking modification time for cache.');
+      if ((filemtime($local_file) - time()) < ($cache * 60 * 60)) {
+        drush_log(dt('modification time is less than !cache hours old.', array('!cache' => $cache)));
+        $no_dump = TRUE;
+        $no_sync = TRUE;
+      }
+    }
+  }
+  if (isset($source_remote_host)) {
+    if (isset($source_remote_port)) {
+      $source_db_url['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)) {
+      $source_intermediate = $source_dump;
+      $mv_intermediate = '';
+      // If we are doing a remote dump and the source is not a temporary file,
+      // then first dump to a temporary file and move it to the specified file after 
+      // the dump is complete.  This will reduce contention during simultaneous dumps 
+      // from different users sharing the same dump file.
+      if (!isset($source_is_tmp)) {
+        $source_intermediate = $source_dump . '-' . date("U");
+        $mv_intermediate = '; mv -f ' . $source_intermediate . ' ' . $source_dump;
+      }
+      drush_set_option('result-file', $source_intermediate);
+      $dump_exec = drush_sql_dump($source_db_url) . $mv_intermediate;
+      if (isset($cache) && !isset($source_is_tmp)) {
+        // Inject some bash commands to remotely test the modification date of the target file
+        // if the cache option is set.
+        $dump_exec = 'if [ ! -s ' . $source_dump . '] || [ $((`date "+%s"`-`stat --format="%Y" ' . $source_dump . '`)) -gt ' . ($cache * 60 *  60) . ' ] ; then ' . $dump_exec . '; fi';
+      }
+      $dump_exec = 'ssh ' . $source_remote_user . $source_at . $source_remote_host . ' ' . escapeshellarg($dump_exec);
+    }
+  }
+  else {
+    if (!isset($no_dump)) {
+      drush_set_option('result-file', $local_file);
+      $dump_exec = drush_sql_dump($source_db_url);
+    }
+    $no_sync = TRUE;
+  }
+  
+  // Call sql dump, either on the local machine or remotely via ssh, as appropriate.
+  if (!empty($dump_exec)) {
+    drush_op('system', $dump_exec);
+    // TODO: IF FAILURE THEN ABORT
+  }
+
+  // If the sql dump was remote, then rsync the file over to the local machine. 
+  if (!isset($no_sync)) {
+    // If the source file is a temporary file, then we will have rsync
+    // delete it for us (remove-source-files option set above).
+    drush_core_call_rsync($source_remote_host . ':' . $source_dump, $local_file, $source_remote_user, $source_remote_pass, $source_rsync_options);
+  }
+  
+  // Prior to database import, we will generate a "create database" command
+  // if the '--create-db' option was specified.  Note that typically the
+  // web server user will not have permissions to create a database; to specify
+  // a different user to use with the create db command, the '--db-su' option
+  // may be used.
+  // Under postgres, "alter role username with createdb;" will give create database 
+  // permissions to the specified user if said user was not created with this right.
+  $pre_import_commands = '';
+  $create_db = drush_get_option('create-db');
+  if (isset($create_db)) {
+    $create_db_target = $target_db_url;
+    $create_db_target['database'] = '';
+    $db_superuser = drush_get_option(array('db-su', 'target-db-su'));
+    if (isset($db_superuser)) {
+      $create_db_target['username'] = $db_superuser;
+    }
+    $db_su_connect = _drush_sql_connect($create_db_target);
+    $pre_import_commands = 'echo "create database ' . $target_db_url['database'] . '" | ' . $db_su_connect . '; ';
+  }
+  
+  // Also prior to the database import, if the --fix-paths option was specified
+  // we will generate a series of 'sed' expressions to fix up the filename paths
+  // in the database dump.
+  $fix_paths = drush_get_option('fix-paths');
+  if (isset($fix_paths)) {
+    // Note:  currently we do the fix-up of the target dump file in place.
+    // There is a trade-off involved when doing fix-up on cached remote
+    // sql dumps.  If you do the dump in place, then you will waste time
+    // rsync'ing all of the deltas back into place again, which reduces
+    // the value of the dump file.  If you fix-up to a secondary file, then
+    // the dump requires twice as much free disk space.  When temporary
+    // dump files are in use, then dump-in-place is always the best option.
+    // TO DO:  if sql dump is cached and not temporary, then fix up to
+    // a temporary file instead of fixing up in place.
+    $fixup_commands = '';
+    $source_settings = drush_get_alias_record($source);
+    $destination_settings = drush_get_alias_record($destination);
+    if ((!empty($source_settings)) && (!empty($destination_settings)) && array_key_exists('path-aliases',$source_settings) && array_key_exists('path-aliases', $destination_settings)) {
+      $source_path_aliases = $source_settings['path-aliases'];
+      $destination_path_aliases = $destination_settings['path-aliases'];
+      $source_root = $source_path_aliases['!root'];
+      $destination_root = $destination_path_aliases['!root'];
+      if (isset($source_root) && isset($destination_root)) {
+        foreach ($source_path_aliases as $key => $value) {
+          // we will do the root last so that any other path
+          // alias that might refer to someplace inside of the
+          // root will be changed before we change the root.
+          if ($key != '!root') {
+            $fixup_commands .= '-e "s|' . $value . '|' . $destination_path_aliases[$key] . '|g" ';
+          }
+        }
+        if (isset($source_settings['uri']) && isset($destination_settings['uri']) && ($source_settings['uri'] != $destination_settings['uri'])) {
+          $fixup_commands .= '-e "s|sites/' . $source_settings['uri'] . '|' . 'sites/'. $destination_settings['uri'] . '|g" ';
+        }
+        // TODO:  Do any of these references to the Drupal root actually need to be fixed up?
+        $fixup_commands .= '-e "s|' . $source_root . '|' . $destination_root . '|g" ';
+      }
+    }
+    if (!empty($fixup_commands)) {
+      $pre_import_commands .= 'sed -i ' . $fixup_commands . $target_dump . '; ';
+    }
+  }
+  
+  // 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_db_url['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') : '';
+    }
+    
+    drush_core_call_rsync($local_file, $target_remote_host . ':' . $target_dump, $target_remote_user, $target_remote_pass);
+    
+    $connect_exec = $pre_import_commands . _drush_sql_connect($target_db_url) . ' < ' . $target_dump;
+    $import_exec = 'ssh ' . $target_remote_user . $target_at . $target_remote_host . ' ' . escapeshellarg($connect_exec);
+    // delete the remote target file if it is a temporary file
+    if ($target_is_tmp) {
+      $import_exec .= '; rm -f ' . escapeshellarg($target_dump);
+    }
+  }
+  else {
+    $import_exec = $pre_import_commands . _drush_sql_connect($target_db_url) . ' < ' . $local_file;
+  }
+  
+  drush_op('system', $import_exec);
+  // delete local temporary files
+  if (($source_is_tmp) && (!isset($source_remote_host))) {
+    //drush_op('system', 'rm -f ' . escapeshellarg($source_dump));
+    drush_op('unlink', $source_dump);
+  }
+  if (($target_is_tmp) && (!isset($target_remote_host))) {
+    //drush_op('system', 'rm -f ' . escapeshellarg($target_dump));
+    drush_op('unlink', $target_dump);
+  }
+}
+
 /**
  * Copy an entire database to another database. For example, migrate from production to dev
  * or dev to staging.
@@ -267,8 +577,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')) {
@@ -296,7 +608,7 @@ function drush_sql_load($source, $destin
       $send = 'mysql' . _drush_sql_get_credentials($destination);
       break;
     case 'pgsql':
-      $send .= 'psql -d ' . _drush_sql_get_credentials($destination) . ' --file -';
+      $send .= 'psql' . _drush_sql_get_credentials($destination) . ' --file -';
       break;
     }
     
@@ -314,7 +626,7 @@ function drush_sql_cli() {
       $command .= _drush_sql_get_credentials();
       break;
     case 'pgsql':
-      $command = ' psql -d ';
+      $command = ' psql';
       $command .= _drush_sql_get_credentials();
       break;
   }
@@ -367,6 +679,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 +708,20 @@ function _drush_sql_get_spec_from_settin
   return _drush_sql_get_db_spec();
 }
 
+function _drush_sql_get_spec_from_options($prefix) {
+  /*$alias = drush_get_option($prefix . 'uri');
+  if (isset($alias)) {
+    return _drush_sql_get_spec_from_site_alias($alias, $prefix);
+  }*/
+  //else {
+    global $db_url;
+  
+    $db_url = drush_get_option($prefix . 'db-url');
+    drush_log(dt("get spec from !dburl", array('!dburl' => $db_url)));
+    return _drush_sql_get_db_spec($db_url);
+  //}
+}
+
 function _drush_sql_get_scheme($db_spec = NULL) {
   if (is_null($db_spec)) {
     $db_spec = _drush_sql_get_db_spec();
@@ -398,31 +736,27 @@ function _drush_sql_get_scheme($db_spec 
  * @return string
  */
 function _drush_sql_get_credentials($db_spec = NULL) {
+  if (is_null($db_spec)) {
+    $db_spec = _drush_sql_get_db_spec();
+  }
+  
   switch (_drush_sql_get_scheme()) {
     case 'mysql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
       $cred = ' -h' . $db_spec['host'] .
          (empty($db_spec['port']) ? '' : ' -P' . $db_spec['port']) .
          ' -u' . $db_spec['username'] .
          (empty($db_spec['password']) ? '' : ' -p' . $db_spec['password']) . ' ' . $db_spec['database'];
       break;
   case 'pgsql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
-      $cred = $db_spec['database'] . ' ' .
-         (empty($db_spec['host']) || ($db_spec['host'] == "localhost") ? '' : '-h ' . $db_spec['host']) . ' ' .
-         (empty($db_spec['port']) ? '' : '-p' . $db_spec['port']) . ' ';
+      $cred = (isset($db_spec['database']) ? ' -d ' . (empty($db_spec['database']) ? 'template1' :  $db_spec['database']) : '') .
+         (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	31 Jul 2009 00:13:21 -0000
@@ -157,7 +157,7 @@ function _drush_backend_integrate($data)
  *   If it executed succesfully, it returns an associative array containing the command
  *   called, the output of the command, and the error code of the command.
  */
-function _drush_proc_open($cmd, $data = NULL) {
+function _drush_proc_open($cmd, $data = NULL, $context = NULL) {
   $descriptorspec = array(
      0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
      1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
@@ -173,7 +173,7 @@ function _drush_proc_open($cmd, $data = 
     $info = stream_get_meta_data($pipes[1]);
     stream_set_blocking($pipes[1], TRUE);
     stream_set_timeout($pipes[1], 1);
-    $stream = '';
+    $string = '';
     while (!feof($pipes[1]) && !$info['timed_out']) { 
       $string .= fgets($pipes[1], 4096);
       $info = stream_get_meta_data($pipes[1]);
@@ -184,7 +184,6 @@ function _drush_proc_open($cmd, $data = 
     $info = stream_get_meta_data($pipes[2]);
     stream_set_blocking($pipes[2], TRUE);
     stream_set_timeout($pipes[2], 1);
-    $stream = '';
     while (!feof($pipes[2]) && !$info['timed_out']) { 
       $string .= fgets($pipes[2], 4096);
       $info = stream_get_meta_data($pipes[2]);
@@ -233,7 +232,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 +285,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 +315,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 +352,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	31 Jul 2009 00:13:21 -0000
@@ -147,7 +147,18 @@ 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])) {
+    drush_log(dt('Apply settings for site alias !alias', array('!alias' => $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,8 +167,10 @@ 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];
+      drush_log(dt("found command: !description", array('!description' => $command['description'])));
     }
     else {
       $arguments[] = array_pop($args);
@@ -167,7 +180,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);
@@ -420,6 +441,7 @@ function _drush_find_commandfiles($phase
     // Scan for drush command files, load if found
     foreach (array_unique($searchpath) as $path) {
       if (is_dir($path)) {
+        // drush_log(dt('Searching for commands in !path', array('!path' => $path)));
         $files = drush_scan_directory($path, '\.drush\.inc$');
         foreach ($files as $filename => $info) {
           require_once($filename);
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	31 Jul 2009 00:13:21 -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.38
diff -u -p -r1.38 environment.inc
--- includes/environment.inc	7 Jul 2009 23:34:53 -0000	1.38
+++ includes/environment.inc	31 Jul 2009 00:13:21 -0000
@@ -399,6 +399,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) {
@@ -433,6 +436,154 @@ function _drush_bootstrap_drush() {
   _drush_find_commandfiles(DRUSH_BOOTSTRAP_DRUSH);
 }
 
+function _drush_process_site_alias() {
+  $args = drush_get_arguments();
+  
+  // Test to see if the first arg is a site specification
+  if (drush_expand_site_specification($args[0])) {
+    drush_log(dt('Apply settings for site alias !alias', array('!alias' => $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);
+    return TRUE;
+  }
+
+  return drush_expand_site_specification(drush_get_option(array('l', 'uri')));
+}
+
+/**
+ * Given an alias:
+ *
+ * If it is the name of a site alias, return the alias record from
+ * the site aliases array.
+ *
+ * If it is the name of a folder in the 'sites' folder, construct
+ * an alias record from values stored in settings.php.
+ *
+ * If it is a site specification, construct an alias record from the
+ * values in the specification.
+ * 
+ * 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'] set in some drushrc file on server)
+ * 5.) #sitename                             (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)      
+ * 6.) sitename                              (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)      
+ *
+ * Note that in the case of the first four forms, it is also possible
+ * to add additional site variable to the specification using uri query
+ * syntax.  For example:
+ *
+ *      user@server/path/to/drupal?db-url=...#sitename
+ */
+function drush_get_alias_record($alias) {
+  $alias_record = array();
+  
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $alias_record = $all_site_aliases[$alias];
+  }
+  else {
+    if (isset($alias)) {
+      // 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) {
+        if ((substr($alias,0,7) != 'http://') && (substr($alias,0,1) != '/')) {
+          // Add on a scheme so that "user:pass@server" will always parse correctly
+          $parsed = parse_url('http://' . $alias);
+        }
+        else {
+          $parsed = parse_url($alias);
+        }
+        foreach (array('user' => 'remote-user', 'pass' => 'remote-pass', 'host' => 'remote-host', 'fragment' => 'uri', 'path' => '!root') as $url_key => $option_key) {
+          if (array_key_exists($url_key, $parsed)) {
+           drush_set_alias_record_element($alias_record, $option_key, $parsed[$url_key]);
+          }
+        }
+        foreach (explode('&', $parsed['query']) as $query_arg) {
+          $query_components = explode('=', $query_arg);
+          drush_set_alias_record_element($alias_record, urldecode($query_components[0]), urldecode($query_components[1]));
+        }
+
+        // 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)) {
+          $alias_record['uri'] = $parsed['host'];
+        }
+      }
+      else {
+        // Case 5.) and 6.):
+        // 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 (substr($alias,0,1) == '#') {
+          $alias = substr($alias,1);
+        }
+        $site_settings_file = $drupal_root . '/sites/' . $alias . '/settings.php';
+        if (isset($drupal_root) && file_exists($site_settings_file)) {
+          $alias_record['uri'] = $alias;
+          global $databases, $db_url;
+          require $site_settings_file;
+          $alias_record['db-url'] = $db_url;
+          $alias_record['path-aliases'] = array(
+            '!root' => $drupal_root,
+            /*'!files' => '/path/to/file/storage', */
+            /*'!dump' => '/path/to/sql/dumpfile', */ );
+        }
+      }
+    }
+
+  }
+  return $alias_record;
+}
+
+function drush_set_alias_record_element(&$alias_record, $key, $value) {
+  if (substr($key,0,1) == '!') {
+    $alias_record['path-aliases'][substr($key,1)] = $value;
+  }
+  elseif (!empty($key)) {
+    $alias_record[$key] = $value;
+  }
+}
+
+function drush_expand_site_specification($alias, $prefix = '') {
+  drush_log("set variables in !prefix", array('!prefix' => $prefix));
+  $site_alias_settings =  drush_get_alias_record($alias);
+  if (!empty($site_alias_settings)) {     
+    // 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');
+        drush_log(dt("set !key to !value", array('!key' => $prefix . $key, '!value' => $value)));
+      }
+    }
+    // Transfer selected path aliases to the drush options.
+    if (array_key_exists('path-aliases', $site_alias_settings)) {
+      foreach (array('!root', '!drush', '!dump', '!include') 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');
+          drush_log(dt("set !key to !value", array('!key' => $prefix . substr($key, 1), '!value' => $site_alias_settings['path-aliases'][$key])));
+        }
+      }
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
 /**
  * Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
  *
