diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
index 75e5c6a..92ce286 100644
--- src/wp-includes/taxonomy.php
+++ src/wp-includes/taxonomy.php
@@ -2843,45 +2843,32 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) {
 		}
 	}
 
-	if ( $term_id = term_exists($slug) ) {
-		$existing_term = $wpdb->get_row( $wpdb->prepare( "SELECT name FROM $wpdb->terms WHERE term_id = %d", $term_id), ARRAY_A );
-		// We've got an existing term in the same taxonomy, which matches the name of the new term:
-		if ( is_taxonomy_hierarchical($taxonomy) && $existing_term['name'] == $name && $exists = term_exists( (int) $term_id, $taxonomy ) ) {
-			// Hierarchical, and it matches an existing term, Do not allow same "name" in the same level.
-			$siblings = get_terms($taxonomy, array('fields' => 'names', 'get' => 'all', 'parent' => $parent ) );
-			if ( in_array($name, $siblings) ) {
-				if ( $slug_provided ) {
-					return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists with this parent.' ), $exists['term_id'] );
-				} else {
-					return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $exists['term_id'] );
+	// Terms with duplicate names are not allowed at the same level of a taxonomy hierarchy.
+	if ( $exists = term_exists( $slug, $taxonomy ) ) {
+		$existing_term = get_term( $exists['term_id'], $taxonomy );
+
+		if ( $name === $existing_term->name ) {
+
+			if ( is_taxonomy_hierarchical( $taxonomy ) ) {
+				$siblings = get_terms( $taxonomy, array( 'fields' => 'names', 'get' => 'all', 'parent' => $parent ) );
+				if ( in_array( $name, $siblings ) ) {
+					return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists with this parent.' ), $exists['term_id'] );
 				}
+
 			} else {
-				$slug = wp_unique_term_slug($slug, (object) $args);
-				if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
-					return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
-				}
-				$term_id = (int) $wpdb->insert_id;
-			}
-		} elseif ( $existing_term['name'] != $name ) {
-			// We've got an existing term, with a different name, Create the new term.
-			$slug = wp_unique_term_slug($slug, (object) $args);
-			if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
-				return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
+				return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists in this taxonomy.' ), $exists['term_id'] );
 			}
-			$term_id = (int) $wpdb->insert_id;
-		} elseif ( $exists = term_exists( (int) $term_id, $taxonomy ) )  {
-			// Same name, same slug.
-			return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists.' ), $exists['term_id'] );
 		}
-	} else {
-		// This term does not exist at all in the database, Create it.
-		$slug = wp_unique_term_slug($slug, (object) $args);
-		if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
-			return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
-		}
-		$term_id = (int) $wpdb->insert_id;
 	}
 
+	$slug = wp_unique_term_slug( $slug, (object) $args );
+
+	if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
+		return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error );
+	}
+
+	$term_id = (int) $wpdb->insert_id;
+
 	// Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
 	if ( empty($slug) ) {
 		$slug = sanitize_title($slug, $term_id);
@@ -2899,9 +2886,28 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) {
 	if ( !empty($tt_id) ) {
 		return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
 	}
+
 	$wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) );
 	$tt_id = (int) $wpdb->insert_id;
 
+	/*
+	 * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than
+	 * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id
+	 * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks
+	 * are not fired.
+	 */
+	$duplicate_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, tt.term_taxonomy_id FROM $wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON ( tt.term_id = t.term_id ) WHERE t.slug = %s AND tt.parent = %d AND tt.taxonomy = %s AND t.term_id < %d AND tt.term_taxonomy_id < %d", $slug, $parent, $taxonomy, $term_id, $tt_id ) );
+	if ( $duplicate_term ) {
+		$wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) );
+		$wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) );
+
+		$term_id = (int) $duplicate_term->term_id;
+		$tt_id   = (int) $duplicate_term->term_taxonomy_id;
+
+		clean_term_cache( $term_id, $taxonomy );
+		return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id );
+	}
+
 	/**
 	 * Fires immediately after a new term is created, before the term cache is cleaned.
 	 *
@@ -3210,6 +3216,11 @@ function wp_unique_term_slug($slug, $term) {
 	if ( ! term_exists( $slug ) )
 		return $slug;
 
+	// As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies.
+	if ( get_option( 'db_version' ) >= 30056 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) {
+		return $slug;
+	}
+
 	// If the taxonomy supports hierarchy and the term has a parent, make the slug unique
 	// by incorporating parent slugs.
 	if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) {
@@ -3373,6 +3384,14 @@ function wp_update_term( $term_id, $taxonomy, $args = array() ) {
 			return new WP_Error('duplicate_term_slug', sprintf(__('The slug &#8220;%s&#8221; is already in use by another term'), $slug));
 	}
 
+	$tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
+
+	// Check to see if this is a shared terms that needs splitting.
+	$_term_id = _split_shared_term( $term_id, $tt_id );
+	if ( ! is_wp_error( $_term_id ) ) {
+		$term_id = $_term_id;
+	}
+
 	/**
 	 * Fires immediately before the given terms are edited.
 	 *
@@ -3398,8 +3417,6 @@ function wp_update_term( $term_id, $taxonomy, $args = array() ) {
 	 */
 	do_action( 'edited_terms', $term_id, $taxonomy );
 
