diff --git src/wp-admin/includes/post.php src/wp-admin/includes/post.php
index 8d7f76b3fe..dd447d9beb 100644
--- src/wp-admin/includes/post.php
+++ src/wp-admin/includes/post.php
@@ -1970,11 +1970,12 @@ function wp_create_post_autosave( $post_data ) {
 		 * Fires before an autosave is stored.
 		 *
 		 * @since 4.1.0
+		 * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created.
 		 *
 		 * @param array $new_autosave Post array - the autosave that is about to be saved.
+		 * @param bool  $is_update    Whether this is an existing autosave.
 		 */
-		do_action( 'wp_creating_autosave', $new_autosave );
-
+		do_action( 'wp_creating_autosave', $new_autosave, true );
 		return wp_update_post( $new_autosave );
 	}
 
@@ -1982,7 +1983,71 @@ function wp_create_post_autosave( $post_data ) {
 	$post_data = wp_unslash( $post_data );
 
 	// Otherwise create the new autosave as a special post revision.
-	return _wp_put_post_revision( $post_data, true );
+	$revision = _wp_put_post_revision( $post_data, true );
+
+	if ( ! is_wp_error( $revision ) && 0 !== $revision ) {
+
+		/** This action is documented in wp-admin/includes/post.php */
+		do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false );
+	}
+
+	return $revision;
+}
+
+/**
+ * Autosave the revisioned meta fields.
+ *
+ * Iterates through the revisioned meta fields and checks each to see if they are set,
+ * and have a changed value. If so, the meta value is saved and attached to the autosave.
+ *
+ * @since 6.4.0
+ *
+ * @param array $new_autosave The new post data being autosaved.
+ */
+function wp_autosave_post_revisioned_meta_fields( $new_autosave ) {
+	/*
+	 * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST
+	 * itself. This sets $posted_data to the correct variable.
+	 *
+	 * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because
+	 * this is hooked on inner core hooks where a valid nonce was already checked.
+	 *
+	 * @phpcs:disable WordPress.Security
+	 */
+	$posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST;
+	// phpcs:enable
+
+	$post_type = get_post_type( $new_autosave['post_parent'] );
+
+	/*
+	 * Go thru the revisioned meta keys and save them as part of the autosave, if
+	 * the meta key is part of the posted data, the meta value is not blank and
+	 * the the meta value has changes from the last autosaved value.
+	 */
+	foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
+
+		if (
+		isset( $posted_data[ $meta_key ] ) &&
+		get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] )
+		) {
+			/*
+			 * Use the underlying delete_metadata() and add_metadata() functions
+			 * vs delete_post_meta() and add_post_meta() to make sure we're working
+			 * with the actual revision meta.
+			 */
+			delete_metadata( 'post', $new_autosave['ID'], $meta_key );
+
+			/*
+			 * One last check to ensure meta value not empty().
+			 */
+			if ( ! empty( $posted_data[ $meta_key ] ) ) {
+				/*
+				 * Add the revisions meta data to the autosave.
+				 */
+				add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] );
+			}
+		}
+	}
 }
 
 /**
diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
index adba75fcd7..090ee61d51 100644
--- src/wp-includes/default-filters.php
+++ src/wp-includes/default-filters.php
@@ -411,6 +411,7 @@ add_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 );
 add_action( 'plugins_loaded', 'wp_maybe_load_embeds', 0 );
 add_action( 'shutdown', 'wp_ob_end_flush_all', 1 );
 // Create a revision whenever a post is updated.
+add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );
 add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
 add_action( 'publish_post', '_publish_post_hook', 5, 1 );
 add_action( 'transition_post_status', '_transition_post_status', 5, 3 );
@@ -719,6 +720,18 @@ add_action( 'init', 'wp_register_persisted_preferences_meta' );
 // CPT wp_block custom postmeta field.
 add_action( 'init', 'wp_create_initial_post_meta' );
 
+// Include revisioned meta when considering whether a post revision has changed.
+add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 );
+
+// Save revisioned post meta immediately after a revision is saved
+add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
+
+// Include revisioned meta when creating or updating an autosave revision.
+add_action( 'wp_creating_autosave', 'wp_autosave_post_revisioned_meta_fields' );
+
+// When restoring revisions, also restore revisioned meta.
+add_action( 'wp_restore_post_revision', 'wp_restore_post_revision_meta', 10, 2 );
+
 // Font management.
 add_action( 'wp_head', 'wp_print_font_faces', 50 );
 
diff --git src/wp-includes/meta.php src/wp-includes/meta.php
index 0b9f08544e..8ee9386ff0 100644
--- src/wp-includes/meta.php
+++ src/wp-includes/meta.php
@@ -1367,6 +1367,7 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype =
  * @since 4.9.8 The `$object_subtype` argument was added to the arguments array.
  * @since 5.3.0 Valid meta types expanded to include "array" and "object".
  * @since 5.5.0 The `$default` argument was added to the arguments array.
+ * @since 6.4.0 The `$revisions_enabled` argument was added to the arguments array.
  *
  * @param string       $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
  *                                  or any other object type with an associated meta table.
@@ -1392,6 +1393,8 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype =
  *                                         support for custom fields for registered meta to be accessible via REST.
  *                                         When registering complex meta values this argument may optionally be an
  *                                         array with 'schema' or 'prepare_callback' keys instead of a boolean.
+ *     @type bool       $revisions_enabled Whether to enable revisions support for this meta_key. Can only be used when the
+ *                                         object type is 'post'.
  * }
  * @param string|array $deprecated Deprecated. Use `$args` instead.
  * @return bool True if the meta key was successfully registered in the global array, false if not.
@@ -1414,6 +1417,7 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
 		'sanitize_callback' => null,
 		'auth_callback'     => null,
 		'show_in_rest'      => false,
+		'revisions_enabled' => false,
 	);
 
 	// There used to be individual args for sanitize and auth callbacks.
@@ -1460,6 +1464,17 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
 	}
 
 	$object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
+	if( $args['revisions_enabled'] ) {
+		if( 'post' !== $object_type ) {
+			_doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object type supports revisions.' ), '6.4.0' );
+
+			return false;
+		} else if ( ! empty( $object_subtype ) && ! post_type_supports( $object_subtype, 'revisions' ) ) {
+			_doing_it_wrong( __FUNCTION__, __( 'Meta keys cannot enable revisions support unless the object subtype supports revisions.' ), '6.4.0' );
+
+			return false;
+		}
+	}
 
 	// If `auth_callback` is not provided, fall back to `is_protected_meta()`.
 	if ( empty( $args['auth_callback'] ) ) {
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
index d2dc249615..dbecd676de 100644
--- src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
@@ -234,8 +234,8 @@ class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
 			 */
 			$autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
 		} else {
-			// Non-draft posts: create or update the post autosave.
-			$autosave_id = $this->create_post_autosave( (array) $prepared_post );
+			// Non-draft posts: create or update the post autosave. Pass the meta data.
+			$autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) );
 		}
 
 		if ( is_wp_error( $autosave_id ) ) {
@@ -348,11 +348,13 @@ class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
 	 * From wp-admin/post.php.
 	 *
 	 * @since 5.0.0
+	 * @since 6.4.0 The `$meta` parameter was added.
 	 *
 	 * @param array $post_data Associative array containing the post data.
+	 * @param array $meta      Associative array containing the post meta data.
 	 * @return mixed The autosave revision ID or WP_Error.
 	 */
-	public function create_post_autosave( $post_data ) {
+	public function create_post_autosave( $post_data, array $meta = array() ) {
 
 		$post_id = (int) $post_data['ID'];
 		$post    = get_post( $post_id );
@@ -372,6 +374,21 @@ class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
 			}
 		}
 
+		// Check if meta values have changed.
+		if ( ! empty( $meta ) ) {
+			$revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type );
+			foreach ( $revisioned_meta_keys as $meta_key ) {
+				// get_metadata_raw is used to avoid retrieving the default value.
+				$old_meta = get_metadata_raw( 'post', $post_id, $meta_key, true );
+				$new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : '';
+
+				if ( $new_meta !== $old_meta ) {
+					$autosave_is_different = true;
+					break;
+				}
+			}
+		}
+
 		$user_id = get_current_user_id();
 
 		// Store one autosave per author. If there is already an autosave, overwrite it.
