Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/patch_set/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,12 +821,17 @@ fn extract_file_op_unidiff<'a, T: Text + ?Sized>(
}
}

/// Strips the trailing `\n` from a line yielded by [`Text::lines`].
/// Strips the trailing line ending from a line yielded by [`Text::lines`].
///
/// [`Text::lines`] includes line endings; strip for matching.
fn strip_line_ending<T: Text + ?Sized>(line: &T) -> &T {
// TODO: GNU patch strips trailing CRs from CRLF patches automatically.
// We should consider adding compat tests for GNU patch.
// And `git apply` seems to reject. Worth adding tests as well.
line.strip_suffix("\n").unwrap_or(line)
// GNU patch strips trailing CRs from CRLF patches automatically.
// Mirror that for header lines so a patch file saved with CRLF line
// endings (e.g. on Windows) parses the same as its LF equivalent:
// without this, extracted values such as the file mode keep their
// trailing `\r` and fail to parse (`invalid file mode: 100644\r`).
// Note `git apply` seems to reject CRLF patches. Worth adding tests
// for both behaviors as well.
let line = line.strip_suffix("\n").unwrap_or(line);
line.strip_suffix("\r").unwrap_or(line)
}
48 changes: 48 additions & 0 deletions src/patch_set/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,54 @@ index 0000000..abc1234
);
}

/// New file with content, with CRLF line endings throughout (e.g. a
/// patch file saved on Windows). The extended header values must not
/// keep the trailing `\r`: `new file mode 100644\r` previously failed
/// with `invalid file mode: 100644\r`.
#[test]
fn crlf_new_file_with_content() {
let input = "\
diff --git a/new.rs b/new.rs
new file mode 100644
index 0000000..abc1234
--- /dev/null
+++ b/new.rs
@@ -0,0 +1 @@
+hello
"
.replace('\n', "\r\n");
let patches = parse_gitdiff(&input);
assert_eq!(patches.len(), 1);
assert!(patches[0].operation().is_create());
assert_eq!(
patches[0].new_mode(),
Some(&super::super::FileMode::Regular),
);
}

/// Mode-only change with CRLF line endings: both `old mode` and
/// `new mode` values must parse despite the trailing `\r`.
#[test]
fn crlf_mode_only_change() {
let input = "\
diff --git a/script.sh b/script.sh
old mode 100644
new mode 100755
"
.replace('\n', "\r\n");
let patches = parse_gitdiff(&input);
assert_eq!(patches.len(), 1);
assert!(patches[0].operation().is_modify());
assert_eq!(
patches[0].old_mode(),
Some(&super::super::FileMode::Regular),
);
assert_eq!(
patches[0].new_mode(),
Some(&super::super::FileMode::Executable),
);
}

/// `diff --git` line with no-prefix paths (`git diff --no-prefix`).
/// Fallback path parsing works when ---/+++ are absent.
#[test]
Expand Down