Skip to content

Commit bf43a16

Browse files
committed
wip
1 parent 06dfe42 commit bf43a16

File tree

11 files changed

+535
-216
lines changed

11 files changed

+535
-216
lines changed

asyncgit/src/asyncjob/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crossbeam_channel::Sender;
77
use std::sync::{Arc, Mutex, RwLock};
88

99
/// Passed to `AsyncJob::run` allowing sending intermediate progress notifications
10+
#[derive(Clone)]
1011
pub struct RunParams<
1112
T: Copy + Send,
1213
P: Clone + Send + Sync + PartialEq,
@@ -37,6 +38,11 @@ impl<T: Copy + Send, P: Clone + Send + Sync + PartialEq>
3738
true
3839
})
3940
}
41+
42+
///
43+
pub fn progress(&self) -> P {
44+
self.progress.read().cl
45+
}
4046
}
4147

4248
/// trait that defines an async task we can run on a threadpool

asyncgit/src/file_history.rs

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
use git2::Repository;
2+
3+
use crate::{
4+
asyncjob::{AsyncJob, RunParams},
5+
error::Result,
6+
sync::{
7+
self,
8+
commit_files::{
9+
commit_contains_file, commit_detect_file_rename,
10+
},
11+
CommitId, CommitInfo, LogWalker, RepoPath,
12+
SharedCommitFilterFn,
13+
},
14+
AsyncGitNotification,
15+
};
16+
use std::{
17+
sync::{Arc, Mutex, RwLock},
18+
time::{Duration, Instant},
19+
};
20+
21+
///
22+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23+
pub enum FileHistoryEntryDelta {
24+
///
25+
None,
26+
///
27+
Added,
28+
///
29+
Deleted,
30+
///
31+
Modified,
32+
///
33+
Renamed,
34+
///
35+
Copied,
36+
///
37+
Typechange,
38+
}
39+
40+
impl From<git2::Delta> for FileHistoryEntryDelta {
41+
fn from(value: git2::Delta) -> Self {
42+
match value {
43+
git2::Delta::Unmodified
44+
| git2::Delta::Ignored
45+
| git2::Delta::Unreadable
46+
| git2::Delta::Conflicted
47+
| git2::Delta::Untracked => FileHistoryEntryDelta::None,
48+
git2::Delta::Added => FileHistoryEntryDelta::Added,
49+
git2::Delta::Deleted => FileHistoryEntryDelta::Deleted,
50+
git2::Delta::Modified => FileHistoryEntryDelta::Modified,
51+
git2::Delta::Renamed => FileHistoryEntryDelta::Renamed,
52+
git2::Delta::Copied => FileHistoryEntryDelta::Copied,
53+
git2::Delta::Typechange => {
54+
FileHistoryEntryDelta::Typechange
55+
}
56+
}
57+
}
58+
}
59+
60+
///
61+
#[derive(Debug, Clone, PartialEq)]
62+
pub struct FileHistoryEntry {
63+
///
64+
pub commit: CommitId,
65+
///
66+
pub delta: FileHistoryEntryDelta,
67+
//TODO: arc and share since most will be the same over the history
68+
///
69+
pub file_path: String,
70+
///
71+
pub info: CommitInfo,
72+
}
73+
74+
///
75+
pub struct CommitFilterResult {
76+
///
77+
pub result: Vec<FileHistoryEntry>,
78+
pub duration: Duration,
79+
}
80+
81+
enum JobState {
82+
Request {
83+
file_path: String,
84+
repo_path: RepoPath,
85+
},
86+
Response(Result<CommitFilterResult>),
87+
}
88+
89+
#[derive(Clone, Default)]
90+
pub struct AsyncFileHistoryResults(Arc<Mutex<Vec<FileHistoryEntry>>>);
91+
92+
impl PartialEq for AsyncFileHistoryResults {
93+
fn eq(&self, other: &Self) -> bool {
94+
if let Ok(left) = self.0.lock() {
95+
if let Ok(right) = other.0.lock() {
96+
return *left == *right;
97+
}
98+
}
99+
100+
false
101+
}
102+
}
103+
104+
impl AsyncFileHistoryResults {
105+
///
106+
pub fn extract_results(&self) -> Result<Vec<FileHistoryEntry>> {
107+
let mut results = self.0.lock()?;
108+
let results =
109+
std::mem::replace(&mut *results, Vec::with_capacity(1));
110+
Ok(results)
111+
}
112+
}
113+
114+
///
115+
#[derive(Clone)]
116+
pub struct AsyncFileHistoryJob {
117+
state: Arc<Mutex<Option<JobState>>>,
118+
results: AsyncFileHistoryResults,
119+
}
120+
121+
///
122+
impl AsyncFileHistoryJob {
123+
///
124+
pub fn new(repo_path: RepoPath, file_path: String) -> Self {
125+
Self {
126+
state: Arc::new(Mutex::new(Some(JobState::Request {
127+
repo_path,
128+
file_path,
129+
}))),
130+
results: AsyncFileHistoryResults::default(),
131+
}
132+
}
133+
134+
///
135+
pub fn result(&self) -> Option<Result<CommitFilterResult>> {
136+
if let Ok(mut state) = self.state.lock() {
137+
if let Some(state) = state.take() {
138+
return match state {
139+
JobState::Request { .. } => None,
140+
JobState::Response(result) => Some(result),
141+
};
142+
}
143+
}
144+
145+
None
146+
}
147+
148+
///
149+
pub fn extract_results(&self) -> Result<Vec<FileHistoryEntry>> {
150+
self.results.extract_results()
151+
}
152+
153+
fn file_history_filter(
154+
file_path: Arc<RwLock<String>>,
155+
results: Arc<Mutex<Vec<FileHistoryEntry>>>,
156+
params: &RunParams<
157+
AsyncGitNotification,
158+
AsyncFileHistoryResults,
159+
>,
160+
) -> SharedCommitFilterFn {
161+
let params = params.clone();
162+
163+
Arc::new(Box::new(
164+
move |repo: &Repository,
165+
commit_id: &CommitId|
166+
-> Result<bool> {
167+
let file_path = file_path.clone();
168+
let results = results.clone();
169+
170+
if fun_name(file_path, results, repo, commit_id)? {
171+
params.send(AsyncGitNotification::FileHistory)?;
172+
Ok(true)
173+
} else {
174+
Ok(false)
175+
}
176+
},
177+
))
178+
}
179+
180+
fn run_request(
181+
&self,
182+
repo_path: &RepoPath,
183+
file_path: String,
184+
params: &RunParams<
185+
AsyncGitNotification,
186+
AsyncFileHistoryResults,
187+
>,
188+
) -> Result<CommitFilterResult> {
189+
let start = Instant::now();
190+
191+
let file_name = Arc::new(RwLock::new(file_path));
192+
let result = params.
193+
194+
let filter = Self::file_history_filter(
195+
file_name,
196+
result.clone(),
197+
params,
198+
);
199+
200+
let repo = sync::repo(repo_path)?;
201+
let mut walker =
202+
LogWalker::new(&repo, None)?.filter(Some(filter));
203+
204+
walker.read(None)?;
205+
206+
let result =
207+
std::mem::replace(&mut *result.lock()?, Vec::new());
208+
209+
let result = CommitFilterResult {
210+
duration: start.elapsed(),
211+
result,
212+
};
213+
214+
Ok(result)
215+
}
216+
}
217+
218+
fn fun_name(
219+
file_path: Arc<RwLock<String>>,
220+
results: Arc<Mutex<Vec<FileHistoryEntry>>>,
221+
repo: &Repository,
222+
commit_id: &CommitId,
223+
) -> Result<bool> {
224+
let current_file_path = file_path.read()?.to_string();
225+
226+
if let Some(delta) = commit_contains_file(
227+
repo,
228+
*commit_id,
229+
current_file_path.as_str(),
230+
)? {
231+
log::info!(
232+
"[history] edit: [{}] ({:?}) - {}",
233+
commit_id.get_short_string(),
234+
delta,
235+
&current_file_path
236+
);
237+
238+
let commit_info =
239+
sync::get_commit_info_repo(repo, commit_id)?;
240+
241+
let entry = FileHistoryEntry {
242+
commit: *commit_id,
243+
delta: delta.clone().into(),
244+
info: commit_info,
245+
file_path: current_file_path.clone(),
246+
};
247+
248+
//note: only do rename test in case file looks like being added in this commit
249+
if matches!(delta, git2::Delta::Added) {
250+
let rename = commit_detect_file_rename(
251+
repo,
252+
*commit_id,
253+
current_file_path.as_str(),
254+
)?;
255+
256+
if let Some(old_name) = rename {
257+
// log::info!(
258+
// "rename: [{}] {:?} <- {:?}",
259+
// commit_id.get_short_string(),
260+
// current_file_path,
261+
// old_name,
262+
// );
263+
264+
(*file_path.write()?) = old_name;
265+
}
266+
}
267+
268+
results.lock()?.push(entry);
269+
270+
return Ok(true);
271+
}
272+
273+
Ok(false)
274+
}
275+
276+
impl AsyncJob for AsyncFileHistoryJob {
277+
type Notification = AsyncGitNotification;
278+
type Progress = AsyncFileHistoryResults;
279+
280+
fn run(
281+
&mut self,
282+
params: RunParams<Self::Notification, Self::Progress>,
283+
) -> Result<Self::Notification> {
284+
if let Ok(mut state) = self.state.lock() {
285+
*state = state.take().map(|state| match state {
286+
JobState::Request {
287+
file_path,
288+
repo_path,
289+
} => JobState::Response(
290+
self.run_request(&repo_path, file_path, &params),
291+
),
292+
JobState::Response(result) => {
293+
JobState::Response(result)
294+
}
295+
});
296+
}
297+
298+
Ok(AsyncGitNotification::FileHistory)
299+
}
300+
}

