forked from mirrors/jj
gitignore: any character can be backslash-escaped
You may use "abc\\" in .gitignore to ignore a file named "abc\". In this case, removing training spaces on "abc\\ " must result in "abc\\" as the trailing space is not escaped, the preceeding backslash being part of the previous "\\" escaping sequence.
This commit is contained in:
parent
60d1537731
commit
84fc66fe50
2 changed files with 17 additions and 21 deletions
|
@ -74,6 +74,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
the jj repo from a Git repo. They will now be considered as non-existent if
|
the jj repo from a Git repo. They will now be considered as non-existent if
|
||||||
referenced explicitly instead of crashing.
|
referenced explicitly instead of crashing.
|
||||||
|
|
||||||
|
* Fixed handling of escaped characters in .gitignore (only keep trailing spaces
|
||||||
|
if escaped properly).
|
||||||
|
|
||||||
### Contributors
|
### Contributors
|
||||||
|
|
||||||
Thanks to the people who made this release happen!
|
Thanks to the people who made this release happen!
|
||||||
|
|
|
@ -27,33 +27,23 @@ struct GitIgnoreLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GitIgnoreLine {
|
impl GitIgnoreLine {
|
||||||
// Remove trailing spaces (unless backslash-escaped)
|
// Remove trailing spaces (unless backslash-escaped). Any character
|
||||||
|
// can be backslash-escaped as well.
|
||||||
fn remove_trailing_space(input: &str) -> &str {
|
fn remove_trailing_space(input: &str) -> &str {
|
||||||
let mut trimmed_len = 0;
|
|
||||||
let mut non_space_seen = false;
|
|
||||||
let mut prev_was_space = false;
|
|
||||||
let mut in_escape = false;
|
|
||||||
let input = input.strip_suffix('\r').unwrap_or(input);
|
let input = input.strip_suffix('\r').unwrap_or(input);
|
||||||
for (i, c) in input.char_indices() {
|
let mut it = input.char_indices().rev().peekable();
|
||||||
if !prev_was_space && non_space_seen {
|
while let Some((i, c)) = it.next() {
|
||||||
trimmed_len = i;
|
if c != ' ' {
|
||||||
|
return &input[..i + c.len_utf8()];
|
||||||
}
|
}
|
||||||
if c == ' ' {
|
if matches!(it.peek(), Some((_, '\\'))) {
|
||||||
if in_escape {
|
if it.skip(1).take_while(|(_, b)| *b == '\\').count() % 2 == 1 {
|
||||||
in_escape = false;
|
return &input[..i];
|
||||||
} else {
|
|
||||||
prev_was_space = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
return &input[..i + 1];
|
||||||
non_space_seen = true;
|
|
||||||
prev_was_space = false;
|
|
||||||
in_escape = c == '\\'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !prev_was_space && non_space_seen {
|
""
|
||||||
trimmed_len = input.len();
|
|
||||||
}
|
|
||||||
input.split_at(trimmed_len).0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(prefix: &str, input: &str) -> Option<GitIgnoreLine> {
|
fn parse(prefix: &str, input: &str) -> Option<GitIgnoreLine> {
|
||||||
|
@ -347,6 +337,9 @@ mod tests {
|
||||||
fn test_gitignore_whitespace() {
|
fn test_gitignore_whitespace() {
|
||||||
assert!(!matches_file(b" \n", " "));
|
assert!(!matches_file(b" \n", " "));
|
||||||
assert!(matches_file(b"\\ \n", " "));
|
assert!(matches_file(b"\\ \n", " "));
|
||||||
|
assert!(matches_file(b"\\\\ \n", "\\"));
|
||||||
|
assert!(!matches_file(b"\\\\ \n", " "));
|
||||||
|
assert!(matches_file(b"\\\\\\ \n", "\\ "));
|
||||||
assert!(matches_file(b" a\n", " a"));
|
assert!(matches_file(b" a\n", " a"));
|
||||||
assert!(matches_file(b"a b\n", "a b"));
|
assert!(matches_file(b"a b\n", "a b"));
|
||||||
assert!(matches_file(b"a b \n", "a b"));
|
assert!(matches_file(b"a b \n", "a b"));
|
||||||
|
|
Loading…
Reference in a new issue