Write a recorded change to the repository. This method inserts a change that was just recorded, updating both the graph and the tree tables. It's the integration point between recording and inserting. Unlike `insert_change`, this method: - Takes the change directly (doesn't load from store) - Updates tree tables for FileAdd hunks - Assigns new inodes to added files # Arguments `outcome` - The
(
&self,
outcome: &RecordOutcome,
mut options: InsertOptions,
)
| 2122 | /// println!("Inserted with state: {}", apply_outcome.new_state.to_base32()); |
| 2123 | /// ``` |
| 2124 | pub fn write_recorded( |
| 2125 | &self, |
| 2126 | outcome: &RecordOutcome, |
| 2127 | mut options: InsertOptions, |
| 2128 | ) -> Result<InsertOutcome, RepositoryError> { |
| 2129 | let trace_record = std::env::var_os("ATOMIC_TRACE_RECORD").is_some(); |
| 2130 | let change = outcome.change(); |
| 2131 | let hash = outcome.hash(); |
| 2132 | |
| 2133 | // A freshly-recorded change is applied to the view it was recorded on. |
| 2134 | // Its dependency closure is complete by construction, so it cannot |
| 2135 | // produce zombie or missing-context conflicts. Disable conflict |
| 2136 | // detection to skip the per-hunk zombie/deleted-context graph scans |
| 2137 | // (the dominant apply cost on large changes); the graph written is |
| 2138 | // identical, only the (empty) conflict report is skipped. |
| 2139 | options.track_conflicts = false; |
| 2140 | |
| 2141 | // Get write transaction |
| 2142 | let mut txn = self |
| 2143 | .pristine |
| 2144 | .write_txn() |
| 2145 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2146 | |
| 2147 | // Register the change to get an internal ID |
| 2148 | let change_id = txn |
| 2149 | .register_change(hash) |
| 2150 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2151 | txn.put_change_deps(change_id, change.dependencies()) |
| 2152 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2153 | |
| 2154 | // Determine which view to use |
| 2155 | let view_name = options.view.as_deref().unwrap_or(&self.current_view); |
| 2156 | let preserve_existing_tree_paths = view_name != self.current_view; |
| 2157 | let tree_ops = collect_tree_ops(&txn, *hash, change, outcome.deleted_files())?; |
| 2158 | |
| 2159 | // Before applying atoms, set up tree entries for FileAdd hunks. |
| 2160 | // This creates the inode→position and path→inode mappings needed |
| 2161 | // for the graph operations. |
| 2162 | // |
| 2163 | // Note: put_tree creates both TREE and REV_TREE entries. |
| 2164 | // put_inode creates both INODES and REV_INODES entries. |
| 2165 | for graph_op in change.hunks() { |
| 2166 | match graph_op { |
| 2167 | GraphOp::FileAdd { |
| 2168 | add_inode, path, .. |
| 2169 | } => { |
| 2170 | // Allocate a new inode for this file |
| 2171 | let new_inode = txn |
| 2172 | .alloc_inode() |
| 2173 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2174 | |
| 2175 | // The inode span position is relative to this change. |
| 2176 | // Since add_inode.start is a ChangePosition within this change's content, |
| 2177 | // we create an internal position using the change_id we just registered. |
| 2178 | let inode_position = Position::new(change_id, add_inode.start); |
| 2179 | |
| 2180 | // Add to tree tables: |
| 2181 | // - put_tree: path ↔ inode (TREE and REV_TREE) |