diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index 6327656..5589dad 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
 use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Field\FieldException;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\field\FieldStorageConfigInterface;
@@ -201,7 +202,10 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac
       return FALSE;
     }
 
-    return $this->getSchemaFromStorageDefinition($storage_definition) != $this->loadFieldSchemaData($original);
+    $current_schema = $this->getSchemaFromStorageDefinition($storage_definition);
+    $this->processFieldStorageSchema($current_schema);
+
+    return $current_schema != $this->loadFieldSchemaData($original);
   }
 
   /**
@@ -852,6 +856,7 @@ protected function loadFieldSchemaData(FieldStorageDefinitionInterface $storage_
    *   The field schema data array.
    */
   protected function saveFieldSchemaData(FieldStorageDefinitionInterface $storage_definition, $schema) {
+    $this->processFieldStorageSchema($schema);
     $this->installedStorageSchema()->set($storage_definition->getTargetEntityTypeId() . '.field_schema_data.' . $storage_definition->getName(), $schema);
   }
 
@@ -1102,6 +1107,23 @@ protected function processIdentifierSchema(&$schema, $key) {
   }
 
   /**
+   * Processes the schema for a field storage definition.
+   *
+   * @param array &$field_storage_schema
+   *   An array that contains the schema data for a field storage definition.
+   */
+  protected function processFieldStorageSchema(array &$field_storage_schema) {
+    // Clean up some schema properties that should not be taken into account
+    // after a field storage has been created.
+    foreach ($field_storage_schema as $table_name => $table_schema) {
+      foreach ($table_schema['fields'] as $key => $schema) {
+        unset($field_storage_schema[$table_name]['fields'][$key]['initial']);
+        unset($field_storage_schema[$table_name]['fields'][$key]['initial_from_field']);
+      }
+    }
+  }
+
+  /**
    * Performs the specified operation on a field.
    *
    * This figures out whether the field is stored in a dedicated or shared table
@@ -1613,29 +1635,58 @@ protected function hasNullFieldPropertyData($table_name, $column_name) {
    *   - foreign keys: The schema definition for the foreign keys.
    *
    * @throws \Drupal\Core\Field\FieldException
-   *   Exception thrown if the schema contains reserved column names.
+   *   Exception thrown if the schema contains reserved column names or if the
+   *   initial values definition is invalid.
    */
   protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
     $schema = [];
+    $table_mapping = $this->storage->getTableMapping();
     $field_schema = $storage_definition->getSchema();
 
     // Check that the schema does not include forbidden column names.
