Index: src/wp-admin/includes/post.php
===================================================================
--- src/wp-admin/includes/post.php	(revision 26908)
+++ src/wp-admin/includes/post.php	(working copy)
@@ -1350,6 +1350,17 @@
 		$new_autosave['ID'] = $old_autosave->ID;
 		$new_autosave['post_author'] = $post_author;

+		// Auto-save revisioned meta fields.
+		foreach ( _wp_post_revision_meta_keys() as $meta_key ) {
+			if ( isset( $_POST[ $meta_key ] ) && get_post_meta( $new_autosave['ID'], $meta_key, true ) != $_POST[ $meta_key ] ) {
+				// Use the underlying delete_metadata and add_metadata 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 );
+				if ( ! empty( $_POST[ $meta_key ] ) )
+					add_metadata( 'post', $new_autosave['ID'], $meta_key, $_POST[ $meta_key ] );
+			}
+		}
+
 		// If the new autosave is the same content as the post, delete the old autosave.
 		$post = get_post( $post_id );
 		$autosave_is_different = false;
Index: src/wp-includes/revision.php
===================================================================
--- src/wp-includes/revision.php	(revision 26908)
+++ src/wp-includes/revision.php	(working copy)
@@ -28,13 +28,20 @@
 	if ( !$fields ) {
 		// Allow these to be versioned
 		$fields = array(
-			'post_title' => __( 'Title' ),
+			'post_title'   => __( 'Title' ),
 			'post_content' => __( 'Content' ),
 			'post_excerpt' => __( 'Excerpt' ),
 		);

-		// Runs only once
-		$fields = apply_filters( '_wp_post_revision_fields', $fields );
+		/**
+		 * Filter the list of post fields that are revisioned.
+		 *
+		 * @since 3.9.0
+		 *
+		 * @param array $fields An array of fields to be be revisioned.
+		 *
+		 */
+		$fields = apply_filters( '_wp_post_revision_fields', $fields ); // Runs only once

 		// WP uses these internally either in versioning or elsewhere - they cannot be versioned
 		foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect )
