diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
index 07d6a05..57f735a 100644
--- a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
@@ -21,6 +21,16 @@ class MigrateUpgradeForm extends ConfirmFormBase {
   use MigrationConfigurationTrait;
 
   /**
+   * If a migration has previously run, perform an incremental migration.
+   */
+  const MIGRATE_UPGRADE_INCREMENTAL = 1;
+
+  /**
+   * If a migration has previously run, roll it back and start fresh.
+   */
+  const MIGRATE_UPGRADE_ROLLBACK = 2;
+
+  /**
    * Mapping of known migrations and their source and destination modules.
    *
    * @todo https://www.drupal.org/node/2569805 Hardcoding this information is
@@ -711,10 +721,19 @@ public function buildOverviewForm(array $form, FormStateInterface $form_state) {
       //   https://www.drupal.org/node/2687849
       $form['upgrade_option_item'] = [
         '#type' => 'item',
-        '#prefix' => $this->t('An upgrade has already been performed on this site. To perform a new migration, create a clean and empty new install of Drupal 8. Rollbacks and incremental migrations are not yet supported through the user interface. For more information, see the <a href=":url">upgrading handbook</a>.', [':url' => 'https://www.drupal.org/upgrade/migrate']),
+        '#prefix' => $this->t('<p>An upgrade has already been performed on this site.</p>'),
         '#description' => $this->t('<p>Last upgrade: @date</p>', ['@date' => $this->dateFormatter->format($date_performed)]),
       ];
-      return $form;
+      $form['upgrade_option'] = array(
+        '#type' => 'radios',
+        '#title' => $this->t('You can rollback the upgrade.'),
+        '#default_value' => static::MIGRATE_UPGRADE_ROLLBACK,
+        '#options' => [
+          static::MIGRATE_UPGRADE_ROLLBACK => $this->t('<strong>Rollback</strong>: Remove content and configuration entities (such as fields and node types). Default values of other configuration will not be reverted (such as site name).'),
+        ],
+      );
+
+      $validate = ['::validateCredentialForm'];
     }
     else {
       $form['info_header'] = [
@@ -764,7 +783,15 @@ public function buildOverviewForm(array $form, FormStateInterface $form_state) {
    *   The current state of the form.
    */
   public function submitOverviewForm(array &$form, FormStateInterface $form_state) {
-    $form_state->setValue('step', 'credentials');
+    switch ($form_state->getValue('upgrade_option')) {
+      case static::MIGRATE_UPGRADE_ROLLBACK:
+        $form_state->setValue('step', 'confirm');
+        break;
+
+      default:
+        $form_state->setValue('step', 'credentials');
+        break;
+    }
     $form_state->setRebuild();
   }
 
@@ -877,7 +904,10 @@ public function buildCredentialForm(array $form, FormStateInterface $form_state)
    *   The current state of the form.
    */
   public function validateCredentialForm(array &$form, FormStateInterface $form_state) {
-
+    // Skip if rollback was chosen.
+    if ($form_state->getValue('upgrade_option') == static::MIGRATE_UPGRADE_ROLLBACK) {
+      return;
+    }
     // Retrieve the database driver from the form, use reflection to get the
     // namespace, and then construct a valid database array the same as in
     // settings.php.
@@ -958,7 +988,7 @@ public function submitCredentialForm(array &$form, FormStateInterface $form_stat
   }
 
   /**
-   * Confirmation form for missing migrations, etc.
+   * Confirmation form for rollbacks, missing migrations, etc.
    *
    * @param array $form
    *   An associative array containing the structure of the form.
@@ -972,90 +1002,98 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
     $form = parent::buildForm($form, $form_state);
     $form['actions']['submit']['#submit'] = ['::submitConfirmForm'];
 
-    $form['actions']['submit']['#value'] = $this->t('Perform upgrade');
+    if ($rollback = $form_state->getValue('upgrade_option') == static::MIGRATE_UPGRADE_ROLLBACK) {
+      $form_state->setStorage(['upgrade_option' => static::MIGRATE_UPGRADE_ROLLBACK]);
+      $form['rollback'] = [
+        '#markup' => $this->t('All previously-imported content, as well as configuration such as field definitions, will be removed.'),
+      ];
+      $form['actions']['submit']['#value'] = $this->t('Perform rollback');
+    }
+    else {
+      $form['actions']['submit']['#value'] = $this->t('Perform upgrade');
 
-    $table_data = [];
-    $system_data = [];
-    foreach ($form_state->get('migrations') as $migration_id => $migration_label) {
-      // Fetch the system data at the first opportunity.
-      if (empty($system_data)) {
-        $system_data = $form_state->get('system_data');
-      }
+      $table_data = [];
+      $system_data = [];
+      foreach ($form_state->get('migrations') as $migration_id => $migration_label) {
+        // Fetch the system data at the first opportunity.
+        if (empty($system_data)) {
+          $system_data = $form_state->get('system_data');
+        }
 
-      // Handle derivatives.
-      list($migration_id,) = explode(':', $migration_id, 2);
-      $source_module = $this->moduleUpgradePaths[$migration_id]['source_module'];
-      $destination_module = $this->moduleUpgradePaths[$migration_id]['destination_module'];
-      $table_data[$source_module][$destination_module][$migration_id] = $migration_label;
-    }
-    // Sort the table by source module names and within that destination
-    // module names.
-    ksort($table_data);
-    foreach ($table_data as $source_module => $destination_module_info) {
-      ksort($table_data[$source_module]);
-    }
-    $unmigrated_source_modules = array_diff_key($system_data['module'], $table_data);
+        // Handle derivatives.
+        list($migration_id,) = explode(':', $migration_id, 2);
+        $source_module = $this->moduleUpgradePaths[$migration_id]['source_module'];
+        $destination_module = $this->moduleUpgradePaths[$migration_id]['destination_module'];
+        $table_data[$source_module][$destination_module][$migration_id] = $migration_label;
+      }
+      // Sort the table by source module names and within that destination
+      // module names.
+      ksort($table_data);
+      foreach ($table_data as $source_module => $destination_module_info) {
+        ksort($table_data[$source_module]);
+      }
+      $unmigrated_source_modules = array_diff_key($system_data['module'], $table_data);
 
-    // Missing migrations.
-    $form['missing_module_list_title'] = [
-      '#type' => 'item',
-      '#title' => $this->t('Missing upgrade paths'),
-      '#description' => $this->t('The following items will not be upgraded. For more information see <a href=":migrate">Upgrading from Drupal 6 or 7 to Drupal 8</a>.', array(':migrate' => 'https://www.drupal.org/upgrade/migrate')),
-    ];
-    $form['missing_module_list'] = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Source'),
-        $this->t('Destination'),
-      ],
-    ];
-    $missing_count = 0;
-    ksort($unmigrated_source_modules);
-    foreach ($unmigrated_source_modules as $source_module => $module_data) {
-      if ($module_data['status']) {
-        $missing_count++;
-        $form['missing_module_list'][$source_module] = [
-          'source_module' => ['#plain_text' => $source_module],
-          'destination_module' => ['#plain_text' => 'Missing'],
-        ];
+      // Missing migrations.
+      $form['missing_module_list_title'] = [
+        '#type' => 'item',
+        '#title' => $this->t('Missing upgrade paths'),
+        '#description' => $this->t('The following items will not be upgraded. For more information see <a href=":migrate">Upgrading from Drupal 6 or 7 to Drupal 8</a>.', array(':migrate' => 'https://www.drupal.org/upgrade/migrate')),
+      ];
+      $form['missing_module_list'] = [
+        '#type' => 'table',
+        '#header' => [
+          $this->t('Source'),
+          $this->t('Destination'),
+        ],
+      ];
+      $missing_count = 0;
+      ksort($unmigrated_source_modules);
+      foreach ($unmigrated_source_modules as $source_module => $module_data) {
+        if ($module_data['status']) {
+          $missing_count++;
+          $form['missing_module_list'][$source_module] = [
+            'source_module' => ['#plain_text' => $source_module],
+            'destination_module' => ['#plain_text' => 'Missing'],
+          ];
+        }
       }
-    }
-    // Available migrations.
-    $form['available_module_list'] = [
-      '#tree' => TRUE,
-      '#type' => 'details',
-      '#title' => $this->t('Available upgrade paths'),
-    ];
+      // Available migrations.
+      $form['available_module_list'] = [
+        '#tree' => TRUE,
+        '#type' => 'details',
+        '#title' => $this->t('Available upgrade paths'),
+      ];
 
-    $form['available_module_list']['module_list'] = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Source'),
-        $this->t('Destination'),
-      ],
-    ];
+      $form['available_module_list']['module_list'] = [
+        '#type' => 'table',
+        '#header' => [
+          $this->t('Source'),
+          $this->t('Destination'),
+        ],
+      ];
 
-    $available_count = 0;
-    foreach ($table_data as $source_module => $destination_module_info) {
-      $available_count++;
-      $destination_details = [];
-      foreach ($destination_module_info as $destination_module => $migration_ids) {
-        $destination_details[$destination_module] = [
-          '#type' => 'item',
-          '#plain_text' => $destination_module,
+      $available_count = 0;
+      foreach ($table_data as $source_module => $destination_module_info) {
+        $available_count++;
+        $destination_details = [];
+        foreach ($destination_module_info as $destination_module => $migration_ids) {
+          $destination_details[$destination_module] = [
+            '#type' => 'item',
+            '#plain_text' => $destination_module,
+          ];
+        }
+        $form['available_module_list']['module_list'][$source_module] = [
+          'source_module' => ['#plain_text' => $source_module],
+          'destination_module' => $destination_details,
         ];
       }
-      $form['available_module_list']['module_list'][$source_module] = [
-        'source_module' => ['#plain_text' => $source_module],
-        'destination_module' => $destination_details,
+      $form['counts'] = [
+        '#type' => 'item',
+        '#title' => '<ul><li>' . $this->t('@count available upgrade paths', ['@count' => $available_count]) . '</li><li>' . $this->t('@count missing upgrade paths', ['@count' => $missing_count]) . '</li></ul>',
+        '#weight' => -15,
       ];
     }
-    $form['counts'] = [
-      '#type' => 'item',
-      '#title' => '<ul><li>' . $this->t('@count available upgrade paths', ['@count' => $available_count]) . '</li><li>' . $this->t('@count missing upgrade paths', ['@count' => $missing_count]) . '</li></ul>',
-      '#weight' => -15,
-    ];
-
     return $form;
   }
 
@@ -1069,26 +1107,65 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
    */
   public function submitConfirmForm(array &$form, FormStateInterface $form_state) {
     $storage = $form_state->getStorage();
+    if (isset($storage['upgrade_option']) && $storage['upgrade_option'] == static::MIGRATE_UPGRADE_ROLLBACK) {
+      $migrations = $this->pluginManager->createInstances([]);
+
+      // Assume we want all those tagged 'Drupal %'.
+      foreach ($migrations as $migration_id => $migration) {
+        $keep = FALSE;
+        $tags = $migration->get('migration_tags');
+        foreach ($tags as $tag) {
+          if (strpos($tag, 'Drupal ') === 0) {
+            $keep = TRUE;
+            break;
+          }
+        }
+        if (!$keep) {
+          unset($migrations[$migration_id]);
+        }
+      }
+      // Roll back in reverse order.
+      $migrations = array_reverse($migrations);
 
-    $migrations = $storage['migrations'];
-    $config['source_base_path'] = $storage['source_base_path'];
-    $batch = [
-      'title' => $this->t('Running upgrade'),
-      'progress_message' => '',
-      'operations' => [
-        [
-          [MigrateUpgradeRunBatch::class, 'run'],
-          [array_keys($migrations), 'import', $config],
+      $batch = [
+        'title' => $this->t('Rolling back upgrade'),
+        'progress_message' => '',
+        'operations' => [
+          [
+            [MigrateUpgradeRunBatch::class, 'run'],
+            [array_keys($migrations), 'rollback', []],
+          ],
         ],
-      ],
-      'finished' => [
-        MigrateUpgradeRunBatch::class,
-        'finished',
-      ],
-    ];
-    batch_set($batch);
-    $form_state->setRedirect('<front>');
-    $this->state->set('migrate_drupal_ui.performed', REQUEST_TIME);
+        'finished' => [
+          MigrateUpgradeRunBatch::class,
+          'finished',
+        ],
+      ];
+      batch_set($batch);
+      $form_state->setRedirect('migrate_drupal_ui.upgrade');
+      $this->state->delete('migrate_drupal_ui.performed');
+    }
+    else {
+      $migrations = $storage['migrations'];
+      $config['source_base_path'] = $storage['source_base_path'];
+      $batch = [
+        'title' => $this->t('Running upgrade'),
+        'progress_message' => '',
+        'operations' => [
+          [
+            [MigrateUpgradeRunBatch::class, 'run'],
+            [array_keys($migrations), 'import', $config],
+          ],
+        ],
+        'finished' => [
+          MigrateUpgradeRunBatch::class,
+          'finished',
+        ],
+      ];
+      batch_set($batch);
+      $form_state->setRedirect('<front>');
+      $this->state->set('migrate_drupal_ui.performed', REQUEST_TIME);
+    }
   }
 
   /**
@@ -1107,7 +1184,12 @@ protected function getDatabaseTypes() {
    * {@inheritdoc}
    */
   public function getQuestion() {
-    return $this->t('Are you sure?');
+    if ($this->state->get('migrate_drupal_ui.performed')) {
+      return $this->t('Are you sure you want to rollback the migration?');
+    }
+    else {
+      return $this->t('Are you sure you want to perform the upgrade?');
+    }
   }
 
   /**
@@ -1121,7 +1203,12 @@ public function getCancelUrl() {
    * {@inheritdoc}
    */
   public function getDescription() {
-    return $this->t('<p><strong>Upgrade analysis report</strong></p>');
+    if ($this->state->get('migrate_drupal_ui.performed')) {
+      return;
+    }
+    else {
+      return $this->t('<p><strong>Upgrade analysis report</strong></p>');
+    }
   }
 
   /**
diff --git a/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php b/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php
index 6ad8957..e31a741 100644
--- a/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php
+++ b/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php
@@ -59,7 +59,7 @@ class MigrateUpgradeRunBatch {
    * @param int[] $initial_ids
    *   The full set of migration IDs to import.
    * @param string $operation
-   *   The operation to perform. Only 'import' is currently supported.
+   *   The operation to perform, 'import' or 'rollback'.
    * @param array $config
    *   An array of additional configuration from the form.
    * @param array $context
@@ -126,6 +126,9 @@ public static function run($initial_ids, $operation, $config, &$context) {
         if ($operation == 'import') {
           $migration_status = $executable->import();
         }
+        else {
+          $migration_status = $executable->rollback();
+        }
       }
       catch (\Exception $e) {
         static::logger()->error($e->getMessage());
@@ -141,6 +144,11 @@ public static function run($initial_ids, $operation, $config, &$context) {
               $context['sandbox']['num_processed'], 'Upgraded @migration (processed 1 item total)', 'Upgraded @migration (processed @num_processed items total)',
               ['@migration' => $migration_name, '@num_processed' => $context['sandbox']['num_processed']]);
           }
+          else {
+            $message = static::getTranslation()->formatPlural(
+              $context['sandbox']['num_processed'], 'Rolled back @migration (processed 1 item total)', 'Rolled back @migration (processed @num_processed items total)',
+              ['@migration' => $migration_name, '@num_processed' => $context['sandbox']['num_processed']]);
+          }
           $context['sandbox']['messages'][] = $message;
           static::logger()->notice($message);
           $context['sandbox']['num_processed'] = 0;
@@ -209,6 +217,13 @@ public static function run($initial_ids, $operation, $config, &$context) {
             '@max' => $context['sandbox']['max'],
           ]) . "<br />\n" . $context['message'];
         }
+        else {
+          $context['message'] = t('Currently rolling back @migration (@current of @max total tasks)', [
+              '@migration' => $migration_name,
+              '@current' => $context['sandbox']['current'],
+              '@max' => $context['sandbox']['max'],
+            ]) . "<br />\n" . $context['message'];
+        }
       }
     }
     else {
@@ -261,6 +276,9 @@ protected static function displayResults($results) {
       if ($results['operation'] == 'import') {
         drupal_set_message(static::getTranslation()->formatPlural($successes, 'Completed 1 upgrade task successfully', 'Completed @count upgrade tasks successfully'));
       }
+      else {
+        drupal_set_message(static::getTranslation()->formatPlural($successes, 'Completed 1 rollback task successfully', 'Completed @count rollback tasks successfully'));
+      }
     }
 
     // If we had failures, log them and show the migration failed.
@@ -269,6 +287,10 @@ protected static function displayResults($results) {
         drupal_set_message(static::getTranslation()->formatPlural($failures, '1 upgrade failed', '@count upgrades failed'));
         drupal_set_message(t('Upgrade process not completed'), 'error');
       }
+      else {
+        drupal_set_message(static::getTranslation()->formatPlural($failures, '1 rollback failed', '@count rollbacks failed'));
+        drupal_set_message(t('Rollback process not completed'), 'error');
+      }
     }
     else {
       if ($results['operation'] == 'import') {
@@ -276,6 +298,9 @@ protected static function displayResults($results) {
         // but we didn't have failures so this is fine.
         drupal_set_message(t('Congratulations, you upgraded Drupal!'));
       }
+      else {
+        drupal_set_message(t('Rollback of the upgrade is complete - you may now start the upgrade process from scratch.'));
+      }
     }
 
     if (\Drupal::moduleHandler()->moduleExists('dblog')) {
