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 @@
1// TODO: add deny(missing_docs)1// TODO: add deny(missing_docs)
2#![forbid(unsafe_code, rust_2018_idioms)]2#![forbid(unsafe_code, rust_2018_idioms)]
3#![allow(dead_code, unused_variables)]
4 3
5///4///
6pub mod tree {5pub mod tree {
7 use git_hash::{oid, ObjectId};6 use git_hash::{oid, ObjectId};
8 use git_object::{7 use git_object::immutable;
9 bstr::{BStr, BString},8 use quick_error::quick_error;
10 immutable, tree,
11 };
12 9
13 const EMPTY_TREE: immutable::Tree<'static> = immutable::Tree::empty();10 const EMPTY_TREE: immutable::Tree<'static> = immutable::Tree::empty();
14 11
15 type PathNodeId = usize;12 quick_error! {
16 13 #[derive(Debug)]
17 /// A tree of paths to make storage and allocation of paths more efficient.14 pub enum Error {
18 #[derive(Default)]15 NotFound(oid: ObjectId) {
19 pub struct PathTree {16 display("The object {} referenced by the tree was not found in the database", oid)
20 graph: petgraph::graph::DiGraph<BString, ()>,17 }
21 }18 Cancelled {
22 19 display("The delegate cancelled the operation")
23 impl PathTree {20 }
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")
30 }21 }
31 }22 }
32 23
33 pub enum Change {24 #[derive(Default, Clone)]
34 Addition {25 pub struct State {
35 mode: tree::Mode,26 buf1: Vec<u8>,
36 oid: ObjectId,27 buf2: Vec<u8>,
37 path: PathNodeId,
38 },
39 Copy,
40 Deletion,
41 Modification,
42 Renaming,
43 Type,
44 }28 }
45 29
46 pub struct Changes<'a>(Option<&'a immutable::Tree<'a>>);30 pub struct VisitChanges<'a>(Option<&'a immutable::Tree<'a>>);
47 31
48 impl<'a, T> From<T> for Changes<'a>32 impl<'a, T> From<T> for VisitChanges<'a>
49 where33 where
50 T: Into<Option<&'a immutable::Tree<'a>>>,34 T: Into<Option<&'a immutable::Tree<'a>>>,
51 {35 {
52 fn from(v: T) -> Self {36 fn from(v: T) -> Self {
53 Changes(v.into())37 VisitChanges(v.into())
54 }38 }
55 }39 }
56 40
57 // Possible things to detect (from git diff --help)41 impl<'a> VisitChanges<'a> {
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> {
67 /// Returns the changes that need to be applied to `self` to get `other`.42 /// Returns the changes that need to be applied to `self` to get `other`.
68 pub fn to_obtain<LocateFn>(43 pub fn to_obtain<LocateFn>(
69 &self,44 &self,
70 _other: &git_object::immutable::Tree<'_>,45 _other: &git_object::immutable::Tree<'_>,
71 buf: (&mut Vec<u8>, &mut Vec<u8>),46 _state: &mut State,
72 locate: LocateFn,47 _locate: LocateFn,
73 out_paths: &mut PathTree,48 _delegate: &mut impl Delegate,
74 out_changes: &mut Vec<Change>,49 ) -> Result<(), Error>
75 ) where50 where
76 LocateFn: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Option<immutable::Tree<'b>>,51 LocateFn: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Option<immutable::Object<'b>>,
77 {52 {
78 out_paths.graph.clear();
79 out_changes.clear();
80
81 let _this = *self.0.as_ref().unwrap_or(&&EMPTY_TREE);53 let _this = *self.0.as_ref().unwrap_or(&&EMPTY_TREE);
82 todo!("changes tree to tree")54 todo!("changes tree to tree")
83 }55 }
84 }56 }
85 57
86 #[cfg(test)]58 pub mod delegate {
87 mod tests {59 use git_hash::ObjectId;
88 use super::*;60 use git_object::{bstr::BStr, tree};
89 61
90 #[test]62 pub enum Change {
91 fn size_of_change() {63 Addition { mode: tree::Mode, oid: ObjectId },
92 assert_eq!(64 Copy,
93 std::mem::size_of::<Change>(),65 Deletion,
94 32,66 Modification,
95 "this type shouldn't grow without us knowing"67 Renaming,
96 )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 }
97 }126 }
98 }127 }
128 pub use delegate::Delegate;
99}129}