Collect view-scoped TREE lifecycle events before applying hunk-level TREE mutations. Recording all adds, moves, and deletes, not just deferred Git imports, keeps replay baselines valid when a later foreground change moves the same inode on another view.
(
txn: &T,
change_hash: Hash,
change: &Change,
deleted_paths: &[String],
)
| 303 | /// imports, keeps replay baselines valid when a later foreground change moves |
| 304 | /// the same inode on another view. |
| 305 | pub(super) fn collect_tree_ops<T: GraphTxnT + TreeTxnT>( |
| 306 | txn: &T, |
| 307 | change_hash: Hash, |
| 308 | change: &Change, |
| 309 | deleted_paths: &[String], |
| 310 | ) -> Result<Vec<DeferredTreeOp>, RepositoryError> { |
| 311 | let mut ops = Vec::new(); |
| 312 | |
| 313 | for graph_op in change.hunks() { |
| 314 | match graph_op { |
| 315 | GraphOp::FileAdd { |
| 316 | add_inode, path, .. |
| 317 | } |
| 318 | | GraphOp::DirAdd { |
| 319 | add_inode, path, .. |
| 320 | } => { |
| 321 | // A foreign view may add the same path as a draft-only file. |
| 322 | // Capture that current occupant so switching away removes it |
| 323 | // and switching back can restore it without overwriting |
| 324 | // TREE/REV_TREE. |
| 325 | let added_position = Position::new(change_hash, add_inode.start); |
| 326 | push_occupant_baseline(txn, change_hash, path, Some(added_position), &mut ops)?; |
| 327 | push_unique( |
| 328 | &mut ops, |
| 329 | DeferredTreeOp { |
| 330 | change: change_hash, |
| 331 | inode: added_position, |
| 332 | baseline_path: None, |
| 333 | action: DeferredTreeAction::Set { path: path.clone() }, |
| 334 | }, |
| 335 | ); |
| 336 | } |
| 337 | GraphOp::FileMove { add, path, .. } => { |
| 338 | let Some(external_position) = external_position(change_hash, add.inode) else { |
| 339 | continue; |
| 340 | }; |
| 341 | push_occupant_baseline(txn, change_hash, path, Some(external_position), &mut ops)?; |
| 342 | let op = DeferredTreeOp { |
| 343 | change: change_hash, |
| 344 | inode: external_position, |
| 345 | baseline_path: current_path_for_position(txn, external_position)?, |
| 346 | action: DeferredTreeAction::Set { path: path.clone() }, |
| 347 | }; |
| 348 | // Keep the event even when TREE already contains `path`. |
| 349 | // `atomic move` updates tracking before the subsequent record, |
| 350 | // and that recorded change must still supersede an older |
| 351 | // deferred rename for views where it is visible. |
| 352 | push_unique(&mut ops, op); |
| 353 | } |
| 354 | GraphOp::FileDel { del, path, .. } | GraphOp::DirDel { del, path } => { |
| 355 | if let Some(inode) = external_position(change_hash, del.inode) { |
| 356 | push_unique( |
| 357 | &mut ops, |
| 358 | DeferredTreeOp { |
| 359 | change: change_hash, |
| 360 | inode, |
| 361 | baseline_path: current_path_for_position(txn, inode)? |
| 362 | .or_else(|| Some(path.clone())), |
no test coverage detected