Index: src/wp-includes/post.php
===================================================================
--- src/wp-includes/post.php	(revision 37858)
+++ src/wp-includes/post.php	(working copy)
@@ -3031,7 +3031,8 @@
  *     @type string $post_mime_type        The mime type of the post. Default empty.
  *     @type string $guid                  Global Unique ID for referencing the post. Default empty.
  *     @type array  $post_category         Array of category names, slugs, or IDs.
- *                                         Defaults to value of the 'default_category' option.
+ *                                         Defaults to value of the 'default_category' option if inserting a new
+ *                                         post.
  *     @type array  $tax_input             Array of taxonomy terms keyed by their taxonomy name. Default empty.
  *     @type array  $meta_input            Array of post meta values keyed by their post meta key. Default empty.
  * }
@@ -3140,6 +3141,9 @@
 		$post_status = 'inherit';
 	}
 
+	// Take note of whether updates to post categories were requested.
+	$update_post_categories = isset( $postarr['post_category'] );
+
 	if ( ! empty( $postarr['post_category'] ) ) {
 		// Filter out empty terms.
 		$post_category = array_filter( $postarr['post_category'] );
@@ -3400,8 +3404,15 @@
 		clean_post_cache( $post_ID );
 	}
 
+	/**
+	 * Add/update categories if:
+	 *     - we're inserting a new post
+	 *     - we're updating a post and post_categories were specifically requested
+	 */
 	if ( is_object_in_taxonomy( $post_type, 'category' ) ) {
-		wp_set_post_categories( $post_ID, $post_category );
+		if ( ! $update || ( $update && $update_post_categories ) ) {
+			wp_set_post_categories( $post_ID, $post_category );
+		}
 	}
 
 	if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) {
Index: tests/phpunit/tests/post/wpInsertPost.php
===================================================================
--- tests/phpunit/tests/post/wpInsertPost.php	(revision 37858)
+++ tests/phpunit/tests/post/wpInsertPost.php	(working copy)
@@ -89,4 +89,41 @@
 		$this->assertEquals( 'about', get_post( $another_about_page_id )->post_name );
 		$this->assertEquals( 'about-2', get_post( $about_page_id )->post_name );
 	}
+
+	function test_updating_a_post_should_not_trash_categories() {
+		// Create a post, and a term, and attach them.
+		$post_id = self::factory()->post->create( array(
+			'post_type' => 'post',
+			'post_title' => 'Post with categories',
+			'post_status' => 'publish'
+		) );
+		$term_id = self::factory()->term->create( array(
+			'name' => 'Term',
+			'taxonomy' => 'category'
+		) );
+		wp_set_object_terms( $post_id, array( $term_id ), 'category' );
+
+		// Validate that the term got assigned.
+		$assigned_terms = wp_get_object_terms( array( $post_id ), array( 'category' ), array() );
+		$this->assertCount( 1, $assigned_terms );
+		$this->assertEquals( $term_id, $assigned_terms[0]->term_id );
+
+		// Update the post with no changes.
+		$post = get_post( $post_id );
+		wp_insert_post( $post );
+
+		// Validate that the term is still assigned.
+		$assigned_terms = wp_get_object_terms( array( $post_id ), array( 'category' ), array() );
+		$this->assertCount( 1, $assigned_terms );
+		$this->assertEquals( $term_id, $assigned_terms[0]->term_id );
+
+		// Remove the term from the post.
+		$post->post_category = array();
+		wp_insert_post($post);
+		$assigned_terms = wp_get_object_terms( array( $post_id ), array( 'category' ), array() );
+
+		// Validate that the post has had the default category assigned again.
+		$this->assertCount( 1, $assigned_terms );
+		$this->assertEquals( get_option( 'default_category' ), $assigned_terms[0]->term_id );
+	}
 }
