Index: src/wp-includes/class-wp-customize-changeset.php
===================================================================
--- src/wp-includes/class-wp-customize-changeset.php	(nonexistent)
+++ src/wp-includes/class-wp-customize-changeset.php	(working copy)
@@ -0,0 +1,388 @@
+<?php
+/**
+ * Class file for WP_Customize_Changeset
+ *
+ * @package WordPress
+ * @subpackage Customize
+ * @since 4.9.0
+ */
+
+/**
+ * Representation of a Customize Changeset.
+ */
+class WP_Customize_Changeset {
+	/**
+	 * Changeset UUID, the post_name for the customize_changeset post containing the customized state.
+	 *
+	 * @since 4.9.0
+	 * @var string
+	 */
+	protected $uuid;
+
+	/**
+	 * Changeset post ID.
+	 *
+	 * @since 4.9.0
+	 * @var int
+	 */
+	protected $post_id;
+
+	/**
+	 * Retrieve a WP_Customize_Changeset instance from a changeset UUID.
+	 *
+	 * Defers to {@see WP_Customize_Changeset::from_post()} if a post for the UUID exists.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @param string $uuid UUID.
+	 * @return WP_Customize_Changeset Changeset instance.
+	 */
+	public static function from_uuid( $uuid ) {
+		$cache_group = 'customize_changeset_post';
+
+		$post_id = wp_cache_get( $uuid, $cache_group );
+
+		if ( $post_id && 'customize_changeset' === get_post_type( $post_id ) ) {
+			return WP_Customize_Changeset::from_post( $post_id );
+		}
+
+		// The full post object is being retrieved so it's cached.
+		$query = new WP_Query( array(
+			'post_type' => 'customize_changeset',
+			'post_status' => get_post_stati(),
+			'name' => $uuid,
+			'posts_per_page' => 1,
+			'no_found_rows' => true,
+			'cache_results' => true,
+			'update_post_meta_cache' => false,
+			'update_post_term_cache' => false,
+			'lazy_load_term_meta' => false,
+		) );
+
+		if ( empty( $query->posts ) ) {
+			$instance = new WP_Customize_Changeset();
+			$instance->set_uuid( $uuid );
+			return $instance;
+		}
+
+		$post_id = $query->posts[0]->ID;
+		wp_cache_set( $uuid, $post_id, $cache_group );
+
+		return WP_Customize_Changeset::from_post( $post_id );
+	}
+
+	/**
+	 * Retrieve a WP_Customize_Changeset instance from a post.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @param int|WP_Post $post Post ID or object.
+	 * @return WP_Customize_Changeset|null Changeset instance or failure return value from {@see get_post()}.
+	 */
+	public static function from_post( $post ) {
+		$post = get_post( $post );
+
+		if ( ! ( $post instanceof WP_Post ) ) {
+			return $post;
+		}
+
+		$instance = new WP_Customize_Changeset();
+		$instance->parse_post( $post );
+		return $instance;
+	}
+
+	/**
+	 * Populate instance properties from a post object.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @param WP_Post $post Post object.
+	 */
+	public function parse_post( $post ) {
+		$this->post_id = $post->ID;
+		$this->uuid = $post->post_name;
+	}
+
+	/**
+	 * Set the instance UUID.
+	 *
+	 * @param string $uuid UUID.
+	 */
+	public function set_uuid( $uuid ) {
+		$this->uuid = $uuid;
+	}
+
+	/**
+	 * Get the changeset UUID.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @return string UUID.
+	 */
+	public function get_uuid() {
+		return $this->uuid;
+	}
+
+	/**
+	 * Get the changeset post ID.
+	 *
+	 * @return int
+	 */
+	public function get_post_id() {
+		return $this->post_id;
+	}
+
+	/**
+	 * Get the data stored in the changeset post, if one exists.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @return array|WP_Error Changeset data or WP_Error on error.
+	 */
+	public function get_post_data() {
+		if ( ! $this->post_id ) {
+			return new WP_Error( 'empty_post_id' );
+		}
+
+		$post = get_post( $this->post_id );
+
+		if ( ! $post ) {
+			return new WP_Error( 'missing_post' );
+		}
+
+		if ( 'customize_changeset' !== get_post_type( $post ) ) {
+			return new WP_Error( 'wrong_post_type' );
+		}
+
+		$data = json_decode( $post->post_content, true );
+
+		if ( function_exists( 'json_last_error' ) ) {
+			$error = json_last_error();
+
+			if ( $error ) {
+				return new WP_Error( 'json_parse_error', '', $error );
+			}
+		}
+
+		if ( ! is_array( $data ) ) {
+			return new WP_Error( 'expected_array' );
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Get the changeset data.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @return array Changeset data.
+	 */
+	public function get_data() {
+		$data = $this->get_post_data();
+		return ( is_wp_error( $data ) ) ? array() : $data;
+	}
+
+	/**
+	 * Save the changeset post.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @param array $args {
+	 *     Array of arguments that make up the changeset.
+	 *
+	 *     @type array  $data     Changeset data.
+	 *     @type string $date_gmt Changeset date in GMT. Optional.
+	 *     @type string $status   Changeset status. Optional.
+	 *     @type string $title    Changeset title. Optional.
+	 * }
+	 * @return int|WP_Error Changeset post ID on success or WP_Error.
+	 */
+	public function save( $args ) {
+		$args = wp_parse_args( $args, array(
+			'data' => array(),
+			'date_gmt' => null,
+			'status' => null,
+			'title' => null,
+		) );
+
+		$json_options = 0;
+
+		if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) {
+			// Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage.
+			$json_options |= JSON_UNESCAPED_SLASHES;
+		}
+
+		// Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139.
+		$json_options |= JSON_PRETTY_PRINT;
+
+		$post_array = array(
+			'post_content' => wp_json_encode( $args['data'], $json_options ),
+		);
+
+		if ( $args['title'] ) {
+			$post_array['post_title'] = $args['title'];
+		}
+
+		// @todo What if there is no UUID or post ID?
+		if ( $this->post_id ) {
+			$post_array['ID'] = $this->post_id;
+		} else {
+			$post_array['post_type'] = 'customize_changeset';
+			$post_array['post_name'] = $this->uuid;
+			$post_array['post_status'] = 'auto-draft';
+		}
+
+		if ( $args['status'] ) {
+			$post_array['post_status'] = $args['status'];
+		}
+
+		// Reset post date to now if we are publishing, otherwise pass post_date_gmt and translate for post_date.
+		if ( 'publish' === $args['status'] ) {
+			$post_array['post_date_gmt'] = '0000-00-00 00:00:00';
+			$post_array['post_date'] = '0000-00-00 00:00:00';
+		} elseif ( $args['date_gmt'] ) {
+			$post_array['post_date_gmt'] = $args['date_gmt'];
+			$post_array['post_date'] = get_date_from_gmt( $args['date_gmt'] );
+		} elseif ( $this->post_id && 'auto-draft' === get_post_status( $this->post_id ) ) {
+			/*
+			 * Keep bumping the date for the auto-draft whenever it is modified;
+			 * this extends its life, preserving it from garbage-collection via
+			 * wp_delete_auto_drafts().
+			 */
+			$post_array['post_date'] = current_time( 'mysql' );
+			$post_array['post_date_gmt'] = '';
+		}
+
+		/*
+		 * Update the changeset post. The 'publish_customize_changeset' action
+		 * will cause the settings in the changeset to be saved via
+		 * WP_Customize_Setting::save().
+		 */
+		$has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) );
+		if ( $has_kses ) {
+			// Prevent KSES from corrupting JSON in post_content.
+			kses_remove_filters();
+		}
+
+		// Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values().
+		if ( $this->post_id ) {
+			// Prevent date clearing.
+			$post_array['edit_date'] = true;
+
+			$result = wp_update_post( wp_slash( $post_array ), true );
+		} else {
+			$result = wp_insert_post( wp_slash( $post_array ), true );
+
+			if ( is_numeric( $result ) ) {
+				$this->post_id = (int) $result;
+			}
+		}
+
+		if ( $has_kses ) {
+			kses_init_filters();
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Publish the changeset values.
+	 *
+	 * @since 4.9.0
+	 *
+	 * @param  WP_Customize_Manager $wp_customize Customize manager instance that
+	 *                                            should publish the changeset.
+	 * @return bool|WP_Error True or a WP_Error.
+	 */
+	public function publish( $wp_customize ) {
+		$result = $wp_customize->_publish_changeset_values( $this->post_id );
+
+		if ( true === $result ) {
+			/*
+			 * Trash the changeset post if revisions are not enabled.
+			 *
+			 * Unpublished changesets by default get garbage collected due to
+			 * their auto-draft status. When a changeset post is published,
+			 * however, it would no longer get cleaned out. Ths is a problem
+			 * when the changeset posts are never displayed anywhere, since they
+			 * would just be endlessly piling up. So here we use the revisions
+			 * feature to indicate whether or not a published changeset should
+			 * get trashed and thus garbage collected.
+			 */
+			if ( ! wp_revisions_enabled( get_post( $this->post_id ) ) ) {
+				$this->trash();
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Trash or delete the changeset post.
+	 *
+	 * The following re-formulates the logic from wp_trash_post() as done in
+	 * wp_publish_post(). The reason for bypassing wp_trash_post() is that it
+	 * will mutate the the post_content and the post_name when they should be
+	 * untouched.
+	 *
+	 * @global wpdb $wpdb WordPress database abstraction object.
+	 *
+	 * @return mixed The trashed post as an array or an empty value on failure.
+	 */
+	public function trash() {
+		global $wpdb;
+
+		if ( ! EMPTY_TRASH_DAYS ) {
+			return wp_delete_post( (int) $this->post_id, true );
+		}
+
+		if ( $this->post_id ) {
+			$post = get_post( $this->post_id );
+		}
+
+
+		if ( empty( $post ) ) {
+			return $post;
+		}
+
+		if ( get_post_status( $post ) === 'trash' ) {
+			return false;
+		}
+
+		$post_id = $post->ID;
+
+		/** This action is documented in wp-includes/post.php */
+		do_action( 'wp_trash_post', $post_id );
+
+		add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status );
+		add_post_meta( $post_id, '_wp_trash_meta_time', time() );
+
+		$old_status = $post->post_status;
+		$new_status = 'trash';
+		$wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) );
+		clean_post_cache( $post->ID );
+
+		$post->post_status = $new_status;
+		wp_transition_post_status( $new_status, $old_status, $post );
+
+		/** This action is documented in wp-includes/post.php */
+		do_action( 'edit_post', $post->ID, $post );
+
+		/** This action is documented in wp-includes/post.php */
+		do_action( "save_post_{$post->post_type}", $post->ID, $post, true );
+
+		/** This action is documented in wp-includes/post.php */
+		do_action( 'save_post', $post->ID, $post, true );
+
+		/** This action is documented in wp-includes/post.php */
+		do_action( 'wp_insert_post', $post->ID, $post, true );
+
+		wp_trash_post_comments( $post_id );
+
+		/** This action is documented in wp-includes/post.php */
+		do_action( 'trashed_post', $post_id );
+
+		return $post->to_array();
+	}
+}
Index: src/wp-includes/class-wp-customize-manager.php
===================================================================
--- src/wp-includes/class-wp-customize-manager.php	(revision 41314)
+++ src/wp-includes/class-wp-customize-manager.php	(working copy)
@@ -205,14 +205,6 @@
 	private $_changeset_post_id;
 
 	/**
-	 * Changeset data loaded from a customize_changeset post.
-	 *
-	 * @since 4.7.0
-	 * @var array
-	 */
-	private $_changeset_data;
-
-	/**
 	 * Constructor.
 	 *
 	 * @since 3.4.0
@@ -796,31 +788,8 @@
 	 * @return int|null Returns post ID on success and null on failure.
 	 */
 	public function find_changeset_post_id( $uuid ) {
-		$cache_group = 'customize_changeset_post';
-		$changeset_post_id = wp_cache_get( $uuid, $cache_group );
-		if ( $changeset_post_id && 'customize_changeset' === get_post_type( $changeset_post_id ) ) {
-			return $changeset_post_id;
-		}
-
-		$changeset_post_query = new WP_Query( array(
-			'post_type' => 'customize_changeset',
-			'post_status' => get_post_stati(),
-			'name' => $uuid,
-			'posts_per_page' => 1,
-			'no_found_rows' => true,
-			'cache_results' => true,
-			'update_post_meta_cache' => false,
-			'update_post_term_cache' => false,
-			'lazy_load_term_meta' => false,
-		) );
-		if ( ! empty( $changeset_post_query->posts ) ) {
-			// Note: 'fields'=>'ids' is not being used in order to cache the post object as it will be needed.
-			$changeset_post_id = $changeset_post_query->posts[0]->ID;
-			wp_cache_set( $this->_changeset_uuid, $changeset_post_id, $cache_group );
-			return $changeset_post_id;
-		}
-
-		return null;
+		$changeset = WP_Customize_Changeset::from_uuid( $uuid );
+		return $changeset->get_post_id();
 	}
 
 	/**
@@ -853,24 +822,14 @@
 	 * @return array|WP_Error Changeset data or WP_Error on error.
 	 */
 	protected function get_changeset_post_data( $post_id ) {
-		if ( ! $post_id ) {
-			return new WP_Error( 'empty_post_id' );
-		}
-		$changeset_post = get_post( $post_id );
-		if ( ! $changeset_post ) {
+		$changeset = WP_Customize_Changeset::from_post( $post_id );
+
+		if ( ! ( $changeset instanceof WP_Customize_Changeset ) ) {
+			// This error code is also used in WP_Customize_Changeset::parse_post().
 			return new WP_Error( 'missing_post' );
 		}
-		if ( 'customize_changeset' !== $changeset_post->post_type ) {
-			return new WP_Error( 'wrong_post_type' );
-		}
-		$changeset_data = json_decode( $changeset_post->post_content, true );
-		if ( function_exists( 'json_last_error' ) && json_last_error() ) {
-			return new WP_Error( 'json_parse_error', '', json_last_error() );
-		}
-		if ( ! is_array( $changeset_data ) ) {
-			return new WP_Error( 'expected_array' );
-		}
-		return $changeset_data;
+
+		return $changeset->get_post_data();
 	}
 
 	/**
@@ -881,21 +840,19 @@
 	 * @return array Changeset data.
 	 */
 	public function changeset_data() {
-		if ( isset( $this->_changeset_data ) ) {
-			return $this->_changeset_data;
-		}
 		$changeset_post_id = $this->changeset_post_id();
+
 		if ( ! $changeset_post_id ) {
-			$this->_changeset_data = array();
+			$data = array();
 		} else {
 			$data = $this->get_changeset_post_data( $changeset_post_id );
-			if ( ! is_wp_error( $data ) ) {
-				$this->_changeset_data = $data;
-			} else {
-				$this->_changeset_data = array();
+
+			if ( is_wp_error( $data ) ) {
+				$data = array();
 			}
 		}
-		return $this->_changeset_data;
+
+		return $data;
 	}
 
 	/**
@@ -2230,6 +2187,12 @@
 		);
 
 		$changeset_post_id = $this->changeset_post_id();
+		$changeset = WP_Customize_Changeset::from_post( $changeset_post_id );
+
+		if ( ! ( $changeset instanceof WP_Customize_Changeset ) ) {
+			$changeset = WP_Customize_Changeset::from_uuid( $this->changeset_uuid() );
+		}
+
 		$existing_changeset_data = array();
 		if ( $changeset_post_id ) {
 			$existing_status = get_post_status( $changeset_post_id );
@@ -2462,69 +2425,19 @@
 			$this->start_previewing_theme();
 		}
 
-		// Gather the data for wp_insert_post()/wp_update_post().
-		$json_options = 0;
-		if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) {
-			$json_options |= JSON_UNESCAPED_SLASHES; // Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage.
-		}
-		$json_options |= JSON_PRETTY_PRINT; // Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139.
-		$post_array = array(
-			'post_content' => wp_json_encode( $data, $json_options ),
-		);
-		if ( $args['title'] ) {
-			$post_array['post_title'] = $args['title'];
-		}
-		if ( $changeset_post_id ) {
-			$post_array['ID'] = $changeset_post_id;
-		} else {
-			$post_array['post_type'] = 'customize_changeset';
-			$post_array['post_name'] = $this->changeset_uuid();
-			$post_array['post_status'] = 'auto-draft';
-		}
-		if ( $args['status'] ) {
-			$post_array['post_status'] = $args['status'];
-		}
-
-		// Reset post date to now if we are publishing, otherwise pass post_date_gmt and translate for post_date.
-		if ( 'publish' === $args['status'] ) {
-			$post_array['post_date_gmt'] = '0000-00-00 00:00:00';
-			$post_array['post_date'] = '0000-00-00 00:00:00';
-		} elseif ( $args['date_gmt'] ) {
-			$post_array['post_date_gmt'] = $args['date_gmt'];
-			$post_array['post_date'] = get_date_from_gmt( $args['date_gmt'] );
-		} elseif ( $changeset_post_id && 'auto-draft' === get_post_status( $changeset_post_id ) ) {
-			/*
-			 * Keep bumping the date for the auto-draft whenever it is modified;
-			 * this extends its life, preserving it from garbage-collection via
-			 * wp_delete_auto_drafts().
-			 */
-			$post_array['post_date'] = current_time( 'mysql' );
-			$post_array['post_date_gmt'] = '';
-		}
-
 		$this->store_changeset_revision = $allow_revision;
 		add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 );
 
-		// Update the changeset post. The publish_customize_changeset action will cause the settings in the changeset to be saved via WP_Customize_Setting::save().
-		$has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) );
-		if ( $has_kses ) {
-			kses_remove_filters(); // Prevent KSES from corrupting JSON in post_content.
-		}
+		$r = $changeset->save( array(
+			'data' => $data,
+			'date_gmt' => $args['date_gmt'],
+			'status' => $args['status'],
+			'title' => $args['title'],
+		) );
 
