Skip to content

Commit 64bbb3d

Browse files
committed
Support for Path tracking (#470)
1 parent ae38660 commit 64bbb3d

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

git-repository/src/object/tree/diff.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
use crate::bstr::{BStr, BString, ByteVec};
1+
use crate::bstr::{BStr, BString, ByteSlice, ByteVec};
22
use crate::ext::ObjectIdExt;
33
use crate::{Repository, Tree};
44
use git_object::TreeRefIter;
55
use git_odb::FindExt;
6+
use std::collections::VecDeque;
67

78
/// The error return by methods on the [diff platform][Platform].
89
#[derive(Debug, thiserror::Error)]
@@ -100,6 +101,7 @@ pub struct Platform<'a, 'repo> {
100101
#[derive(Clone, Copy)]
101102
enum Tracking {
102103
FileName,
104+
Path,
103105
}
104106

105107
/// Configuration
@@ -109,6 +111,14 @@ impl<'a, 'repo> Platform<'a, 'repo> {
109111
self.tracking = Some(Tracking::FileName);
110112
self
111113
}
114+
115+
/// Keep track of the entire path of a change, relative to the repository.
116+
///
117+
/// This makes the [`location`][Change::location] field usable.
118+
pub fn track_path(&mut self) -> &mut Self {
119+
self.tracking = Some(Tracking::Path);
120+
self
121+
}
112122
}
113123

114124
/// Add the item to compare to.
@@ -128,6 +138,7 @@ impl<'a, 'repo> Platform<'a, 'repo> {
128138
other_repo: other.repo,
129139
tracking: self.tracking,
130140
location: BString::default(),
141+
path_deque: Default::default(),
131142
visit: for_each,
132143
err: None,
133144
};
@@ -149,19 +160,47 @@ struct Delegate<'repo, 'other_repo, VisitFn, E> {
149160
other_repo: &'other_repo Repository,
150161
tracking: Option<Tracking>,
151162
location: BString,
163+
path_deque: VecDeque<BString>,
152164
visit: VisitFn,
153165
err: Option<E>,
154166
}
155167

168+
impl<A, B> Delegate<'_, '_, A, B> {
169+
fn pop_element(&mut self) {
170+
if let Some(pos) = self.location.rfind_byte(b'/') {
171+
self.location.resize(pos, 0);
172+
} else {
173+
self.location.clear();
174+
}
175+
}
176+
177+
fn push_element(&mut self, name: &BStr) {
178+
if !self.location.is_empty() {
179+
self.location.push(b'/');
180+
}
181+
self.location.push_str(name);
182+
}
183+
}
184+
156185
impl<'repo, 'other_repo, VisitFn, E> git_diff::tree::Visit for Delegate<'repo, 'other_repo, VisitFn, E>
157186
where
158187
VisitFn: for<'delegate> FnMut(Change<'delegate, 'repo, 'other_repo>) -> Result<Action, E>,
159188
E: std::error::Error + Sync + Send + 'static,
160189
{
161-
fn pop_front_tracked_path_and_set_current(&mut self) {}
190+
fn pop_front_tracked_path_and_set_current(&mut self) {
191+
if let Some(Tracking::Path) = self.tracking {
192+
self.location = self
193+
.path_deque
194+
.pop_front()
195+
.expect("every call is matched with push_tracked_path_component");
196+
}
197+
}
162198

163-
fn push_back_tracked_path_component(&mut self, _component: &BStr) {
164-
{}
199+
fn push_back_tracked_path_component(&mut self, component: &BStr) {
200+
if let Some(Tracking::Path) = self.tracking {
201+
self.push_element(component);
202+
self.path_deque.push_back(self.location.clone());
203+
}
165204
}
166205

167206
fn push_path_component(&mut self, component: &BStr) {
@@ -170,11 +209,18 @@ where
170209
self.location.clear();
171210
self.location.push_str(component);
172211
}
212+
Some(Tracking::Path) => {
213+
self.push_element(component);
214+
}
173215
None => {}
174216
}
175217
}
176218

177-
fn pop_path_component(&mut self) {}
219+
fn pop_path_component(&mut self) {
220+
if let Some(Tracking::Path) = self.tracking {
221+
self.pop_element();
222+
}
223+
}
178224

179225
fn visit(&mut self, change: git_diff::tree::visit::Change) -> git_diff::tree::visit::Action {
180226
use git_diff::tree::visit::Change::*;

git-repository/tests/object/tree.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,17 @@ mod diff {
4949
Ok(Default::default())
5050
})
5151
.unwrap();
52-
assert_eq!(expected, Vec::<&str>::new(), "all paths should have been seen")
52+
assert_eq!(expected, Vec::<&str>::new(), "all paths should have been seen");
53+
54+
let mut expected = vec!["a", "b", "dir/c"];
55+
from.changes()
56+
.track_path()
57+
.for_each_to_obtain_tree(&to, |change| -> Result<_, Infallible> {
58+
expected.retain(|name| name != change.location);
59+
Ok(Default::default())
60+
})
61+
.unwrap();
62+
assert_eq!(expected, Vec::<&str>::new(), "all paths should have been seen");
5363
}
5464

5565
fn tree_named<'repo>(repo: &'repo git::Repository, rev_spec: &str) -> git::Tree<'repo> {

0 commit comments

Comments
 (0)