diff --git a/core/lib/Drupal/Core/Entity/RevisionableSchemaConverter.php b/core/lib/Drupal/Core/Entity/RevisionableSchemaConverter.php
new file mode 100644
index 0000000..2c5ca86
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/RevisionableSchemaConverter.php
@@ -0,0 +1,241 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Convert an entity to be revisionable.
+ *
+ * @internal
+ *   This class should only be used via core update hooks.
+ */
+class RevisionableSchemaConverter implements RevisionableSchemaConverterInterface {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   *   Last installed schemea repository service.
+   */
+  protected $lastInstalledSchemaRepository;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   *   Entity Type Manager service.
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   *   Entity definition update manager service.
+   */
+  protected $entityDefinitionUpdateManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   *   Entity field manager service.
+   */
+  protected $entityFieldManager;
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   *   Database connection.
+   */
+  protected $database;
+
+  /**
+   * EntitySchemaUpdater constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository
+   *   Last installed schema repository service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   Entity type manager service.
+   * @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $entity_definition_update_manager
+   *   Entity definition update manager service.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   Entity field manager service.
+   * @param \Drupal\Core\Database\Connection $database
+   *   Database connection.
+   */
+  public function __construct(EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository, EntityTypeManagerInterface $entity_type_manager, EntityDefinitionUpdateManagerInterface $entity_definition_update_manager, EntityFieldManagerInterface $entity_field_manager, Connection $database) {
+    $this->lastInstalledSchemaRepository = $last_installed_schema_repository;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityDefinitionUpdateManager = $entity_definition_update_manager;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->database = $database;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function convertSchema($entity_type_id, $options = []) {
+    $default_options = [
+      'revision' => 'revision_id',
+      'revision_table' => $entity_type_id . '_revision',
+      'revision_data_table' => $entity_type_id . '_field_revision',
+      'revision_created' => 'revision_created',
+      'revision_user' => 'revision_user',
+      'revision_log_message' => 'revision_log_message',
+      'langcode' => 'langcode',
+    ];
+    $options = array_merge($default_options, $options);
+    $this->updateEntityType($entity_type_id, $options);
+    $this->createTables($entity_type_id, $options);
+    $this->installRevisionableFields($entity_type_id, $options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function copyData(EntityTypeInterface $entity_type, array &$sandbox) {
+    // If 'progress' is not set, then this will be the first run of the batch.
+    if (!isset($sandbox['progress'])) {
+      $base_table = $entity_type->getBaseTable();
+      $sandbox['progress'] = 0;
+      $sandbox['current_id'] = 0;
+      $sandbox['max'] = $this->database->select($base_table)
+        ->countQuery()
+        ->execute()
+        ->fetchField();
+    }
+
+    /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $table_mapping */
+    $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping();
+    $table_names = $table_mapping->getTableNames();
+    $id = $entity_type->getKey('id');
+    $revision_id = $entity_type->getKey('revision');
+
+    $data = [];
+    // Loop through all tables for the entity type and combine the data.
+    foreach ($table_names as $table_name) {
+      $column_names = $table_mapping->getAllColumns($table_name);
+      // Process 5 entities per batch.
+      $results = $this->database->select($table_name, 't')
+        ->fields('t')
+        ->condition($id, $sandbox['current_id'], '>')
+        ->range(0, 5)
+        ->orderBy($id)
+        ->execute()
+        ->fetchAll();
+      foreach ($results as $key => $result) {
+        foreach ($column_names as $column_name) {
+          if (!empty($result->{$column_name})) {
+            $data[$key][$column_name] = $result->{$column_name};
+          }
+        }
+      }
+    }
+
+    // Loop through all the collected data and update / insert missing rows.
+    foreach ($data as $record) {
+      if (!empty($record[$id])) {
+        $record[$revision_id] = $record[$id];
+      }
+      foreach ($table_names as $table_name) {
+        $values = [];
+        $column_names = $table_mapping->getAllColumns($table_name);
+        foreach ($column_names as $column_name) {
+          if (!empty($record[$column_name])) {
+            $values[$column_name] = $record[$column_name];
+          }
+        }
+        $this->database->upsert($table_name)->key('id')->fields($values)->execute();
+      }
+      $sandbox['progress']++;
+      $sandbox['current_id'] = $record['id'];
+    }
+    $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
+  }
+
+  /**
+   * Updates installed entity type definition.
+   *
+   * @param string $entity_type_id
+   *   ID of the entity type to update.
+   * @param array $options
+   *   Options to update the entity type with.
+   */
+  protected function updateEntityType($entity_type_id, $options) {
+    $last_entity_type = $this->lastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id);
+    $keys = $last_entity_type->getKeys();
+    $keys['revision'] = $options['revision'];
+    $last_entity_type->set('entity_keys', $keys);
+    $last_entity_type->set('revision_table', $options['revision_table']);
+    $last_entity_type->set('revision_data_table', $options['revision_data_table']);
+    $this->lastInstalledSchemaRepository->setLastInstalledDefinition($last_entity_type);
+  }
+
+  /**
+   * Creates missing tables.
+   *
+   * @param string $entity_type_id
+   *   ID of the entity type to update.
+   */
+  protected function createTables($entity_type_id) {
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
+    $entity_type = $this->entityDefinitionUpdateManager->getEntityType($entity_type_id);
+    if ($storage instanceof DynamicallyFieldableEntityStorageSchemaInterface) {
+      $storage->entityTypeResolveMissingSchema($entity_type);
+    }
+  }
+
+  /**
+   * Installs new fields.
+   *
+   * @param string $entity_type_id
+   *   ID of the entity type to update.
+   * @param array $options
+   *   Options to update the entity type with.
+   */
+  protected function installRevisionableFields($entity_type_id, $options) {
+    $revision_id = BaseFieldDefinition::create('integer')
+      ->setLabel(new TranslatableMarkup('Revision ID'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE);
+    $this->entityDefinitionUpdateManager
+      ->installFieldStorageDefinition($options['revision'], $entity_type_id, $entity_type_id, $revision_id);
+
+    $revision_created = BaseFieldDefinition::create('created')
+      ->setLabel(t('Revision create time'))
+      ->setDescription(t('The time that the current revision was created.'))
+      ->setRevisionable(TRUE);
+    $this->entityDefinitionUpdateManager
+      ->installFieldStorageDefinition($options['revision_created'], $entity_type_id, $entity_type_id, $revision_created);
+
+    $revision_user = BaseFieldDefinition::create('entity_reference')
+      ->setLabel(t('Revision user'))
+      ->setDescription(t('The user ID of the author of the current revision.'))
+      ->setSetting('target_type', 'user')
+      ->setRevisionable(TRUE);
+    $this->entityDefinitionUpdateManager
+      ->installFieldStorageDefinition($options['revision_user'], $entity_type_id, $entity_type_id, $revision_user);
+
+    $revision_log_message = BaseFieldDefinition::create('string_long')
+      ->setLabel(t('Revision log message'))
+      ->setDescription(t('Briefly describe the changes you have made.'))
+      ->setRevisionable(TRUE)
+      ->setDefaultValue('')
+      ->setDisplayOptions('form', [
+        'type' => 'string_textarea',
+        'weight' => 25,
+        'settings' => [
+          'rows' => 4,
+        ],
+      ]);
+    $this->entityDefinitionUpdateManager
+      ->installFieldStorageDefinition($options['revision_log_message'], $entity_type_id, $entity_type_id, $revision_log_message);
+
+    /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions */
+    $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
+    foreach ($field_definitions as $field_definition) {
+      $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($field_definition);
+    }
+
+    $schema = $this->database->schema();
+    $schema->dropPrimaryKey($options['revision_data_table']);
+    $schema->addPrimaryKey($options['revision_data_table'], [$options['revision'], $options['langcode']]);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/RevisionableSchemaConverterInterface.php b/core/lib/Drupal/Core/Entity/RevisionableSchemaConverterInterface.php
new file mode 100644
index 0000000..7239af4
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/RevisionableSchemaConverterInterface.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+interface RevisionableSchemaConverterInterface {
+
+  /**
+   * Converts the schema to be revisionable.
+   *
+   * @param string $entity_type_id
+   *   The id of the entity type to convert.
+   * @param array $options
+   *   - revision_id - Revision id entity key.
+   *   - revision_table - Revision table name.
+   *   - revision_data_table - Revision data table name.
+   */
+  public function convertSchema($entity_type_id, $options = []);
+
+  /**
+   * Copies existing data to new tables.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type for the data that needs copying.
+   * @param array $sandbox
+   *   The sandbox array from hook_update().
+   */
+  public function copyData(EntityTypeInterface $entity_type, array &$sandbox);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Schema/DynamicallyFieldableEntityStorageSchemaInterface.php b/core/lib/Drupal/Core/Entity/Schema/DynamicallyFieldableEntityStorageSchemaInterface.php
index 807db45..a15dc0f 100644
--- a/core/lib/Drupal/Core/Entity/Schema/DynamicallyFieldableEntityStorageSchemaInterface.php
+++ b/core/lib/Drupal/Core/Entity/Schema/DynamicallyFieldableEntityStorageSchemaInterface.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Entity\Schema;
 
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
 
@@ -17,6 +18,13 @@
 interface DynamicallyFieldableEntityStorageSchemaInterface extends EntityStorageSchemaInterface, FieldStorageDefinitionListenerInterface {
 
   /**
+   * Resolve missing schema items after entity type update.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   */
+  public function entityTypeResolveMissingSchema(EntityTypeInterface $entity_type);
+
+  /**
    * Checks if the changes to the storage definition requires schema changes.
    *
    * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index e115bd2..84f2957 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -1337,6 +1337,15 @@ protected function deleteRevisionFromDedicatedTables(ContentEntityInterface $ent
   /**
    * {@inheritdoc}
    */
+  public function entityTypeResolveMissingSchema(EntityTypeInterface $entity_type) {
+    $this->wrapSchemaException(function () use ($entity_type) {
+      $this->getStorageSchema()->entityTypeResolveMissingSchema($entity_type);
+    });
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
     return $this->getStorageSchema()->requiresEntityStorageSchemaChanges($entity_type, $original);
   }
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index ec3ebaf..93de0de 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -227,6 +227,39 @@ protected function getSchemaFromStorageDefinition(FieldStorageDefinitionInterfac
   /**
    * {@inheritdoc}
    */
+  public function entityTypeResolveMissingSchema(EntityTypeInterface $entity_type) {
+    $this->checkEntityType($entity_type);
+    $schema_handler = $this->database->schema();
+
+    // Create entity tables.
+    $schema = $this->getEntitySchema($entity_type, TRUE);
+    foreach ($schema as $table_name => $table_schema) {
+      if (!$schema_handler->tableExists($table_name)) {
+        $schema_handler->createTable($table_name, $table_schema);
+      }
+    }
+
+    // Create dedicated field tables.
+    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type->id());
+    $table_mapping = $this->storage->getTableMapping($field_storage_definitions);
+    foreach ($field_storage_definitions as $field_storage_definition) {
+      if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
+        $this->createDedicatedTableSchema($field_storage_definition);
+      }
+      elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
+        // The shared tables are already fully created, but we need to save the
+        // per-field schema definitions for later use.
+        $this->createSharedTableSchema($field_storage_definition, TRUE);
+      }
+    }
+
+    // Save data about entity indexes and keys.
+    $this->saveEntitySchemaData($entity_type, $schema);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
     // If the original storage has existing entities, or it is impossible to
     // determine if that is the case, require entity data to be migrated.
@@ -1138,6 +1171,12 @@ protected function createSharedTableSchema(FieldStorageDefinitionInterface $stor
           $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);
           if (!$only_save) {
             foreach ($schema[$table_name]['fields'] as $name => $specifier) {
+              // Use the value of the entity ID as the initial value for the
+              // revision ID field.
+              if($created_field_name == $this->entityType->getKey('revision')) {
+                $id_column_name = $table_mapping->getColumnNames($this->entityType->getKey('id'));
+                $specifier['initial_from_field'] = reset($id_column_name);
+              }
               // Check if the field exists because it might already have been
               // created as part of the earlier entity type update event.
               if (!$schema_handler->fieldExists($table_name, $name)) {
diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install
index 8df408f..dbed0f5 100644
--- a/core/modules/shortcut/shortcut.install
+++ b/core/modules/shortcut/shortcut.install
@@ -67,3 +67,40 @@ function shortcut_uninstall() {
     \Drupal::configFactory()->getEditable('seven.settings')->clear('third_party_settings.shortcut')->save(TRUE);
   }
 }
+
+/**
+ * Convert Shortcut entity schema.
+ */
+function shortcut_update_8001() {
+  $schema_converter = new \Drupal\Core\Entity\RevisionableSchemaConverter(
+    \Drupal::service('entity.last_installed_schema.repository'),
+    \Drupal::service('entity_type.manager'),
+    \Drupal::service('entity.definition_update_manager'),
+    \Drupal::service('entity_field.manager'),
+    \Drupal::service('database')
+  );
+  $schema_converter->convertSchema('shortcut', [
+    'revision' => 'revision_id',
+    'revision_table' => 'shortcut_revision',
+    'revision_data_table' => 'shortcut_field_revision',
+    'revision_created' => 'revision_created',
+    'revision_user' => 'revision_user',
+    'revision_log_message' => 'revision_log_message',
+    'langcode' => 'langcode',
+  ]);
+}
+
+/**
+ * Copy Shortcut entity data to new schema.
+ */
+function shortcut_update_8002(&$sandbox) {
+  $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('shortcut');
+  $schema_converter = new \Drupal\Core\Entity\RevisionableSchemaConverter(
+    \Drupal::service('entity.last_installed_schema.repository'),
+    \Drupal::service('entity_type.manager'),
+    \Drupal::service('entity.definition_update_manager'),
+    \Drupal::service('entity_field.manager'),
+    \Drupal::service('database')
+  );
+  $schema_converter->copyData($entity_type, $sandbox);
+}
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 6ddaf05..9f60fe0 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -3,9 +3,9 @@
 namespace Drupal\shortcut\Entity;
 
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Entity\ContentEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\RevisionableContentEntityBase;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\link\LinkItemInterface;
 use Drupal\shortcut\ShortcutInterface;
@@ -30,9 +30,12 @@
  *   },
  *   base_table = "shortcut",
  *   data_table = "shortcut_field_data",
+ *   revision_table = "shortcut_revision",
+ *   revision_data_table = "shortcut_field_revision",
  *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
+ *     "revision" = "revision_id",
  *     "uuid" = "uuid",
  *     "bundle" = "shortcut_set",
  *     "label" = "title",
@@ -47,7 +50,7 @@
  *   bundle_entity_type = "shortcut_set"
  * )
  */
-class Shortcut extends ContentEntityBase implements ShortcutInterface {
+class Shortcut extends RevisionableContentEntityBase implements ShortcutInterface {
 
   /**
    * {@inheritdoc}
@@ -122,6 +125,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDescription(t('The name of the shortcut.'))
       ->setRequired(TRUE)
       ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
       ->setSetting('max_length', 255)
       ->setDisplayOptions('form', array(
         'type' => 'string_textfield',
@@ -139,6 +143,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Path'))
       ->setDescription(t('The location this shortcut points to.'))
       ->setRequired(TRUE)
+      ->setRevisionable(TRUE)
       ->setSettings(array(
         'link_type' => LinkItemInterface::LINK_INTERNAL,
         'title' => DRUPAL_DISABLED,
diff --git a/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php b/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php
index 0d58c31..c8517ce 100644
--- a/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php
+++ b/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php
@@ -21,6 +21,7 @@ public function testInstallUninstall() {
     \Drupal::service('theme_installer')->install(['seven']);
     $this->assertNull($this->config('seven.settings')->get('third_party_settings.shortcut'), 'There are no shortcut settings in seven.settings.');
 
+    \Drupal::service('module_installer')->install(['user']);
     \Drupal::service('module_installer')->install(['shortcut']);
     $this->assertTrue($this->config('seven.settings')->get('third_party_settings.shortcut.module_link'), 'The shortcut module_link setting is in seven.settings.');
 
diff --git a/core/tests/Drupal/Tests/Core/Entity/RevisionableSchemaConverterTest.php b/core/tests/Drupal/Tests/Core/Entity/RevisionableSchemaConverterTest.php
new file mode 100644
index 0000000..b244252
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/RevisionableSchemaConverterTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\Tests\Core\Entity;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+
+/**
+ * Testing RevisionableSchemaConverter correctly updates Shortcut entity to be
+ * revisionable.
+ *
+ * @package Drupal\system\Tests\Update
+ *
+ * @group entity
+ */
+class RevisionableSchemaConverterTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [__DIR__ . '/../../../../../modules/system/tests/fixtures/update/drupal-8.filled.standard.php.gz'];
+  }
+
+  /**
+   * Tests the entity type is revisionable and shortcut entities are accessible.
+   */
+  public function testMakeRevisionable() {
+    $this->runUpdates();
+
+    $entity_type_manager = $this->container->get('entity_type.manager');
+    $shortcut = $entity_type_manager->getStorage('shortcut')->getEntityType();
+
+    /** @var ContentEntityInterface $last_installed_shortcut */
+    $last_installed_shortcut = $this->container->get('entity.last_installed_schema.repository')->getLastInstalledDefinition($shortcut->id());
+    $this->assertTrue($last_installed_shortcut->isRevisionable());
+
+    $shortcut_storage = $entity_type_manager->getStorage($shortcut->id());
+    $shortcut_entities = $shortcut_storage->loadMultiple();
+    $this->assertEqual(count($shortcut_entities), 4, "Four Shortcut entities found");
+
+    /** @var \Drupal\shortcut\ShortcutInterface $revision_one */
+    $revision_one = $shortcut_storage->loadRevision(1);
+    $this->assertEqual(1, $revision_one->getRevisionId(), "Revision 1 is revision 1");
+
+    $revision_one->setNewRevision(TRUE);
+    $revision_one->save();
+    $this->assertEqual(5, $revision_one->getRevisionId(), "New shortcut revision created");
+  }
+
+}
