diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php
index e4a7d648b31925bb21e2da4ea2b6903321870abf..d5b0a0200ba48b6f36b97cad5a7b715635066f9b 100644
--- a/core/lib/Drupal/Core/Config/ConfigManager.php
+++ b/core/lib/Drupal/Core/Config/ConfigManager.php
@@ -22,6 +22,7 @@
 class ConfigManager implements ConfigManagerInterface {
   use StringTranslationTrait;
   use DeprecatedServicePropertyTrait;
+  use StorageCopyTrait;
 
   /**
    * {@inheritdoc}
@@ -207,23 +208,7 @@ public function diff(StorageInterface $source_storage, StorageInterface $target_
    * {@inheritdoc}
    */
   public function createSnapshot(StorageInterface $source_storage, StorageInterface $snapshot_storage) {
-    // Empty the snapshot of all configuration.
-    $snapshot_storage->deleteAll();
-    foreach ($snapshot_storage->getAllCollectionNames() as $collection) {
-      $snapshot_collection = $snapshot_storage->createCollection($collection);
-      $snapshot_collection->deleteAll();
-    }
-    foreach ($source_storage->listAll() as $name) {
-      $snapshot_storage->write($name, $source_storage->read($name));
-    }
-    // Copy collections as well.
-    foreach ($source_storage->getAllCollectionNames() as $collection) {
-      $source_collection = $source_storage->createCollection($collection);
-      $snapshot_collection = $snapshot_storage->createCollection($collection);
-      foreach ($source_collection->listAll() as $name) {
-        $snapshot_collection->write($name, $source_collection->read($name));
-      }
-    }
+    self::replaceStorageContents($source_storage, $snapshot_storage);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Config/StorageCopyTrait.php b/core/lib/Drupal/Core/Config/StorageCopyTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..9cd80199a0886dacb0a057b5f5264f3880845e52
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/StorageCopyTrait.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\Core\Config;
+
+/**
+ * Utility trait to copy configuration from one storage to another.
+ */
+trait StorageCopyTrait {
+
+  /**
+   * Copy the configuration from one storage to another and remove stale items.
+   *
+   * This method empties target storage and copies all collections from source.
+   * Configuration is only copied and not imported, should not be used
+   * with the active storage as the target.
+   *
+   * @param \Drupal\Core\Config\StorageInterface $source
+   *   The configuration storage to copy from.
+   * @param \Drupal\Core\Config\StorageInterface $target
+   *   The configuration storage to copy to.
+   */
+  protected static function replaceStorageContents(StorageInterface $source, StorageInterface &$target) {
+    // Make sure there is no stale configuration in the target storage.
+    foreach (array_merge([StorageInterface::DEFAULT_COLLECTION], $target->getAllCollectionNames()) as $collection) {
+      $target->createCollection($collection)->deleteAll();
+    }
+
+    // Copy all the configuration from all the collections.
+    foreach (array_merge([StorageInterface::DEFAULT_COLLECTION], $source->getAllCollectionNames()) as $collection) {
+      $source_collection = $source->createCollection($collection);
+      $target_collection = $target->createCollection($collection);
+      foreach ($source_collection->listAll() as $name) {
+        $target_collection->write($name, $source_collection->read($name));
+      }
+    }
+
+    // Make sure that the target is set to the same collection as the source.
+    $target = $target->createCollection($source->getCollectionName());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/ConfigTestTrait.php b/core/tests/Drupal/Tests/ConfigTestTrait.php
index 5bab85949fed9fbac5121c666dd85dee16f32b91..45a7c728bf509f1e71e07a9e9196ae6d2cfb2c6d 100644
--- a/core/tests/Drupal/Tests/ConfigTestTrait.php
+++ b/core/tests/Drupal/Tests/ConfigTestTrait.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Config\ConfigImporter;
 use Drupal\Core\Config\StorageComparer;
+use Drupal\Core\Config\StorageCopyTrait;
 use Drupal\Core\Config\StorageInterface;
 
 /**
@@ -11,6 +12,8 @@
  */
 trait ConfigTestTrait {
 
+  use StorageCopyTrait;
+
   /**
    * Returns a ConfigImporter object to import test configuration.
    *
@@ -49,10 +52,7 @@ protected function configImporter() {
    *   The target config storage service.
    */
   protected function copyConfig(StorageInterface $source_storage, StorageInterface $target_storage) {
-    $target_storage->deleteAll();
-    foreach ($source_storage->listAll() as $name) {
-      $target_storage->write($name, $source_storage->read($name));
-    }
+    static::replaceStorageContents($source_storage, $target_storage);
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Config/StorageCopyTraitTest.php b/core/tests/Drupal/Tests/Core/Config/StorageCopyTraitTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb00129a20b4efe0416623f2ad220b2d54deaabe
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Config/StorageCopyTraitTest.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Drupal\Tests\Core\Config;
+
+use Drupal\Core\Config\MemoryStorage;
+use Drupal\Core\Config\StorageCopyTrait;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Config\StorageCopyTrait
+ * @group Config
+ */
+class StorageCopyTraitTest extends UnitTestCase {
+
+  use StorageCopyTrait;
+
+  /**
+   * @covers ::replaceStorageContents
+   *
+   * @dataProvider providerTestReplaceStorageContents
+   */
+  public function testReplaceStorageContents($source_collections, $target_collections) {
+    $source = new MemoryStorage();
+    $target = new MemoryStorage();
+    // Empty the storage should be the same.
+    $this->assertArrayEquals(self::toArray($source), self::toArray($target));
+
+    // When the source is populated, they are not the same any more.
+    $this->generateRandomData($source, $source_collections);
+    $this->assertNotEquals(self::toArray($source), self::toArray($target));
+
+    // When the target is filled with random data they are also not the same.
+    $this->generateRandomData($target, $target_collections);
+    $this->assertNotEquals(self::toArray($source), self::toArray($target));
+
+    // Set the active collection to a random one on both source and target.
+    if ($source_collections) {
+      $collections = $source->getAllCollectionNames();
+      $source = $source->createCollection($collections[array_rand($collections)]);
+    }
+    if ($target_collections) {
+      $collections = $target->getAllCollectionNames();
+      $target = $target->createCollection($collections[array_rand($collections)]);
+    }
+
+    $source_data = self::toArray($source);
+    $source_name = $source->getCollectionName();
+
+    // After copying they are the same, this asserts that items not present
+    // in the source get removed from the target.
+    self::replaceStorageContents($source, $target);
+    $this->assertArrayEquals($source_data, self::toArray($target));
+    // Assert that the copy method did indeed not change the source.
+    $this->assertArrayEquals($source_data, self::toArray($source));
+
+    // Assert that the active collection is the same as the original source.
+    $this->assertEquals($source_name, $source->getCollectionName());
+    $this->assertEquals($source_name, $target->getCollectionName());
+  }
+
+  /**
+   * Provides data for testCheckRequirements().
+   */
+  public function providerTestReplaceStorageContents() {
+    $data = [];
+    $data[] = [TRUE, TRUE];
+    $data[] = [TRUE, FALSE];
+    $data[] = [FALSE, TRUE];
+    $data[] = [FALSE, FALSE];
+
+    return $data;
+  }
+
+  /**
+   * Get the protected config data out of a MemoryStorage.
+   *
+   * @param \Drupal\Core\Config\MemoryStorage $storage
+   *   The config storage to extract the data from.
+   *
+   * @return array
+   */
+  protected static function toArray(MemoryStorage $storage) {
+    $reflection = new \ReflectionObject($storage);
+    $property = $reflection->getProperty('config');
+    $property->setAccessible(TRUE);
+
+    return $property->getValue($storage)->getArrayCopy();
+  }
+
+  /**
+   * Generate random data in a config storage.
+   *
+   * @param \Drupal\Core\Config\StorageInterface $storage
+   *   The storage to populate with random data.
+   * @param bool $collections
+   *   Add random collections or not.
+   */
+  protected function generateRandomData(StorageInterface $storage, $collections = TRUE) {
+    $generator = $this->getRandomGenerator();
+    for ($i = 0; $i < rand(2, 10); $i++) {
+      $storage->write($this->randomMachineName(), (array) $generator->object());
+    }
+    if ($collections) {
+      for ($i = 0; $i < rand(1, 5); $i++) {
+        $collection = $storage->createCollection($this->randomMachineName());
+        for ($i = 0; $i < rand(2, 10); $i++) {
+          $collection->write($this->randomMachineName(), (array) $generator->object());
+        }
+      }
+    }
+  }
+
+}