diff --git a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php
index 4bf846e854eb0dd250be2d5eb6c8a3141736c5f7..ec22c3f65e8ad49c9bfbbea07b45af01c1d2fcc1 100644
--- a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php
@@ -108,11 +108,6 @@ abstract class SqlBase extends SourcePluginBase implements ContainerFactoryPlugi
   public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state) {
     parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
     $this->state = $state;
-    // If we are using high water, but haven't yet set a high water mark, skip
-    // joining the map table, as we want to get all available records.
-    if ($this->getHighWaterProperty() && $this->getHighWater() === NULL) {
-      $this->configuration['ignore_map'] = TRUE;
-    }
   }
 
   /**
@@ -285,7 +280,7 @@ protected function initializeIterator() {
       $conditions = $this->query->orConditionGroup();
       $condition_added = FALSE;
       $added_fields = [];
-      if (empty($this->configuration['ignore_map']) && $this->mapJoinable()) {
+      if ($this->mapJoinable()) {
         // Build the join to the map table. Because the source key could have
         // multiple fields, we need to build things up.
         $count = 1;
@@ -404,6 +399,24 @@ public function count($refresh = FALSE) {
    *   TRUE if we can join against the map table otherwise FALSE.
    */
   protected function mapJoinable() {
+
+    // Do not join map if explicitly configured not to.
+    if (isset($this->configuration['ignore_map'])  && $this->configuration['ignore_map']) {
+      return FALSE;
+    }
+
+    // If we are using high water, but haven't yet set a high water mark, do not
+    // join the map table, as we want to get all available records.
+    if ($this->getHighWaterProperty() && $this->getHighWater() === NULL) {
+      return FALSE;
+    }
+
+    // If we are tracking changes, we also need to retrieve all rows to compare
+    // hashes
+    if ($this->trackChanges) {
+      return FALSE;
+    }
+
     if (!$this->getIds()) {
       return FALSE;
     }
diff --git a/core/modules/migrate/tests/modules/migrate_track_changes_test/config/install/taxonomy.vocabulary.track_changes_import_term.yml b/core/modules/migrate/tests/modules/migrate_track_changes_test/config/install/taxonomy.vocabulary.track_changes_import_term.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8bbc5d7f048fce4dd8467fbd4b67e5b048210e72
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_track_changes_test/config/install/taxonomy.vocabulary.track_changes_import_term.yml
@@ -0,0 +1,7 @@
+langcode: en
+status: true
+dependencies: {  }
+name: Track changes import term
+vid: track_changes_import_term
+description: ''
+weight: 0
diff --git a/core/modules/migrate/tests/modules/migrate_track_changes_test/migrate_track_changes_test.info.yml b/core/modules/migrate/tests/modules/migrate_track_changes_test/migrate_track_changes_test.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6f41410692907a36da807b1cf5bfce7621cef7e2
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_track_changes_test/migrate_track_changes_test.info.yml
@@ -0,0 +1,7 @@
+type: module
+name: Migration Track Changes Test
+description: 'Provides test fixtures for testing track changes marks.'
+package: Testing
+core: 8.x
+dependencies:
+  - drupal:migrate
diff --git a/core/modules/migrate/tests/modules/migrate_track_changes_test/migrations/track_changes_test.yml b/core/modules/migrate/tests/modules/migrate_track_changes_test/migrations/track_changes_test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..31d03476fc666ae8954116651b0b84c44dc64c9e
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_track_changes_test/migrations/track_changes_test.yml
@@ -0,0 +1,18 @@
+id: track_changes_test
+label: Track changes test.
+source:
+  plugin: track_changes_test
+  track_changes: true
+destination:
+  plugin: entity:taxonomy_term
+migration_tags:
+  test: test
+process:
+  name: name
+  vid:
+    plugin: default_value
+    default_value: track_changes_import_term
+  'description/value': description
+  'description/format':
+    plugin: default_value
+    default_value: 'basic_html'
diff --git a/core/modules/migrate/tests/modules/migrate_track_changes_test/src/Plugin/migrate/source/TrackChangesTest.php b/core/modules/migrate/tests/modules/migrate_track_changes_test/src/Plugin/migrate/source/TrackChangesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..202a3057923aeea61912381d1da3a330df05445b
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_track_changes_test/src/Plugin/migrate/source/TrackChangesTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\migrate_track_changes_test\Plugin\migrate\source;
+
+use Drupal\migrate\Plugin\migrate\source\SqlBase;
+
+/**
+ * Source plugin for migration track changes tests.
+ *
+ * @MigrateSource(
+ *   id = "track_changes_test"
+ * )
+ */
+class TrackChangesTest extends SqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    $field_names = array_keys($this->fields());
+    $query = $this
+      ->select('track_changes_term', 't')
+      ->fields('t', $field_names);
+    foreach ($field_names as $field_name) {
+      $query->groupBy($field_name);
+    }
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    $fields = [
+      'tid' => $this->t('Term id'),
+      'name' => $this->t('Name'),
+      'description' => $this->t('Description'),
+    ];
+
+    return $fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    return [
+      'tid' => [
+        'type' => 'integer',
+      ],
+    ];
+  }
+
+}
diff --git a/core/modules/migrate/tests/src/Kernel/TrackChangesTest.php b/core/modules/migrate/tests/src/Kernel/TrackChangesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e4bb1a988dd9a8f66f808dcf9856a35d1839749
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/TrackChangesTest.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Drupal\Tests\migrate\Kernel;
+
+/**
+ * Tests migration track changes property.
+ *
+ * @group migrate
+ */
+class TrackChangesTest extends MigrateTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'system',
+    'user',
+    'taxonomy',
+    'migrate',
+    'migrate_track_changes_test',
+    'text',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    // Create source test table.
+    $this->sourceDatabase->schema()->createTable('track_changes_term', [
+      'fields' => [
+        'tid' => [
+          'description' => 'Serial',
+          'type' => 'serial',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+        ],
+        'name' => [
+          'description' => 'Name',
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+        ],
+        'description' => [
+          'description' => 'Name',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => FALSE,
+          'default' => '',
+        ],
+      ],
+      'primary key' => [
+        'tid',
+      ],
+      'description' => 'Contains taxonomy terms to import',
+    ]);
+
+    // Add 4 items to source table.
+    $this->sourceDatabase->insert('track_changes_term')
+      ->fields([
+        'name',
+        'description',
+      ])
+      ->values([
+        'name' => 'Item 1',
+        'description' => 'Text item 1',
+      ])
+      ->values([
+        'name' => 'Item 2',
+        'description' => 'Text item 2',
+      ])
+      ->values([
+        'name' => 'Item 3',
+        'description' => 'Text item 3',
+      ])
+      ->values([
+        'name' => 'Item 4',
+        'description' => 'Text item 4',
+      ])
+      ->execute();
+
+    $this->installEntitySchema('taxonomy_term');
+    $this->installEntitySchema('user');
+
+    $this->executeMigration('track_changes_test');
+  }
+
+  /**
+   * Tests track changes property of SqlBase.
+   */
+  public function testTrackChanges() {
+    // Assert all of the terms have been imported.
+    $this->assertTermExists('name', 'Item 1');
+    $this->assertTermExists('name', 'Item 2');
+    $this->assertTermExists('description', 'Text item 3');
+    $this->assertTermExists('description', 'Text item 4');
+
+    // Update Item 1 triggering its track_changes by name.
+    $this->sourceDatabase->update('track_changes_term')
+      ->fields([
+        'name' => 'Item 1 updated',
+      ])
+      ->condition('name', 'Item 1')
+      ->execute();
+
+    // Update Item 2 keeping it's track_changes name the same.
+    $this->sourceDatabase->update('track_changes_term')
+      ->fields([
+        'name' => 'Item 2',
+      ])
+      ->condition('name', 'Item 2')
+      ->execute();
+
+    // Update Item 3 triggering its track_changes by field.
+    $this->sourceDatabase->update('track_changes_term')
+      ->fields([
+        'description' => 'Text item 3 updated',
+      ])
+      ->condition('name', 'Item 3')
+      ->execute();
+
+    // Update Item 2 keeping it's track_changes field the same.
+    $this->sourceDatabase->update('track_changes_term')
+      ->fields([
+        'description' => 'Text item 4',
+      ])
+      ->condition('name', 'Item 4')
+      ->execute();
+
+    // Execute migration again.
+    $this->executeMigration('track_changes_test');
+
+    // Item with name changes should be updated.
+    $this->assertTermExists('name', 'Item 1 updated');
+    $this->assertTermDoesNotExist('name', 'Item 1');
+
+    // Item without name changes should not be updated.
+    $this->assertTermExists('name', 'Item 2');
+
+    // Item with field changes should be updated.
+    $this->assertTermExists('description', 'Text item 3 updated');
+    $this->assertTermDoesNotExist('description', 'Text item 3');
+
+    // Item without field changes should not be updated.
+    $this->assertTermExists('description', 'Text item 4');
+  }
+
+  /**
+   * Assert that term with given name exists.
+   *
+   * @param string $property
+   *   Property to evaluate.
+   * @param string $value
+   *   Value to evaluate.
+   */
+  protected function assertTermExists($property, $value) {
+    self::assertTrue($this->termExists($property, $value));
+  }
+
+  /**
+   * Assert that term with given title does not exist.
+   *
+   * @param string $property
+   *   Property to evaluate.
+   * @param string $value
+   *   Value to evaluate.
+   */
+  protected function assertTermDoesNotExist($property, $value) {
+    self::assertFalse($this->termExists($property, $value));
+  }
+
+  /**
+   * Checks if term with given name exists.
+   *
+   * @param string $property
+   *   Property to evaluate.
+   * @param string $value
+   *   Value to evaluate.
+   *
+   * @return bool
+   */
+  protected function termExists($property, $value) {
+    $property = $property === 'description' ? 'description__value' : $property;
+    $query = \Drupal::entityQuery('taxonomy_term');
+    $result = $query
+      ->condition($property, $value)
+      ->range(0, 1)
+      ->execute();
+
+    return !empty($result);
+  }
+
+}