From 1a14bac2e1974c11de7ff68a247eb008abcbe87d Mon Sep 17 00:00:00 2001 From: root Date: Sun, 8 Nov 2026 21:31:14 +0000 Subject: [PATCH 1/2] adapter/jj: fix false positive conflict detection on literal marker text extract_snapshot_with_conflicts flagged a file as conflicted when any single merge-marker line appeared in its content. Legitimate files (docs, test fixtures) that contain text like '<<<<<<< HEAD' were silently excluded from the snapshot, so adjudication missed them. Require both an opening (<<<<<<<) and closing (>>>>>>>) marker to identify a real conflict region. --- src/adapter/jj.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/adapter/jj.rs b/src/adapter/jj.rs index ce8818d..13781d4 100644 --- a/src/adapter/jj.rs +++ b/src/adapter/jj.rs @@ -52,6 +52,28 @@ fn is_conflict_marker_line(line: &str) -> bool { || line.trim() == "=======" } +/// True if file `content` contains a merge conflict (as materialized by jj/git). +/// +/// A real conflict forms a region with an opening marker (`<<<<<<<`) and a +/// closing marker (`>>>>>>>`). Requiring both avoids false positives from +/// legitimate content that happens to contain a marker-like line (for example, +/// documentation or test fixtures that show what a conflict looks like). +fn content_has_conflict_markers(content: &str) -> bool { + let mut open = false; + let mut close = false; + for line in content.lines() { + if line.starts_with("<<<<<<<") { + open = true; + } else if line.starts_with(">>>>>>>") { + close = true; + } + if open && close { + return true; + } + } + false +} + impl JjAdapter { /// Create a new `JjAdapter` rooted at the Jujutsu workspace containing `repo_path`. pub fn new(repo_path: impl AsRef) -> Result { @@ -228,7 +250,7 @@ impl JjAdapter { crate::store::validate_tree_path(path)?; let content = self.run_bytes(&["file", "show", "-r", rev, "--", path])?; let text = String::from_utf8_lossy(&content); - if text.lines().any(is_conflict_marker_line) { + if content_has_conflict_markers(&text) { conflicted.push(path.to_string()); } else { files.insert(path.to_string(), content); @@ -388,4 +410,27 @@ mod tests { assert!(!is_conflict_marker_line("let marker = \"<<<<<<<\";")); assert!(!is_conflict_marker_line("")); } + + #[test] + fn test_literal_marker_text_is_not_a_conflicted_file() { + // Legitimate content (docs, fixtures) may contain a conflict-marker + // string; that does not mean the file is in a conflicted state. + let content = "example:\n<<<<<<< HEAD\nsome text\n"; + assert!( + !content_has_conflict_markers(content), + "a lone marker line in normal content must not flag the file as conflicted" + ); + } + + #[test] + fn test_real_conflict_block_is_detected() { + let content = "before\n<<<<<<< ConflictMarker\nleft\n=======\nright\n>>>>>>> ConflictMarker\nafter\n"; + assert!(content_has_conflict_markers(content)); + } + + #[test] + fn test_diff3_conflict_block_is_detected() { + let content = "<<<<<<< ConflictMarker\nleft\n||||||| ConflictMarker\nbase\n=======\nright\n>>>>>>> ConflictMarker\n"; + assert!(content_has_conflict_markers(content)); + } } From 6f6ee7866e5cad72fb193a040ac0c79162933a93 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 8 Nov 2026 21:37:05 +0000 Subject: [PATCH 2/2] adapter/jj: fix clippy dead_code warning (use marker-line helper) --- src/adapter/jj.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/adapter/jj.rs b/src/adapter/jj.rs index 13781d4..6ffe2a7 100644 --- a/src/adapter/jj.rs +++ b/src/adapter/jj.rs @@ -62,10 +62,12 @@ fn content_has_conflict_markers(content: &str) -> bool { let mut open = false; let mut close = false; for line in content.lines() { - if line.starts_with("<<<<<<<") { - open = true; - } else if line.starts_with(">>>>>>>") { - close = true; + if is_conflict_marker_line(line) { + if line.starts_with("<<<<<<<") { + open = true; + } else if line.starts_with(">>>>>>>") { + close = true; + } } if open && close { return true; @@ -424,7 +426,8 @@ mod tests { #[test] fn test_real_conflict_block_is_detected() { - let content = "before\n<<<<<<< ConflictMarker\nleft\n=======\nright\n>>>>>>> ConflictMarker\nafter\n"; + let content = + "before\n<<<<<<< ConflictMarker\nleft\n=======\nright\n>>>>>>> ConflictMarker\nafter\n"; assert!(content_has_conflict_markers(content)); }