add diff_file method for Repository.

Also refactored `FileDiff` type.

Signed-off-by: Han Xu <keepsimple@gmail.com>
This commit is contained in:
Han Xu
2023-01-31 09:10:21 -08:00
parent e7766d65d8
commit 41a2cf5bdc
9 changed files with 409 additions and 221 deletions
Binary file not shown.
+7 -7
View File
@@ -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 ");
}
@@ -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.
+176 -44
View File
@@ -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<Added>,
pub deleted: Vec<Deleted>,
pub moved: Vec<Moved>,
pub copied: Vec<Copied>,
pub modified: Vec<Modified>,
pub stats: Stats,
files: Vec<FileDiff>,
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<Item = &FileDiff> {
self.files.iter()
}
/// Returns owned files in the diff.
pub fn into_files(self) -> Vec<FileDiff> {
self.files
}
pub fn added(&self) -> impl Iterator<Item = &Added> {
self.files().filter_map(|x| match x {
FileDiff::Added(a) => Some(a),
_ => None,
})
}
pub fn deleted(&self) -> impl Iterator<Item = &Deleted> {
self.files().filter_map(|x| match x {
FileDiff::Deleted(a) => Some(a),
_ => None,
})
}
pub fn moved(&self) -> impl Iterator<Item = &Moved> {
self.files().filter_map(|x| match x {
FileDiff::Moved(a) => Some(a),
_ => None,
})
}
pub fn modified(&self) -> impl Iterator<Item = &Modified> {
self.files().filter_map(|x| match x {
FileDiff::Modified(a) => Some(a),
_ => None,
})
}
pub fn copied(&self) -> impl Iterator<Item = &Copied> {
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<Hunks<Modification>>,
eof: Option<EofNewLine>,
) {
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<Addition>) {
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<Deletion>) {
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<Addition>,
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<Deletion>,
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<Modification>,
pub diff: DiffContent,
/// Was there an EOF newline present.
pub eof: Option<EofNewLine>,
}
@@ -152,12 +221,75 @@ pub struct Modified {
serde(tag = "type", rename_all = "camelCase")
)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FileDiff<T> {
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<T> },
Plain {
hunks: Hunks<Modification>,
},
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Diff", 6)?;
state.serialize_field("added", &self.added().collect::<Vec<_>>())?;
state.serialize_field("deleted", &self.deleted().collect::<Vec<_>>())?;
state.serialize_field("moved", &self.moved().collect::<Vec<_>>())?;
state.serialize_field("copied", &self.copied().collect::<Vec<_>>())?;
state.serialize_field("modified", &self.modified().collect::<Vec<_>>())?;
state.serialize_field("stats", &self.stats())?;
state.end()
}
}
/// Statistics describing a particular [`Diff`].
+12 -12
View File
@@ -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(())
}
+23 -1
View File
@@ -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<P: AsRef<Path>, R: Revision>(
&self,
path: &P,
from: R,
to: R,
) -> Result<FileDiff, Error> {
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<Oid, Error> {
Ok(self.inner.revparse_single(oid)?.id().into())
+171 -140
View File
@@ -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<u8> = 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
+17 -17
View File
@@ -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");
}
+2
View File
@@ -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")),
]