Repository: directus/directus
File: app/src/modules/content/composables/use-comparison.ts (TypeScript)

Code before:

		if (currentVersion) {
			const versionComparison = await fetchVersionComparison(currentVersion.id);
			mainItem = versionComparison.base || {};
			versionDelta = versionComparison.incoming || {};
		} else if ('collection' in revision && 'item' in revision) {
			const { collection, item } = revision as { collection: string; item: string | number };
			const itemEndpoint = getItemEndpoint(collection, item);

			try {
				const itemResponse = await api.get(itemEndpoint);
				mainItem = itemResponse.data?.data || {};
			} catch (error: any) {
				unexpectedError(error);
				mainItem = {};
			}
		}

		const baseMerged = mergeWith({}, mainItem, versionDelta || {}, replaceArraysInMergeCustomizer);

		const targetCollection = revision.collection || collection?.value || '';
		const fields = fieldsStore.getFieldsForCollection(targetCollection);

		const revisionData = revision.data || {};
		const revisionDataWithDefaults = mergeMissingMainItemKeysIntoRevision(revisionData, baseMerged, fields);
		const incomingMerged = mergeWith({}, baseMerged, revisionDataWithDefaults, replaceArraysInMergeCustomizer);

		if ('activity' in revision && (revision as any)?.activity?.timestamp) {
			incomingMerged.date_updated = (revision as any).activity.timestamp;
		}

		const incomingWithRelationalFields = copySpecialFieldsFromBaseToIncoming(baseMerged, incomingMerged, fields);
		const revisionsList = revisions || [];
		const revisionId = 'id' in revision ? revision.id : null;

		const revisionFields = new Set(Object.keys(revision.delta ?? {}));

		return {
			base: baseMerged,
			incoming: incomingWithRelationalFields,
			selectableDeltas: revisionsList,
			revisionFields,
			comparisonType: 'revision',
			outdated: false,
			mainHash: '',
			currentVersion: currentVersion || null,
			initialSelectedDeltaId: revisionId || undefined,
		};
	}

	return {

Review comment (suggestion):
This creates a new issue. If a field that was previously blank becomes populated in a later revision, you cannot apply the previous blank state, as all revisions are being merged onto the base item, which has the populated field.

To reproduce this:
- Create a new item with 3 fields populated and at least one field left blank.
- Create a version
- Make a change to one of the fields that has a value (do this two times)
- Now add a value to one of the fields you left blank.
- Try to go back to a revision that includes the blank state of the field.


Code after:
			throw error;
		}
	}

	async function fetchMainVersion(collection: string, item: PrimaryKey): Promise<Record<string, any>> {
		const itemEndpoint = getItemEndpoint(collection, item);

		try {
			const itemResponse = await api.get(itemEndpoint);
			return itemResponse.data?.data || {};
		} catch (error) {
			unexpectedError(error);
			throw error;
		}
	}

	async function buildRevisionComparison(
		revision: Revision | RevisionComparisonResponse,
		currentVersion?: ContentVersion | null,
		revisions?: Revision[] | null,
	): Promise<ComparisonData> {
		let base: Record<string, any> = {};
		let incoming = revision.data || {};
		const targetCollection = revision.collection || collection?.value || '';
		const fields = fieldsStore.getFieldsForCollection(targetCollection);

		if (currentVersion) {
			const versionComparison = await fetchVersionComparisonForRevision(currentVersion.id);
			base = versionComparison.base;
			incoming = mergeWith({}, versionComparison.main, incoming, replaceArraysInMergeCustomizer);
		} else if ('collection' in revision && 'item' in revision) {
			const { collection, item } = revision as { collection: string; item: string | number };
			base = await fetchMainVersion(collection, item);
			const defaultValues = getDefaultValuesFromFields(fields).value;
			incoming = mergeWith({}, defaultValues, incoming, replaceArraysInMergeCustomizer);
		}

		// Revisions don’t support relational fields, so we need to merge them into incoming. Primary Key and User Reference fields (user_created, user_updated) as well.
		incoming = copySpecialFieldsFromBaseToIncoming(base, incoming, fields);

		if ('activity' in revision && (revision as any)?.activity?.timestamp) {
			incoming.date_updated = (revision as any).activity.timestamp;
		}

		const revisionsList = revisions || [];
		const revisionId = 'id' in revision ? revision.id : null;
		const revisionFields = new Set(Object.keys(revision.delta ?? {}));

		return {
			base,
			incoming,