Git · merging: markers split at LF only, as Git writes them

This commit is contained in:
ZacharyZhang-NY committed 2026-10-07 03:51:31 -04:00
1 parent 7bb53f4779
commit a284c71e92
1 file changed
+22 -11
+22 -11
View File
@@ -146,11 +146,16 @@ fn marker(line: &str, mark: char, size: usize, labelled: bool) -> bool {
run == size && (line.len() == size || (labelled && line[size..].starts_with(' ')))
}
/// Lines as Git splits them: at LF only.
fn git_lines(text: &str) -> Vec<&str> {
text.split_inclusive('\n').collect()
}
/// The conflicts marked in `text` with markers `size` wide.
pub fn conflicts(text: &str, size: usize) -> Vec<Conflict> {
let mut found = Vec::new();
let (mut start, mut base, mut middle) = (None, None, None);
for (ix, line) in text.tokenize_lines().into_iter().enumerate() {
for (ix, line) in git_lines(text).into_iter().enumerate() {
if marker(line, '<', size, true) {
(start, base, middle) = (Some(ix), None, None);
} else if marker(line, '|', size, true) && start.is_some() && middle.is_none() {
@@ -173,11 +178,7 @@ pub fn conflicts(text: &str, size: usize) -> Vec<Conflict> {
/// Regions as Git grouped marked `text`; base from diff3.
pub fn marked(text: &str, size: usize) -> Vec<Region> {
let lines: Vec<String> = text
.tokenize_lines()
.into_iter()
.map(str::to_string)
.collect();
let lines: Vec<String> = git_lines(text).into_iter().map(str::to_string).collect();
let same = |range: std::ops::Range<usize>| Region {
kind: RegionKind::Unchanged,
base: lines[range.clone()].to_vec(),
@@ -213,7 +214,7 @@ pub fn resolve(text: &str, ix: usize, take: Take, size: usize) -> Option<String>
log::error!("no conflict {ix} to resolve in this text");
return None;
};
let lines = text.tokenize_lines();
let lines = git_lines(text);
// A diff3 conflict's base section is neither side.
let ours = lines[conflict.start + 1..conflict.base.unwrap_or(conflict.middle)].concat();
let theirs = lines[conflict.middle + 1..conflict.end].concat();
@@ -324,10 +325,6 @@ mod tests {
assert_eq!(result(&changed, &[]).0, "a\rB\n");
let held = result(&regions("a\r", "b\r", "c\r"), &[]).0;
assert_eq!(held, "<<<<<<< ours\rb\r=======\rc\r>>>>>>> theirs\r");
assert_eq!(
resolve(&held, 0, Take::Theirs, MARKER).as_deref(),
Some("c\r")
);
assert_eq!(
resolve("plain\n", 0, Take::Ours, MARKER),
None,
@@ -335,6 +332,20 @@ mod tests {
);
}
#[test]
fn git_markers_split_at_lf_only() {
let text = "<<<<<<< HEAD\nprefix\r=======\rOURS_END\n=======\nTHEIRS\n>>>>>>> side\n";
assert_eq!(
resolve(text, 0, Take::Theirs, MARKER).as_deref(),
Some("THEIRS\n")
);
assert_eq!(
marked(text, MARKER)[0].ours,
["prefix\r=======\rOURS_END\n"]
);
assert!(conflicts("<<<<<<< a\rb\r=======\rc\r>>>>>>> d\r", MARKER).is_empty());
}
#[test]
fn taking_both_adds_no_line_to_an_empty_side() {
assert_eq!(result(&regions("a", "b", ""), &[Some(Take::Both)]).0, "b");