asyncgit/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ mod commit_files;
3838
mod diff;
3939
mod error;
4040
mod fetch_job;
41+
mod file_history;
4142
mod filter_commits;
4243
mod progress;
4344
mod pull;
@@ -58,6 +59,9 @@ pub use crate::{
5859
diff::{AsyncDiff, DiffParams, DiffType},
5960
error::{Error, Result},
6061
fetch_job::AsyncFetchJob,
62+
file_history::{
63+
AsyncFileHistoryJob, FileHistoryEntry, FileHistoryEntryDelta,
64+
},
6165
filter_commits::{AsyncCommitFilterJob, CommitFilterResult},
6266
progress::ProgressPercent,
6367
pull::{AsyncPull, FetchRequest},
@@ -115,6 +119,8 @@ pub enum AsyncGitNotification {
115119
TreeFiles,
116120
///
117121
CommitFilter,
122+
///
123+
FileHistory,
118124
}
119125

120126
/// helper function to calculate the hash of an arbitrary type that implements the `Hash` trait

asyncgit/src/revlog.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
use crossbeam_channel::Sender;
99
use scopetime::scope_time;
1010
use std::{
11+
cell::RefCell,
1112
sync::{
1213
atomic::{AtomicBool, Ordering},
1314
Arc, Mutex,
@@ -201,17 +202,17 @@ impl AsyncLog {
201202
) -> Result<()> {
202203
let start_time = Instant::now();
203204

204-
let mut entries = Vec::with_capacity(LIMIT_COUNT);
205+
let entries = RefCell::new(Vec::with_capacity(LIMIT_COUNT));
205206
let r = repo(repo_path)?;
206207
let mut walker =
207-
LogWalker::new(&r, LIMIT_COUNT)?.filter(filter);
208+
LogWalker::new(&r, Some(LIMIT_COUNT))?.filter(filter);
208209

209210
loop {
210-
entries.clear();
211-
let read = walker.read(&mut entries)?;
211+
entries.borrow_mut().clear();
212+
let read = walker.read(Some(&entries))?;
212213

213214
let mut current = arc_current.lock()?;
214-
current.commits.extend(entries.iter());
215+
current.commits.extend(entries.borrow().iter());
215216
current.duration = start_time.elapsed();
216217

217218
if read == 0 {

asyncgit/src/sync/commit.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,14 @@ mod tests {
134134
};
135135
use commit::{amend, tag_commit};
136136
use git2::Repository;
137+
use std::cell::RefCell;
137138
use std::{fs::File, io::Write, path::Path};
138139

139140
fn count_commits(repo: &Repository, max: usize) -> usize {
140-
let mut items = Vec::new();
141-
let mut walk = LogWalker::new(repo, max).unwrap();
142-
walk.read(&mut items).unwrap();
143-
items.len()
141+
let items = RefCell::new(Vec::new());
142+
let mut walk = LogWalker::new(repo, Some(max)).unwrap();
143+
walk.read(Some(&items)).unwrap();
144+
items.take().len()
144145
}
145146

146147
#[test]

0 commit comments

Comments
 (0)