Skip to content

Commit 1f22f1a

Browse files
committed
Handle git diffs that only rename files
Fixes #20
1 parent 4f70fef commit 1f22f1a

5 files changed

Lines changed: 72 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55

66
- `Patch::from_multiple` no longer returns an error on an input that contains no patches, including an empty string. It instead returns an empty vector.
77

8+
- `Patch` now has a `binary` member.
9+
810
### Fixed
11+
912
- Issue #4: Fixed parsing of “No newline at end of file” markers so they are recognized even when not the final line of a hunk.
1013

14+
- Issue #20: Handle patches containing git file renames with no changes.
15+
1116
### Changed
1217

1318
## [v0.7]

src/ast.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ impl fmt::Display for Patch<'_> {
3030
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3131
// Display implementations typically hold up the invariant that there is no trailing
3232
// newline. This isn't enforced, but it allows them to work well with `println!`
33+
let Patch { old, new, .. } = &self;
3334
if self.binary {
34-
assert!(self.hunks.is_empty());
35-
writeln!(f, "Binary files {} and {} differ", self.old, self.new)?;
35+
assert!(self.hunks.is_empty(), "binary diff is not expected to have hunks");
36+
write!(f, "Binary files {old} and {new} differ")?;
37+
} else if self.hunks.is_empty() && old != new {
38+
writeln!(f, "diff --git a/{old} b/{new}")?; // TODO: Perhaps should be emitted in every case?
39+
writeln!(f, "similarity index 100%")?;
40+
writeln!(f, "rename from {old}")?;
41+
write!(f, "rename to {new}")?;
3642
} else {
37-
writeln!(f, "--- {}", self.old)?;
38-
write!(f, "+++ {}", self.new)?;
43+
writeln!(f, "--- {old}")?;
44+
write!(f, "+++ {new}")?;
3945
for (i, hunk) in self.hunks.iter().enumerate() {
4046
writeln!(f)?;
4147
if i == self.hunks.len() - 1 {

src/parser.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ fn multiple_patches(input: Input) -> IResult<Input, Vec<Patch>> {
102102
}
103103

104104
fn patch(input: Input) -> IResult<Input, Patch> {
105+
let (input, _diff_command) = diff_command(input)?;
105106
if let Ok(patch) = file_rename_only(input) {
106107
return Ok(patch);
107108
}
108-
let (input, _diff_command) = diff_command(input)?;
109109
let (input, _git_index) = git_index_line(input)?;
110110
if let Ok(patch) = binary_files_differ(input) {
111111
return Ok(patch);
@@ -172,14 +172,13 @@ fn binary_files_differ(input: Input) -> IResult<Input, Patch> {
172172
))
173173
}
174174

175-
/// Parse patches with "similarity index 100%", i.e., patches where a file is renamed without any
176-
/// other change in its diff.
175+
/// Attempt to match patches with "similarity index 100%", i.e., patches where a file is renamed
176+
/// without any other change in its diff.
177177
///
178-
/// The `parse` function should handle rename diffs with similary index less than 100%, at least as per the test
178+
/// The `parse` function should handle rename diffs with similarity index less than 100%, at least as per the test
179179
/// `parses_file_renames_with_some_diff`.
180180
fn file_rename_only(input: Input<'_>) -> IResult<Input<'_>, Patch<'_>> {
181-
let (rest, _parsed) = take_until("\nsimilarity index 100%\n")(input)?;
182-
let (rest, _parsed) = tag("\nsimilarity index 100%\n")(rest)?;
181+
let (rest, _parsed) = tag("similarity index 100%\n")(input)?;
183182

184183
let (rest, old_name) = delimited(tag("rename from "), take_until("\n"), line_ending)(rest)?;
185184

@@ -210,7 +209,7 @@ fn file_rename_only(input: Input<'_>) -> IResult<Input<'_>, Patch<'_>> {
210209
/// `diff --git a/file1 b/file2`
211210
fn diff_command(input: Input<'_>) -> IResult<Input<'_>, Option<LocatedSpan<&'_ str>>> {
212211
context(
213-
"diff command line ",
212+
"diff command line",
214213
opt(recognize(pair(
215214
tag("diff "),
216215
terminated(not_line_ending, opt(line_ending)),
@@ -815,4 +814,31 @@ mod tests {
815814

816815
Ok(())
817816
}
817+
818+
#[test]
819+
fn test_git_file_rename_without_changes() -> ParseResult<'static, ()> {
820+
let sample = "\
821+
diff --git a/tests/test-utils/Cargo.toml b/test-utils/Cargo.toml
822+
similarity index 100%
823+
rename from tests/test-utils/Cargo.toml
824+
rename to test-utils/Cargo.toml
825+
";
826+
let expected = Patch {
827+
old: File {
828+
path: "tests/test-utils/Cargo.toml".into(),
829+
meta: None,
830+
},
831+
new: File {
832+
path: "test-utils/Cargo.toml".into(),
833+
meta: None,
834+
},
835+
hunks: Vec::new(),
836+
old_missing_newline: false,
837+
new_missing_newline: false,
838+
binary: false,
839+
};
840+
test_parser!(patch(sample) -> expected);
841+
assert_eq!(format!("{expected}\n"), sample);
842+
Ok(())
843+
}
818844
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
diff --git a/tests/test-utils/Cargo.toml b/test-utils/Cargo.toml
2+
similarity index 100%
3+
rename from tests/test-utils/Cargo.toml
4+
rename to test-utils/Cargo.toml
5+
diff --git a/tests/test-utils/src/io.rs b/test-utils/src/io.rs
6+
similarity index 100%
7+
rename from tests/test-utils/src/io.rs
8+
rename to test-utils/src/io.rs
9+
diff --git a/tests/test-utils/src/lib.rs b/test-utils/src/lib.rs
10+
similarity index 100%
11+
rename from tests/test-utils/src/lib.rs
12+
rename to test-utils/src/lib.rs
13+
diff --git a/tests/test-utils/src/read.rs b/test-utils/src/read.rs
14+
similarity index 100%
15+
rename from tests/test-utils/src/read.rs
16+
rename to test-utils/src/read.rs
17+
diff --git a/tests/test-utils/src/write.rs b/test-utils/src/write.rs
18+
similarity index 100%
19+
rename from tests/test-utils/src/write.rs
20+
rename to test-utils/src/write.rs
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
diff --git a/tests/test-utils/Cargo.toml b/test-utils/Cargo.toml
2+
similarity index 100%
3+
rename from tests/test-utils/Cargo.toml
4+
rename to test-utils/Cargo.toml

0 commit comments

Comments
 (0)