@@ -390,11 +407,26 @@ class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
 			do_action( 'wp_creating_autosave', $new_autosave );
 
 			// wp_update_post() expects escaped array.
-			return wp_update_post( wp_slash( $new_autosave ) );
+			$revision_id = wp_update_post( wp_slash( $new_autosave ) );
+		} else {
+			// Create the new autosave as a special post revision.
+			$revision_id = _wp_put_post_revision( $post_data, true );
+		}
+
+		if( is_wp_error( $revision_id ) || 0 === $revision_id ) {
+			return $revision_id;
+		}
+
+		// Attached any passed meta values that have revisions enabled.
+		if ( ! empty( $meta ) && $revision_id ) {
+			foreach ( $revisioned_meta_keys as $meta_key ) {
+				if ( isset( $meta[ $meta_key ] ) ) {
+					update_metadata( 'post', $revision_id, $meta_key, $meta[ $meta_key ] );
+				}
+			}
 		}
 
-		// Create the new autosave as a special post revision.
-		return _wp_put_post_revision( $post_data, true );
+		return $revision_id;
 	}
 
 	/**
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
index 16aceb0d74..5501c190c1 100644
--- src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
@@ -24,6 +24,14 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 	 */
 	private $parent_post_type;
 
+	/**
+	 * Instance of a revision meta fields object.
+	 *
+	 * @since 6.4.0
+	 * @var WP_REST_Post_Meta_Fields
+	 */
+	protected $meta;
+
 	/**
 	 * Parent controller.
 	 *
@@ -60,6 +68,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 		$this->rest_base         = 'revisions';
 		$this->parent_base       = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
 		$this->namespace         = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
+		$this->meta              = new WP_REST_Post_Meta_Fields( $parent_post_type );
 	}
 
 	/**
@@ -619,6 +628,10 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 			);
 		}
 
+		if ( rest_is_field_included( 'meta', $fields ) ) {
+			$data['meta'] = $this->meta->get_value( $post->ID, $request );
+		}
+
 		$context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
 		$data     = $this->add_additional_fields_to_object( $data, $request );
 		$data     = $this->filter_response_by_context( $data, $context );
@@ -752,6 +765,8 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 			$schema['properties']['guid'] = $parent_schema['properties']['guid'];
 		}
 
+		$schema['properties']['meta'] = $this->meta->get_field_schema();
+
 		$this->schema = $schema;
 
 		return $this->add_additional_fields_schema( $this->schema );
diff --git src/wp-includes/revision.php src/wp-includes/revision.php
index d5a5829d7e..d17c79b1b6 100644
--- src/wp-includes/revision.php
+++ src/wp-includes/revision.php
@@ -95,6 +95,27 @@ function _wp_post_revision_data( $post = array(), $autosave = false ) {
 	return $revision_data;
 }
 
+/**
+ * Saves revisions for a post after all changes have been made.
+ *
+ * @since 6.4.0
+ *
+ * @param int     $post_id The post id that was inserted.
+ * @param WP_Post $post    The post object that was inserted.
+ * @param bool    $update  Whether this insert is updating an existing post.
+ */
+function wp_save_post_revision_on_insert( $post_id, $post, $update ) {
+	if ( ! $update ) {
+		return;
+	}
+
+	if ( ! has_action( 'post_updated', 'wp_save_post_revision' ) ) {
+		return;
+	}
+
+	wp_save_post_revision( $post_id );
+}
+
 /**
  * Creates a revision for the current version of a post.
  *
@@ -111,6 +132,11 @@ function wp_save_post_revision( $post_id ) {
 		return;
 	}
 
+	// Prevent saving post revisions if revisions should be saved on wp_after_insert_post.
+	if ( doing_action( 'post_updated' ) && has_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert' ) ) {
+		return;
+	}
+
 	$post = get_post( $post_id );
 
 	if ( ! $post ) {
@@ -361,15 +387,39 @@ function _wp_put_post_revision( $post = null, $autosave = false ) {
 		 * Fires once a revision has been saved.
 		 *
 		 * @since 2.6.0
+		 * @since 6.4.0 The post_id parameter was added.
 		 *
 		 * @param int $revision_id Post revision ID.
+		 * @param int $post_id     Post ID.
 		 */
-		do_action( '_wp_put_post_revision', $revision_id );
+		do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] );
 	}
 
 	return $revision_id;
 }
 
