diff --git a/radicle-surf/data/git-platinum.tgz b/radicle-surf/data/git-platinum.tgz index acf440e..20cd219 100644 Binary files a/radicle-surf/data/git-platinum.tgz and b/radicle-surf/data/git-platinum.tgz differ diff --git a/radicle-surf/examples/diff.rs b/radicle-surf/examples/diff.rs index 36ba923..cad72aa 100644 --- a/radicle-surf/examples/diff.rs +++ b/radicle-surf/examples/diff.rs @@ -57,22 +57,22 @@ fn init_repository_or_exit(path_to_repo: &str) -> Repository { } fn print_diff_summary(diff: &Diff, elapsed_nanos: u128) { - diff.added.iter().for_each(|created| { + diff.added().for_each(|created| { println!("+++ {:?}", created.path); }); - diff.deleted.iter().for_each(|deleted| { + diff.deleted().for_each(|deleted| { println!("--- {:?}", deleted.path); }); - diff.modified.iter().for_each(|modified| { + diff.modified().for_each(|modified| { println!("mod {:?}", modified.path); }); println!( "created {} / deleted {} / modified {} / total {}", - diff.added.len(), - diff.deleted.len(), - diff.modified.len(), - diff.added.len() + diff.deleted.len() + diff.modified.len() + diff.added().count(), + diff.deleted().count(), + diff.modified().count(), + diff.added().count() + diff.deleted().count() + diff.modified().count() ); println!("diff took {elapsed_nanos} nanos "); } diff --git a/radicle-surf/scripts/update-git-platinum.sh b/radicle-surf/scripts/update-git-platinum.sh index beb4b3c..416f9aa 100755 --- a/radicle-surf/scripts/update-git-platinum.sh +++ b/radicle-surf/scripts/update-git-platinum.sh @@ -23,6 +23,7 @@ rm -rf "$PLATINUM_REPO" # Clone an up-to-date version of git-platinum. git clone https://github.com/radicle-dev/git-platinum.git "$PLATINUM_REPO" git -C "$PLATINUM_REPO" checkout empty-branch +git -C "$PLATINUM_REPO" checkout diff-test git -C "$PLATINUM_REPO" checkout dev # Add the necessary refs. diff --git a/radicle-surf/src/diff.rs b/radicle-surf/src/diff.rs index db062a1..9ad615f 100644 --- a/radicle-surf/src/diff.rs +++ b/radicle-surf/src/diff.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; #[cfg(feature = "serde")] -use serde::{ser, Serialize, Serializer}; +use serde::{ser, ser::SerializeStruct, Serialize, Serializer}; pub mod git; @@ -29,59 +29,115 @@ pub mod git; /// A [`Diff`] can be retrieved by the following functions: /// * [`crate::Repository::diff`] /// * [`crate::Repository::diff_commit`] -#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))] #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Diff { - pub added: Vec, - pub deleted: Vec, - pub moved: Vec, - pub copied: Vec, - pub modified: Vec, - pub stats: Stats, + files: Vec, + stats: Stats, } impl Diff { - pub fn new() -> Self { + /// Creates an empty diff. + pub(crate) fn new() -> Self { Diff::default() } - fn modified( + /// Returns an iterator of the file in the diff. + pub fn files(&self) -> impl Iterator { + self.files.iter() + } + + /// Returns owned files in the diff. + pub fn into_files(self) -> Vec { + self.files + } + + pub fn added(&self) -> impl Iterator { + self.files().filter_map(|x| match x { + FileDiff::Added(a) => Some(a), + _ => None, + }) + } + + pub fn deleted(&self) -> impl Iterator { + self.files().filter_map(|x| match x { + FileDiff::Deleted(a) => Some(a), + _ => None, + }) + } + + pub fn moved(&self) -> impl Iterator { + self.files().filter_map(|x| match x { + FileDiff::Moved(a) => Some(a), + _ => None, + }) + } + + pub fn modified(&self) -> impl Iterator { + self.files().filter_map(|x| match x { + FileDiff::Modified(a) => Some(a), + _ => None, + }) + } + + pub fn copied(&self) -> impl Iterator { + self.files().filter_map(|x| match x { + FileDiff::Copied(a) => Some(a), + _ => None, + }) + } + + pub fn stats(&self) -> &Stats { + &self.stats + } + + fn insert_modified( &mut self, path: PathBuf, hunks: impl Into>, eof: Option, ) { - self.modified.push(Modified { + let diff = DiffContent::Plain { + hunks: hunks.into(), + }; + let diff = FileDiff::Modified(Modified { path, eof, diff }); + self.files.push(diff) + } + + fn insert_moved(&mut self, old_path: PathBuf, new_path: PathBuf) { + let diff = FileDiff::Moved(Moved { + old_path, + new_path, + diff: DiffContent::Empty, + }); + self.files.push(diff); + } + + fn insert_copied(&mut self, old_path: PathBuf, new_path: PathBuf) { + let diff = FileDiff::Copied(Copied { + old_path, + new_path, + diff: DiffContent::Empty, + }); + self.files.push(diff); + } + + fn insert_modified_binary(&mut self, path: PathBuf) { + let diff = FileDiff::Modified(Modified { path, - diff: FileDiff::Plain { - hunks: hunks.into(), - }, - eof, - }) - } - - fn moved(&mut self, old_path: PathBuf, new_path: PathBuf) { - self.moved.push(Moved { old_path, new_path }); - } - - fn copied(&mut self, old_path: PathBuf, new_path: PathBuf) { - self.copied.push(Copied { old_path, new_path }); - } - - fn modified_binary(&mut self, path: PathBuf) { - self.modified.push(Modified { - path, - diff: FileDiff::Binary, eof: None, - }) + diff: DiffContent::Binary, + }); + self.files.push(diff) } - fn added(&mut self, path: PathBuf, diff: FileDiff) { - self.added.push(Added { path, diff }) + fn insert_added(&mut self, path: PathBuf, diff: DiffContent) { + let diff = FileDiff::Added(Added { path, diff }); + self.files.push(diff); } - fn deleted(&mut self, path: PathBuf, diff: FileDiff) { - self.deleted.push(Deleted { path, diff }) + fn insert_deleted(&mut self, path: PathBuf, diff: DiffContent) { + let diff = FileDiff::Deleted(Deleted { path, diff }); + self.files.push(diff); } } @@ -91,8 +147,7 @@ impl Diff { pub struct Added { /// The path to this file, relative to the repository root. pub path: PathBuf, - /// The set of [`Addition`]s to this file. - pub diff: FileDiff, + pub diff: DiffContent, } /// A file that was deleted within a [`Diff`]. @@ -101,18 +156,32 @@ pub struct Added { pub struct Deleted { /// The path to this file, relative to the repository root. pub path: PathBuf, - /// The set of [`Deletion`]s to this file. - pub diff: FileDiff, + pub diff: DiffContent, } /// A file that was moved within a [`Diff`]. -#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Moved { /// The old path to this file, relative to the repository root. pub old_path: PathBuf, /// The new path to this file, relative to the repository root. pub new_path: PathBuf, + pub diff: DiffContent, +} + +#[cfg(feature = "serde")] +impl Serialize for Moved { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Moved", 2)?; + state.serialize_field("oldPath", &self.old_path)?; + state.serialize_field("newPath", &self.new_path)?; + // `DiffContent` is not serialized yet for `Moved`, only + // to keep the serialization same as before. + state.end() + } } /// A file that was copied within a [`Diff`]. @@ -123,6 +192,7 @@ pub struct Copied { pub old_path: PathBuf, /// The new path to this file, relative to the repository root. pub new_path: PathBuf, + pub diff: DiffContent, } #[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))] @@ -139,8 +209,7 @@ pub enum EofNewLine { pub struct Modified { /// The path to this file, relative to the repository root. pub path: PathBuf, - /// The set of [`Modification`]s to this file. - pub diff: FileDiff, + pub diff: DiffContent, /// Was there an EOF newline present. pub eof: Option, } @@ -152,12 +221,75 @@ pub struct Modified { serde(tag = "type", rename_all = "camelCase") )] #[derive(Clone, Debug, PartialEq, Eq)] -pub enum FileDiff { +pub enum DiffContent { /// The file is a binary file and so no set of changes can be provided. Binary, /// The set of changes, as [`Hunks`] for a plaintext file. #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] - Plain { hunks: Hunks }, + Plain { + hunks: Hunks, + }, + Empty, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FileDiff { + Added(Added), + Deleted(Deleted), + Modified(Modified), + Moved(Moved), + Copied(Copied), +} + +#[cfg(feature = "serde")] +impl Serialize for FileDiff { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("FileDiff", 7)?; + match &self { + FileDiff::Added(x) => { + state.serialize_field("path", &x.path)?; + state.serialize_field("diff", &x.diff)? + }, + FileDiff::Deleted(x) => { + state.serialize_field("path", &x.path)?; + state.serialize_field("diff", &x.diff)? + }, + FileDiff::Modified(x) => { + state.serialize_field("path", &x.path)?; + state.serialize_field("diff", &x.diff)?; + state.serialize_field("eof", &x.eof)? + }, + FileDiff::Moved(x) => { + state.serialize_field("oldPath", &x.old_path)?; + state.serialize_field("newPath", &x.new_path)? + }, + FileDiff::Copied(x) => { + state.serialize_field("oldPath", &x.old_path)?; + state.serialize_field("newPath", &x.new_path)? + }, + } + state.end() + } +} + +#[cfg(feature = "serde")] +impl Serialize for Diff { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Diff", 6)?; + state.serialize_field("added", &self.added().collect::>())?; + state.serialize_field("deleted", &self.deleted().collect::>())?; + state.serialize_field("moved", &self.moved().collect::>())?; + state.serialize_field("copied", &self.copied().collect::>())?; + state.serialize_field("modified", &self.modified().collect::>())?; + state.serialize_field("stats", &self.stats())?; + state.end() + } } /// Statistics describing a particular [`Diff`]. diff --git a/radicle-surf/src/diff/git.rs b/radicle-surf/src/diff/git.rs index eabe58f..a572db1 100644 --- a/radicle-surf/src/diff/git.rs +++ b/radicle-surf/src/diff/git.rs @@ -241,16 +241,16 @@ fn created( let patch = git2::Patch::from_diff(git_diff, idx)?; if let Some(patch) = patch { - diff.added( + diff.insert_added( path, - diff::FileDiff::Plain { + diff::DiffContent::Plain { hunks: Hunks::try_from(patch)?, }, ); } else { - diff.added( + diff.insert_added( path, - diff::FileDiff::Plain { + diff::DiffContent::Plain { hunks: Hunks::default(), }, ); @@ -271,16 +271,16 @@ fn deleted( .to_path_buf(); let patch = git2::Patch::from_diff(git_diff, idx)?; if let Some(patch) = patch { - diff.deleted( + diff.insert_deleted( path, - diff::FileDiff::Plain { + diff::DiffContent::Plain { hunks: Hunks::try_from(patch)?, }, ); } else { - diff.deleted( + diff.insert_deleted( path, - diff::FileDiff::Plain { + diff::DiffContent::Plain { hunks: Hunks::default(), }, ); @@ -340,10 +340,10 @@ fn modified( (false, true) => Some(EofNewLine::NewMissing), (false, false) => None, }; - diff.modified(path, hunks, eof); + diff.insert_modified(path, hunks, eof); Ok(()) } else if diff_file.is_binary() { - diff.modified_binary(path); + diff.insert_modified_binary(path); Ok(()) } else { Err(error::Diff::PatchUnavailable(path)) @@ -360,7 +360,7 @@ fn renamed(diff: &mut Diff, delta: &git2::DiffDelta<'_>) -> Result<(), error::Di .path() .ok_or(error::Diff::PathUnavailable)?; - diff.moved(old.to_path_buf(), new.to_path_buf()); + diff.insert_moved(old.to_path_buf(), new.to_path_buf()); Ok(()) } @@ -374,6 +374,6 @@ fn copied(diff: &mut Diff, delta: &git2::DiffDelta<'_>) -> Result<(), error::Dif .path() .ok_or(error::Diff::PathUnavailable)?; - diff.copied(old.to_path_buf(), new.to_path_buf()); + diff.insert_copied(old.to_path_buf(), new.to_path_buf()); Ok(()) } diff --git a/radicle-surf/src/repo.rs b/radicle-surf/src/repo.rs index 59040b5..9ef689a 100644 --- a/radicle-surf/src/repo.rs +++ b/radicle-surf/src/repo.rs @@ -27,7 +27,7 @@ use radicle_git_ext::Oid; use crate::{ blob::{Blob, BlobRef}, - diff::Diff, + diff::{Diff, FileDiff}, fs::{Directory, File, FileContent}, refs::{BranchNames, Branches, Categories, Namespaces, TagNames, Tags}, tree::{Entry, Tree}, @@ -201,6 +201,28 @@ impl Repository { } } + /// Get the [`FileDiff`] between two revisions for a file at `path`. + /// + /// If `path` is only a directory name, not a file, returns + /// a [`FileDiff`] for any file under `path`. + pub fn diff_file, R: Revision>( + &self, + path: &P, + from: R, + to: R, + ) -> Result { + let from_commit = self.find_commit(self.object_id(&from)?)?; + let to_commit = self.find_commit(self.object_id(&to)?)?; + let diff = self + .diff_commits(Some(path.as_ref()), Some(&from_commit), &to_commit) + .and_then(|diff| Diff::try_from(diff).map_err(Error::from))?; + let file_diff = diff + .into_files() + .pop() + .ok_or(error::Repo::PathNotFound(path.as_ref().to_path_buf()))?; + Ok(file_diff) + } + /// Parse an [`Oid`] from the given string. pub fn oid(&self, oid: &str) -> Result { Ok(self.inner.revparse_single(oid)?.id().into()) diff --git a/radicle-surf/t/src/diff.rs b/radicle-surf/t/src/diff.rs index 8c153cd..e82ef1e 100644 --- a/radicle-surf/t/src/diff.rs +++ b/radicle-surf/t/src/diff.rs @@ -4,17 +4,14 @@ use radicle_git_ext::Oid; use radicle_surf::{ diff::{ Added, - Addition, - Deleted, - Deletion, Diff, + DiffContent, EofNewLine, FileDiff, Hunk, Line, Modification, Modified, - Moved, Stats, }, Branch, @@ -33,35 +30,32 @@ fn test_initial_diff() -> Result<(), Error> { assert!(commit.parents.is_empty()); let diff = repo.diff_commit(oid)?; + let diff_stats = *diff.stats(); + let diff_files = diff.into_files(); - let expected_diff = Diff { - added: vec![Added { - path: Path::new("README.md").to_path_buf(), - diff: FileDiff::Plain { - hunks: vec![Hunk { - header: Line::from(b"@@ -0,0 +1 @@\n".to_vec()), - lines: vec![Addition { - line: - b"This repository is a data source for the Upstream front-end tests.\n" - .to_vec() - .into(), - line_no: 1, - }], - }] - .into(), - }, - }], - deleted: vec![], - moved: vec![], - copied: vec![], - modified: vec![], - stats: Stats { - files_changed: 1, - insertions: 1, - deletions: 0, + let expected_files = vec![FileDiff::Added(Added { + path: Path::new("README.md").to_path_buf(), + diff: DiffContent::Plain { + hunks: vec![Hunk { + header: Line::from(b"@@ -0,0 +1 @@\n".to_vec()), + lines: vec![Modification::addition( + b"This repository is a data source for the Upstream front-end tests.\n" + .to_vec(), + 1, + )], + }] + .into(), }, + })]; + + let expected_stats = Stats { + files_changed: 1, + insertions: 1, + deletions: 0, }; - assert_eq!(expected_diff, diff); + + assert_eq!(expected_files, diff_files); + assert_eq!(expected_stats, diff_stats); Ok(()) } @@ -70,10 +64,35 @@ fn test_initial_diff() -> Result<(), Error> { fn test_diff_of_rev() -> Result<(), Error> { let repo = Repository::open(GIT_PLATINUM)?; let diff = repo.diff_commit("80bacafba303bf0cdf6142921f430ff265f25095")?; - assert_eq!(diff.added.len(), 0); - assert_eq!(diff.deleted.len(), 0); - assert_eq!(diff.moved.len(), 0); - assert_eq!(diff.modified.len(), 1); + assert_eq!(diff.files().count(), 1); + Ok(()) +} + +#[test] +fn test_diff_file() -> Result<(), Error> { + let repo = Repository::open(GIT_PLATINUM)?; + let path_buf = Path::new("README.md").to_path_buf(); + let diff = repo.diff_file( + &path_buf, + "d6880352fc7fda8f521ae9b7357668b17bb5bad5", + "223aaf87d6ea62eef0014857640fd7c8dd0f80b5", + )?; + let expected_diff = FileDiff::Modified(Modified { + path: path_buf, + eof: None, + diff: DiffContent::Plain { + hunks: vec![Hunk { + header: Line::from(b"@@ -1 +1,2 @@\n".to_vec()), + lines: vec![ + Modification::deletion(b"This repository is a data source for the Upstream front-end tests.\n".to_vec(), 1), + Modification::addition(b"This repository is a data source for the Upstream front-end tests and the\n".to_vec(), 1), + Modification::addition(b"[`radicle-surf`](https://github.com/radicle-dev/git-platinum) unit tests.\n".to_vec(), 2), + ] + }].into() + }, + }); + assert_eq!(expected_diff, diff); + Ok(()) } @@ -85,14 +104,10 @@ fn test_diff() -> Result<(), Error> { let parent_oid = commit.parents.get(0).unwrap(); let diff = repo.diff(*parent_oid, oid)?; - let expected_diff = Diff { - added: vec![], - deleted: vec![], - moved: vec![], - copied: vec![], - modified: vec![Modified { + let expected_files = vec![FileDiff::Modified(Modified { path: Path::new("README.md").to_path_buf(), - diff: FileDiff::Plain { + eof: None, + diff: DiffContent::Plain { hunks: vec![Hunk { header: Line::from(b"@@ -1 +1,2 @@\n".to_vec()), lines: vec![ @@ -102,15 +117,16 @@ fn test_diff() -> Result<(), Error> { ] }].into() }, - eof: None, - }], - stats: Stats { - files_changed: 1, - insertions: 2, - deletions: 1, - }, + })]; + let expected_stats = Stats { + files_changed: 1, + insertions: 2, + deletions: 1, }; - assert_eq!(expected_diff, diff); + let diff_stats = *diff.stats(); + let diff_files = diff.into_files(); + assert_eq!(expected_files, diff_files); + assert_eq!(expected_stats, diff_stats); Ok(()) } @@ -118,89 +134,59 @@ fn test_diff() -> Result<(), Error> { #[test] fn test_branch_diff() -> Result<(), Error> { let repo = Repository::open(GIT_PLATINUM)?; - let diff = repo.diff( - Branch::local(refname!("master")), - Branch::local(refname!("dev")), - )?; + let rev_from = Branch::local(refname!("master")); + let rev_to = Branch::local(refname!("dev")); + let diff = repo.diff(&rev_from, &rev_to)?; println!("Diff two branches: master -> dev"); println!( "result: added {} deleted {} moved {} modified {}", - diff.added.len(), - diff.deleted.len(), - diff.moved.len(), - diff.modified.len() + diff.added().count(), + diff.deleted().count(), + diff.moved().count(), + diff.modified().count() ); - assert_eq!(diff.added.len(), 1); - assert_eq!(diff.deleted.len(), 11); - assert_eq!(diff.moved.len(), 1); - assert_eq!(diff.modified.len(), 2); - for c in diff.added.iter() { + assert_eq!(diff.added().count(), 1); + assert_eq!(diff.deleted().count(), 11); + assert_eq!(diff.moved().count(), 1); + assert_eq!(diff.modified().count(), 2); + for c in diff.added() { println!("added: {:?}", &c.path); } - for d in diff.deleted.iter() { + for d in diff.deleted() { println!("deleted: {:?}", &d.path); } - for m in diff.moved.iter() { + for m in diff.moved() { println!("moved: {:?} -> {:?}", &m.old_path, &m.new_path); } - for m in diff.modified.iter() { + for m in diff.modified() { println!("modified: {:?}", &m.path); } + + // Verify moved. + let diff_moved = diff.moved().next().unwrap(); + + // We can find a `FileDiff` for the old_path in a move. + let file_diff = repo.diff_file(&diff_moved.old_path, &rev_from, &rev_to)?; + println!("old path file diff: {:?}", &file_diff); + + // We can find a `FileDiff` for the new_path in a move. + let file_diff = repo.diff_file(&diff_moved.new_path, &rev_from, &rev_to)?; + println!("new path file diff: {:?}", &file_diff); + + // We can find a `FileDiff` if given a directory name. + let dir_diff = repo.diff_file(&"special/", &rev_from, &rev_to)?; + println!("dir diff: {dir_diff:?}"); + Ok(()) } #[test] -fn test_diff_serde() { - let diff = Diff { - added: vec![ Added { - path: Path::new("LICENSE").to_path_buf(), - diff: FileDiff::Plain { - hunks: vec![Hunk { - header: Line::from(b"@@ -0,0 +1,1".to_vec()), - lines: vec![ - Addition { line: Line::from(b"MIT".to_vec()), line_no: 1 } - ] - }].into() - } - }], - deleted: vec![ Deleted { - path: Path::new("DCO").to_path_buf(), - diff: FileDiff::Plain { - hunks: vec![Hunk { - header: Line::from(b"@@ -0,0 +1,1".to_vec()), - lines: vec![ - Deletion { line: Line::from(b"TODO".to_vec()), line_no: 1 } - ] - }].into() - } - }], - moved: vec![ Moved { - old_path: Path::new("CONTRIBUTING").to_path_buf(), - new_path: Path::new("CONTRIBUTING.md").to_path_buf(), - }], - copied: vec![], - modified: vec![ Modified { - path: Path::new("README.md").to_path_buf(), - diff: FileDiff::Plain { - hunks: vec![Hunk { - header: Line::from(b"@@ -1 +1,2 @@\n".to_vec()), - lines: vec![ - Modification::deletion(b"This repository is a data source for the Upstream front-end tests.\n".to_vec(), 1), - Modification::addition(b"This repository is a data source for the Upstream front-end tests and the\n".to_vec(), 1), - Modification::addition(b"[`radicle-surf`](https://github.com/radicle-dev/git-platinum) unit tests.\n".to_vec(), 2), - Modification::context(b"\n".to_vec(), 3, 4), - ] - }].into() - }, - eof: None, - }], - stats: Stats { - files_changed: 3, - insertions: 2, - deletions: 1, - }, - }; +fn test_diff_serde() -> Result<(), Error> { + let repo = Repository::open(GIT_PLATINUM)?; + let rev_from = Branch::local(refname!("master")); + let rev_to = Branch::local(refname!("diff-test")); + let diff = repo.diff(rev_from, rev_to)?; let eof: Option = None; let json = serde_json::json!({ @@ -209,66 +195,108 @@ fn test_diff_serde() { "diff": { "type": "plain", "hunks": [{ - "header": "@@ -0,0 +1,1", + "header": "@@ -0,0 +1,2 @@\n", "lines": [{ - "line": "MIT", + "line": "This is a license file.\n", "lineNo": 1, "type": "addition", + }, + { + "line": "\n", + "lineNo": 2, + "type": "addition", }] }] }, }], "deleted": [{ - "path": "DCO", + "path": "text/arrows.txt", "diff": { "type": "plain", "hunks": [{ - "header": "@@ -0,0 +1,1", - "lines": [{ - "line": "TODO", + "header": "@@ -1,7 +0,0 @@\n", + "lines": [ + { + "line": " ;;;;; ;;;;; ;;;;;\n", "lineNo": 1, "type": "deletion", - }] + }, + { + "line": " ;;;;; ;;;;; ;;;;;\n", + "lineNo": 2, + "type": "deletion", + }, + { + "line": " ;;;;; ;;;;; ;;;;;\n", + "lineNo": 3, + "type": "deletion", + }, + { + "line": " ;;;;; ;;;;; ;;;;;\n", + "lineNo": 4, + "type": "deletion", + }, + { + "line": "..;;;;;.. ..;;;;;.. ..;;;;;..\n", + "lineNo": 5, + "type": "deletion", + }, + { + "line": " ':::::' ':::::' ':::::'\n", + "lineNo": 6, + "type": "deletion", + }, + { + "line": " ':` ':` ':`\n", + "lineNo": 7, + "type": "deletion", + }, + ] }] }, }], - "moved": [{ "oldPath": "CONTRIBUTING", "newPath": "CONTRIBUTING.md" }], + "moved": [{ + "oldPath": "text/emoji.txt", + "newPath": "emoji.txt", + }], "copied": [], "modified": [{ "path": "README.md", "diff": { "type": "plain", "hunks": [{ - "header": "@@ -1 +1,2 @@\n", + "header": "@@ -1,2 +1,2 @@\n", "lines": [ - { "lineNo": 1, - "line": "This repository is a data source for the Upstream front-end tests.\n", - "type": "deletion" - }, { "lineNo": 1, "line": "This repository is a data source for the Upstream front-end tests and the\n", - "type": "addition" + "type": "deletion" }, { "lineNo": 2, "line": "[`radicle-surf`](https://github.com/radicle-dev/git-platinum) unit tests.\n", + "type": "deletion" + }, + { "lineNo": 1, + "line": "This repository is a data source for the upstream front-end tests and the\n", + "type": "addition" + }, + { "lineNo": 2, + "line": "[`radicle-surf`](https://github.com/radicle-dev/radicle-surf) unit tests.\n", "type": "addition" }, - { "lineNoOld": 3, "lineNoNew": 4, - "line": "\n", - "type": "context" - } ] }] }, - "eof" : eof, + "eof": eof, }], "stats": { - "deletions": 1, - "filesChanged": 3, - "insertions": 2, + "deletions": 9, + "filesChanged": 4, + "insertions": 4, } }); - assert_eq!(serde_json::to_value(&diff).unwrap(), json); + assert_eq!(serde_json::to_value(diff).unwrap(), json); + + Ok(()) } #[test] @@ -286,7 +314,10 @@ index f89e4c0..7c56eb7 100644 "#; let diff = git2::Diff::from_buffer(buf.as_bytes()).unwrap(); let diff = Diff::try_from(diff).unwrap(); - assert_eq!(diff.modified[0].eof, Some(EofNewLine::BothMissing)); + assert_eq!( + diff.modified().next().unwrap().eof, + Some(EofNewLine::BothMissing) + ); } #[test] @@ -302,7 +333,7 @@ index f89e4c0..7c56eb7 100644 "#; let diff = git2::Diff::from_buffer(buf.as_bytes()).unwrap(); let diff = Diff::try_from(diff).unwrap(); - assert_eq!(diff.modified[0].eof, None); + assert_eq!(diff.modified().next().unwrap().eof, None); } // TODO(xphoniex): uncomment once libgit2 has fixed the bug diff --git a/radicle-surf/t/src/source.rs b/radicle-surf/t/src/source.rs index e634c73..0a4fe2f 100644 --- a/radicle-surf/t/src/source.rs +++ b/radicle-surf/t/src/source.rs @@ -209,23 +209,23 @@ fn commit_branches() { let glob = Glob::all_heads().branches().and(Glob::all_remotes()); let branches = repo.revision_branches(init_commit, glob).unwrap(); - assert_eq!(branches.len(), 9); - assert_eq!(branches[0].refname().as_str(), "refs/heads/dev"); - assert_eq!(branches[1].refname().as_str(), "refs/heads/empty-branch"); - assert_eq!(branches[2].refname().as_str(), "refs/heads/master"); + assert_eq!(branches.len(), 11); + + let refnames: Vec<_> = branches.iter().map(|b| b.refname().to_string()).collect(); assert_eq!( - branches[3].refname().as_str(), - "refs/remotes/banana/orange/pineapple" + refnames, + vec![ + "refs/heads/dev", + "refs/heads/diff-test", + "refs/heads/empty-branch", + "refs/heads/master", + "refs/remotes/banana/orange/pineapple", + "refs/remotes/banana/pineapple", + "refs/remotes/origin/HEAD", + "refs/remotes/origin/dev", + "refs/remotes/origin/diff-test", + "refs/remotes/origin/empty-branch", + "refs/remotes/origin/master" + ] ); - assert_eq!( - branches[4].refname().as_str(), - "refs/remotes/banana/pineapple" - ); - assert_eq!(branches[5].refname().as_str(), "refs/remotes/origin/HEAD"); - assert_eq!(branches[6].refname().as_str(), "refs/remotes/origin/dev"); - assert_eq!( - branches[7].refname().as_str(), - "refs/remotes/origin/empty-branch" - ); - assert_eq!(branches[8].refname().as_str(), "refs/remotes/origin/master"); } diff --git a/radicle-surf/t/src/threading.rs b/radicle-surf/t/src/threading.rs index 762b00d..0b39dfc 100644 --- a/radicle-surf/t/src/threading.rs +++ b/radicle-surf/t/src/threading.rs @@ -20,12 +20,14 @@ fn basic_test() -> Result<(), Error> { branches, vec![ Branch::local(refname!("dev")), + Branch::local(refname!("diff-test")), Branch::local(refname!("empty-branch")), Branch::local(refname!("master")), Branch::remote(banana.clone(), refname!("orange/pineapple")), Branch::remote(banana, refname!("pineapple")), Branch::remote(origin.clone(), refname!("HEAD")), Branch::remote(origin.clone(), refname!("dev")), + Branch::remote(origin.clone(), refname!("diff-test")), Branch::remote(origin.clone(), refname!("empty-branch")), Branch::remote(origin, refname!("master")), ]