I am running ArcGIS Pro 3.4.3, with its Python 3.11 interpreter. I have created a tool written in Python that takes a SDE file as input and loops through the child versions listed within that SDE file, trying to gather and report the version differences (inserts, updates, deletes) for each of those child versions. The code reports different difference data and values for each of the different child versions, but does not appear to even closely resemble the difference information reported within ArcGIS Pro's "Version Changes" tool button.
For example, within ArcGIS Pro the Version Differences for the version I'm testing the code on displays:
dbo.report_points
--Delete (1)
dbo.report_polys:
--Insert (3)
--Delete (7)
--Update (1)
dbo.report_other:
--Delete (1)
--Update (1)
dbo.report_cfmou
--Insert (1)
However, my code's output (debug and other messages) displays:
Processing Feature Layer: DBO.informal_resource_lines
my_layer=DBO.informal_resource_lines, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.informal_resource_lines
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.informal_resource_lines':
Added features: 0
Deleted features: 0
Updated features: 2
Processing Feature Layer: DBO.informal_resource_points
my_layer=DBO.informal_resource_points, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.informal_resource_points
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.informal_resource_points':
Added features: 15
Deleted features: 6
Updated features: 1
Processing Feature Layer: DBO.informal_resource_polys
my_layer=DBO.informal_resource_polys, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.informal_resource_polys
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.informal_resource_polys':
Added features: 23
Deleted features: 8
Updated features: 3
Processing Feature Layer: DBO.report_lines
my_layer=DBO.report_lines, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_lines
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_lines':
Added features: 5
Deleted features: 193
Updated features: 15
Processing Feature Layer: DBO.report_points
my_layer=DBO.report_points, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_points
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_points':
Added features: 26
Deleted features: 201
Updated features: 14
Processing Feature Layer: DBO.report_polys
my_layer=DBO.report_polys, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_polys
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_polys':
Added features: 123
Deleted features: 4526
Updated features: 162
Processing Feature Layer: DBO.report_aprxloc
my_layer=DBO.report_aprxloc, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_aprxloc
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_aprxloc':
Added features: 25
Deleted features: 449
Updated features: 34
Processing Feature Layer: DBO.report_other
my_layer=DBO.report_other, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_other
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_other':
Added features: 28
Deleted features: 310
Updated features: 22
Processing Feature Layer: DBO.report_restricted
my_layer=DBO.report_restricted, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_restricted
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_restricted':
Added features: 0
Deleted features: 0
Updated features: 0
Processing Feature Layer: DBO.report_cfmou
my_layer=DBO.report_cfmou, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_cfmou
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_cfmou':
Added features: 1
Deleted features: 0
Updated features: 0
Processing Feature Layer: DBO.resource_districts
my_layer=DBO.resource_districts, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_districts
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_districts':
Added features: 1
Deleted features: 24
Updated features: 12
Processing Feature Layer: DBO.resource_lines
my_layer=DBO.resource_lines, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_lines
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_lines':
Added features: 21
Deleted features: 388
Updated features: 76
Processing Feature Layer: DBO.resource_points
my_layer=DBO.resource_points, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_points
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_points':
Added features: 42
Deleted features: 1055
Updated features: 21
Processing Feature Layer: DBO.resource_polys
my_layer=DBO.resource_polys, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_polys
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_polys':
Added features: 135
Deleted features: 2627
Updated features: 353
Processing Feature Layer: DBO.resource_aprxloc
my_layer=DBO.resource_aprxloc, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_aprxloc
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_aprxloc':
Added features: 58
Deleted features: 627
Updated features: 103
Processing Feature Layer: DBO.resource_restricted
my_layer=DBO.resource_restricted, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_restricted
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_restricted':
Added features: 1
Deleted features: 2
Updated features: 5
"NWIC\RINERG".GR_report_voids_1 Version Changes = 11743
If someone can review my code and pass along any tips regarding what I am missing or doing incorrectly, it would be appreciated.
The main code I have performing the version processing for differences is as follows:
# start the outer loop through the list of versions
for ver_name in versions_list:
if ver_name in db_roots: # check for versions to ignore
arcpy.AddMessage("**SKIPPING** ROOT VERSION {0}".format(ver_name.name))
continue
arcpy.AddMessage("PROCESSING VERSION {0}".format(ver_name.name))
VersionChangesTally = 0
for curr_feature in feature_list:
arcpy.AddMessage("Processing Feature Layer: {0}".format(curr_feature))
arcpy.AddMessage("my_layer={0}, ver_name.name={1}, parent ver={2}".format(curr_feature, ver_name.name, ver_name.parentVersionName))
# Get add, update, delete counts
# --- Define workspace and versions ---
parent_version = "dbo.DEFAULT"
child_version = ver_name.name
# --- Create temporary table views for comparison ---
parent_view = "parent_view"
child_view = "child_view"
# Ensure temporary views are cleared
if arcpy.Exists(parent_view):
arcpy.Delete_management(parent_view)
if arcpy.Exists(child_view):
arcpy.Delete_management(child_view)
arcpy.AddMessage("Creating Feature Class {0}".format(curr_feature))
# Make table views for the parent and child versions
try:
# Set the workspace to the parent version and make a table view
arcpy.env.workspace = sde_file
parent_layer = arcpy.management.MakeFeatureLayer(curr_feature, parent_view)
arcpy.ChangeVersion_management(parent_view, "TRANSACTIONAL", parent_version)
# Set the workspace to the child version and make a table view
child_layer = arcpy.management.MakeFeatureLayer(curr_feature, child_view)
arcpy.ChangeVersion_management(child_view, "TRANSACTIONAL", child_version)
arcpy.MakeTableView_management(curr_feature, child_view) # NOTE: this code seems to have no effect on execution
# --- Extract OIDs into Python sets for efficient comparison ---
parent_oids = set()
with arcpy.da.SearchCursor(parent_view, "OBJECTID") as cursor:
for row in cursor:
parent_oids.add(row[0])
child_oids = set()
with arcpy.da.SearchCursor(child_view, "OBJECTID") as cursor:
for row in cursor:
child_oids.add(row[0])
try:
add_oids = child_oids - parent_oids
adds = len(add_oids)
del_oids = parent_oids - child_oids
deletes = len(del_oids)
update_oids = set() # empty set to be added to below
updates = 0
common_oids = parent_oids.intersection(child_oids)
finally:
parent_oids.clear()
child_oids.clear()
# A more robust update check would involve comparing a field value
# (e.g., a last_edited_date) or hashing the row values.
with arcpy.da.SearchCursor(parent_view, ["OBJECTID", "SHAPE@JSON"]) as p_cursor, \
arcpy.da.SearchCursor(child_view, ["OBJECTID", "SHAPE@JSON"]) as c_cursor:
parent_dict = {row[0]: row[1] for row in p_cursor if row[0] in common_oids}
child_dict = {row[0]: row[1] for row in c_cursor if row[0] in common_oids}
with arcpy.da.SearchCursor(child_view, ["OBJECTID", "OtherID"]) as c_cursor:
child_otherid_dict = {row[0]: row[1] for row in c_cursor} #if row[0] in common_oids}
with arcpy.da.SearchCursor(parent_view, ["OBJECTID", "OtherID"]) as p_cursor:
parent_otherid_dict = {row[0]: row[1] for row in p_cursor} #if row[0] in common_oids}
for oid in common_oids:
if parent_dict[oid] != child_dict[oid]:
updates += 1
update_oids.add(oid)
# --- Report results ---
arcpy.AddMessage(f"Comparison between '{parent_version}' and '{child_version}' for feature '{curr_feature}':")
arcpy.AddMessage(f"Added features: {adds}")
arcpy.AddMessage(f"Deleted features: {deletes}")
arcpy.AddMessage(f"Updated features: {updates}")
# innermost loop: go through the 3 kinds of differences (inserts, deletes, updates)
for oid in del_oids:
VersionChangesTally += 1
Primary = "<unknown>"
xls_file.row(child_version, curr_feature, "Deletes", Primary, oid, parent_otherid_dict[oid]) #"other")
for oid in update_oids:
VersionChangesTally += 1
Primary = "<unknown>"
xls_file.row(child_version, curr_feature, "Updates", Primary, oid, child_otherid_dict[oid]) #"other")
for oid in add_oids:
VersionChangesTally += 1
Primary = "<unknown>"
xls_file.row(child_version, curr_feature, "Inserts", Primary, oid, child_otherid_dict[oid]) #"other")
#arcpy.AddMessage(f"VERSION CHANGE COUNT: {VersionChangesTally}")
except arcpy.ExecuteError:
arcpy.AddMessage(arcpy.GetMessages(2))
finally:
# Clean up temporary table views
if arcpy.Exists(parent_view):
arcpy.Delete_management(parent_view)
if arcpy.Exists(child_view):
arcpy.Delete_management(child_view)
# report tallied changes
arcpy.AddMessage("{0} Version Changes = {1}".format(ver_name.name, VersionChangesTally))
If you're using branch versioning, you can use the Differences method on the Version object to get all the differences.
When the parent view is default, it's not enough to make a view looking at default. Instead, you must look at the ancestor moment of default that the child version references.