History

delegate-based tree diff traversal for maximum flexibility and performance

Sebastian Thiel committed 5 years ago

Commit cbacca01 parent 230ef045 files · +104 −72Browse files

Showing the changes to git-diff/src (1 of 5 files). Show all

Unified · Split

git-diff/src/lib.rs+94−64
@@ -1,99 +1,129 @@
11 // TODO: add deny(missing_docs)
22 #![forbid(unsafe_code, rust_2018_idioms)]
3−#![allow(dead_code, unused_variables)]
43
54 ///
65 pub mod tree {
76 use git_hash::{oid, ObjectId};
8− use git_object::{
9− bstr::{BStr, BString},
10− immutable, tree,
11− };
7+ use git_object::immutable;
8+ use quick_error::quick_error;
129
1310 const EMPTY_TREE: immutable::Tree<'static> = immutable::Tree::empty();
1411
15− type PathNodeId = usize;
16−
17− /// A tree of paths to make storage and allocation of paths more efficient.
18− #[derive(Default)]
19− pub struct PathTree {
20− graph: petgraph::graph::DiGraph<BString, ()>,
21− }
22−
23− impl PathTree {
24− /// Find the path with the given `id` and place all of its elements in `out_elements`.
25− /// Returns the amount of elements the path contains, like `&["a", "b", "c"]` would be `Some(3)`.
26− ///
27− /// The output vector will be cleared beforehand.
28− pub fn elements(&self, id: PathNodeId, out_elements: &mut Vec<&BStr>) -> Option<usize> {
29− todo!("fetch elements")
12+ quick_error! {
13+ #[derive(Debug)]
14+ pub enum Error {
15+ NotFound(oid: ObjectId) {
16+ display("The object {} referenced by the tree was not found in the database", oid)
17+ }
18+ Cancelled {
19+ display("The delegate cancelled the operation")
20+ }
3021 }
3122 }
3223
33− pub enum Change {
34− Addition {
35− mode: tree::Mode,
36− oid: ObjectId,
37− path: PathNodeId,
38− },
39− Copy,
40− Deletion,
41− Modification,
42− Renaming,
43− Type,
24+ #[derive(Default, Clone)]
25+ pub struct State {
26+ buf1: Vec<u8>,
27+ buf2: Vec<u8>,
4428 }
4529
46− pub struct Changes<'a>(Option<&'a immutable::Tree<'a>>);
30+ pub struct VisitChanges<'a>(Option<&'a immutable::Tree<'a>>);
4731
48− impl<'a, T> From<T> for Changes<'a>
32+ impl<'a, T> From<T> for VisitChanges<'a>
4933 where
5034 T: Into<Option<&'a immutable::Tree<'a>>>,
5135 {
5236 fn from(v: T) -> Self {
53− Changes(v.into())
37+ VisitChanges(v.into())
5438 }
5539 }
5640
57− // Possible things to detect (from git diff --help)
58− // o A: addition of a file
59− // o C: copy of a file into a new one
60− // o D: deletion of a file
61− // o M: modification of the contents or mode of a file
62− // o R: renaming of a file
63− // o T: change in the type of the file
64− // o U: file is unmerged (you must complete the merge before it can be committed)
65− // o X: "unknown" change type (most probably a bug, please report it)
66− impl<'a> Changes<'a> {
41+ impl<'a> VisitChanges<'a> {
6742 /// Returns the changes that need to be applied to `self` to get `other`.
6843 pub fn to_obtain<LocateFn>(
6944 &self,
7045 _other: &git_object::immutable::Tree<'_>,
71− buf: (&mut Vec<u8>, &mut Vec<u8>),
72− locate: LocateFn,
73− out_paths: &mut PathTree,
74− out_changes: &mut Vec<Change>,
75− ) where
76− LocateFn: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Option<immutable::Tree<'b>>,
46+ _state: &mut State,
47+ _locate: LocateFn,
48+ _delegate: &mut impl Delegate,
49+ ) -> Result<(), Error>
50+ where
51+ LocateFn: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Option<immutable::Object<'b>>,
7752 {
78− out_paths.graph.clear();
79− out_changes.clear();
80−
8153 let _this = *self.0.as_ref().unwrap_or(&&EMPTY_TREE);
8254 todo!("changes tree to tree")
8355 }
8456 }
8557
86− #[cfg(test)]
87− mod tests {
88− use super::*;
89−
90− #[test]
91− fn size_of_change() {
92− assert_eq!(
93− std::mem::size_of::<Change>(),
94− 32,
95− "this type shouldn't grow without us knowing"
96− )
58+ pub mod delegate {
59+ use git_hash::ObjectId;
60+ use git_object::{bstr::BStr, tree};
61+
62+ pub enum Change {
63+ Addition { mode: tree::Mode, oid: ObjectId },
64+ Copy,
65+ Deletion,
66+ Modification,
67+ Renaming,
68+ Type,
69+ }
70+
71+ #[derive(Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
72+ pub enum PathComponentMode {
73+ Replace,
74+ Push,
75+ }
76+
77+ #[derive(Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
78+ pub struct PathComponent<'a> {
79+ pub component: &'a BStr,
80+ /// An ID referring uniquely to the path built thus far. Used to keep track of source paths
81+ /// in case of [renames][Change::Rename] and [copies][Change::Copy].
82+ pub id: usize,
83+ }
84+
85+ #[derive(Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
86+ pub enum Action {
87+ Continue,
88+ Cancel,
89+ }
90+
91+ pub trait Delegate {
92+ fn update_path_component(&mut self, component: PathComponent<'_>, mode: PathComponentMode);
93+ fn pop_path_component(&mut self);
94+ fn record(change: Change) -> Action;
95+ }
96+
97+ #[derive(Clone, Default)]
98+ pub struct Recorder;
99+
100+ impl Delegate for Recorder {
101+ fn update_path_component(&mut self, _component: PathComponent<'_>, _mode: PathComponentMode) {
102+ todo!()
103+ }
104+
105+ fn pop_path_component(&mut self) {
106+ todo!()
107+ }
108+
109+ fn record(_change: Change) -> Action {
110+ todo!()
111+ }
112+ }
113+
114+ #[cfg(test)]
115+ mod tests {
116+ use super::*;
117+
118+ #[test]
119+ fn size_of_change() {
120+ assert_eq!(
121+ std::mem::size_of::<Change>(),
122+ 22,
123+ "this type shouldn't grow without us knowing"
124+ )
125+ }
97126 }
98127 }
128+ pub use delegate::Delegate;
99129 }