+
+/**
+ * Save the revisioned meta fields.
+ *
+ * @since 6.4.0
+ *
+ * @param int $revision_id The ID of the revision to save the meta to.
+ * @param int $post_id     The ID of the post the revision is associated with.
+ */
+function wp_save_revisioned_meta_fields( $revision_id, $post_id ) {
+	$post_type = get_post_type( $post_id );
+	if ( ! $post_type ) {
+		return;
+	}
+
+	foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
+		if ( metadata_exists( 'post', $post_id, $meta_key ) ) {
+			_wp_copy_post_meta( $post_id, $revision_id, $meta_key );
+		}
+	}
+}
+
 /**
  * Gets a post revision.
  *
@@ -450,6 +500,9 @@ function wp_restore_post_revision( $revision, $fields = null ) {
 	// Update last edit user.
 	update_post_meta( $post_id, '_edit_last', get_current_user_id() );
 
+	// Restore any revisioned meta fields.
+	wp_restore_post_revision_meta( $post_id, $revision['ID'] );
+
 	/**
 	 * Fires after a post revision has been restored.
 	 *
@@ -463,6 +516,105 @@ function wp_restore_post_revision( $revision, $fields = null ) {
 	return $post_id;
 }
 
+/**
+ * Restore the revisioned meta values for a post.
+ *
+ * @param int $post_id     The ID of the post to restore the meta to.
+ * @param int $revision_id The ID of the revision to restore the meta from.
+ *
+ * @since 6.4.0
+ */
+function wp_restore_post_revision_meta( $post_id, $revision_id ) {
+	$post_type = get_post_type( $post_id );
+	if ( ! $post_type ) {
+		return;
+	}
+
+	// Restore revisioned meta fields.
+	foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) {
+
+		// Clear any existing meta.
+		delete_post_meta( $post_id, $meta_key );
+
+		_wp_copy_post_meta( $revision_id, $post_id, $meta_key );
+	}
+}
+
+/**
+ * Copy post meta for the given key from one post to another.
+ *
+ * @param int    $source_post_id Post ID to copy meta value(s) from.
+ * @param int    $target_post_id Post ID to copy meta value(s) to.
+ * @param string $meta_key       Meta key to copy.
+ *
+ * @since 6.4.0
+ */
+function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) {
+
+	foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) {
+		/**
+		 * We use add_metadata() function vs add_post_meta() here
+		 * to allow for a revision post target OR regular post.
+		 */
+		add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) );
+	}
+}
+
+/**
+ * Determine which post meta fields should be revisioned.
+ *
+ * @since 6.4.0
+ *
+ * @param string $post_type The post type being revisioned.
+ *
+ * @return array An array of meta keys to be revisioned.
+ */
+function wp_post_revision_meta_keys( $post_type ) {
+	$registered_meta = array_merge(
+		get_registered_meta_keys( 'post' ),
+		get_registered_meta_keys( 'post', $post_type )
+	);
+
+	$wp_revisioned_meta_keys = array();
+
+	foreach ( $registered_meta as $name => $args ) {
+		if ( $args['revisions_enabled'] ) {
+			$wp_revisioned_meta_keys[ $name ] = true;
+		}
+	}
+
+	$wp_revisioned_meta_keys = array_keys( $wp_revisioned_meta_keys );
+
+	/**
+	 * Filter the list of post meta keys to be revisioned.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param array $keys       An array of meta fields to be revisioned.
+	 * @param string $post_type The post type being revisioned.
+	 */
+	return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys, $post_type );
+}
+
+/**
+ * Check whether revisioned post meta fields have changed.
+ *
+ * @param bool    $post_has_changed Whether the post has changed.
+ * @param WP_Post $last_revision    The last revision post object.
+ * @param WP_Post $post             The post object.
+ *
+ * @since 6.4.0
+ */
+function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) {
+	foreach ( wp_post_revision_meta_keys( $post->post_type ) as $meta_key ) {
+		if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) {
+			$post_has_changed = true;
+			break;
+		}
+	}
+	return $post_has_changed;
+}
+
 /**
  * Deletes a revision.
  *
@@ -728,6 +880,7 @@ function _set_preview( $post ) {
 
 	add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
 	add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
+	add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 );
 
 	return $post;
 }
@@ -946,3 +1099,38 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) {
 
 	return true;
 }
+
+/**
+ * Filters preview post meta retrieval to get values from the autosave.
+ *
+ * Filters revisioned meta keys only.
+ *
+ * @since 6.4.0
+ *
+ * @param mixed  $value     Meta value to filter.
+ * @param int    $object_id Object ID.
+ * @param string $meta_key  Meta key to filter a value for.
+ * @param bool   $single    Whether to return a single value. Default false.
+ * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist,
+ *               the post type is a revision or the post ID doesn't match the object ID.
+ *               Otherwise, the revisioned meta value is returned for the preview.
+ */
+function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) {
+
+	$post = get_post();
+	if (
+		empty( $post ) ||
+		$post->ID !== $object_id ||
+		! in_array( $meta_key, wp_post_revision_meta_keys( $post->post_type ), true ) ||
+		'revision' === $post->post_type
+	) {
+		return $value;
+	}
+
+	$preview = wp_get_post_autosave( $post->ID );
+	if ( false === $preview ) {
+		return $value;
+	}
+
+	return get_post_meta( $preview->ID, $meta_key, $single );
+}
diff --git tests/phpunit/tests/meta/registerMeta.php tests/phpunit/tests/meta/registerMeta.php
index 4b08e4d82b..9909d36000 100644
--- tests/phpunit/tests/meta/registerMeta.php
+++ tests/phpunit/tests/meta/registerMeta.php
@@ -97,6 +97,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -121,6 +122,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -175,6 +177,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ),
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -342,6 +345,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -395,6 +399,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -1081,4 +1086,36 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 			array( 'user', 'user' ),
 		);
 	}
+
+	/**
+	 * Test that attempting to register meta with revisions_enabled set to true on a
+	 * post type that does not have revisions enabled fails and throws a `doing_it_wrong` notice.
+	 *
+	 * @ticket 20564
+	 */
+	public function test_register_meta_with_revisions_enabled_on_post_type_without_revisions() {
+		$this->setExpectedIncorrectUsage( 'register_meta' );
+
+		// Set up a custom post type with revisions disabled.
+		register_post_type(
+			'test_post_type',
+			array(
+				'supports' => array( 'title', 'editor' ),
+			)
+		);
+
+		$meta_key = 'registered_key1';
+		$args     = array(
+			'revisions_enabled' => true,
+		);
+
+		$register = register_meta(
+			'test_post_type',
+			$meta_key,
+			$args
+		);
+
+		$this->assertFalse( $register );
+	}
+
 }