-    if (array_intersect(array_keys($field_schema['columns']), $this->storage->getTableMapping()->getReservedColumns())) {
+    if (array_intersect(array_keys($field_schema['columns']), $table_mapping->getReservedColumns())) {
       throw new FieldException("Illegal field column names on {$storage_definition->getName()}");
     }
 
     $field_name = $storage_definition->getName();
     $base_table = $this->storage->getBaseTable();
 
+    // Define the initial values, if any.
+    $initial_value = $initial_value_from_field = [];
+    $storage_definition_is_new = empty($this->loadFieldSchemaData($storage_definition));
+    if ($storage_definition_is_new && $storage_definition instanceof BaseFieldDefinition && $table_mapping->allowsSharedTableStorage($storage_definition)) {
+      if (($initial_storage_value = $storage_definition->getInitialValue()) && !empty($initial_storage_value)) {
+        $initial_value = $initial_storage_value[0];
+      }
+
+      if ($initial_value_field_name = $storage_definition->getInitialValueFromField()) {
+        // Check that the field used for populating initial values is valid. We
+        // must use the last installed version of that, as the new field might
+        // be created in an update function and the storage definition of the
+        // "from" field might get changed later.
+        $last_installed_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($this->entityType->id());
+        if (!isset($last_installed_storage_definitions[$initial_value_field_name])) {
+          throw new FieldException("Illegal initial value definition on {$storage_definition->getName()}: The field $initial_value_field_name does not exist.");
+        }
+
+        if ($storage_definition->getType() !== $last_installed_storage_definitions[$initial_value_field_name]->getType()) {
+          throw new FieldException("Illegal initial value definition on {$storage_definition->getName()}: The field types do not match.");
+        }
+
+        if (!$table_mapping->allowsSharedTableStorage($last_installed_storage_definitions[$initial_value_field_name])) {
+          throw new FieldException("Illegal initial value definition on {$storage_definition->getName()}: Both fields have to be stored in the shared entity tables.");
+        }
+
+        $initial_value_from_field = $table_mapping->getColumnNames($initial_value_field_name);
+      }
+    }
+
     // A shared table contains rows for entities where the field is empty
     // (since other fields stored in the same table might not be empty), thus
     // the only columns that can be 'not null' are those for required
-    // properties of required fields. However, even those would break in the
-    // case where a new field is added to a table that contains existing rows.
-    // For now, we only hardcode 'not null' to a couple "entity keys", in order
-    // to keep their indexes optimized.
-    // @todo Revisit once we have support for 'initial' in
-    //   https://www.drupal.org/node/2346019.
+    // properties of required fields. For now, we only hardcode 'not null' to a
+    // few "entity keys", in order to keep their indexes optimized.
+    // @todo Fix this in https://www.drupal.org/node/2841291.
     $not_null_keys = $this->entityType->getKeys();
     // Label fields are not necessarily required.
     unset($not_null_keys['label']);
@@ -1654,6 +1705,14 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st
 
       $schema['fields'][$schema_field_name] = $column_schema;
       $schema['fields'][$schema_field_name]['not null'] = in_array($field_name, $not_null_keys);
+
+      // Use the initial value of the field storage, if available.
+      if ($initial_value && isset($initial_value[$field_column_name])) {
+        $schema['fields'][$schema_field_name]['initial'] = $initial_value[$field_column_name];
+      }
+      elseif (!empty($initial_value_from_field)) {
+        $schema['fields'][$schema_field_name]['initial_from_field'] = $initial_value_from_field[$field_column_name];
+      }
     }
 
     if (!empty($field_schema['indexes'])) {
diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
index 37cc103..16df7c6 100644
--- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
@@ -510,6 +510,83 @@ public function setDefaultValueCallback($callback) {
   }
 
   /**
+   * Returns the initial value for the field.
+   *
+   * @return array
+   *   The initial value for the field, as a numerically indexed array of items,
+   *   each item being a property/value array (array() for no default value).
+   */
+  public function getInitialValue() {
+    $value = isset($this->definition['initial_value']) ? $this->definition['initial_value'] : [];
+
+    // Normalize into the "array keyed by delta" format.
+    if (isset($value) && !is_array($value)) {
+      $value = [
+        [$this->getMainPropertyName() => $value],
+      ];
+    }
+
+    return $value;
+  }
+
+  /**
+   * Sets an initial value for the field.
+   *
+   * @param mixed $value
+   *   The initial value for the field. This can be either:
+   *   - a literal, in which case it will be assigned to the first property of
+   *     the first item;
+   *   - a numerically indexed array of items, each item being a property/value
+   *     array;
+   *   - a non-numerically indexed array, in which case the array is assumed to
+   *     be a property/value array and used as the first item;
+   *   - an empty array for no initial value.
+   *
+   * @return $this
+   */
+  public function setInitialValue($value) {
+    if ($value === NULL) {
+      $value = [];
+    }
+    // Unless the value is an empty array, we may need to transform it.
+    if (!is_array($value) || !empty($value)) {
+      if (!is_array($value)) {
+        $value = [[$this->getMainPropertyName() => $value]];
+      }
+      elseif (is_array($value) && !is_numeric(array_keys($value)[0])) {
+        $value = [0 => $value];
+      }
+    }
+    $this->definition['initial_value'] = $value;
+
+    return $this;
+  }
+
+  /**
+   * Returns the name of the field that will be used for getting initial values.
+   *
+   * @return string|null
+   *   The field name.
+   */
+  public function getInitialValueFromField() {
+    return isset($this->definition['initial_value_from_field']) ? $this->definition['initial_value_from_field'] : NULL;
+  }
+
+  /**
+   * Sets a field that will be used for getting initial values.
+   *
+   * @param string $field_name
+   *   The name of the field that will be used for getting initial values.
+   *
+   * @return $this
+   */
+  public function setInitialValueFromField($field_name) {
+    $this->definition['initial_value_from_field'] = $field_name;
+
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
index bb17fe6..86202f6 100644
--- a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
+++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -70,6 +71,16 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+    // Entity type definition updates can change the schema by adding or
+    // removing entity tables (for example when switching an entity type from
+    // non-revisionable to revisionable), so CRUD operations on a field storage
+    // definition need to use the last installed entity type schema.
+    if ($storage instanceof SqlContentEntityStorage
+       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
+      $storage->setEntityType($last_installed_entity_type);
+    }
+
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionCreate($storage_definition);
     }
@@ -89,6 +100,16 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+    // Entity type definition updates can change the schema by adding or
+    // removing entity tables (for example when switching an entity type from
+    // non-revisionable to revisionable), so CRUD operations on a field storage
+    // definition need to use the last installed entity type schema.
+    if ($storage instanceof SqlContentEntityStorage
+       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
+      $storage->setEntityType($last_installed_entity_type);
+    }
+
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionUpdate($storage_definition, $original);
     }
@@ -108,6 +129,16 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+    // Entity type definition updates can change the schema by adding or
+    // removing entity tables (for example when switching an entity type from
+    // non-revisionable to revisionable), so CRUD operations on a field storage
+    // definition need to use the last installed entity type schema.
+    if ($storage instanceof SqlContentEntityStorage
+       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
+      $storage->setEntityType($last_installed_entity_type);
+    }
+
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionDelete($storage_definition);
     }
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 47241c2..c244fe4 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -184,3 +184,14 @@ function comment_update_8301() {
   $entity_type->set('entity_keys', $keys);
   $definition_update_manager->updateEntityType($entity_type);
 }
+
+/**
+ * Update the status field.
+ */
+function comment_update_8400() {
+  // The status field was promoted to an entity key in comment_update_8301(),
+  // which makes it NOT NULL in the default SQL storage, which means its storage
+  // definition needs to be updated as well.
+  $entity_definition_update_manager = \Drupal::service('entity.definition_update_manager');
+  $entity_definition_update_manager->updateFieldStorageDefinition($entity_definition_update_manager->getFieldStorageDefinition('status', 'comment'));
+}
diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install
index dc12d9f..67b161c 100644
--- a/core/modules/content_translation/content_translation.install
+++ b/core/modules/content_translation/content_translation.install
@@ -5,6 +5,8 @@
  * Installation functions for Content Translation module.
  */
 
