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,
)
| 2067 | /// println!("Inserted with state: {}", apply_outcome.new_state.to_base32()); |
| 2068 | /// ``` |
| 2069 | pub fn write_recorded( |
| 2070 | &self, |
| 2071 | outcome: &RecordOutcome, |
| 2072 | mut options: InsertOptions, |
| 2073 | ) -> Result<InsertOutcome, RepositoryError> { |
| 2074 | let trace_record = std::env::var_os("ATOMIC_TRACE_RECORD").is_some(); |
| 2075 | let change = outcome.change(); |
| 2076 | let hash = outcome.hash(); |
| 2077 | |
| 2078 | // A freshly-recorded change is applied to the view it was recorded on. |
| 2079 | // Its dependency closure is complete by construction, so it cannot |
| 2080 | // produce zombie or missing-context conflicts. Disable conflict |
| 2081 | // detection to skip the per-hunk zombie/deleted-context graph scans |
| 2082 | // (the dominant apply cost on large changes); the graph written is |
| 2083 | // identical, only the (empty) conflict report is skipped. |
| 2084 | options.track_conflicts = false; |
| 2085 | |
| 2086 | // Get write transaction |
| 2087 | let mut txn = self |
| 2088 | .pristine |
| 2089 | .write_txn() |
| 2090 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2091 | |
| 2092 | // Register the change to get an internal ID |
| 2093 | let change_id = txn |
| 2094 | .register_change(hash) |
| 2095 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2096 | txn.put_change_deps(change_id, change.dependencies()) |
| 2097 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2098 | |
| 2099 | // Determine which view to use |
| 2100 | let view_name = options.view.as_deref().unwrap_or(&self.current_view); |
| 2101 | let preserve_existing_tree_paths = view_name != self.current_view; |
| 2102 | let tree_ops = collect_tree_ops(&txn, *hash, change, outcome.deleted_files())?; |
| 2103 | |
| 2104 | // Before applying atoms, set up tree entries for FileAdd hunks. |
| 2105 | // This creates the inode→position and path→inode mappings needed |
| 2106 | // for the graph operations. |
| 2107 | // |
| 2108 | // Note: put_tree creates both TREE and REV_TREE entries. |
| 2109 | // put_inode creates both INODES and REV_INODES entries. |
| 2110 | for graph_op in change.hunks() { |
| 2111 | match graph_op { |
| 2112 | GraphOp::FileAdd { |
| 2113 | add_inode, path, .. |
| 2114 | } => { |
| 2115 | // Allocate a new inode for this file |
| 2116 | let new_inode = txn |
| 2117 | .alloc_inode() |
| 2118 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 2119 | |
| 2120 | // The inode span position is relative to this change. |
| 2121 | // Since add_inode.start is a ChangePosition within this change's content, |
| 2122 | // we create an internal position using the change_id we just registered. |
| 2123 | let inode_position = Position::new(change_id, add_inode.start); |
| 2124 | |
| 2125 | // Add to tree tables: |
| 2126 | // - put_tree: path ↔ inode (TREE and REV_TREE) |