diff --git tests/phpunit/tests/post/metaRevisions.php tests/phpunit/tests/post/metaRevisions.php
new file mode 100644
index 0000000000..74442b53c8
--- /dev/null
+++ tests/phpunit/tests/post/metaRevisions.php
@@ -0,0 +1,722 @@
+<?php
+
+/**
+ *
+ * Tests for post meta revisioning.
+ *
+ * @group post
+ * @group revision
+ * @group meta
+ * @group meta-revisions
+ */
+class MetaRevisionTests extends WP_UnitTestCase {
+
+	/**
+	 * Callback function to add the revisioned keys.
+	 *
+	 * @param array $keys The array of revisioned keys.
+	 *
+	 * @return array
+	 */
+	public function add_revisioned_keys( $keys ) {
+		$keys[] = 'meta_revision_test';
+		$keys[] = 'meta_multiples_test';
+		return $keys;
+	}
+
+	/**
+	 * Test the revisions system for storage of meta values with slashes.
+	 *
+	 * @param string $passed   The passed data for testing.
+	 *
+	 * @param string $expected The expected value after storing & retrieving.
+	 *
+	 * @group revision
+	 * @group slashed
+	 * @dataProvider slashed_data_provider
+	 */
+	public function test_revisions_stores_meta_values_with_slashes( $passed, $expected ) {
+		// Set up a new post.
+		$post_id = $this->factory->post->create();
+
+		// And update to store an initial revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some initial content',
+				'ID'           => $post_id,
+			)
+		);
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Store a custom meta value, which is not revisioned by default.
+		update_post_meta( $post_id, 'meta_revision_test', wp_slash( $passed ) );
+		$this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content',
+				'ID'           => $post_id,
+			)
+		);
+
+		// Overwrite.
+		update_post_meta( $post_id, 'meta_revision_test', 'original' );
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content again',
+				'ID'           => $post_id,
+			)
+		);
+
+		// Restore the previous revision.
+		$revisions = (array) wp_get_post_revisions( $post_id );
+
+		// Go back to load the previous revision.
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+
+		// Restore!
+		wp_restore_post_revision( $last_revision->ID );
+
+		$this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) );
+	}
+
+	/**
+	 * Provide data for the slashed data tests.
+	 */
+	public function slashed_data_provider() {
+		return array(
+			array(
+				'some\text',
+				'some\text',
+			),
+			array(
+				'test some\ \\extra \\\slashed \\\\text ',
+				'test some\ \\extra \\\slashed \\\\text ',
+			),
+			array(
+				"This \'is\' an example \n of a \"quoted\" string",
+				"This \'is\' an example \n of a \"quoted\" string",
+			),
+			array(
+				'some unslashed text just to test! % & * ( ) #',
+				'some unslashed text just to test! % & * ( ) #',
+			),
+		);
+	}
+
+	/**
+	 * Test the revisions system for storage of meta values.
+	 *
+	 * @group revision
+	 */
+	public function test_revisions_stores_meta_values() {
+		/*
+		 * Set Up.
+		 */
+
+		// Set up a new post.
+		$post_id          = $this->factory->post->create();
+		$original_post_id = $post_id;
+
+		// And update to store an initial revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some initial content',
+				'ID'           => $post_id,
+			)
+		);
+
+		// One revision so far.
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 1, $revisions );
+
+		/*
+		 * First set up a meta value.
+		 */
+
+		// Store a custom meta value, which is not revisioned by default.
+		update_post_meta( $post_id, 'meta_revision_test', 'original' );
+
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content',
+				'ID'           => $post_id,
+			)
+		);
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 2, $revisions );
+
+		// Next, store some updated meta values for the same key.
+		update_post_meta( $post_id, 'meta_revision_test', 'update1' );
+
+		// Save the post, changing content to force a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some updated content',
+				'ID'           => $post_id,
+			)
+		);
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 3, $revisions );
+
+		/*
+		 * Now restore the original revision.
+		 */
+
+		// Restore the previous revision.
+		$revisions = (array) wp_get_post_revisions( $post_id );
+
+		// Go back two to load the previous revision.
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+
+		// Restore!
+		wp_restore_post_revision( $last_revision->ID );
+
+		wp_update_post( array( 'ID' => $post_id ) );
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 4, $revisions );
+
+		/*
+		 * Check the meta values to verify they are NOT revisioned - they are not revisioned by default.
+		 */
+
+		// Custom post meta should NOT be restored, orignal value should not be restored, value still 'update1'.
+		$this->assertEquals( 'update1', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		update_post_meta( $post_id, 'meta_revision_test', 'update2' );
+
+		/*
+		 * Test the revisioning of custom meta when enabled by the wp_post_revision_meta_keys filter.
+		 */
+
+		// Add the custom field to be revised via the wp_post_revision_meta_keys filter.
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Save the post, changing content to force a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'more updated content',
+				'ID'           => $post_id,
+			)
+		);
+
+		$revisions = array_values( wp_get_post_revisions( $post_id ) );
+		$this->assertCount( 5, $revisions );
+		$this->assertEquals( 'update2', get_post_meta( $revisions[0]->ID, 'meta_revision_test', true ) );
+
+		// Store custom meta values, which should now be revisioned.
+		update_post_meta( $post_id, 'meta_revision_test', 'update3' );
+
+		/*
+		 * Save the post again, custom meta should now be revisioned.
+		 *
+		 * Note that a revision is saved even though there is no change
+		 * in post content, because the revisioned post_meta has changed.
+		 */
+		wp_update_post(
+			array(
+				'ID' => $post_id,
+			)
+		);
+
+		// This revision contains the existing post meta ('update3').
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 6, $revisions );
+
+		// Verify that previous post meta is set.
+		$this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Restore the previous revision.
+		$revisions = wp_get_post_revisions( $post_id );
+
+		// Go back two to load the previous revision.
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that previous post meta is restored.
+		 */
+		$this->assertEquals( 'update2', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Try storing a blank meta.
+		update_post_meta( $post_id, 'meta_revision_test', '' );
+		wp_update_post(
+			array(
+				'ID' => $post_id,
+			)
+		);
+
+		update_post_meta( $post_id, 'meta_revision_test', 'update 4' );
+		wp_update_post(
+			array(
+				'ID' => $post_id,
+			)
+		);
+
+		// Restore the previous revision.
+		$revisions = wp_get_post_revisions( $post_id );
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that previous blank post meta is restored.
+		 */
+		$this->assertEquals( '', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		/*
+		 * Test not tracking a key - remove the key from the revisioned meta.
+		 */
+		remove_all_filters( 'wp_post_revision_meta_keys' );
+
+		// Meta should no longer be revisioned.
+		update_post_meta( $post_id, 'meta_revision_test', 'update 5' );
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'changed content',
+			)
+		);
+		update_post_meta( $post_id, 'meta_revision_test', 'update 6' );
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'go updated content',
+			)
+		);
+
+		// Restore the previous revision.
+		$revisions = wp_get_post_revisions( $post_id );
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that previous post meta is NOT restored.
+		 */
+		$this->assertEquals( 'update 6', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Add the custom field to be revised via the wp_post_revision_meta_keys filter.
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		/*
+		 * Test the revisioning of multiple meta keys.
+		 */
+
+		// Add three values for meta.
+		update_post_meta( $post_id, 'meta_revision_test', 'update 7' );
+		add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 2' );
+		add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 3' );
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Update all three values.
+		update_post_meta( $post_id, 'meta_revision_test', 'update 8', 'update 7' );
+		update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 2', 'update 7 number 2' );
+		update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 3', 'update 7 number 3' );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that multiple metas stored correctly.
+		 */
+		$this->assertEquals( array( 'update 7', 'update 7 number 2', 'update 7 number 3' ), get_post_meta( $post_id, 'meta_revision_test' ) );
+
+		/*
+		 * Test the revisioning of a multidimensional array.
+		 */
+		$test_array = array(
+			'a' => array(
+				'1',
+				'2',
+				'3',
+			),
+			'b' => 'ok',
+			'c' => array(
+				'multi' => array(
+					'a',
+					'b',
+					'c',
+				),
+				'not'   => 'ok',
+			),
+		);
+
+		// Clear any old value.
+		delete_post_meta( $post_id, 'meta_revision_test' );
+
+		// Set the test meta to the array.
+		update_post_meta( $post_id, 'meta_revision_test', $test_array );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Set the test meta blank.
+		update_post_meta( $post_id, 'meta_revision_test', '' );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify  multidimensional array stored correctly.
+		 */
+		$stored_array = get_post_meta( $post_id, 'meta_revision_test' );
+		$this->assertEquals( $test_array, $stored_array[0] );
+		/*
+
+		 * Test multiple revisions on the same key.
+		 */
+
+		// Set the test meta to the array.
+		add_post_meta( $post_id, 'meta_multiples_test', 'test1' );
+		add_post_meta( $post_id, 'meta_multiples_test', 'test2' );
+		add_post_meta( $post_id, 'meta_multiples_test', 'test3' );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		$stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
+		$expect       = array( 'test1', 'test2', 'test3' );
+
+		$this->assertEquals( $expect, $stored_array );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		$stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
+		$expect       = array( 'test1', 'test2', 'test3' );
+
+		$this->assertEquals( $expect, $stored_array );
+
+		// Cleanup!
+		wp_delete_post( $original_post_id );
+	}
+
+	/**
+	 * Verify that only existing meta is revisioned.
+	 */
+	public function only_existing_meta_is_revisioned() {
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+			)
+		);
+
+		// Revision v1.
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'updated content v1',
+			)
+		);
+
+		$this->assertPostNotHasMetaKey( $post_id, 'foo' );
+		$this->assertPostNotHasMetaKey( $post_id, 'bar' );
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$revision  = array_shift( $revisions );
+		$this->assertEmpty( get_metadata( 'post', $revision->ID ) );
+
+		// Revision v2.
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'updated content v2',
+				'meta_input'   => array(
+					'foo' => 'foo v2',
+				),
+			)
+		);
+
+		$this->assertPostHasMetaKey( $post_id, 'foo' );
+		$this->assertPostNotHasMetaKey( $post_id, 'bar' );
+		$this->assertPostNotHasMetaKey( $post_id, 'meta_revision_test' );
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$revision  = array_shift( $revisions );
+		$this->assertPostHasMetaKey( $revision->ID, 'foo' );
+		$this->assertPostNotHasMetaKey( $revision->ID, 'bar' );
+		$this->assertPostNotHasMetaKey( $revision->ID, 'meta_revision_test' );
+	}
+
+	/**
+	 * Verify that blank strings are revisioned correctly.
+	 */
+	public function blank_meta_is_revisioned() {
+
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+				'meta_input'   => array(
+					'foo' => 'foo',
+				),
+			)
+		);
+
+		// Set the test meta to an empty string.
+		update_post_meta( $post_id, 'foo', '' );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		$stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
+		$expect       = array( 'test1', 'test2', 'test3' );
+
+		$this->assertEquals( $expect, $stored_array );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+		$stored_data = get_post_meta( $post_id, 'foo' );
+		$this->assertEquals( '', $stored_data[0] );
+	}
+
+	/**
+	 * Test revisioning of meta with a default value.
+	 */
+	public function test_revisionining_of_meta_with_default_value() {
+
+		// Add a meta field to revision that includes a default value.
+		register_post_meta(
+			'post',
+			'meta_revision_test',
+			array(
+				'single'            => true,
+				'default'           => 'default value',
+				'revisions_enabled' => true,
+			)
+		);
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+				'meta_input'   => array(
+					'meta_revision_test' => 'foo',
+				),
+			)
+		);
+
+		// Set the test meta to an empty string.
+		update_post_meta( $post_id, 'meta_revision_test', '' );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Check that the meta is blank.
+		$stored_data = get_post_meta( $post_id, 'meta_revision_test', true );
+		$this->assertEquals( '', $stored_data );
+
+		// Also verify that the latest revision has blank stored for the meta.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		$stored_data   = get_post_meta( $last_revision->ID, 'meta_revision_test', true );
+		$this->assertEquals( '', $stored_data );
+
+		// Delete the meta.
+		delete_post_meta( $post_id, 'meta_revision_test' );
+
+		// Update to save.
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'content update 1',
+			)
+		);
+
+		// Check that the default meta value is returned.
+		$this->assertEquals( 'default value', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Also verify that the latest revision has the default value returned for the meta.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+
+		// No ,eta data should be stored in the revision.
+		$this->assertEquals( array(), get_post_meta( $last_revision->ID ) );
+
+		// Set the test meta again.
+		update_post_meta( $post_id, 'meta_revision_test', 'test' );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Now restore the previous revision.
+		wp_restore_post_revision( $last_revision->ID );
+
+		// Verify the default meta value is still returned.
+		$this->assertEquals( 'default value', get_post_meta( $post_id, 'meta_revision_test', true ) );
+	}
+
+	/**
+	 * @dataProvider data_register_post_meta_supports_revisions
+	 */
+	public function test_register_post_meta_supports_revisions( $post_type, $meta_key, $args, $expected_is_revisioned ) {
+		register_post_meta( $post_type, $meta_key, $args );
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+				'post_type'    => $post_type,
+				'meta_input'   => array(
+					$meta_key => 'foo',
+				),
+			)
+		);
+
+		// Update the post meta and post to save.
+		update_post_meta( $post_id, $meta_key, 'bar' );
+		wp_update_post(
+			array(
+				'ID'         => $post_id,
+				'post_title' => 'updated title',
+			)
+		);
+
+		// Check the last revision for the post to see if the meta key was revisioned
+		$revisions       = wp_get_post_revisions( $post_id );
+		$revision        = array_shift( $revisions );
+		$revisioned_meta = get_post_meta( $revision->ID, $meta_key, true );
+		$this->assertEquals( $expected_is_revisioned, 'bar' === $revisioned_meta );
+
+		// Reset global so subsequent data tests do not get polluted.
+		$GLOBALS['wp_meta_keys'] = array();
+	}
+
+	public function data_register_post_meta_supports_revisions() {
+		return array(
+			array( 'post', 'registered_key1', array( 'single' => true ), false ),
+			array(
+				'post',
+				'registered_key1',
+				array(
+					'single'            => true,
+					'revisions_enabled' => true,
+				),
+				true,
+			),
+			array( 'page', 'registered_key2', array( 'revisions_enabled' => false ), false ),
+			array( 'page', 'registered_key2', array( 'revisions_enabled' => true ), true ),
+			array( '', 'registered_key3', array( 'revisions_enabled' => false ), false ),
+			array( '', 'registered_key3', array( 'revisions_enabled' => true ), true ),
+		);
+	}
+
+	/**
+	 * Assert the a post has a meta key.
+	 *
+	 * @param int    $post_id        The ID of the post to check.
+	 * @param string $meta_key The meta key to check for.
+	 */
+	protected function assertPostHasMetaKey( $post_id, $meta_key ) {
+		$this->assertArrayHasKey( $meta_key, get_metadata( 'post', $post_id ) );
+	}
+
+	/**
+	 * Assert that post does not have a meta key.
+	 *
+	 * @param int    $post_id        The ID of the post to check.
+	 * @param string $meta_key The meta key to check for.
+	 */
+	protected function assertPostNotHasMetaKey( $post_id, $meta_key ) {
+		$this->assertArrayNotHasKey( $meta_key, get_metadata( 'post', $post_id ) );
+	}
+
+	/**
+	 * Test post meta revisioning with a custom post type, as well as the "page" post type.
+	 *
+	 * @dataProvider page_post_type_data_provider
+	 */
+	public function test_revisions_stores_meta_values_page_and_cpt( $passed, $expected, $post_type, $supports_revisions = false ) {
+
+		// If the post type doesn't exist, create it, potentially supporting revisions.
+		if ( ! post_type_exists( $post_type ) ) {
+			register_post_type(
+				$post_type,
+				array(
+					'public'   => true,
+					'supports' => $supports_revisions ? array( 'revisions' ) : array(),
+				)
+			);
+		}
+
+		// Create a test post.
+		$page_id = $this->factory->post->create(
+			array(
+				'post_type'    => $post_type,
+				'post_content' => 'some initial content',
+			)
+		);
+
+		// Add the revisioning filter.
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Test revisioning.
+		update_post_meta( $page_id, 'meta_revision_test', wp_slash( $passed ) );
+
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content',
+				'ID'           => $page_id,
+			)
+		);
+
+		// Retrieve the created revision.
+		$revisions = (array) wp_get_post_revisions( $page_id );
+
+		if ( $expected ) {
+			// Go back to load the previous revision.
+			$last_revision = array_shift( $revisions );
+				wp_restore_post_revision( $last_revision->ID );
+			$this->assertEquals( $expected, get_post_meta( $page_id, 'meta_revision_test', true ) );
+		} else {
+			$this->assertEmpty( $revisions );
+		}
+	}
+
+	/**
+	 * Provide data for the page post type tests.
+	 */
+	public function page_post_type_data_provider() {
+		return array(
+			array(
+				'Test string',
+				'Test string',
+				'page',
+			),
+			array(
+				'Test string',
+				false,
+				'custom_type',
+			),
+			array(
+				'Test string',
+				'Test string',
+				'custom_type',
+				true,
+			),
+		);
+	}
+}
diff --git tests/phpunit/tests/rest-api/rest-autosaves-controller.php tests/phpunit/tests/rest-api/rest-autosaves-controller.php
index fd8f92f7b0..4108d05efc 100644
--- tests/phpunit/tests/rest-api/rest-autosaves-controller.php
+++ tests/phpunit/tests/rest-api/rest-autosaves-controller.php
@@ -215,12 +215,13 @@ class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle
 			'author',
 			'date',
 			'date_gmt',