+use \Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
+use \Drupal\Core\Language\LanguageInterface;
 use \Drupal\Core\Url;
 
 /**
@@ -44,3 +46,57 @@ function content_translation_update_8001() {
 function content_translation_update_8002() {
   \Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions();
 }
+
+/**
+ * Fix the initial values for content translation metadata fields.
+ */
+function content_translation_update_8400() {
+  $database = \Drupal::database();
+  /** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
+  $content_translation_manager = \Drupal::service('content_translation.manager');
+  /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
+  $last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
+  $entity_type_manager = \Drupal::entityTypeManager();
+  $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  foreach ($content_translation_manager->getSupportedEntityTypes() as $entity_type_id => $entity_type_definition) {
+    $storage = $entity_type_manager->getStorage($entity_type_id);
+    if ($storage instanceof SqlEntityStorageInterface) {
+      $entity_type = $entity_definition_update_manager->getEntityType($entity_type_id);
+      $storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type_id);
+
+      // Since the entity type is managed by Content Translation, we can assume
+      // that it is translatable, so we use the data and revision data tables.
+      $tables_to_update = [$entity_type->getDataTable()];
+      if ($entity_type->isRevisionable()) {
+        $tables_to_update += [$entity_type->getRevisionDataTable()];
+      }
+
+      foreach ($tables_to_update as $table_name) {
+        // Fix the values of the 'content_translation_source' field.
+        if (isset($storage_definitions['content_translation_source'])) {
+          $database->update($table_name)
+            ->fields(['content_translation_source' => LanguageInterface::LANGCODE_NOT_SPECIFIED])
+            ->isNull('content_translation_source')
+            ->execute();
+        }
+
+        // Fix the values of the 'content_translation_outdated' field.
+        if (isset($storage_definitions['content_translation_outdated'])) {
+          $database->update($table_name)
+            ->fields(['content_translation_outdated' => 0])
+            ->isNull('content_translation_outdated')
+            ->execute();
+        }
+
+        // Fix the values of the 'content_translation_status' field.
+        if (isset($storage_definitions['content_translation_status'])) {
+          $database->update($table_name)
+            ->fields(['content_translation_status' => 1])
+            ->isNull('content_translation_status')
+            ->execute();
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index c1688b9..3f7baa7 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -117,14 +117,16 @@ public function getFieldDefinitions() {
       ->setDescription(t('The source language from which this translation was created.'))
       ->setDefaultValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
       ->setRevisionable(TRUE)
-      ->setTranslatable(TRUE);
+      ->setTranslatable(TRUE)
+      ->setInitialValue(LanguageInterface::LANGCODE_NOT_SPECIFIED);
 
     $definitions['content_translation_outdated'] = BaseFieldDefinition::create('boolean')
       ->setLabel(t('Translation outdated'))
       ->setDescription(t('A boolean indicating whether this translation needs to be updated.'))
       ->setDefaultValue(FALSE)
       ->setRevisionable(TRUE)
-      ->setTranslatable(TRUE);
+      ->setTranslatable(TRUE)
+      ->setInitialValue(0);
 
     if (!$this->hasAuthor()) {
       $definitions['content_translation_uid'] = BaseFieldDefinition::create('entity_reference')
@@ -143,7 +145,8 @@ public function getFieldDefinitions() {
         ->setDescription(t('A boolean indicating whether the translation is visible to non-translators.'))
         ->setDefaultValue(TRUE)
         ->setRevisionable(TRUE)
-        ->setTranslatable(TRUE);
+        ->setTranslatable(TRUE)
+        ->setInitialValue(1);
     }
 
     if (!$this->hasCreatedTime()) {
diff --git a/core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php b/core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php
new file mode 100644
index 0000000..85b952c
--- /dev/null
+++ b/core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\content_translation\Tests\Update;
+
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+
+/**
+ * Tests the upgrade path for the Content Translation module.
+ *
+ * @group Update
+ */
+class ContentTranslationUpdateTest extends UpdatePathTestBase {
+
+  use EntityDefinitionTestTrait;
+
+  /**
+   * The database connection used.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The entity definition update manager.
+   *
+   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   */
+  protected $entityDefinitionUpdateManager;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->database = \Drupal::database();
+    $this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
+    $this->entityManager = \Drupal::entityManager();
+    $this->state = \Drupal::state();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
+    ];
+  }
+
+  /**
+   * Tests that initial values for metadata fields are populated correctly.
+   */
+  public function testContentTranslationUpdate8400() {
+    $this->updateEntityTypeToTranslatable();
+
+    // The test database dump contains NULL values for
+    // 'content_translation_source', 'content_translation_outdated' and
+    // 'content_translation_status' for the first 50 test entities.
+    // @see _entity_test_update_create_test_entities()
+    $first_entity_record = $this->database->select('entity_test_update_data', 'etud')
+      ->fields('etud')
+      ->condition('etud.id', 1)
+      ->execute()
+      ->fetchAllAssoc('id');
+    $this->assertNull($first_entity_record[1]->content_translation_source);
+    $this->assertNull($first_entity_record[1]->content_translation_outdated);
+    $this->assertNull($first_entity_record[1]->content_translation_status);
+
+    $this->runUpdates();
+
+    // After running the updates, all those fields should be populated with
+    // their default values.
+    $first_entity_record = $this->database->select('entity_test_update_data', 'etud')
+      ->fields('etud')
+      ->condition('etud.id', 1)
+      ->execute()
+      ->fetchAllAssoc('id');
+    $this->assertEqual(LanguageInterface::LANGCODE_NOT_SPECIFIED, $first_entity_record[1]->content_translation_source);
+    $this->assertEqual(0, $first_entity_record[1]->content_translation_outdated);
+    $this->assertEqual(1, $first_entity_record[1]->content_translation_status);
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Update/EntityUpdateToRevisionableAndPublishableTest.php b/core/modules/system/src/Tests/Update/EntityUpdateToRevisionableAndPublishableTest.php
new file mode 100644
index 0000000..8f480d1
--- /dev/null
+++ b/core/modules/system/src/Tests/Update/EntityUpdateToRevisionableAndPublishableTest.php
@@ -0,0 +1,216 @@
+<?php
+
+namespace Drupal\system\Tests\Update;
+
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+
+/**
+ * Tests the upgrade path for making an entity revisionable and publishable.
+ *
+ * @see https://www.drupal.org/node/2841291
+ *
+ * @group Update
+ */
+class EntityUpdateToRevisionableAndPublishableTest extends UpdatePathTestBase {
+
+  use EntityDefinitionTestTrait;
+  use DbUpdatesTrait;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The entity definition update manager.
+   *
+   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   */
+  protected $entityDefinitionUpdateManager;
+
+  /**
+   * The last installed schema repository service.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   */
+  protected $lastInstalledSchemaRepository;
+
+  /**
+   * The key-value collection for tracking installed storage schema.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $installedStorageSchema;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->entityManager = \Drupal::entityManager();
+    $this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
+    $this->lastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository');
+    $this->installedStorageSchema = \Drupal::keyValue('entity.storage_schema.sql');
+    $this->state = \Drupal::state();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.entity-test-schema-converter-enabled.php',
+    ];
+  }
+
+  /**
+   * Tests the conversion of an entity type to revisionable and publishable.
+   */
+  public function testConvertToRevisionableAndPublishable() {
+    // Check that entity type is not revisionable nor publishable prior to
+    // running the update process.
+    $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+    $this->assertFalse($entity_test_update->isRevisionable());
+    $this->assertFalse($entity_test_update->getKey('published'));
+
+    // Make the entity type revisionable, translatable and publishable.
+    $this->updateEntityTypeDefinition();
+
+    $this->enableUpdates('entity_test_update', 'entity_rev_pub_updates', 8400);
+    $this->runUpdates();
+
+    /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_test_update */
+    $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+    $this->assertTrue($entity_test_update->isRevisionable());
+    $this->assertEqual('status', $entity_test_update->getKey('published'));
+
+    /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
+    $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+    $this->assertEqual(count($storage->loadMultiple()), 102, 'All test entities were found.');
+
+    // The conversion to revisionable is already tested by
+    // \Drupal\system\Tests\Entity\Update\SqlContentEntityStorageSchemaConverterTest::testMakeRevisionable()
+    // so we only need to check that some special cases are handled.
+    // All the checks implemented here are taking into consideration the special
+    // conditions in which the test database was created.
+    // @see _entity_test_update_create_test_entities()
+
+    // The test entity with ID 50 was created before Content Translation was
+    // enabled, which means it didn't have a 'content_translation_status' field.
+    // content_translation_update_8400() added values for that field which
+    // should now be reflected in the entity's 'status' field.
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
+    $revision = $storage->loadRevision(50);
+    $this->assertEqual(1, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(1, $translation->status->value);
+
+    // The test entity with ID 100 was created with Content Translation enabled
+    // and it should have the same values as entity 50.
+    $revision = $storage->loadRevision(100);
+    $this->assertEqual(1, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(1, $translation->status->value);
+
+    // The test entity 101 had 'content_translation_status' set to 0 for the
+    // English (source) language.
+    $revision = $storage->loadRevision(101);
+    $this->assertEqual(0, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(1, $translation->status->value);
+
+    // The test entity 102 had 'content_translation_status' set to 0 for the
+    // Romanian language.
+    $revision = $storage->loadRevision(102);
+    $this->assertEqual(1, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(0, $translation->status->value);
+  }
+
+  /**
+   * Updates the 'entity_test_update' entity type to revisionable,
+   * translatable, publishable and adds revision metadata keys.
+   */
+  protected function updateEntityTypeDefinition() {
+    $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+    $keys = $entity_type->getKeys();
+    $keys['revision'] = 'revision_id';
+    $keys['published'] = 'status';
+    $entity_type->set('entity_keys', $keys);
+
+    $revision_metadata_keys = [
+      'revision_user' => 'revision_user',
+      'revision_created' => 'revision_created',
+      'revision_log_message' => 'revision_log_message'
+    ];
+    $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
+
+
+    $entity_type->set('translatable', TRUE);
+    $entity_type->set('data_table', 'entity_test_update_data');
+    $entity_type->set('revision_table', 'entity_test_update_revision');
+    $entity_type->set('revision_data_table', 'entity_test_update_revision_data');
+
+    $this->state->set('entity_test_update.entity_type', $entity_type);
+
+    // Also add the status and revision metadata base fields to the entity type.
+    $status = BaseFieldDefinition::create('boolean')
+      ->setLabel(t('Publishing status'))
+      ->setDescription(t('A boolean indicating the published state.'))
+      ->setRevisionable(TRUE)
+      ->setTranslatable(TRUE)
+      ->setRequired(TRUE)
+      ->setDefaultValue(TRUE);
+
+    $revision_created = BaseFieldDefinition::create('created')
+      ->setLabel(t('Revision create time'))
+      ->setDescription(t('The time that the current revision was created.'))
+      ->setRevisionable(TRUE);
+
+    $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);
+
+    $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->state->set('entity_test_update.additional_base_field_definitions', [
+      'status' => $status,
+      'revision_created' => $revision_created,
+      'revision_user' => $revision_user,
+      'revision_log_message' => $revision_log_message,
+    ]);
+
+    $this->entityManager->clearCachedDefinitions();
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestUpdate.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestUpdate.php
deleted file mode 100644
index 6d72f7a..0000000
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestUpdate.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-namespace Drupal\entity_test\Entity;
-
-use Drupal\Core\Entity\EntityTypeInterface;
-
-/**
- * Defines the test entity class for testing definition updates.
- *
- * This entity type starts out non-revisionable by lacking a "revision_id" key,
- * but during an update test, can be made revisionable by adding that key.
- *
- * @ContentEntityType(
- *   id = "entity_test_update",
- *   label = @Translation("Test entity update"),
- *   handlers = {
- *     "storage_schema" = "Drupal\entity_test\EntityTestStorageSchema"
- *   },
- *   base_table = "entity_test_update",
- *   revision_table = "entity_test_update_revision",
- *   persistent_cache = FALSE,
- *   entity_keys = {
- *     "id" = "id",
- *     "uuid" = "uuid",
- *     "bundle" = "type",
- *     "label" = "name",
- *     "langcode" = "langcode",
- *   }
- * )
- */
-class EntityTestUpdate extends EntityTestRev {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
-    $fields = parent::baseFieldDefinitions($entity_type);
-    $fields += \Drupal::state()->get('entity_test_update.additional_base_field_definitions', []);
-    return $fields;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
-    $fields = parent::bundleFieldDefinitions($entity_type, $bundle, $base_field_definitions);
-    $fields += \Drupal::state()->get('entity_test_update.additional_bundle_field_definitions.' . $bundle, []);
-    return $fields;
-  }
-
-}
diff --git a/core/modules/system/tests/modules/entity_test_update/entity_test_update.install b/core/modules/system/tests/modules/entity_test_update/entity_test_update.install
new file mode 100644
index 0000000..1423308
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test_update/entity_test_update.install
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the Entity Test Update module.
+ */
+
+use Drupal\system\Tests\Update\DbUpdatesTrait;
+
+DbUpdatesTrait::includeUpdates('entity_test_update', 'entity_rev_pub_updates');
diff --git a/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc b/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
new file mode 100644
index 0000000..51ad4cc
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Defines the 8400 db update for the "entity_rev_pub_updates" group.
+ */
+
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function entity_test_update_update_dependencies() {
+  // The update function that adds the status field must run after
+  // content_translation_update_8400() which fixes NULL values for the
+  // 'content_translation_status' field.
+  $dependencies['entity_test_update'][8400] = [
+    'content_translation' => 8400,
+  ];
+
+  return $dependencies;
+}
+
+/**
+ * Add the 'published' and revisionable metadata fields to entity_test_update.
+ */
+function entity_test_update_update_8400() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  // Add the published entity key and revisionable metadata fields to the
+  // entity_test_update entity type.
+  $entity_type = $definition_update_manager->getEntityType('entity_test_update');
+
+  $entity_keys = $entity_type->getKeys();
+  $entity_keys['published'] = 'status';
+  $entity_type->set('entity_keys', $entity_keys);
+
+  $revision_metadata_keys = [
+    'revision_user' => 'revision_user',
+    'revision_created' => 'revision_created',
+    'revision_log_message' => 'revision_log_message'
+  ];
+  $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
+
+  $definition_update_manager->updateEntityType($entity_type);
+
+  // Add the status field.
+  $status = BaseFieldDefinition::create('boolean')
+    ->setLabel(t('Publishing status'))
+    ->setDescription(t('A boolean indicating the published state.'))
+    ->setRevisionable(TRUE)
+    ->setTranslatable(TRUE)
+    ->setRequired(TRUE)
+    ->setDefaultValue(TRUE);
+
+  $has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'entity_test_update');
+  if ($has_content_translation_status_field) {
+    $status->setInitialValueFromField('content_translation_status');
+  }
+  else {
+    $status->setInitialValue(1);
+  }
+  $definition_update_manager->installFieldStorageDefinition('status', 'entity_test_update', 'entity_test_update', $status);
+
+  // Add the revision metadata fields.
+  $revision_created = BaseFieldDefinition::create('created')
+    ->setLabel(t('Revision create time'))
+    ->setDescription(t('The time that the current revision was created.'))
+    ->setRevisionable(TRUE);
+  $definition_update_manager->installFieldStorageDefinition('revision_created', 'entity_test_update', 'entity_test_update', $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);
+  $definition_update_manager->installFieldStorageDefinition('revision_user', 'entity_test_update', 'entity_test_update', $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,
+      ],
+    ]);
+  $definition_update_manager->installFieldStorageDefinition('revision_log_message', 'entity_test_update', 'entity_test_update', $revision_log_message);
+
+  // Uninstall the 'content_translation_status' field if needed.
+  $database = \Drupal::database();
+  if ($has_content_translation_status_field) {
+    // First we have to remove the field data.
+    $database->update($entity_type->getDataTable())
+      ->fields(['content_translation_status' => NULL])
+      ->execute();
+
+    // A site may have disabled revisionability for this entity type.
+    if ($entity_type->isRevisionable()) {
+      $database->update($entity_type->getRevisionDataTable())
+        ->fields(['content_translation_status' => NULL])
+        ->execute();
+    }
+
+    $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'entity_test_update');
+    $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
+  }
+}
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index 03491ab..655fa05 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -2,8 +2,7 @@
 
 namespace Drupal\taxonomy\Entity;
 