-		// Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values().
-		if ( $changeset_post_id ) {
-			$post_array['edit_date'] = true; // Prevent date clearing.
-			$r = wp_update_post( wp_slash( $post_array ), true );
-		} else {
-			$r = wp_insert_post( wp_slash( $post_array ), true );
-			if ( ! is_wp_error( $r ) ) {
-				$this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset.
-			}
+		if ( ! is_wp_error( $r ) ) {
+			$this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset.
 		}
-		if ( $has_kses ) {
-			kses_init_filters();
-		}
-		$this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents.
 
 		remove_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ) );
 
@@ -2605,8 +2518,6 @@
 		$this->_changeset_post_id   = $changeset_post_id;
 		$previous_changeset_uuid    = $this->_changeset_uuid;
 		$this->_changeset_uuid      = $changeset_post->post_name;
-		$previous_changeset_data    = $this->_changeset_data;
-		$this->_changeset_data      = $publishing_changeset_data;
 
 		// Parse changeset data to identify theme mod settings and user IDs associated with settings to be saved.
 		$setting_user_ids = array();
@@ -2613,7 +2524,7 @@
 		$theme_mod_settings = array();
 		$namespace_pattern = '/^(?P<stylesheet>.+?)::(?P<setting_id>.+)$/';
 		$matches = array();