+			'id',
+			'meta',
 			'modified',
 			'modified_gmt',
-			'guid',
-			'id',
 			'parent',
 			'slug',
+			'guid',
 			'title',
 			'excerpt',
 			'content',
@@ -288,7 +289,7 @@ class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle
 		$response   = rest_get_server()->dispatch( $request );
 		$data       = $response->get_data();
 		$properties = $data['schema']['properties'];
-		$this->assertCount( 13, $properties );
+		$this->assertCount( 14, $properties );
 		$this->assertArrayHasKey( 'author', $properties );
 		$this->assertArrayHasKey( 'content', $properties );
 		$this->assertArrayHasKey( 'date', $properties );
@@ -302,6 +303,7 @@ class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Post_Type_Controlle
 		$this->assertArrayHasKey( 'slug', $properties );
 		$this->assertArrayHasKey( 'title', $properties );
 		$this->assertArrayHasKey( 'preview_link', $properties );
+		$this->assertArrayHasKey( 'meta', $properties );
 	}
 
 	public function test_create_item() {
diff --git tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
index 9e580d5f3c..30e5b983ea 100644
--- tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
+++ tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
@@ -132,7 +132,7 @@ class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr
 			),
 		);
 
-		wp_update_post( $new_styles_post, true, false );
+		wp_update_post( $new_styles_post, true );
 
 		$new_styles_post = array(
 			'ID'           => self::$global_styles_id,
@@ -162,7 +162,7 @@ class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr
 			),
 		);
 