-use Drupal\Core\Entity\ContentEntityBase;
-use Drupal\Core\Entity\EntityChangedTrait;
+use Drupal\Core\Entity\EditorialContentEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
@@ -30,14 +29,23 @@
  *   },
  *   base_table = "taxonomy_term_data",
  *   data_table = "taxonomy_term_field_data",
+ *   revision_table = "taxonomy_term_revision",
+ *   revision_data_table = "taxonomy_term_field_revision",
  *   uri_callback = "taxonomy_term_uri",
  *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "tid",
+ *     "revision" = "revision_id",
  *     "bundle" = "vid",
  *     "label" = "name",
  *     "langcode" = "langcode",
- *     "uuid" = "uuid"
+ *     "uuid" = "uuid",
+ *     "published" = "status",
+ *   },
+ *   revision_metadata_keys = {
+ *     "revision_user" = "revision_user",
+ *     "revision_created" = "revision_created",
+ *     "revision_log_message" = "revision_log_message",
  *   },
  *   bundle_entity_type = "taxonomy_vocabulary",
  *   field_ui_base_route = "entity.taxonomy_vocabulary.overview_form",
@@ -51,9 +59,7 @@
  *   permission_granularity = "bundle"
  * )
  */
-class Term extends ContentEntityBase implements TermInterface {
-
-  use EntityChangedTrait;
+class Term extends EditorialContentEntityBase implements TermInterface {
 
   /**
    * {@inheritdoc}
@@ -118,6 +124,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['name'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Name'))
       ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
       ->setRequired(TRUE)
       ->setSetting('max_length', 255)
       ->setDisplayOptions('view', [
@@ -134,6 +141,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['description'] = BaseFieldDefinition::create('text_long')
       ->setLabel(t('Description'))
       ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
       ->setDisplayOptions('view', [
         'label' => 'hidden',
         'type' => 'text_default',
@@ -161,7 +169,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['changed'] = BaseFieldDefinition::create('changed')
       ->setLabel(t('Changed'))
       ->setDescription(t('The time that the term was last edited.'))
-      ->setTranslatable(TRUE);
+      ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE);
 
     return $fields;
   }
diff --git a/core/modules/taxonomy/src/TermStorageSchema.php b/core/modules/taxonomy/src/TermStorageSchema.php
index 5bcb088..822d1f4 100644
--- a/core/modules/taxonomy/src/TermStorageSchema.php
+++ b/core/modules/taxonomy/src/TermStorageSchema.php
@@ -17,7 +17,7 @@ class TermStorageSchema extends SqlContentEntityStorageSchema {
   protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
     $schema = parent::getEntitySchema($entity_type, $reset = FALSE);
 
-    $schema['taxonomy_term_field_data']['indexes'] += [
+    $schema[$entity_type->getDataTable()]['indexes'] += [
       'taxonomy_term__tree' => ['vid', 'weight', 'name'],
       'taxonomy_term__vid_name' => ['vid', 'name'],
     ];
diff --git a/core/modules/taxonomy/src/Tests/Update/TaxonomyUpdateTest.php b/core/modules/taxonomy/src/Tests/Update/TaxonomyUpdateTest.php
new file mode 100644
index 0000000..5ab153c
--- /dev/null
+++ b/core/modules/taxonomy/src/Tests/Update/TaxonomyUpdateTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\taxonomy\Tests\Update;
+
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+
+/**
+ * Tests the upgrade path for taxonomy terms.
+ *
+ * @group Update
+ */
+class TaxonomyUpdateTest extends UpdatePathTestBase  {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.filled.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests the conversion of taxonomy terms to be revisionable and publishable.
+   *
+   * @see taxonomy_update_8400()
+   * @see taxonomy_post_update_make_taxonomy_term_revisionable()
+   */
+  public function testConversionToRevisionableAndPublishable() {
+    $this->runUpdates();
+
+    // Make sure our vocabulary exists.
+    $this->drupalGet('admin/structure/taxonomy/manage/test_vocabulary/overview');
+
+    // Make sure our terms exist.
+    $this->assertText('Test root term');
+    $this->assertText('Test child term');
+    $this->drupalGet('taxonomy/term/3');
+    $this->assertResponse('200');
+
+    // Make sure the terms are still translated.
+    $this->drupalGet('taxonomy/term/2/translations');
+    $this->assertLink('Test root term - Spanish');
+
+    // Check that taxonomy terms can be created, saved and then loaded.
+    $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
+    /** @var \Drupal\taxonomy\Entity\Term $term */
+    $term = $storage->create([
+      'name' => 'Test term',
+      'vid' => 'article',
+    ]);
+    $term->save();
+
+    $storage->resetCache();
+    $term = $storage->load($term->id());
+
+    $this->assertEqual('Test term', $term->label());
+    $this->assertEqual('article', $term->bundle());
+    $this->assertTrue($term->isPublished());
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php
index bfd65cf..08ff643 100644
--- a/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php
+++ b/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\node\Entity\Node;
 use Drupal\user\Entity\Role;
 use Drupal\user\RoleInterface;
 use Drupal\views\Views;
@@ -124,6 +125,11 @@ public function testTaxonomyTermView() {
     // query anymore.
     // @see \Drupal\views\Plugin\views\filter\LanguageFilter::query()
     $node->delete();
+    // We also have to remove the nodes created by the parent ::setUp() method
+    // if we want to be able to uninstall the Content Translation module.
+    foreach (Node::loadMultiple() as $node) {
+      $node->delete();
+    }
     \Drupal::service('module_installer')->uninstall(['content_translation', 'language']);
 
     $view = Views::getView('taxonomy_term');
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
new file mode 100644
index 0000000..5e19b02
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.install
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the Taxonomy module.
+ */
+
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function taxonomy_update_dependencies() {
+  // The update function that adds the status field must run after
+  // content_translation_update_8400() which fixes NULL values for the
+  // 'content_translation_status' field.
+  $dependencies['taxonomy'][8400] = [
+    'content_translation' => 8400,
+  ];
+
+  return $dependencies;
+}
+
+/**
+ * Add the 'published' and revisionable metadata fields to taxonomy terms.
+ */
+function taxonomy_update_8400() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  // Add the published entity key and revisionable metadata fields to the
+  // taxonomy_term entity type.
+  $entity_type = $definition_update_manager->getEntityType('taxonomy_term');
+
+  $entity_keys = $entity_type->getKeys();
+  $entity_keys['published'] = 'status';
+  $entity_type->set('entity_keys', $entity_keys);
+
+  $revision_metadata_keys = [
+    'revision_user' => 'revision_user',
+    'revision_created' => 'revision_created',
+    'revision_log_message' => 'revision_log_message'
+  ];
+  $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
+
+  $definition_update_manager->updateEntityType($entity_type);
+
+  // Add the status field.
+  $status = BaseFieldDefinition::create('boolean')
+    ->setLabel(t('Publishing status'))
+    ->setDescription(t('A boolean indicating the published state.'))
+    ->setRevisionable(TRUE)
+    ->setTranslatable(TRUE)
+    ->setDefaultValue(TRUE);
+
+  $has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
+  if ($has_content_translation_status_field) {
+    $status->setInitialValueFromField('content_translation_status');
+  }
+  else {
+    $status->setInitialValue(1);
+  }
+  $definition_update_manager->installFieldStorageDefinition('status', 'taxonomy_term', 'taxonomy_term', $status);
+
+  // Add the revision metadata fields.
+  $revision_created = BaseFieldDefinition::create('created')
+    ->setLabel(t('Revision create time'))
+    ->setDescription(t('The time that the current revision was created.'))
+    ->setRevisionable(TRUE);
+  $definition_update_manager->installFieldStorageDefinition('revision_created', 'taxonomy_term', 'taxonomy_term', $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);
+  $definition_update_manager->installFieldStorageDefinition('revision_user', 'taxonomy_term', 'taxonomy_term', $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,
+      ],
+    ]);
+  $definition_update_manager->installFieldStorageDefinition('revision_log_message', 'taxonomy_term', 'taxonomy_term', $revision_log_message);
+
+  // Uninstall the 'content_translation_status' field if needed.
+  $database = \Drupal::database();
+  if ($has_content_translation_status_field) {
+    // First we have to remove the field data.
+    $database->update($entity_type->getDataTable())
+      ->fields(['content_translation_status' => NULL])
+      ->execute();
+
+    // A site may have disabled revisionability for this entity type.
+    if ($entity_type->isRevisionable()) {
+      $database->update($entity_type->getRevisionDataTable())
+        ->fields(['content_translation_status' => NULL])
+        ->execute();
+    }
+
+    $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
+    $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
+  }
+
+  return t('Taxonomy terms have been converted to revisionable and publishable.');
+}
diff --git a/core/modules/taxonomy/taxonomy.post_update.php b/core/modules/taxonomy/taxonomy.post_update.php
new file mode 100644
index 0000000..993d260
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.post_update.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Post update functions for the Taxonomy module.
+ */
+
+use \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchemaConverter;
+
+/**
+ * Update taxonomy terms to be revisionable.
+ */
+function taxonomy_post_update_make_taxonomy_term_revisionable(&$sandbox) {
+  $schema_converter = new SqlContentEntityStorageSchemaConverter(
+    'taxonomy_term',
+    \Drupal::entityTypeManager(),
+    \Drupal::entityDefinitionUpdateManager(),
+    \Drupal::service('entity.last_installed_schema.repository'),
+    \Drupal::keyValue('entity.storage_schema.sql'),
+    \Drupal::database()
+  );
+
+  $schema_converter->convertToRevisionable(
+    $sandbox,
+    [
+      'name',
+      'description',
+      'changed',
+    ]
+  );
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
index 646ad2e..a066616 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityTypeEvents;
 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldException;
 use Drupal\Core\Field\FieldStorageDefinitionEvents;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\entity_test_update\Entity\EntityTestUpdate;
@@ -776,6 +777,11 @@ public function testBaseFieldEntityKeyUpdateWithExistingData() {
     // of a NOT NULL constraint.
     $this->makeBaseFieldEntityKey();
 
+    // Field storage CRUD operations use the last installed entity type
+    // definition so we need to update it before doing any other field storage
+    // updates.
+    $this->entityDefinitionUpdateManager->updateEntityType($this->state->get('entity_test_update.entity_type'));
+
     // Try to apply the update and verify they fail since we have a NULL value.
     $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
     try {
@@ -817,4 +823,119 @@ public function testLongNameFieldIndexes() {
     $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.');
   }
 
+  /**
+   * Tests adding a base field with initial values.
+   */
+  public function testInitialValue() {
+    $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+    $db_schema = $this->database->schema();
+
+    // Create two entities before adding the base field.
+    /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
+    $storage->create()->save();
+    $storage->create()->save();
+
+    // Add a base field with an initial value.
+    $this->addBaseField();
+    $storage_definition = BaseFieldDefinition::create('string')
+      ->setLabel(t('A new base field'))
+      ->setInitialValue('test value');
+
+    $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
+    $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
+    $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
+
+    // Check that the initial values have been applied.
+    $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+    $entities = $storage->loadMultiple();
+    $this->assertEquals('test value', $entities[1]->get('new_base_field')->value);
+    $this->assertEquals('test value', $entities[2]->get('new_base_field')->value);
+  }
+
+  /**
+   * Tests adding a base field with initial values inherited from another field.
+   */
+  public function testInitialValueFromField() {
+    $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+    $db_schema = $this->database->schema();
+
+    // Create two entities before adding the base field.
+    /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
+    $storage->create(['name' => 'First entity'])->save();
+    $storage->create(['name' => 'Second entity'])->save();
+
+    // Add a base field with an initial value inherited from another field.
+    $this->addBaseField();
+    $storage_definition = BaseFieldDefinition::create('string')
+      ->setLabel(t('A new base field'))
+      ->setInitialValueFromField('name');
+
+    $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
+    $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
+    $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
+
+    // Check that the initial values have been applied.
+    $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+    $entities = $storage->loadMultiple();
+    $this->assertEquals('First entity', $entities[1]->get('new_base_field')->value);
+    $this->assertEquals('Second entity', $entities[2]->get('new_base_field')->value);
+  }
+
+  /**
+   * Tests the error handling when using initial values from another field.
+   */
+  public function testInitialValueFromFieldErrorHandling() {
+    // Check that setting invalid values for 'initial value from field' doesn't
+    // work.
+    try {
+      $this->addBaseField();
+      $storage_definition = BaseFieldDefinition::create('string')
+        ->setLabel(t('A new base field'))
+        ->setInitialValueFromField('field_that_does_not_exist');
+      $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
+      $this->fail('Using a non-existent field as initial value does not work.');
+    }
+    catch (FieldException $e) {
+      $this->assertEquals('Illegal initial value definition on new_base_field: The field field_that_does_not_exist does not exist.', $e->getMessage());
+      $this->pass('Using a non-existent field as initial value does not work.');
+    }
+
+    try {
+      $this->addBaseField();
+      $storage_definition = BaseFieldDefinition::create('integer')
+        ->setLabel(t('A new base field'))
+        ->setInitialValueFromField('name');
+      $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
+      $this->fail('Using a field of a different type as initial value does not work.');
+    }
+    catch (FieldException $e) {
+      $this->assertEquals('Illegal initial value definition on new_base_field: The field types do not match.', $e->getMessage());
+      $this->pass('Using a field of a different type as initial value does not work.');
+    }
+
+    try {
+      // Add a base field that will not be stored in the shared tables.
+      $initial_field = BaseFieldDefinition::create('string')
+        ->setName('initial_field')
+        ->setLabel(t('An initial field'))
+        ->setCardinality(2);
+      $this->state->set('entity_test_update.additional_base_field_definitions', ['initial_field' => $initial_field]);
+      $this->entityDefinitionUpdateManager->installFieldStorageDefinition('initial_field', 'entity_test_update', 'entity_test', $initial_field);
+
+      // Now add the base field which will try to use the previously added field
+      // as the source of its initial values.
+      $new_base_field = BaseFieldDefinition::create('string')
+        ->setName('new_base_field')
+        ->setLabel(t('A new base field'))
+        ->setInitialValueFromField('initial_field');
+      $this->state->set('entity_test_update.additional_base_field_definitions', ['initial_field' => $initial_field, 'new_base_field' => $new_base_field]);
+      $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $new_base_field);
+      $this->fail('Using a field that is not stored in the shared tables as initial value does not work.');
+    }
+    catch (FieldException $e) {
+      $this->assertEquals('Illegal initial value definition on new_base_field: Both fields have to be stored in the shared entity tables.', $e->getMessage());
+      $this->pass('Using a field that is not stored in the shared tables as initial value does not work.');
+    }
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php
index 341ed9c..616f6ba 100644
--- a/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php
@@ -195,6 +195,52 @@ public function testFieldDefaultValue() {
   }
 
   /**
+   * Tests field initial value.
+   *
+   * @covers ::getInitialValue
+   * @covers ::setInitialValue
+   */
+  public function testFieldInitialValue() {
+    $definition = BaseFieldDefinition::create($this->fieldType);
+    $default_value = [
+      'value' => $this->randomMachineName(),
+    ];
+    $expected_default_value = [$default_value];
+    $definition->setInitialValue($default_value);
+    $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
+      ->disableOriginalConstructor()
+      ->getMock();
+    // Set the field item list class to be used to avoid requiring the typed
+    // data manager to retrieve it.
+    $definition->setClass('Drupal\Core\Field\FieldItemList');
+    $this->assertEquals($expected_default_value, $definition->getInitialValue($entity));
+
+    $data_definition = $this->getMockBuilder('Drupal\Core\TypedData\DataDefinition')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $data_definition->expects($this->any())
+      ->method('getClass')
+      ->will($this->returnValue('Drupal\Core\Field\FieldItemBase'));
+    $definition->setItemDefinition($data_definition);
+
+    // Set default value only with a literal.
+    $definition->setInitialValue($default_value['value']);
+    $this->assertEquals($expected_default_value, $definition->getInitialValue($entity));
+
+    // Set default value with an indexed array.
+    $definition->setInitialValue($expected_default_value);
+    $this->assertEquals($expected_default_value, $definition->getInitialValue($entity));
+
+    // Set default value with an empty array.
+    $definition->setInitialValue([]);
+    $this->assertEquals([], $definition->getInitialValue($entity));
+
+    // Set default value with NULL.
+    $definition->setInitialValue(NULL);
+    $this->assertEquals([], $definition->getInitialValue($entity));
+  }
+
+  /**
    * Tests field translatable methods.
    *
    * @covers ::isTranslatable
