(
ctx: &mut GlobalizeContext<'_, T>,
built: &BuiltHunk,
inode: Inode,
inode_pos: Position<NodeId>,
content: &[u8],
local: Local,
encoding: Option<Encoding>,
)
| 263 | /// before per-line vertex creation), we fall back to whole-file Replace. |
| 264 | #[allow(clippy::too_many_arguments)] |
| 265 | fn globalize_replace<T>( |
| 266 | ctx: &mut GlobalizeContext<'_, T>, |
| 267 | built: &BuiltHunk, |
| 268 | inode: Inode, |
| 269 | inode_pos: Position<NodeId>, |
| 270 | content: &[u8], |
| 271 | local: Local, |
| 272 | encoding: Option<Encoding>, |
| 273 | ) -> GlobalizeResult<Vec<GraphOp<Option<Hash>>>> |
| 274 | where |
| 275 | T: GraphTxnT + TreeTxnT + InodeGraphOps, |
| 276 | { |
| 277 | if should_use_opaque_generated_vertices(&local.path) { |
| 278 | return globalize_replace_whole_file(ctx, inode, inode_pos, content, local, encoding); |
| 279 | } |
| 280 | |
| 281 | let sorted = collect_sorted_content_vertices_cached(ctx, inode, inode_pos)?; |
| 282 | |
| 283 | // Legacy: file has no visible content vertices in this view. Fall |
| 284 | // back to whole-file replace, which walks INODE_GRAPH unfiltered. |
| 285 | // |
| 286 | // NOTE: we use `== 0`, not `<= 1`. A single-line file produces |
| 287 | // exactly one per-line vertex, and the targeted path below handles |
| 288 | // that just fine (predecessor=inode, successor=None, delete that |
| 289 | // one vertex, insert the replacement lines). Falling back to |
| 290 | // whole-file replace for single-line files would have us re-read |
| 291 | // edges through the unfiltered INODE_GRAPH index, which picks up |
| 292 | // vertices from OTHER views and silently adds their changes as |
| 293 | // dependencies — producing a phantom supersession relationship |
| 294 | // between the just-recorded change and the other view's edits. |
| 295 | if sorted.is_empty() { |
| 296 | return globalize_replace_whole_file(ctx, inode, inode_pos, content, local, encoding); |
| 297 | } |
| 298 | |
| 299 | // Pure insertion (no lines deleted) — treat as a middle/append insert. |
| 300 | // This happens when the diff produces a Replace with old_len == 0 |
| 301 | // (an insertion that the algorithm classified as a replacement). |
| 302 | if built.deleted_lines.is_empty() { |
| 303 | let position = |
| 304 | match classify_insert(&sorted, inode_pos, built.old_start, Some(sorted.len())) { |
| 305 | Ok(p) => p, |
| 306 | Err(_) => { |
| 307 | return globalize_replace_whole_file( |
| 308 | ctx, inode, inode_pos, content, local, encoding, |
| 309 | ); |
| 310 | } |
| 311 | }; |
| 312 | |
| 313 | let (predecessors, successors) = match position { |
| 314 | InsertPosition::Prepend { first_content } => (vec![inode_pos], vec![first_content]), |
| 315 | InsertPosition::Append { last_content_end } => (vec![last_content_end], vec![]), |
| 316 | InsertPosition::Middle { |
| 317 | predecessor_end, |
| 318 | successor_start, |
| 319 | } => (vec![predecessor_end], vec![successor_start]), |
| 320 | }; |
| 321 | |
| 322 | let insertions = create_content_vertices_per_line( |
no test coverage detected