-		wp_update_post( $new_styles_post, true, false );
+		wp_update_post( $new_styles_post, true );
 
 		$new_styles_post = array(
 			'ID'           => self::$global_styles_id,
@@ -192,7 +192,7 @@ class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr
 			),
 		);
 
-		wp_update_post( $new_styles_post, true, false );
+		wp_update_post( $new_styles_post, true );
 		wp_set_current_user( 0 );
 	}
 
@@ -326,7 +326,7 @@ class WP_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_REST_Contr
 			'post_content' => wp_json_encode( $config ),
 		);
 
-		wp_update_post( $updated_styles_post, true, false );
+		wp_update_post( $updated_styles_post, true );
 
 		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
 		$response = rest_get_server()->dispatch( $request );
diff --git tests/phpunit/tests/rest-api/rest-post-meta-fields.php tests/phpunit/tests/rest-api/rest-post-meta-fields.php
index 11d06b86be..d049f7cce4 100644
--- tests/phpunit/tests/rest-api/rest-post-meta-fields.php
+++ tests/phpunit/tests/rest-api/rest-post-meta-fields.php
@@ -17,7 +17,7 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase {
 			'cpt',
 			array(
 				'show_in_rest' => true,
-				'supports'     => array( 'custom-fields' ),
+				'supports'     => array( 'custom-fields', 'revisions' ),
 			)
 		);
 
