| 65 | type DeferredPathClaim = (Position<Hash>, Option<usize>); |
| 66 | |
| 67 | fn desired_tree_paths( |
| 68 | ops: &[DeferredTreeOp], |
| 69 | visible_changes: &HashSet<Hash>, |
| 70 | ) -> HashMap<Position<Hash>, DesiredTreePath> { |
| 71 | let mut desired = HashMap::new(); |
| 72 | |
| 73 | for (order, op) in ops.iter().enumerate() { |
| 74 | let state = desired.entry(op.inode).or_insert_with(|| DesiredTreePath { |
| 75 | desired_path: op.baseline_path.clone(), |
| 76 | last_set_order: None, |
| 77 | }); |
| 78 | if !visible_changes.contains(&op.change) { |
| 79 | continue; |
| 80 | } |
| 81 | |
| 82 | match &op.action { |
| 83 | DeferredTreeAction::Set { path } => { |
| 84 | state.desired_path = Some(path.clone()); |
| 85 | state.last_set_order = Some(order); |
| 86 | } |
| 87 | DeferredTreeAction::Delete => { |
| 88 | state.desired_path = None; |
| 89 | state.last_set_order = None; |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // TREE is a one-to-one path↔inode index, while two overlay-visible changes |
| 95 | // can independently add the same path. Match the lifecycle order Atomic |
| 96 | // established when those changes were recorded/imported: the latest |
| 97 | // visible Set owns the path. Baseline-only duplicates remain untouched so |
| 98 | // apply_deferred_tree_ops_in_txn still fails closed on corrupt TREE state. |
| 99 | let mut claims: HashMap<String, Vec<DeferredPathClaim>> = HashMap::new(); |
| 100 | for (inode, state) in &desired { |
| 101 | if let Some(path) = &state.desired_path { |
| 102 | claims |
| 103 | .entry(path.clone()) |
| 104 | .or_default() |
| 105 | .push((*inode, state.last_set_order)); |
| 106 | } |
| 107 | } |
| 108 | for claimants in claims.into_values().filter(|claimants| claimants.len() > 1) { |
| 109 | let Some(max_order) = claimants.iter().filter_map(|(_, order)| *order).max() else { |
| 110 | continue; |
| 111 | }; |
| 112 | if claimants |
| 113 | .iter() |
| 114 | .filter(|(_, order)| *order == Some(max_order)) |
| 115 | .count() |
| 116 | != 1 |
| 117 | { |
| 118 | continue; |
| 119 | } |
| 120 | for (inode, order) in claimants { |
| 121 | if order != Some(max_order) { |
| 122 | desired |
| 123 | .get_mut(&inode) |
| 124 | .expect("claimant exists") |