Skip to content

Commit 9a7c219

Browse files
authored
make commit filtering an async job (#1842)
1 parent 2675934 commit 9a7c219

File tree

8 files changed

+239
-55
lines changed

8 files changed

+239
-55
lines changed

asyncgit/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ openssl-sys = { version = '0.9', features = ["vendored"], optional = true }
2525
rayon-core = "1.11"
2626
scopetime = { path = "../scopetime", version = "0.1" }
2727
serde = { version = "1.0", features = ["derive"] }
28-
shellexpand = "3.1"
28+
shellexpand = "3.1"
2929
thiserror = "1.0"
3030
unicode-truncate = "0.2.0"
3131
url = "2.4"

asyncgit/src/filter_commits.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use crate::{
2+
asyncjob::{AsyncJob, RunParams},
3+
error::Result,
4+
sync::{self, CommitId, LogWalkerFilter, RepoPath},
5+
AsyncGitNotification, ProgressPercent,
6+
};
7+
use std::{
8+
sync::{Arc, Mutex},
9+
time::{Duration, Instant},
10+
};
11+
12+
///
13+
pub struct CommitFilterResult {
14+
///
15+
pub result: Vec<CommitId>,
16+
///
17+
pub duration: Duration,
18+
}
19+
20+
enum JobState {
21+
Request {
22+
commits: Vec<CommitId>,
23+
repo_path: RepoPath,
24+
},
25+
Response(Result<CommitFilterResult>),
26+
}
27+
28+
///
29+
#[derive(Clone)]
30+
pub struct AsyncCommitFilterJob {
31+
state: Arc<Mutex<Option<JobState>>>,
32+
filter: LogWalkerFilter,
33+
}
34+
35+
///
36+
impl AsyncCommitFilterJob {
37+
///
38+
pub fn new(
39+
repo_path: RepoPath,
40+
commits: Vec<CommitId>,
41+
filter: LogWalkerFilter,
42+
) -> Self {
43+
Self {
44+
state: Arc::new(Mutex::new(Some(JobState::Request {
45+
repo_path,
46+
commits,
47+
}))),
48+
filter,
49+
}
50+
}
51+
52+
///
53+
pub fn result(&self) -> Option<Result<CommitFilterResult>> {
54+
if let Ok(mut state) = self.state.lock() {
55+
if let Some(state) = state.take() {
56+
return match state {
57+
JobState::Request { .. } => None,
58+
JobState::Response(result) => Some(result),
59+
};
60+
}
61+
}
62+
63+
None
64+
}
65+
66+
fn run_request(
67+
&self,
68+
repo_path: &RepoPath,
69+
commits: Vec<CommitId>,
70+
params: &RunParams<AsyncGitNotification, ProgressPercent>,
71+
) -> JobState {
72+
let response = sync::repo(repo_path)
73+
.map(|repo| self.filter_commits(&repo, commits, params))
74+
.map(|(start, result)| CommitFilterResult {
75+
result,
76+
duration: start.elapsed(),
77+
});
78+
79+
JobState::Response(response)
80+
}
81+
82+
fn filter_commits(
83+
&self,
84+
repo: &git2::Repository,
85+
commits: Vec<CommitId>,
86+
params: &RunParams<AsyncGitNotification, ProgressPercent>,
87+
) -> (Instant, Vec<CommitId>) {
88+
let total_amount = commits.len();
89+
let start = Instant::now();
90+
91+
let mut progress = ProgressPercent::new(0, total_amount);
92+
93+
let result = commits
94+
.into_iter()
95+
.enumerate()
96+
.filter_map(|(idx, c)| {
97+
let new_progress =
98+
ProgressPercent::new(idx, total_amount);
99+
100+
if new_progress != progress {
101+
Self::update_progress(params, new_progress);
102+
progress = new_progress;
103+
}
104+
105+
(*self.filter)(repo, &c)
106+
.ok()
107+
.and_then(|res| res.then_some(c))
108+
})
109+
.collect::<Vec<_>>();
110+
111+
(start, result)
112+
}
113+
114+
fn update_progress(
115+
params: &RunParams<AsyncGitNotification, ProgressPercent>,
116+
new_progress: ProgressPercent,
117+
) {
118+
if let Err(e) = params.set_progress(new_progress) {
119+
log::error!("progress error: {e}");
120+
} else if let Err(e) =
121+
params.send(AsyncGitNotification::CommitFilter)
122+
{
123+
log::error!("send error: {e}");
124+
}
125+
}
126+
}
127+
128+
impl AsyncJob for AsyncCommitFilterJob {
129+
type Notification = AsyncGitNotification;
130+
type Progress = ProgressPercent;
131+
132+
fn run(
133+
&mut self,
134+
params: RunParams<Self::Notification, Self::Progress>,
135+
) -> Result<Self::Notification> {
136+
if let Ok(mut state) = self.state.lock() {
137+
*state = state.take().map(|state| match state {
138+
JobState::Request { commits, repo_path } => {
139+
self.run_request(&repo_path, commits, &params)
140+
}
141+
JobState::Response(result) => {
142+
JobState::Response(result)
143+
}
144+
});
145+
}
146+
147+
Ok(AsyncGitNotification::CommitFilter)
148+
}
149+
}

asyncgit/src/lib.rs

Lines changed: 4 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 filter_commits;
4142
mod progress;
4243
mod pull;
4344
mod push;
@@ -57,6 +58,7 @@ pub use crate::{
5758
diff::{AsyncDiff, DiffParams, DiffType},
5859
error::{Error, Result},
5960
fetch_job::AsyncFetchJob,
61+
filter_commits::{AsyncCommitFilterJob, CommitFilterResult},
6062
progress::ProgressPercent,
6163
pull::{AsyncPull, FetchRequest},
6264
push::{AsyncPush, PushRequest},
@@ -111,6 +113,8 @@ pub enum AsyncGitNotification {
111113
Branches,
112114
///
113115
TreeFiles,
116+
///
117+
CommitFilter,
114118
}
115119

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

asyncgit/src/revlog.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ impl AsyncLog {
8181
start_index: usize,
8282
amount: usize,
8383
) -> Result<Vec<CommitId>> {
84+
if self.partial_extract.load(Ordering::Relaxed) {
85+
return Err(Error::Generic(String::from("Faulty usage of AsyncLog: Cannot partially extract items and rely on get_items slice to still work!")));
86+
}
87+
8488
let list = &self.current.lock()?.commits;
8589
let list_len = list.len();
8690
let min = start_index.min(list_len);

asyncgit/src/sync/logwalker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ impl<'a> Ord for TimeOrderedCommit<'a> {
3232
}
3333
}
3434

35+
//TODO: since its used in more than just the log walker now, we should rename and put in its own file
3536
///
3637
pub type LogWalkerFilter = Arc<
3738
Box<dyn Fn(&Repository, &CommitId) -> Result<bool> + Send + Sync>,

src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ impl App {
976976
self.reset_popup.open(id)?;
977977
}
978978
InternalEvent::CommitSearch(options) => {
979-
self.revlog.search(options)?;
979+
self.revlog.search(options);
980980
}
981981
};
982982

src/components/commitlist.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ impl CommitList {
9797
self.items.clear();
9898
}
9999

100+
///
101+
pub fn copy_items(&self) -> Vec<CommitId> {
102+
self.commits.clone()
103+
}
104+
100105
///
101106
pub fn set_tags(&mut self, tags: Tags) {
102107
self.tags = Some(tags);

0 commit comments

Comments
 (0)