-	$tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
-
 	/**
 	 * Fires immediate before a term-taxonomy relationship is updated.
 	 *
@@ -4029,6 +4046,59 @@ function _update_generic_term_count( $terms, $taxonomy ) {
 }
 
 /**
+ * Create a new term for a term_taxonomy item that currently shares its term.
+ *
+ * @param int   $term_id          ID of the shared term.
+ * @param int   $term_taxonomy_id ID of the term taxonomy item to receive a new term.
+ * @param array $shared_tts       Sibling term taxonomies, used for busting caches.
+ * @return int  Term ID.
+ */
+function _split_shared_term( $term_id, $term_taxonomy_id ) {
+	global $wpdb;
+
+	// Don't try to split terms if database schema does not support shared slugs.
+	$current_db_version = get_option( 'db_version' );
+	if ( $current_db_version < 30056 ) {
+		return $term_id;
+	}
+
+	// If there are no shared term_taxonomy rows, there's nothing to do here.
+	$shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
+	if ( ! $shared_tt_count ) {
+		return $term_id;
+	}
+
+	// Pull up data about the currently shared slug, which we'll use to populate the new one.
+	$shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) );
+
+	$new_term_data = array(
+		'name' => $shared_term->name,
+		'slug' => $shared_term->slug,
+		'term_group' => $shared_term->term_group,
+	);
+
+	if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) {
+		return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error );
+	}
+
+	$new_term_id = (int) $wpdb->insert_id;
+
+	// Update the existing term_taxonomy to point to the newly created term.
+	$wpdb->update( $wpdb->term_taxonomy,
+		array( 'term_id' => $new_term_id ),
+		array( 'term_taxonomy_id' => $term_taxonomy_id )
+	);
+
+	// Clean the cache for term taxonomies formerly shared with the current term.
+	$shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) );
+	foreach ( (array) $shared_term_taxonomies as $shared_term_taxonomy ) {
+		clean_term_cache( $term_id, $shared_term_taxonomy );
+	}
+
+	return $new_term_id;
+}
+
+/**
  * Generate a permalink for a taxonomy term archive.
  *
  * @since 2.5.0
diff --git tests/phpunit/tests/term.php tests/phpunit/tests/term.php
index b6fe994..1258c4b 100644
--- tests/phpunit/tests/term.php
+++ tests/phpunit/tests/term.php
@@ -226,6 +226,93 @@ class Tests_Term extends WP_UnitTestCase {
 		$this->assertFalse( is_wp_error( $term20 ) );
 	}
 
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_insert_term_duplicate_slug_same_taxonomy() {
+		register_taxonomy( 'wptests_tax', 'post' );
+		$t = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'taxonomy' => 'wptests_tax',
+		) );
+
+		$term = get_term( $t, 'wptests_tax' );
+
+		$created = wp_insert_term( 'Foo 2', 'wptests_tax', array(
+			'slug' => 'foo',
+		) );
+
+		$created_term = get_term( $created['term_id'], 'wptests_tax' );
+		$this->assertSame( 'foo-2', $created_term->slug );
+
+		_unregister_taxonomy( 'wptests_tax', 'post' );
+	}
+
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_insert_term_duplicate_slug_different_taxonomy() {
+		register_taxonomy( 'wptests_tax', 'post' );
+		register_taxonomy( 'wptests_tax_2', 'post' );
+		$t = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'taxonomy' => 'wptests_tax',
+		) );
+
+		$term = get_term( $t, 'wptests_tax' );
+
+		$created = wp_insert_term( 'Foo 2', 'wptests_tax_2', array(
+			'slug' => 'foo',
+		) );
+
+		$this->assertFalse( is_wp_error( $created ) );
+
+		$new_term = get_term( $created['term_id'], 'wptests_tax_2' );
+
+		$this->assertSame( 'foo', $new_term->slug );
+
+		_unregister_taxonomy( 'wptests_tax', 'post' );
+	}
+
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_insert_term_duplicate_slug_different_taxonomy_before_410_schema_change() {
+
+		$db_version = get_option( 'db_version' );
+		update_option( 'db_version', 30055 );
+
+		register_taxonomy( 'wptests_tax', 'post' );
+		register_taxonomy( 'wptests_tax_2', 'post' );
+		$t = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'taxonomy' => 'wptests_tax',
+		) );
+
+		$term = get_term( $t, 'wptests_tax' );
+
+		$created = wp_insert_term( 'Foo 2', 'wptests_tax_2', array(
+			'slug' => 'foo',
+		) );
+
+		$this->assertFalse( is_wp_error( $created ) );
+
+		$new_term = get_term( $created['term_id'], 'wptests_tax_2' );
+
+		/*
+		 * As of 4.1, we no longer create a shared term, but we also do not
+		 * allow for duplicate slugs.
+		 */
+		$this->assertSame( 'foo-2', $new_term->slug );
+		$this->assertNotEquals( $new_term->term_id, $term->term_id );
+
+		_unregister_taxonomy( 'wptests_tax', 'post' );
+		update_option( 'db_version', $db_version );
+	}
+
 	public function test_wp_insert_term_alias_of_no_term_group() {
 		register_taxonomy( 'wptests_tax', 'post' );
 		$t1 = $this->factory->term->create( array(
@@ -353,6 +440,19 @@ class Tests_Term extends WP_UnitTestCase {
 		$this->assertEquals( $existing_term, $found->get_error_data() );
 	}
 
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_insert_term_should_not_create_shared_term() {
+		register_taxonomy( 'wptests_tax', 'post' );
+		register_taxonomy( 'wptests_tax_2', 'post' );
+
+		$t1 = wp_insert_term( 'Foo', 'wptests_tax' );
+		$t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
+
+		$this->assertNotEquals( $t1['term_id'], $t2['term_id'] );
+	}
+
 	public function test_wp_insert_term_should_return_term_id_and_term_taxonomy_id() {
 		register_taxonomy( 'wptests_tax', 'post' );
 		$found = wp_insert_term( 'foo', 'wptests_tax' );
@@ -537,6 +637,141 @@ class Tests_Term extends WP_UnitTestCase {
 		_unregister_taxonomy( 'wptests_tax' );
 	}
 
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_update_term_duplicate_slug_same_taxonomy() {
+		register_taxonomy( 'wptests_tax', 'post' );
+
+		$t1 = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'taxonomy' => 'wptests_tax',
+		) );
+
+		$t2 = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'bar',
+			'taxonomy' => 'wptests_tax',
+		) );
+
+		$updated = wp_update_term( $t2, 'wptests_tax', array(
+			'slug' => 'foo',
+		) );
+
+		$this->assertWPError( $updated );
+		$this->assertSame( 'duplicate_term_slug', $updated->get_error_code() );
+	}
+
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_update_term_duplicate_slug_different_taxonomy() {
+		register_taxonomy( 'wptests_tax', 'post' );
+		register_taxonomy( 'wptests_tax_2', 'post' );
+
+		$t1 = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'taxonomy' => 'wptests_tax',
+		) );
+
+		$t2 = $this->factory->term->create( array(
+			'name' => 'Foo',
+			'slug' => 'bar',
+			'taxonomy' => 'wptests_tax_2',
+		) );
+
+		$updated = wp_update_term( $t2, 'wptests_tax_2', array(
+			'slug' => 'foo',
+		) );
+
+		$this->assertWPError( $updated );
+		$this->assertSame( 'duplicate_term_slug', $updated->get_error_code() );
+	}
+
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_update_term_should_split_shared_term() {
+		global $wpdb;
+
+		register_taxonomy( 'wptests_tax', 'post' );
+		register_taxonomy( 'wptests_tax_2', 'post' );
+
+		$t1 = wp_insert_term( 'Foo', 'wptests_tax' );
+		$t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
+
+		// Manually modify because split terms shouldn't naturally occur.
+		$wpdb->update( $wpdb->term_taxonomy,
+			array( 'term_id' => $t1['term_id'] ),
+			array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
+			array( '%d' ),
+			array( '%d' )
+		);
+
+		$posts = $this->factory->post->create_many( 2 );
+		wp_set_object_terms( $posts[0], array( 'Foo' ), 'wptests_tax' );
+		wp_set_object_terms( $posts[1], array( 'Foo' ), 'wptests_tax_2' );
+
+		// Verify that the terms are shared.
+		$t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
+		$t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
+		$this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
+
+		wp_update_term( $t2_terms[0]->term_id, 'wptests_tax_2', array(
+			'name' => 'New Foo',
+		) );
+
+		$t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
+		$t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
+		$this->assertNotEquals( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
+	}
+
+	/**
+	 * @ticket 5809
+	 */
+	public function test_wp_update_term_should_not_split_shared_term_before_410_schema_change() {
+		global $wpdb;
+
+		$db_version = get_option( 'db_version' );
+		update_option( 'db_version', 30055 );
+
+		register_taxonomy( 'wptests_tax', 'post' );
+		register_taxonomy( 'wptests_tax_2', 'post' );
+
+		$t1 = wp_insert_term( 'Foo', 'wptests_tax' );
+		$t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
+
+		// Manually modify because split terms shouldn't naturally occur.
+		$wpdb->update( $wpdb->term_taxonomy,
+			array( 'term_id' => $t1['term_id'] ),
+			array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
+			array( '%d' ),
+			array( '%d' )
+		);
+
+		$posts = $this->factory->post->create_many( 2 );
+		wp_set_object_terms( $posts[0], array( 'Foo' ), 'wptests_tax' );
+		wp_set_object_terms( $posts[1], array( 'Foo' ), 'wptests_tax_2' );
+
+		// Verify that the term is shared.
+		$t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
+		$t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
+		$this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
+
+		wp_update_term( $t2_terms[0]->term_id, 'wptests_tax_2', array(
+			'name' => 'New Foo',
+		) );
+
+		// Term should still be shared.
+		$t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
+		$t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
+		$this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
+
+		update_option( 'db_version', $db_version );
+	}
+
 	public function test_wp_update_term_alias_of_no_term_group() {
 		register_taxonomy( 'wptests_tax', 'post' );
 		$t1 = $this->factory->term->create( array(
@@ -1366,52 +1601,6 @@ class Tests_Term extends WP_UnitTestCase {
 	}
 
 	/**
-	 * @ticket 5809
-	 */
-	function test_update_shared_term() {
-		$random_tax = __FUNCTION__;
-
-		register_taxonomy( $random_tax, 'post' );
-
-		$post_id = $this->factory->post->create();
-
-		$old_name = 'Initial';
-
-		$t1 = wp_insert_term( $old_name, 'category' );
-		$t2 = wp_insert_term( $old_name, 'post_tag' );
-
-		$this->assertEquals( $t1['term_id'], $t2['term_id'] );
-
-		wp_set_post_categories( $post_id, array( $t1['term_id'] ) );
-		wp_set_post_tags( $post_id, array( (int) $t2['term_id'] ) );
-
-		$new_name = 'Updated';
-
-		// create the term in a third taxonomy, just to keep things interesting
-		$t3 = wp_insert_term( $old_name, $random_tax );
-		wp_set_post_terms( $post_id, array( (int) $t3['term_id'] ), $random_tax );
-		$this->assertPostHasTerms( $post_id, array( $t3['term_id'] ), $random_tax );
-
-		$t2_updated = wp_update_term( $t2['term_id'], 'post_tag', array(
-			'name' => $new_name
-		) );
-
-		$this->assertNotEquals( $t2_updated['term_id'], $t3['term_id'] );
-
-		// make sure the terms have split
-		$this->assertEquals( $old_name, get_term_field( 'name', $t1['term_id'], 'category' ) );
-		$this->assertEquals( $new_name, get_term_field( 'name', $t2_updated['term_id'], 'post_tag' ) );
-
-		// and that they are still assigned to the correct post
-		$this->assertPostHasTerms( $post_id, array( $t1['term_id'] ), 'category' );
-		$this->assertPostHasTerms( $post_id, array( $t2_updated['term_id'] ), 'post_tag' );
-		$this->assertPostHasTerms( $post_id, array( $t3['term_id'] ), $random_tax );
-
-		// clean up
-		unset( $GLOBALS['wp_taxonomies'][ $random_tax ] );
-	}
-
-	/**
 	 * @ticket 25852
 	 */
 	function test_sanitize_term_field() {
