? includes/table.inc
Index: commands/pm/pm.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/pm.drush.inc,v
retrieving revision 1.118
diff -u -p -r1.118 pm.drush.inc
--- commands/pm/pm.drush.inc	1 Aug 2010 16:18:13 -0000	1.118
+++ commands/pm/pm.drush.inc	4 Aug 2010 05:47:05 -0000
@@ -95,7 +95,13 @@ function pm_drush_command() {
       'package_handler' => 'Determine how to download/checkout new projects and acquire updates to projects.',
     ),
   );
-
+  $update_options = array(
+    '--lock' => 'Add a persistent lock to remove the specified projects from consideration during updates.  Locks may be removed with the --unlock parameter, or overridden by specifically naming the module as a parameter to pm-update or pm-updatecode.  The lock does not affect pm-download.',
+    '--lock-message' => 'A brief message explaining why a project is being locked; displayed during pm-updatecode.  Optional.',
+    '--unlock' => 'Remove the persistent lock from the specified projects so that they may be updated again.',
+    '--backup-dir' => 'Specify a directory to backup packages into, defaults to a backup directory within your Drupal root.',
+  );
+  
   $items['pm-enable'] = array(
     'description' => 'Enable one or more modules or themes.',
     'arguments' => array(
@@ -155,9 +161,8 @@ function pm_drush_command() {
       'projects' => 'Optional. A space delimited list of installed projects (modules or themes) to update.',
     ),
     'options' => array(
-      '--backup-dir' => 'Specify a directory to backup packages into, defaults to a backup directory within your Drupal root.',
       '--pipe' => 'Returns a space delimited list of enabled modules and their respective version and update information, one module per line. Order: module name, current version, recommended version, update status.',
-    ),
+    ) + $update_options,
     'aliases' => array('upc'),
     'deprecated-aliases' => array('updatecode'),
   ) + $engines;
@@ -168,9 +173,8 @@ function pm_drush_command() {
       'projects' => 'Optional. A space delimited list of installed projects (modules or themes) to update.',
     ),
     'options' => array(
-      '--backup-dir' => 'Specify a directory to backup packages into, defaults to a backup directory within your Drupal root.',
       '--php' => 'Specify absolute path to you php binary. Use this if you see errors.'
-    ),
+    ) + $update_options,
     'aliases' => array('up'),
     'deprecated-aliases' => array('update'),
   );
@@ -1457,3 +1461,75 @@ function pm_drush_pm_adjust_download_des
     }
   }
 }