-		foreach ( $this->_changeset_data as $raw_setting_id => $setting_params ) {
+		foreach ( $this->changeset_data() as $raw_setting_id => $setting_params ) {
 			$actual_setting_id = null;
 			$is_theme_mod_setting = (
 				isset( $setting_params['value'] )
@@ -2722,7 +2633,6 @@
 		}
 
 		// Restore original changeset data.
-		$this->_changeset_data    = $previous_changeset_data;
 		$this->_changeset_post_id = $previous_changeset_post_id;
 		$this->_changeset_uuid    = $previous_changeset_uuid;
 
Index: src/wp-includes/theme.php
===================================================================
--- src/wp-includes/theme.php	(revision 41314)
+++ src/wp-includes/theme.php	(working copy)
@@ -2836,7 +2836,6 @@
  * @since 4.7.0
  * @access private
  *
- * @global wpdb                 $wpdb         WordPress database abstraction object.
  * @global WP_Customize_Manager $wp_customize Customizer instance.
  *
  * @param string  $new_status     New post status.
@@ -2844,7 +2843,7 @@
  * @param WP_Post $changeset_post Changeset post object.
  */
 function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) {
-	global $wp_customize, $wpdb;
+	global $wp_customize;
 
 	$is_publishing_changeset = (
 		'customize_changeset' === $changeset_post->post_type
@@ -2857,6 +2856,12 @@
 		return;
 	}
 
+	$changeset = WP_Customize_Changeset::from_post( $changeset_post );
+
+	if ( ! $changeset ) {
+		return;
+	}
+
 	if ( empty( $wp_customize ) ) {
 		require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
 		$wp_customize = new WP_Customize_Manager( array(
@@ -2886,60 +2891,8 @@
 		/** This filter is documented in /wp-includes/class-wp-customize-manager.php */
 		do_action( 'customize_register', $wp_customize );
 	}
-	$wp_customize->_publish_changeset_values( $changeset_post->ID ) ;
 
-	/*
-	 * Trash the changeset post if revisions are not enabled. Unpublished
-	 * changesets by default get garbage collected due to the auto-draft status.
-	 * When a changeset post is published, however, it would no longer get cleaned
-	 * out. Ths is a problem when the changeset posts are never displayed anywhere,
-	 * since they would just be endlessly piling up. So here we use the revisions
-	 * feature to indicate whether or not a published changeset should get trashed
-	 * and thus garbage collected.
-	 */
-	if ( ! wp_revisions_enabled( $changeset_post ) ) {
-		$post = $changeset_post;
-		$post_id = $changeset_post->ID;
-
-		/*
-		 * The following re-formulates the logic from wp_trash_post() as done in
-		 * wp_publish_post(). The reason for bypassing wp_trash_post() is that it
-		 * will mutate the the post_content and the post_name when they should be
-		 * untouched.
-		 */
-		if ( ! EMPTY_TRASH_DAYS ) {
-			wp_delete_post( $post_id, true );
-		} else {
-			/** This action is documented in wp-includes/post.php */
-			do_action( 'wp_trash_post', $post_id );
-
-			add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status );
-			add_post_meta( $post_id, '_wp_trash_meta_time', time() );
-
-			$old_status = $post->post_status;
-			$new_status = 'trash';
-			$wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) );
-			clean_post_cache( $post->ID );
-
-			$post->post_status = $new_status;
-			wp_transition_post_status( $new_status, $old_status, $post );
-
-			/** This action is documented in wp-includes/post.php */
-			do_action( 'edit_post', $post->ID, $post );
-
-			/** This action is documented in wp-includes/post.php */
-			do_action( "save_post_{$post->post_type}", $post->ID, $post, true );
-
-			/** This action is documented in wp-includes/post.php */
-			do_action( 'save_post', $post->ID, $post, true );
-
-			/** This action is documented in wp-includes/post.php */
-			do_action( 'wp_insert_post', $post->ID, $post, true );
-
-			/** This action is documented in wp-includes/post.php */
-			do_action( 'trashed_post', $post_id );
-		}
-	}
+	$changeset->publish( $wp_customize );
 }
 
 /**
Index: src/wp-settings.php
===================================================================
--- src/wp-settings.php	(revision 41314)
+++ src/wp-settings.php	(working copy)
@@ -218,6 +218,7 @@
 require( ABSPATH . WPINC . '/class-wp-widget-factory.php' );
 require( ABSPATH . WPINC . '/nav-menu.php' );
 require( ABSPATH . WPINC . '/nav-menu-template.php' );
+require( ABSPATH . WPINC . '/class-wp-customize-changeset.php' );
 require( ABSPATH . WPINC . '/admin-bar.php' );
 require( ABSPATH . WPINC . '/rest-api.php' );
 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );
