protected function replaceNodeData(NodeInterface $sourceNode, NodeData $targetNodeData)
    {
        $sourceNodeData = $sourceNode->getNodeData();

        // The source node is a regular, not moved node and the target node is a moved shadow node
        if (!$sourceNode->getNodeData()->isRemoved() && $sourceNode->getNodeData()->getMovedTo() === null && $targetNodeData->isRemoved() && $targetNodeData->getMovedTo() !== null) {
            $sourceNodeData->move($sourceNodeData->getPath(), $targetNodeData->getWorkspace());
            return;
        }

        if ($sourceNodeData->getParentPath() !== $targetNodeData->getParentPath()) {
            // When $targetNodeData is moved, the NodeData::move() operation may transform it to a shadow node.
            // moveTargetNodeDataToNewPosition() will return the correct (non-shadow) node in any case.
            $targetNodeData = $this->moveTargetNodeDataToNewPosition($targetNodeData, $sourceNode->getPath());
        }

        $this->adjustShadowNodeDataForNodePublishing($sourceNodeData, $targetNodeData->getWorkspace(), $targetNodeData);

        // Technically this shouldn't be needed but due to doctrines behavior we need it.
        if ($sourceNodeData->isRemoved() && $targetNodeData->getWorkspace()->getBaseWorkspace() === null) {
            $this->nodeDataRepository->remove($targetNodeData);
            $this->nodeDataRepository->remove($sourceNodeData);
            return;
        }

        $targetNodeData->similarize($sourceNodeData);
        $targetNodeData->setLastPublicationDateTime($this->now);

        $sourceNode->setNodeData($targetNodeData);
        $this->nodeService->cleanUpProperties($sourceNode);

        // If the source node was "removed", make sure that the new target node data is "removed" as well.
        $targetNodeData->setRemoved($sourceNodeData->isRemoved());

        $this->nodeDataRepository->remove($sourceNodeData);
    }