Apply a Insertion atom to the graph. Creates a new span and connects it to the graph via context edges. This is how new content is inserted into files. # Arguments `txn` - Write transaction for graph modifications `workspace` - Workspace for tracking state and conflicts `change_id` - Internal ID of the change being applied `insertion` - The Insertion specification `change` - The full change for
(
txn: &mut CachedWriteGraphTxn<'_, '_>,
workspace: &mut Workspace,
change_id: NodeId,
insertion: &Insertion<Option<Hash>>,
change: &Change,
detect_conflicts: bool,
)
| 84 | /// - `BlockNotFound`: Context position not found |
| 85 | /// - `Internal`: Database error |
| 86 | pub fn write_new_vertex( |
| 87 | txn: &mut CachedWriteGraphTxn<'_, '_>, |
| 88 | workspace: &mut Workspace, |
| 89 | change_id: NodeId, |
| 90 | insertion: &Insertion<Option<Hash>>, |
| 91 | change: &Change, |
| 92 | detect_conflicts: bool, |
| 93 | ) -> Result<(), LocalApplyError> { |
| 94 | // Create the new span |
| 95 | let node = GraphNode { |
| 96 | change: change_id, |
| 97 | start: insertion.start, |
| 98 | end: insertion.end, |
| 99 | }; |
| 100 | |
| 101 | // Clear workspace context for this span |
| 102 | workspace.clear_context(); |
| 103 | |
| 104 | // Fast path for the common "append one more line from this same change" |
| 105 | // pattern used by large FileAdd chains. The predecessor is a vertex we |
| 106 | // already inserted in this change, there are no successors, and the new |
| 107 | // edge can be wired directly without re-running the general context walk. |
| 108 | if insertion.successors.is_empty() && insertion.predecessors.len() == 1 { |
| 109 | let internal_pos = resolve_position(txn, &insertion.predecessors[0], change_id)?; |
| 110 | if let Some(up_vertex) = workspace.get_current_vertex(internal_pos, true) { |
| 111 | let resolved_inode = resolve_inode(txn, &insertion.inode, change_id)?; |
| 112 | let up_flag = insertion.flag | EdgeFlags::BLOCK; |
| 113 | add_edge_with_reverse(txn, resolved_inode, up_flag, up_vertex, node, change_id)?; |
| 114 | workspace.add_up_context(up_vertex.end_pos()); |
| 115 | workspace.add_up_context_vertex(up_vertex); |
| 116 | workspace.register_current_vertex(node); |
| 117 | return Ok(()); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | // Resolve predecessors: vertices that come BEFORE this new content. |
| 122 | // Uses the unified overlay-aware resolver so Local stacks can find |
| 123 | // vertices written to GRAPH earlier in the same change. |
| 124 | for up_pos in &insertion.predecessors { |
| 125 | let internal_pos = resolve_position(txn, up_pos, change_id)?; |
| 126 | let up_vertex = workspace |
| 127 | .get_current_vertex(internal_pos, true) |
| 128 | .unwrap_or(resolve_context_vertex(txn, internal_pos, true)?); |
| 129 | // Store the end position (where new content connects) |
| 130 | workspace.add_up_context(up_vertex.end_pos()); |
| 131 | workspace.add_up_context_vertex(up_vertex); |
| 132 | |
| 133 | // Check if predecessors was deleted by an unknown change |
| 134 | if detect_conflicts { |
| 135 | check_deleted_context(txn, workspace, change, up_vertex)?; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | let exact_inode_successor = resolve_position(txn, &insertion.inode, change_id).ok(); |
| 140 | |
| 141 | // Resolve successors: vertices that come AFTER this new content. |
| 142 | for down_pos in &insertion.successors { |
| 143 | let internal_pos = resolve_position(txn, down_pos, change_id)?; |
no test coverage detected