@@ -157,7 +157,7 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase {
 			'cpt',
 			array(
 				'show_in_rest' => true,
-				'supports'     => array( 'custom-fields' ),
+				'supports'     => array( 'custom-fields', 'revisions' ),
 			)
 		);
 
@@ -1376,8 +1376,6 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase {
 	 * @dataProvider data_update_value_return_success_with_same_value
 	 */
 	public function test_update_value_return_success_with_same_value( $meta_key, $meta_value ) {
-		add_post_meta( self::$post_id, $meta_key, $meta_value );
-
 		$this->grant_write_permission();
 
 		$data = array(
@@ -1392,6 +1390,13 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase {
 		$response = rest_get_server()->dispatch( $request );
 
 		$this->assertSame( 200, $response->get_status() );
+
+		// Verify the returned meta value is correct.
+		$data = $response->get_data();
+		$this->assertArrayHasKey( 'meta', $data );
+		$this->assertArrayHasKey( $meta_key, $data['meta'] );
+		$this->assertSame( $meta_value, $data['meta'][ $meta_key ] );
+
 	}
 
 	public function data_update_value_return_success_with_same_value() {
@@ -3112,4 +3117,359 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase {
 		}
 		return $query;
 	}
+
+
+	/**
+	 * Test that single post meta is revisioned when saving to the posts REST API endpoint.
+	 *
+	 * @ticket 20564
+	 */
+	public function test_revisioned_single_post_meta_with_posts_endpoint() {
+		$this->grant_write_permission();
+
+		register_post_meta(
+			'post',
+			'foo',
+			array(
+				'single'            => true,
+				'show_in_rest'      => true,
+				'revisions_enabled' => true,
+			)
+		);
+
+		$post_id = self::$post_id;
+
+		// Update the post, saving the meta.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 1',
+				'meta'  => array(
+					'foo' => 'bar',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions   = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id = array_shift( $revisions )->ID;
+
+		// @todo Ensure the revisions endpoint returns the correct meta values
+		// Check that the revisions endpoint returns the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$data = $response->get_data();
+		$this->assertSame( 'bar', $response->get_data()['meta']['foo'] );
+
+		// Check that the post meta is set correctly.
+		$this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) );
+
+		// Create two more revisions with different meta values for the foo key.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 2',
+				'meta'  => array(
+					'foo' => 'baz',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_2 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$this->assertSame( 'baz', $response->get_data()['meta']['foo'] );
+
+		// Check that the post meta is set correctly.
+		$this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) );
+
+		// One more revision!
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 3',
+				'meta'  => array(
+					'foo' => 'qux',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_3 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$this->assertSame( 'qux', $response->get_data()['meta']['foo'] );
+
+		// Check that the post meta is set correctly.
+		$this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) );
+
+		// Restore Revision 3 and verify the post gets the correct meta value.
+		wp_restore_post_revision( $revision_id_3 );
+		$this->assertSame( 'qux', get_post_meta( $post_id, 'foo', true ) );
+
+		// Restore Revision 2 and verify the post gets the correct meta value.
+		wp_restore_post_revision( $revision_id_2 );
+		$this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) );
+	}
+
+	/**
+	 * Test that multi-post meta is revisioned when saving to the posts REST API endpoint.
+	 *
+	 * @ticket 20564
+	 */
+	public function test_revisioned_multiple_post_meta_with_posts_endpoint() {
+		$this->grant_write_permission();
+
+		register_post_meta(
+			'post',
+			'foo',
+			array(
+				'single'            => false,
+				'show_in_rest'      => true,
+				'revisions_enabled' => true,
+			)
+		);
+
+		$post_id = self::$post_id;
+
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 1',
+				'meta'  => array(
+					'foo' => array(
+						'bar',
+						'bat',
+						'baz',
+					),
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Log the current post meta.
+		$meta = get_post_meta( $post_id );
+
+		// Update the post.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 1 update',
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_1 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_1 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		$this->assertSame(
+			array( 'bar', 'bat', 'baz' ),
+			$response->get_data()['meta']['foo']
+		);
+		$this->assertSame(
+			array( 'bar', 'bat', 'baz' ),
+			get_post_meta( $revision_id_1, 'foo' )
+		);
+
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 2',
+				'meta'  => array(
+					'foo' => array(
+						'car',
+						'cat',
+					),
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_2 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		$this->assertSame(
+			array( 'car', 'cat' ),
+			$response->get_data()['meta']['foo']
+		);
+		$this->assertSame( array( 'car', 'cat' ), get_post_meta( $revision_id_2, 'foo' ) );
+
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 3',
+				'meta'  => array(
+					'foo' => null,
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_3 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		$this->assertSame(
+			array(),
+			$response->get_data()['meta']['foo']
+		);
+		$this->assertSame( array(), get_post_meta( $revision_id_3, 'foo' ) );
+
+		// Restore Revision 3 and verify the post gets the correct meta value.
+		wp_restore_post_revision( $revision_id_3 );
+		$this->assertSame( array(), get_post_meta( $post_id, 'foo' ) );
+
+		// Restore Revision 2 and verify the post gets the correct meta value.
+		wp_restore_post_revision( $revision_id_2 );
+		$this->assertSame( array( 'car', 'cat' ), get_post_meta( $post_id, 'foo' ) );
+	}
+
+	/**
+	 * Test post meta revisions with a custom post type and the page post type.
+	 *
+	 * @group revision
+	 * @dataProvider test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt_data_provider
+	 */
+	public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt( $passed, $expected, $post_type ) {
+
+		$this->grant_write_permission();
+
+		// Create the custom meta.
+		register_post_meta(
+			$post_type,
+			'foo',
+			array(
+				'show_in_rest'      => true,
+				'revisions_enabled' => true,
+				'single'            => true,
+				'type'              => 'string',
+			)
+		);
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+				'post_type'    => $post_type,
+				'meta_input'   => array(
+					'foo' => 'foo',
+				),
+			)
+		);
+
+		$plural_mapping = array(
+			'page' => 'pages',
+			'cpt'  => 'cpt',
+		);
+		$request        = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s', $plural_mapping[ $post_type ] ) );
+
+		$response = rest_get_server()->dispatch( $request );
+
+		$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $plural_mapping[ $post_type ], $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 1',
+				'meta'  => array(
+					'foo' => $passed,
+				),
+			)
+		);
+
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Update the post.
+		$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $plural_mapping[ $post_type ], $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 1 update',
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+
+		$revision_id_1 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s/%d/revisions/%d', $plural_mapping[ $post_type ], $post_id, $revision_id_1 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		$this->assertSame(
+			$passed,
+			$response->get_data()['meta']['foo']
+		);
+
+		$this->assertSame(
+			array( $passed ),
+			get_post_meta( $revision_id_1, 'foo' )
+		);
+
+		unregister_post_meta( $post_type, 'foo' );
+		wp_delete_post( $post_id, true );
+	}
+
+	/**
+	 * Provide data for the meta revision checks.
+	 */
+	public function test_revisioned_single_post_meta_with_posts_endpoint_page_and_cpt_data_provider() {
+		return array(
+			array(
+				'Test string',
+				'Test string',
+				'cpt',
+			),
+			array(
+				'Test string',
+				'Test string',
+				'page',
+			),
+			array(
+				'Test string',
+				false,
+				'cpt',
+			),
+
+		);
+	}
 }
