Index: src/wp-admin/includes/post.php
===================================================================
--- src/wp-admin/includes/post.php	(revision 28832)
+++ src/wp-admin/includes/post.php	(working copy)
@@ -1523,6 +1523,24 @@
 		$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 ] )
+				&& '' !== $_POST[ $meta_key ]
+				&& get_post_meta( $new_autosave['ID'], $meta_key, true ) != wp_unslash( $_POST[ $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 );
+				if ( ! empty( $_POST[ $meta_key ] ) ) {
+					add_metadata( 'post', $new_autosave['ID'], $meta_key, $_POST[ $meta_key ] );
+				}
+			}
+		}
+
 		// If the new autosave has the same content as the post, delete the autosave.
 		$post = get_post( $post_id );
 		$autosave_is_different = false;
Index: src/wp-content/themes/twentyfourteen/functions.php
===================================================================
--- src/wp-content/themes/twentyfourteen/functions.php	(revision 28832)
+++ src/wp-content/themes/twentyfourteen/functions.php	(working copy)
@@ -25,6 +25,7 @@
  * @since Twenty Fourteen 1.0
  */
 
+
 /**
  * Set up the content width value based on the theme's design.
  *
Index: src/wp-includes/revision.php
===================================================================
--- src/wp-includes/revision.php	(revision 28832)
+++ src/wp-includes/revision.php	(working copy)
@@ -70,6 +70,25 @@
 }
 
 /**
+ * Determine which post meta fields should be revisioned.
+ *
+ * @access private
+ * @since 4.0.0
+ *
+ * @return array An array of meta keys to be revisioned.
+ */
+function _wp_post_revision_meta_keys() {
+	/**
+	 * Filter the list of post meta keys to be revisioned.
+	 *
+	 * @since 4.0.0
+	 *
+	 * @param array $keys An array of default meta fields to be revisioned.
+	 */
+	return apply_filters( 'wp_post_revision_meta_keys', array() );
+}
+
+/**
  * Saves an already existing post as a post revision.
  *
  * Typically used immediately after post updates.
@@ -127,6 +146,7 @@
 		if ( isset( $last_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', $check_for_changes = true, $last_revision, $post ) ) {
 			$post_has_changed = false;
 
+			// Check whether revisioned post 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;
@@ -133,7 +153,16 @@
 					break;
 				}
 			}
-			//don't save revision if post unchanged
+
+			// Check whether revisioned post 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 the post is unchanged.
 			if( ! $post_has_changed )
 				return;
 		}
@@ -253,6 +282,7 @@
 	if ( isset($post['post_type']) && 'revision' == $post['post_type'] )
 		return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) );
 
+	$post_id = (int) $post['ID'];
 	$post = _wp_post_revision_fields( $post, $autosave );
 	$post = wp_slash($post); //since data is from db
 
@@ -271,6 +301,23 @@
 		do_action( '_wp_put_post_revision', $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 );
+
+		// Don't save blank meta values
+		if( '' == $meta_value ) {
+			continue;
+		}
+		/*
+		 * Use the underlying add_metadata() function vs add_post_meta()
+		 * to ensure metadata is added to the revision post and not its parent.
+		 */
+		add_metadata( 'post', $revision_id, $meta_key, wp_slash( $meta_value ) );
+	}
+	// Save the revisioned meta keys so we know which meta keys were revisioned
+	add_metadata( 'post', $revision_id, '_wp_post_revision_meta_keys',  _wp_post_revision_meta_keys() );
+
 	return $revision_id;
 }
 
@@ -336,6 +383,17 @@
 
 	$update['ID'] = $revision['post_parent'];
 
+	// Restore revisioned meta fields; first get the keys for this revision
+	$metas_revisioned =  wp_unslash( get_metadata( 'post', $revision_id, '_wp_post_revision_meta_keys' ) );
+
+	if ( ! empty( $metas_revisioned[0] ) ) {
+		foreach ( $metas_revisioned[0] as $meta_key ) {
+			// Get the stored meta, not stored === blank
+			$meta_value = get_post_meta( $revision['ID'], $meta_key, true );
+			// Add slashes to data pulled from the database.
+			update_post_meta( $update['ID'], $meta_key, wp_slash( $meta_value ) );
+		}
+	}
 	$update = wp_slash( $update ); //since data is from db
 
 	$post_id = wp_update_post( $update );
@@ -503,6 +561,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;
 }
@@ -526,6 +585,42 @@
 }
 
 /**
+ * Filters post meta retrieval to get values from the actual autosave post,
+ * and not its parent.
+ *
+ * Filters revisioned meta keys only.
+ *
+ * @access private
+ * @since 4.0.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 revisionm 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() )
+		|| 'revision' == $post->post_type )
+	{
+		return $value;
+	}
+
+	// Grab the autosave.
+	$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 28832)
+++ tests/phpunit/tests/post/revisions.php	(working copy)
@@ -385,4 +385,152 @@
 		$this->assertEquals( 100, $ok );
 		$this->assertEquals( 0, $reversed );
 	}
+
+	/**
+	 * 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 ) );
+
+		// 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' );
+
+
+		/*
+		 * 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 the post, changing content to force a revision
+		wp_update_post( array( 'post_content'	=> 'more updated content', 'ID' => $post_id ) );
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 5, $revisions );
+
+		// 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, becaused 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 ) );
+
+		// Retore 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 ) );
+
+		// Retore 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 restored
+		$this->assertEquals( '', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Test not tracking a key
+		remove_all_filters( 'wp_post_revision_meta_keys' );
+
+		// 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' ) );
+		// Retore 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 ) );
+
+
+		// Cleanup!
+		wp_delete_post( $original_post_id );
+
+	}
 }