@@ -59,6 +66,30 @@
 }

 /**
+ * Determines which post meta fields are revisioned.
+ *
+ * @since 3.9.0
+ *
+ * @access private
+ * @return array An array of meta keys that should be revisioned.
+ *
+ */
+function _wp_post_revision_meta_keys() {
+	// Default meta keys to be revisioned
+	$keys = array();
+
+	/**
+	 * Filter the list of post meta keys that are revisioned.
+	 *
+	 * @since 3.9.0
+	 *
+	 * @param array $keys An array of fields to be be revisioned.
+	 *
+	 */
+	return apply_filters( 'wp_post_revision_meta_keys', $keys );
+}
+
+/**
  * Saves an already existing post as a post revision.
  *
  * Typically used immediately after post updates.
@@ -102,13 +133,23 @@
 		if ( isset( $last_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', true, $last_revision, $post ) ) {
 			$post_has_changed = false;

+			// Check whether revisioned fields have been changed
 			foreach ( array_keys( _wp_post_revision_fields() ) as $field ) {
 				if ( normalize_whitespace( $post->$field ) != normalize_whitespace( $last_revision->$field ) ) {
 					$post_has_changed = true;
 					break;
 				}
 			}
-			//don't save revision if post unchanged
+
+			// Check whether revisioned meta fields have changed
+			foreach ( _wp_post_revision_meta_keys() as $meta_key ) {
+				if ( get_post_meta( $post->ID, $meta_key ) != get_post_meta( $last_revision->ID, $meta_key ) ) {
+					$post_has_changed = true;
+					break;
+				}
+			}
+
+			// Don't save revision if post unchanged
 			if( ! $post_has_changed )
 				return;
 		}
@@ -240,9 +281,19 @@
 	if ( $revision_id )
 		do_action( '_wp_put_post_revision', $revision_id );

-	return $revision_id;
-}
+	// Save revisioned meta fields.
+	foreach ( _wp_post_revision_meta_keys() as $meta_key ) {
+		$meta_value = get_post_meta( $post_id, $meta_key, true );
+		if ( empty( $meta_value ) )
+			continue;
+		// Use the underlying add_metadata vs add_post_meta to make sure
+		// metadata is added to the revision post and not its parent.
+		add_metadata( 'post', $revision_id, $meta_key, wp_slash( $meta_value ) );
+	}

+		return $revision_id;
+	}
+
 /**
  * Gets a post revision.
  *
@@ -308,8 +359,28 @@

 	$update['ID'] = $revision['post_parent'];

+	// Restore revisioned meta fields.
+	foreach ( _wp_post_revision_meta_keys() as $meta_key ) {
+		$meta_value = get_post_meta( $revision['ID'], $meta_key, true );
+		if ( empty( $meta_value ) )
+			$meta_value = '';
+		// Add slashes to data pulled from the db
+		update_post_meta( $update['ID'], $meta_key, wp_slash( $meta_value ) );
+	}
+
 	$update = wp_slash( $update ); //since data is from db

+	// Restore revisioned meta fields.
+	foreach ( _wp_post_revision_meta_keys() as $meta_key ) {
+		delete_post_meta( $update['ID'], $meta_key );
+		$meta_values = get_post_meta( $revision['ID'], $meta_key );
+		if ( false === $meta_values )
+			continue;
+
+		foreach ( $meta_values as $meta_value )
+			add_post_meta( $update['ID'], $meta_key, $meta_value );
+	}
+
 	$post_id = wp_update_post( $update );
 	if ( ! $post_id || is_wp_error( $post_id ) )
 		return $post_id;
@@ -449,6 +520,7 @@
 	$post->post_excerpt = $preview->post_excerpt;

 	add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
+  	add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 );

 	return $post;
 }
@@ -472,6 +544,23 @@
 }

 /**
+ * Filters post meta retrieval to get values from the actual autosave post,
+ * and not its parent. Filters revisioned meta keys only.
+ *
+ * @since 3.9.0
+ * @access private
+ */
+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() ) || 'revision' == $post->post_type )
+		return $value;
+	$preview = wp_get_post_autosave( $post->ID );
+	if ( ! is_object( $preview ) )
+		return $value;
+	return get_post_meta( $preview->ID, $meta_key, $single );
+}
+
+/**
  * Filters terms lookup to set the post format.
  *
  * @since 3.6.0
Index: tests/phpunit/tests/post/revisions.php
===================================================================
--- tests/phpunit/tests/post/revisions.php	(revision 26908)
+++ tests/phpunit/tests/post/revisions.php	(working copy)
@@ -338,4 +338,90 @@
 			$this->assertTrue( user_can( $author_user_id, 'read_post', $revision->ID ) );
 		}
 	}
+
+	/**
+	 * Test the revisions system for storage of meta values
+	 * @ticket 20564
+	 */
+	function test_revisions_stores_meta_values() {
+		// Set up a new post
+		$original_post_id = $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 ) );
+
+		/**
+		 * 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 ) );
+
+		//  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 ) );
+
+		/**
+		 * Now restore the original revision
+		 */
+
+		// Get all the revisions
+		$revisions = (Array)wp_get_post_revisions( $post_id );
+
+		// Go back two revisions (the 1st 'previous' revision matches the current post)
+		array_pop( $revisions );
+		$last_revision = array_pop( $revisions );
+
+		// Restore!
+		wp_restore_post_revision( $last_revision->ID );
+
+		/**
+		 * 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 ) );
+
+		/*
+		 * Now 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', function( $keys ) {
+			$keys[] = 'meta_revision_test';
+			return $keys;
+		});
+
+		/**
+		 * Save new meta in the revisioned field
+		 */
+
+		// Save the post again, custom meta should now be revisioned - note no change in content, revision still saved
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Store custom meta values, which should now be revisioned
+		update_post_meta( $post_id, 'meta_revision_test', 'update2' );
+
+		// Save the post again
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Retore the previous revision
+		$revisions = wp_get_post_revisions( $post_id );
+
+		// Go back two to load the previous revision
+		array_pop( $revisions );
+		$last_revision = array_pop( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		// Verify that previous post meta is restored
+		$this->assertEquals( 'update1', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Cleanup!
+		wp_delete_post( $original_post_id );
+
+	}
 }
