.. _audit_tutorial_hierarchies: ================================== Audit trails for hierarchy objects ================================== ReGraph implements a framework for the version control (VC) of graph transformations in hierarchies. The data structure `VersionedHierarchy` allows to store the history of transformations of a hierarchy and perform the following VC operations: - **Rewrite**: perform a rewriting of the hierarchy with a commit to the revision history - **Branch**: create a new branch (with a diverged version of the graph object) - **Merge branches**: merge branches - **Rollback**: rollback to a point in the history of transformations Let us start this tutorial by performing necessary imports: :: from regraph import NXGraph, NXHierarchy from regraph.audit import VersionedHierarchy from regraph.rules import Rule from regraph import print_graph, plot_rule, plot_graph Let us now create a small hierarchy. :: hierarchy = NXHierarchy() shapes = NXGraph() shapes.add_nodes_from(["circle", "square"]) hierarchy.add_graph("shapes", shapes) colors = NXGraph() colors.add_nodes_from(["white", "black"]) hierarchy.add_graph("colors", colors) ag = NXGraph() ag.add_nodes_from( ["wc", "bc", "ws", "bs"]) hierarchy.add_graph("metamodel", ag) nugget = NXGraph() nugget.add_nodes_from( ["wc1", "wc2", "bc1", "ws1", "bs2"]) hierarchy.add_graph("data", nugget) hierarchy.add_typing( "metamodel", "shapes", { "wc": "circle", "bc": "circle", "ws": "square", "bs": "square" }) hierarchy.add_typing( "metamodel", "colors", { "wc": "white", "bc": "black", "ws": "white", "bs": "black" }) hierarchy.add_typing( "data", "metamodel", { "wc1": "wc", "wc2": "wc", "bc1": "bc", "ws1": "ws", "bs2": "bs" }) hierarchy.add_typing( "data", "colors", { "wc1": "white", "wc2": "white", "bc1": "black", "ws1": "white", "bs2": "black" }) base = NXGraph() base.add_nodes_from(["node"]) hierarchy.add_graph("base", base) hierarchy.add_typing( "colors", "base", { "white": "node", "black": "node" }) We pass the hierarchy to the `VersionedHierarchy` wrapper that will take care of the version control: >>> h = VersionedHierarchy(hierarchy) >>> print("Branches: ", h.branches()) Branches: ['master'] >>> print("Current branch: ", h.current_branch()) Current branch: master Let us create a new branch `test1`: >>> h.branch("test1") We will now rewrite our hierarchy at the current branch of the audit trail. :: pattern = NXGraph() pattern.add_nodes_from(["s"]) rule = Rule.from_transform(pattern) rule.inject_remove_node("s") rhs_instances, commit_id = h.rewrite( "shapes", rule, {"s": "square"}, message="Remove square in shapes") The `rewrite` method of `VersionedHierarchy` returns the instances of the RHS of the applied rule in different graphs and the id of the newly created commit corresponding to this rewrite. >>> print(rhs_instances) {'shapes': {}, 'metamodel': {}, 'data': {}, 'colors': {'ws': 'white', 'bs': 'black'}, 'base': {'bs_ws': 'node'}} >>> print(commit_id) 5ebdb406-eee6-44b9-a2a0-005e4b5ef94f We switch back to the `master` branch. >>> h.switch_branch("master") We will now rewrite the hierarchy corresponding to the current branch. :: pattern = NXGraph() pattern.add_nodes_from(["wc"]) rule = Rule.from_transform(pattern) rule.inject_clone_node("wc") _, clone_commit = h.rewrite( "metamodel", rule, {"wc": "wc"}, message="Clone 'wc' in ag") After running the snippet above, we obtain the following resivions history: >>> h.print_history() 2020-01-24 14:37:14.627367 78f98e3c-2361-4d96-9737-31678d507ac6 master Initial commit 2020-01-24 14:37:14.635519 b135a358-2af2-41f8-9c66-aa392ca21660 test1 Created branch 'test1' 2020-01-24 14:37:14.646972 5ebdb406-eee6-44b9-a2a0-005e4b5ef94f test1 Remove square in shapes 2020-01-24 14:37:14.697650 4e00034e-17d5-48ed-9078-d070a0d65d03 master Clone 'wc' in ag Let us perform another rewriting as follows: :: pattern = NXGraph() pattern.add_nodes_from(["wc1"]) rule = Rule.from_transform(pattern) rule.inject_add_node("new_node") rule.inject_add_edge("new_node", "wc1") _ = h.rewrite( "data", rule, {"wc1": "wc1"}, message="Add a new node to 'data'") Now we merge the branch `test1` in into `master`. >>> h.merge_with("test1") >>> h.print_history() 2020-01-24 14:37:14.627367 78f98e3c-2361-4d96-9737-31678d507ac6 master Initial commit 2020-01-24 14:37:14.635519 b135a358-2af2-41f8-9c66-aa392ca21660 test1 Created branch 'test1' 2020-01-24 14:37:14.646972 5ebdb406-eee6-44b9-a2a0-005e4b5ef94f test1 Remove square in shapes 2020-01-24 14:37:14.697650 4e00034e-17d5-48ed-9078-d070a0d65d03 master Clone 'wc' in ag 2020-01-24 14:37:14.728376 dc371f55-18fb-4797-ba75-8ff9217bfe65 master Add a new node to 'data' 2020-01-24 14:37:14.751670 f2239fa2-2632-4650-8b53-78f03cf2d795 master Merged branch 'test1' into 'master' Let us now rollback to the commit `clone_commit`. >>> h.rollback(clone_commit) Created the new head for 'test1' Created the new head for 'master' >>> h.print_history() 2020-01-24 14:37:14.627367 78f98e3c-2361-4d96-9737-31678d507ac6 master Initial commit 2020-01-24 14:37:14.635519 b135a358-2af2-41f8-9c66-aa392ca21660 test1 Created branch 'test1' 2020-01-24 14:37:14.646972 5ebdb406-eee6-44b9-a2a0-005e4b5ef94f test1 Remove square in shapes 2020-01-24 14:37:14.697650 4e00034e-17d5-48ed-9078-d070a0d65d03 master Clone 'wc' in ag >>> print(h.branches()) ['master', 'test1'] We can see that the revision history came back to the previous state (right after the clone commit), and we still have two branches `master` and `test1`. -------- See more -------- Module reference: :ref:`audit`