Problem/Motivation
Structure Sync currently only exports and imports the current version of taxonomy terms. If you've been editing a term through multiple revisions, all that history gets lost when you sync to other envs.
This is a problem if you need:
- Audit trails showing who changed what and when
- The full historical context of term changes (not just the latest version)
- Consistent term IDs (TIDs) across environments
- To avoid UUID constraint errors when terms already exist with different names
Steps to reproduce
1. Create a taxonomy term with multiple revisions:
- Initial: "Product Category A"
- Revision 2: "Product Category B"
- Revision 3: "Product Category C" (current)
2. Export taxonomies using Structure Sync
3. Check structure_sync.data.yml
taxonomies:
product:
- vid: product
tid: '123'
name: 'Product Category C'
# Only current version - no revision history4. Import on other envs using any import mode
5. Result:
- Only "Product Category C" exists
- All revision history (A, B) is lost
- TID may change (becomes a new auto-increment ID)
- Term references may break
Proposed resolution
Add revision support to taxonomy export/import.
Export
- Query all revisions for each term (not just current)
- Export revision metadata: revision_id, timestamp, user, log message
- Export field values for each historical revision
- Pack it all into a revisions array in the YAML
Import
The behavior differs by import mode:
Full Import:
- Preserve the original TID when creating terms
- Import the complete revision history (for both new and updated terms)
- Keep revisions in chronological order
Force Import:
- Same as Full - preserve TID and import all revisions
- Clean slate approach but with full history intact
Safe Import:
- Check both name AND UUID before creating (prevents duplicate errors)
- Skip terms that already exist
- Do NOT import revisions (safe mode = current state only, no history tampering)
Implementation
New helper function importTermRevisions() handles the revision import:
- Imports revisions in chronological order
- Preserves all custom field values
- Restores the current version as the final revision
It's all done through standard Drupal APIs - nothing fancy.
Remaining tasks
The code is working and tested with several fresh DB's, but still needs Automated tests
The patch currently has no test coverage. Would be good to add tests for:
- Terms with multiple revisions
- All three import modes (Full/Safe/Force)
- Custom fields and entity references in revisions
- UUID conflict scenarios
User interface changes
No UI changes.
API changes
New Private Static Method
private static function importTermRevisions($term, array $revision_data_array, array $entity_fields)
Purpose: Import revision history for a taxonomy term
Parameters:
- $term - The term entity to import revisions for
- $revision_data_array - Array of revision data from export
- $entity_fields - Array of custom field definitions
Behavior:
1. Stores current term values
2. Sorts revisions chronologically
3. Creates each historical revision with proper metadata
4. Restores current version as the latest revision
Modified Public Static Methods
exportTaxonomies()
- Now exports revisions array with each term when historical revisions exist.
importTaxonomiesFull()
- Now accepts tid in entity properties
- Calls importTermRevisions() for terms with revision data
importTaxonomiesSafe()
- Now includes UUID verification query
- Prevents duplicate UUID constraint violations
importTaxonomiesForce()
- Now accepts tid in entity properties
- Calls importTermRevisions() for terms with revision data
Data model changes
Export YAML Structure
Terms now include an optional revisions array:
yaml
taxonomies:
my_vocabulary:
- vid: my_vocabulary
tid: '91'
langcode: en
name: 'Current Term Name'
description__value: 'Current description'
description__format: plain_text
weight: '0'
parent: '0'
uuid: ec3ae37d-a3e6-4420-8d20-5c9f2b46247d
field_custom_field:
- value: 'current value'
revisions:
- revision_id: '91'
revision_created: '1765305454'
revision_user: null
revision_log_message: null
name: 'Historical Name 1'
description__value: 'Old description'
description__format: plain_text
weight: '0'
field_custom_field:
- value: 'old value'
- revision_id: '337'
revision_created: '1772128645'
revision_user: '1'
revision_log_message: 'Updated term'
name: 'Historical Name 2'
description__value: 'Newer description'
description__format: plain_text
weight: '0'
field_custom_field:
- value: 'newer value'Backward Compatibility
Fully backward compatible:
- Terms without revisions array import normally (current behavior)
- Old exports work with new code
- New exports work with old code (revisions array is ignored)
- No database schema changes required
Database Impact
- Uses existing revision tables (taxonomy_term_revision, taxonomy_term_field_revision)
- No new tables or columns required
- Preserves TID in Full and Force modes (uses existing tid column)
- UUID validation uses existing unique constraint
| Comment | File | Size | Author |
|---|---|---|---|
| #3 | structure_sync-3576231-taxonomy-revisions_v2.patch | 11.11 KB | gonz@meiz |
| #2 | structure_sync-3576231-taxonomy-revisions.patch | 10.43 KB | gonz@meiz |
Comments
Comment #2
gonz@meiz commentedAttaching patch that adds taxonomy term revision export/import support.
Tested on Drupal 11 with Structure Sync 2.0.8.
Comment #3
gonz@meiz commentedThe structure_sync-3576231-taxonomy-revisions patch was incomplete — importTermRevisions() was being called in Full and Force modes but was missing in Safe mode. When creating a new term in Safe mode, revisions were silently skipped.
Safe mode now follows the same pattern as Full and Force: