mirror of
https://seed.radicle.dev/rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt.git
synced 2026-10-24 13:50:36 +08:00
add diff_file method for Repository.
Also refactored `FileDiff` type. Signed-off-by: Han Xu <keepsimple@gmail.com>
This commit is contained in:
Binary file not shown.
@@ -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
@@ -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`].
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 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 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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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")),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user