+
+/**
+ * Update the locked status of all of the candidate projects
+ * to be updated.
+ *
+ * @param array &$projects
+ *   The projects array from pm_updatecode.  $project['locked'] will
+ *   be set for every file where a persistent lockfile can be found.
+ *   The 'lock' and 'unlock' operations are processed first.
+ * @param array $projects_to_lock
+ *   A list of projects to create peristent lock files for
+ * @param array $projects_to_unlock
+ *   A list of projects to clear the persistent lock on
+ * @param string $lock_message
+ *   The reason the project is being locked; stored in the lockfile.
+ *
+ * @return array
+ *   A list of projects that are locked.
+ */
+function drush_pm_update_lock(&$projects, $projects_to_lock, $projects_to_unlock, $lock_message = NULL) {
+  $locked_result = array();
+  
+  // --lock with no values is a no-op
+  if ($projects_to_lock == array('1')) {
+    $projects_to_lock = array();
+  }
+  // --unlock with no values is --unlock=all
+  if ($projects_to_unlock == array('1')) {
+    $projects_to_unlock = array('all');
+  }
+  
+  // Log if we are going to lock or unlock anything
+  if (!empty($projects_to_unlock)) {
+    drush_log(dt('Unlocking !projects', array('!projects' => implode(',', $projects_to_unlock))), 'ok');
+  }
+  if (!empty($projects_to_lock)) {
+    drush_log(dt('Locking !projects', array('!projects' => implode(',', $projects_to_lock))), 'ok');
+  }
+  
+  $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
+  foreach ($projects as $name => $project) {
+    $message = NULL;
+    $lockfile = $drupal_root . '/' . $project['path'] . '/.drush-lock-update';
+    
+    // Remove the lock file if the --unlock option was specified
+    if (((in_array($name, $projects_to_unlock)) || (in_array('all', $projects_to_unlock))) && (file_exists($lockfile))) {
+      drush_op('unlink', $lockfile);
+    }
+    
+    // Create the lock file if the --lock option was specified
+    if ((in_array($name, $projects_to_lock)) || (in_array('all', $projects_to_lock))) {
+      drush_op('file_put_contents', $lockfile, $lock_message != NULL ? $lock_message : "Locked via drush.");
+      // Note that the project is locked.  This will work even if we are simulated,
+      // or if we get permission denied from the file_put_contents.
+      $message = drush_get_context('DRUSH_SIMULATE') ? 'Simulated lock.' : $lock_message;
+    }
+    
+    // If the persistent lock file exists, then mark the project as locked.
+    if (file_exists($lockfile)) {
+      $message = trim(file_get_contents($lockfile));
+    }
+    
+    // If there is a message set, then mark the project as locked.
+    if (isset($message)) {
+      $projects[$name]['locked'] = !empty($message) ? $message : "Locked.";
+      $locked_result[$name] = $project;
+    }
+  }
+  
+  return $locked_result;
+}
+
Index: commands/pm/updatecode.pm.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/updatecode.pm.inc,v
retrieving revision 1.11
diff -u -p -r1.11 updatecode.pm.inc
--- commands/pm/updatecode.pm.inc	2 Aug 2010 14:13:17 -0000	1.11
+++ commands/pm/updatecode.pm.inc	4 Aug 2010 05:47:05 -0000
@@ -14,13 +14,16 @@ function drush_pm_updatecode() {
 
   // Get update status information.
   $projects = _pm_get_update_info();
+  
+  // Process locks specified on the command line
+  $locked_list = drush_pm_update_lock($projects, drush_get_option_list('lock'), drush_get_option_list('unlock'), drush_get_option('lock-message'));
 
   // Get specific requests
   $requests = func_get_args();
 
   // Parse out project name and version
   $requests = pm_parse_project_version($requests);
-
+  
   // Preprocess releases
   if (!empty($requests)) {
     // Force update projects where a specific version is reqested
@@ -87,7 +90,22 @@ function drush_pm_updatecode() {
       }
     }
   }
+  
+  // If there are any locked projects that were not requested, then remove them
+  if (!empty($locked_list)) {
+    foreach ($updateable as $name => $project) {
+      if ((isset($locked_list[$name])) && (!isset($requests[$name]))) {
+        unset($updateable[$name]);
+      }
+    }
+  }
 
+  // Do no updates in simulated mode
+  if (drush_get_context('DRUSH_SIMULATE')) {
+    return drush_log(dt('No action taken in simulated mode.'), 'ok');
+    return TRUE;
+  }
+  
   if (isset($updateable['drupal'])) {
     $drupal_project = $updateable['drupal'];
     unset($projects['drupal']);
@@ -349,6 +367,10 @@ function pm_project_filter(&$projects, &
         $status = pm_update_filter($project);
         break;
     }
+    
+    if (isset($project['locked'])) {
+      $status = $project['locked'] . " ($status)";
+    }
 
     // Persist candidate_version in $projects (plural).
     if (empty($project['candidate_version'])) {
Index: includes/context.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/context.inc,v
retrieving revision 1.31
diff -u -p -r1.31 context.inc
--- includes/context.inc	27 May 2010 13:36:41 -0000	1.31
+++ includes/context.inc	4 Aug 2010 05:47:06 -0000
@@ -387,6 +387,30 @@ function drush_get_option($option, $defa
 }
 
 /**
+ * Get the value for an option and return it as a list.  If the 
+ * option in question is passed on the command line, its value should
+ * be a comma-separated list (e.g. --flag=1,2,3).  If the option
+ * was set in a drushrc.php file, then its value may be either a
+ * comma-separated list or an array of values (e.g. $option['flag'] = array('1', '2', '3')).
+ *
+ * @param option
+ *   The name of the option to get
+ * @param default
+ *   Optional. The value to return if the option has not been set
+ * @param context
+ *   Optional. The context to check for the option. If this is set, only this context will be searched.
+ */
+function drush_get_option_list($option, $default = array(), $context = NULL) {
+  $result = drush_get_option($option, $default, $context);
+  
+  if (!is_array($result)) {
+    $result = explode(',', $result);
+  }
+  
+  return $result;
+}
+
+/**
  * Get the value for an option, but first checks the provided option overrides.
  *
  * The feature of drush_get_option that allows a list of option names
