Skip to content

Commit 15e9222

Browse files
authored
refactoring commit filter (#1843)
1 parent 86c4f7f commit 15e9222

File tree

5 files changed

+238
-225
lines changed

5 files changed

+238
-225
lines changed

asyncgit/src/filter_commits.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
asyncjob::{AsyncJob, RunParams},
33
error::Result,
4-
sync::{self, CommitId, LogWalkerFilter, RepoPath},
4+
sync::{self, CommitId, RepoPath, SharedCommitFilterFn},
55
AsyncGitNotification, ProgressPercent,
66
};
77
use std::{
@@ -29,7 +29,7 @@ enum JobState {
2929
#[derive(Clone)]
3030
pub struct AsyncCommitFilterJob {
3131
state: Arc<Mutex<Option<JobState>>>,
32-
filter: LogWalkerFilter,
32+
filter: SharedCommitFilterFn,
3333
}
3434

3535
///
@@ -38,7 +38,7 @@ impl AsyncCommitFilterJob {
3838
pub fn new(
3939
repo_path: RepoPath,
4040
commits: Vec<CommitId>,
41-
filter: LogWalkerFilter,
41+
filter: SharedCommitFilterFn,
4242
) -> Self {
4343
Self {
4444
state: Arc::new(Mutex::new(Some(JobState::Request {

asyncgit/src/revlog.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::{
22
error::Result,
3-
sync::{repo, CommitId, LogWalker, LogWalkerFilter, RepoPath},
3+
sync::{
4+
repo, CommitId, LogWalker, RepoPath, SharedCommitFilterFn,
5+
},
46
AsyncGitNotification, Error,
57
};
68
use crossbeam_channel::Sender;
@@ -39,7 +41,7 @@ pub struct AsyncLog {
3941
sender: Sender<AsyncGitNotification>,
4042
pending: Arc<AtomicBool>,
4143
background: Arc<AtomicBool>,
42-
filter: Option<LogWalkerFilter>,
44+
filter: Option<SharedCommitFilterFn>,
4345
partial_extract: AtomicBool,
4446
repo: RepoPath,
4547
}
@@ -53,7 +55,7 @@ impl AsyncLog {
5355
pub fn new(
5456
repo: RepoPath,
5557
sender: &Sender<AsyncGitNotification>,
56-
filter: Option<LogWalkerFilter>,
58+
filter: Option<SharedCommitFilterFn>,
5759
) -> Self {
5860
Self {
5961
repo,
@@ -195,7 +197,7 @@ impl AsyncLog {
195197
arc_current: &Arc<Mutex<AsyncLogResult>>,
196198
arc_background: &Arc<AtomicBool>,
197199
sender: &Sender<AsyncGitNotification>,
198-
filter: Option<LogWalkerFilter>,
200+
filter: Option<SharedCommitFilterFn>,
199201
) -> Result<()> {
200202
let start_time = Instant::now();
201203

asyncgit/src/sync/commit_filter.rs

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
use super::{commit_files::get_commit_diff, CommitId};
2+
use crate::error::Result;
3+
use bitflags::bitflags;
4+
use fuzzy_matcher::FuzzyMatcher;
5+
use git2::{Diff, Repository};
6+
use std::sync::Arc;
7+
8+
///
9+
pub type SharedCommitFilterFn = Arc<
10+
Box<dyn Fn(&Repository, &CommitId) -> Result<bool> + Send + Sync>,
11+
>;
12+
13+
///
14+
pub fn diff_contains_file(file_path: String) -> SharedCommitFilterFn {
15+
Arc::new(Box::new(
16+
move |repo: &Repository,
17+
commit_id: &CommitId|
18+
-> Result<bool> {
19+
let diff = get_commit_diff(
20+
repo,
21+
*commit_id,
22+
Some(file_path.clone()),
23+
None,
24+
None,
25+
)?;
26+
27+
let contains_file = diff.deltas().len() > 0;
28+
29+
Ok(contains_file)
30+
},
31+
))
32+
}
33+
34+
bitflags! {
35+
///
36+
pub struct SearchFields: u32 {
37+
///
38+
const MESSAGE = 1 << 0;
39+
///
40+
const FILENAMES = 1 << 1;
41+
///
42+
const AUTHORS = 1 << 2;
43+
//TODO:
44+
// const COMMIT_HASHES = 1 << 3;
45+
// ///
46+
// const DATES = 1 << 4;
47+
// ///
48+
// const DIFFS = 1 << 5;
49+
}
50+
}
51+
52+
impl Default for SearchFields {
53+
fn default() -> Self {
54+
Self::MESSAGE
55+
}
56+
}
57+
58+
bitflags! {
59+
///
60+
pub struct SearchOptions: u32 {
61+
///
62+
const CASE_SENSITIVE = 1 << 0;
63+
///
64+
const FUZZY_SEARCH = 1 << 1;
65+
}
66+
}
67+
68+
impl Default for SearchOptions {
69+
fn default() -> Self {
70+
Self::empty()
71+
}
72+
}
73+
74+
///
75+
#[derive(Default, Debug, Clone)]
76+
pub struct LogFilterSearchOptions {
77+
///
78+
pub search_pattern: String,
79+
///
80+
pub fields: SearchFields,
81+
///
82+
pub options: SearchOptions,
83+
}
84+
85+
///
86+
#[derive(Default)]
87+
pub struct LogFilterSearch {
88+
///
89+
pub matcher: fuzzy_matcher::skim::SkimMatcherV2,
90+
///
91+
pub options: LogFilterSearchOptions,
92+
}
93+
94+
impl LogFilterSearch {
95+
///
96+
pub fn new(options: LogFilterSearchOptions) -> Self {
97+
let mut options = options;
98+
if !options.options.contains(SearchOptions::CASE_SENSITIVE) {
99+
options.search_pattern =
100+
options.search_pattern.to_lowercase();
101+
}
102+
Self {
103+
matcher: fuzzy_matcher::skim::SkimMatcherV2::default(),
104+
options,
105+
}
106+
}
107+
108+
fn match_diff(&self, diff: &Diff<'_>) -> bool {
109+
diff.deltas().any(|delta| {
110+
if delta
111+
.new_file()
112+
.path()
113+
.and_then(|file| file.as_os_str().to_str())
114+
.map(|file| self.match_text(file))
115+
.unwrap_or_default()
116+
{
117+
return true;
118+
}
119+
120+
delta
121+
.old_file()
122+
.path()
123+
.and_then(|file| file.as_os_str().to_str())
124+
.map(|file| self.match_text(file))
125+
.unwrap_or_default()
126+
})
127+
}
128+
129+
///
130+
pub fn match_text(&self, text: &str) -> bool {
131+
if self.options.options.contains(SearchOptions::FUZZY_SEARCH)
132+
{
133+
self.matcher
134+
.fuzzy_match(
135+
text,
136+
self.options.search_pattern.as_str(),
137+
)
138+
.is_some()
139+
} else if self
140+
.options
141+
.options
142+
.contains(SearchOptions::CASE_SENSITIVE)
143+
{
144+
text.contains(self.options.search_pattern.as_str())
145+
} else {
146+
text.to_lowercase()
147+
.contains(self.options.search_pattern.as_str())
148+
}
149+
}
150+
}
151+
152+
///
153+
pub fn filter_commit_by_search(
154+
filter: LogFilterSearch,
155+
) -> SharedCommitFilterFn {
156+
Arc::new(Box::new(
157+
move |repo: &Repository,
158+
commit_id: &CommitId|
159+
-> Result<bool> {
160+
let commit = repo.find_commit((*commit_id).into())?;
161+
162+
let msg_match = filter
163+
.options
164+
.fields
165+
.contains(SearchFields::MESSAGE)
166+
.then(|| {
167+
commit.message().map(|msg| filter.match_text(msg))
168+
})
169+
.flatten()
170+
.unwrap_or_default();
171+
172+
let file_match = filter
173+
.options
174+
.fields
175+
.contains(SearchFields::FILENAMES)
176+
.then(|| {
177+
get_commit_diff(
178+
repo, *commit_id, None, None, None,
179+
)
180+
.ok()
181+
})
182+
.flatten()
183+
.map(|diff| filter.match_diff(&diff))
184+
.unwrap_or_default();
185+
186+
let authors_match = filter
187+
.options
188+
.fields
189+
.contains(SearchFields::AUTHORS)
190+
.then(|| {
191+
let name_match = commit
192+
.author()
193+
.name()
194+
.map(|name| filter.match_text(name))
195+
.unwrap_or_default();
196+
let mail_match = commit
197+
.author()
198+
.email()
199+
.map(|name| filter.match_text(name))
200+
.unwrap_or_default();
201+
202+
name_match || mail_match
203+
})
204+
.unwrap_or_default();
205+
206+
Ok(msg_match || file_match || authors_match)
207+
},
208+
))
209+
}

0 commit comments

Comments
 (0)