--- includes/update.inc	Sun Oct 11 11:34:10 2009 -0700
+++ includes/update.inc	Sun Oct 11 12:47:28 2009 -0700
@@ -22,10 +22,10 @@ function update_fix_compatibility() {
     }
   }
   if (!empty($incompatible)) {
-  	db_update('system')
-  	  ->fields(array('status' => 0))
-  	  ->condition('name', $incompatible, 'IN')
-  	  ->execute();
+    db_update('system')
+      ->fields(array('status' => 0))
+      ->condition('name', $incompatible, 'IN')
+      ->execute();
   }
 }
 
@@ -350,7 +350,107 @@ 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.
+ * 
+ * TODO: validate the update process before calling this update_batch.
+ * What we have done so far is adding the doxygen dependants to the graph for 
+ * ordening purpose only. They are removed from the actual list because there
+ * were not requested through the update.php form.
+ * 
+ * @param $start
+ * @return Sorted Topological Sorted List of module/schema versions
+ */
+function _update_calculate_graph($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();
+
+      // @requires : forward dependency.
+      if (preg_match_all('/^\s*\*\s+@requires\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];
+        }
+      }
+
+      // @block : reverse dependency
+      if (preg_match_all('/^\s*\*\s+@blocks\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_graph() which returns
+ * an ordered list of versions.
+ * 
+ * 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.
@@ -374,19 +474,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_graph($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',
--- modules/comment/comment.install	Sun Oct 11 11:34:10 2009 -0700
+++ modules/comment/comment.install	Sun Oct 11 12:47:28 2009 -0700
@@ -114,6 +114,8 @@ function comment_update_7004() {
 
 /**
  * Create comment Field API bundles.
+ *
+ * @requires system_update_7038
  */
 function comment_update_7005() {
   foreach (node_type_get_types() as $info) {
--- modules/simpletest/simpletest.info	Sun Oct 11 11:34:10 2009 -0700
+++ modules/simpletest/simpletest.info	Sun Oct 11 12:47:28 2009 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update.test	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,75 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Tests for update.inc.
+ */
+
+/**
+ * Unit tests for the module API.
+ */
+class UpdateUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Update',
+      'description' => 'Test low-level update functions.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+  	// Provoke the inclusion of graph.inc.
+    require_once 'includes/graph.inc';
+    require_once 'includes/update.inc';
+    // Note: we do not install update_test_3 but it is @blocks by update_test_2
+    parent::setUp('update_test_1', 'update_test_2');
+  }
+
+  function testUpdateCalculateGraph() {
+  	$start=array(
+  	  'update_test_1' => 7001,
+  	);
+    $expected= array(
+      'update_test_1_update_7001',
+      'update_test_1_update_7002',
+    );
+  	$tsl = _update_calculate_graph($start);
+  	$result = array_keys($tsl);
+  	$this->assertEqual($expected, $result, 'Update graph module 1 from 7001 to 7002: ' . join( ' &gt; ',$result));
+  	
+    $start=array(
+      'update_test_1' => 7000,
+    );
+    $expected= array(
+      'update_test_1_update_7001',
+      'update_test_1_update_7002',
+    );
+    $tsl = _update_calculate_graph($start);
+    $result = array_keys($tsl);
+    //TODO : Is it ok to run from non-existing?
+    $this->assertEqual($expected, $result, 'Update graph from non existing schema version 7000: ' . join( ' &gt; ',$result));
+  }
+
+  function testUpdateModuleInterdependency() {
+    $start=array(
+      'update_test_2' => 7001,
+      'update_test_1' => 7001,
+    );
+    $expected= array(
+      'update_test_2_update_7001',
+      'update_test_2_update_7002',
+      'update_test_1_update_7001',
+      'update_test_1_update_7002',
+      'update_test_2_update_7003',
+      'update_test_2_update_7004',
+    );
+    
+    $tsl = _update_calculate_graph($start);
+    $result = array_keys($tsl);
+    
+    $this->assertEqual($expected, $result, 'Update graph with blocks and requires: ' . join( ' &gt; ',$result));
+  	
+  }
+  
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_1.info	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,8 @@
+; $Id$
+name = "Update test"
+description = "Support module for update testing."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = update_test_1.module
+hidden = TRUE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_1.install	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,42 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_test_1 module.
+ */
+
+function update_test_1_update_7001() {
+  return array();
+}
+
+function update_test_1_update_7002() {
+  return array();
+}
+
+/**
+ * Implement hook_schema().
+ */
+function update_test_1_schema() {
+  $schema=array();
+  $schema['update_test_1'] = array(
+    'description' => 'Dummy schema update_test_1.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('tid'),
+  );
+
+  return $schema;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_1.module	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,2 @@
+<?php
+// $Id$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_2.info	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,8 @@
+; $Id$
+name = "Update test"
+description = "Support module for update testing."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = update_test_2.module
+hidden = TRUE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_2.install	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,57 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_test_1 module.
+ */
+
+function update_test_2_update_7001() {
+  return array();
+}
+
+/**
+ * @blocks update_test_1_update_7001
+ */
+function update_test_2_update_7002() {
+  return array();
+}
+
+function update_test_2_update_7003() {
+  return array();
+}
+
+/**
+ * @requires update_test_1_update_7002
+ * @blocks update_test_3_update_7002
+ */
+function update_test_2_update_7004() {
+  return array();
+}
+
+/**
+ * Implement hook_schema().
+ */
+function update_test_2_schema() {
+  $schema=array();
+  $schema['update_test_2'] = array(
+    'description' => 'Dummy schema update_test_2.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('tid'),
+  );
+
+  return $schema;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_2.module	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,2 @@
+<?php
+// $Id$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_3.info	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,8 @@
+; $Id$
+name = "Update test"
+description = "Support module for update testing."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = update_test_3.module
+hidden = TRUE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_3.install	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,50 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the update_test_1 module.
+ */
+
+function update_test_1_update_7001() {
+  return array();
+}
+
+function update_test_1_update_7002() {
+  return array();
+}
+
+function update_test_1_update_7003() {
+  return array();
+}
+
+function update_test_1_update_7004() {
+  return array();
+}
+
+/**
+ * Implement hook_schema().
+ */
+function update_test_1_schema() {
+  $schema=array();
+  $schema['update_test_1'] = array(
+    'description' => 'Dummy schema update_test_1.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('tid'),
+  );
+
+  return $schema;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ modules/simpletest/tests/update_test_3.module	Sun Oct 11 12:47:28 2009 -0700
@@ -0,0 +1,2 @@
+<?php
+// $Id$
