@Override
	public synchronized void write(DBHandle dbh, boolean isRecoverySave) throws IOException {

		// we are in a save, first move changes since last save into since last checkout set.
		changedAddrsSinceCheckout.add(changedAddrsSinceSave);
		changedRegAddrsSinceCheckout.add(changedRegAddrsSinceSave);

		// when the user saves a program clear out these sets. (They are saved just above in the "since checkout" sets.)
		if (!isRecoverySave) {
			changedAddrsSinceSave.clear();
			changedRegAddrsSinceSave.clear();
		}

		long txId = dbh.startTransaction();
		boolean success = false;
		try {

			writeIdRecords(dbh, DATATYPE_ADDITIONS, addedDataTypeIds);
			writeIdRecords(dbh, DATATYPE_CHANGES, changedDataTypeIds);
			writeIdRecords(dbh, CATEGORY_ADDITIONS, addedCategoryIds);
			writeIdRecords(dbh, CATEGORY_CHANGES, changedCategoryIds);
			writeIdRecords(dbh, PROGRAM_TREE_ADDITIONS, addedProgramTreeIds);
			writeIdRecords(dbh, PROGRAM_TREE_CHANGES, changedProgramTreeIds);
			writeIdRecords(dbh, SYMBOL_ADDITIONS, addedSymbolIds);
			writeIdRecords(dbh, SYMBOL_CHANGES, changedSymbolIds);
			writeIdRecords(dbh, SOURCE_ARCHIVE_ADDITIONS, addedSourceArchiveIds);
			writeIdRecords(dbh, SOURCE_ARCHIVE_CHANGES, changedSourceArchiveIds);
			writeIdRecords(dbh, FUNCTION_TAG_ADDITIONS, addedTagIds);
			writeIdRecords(dbh, FUNCTION_TAG_CHANGES, changedTagIds);

			writeAddressRangeRecords(dbh, ADDRESS_CHANGES, changedAddrsSinceCheckout);
			writeAddressRangeRecords(dbh, REGISTER_ADDRESS_CHANGES, changedRegAddrsSinceCheckout);

			success = true;
		}
		finally {
			dbh.endTransaction(txId, success);
		}
	}
Construct a new ProgramChangeSet.
@param addrMap the address map.
@param numUndos the number of undo change sets to track.
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDBChangeSet.java