=== modified file 'includes/update.inc'
--- includes/update.inc	2009-11-09 02:00:03 +0000
+++ includes/update.inc	2009-11-09 23:09:52 +0000
@@ -508,7 +508,108 @@ function update_do_one($module, $number,
 class DrupalUpdateException extends Exception { }
 
 /**
+ * Calculate an ordered update scheme
+ * 
+ * We build the graph of requested schema version start points
+ * to the final available versions.
+ * 
+ * In the process we add @required and @blocked schema versions of other modules
+ * to the graph.
+ * 
+ * In the end we sort this to a Topological Sorted List of which we remove all
+ * but the modules named in $start.
+ * 
+ * @param $start
+ * @return Sorted Topological Sorted List of module/schema versions
+ */
+function  _update_calculate_plan($start) {
+  // Build the graph of the updates.
+  $updates_graph = array();
+
+  // Set the installed version so updates start at the correct place.
+  foreach ($start as $module => $version) {
+    //TODO: why set schema -1 ?
+    drupal_set_installed_schema_version($module, $version - 1);
+    $updates = drupal_get_schema_versions($module);
+    //TODO: why test for maximum version?
+    $max_version = max($updates);
+    if ($version > $max_version) {
+      continue;
+    }
+
+    while ($update = array_shift($updates)) {
+      // We cannot choose a version anymore on update.php right?
+      if ($update < $version) {
+        continue;
+      }
+
+      $updates_graph[$module . '_update_' . $update]['module'] = $module;
+      $updates_graph[$module . '_update_' . $update]['update'] = $update;
+
+      // Update N+1 depends on update N of the same module.
+      if ($next_update = reset($updates)) {
+        $updates_graph[$module . '_update_' . $update]['edges'][$module . '_update_' . $next_update] = TRUE;
+      }
+
+      // Fetch dependencies from the update Doxygen.
+      $func = new ReflectionFunction($module . '_update_' . $update);
+      $func_doxygen = $func->getDocComment();
+
+      // @after : forward dependency.
+      if (preg_match_all('/^\s*\*\s+@after\s+(.*)$/m', $func_doxygen, $matches)) {
+        foreach ($matches[1] as $function) {
+          $updates_graph[$function]['edges'][$module . '_update_' . $update] = TRUE;
+          // Add $function as participant to the graph
+          // TODO : sloppy code
+          $split=split('_update_', $function);
+          $updates_graph[$function]['module'] = $split[0];
+          $updates_graph[$function]['update'] = $split[1];
+        }
+      }
+
+      // @before : reverse dependency
+      if (preg_match_all('/^\s*\*\s+@before\s+(.*)$/m', $func_doxygen, $matches)) {
+        foreach ($matches[1] as $function) {
+          $updates_graph[$module . '_update_' . $update]['edges'][$function] = TRUE;
+          // Add $function as participant to the graph
+          // TODO : sloppy code
+          $split=split('_update_', $function);
+          $updates_graph[$function]['module'] = $split[0];
+          $updates_graph[$function]['update'] = $split[1];
+        }
+      }
+    }
+  }
+  
+
+  // Determine the update order based on a topological sort of the update graph.
+  drupal_depth_first_search($updates_graph);
+  uasort($updates_graph, 'drupal_sort_weight');
+
+  // Remove non requested modules
+  $result=array();
+  foreach ($updates_graph as $function => $graph_node) {
+    if (array_key_exists($graph_node['module'], $start)) {
+      $result[$function] = $graph_node;
+    }
+  }
+    
+  return $result;
+}
+
+/**
  * Start the database update batch process.
+ * 
+ * This batch uses  _update_calculate_plan() which returns
+ * an ordered list of versions.
+ * 
+ * TODO: validate the update process before calling this update_batch.
+ * What we have done so far is adding the doxygen dependents to the graph for
+ * ordering purpose only. They are removed from the actual list because there
+ * were not requested through the update.php form.
+ * 
+ * TODO: But there is no test for not being able to install the requested
+ * dependencies in full order. This should be done on the update.php form(s) 
  *
  * @param $start
  *   An array of all the modules and which update to start at.
@@ -532,19 +633,13 @@ function update_batch($start, $redirect 
   }
 
   $operations = array();
-  // Set the installed version so updates start at the correct place.
-  foreach ($start as $module => $version) {
-    drupal_set_installed_schema_version($module, $version - 1);
-    $updates = drupal_get_schema_versions($module);
-    $max_version = max($updates);
-    if ($version <= $max_version) {
-      foreach ($updates as $update) {
-        if ($update >= $version) {
-          $operations[] = array('update_do_one', array($module, $update));
-        }
-      }
-    }
+  
+  $updates_graph =  _update_calculate_plan($start);
+
+  foreach ($updates_graph as $function => $graph_node) {
+    $operations[] = array('update_do_one', array($graph_node['module'], $graph_node['update']));
   }
+
   $batch['operations'] = $operations;
   $batch += array(
     'title' => 'Updating',
@@ -650,3 +745,32 @@ function update_get_update_list() {
   return $ret;
 }
 
+/**
+ * Check whether or not the dependencies for a given update as met.
+ *
+ * @param $update
+ *   An individual update as returned from _update_calculate_plan().
+ *
+ * @return
+ *   TRUE if the dependencies are met.
+ */
+function update_check_dependencies($update) {
+  $ret = TRUE;
+  if (isset($update['reverse_paths'])) {
+    foreach ($update['reverse_paths'] as $function => $value) {
+      $split = explode('_update_', $function);
+      $module = $split[0];
+      $version = $split[1];
+      if (function_exists($function) || (drupal_get_installed_schema_version($module) > $version)) {
+        $dependency = _update_calculate_plan(array($module => $version));
+        if (!update_check_dependencies($dependency[$module . '_update_' . $version])) {
+          $ret = FALSE;
+        } 
+      } else {
+        $ret = FALSE;
+      }
+    }
+  }
+
+  return ($ret) ? TRUE : FALSE;
+}

=== modified file 'modules/comment/comment.install'
--- modules/comment/comment.install	2009-10-16 14:00:04 +0000
+++ modules/comment/comment.install	2009-11-09 23:09:52 +0000
@@ -114,6 +114,8 @@ function comment_update_7004() {
 
 /**
  * Create comment Field API bundles.
+ *
+ * @after system_update_7038
  */
 function comment_update_7005() {
   foreach (node_type_get_types() as $info) {

=== modified file 'modules/simpletest/simpletest.info'
--- modules/simpletest/simpletest.info	2009-11-07 15:00:03 +0000
+++ modules/simpletest/simpletest.info	2009-11-09 23:07:18 +0000
@@ -34,4 +34,5 @@ files[] = tests/schema.test
 files[] = tests/session.test
 files[] = tests/theme.test
 files[] = tests/unicode.test
+files[] = tests/update.test
 files[] = tests/xmlrpc.test

=== modified file 'update.php'
--- update.php	2009-11-04 06:00:03 +0000
+++ update.php	2009-11-09 23:09:52 +0000
@@ -54,7 +54,7 @@ function update_script_selection_form($f
     if (!isset($update['start'])) {
       $form['start'][$module] = array(
         '#title' => $module,
-        '#item'  => $update['warning'],
+        '#markup'  => $update['warning'],
         '#prefix' => '<div class="warning">',
         '#suffix' => '</div>',
       );
@@ -93,7 +93,7 @@ function update_script_selection_form($f
     );
     $form['submit'] = array(
       '#type' => 'submit',
-      '#value' => 'Apply pending updates',
+      '#value' => 'Submit',
     );
   }
   return $form;
@@ -284,6 +284,108 @@ function update_check_requirements() {
   }
 }
 
+/**
+ * Validate the selected updates.
+ */
+function update_validate_upgrade_plan() {
+  global $base_url;
+
+  drupal_set_title('Drupal database update');
+  update_task_list('select');
+  $update_plan = _update_calculate_plan($_POST['start']);
+  $errors = array();
+
+  foreach ($update_plan as $index => $update) {
+    if (!update_check_dependencies($update)) {
+      $errors[] = $update;
+      unset($_POST[$update['module']]);
+    }
+  }
+
+  if (empty($errors)) {
+    update_batch($_POST['start'], $base_url . '/update.php?op=results', $base_url . '/update.php');
+  } else {
+    return drupal_render(drupal_get_form('update_script_validate_form', $errors));
+  }
+}
+
+function update_script_validate_form($form, &$form_state, $errors) {
+  $form = array();
+  $count = 0;
+  $form['error'] = array(
+    '#tree' => TRUE,
+    '#type' => 'fieldset',
+    '#collapsed' => FALSE,
+    '#collapsible' => TRUE,
+    '#title' => 'Incompatible updates',
+  );
+  $form['start'] = array(
+    '#tree' => TRUE,
+    '#type' => 'fieldset',
+    '#collapsed' => TRUE,
+    '#collapsible' => TRUE,
+  );
+  
+  // Ensure system.module's updates appear first
+  $form['start']['system'] = array();
+
+  $updates = update_get_update_list();
+  foreach ($errors as $error) {
+    $updates[$error['module']] = array('warning' => 'The <em>' . $error['component'] . '</em> update can not be run. The following dependencies could not be met: <em>' . implode(',', array_keys($error['reverse_paths'])) . '</em>.',
+    );
+  }
+  foreach ($updates as $module => $update) {
+    if (!isset($update['start'])) {
+      $form['error'][$module] = array(
+        '#title' => $module,
+        '#markup'  => $update['warning'],
+        '#prefix' => '<div class="warning">',
+        '#suffix' => '</div>',
+      );
+      continue;
+    }
+    if (!empty($update['pending'])) {
+      $form['start'][$module] = array(
+        '#type' => 'hidden',
+        '#value' => $update['start'],
+      );
+      $form['start'][$module . '_updates'] = array(
+        '#markup' => theme('item_list', array('items' => $update['pending'], 'title' => $module . ' module')),
+      );
+    }
+    if (isset($update['pending'])) {
+      $count = $count + count($update['pending']);
+    }
+  }
+
+  
+  $form['help'] = array(
+    '#markup' => '<p>Some of the selected updates could not be applied because their dependencies were not met.</p>',
+    '#weight' => -5,
+  );
+  $form['start']['#title'] = format_plural($count, '1 pending update', '@count pending updates');
+  $form['has_js'] = array(
+    '#type' => 'hidden',
+    '#default_value' => FALSE,
+  );
+  if ($count > 0) {
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => 'Apply remaining updates',
+    );
+  }
+  else {
+    $links = update_helpful_links();
+    $form['start']['links'] = array(
+      '#markup' => theme('item_list', array('items' => $links)),
+      '#weight' => 5,
+    );
+    $form['start']['#collapsed'] = FALSE;
+  }
+  
+  return $form;
+}
+
 // Some unavoidable errors happen because the database is not yet up-to-date.
 // Our custom error handler is not yet installed, so we just suppress them.
 ini_set('display_errors', FALSE);
@@ -369,7 +471,13 @@ if (update_access_allowed()) {
         break;
       }
 
-    case 'Apply pending updates':
+    case 'Submit':
+      if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) {
+        $output = update_validate_upgrade_plan();
+        break;
+      }
+
+    case 'Apply remaining updates':
       if (isset($_GET['token']) && $_GET['token'] == drupal_get_token('update')) {
         update_batch($_POST['start'], $base_url . '/update.php?op=results', $base_url . '/update.php');
         break;

