예제 #1
0
def merge_nodes(mine: TscnFile, theirs: TscnFile, result: TscnFile,
                resolution: Callable[[str, Printable, Printable], Resolution]):
    # We start at the root node and work down the tree of each file
    # we tree to keep node order as is as far as possible.

    assert len(mine.nodes) > 0, "MINE file has no node. This is invalid."
    assert len(theirs.nodes) > 0, "THEIRS file has no node. This is invalid."

    # Now we need to partition up the nodes so we can do a comparison between them and start the merging
    partitions: list[NodePartition] = partition_nodes(mine.nodes, theirs.nodes)

    # Now we can walk the partitions and merge if necessary
    for partition in partitions:
        if partition.is_only_mine:
            # node exists only on my side, so we can just add it to the result
            for my_node, _ in partition.nodes:
                result.nodes.append(my_node)

        elif partition.is_only_theirs:
            # node exists only on their side, so we can just add it to the result
            for _, their_node in partition.nodes:
                result.nodes.append(their_node)

        else:
            # node exists on both sides and we need to merge it
            for my_node, their_node in partition.nodes:
                if not my_node.represents_same_thing(their_node):
                    # If the two nodes represent different things, there is no way to merge them as the properties are
                    # dependent on the type of the node and therefore merging properties of two different
                    # types together just makes not any sense. Therefore in this case the only thing
                    # we can do is ask which of the both nodes we would like to have.

                    decision = resolution(
                        "The same node has different types/scripts. "
                        "This cannot be merged. Which node should I take?",
                        my_node, their_node)

                    if decision == Resolution.MINE:
                        result.nodes.append(my_node)
                    elif decision == Resolution.THEIRS:
                        result.nodes.append(their_node)
                    else:
                        assert False, "Unexpected input."
                else:
                    # The nodes represent the same thing. Now we can compare their properties and build a result node.
                    result_node = Node(my_node.type, my_node.name,
                                       my_node.parent, my_node.instance,
                                       my_node.instance_placeholder)

                    properties: DiffResult[tuple[str,
                                                 Value]] = diff_bag_properties(
                                                     my_node, their_node)

                    # Stuff that is same in both nodes, goes to the result
                    for (key, value), _ in properties.same:
                        result_node.set_property(key, value)

                    # Stuff that is in my node only, also goes into the result
                    for key, value in properties.only_in_mine:
                        result_node.set_property(key, value)

                    # Stuff that is in their node only, also goes into the result
                    for key, value in properties.only_in_theirs:
                        result_node.set_property(key, value)

                    # Stuff that is different in both needs to be decided upon
                    for (key, my_value), (_,
                                          their_value) in properties.different:
                        decision = resolution(
                            f"Node {result_node.full_path_reference().to_string()} -> "
                            f"Property \"{key}\" is different in both sides. "
                            f"Which one should I take?", my_value, their_value)

                        if decision == Resolution.MINE:
                            result_node.set_property(key, my_value)
                        elif decision == Resolution.THEIRS:
                            result_node.set_property(key, their_value)
                        else:
                            assert False, "Unexpected input."

                    result.nodes.append(result_node)