diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
index d64a0d6d57..ec5700e3f6 100644
--- a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
@@ -14,6 +14,16 @@
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
+ * 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;
+
+/**
  * Defines a multi-step form for performing direct site upgrades.
  */
 class MigrateUpgradeForm extends ConfirmFormBase {
@@ -795,10 +805,18 @@ 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('Last upgrade: @date', ['@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'] = [
@@ -852,6 +870,14 @@ public function buildOverviewForm(array $form, FormStateInterface $form_state) {
    */
   public function submitOverviewForm(array &$form, FormStateInterface $form_state) {
     $form_state->set('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();
   }
 
@@ -964,7 +990,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.
@@ -1039,7 +1068,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.
@@ -1054,7 +1083,16 @@ public function buildConfirmForm(array $form, FormStateInterface $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) {
@@ -1081,7 +1119,7 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
     $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>.', [':migrate' => 'https://www.drupal.org/upgrade/migrate']),
+      '#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',
@@ -1101,6 +1139,7 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
         ];
       }
     }
+
     // Available migrations.
     $form['available_module_list'] = [
       '#tree' => TRUE,
@@ -1154,25 +1193,63 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
    */
   public function submitConfirmForm(array &$form, FormStateInterface $form_state) {
     $storage = $form_state->getStorage();
-
-    $migrations = $storage['migrations'];
-    $config['source_base_path'] = $storage['source_base_path'];
-    $batch = [
-      'title' => $this->t('Running upgrade'),
-      'progress_message' => '',
-      'operations' => [
-        [
-          [MigrateUpgradeImportBatch::class, 'run'],
-          [array_keys($migrations), $config],
+    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);
+      $batch = [
+        'title' => $this->t('Rolling back upgrade'),
+        'progress_message' => '',
+        'operations' => [
+          [
+            [MigrateUpgradeRunBatch::class, 'run'],
+            [array_keys($migrations), 'rollback', []],
+          ],
         ],
-      ],
-      'finished' => [
-        MigrateUpgradeImportBatch::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);
+    }
   }
 
   /**
@@ -1191,7 +1268,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?');
+    }
   }
 
   /**
@@ -1207,7 +1289,12 @@ public function getCancelUrl() {
   public function getDescription() {
     // The description is added by the buildConfirmForm() method.
     // @see \Drupal\migrate_drupal_ui\Form\MigrateUpgradeForm::buildConfirmForm()
-    return;
+    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/Tests/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/src/Tests/MigrateUpgradeTestBase.php
index b635f2ab22..57a1db13a7 100644
--- a/core/modules/migrate_drupal_ui/src/Tests/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/src/Tests/MigrateUpgradeTestBase.php
@@ -143,7 +143,7 @@ public function testMigrateUpgrade() {
 
     $this->drupalPostForm(NULL, $edits, t('Review upgrade'));
     $this->assertResponse(200);
-    $this->assertText('Are you sure?');
+    $this->assertText('Are you sure you want to perform the upgrade?');
     $this->drupalPostForm(NULL, [], t('Perform upgrade'));
     $this->assertText(t('Congratulations, you upgraded Drupal!'));
 