diff --git tests/phpunit/tests/rest-api/rest-revisions-controller.php tests/phpunit/tests/rest-api/rest-revisions-controller.php
index e0b713c882..74cd040d85 100644
--- tests/phpunit/tests/rest-api/rest-revisions-controller.php
+++ tests/phpunit/tests/rest-api/rest-revisions-controller.php
@@ -179,6 +179,7 @@ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase
 			'modified_gmt',
 			'guid',
 			'id',
+			'meta',
 			'parent',
 			'slug',
 			'title',
@@ -335,7 +336,7 @@ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase
 		$response   = rest_get_server()->dispatch( $request );
 		$data       = $response->get_data();
 		$properties = $data['schema']['properties'];
-		$this->assertCount( 12, $properties );
+		$this->assertCount( 13, $properties );
 		$this->assertArrayHasKey( 'author', $properties );
 		$this->assertArrayHasKey( 'content', $properties );
 		$this->assertArrayHasKey( 'date', $properties );
@@ -348,6 +349,7 @@ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase
 		$this->assertArrayHasKey( 'parent', $properties );
 		$this->assertArrayHasKey( 'slug', $properties );
 		$this->assertArrayHasKey( 'title', $properties );
+		$this->assertArrayHasKey( 'meta', $properties );
 	}
 
 	public function test_create_item() {
diff --git tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
index 9c4725ffbf..8af6ae9b81 100644
--- tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
+++ tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
@@ -52,6 +52,7 @@ class Tests_User_WpRegisterPersistedPreferencesMeta extends WP_UnitTestCase {
 						'additionalProperties' => true,
 					),
 				),
+				'revisions_enabled' => false,
 			),
 			$wp_meta_keys['user'][''][ $meta_key ],
 			'The registered metadata did not have the expected structure'
diff --git tests/qunit/fixtures/wp-api-generated.js tests/qunit/fixtures/wp-api-generated.js
index 36cab80cc1..8341830452 100644
--- tests/qunit/fixtures/wp-api-generated.js
+++ tests/qunit/fixtures/wp-api-generated.js
@@ -11782,6 +11782,9 @@ mockedApiResponse.postRevisions = [
         "excerpt": {
             "rendered": ""
         },
+        "meta": {
+            "meta_key": ""
+        },
         "_links": {
             "parent": [
                 {
@@ -11811,6 +11814,9 @@ mockedApiResponse.postRevisions = [
         "excerpt": {
             "rendered": "<p>REST API Client Fixture: Post</p>\n"
         },
+        "meta": {
+            "meta_key": ""
+        },
         "_links": {
             "parent": [
                 {
@@ -11841,6 +11847,9 @@ mockedApiResponse.revision = {
     },
     "excerpt": {
         "rendered": "<p>REST API Client Fixture: Post</p>\n"
+    },
+    "meta": {
+        "meta_key": ""
     }
 };
 
@@ -11866,6 +11875,9 @@ mockedApiResponse.postAutosaves = [
         "excerpt": {
             "rendered": ""
         },
+        "meta": {
+            "meta_key": ""
+        },
         "_links": {
             "parent": [
                 {
@@ -11896,6 +11908,9 @@ mockedApiResponse.autosave = {
     },
     "excerpt": {
         "rendered": ""
+    },
+    "meta": {
+        "meta_key": ""
     }
 };
 
@@ -12042,6 +12057,9 @@ mockedApiResponse.pageRevisions = [
         "excerpt": {
             "rendered": ""
         },
+        "meta": {
+            "meta_key": ""
+        },
         "_links": {
             "parent": [
                 {
@@ -12071,6 +12089,9 @@ mockedApiResponse.pageRevisions = [
         "excerpt": {
             "rendered": "<p>REST API Client Fixture: Page</p>\n"
         },
+        "meta": {
+            "meta_key": ""
+        },
         "_links": {
             "parent": [
                 {
@@ -12101,6 +12122,9 @@ mockedApiResponse.pageRevision = {
     },
     "excerpt": {
         "rendered": "<p>REST API Client Fixture: Page</p>\n"
+    },
+    "meta": {
+        "meta_key": ""
     }
 };
 
@@ -12126,6 +12150,9 @@ mockedApiResponse.pageAutosaves = [
         "excerpt": {
             "rendered": ""
         },
+        "meta": {
+            "meta_key": ""
+        },
         "_links": {
             "parent": [
                 {
@@ -12156,6 +12183,9 @@ mockedApiResponse.pageAutosave = {
     },
     "excerpt": {
         "rendered": ""
+    },
+    "meta": {
+        "meta_key": ""
     }
 };
 
