Skip to content

Commit de6ef67

Browse files
Merge remote-tracking branch 'upstream/master'
2 parents 76577fc + d7aa8c8 commit de6ef67

File tree

15 files changed

+1014
-126
lines changed

15 files changed

+1014
-126
lines changed

Cargo.lock

Lines changed: 794 additions & 110 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ filetreelist = { path = "./filetreelist", version = "0.5" }
3232
fuzzy-matcher = "0.3"
3333
gh-emoji = { version = "1.0", optional = true }
3434
indexmap = "2"
35-
itertools = "0.12"
35+
itertools = "0.13"
3636
log = "0.4"
3737
notify = "6.1"
3838
notify-debouncer-mini = "0.4"
3939
once_cell = "1"
4040
# pin until upgrading this does not introduce a duplicte dependency
4141
parking_lot_core = "=0.9.9"
42-
ratatui = { version = "0.26", default-features = false, features = [
42+
ratatui = { version = "0.27", default-features = false, features = [
4343
'crossterm',
4444
'serde',
4545
] }
@@ -57,7 +57,7 @@ syntect = { version = "5.2", default-features = false, features = [
5757
"default-themes",
5858
"html",
5959
] }
60-
tui-textarea = "0.4.0"
60+
tui-textarea = "0.5"
6161
two-face = { version = "0.4.0", default-features = false }
6262
unicode-segmentation = "1.11"
6363
unicode-truncate = "1.0"

asyncgit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ easy-cast = "0.5"
1919
fuzzy-matcher = "0.3"
2020
git2 = "0.19"
2121
git2-hooks = { path = "../git2-hooks", version = "0.3" }
22+
gix = { version = "0.63", default-features = false, features = ["max-performance", "revision"] }
2223
log = "0.4"
2324
# git2 = { path = "../../extern/git2-rs", features = ["vendored-openssl"]}
2425
# git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]}

asyncgit/src/error.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,24 @@ pub enum Error {
9393
#[error("sign error: {0}")]
9494
Sign(#[from] crate::sync::sign::SignError),
9595

96+
///
97+
#[error("gix::open error: {0}")]
98+
GixOpen(#[from] Box<gix::open::Error>),
99+
100+
///
101+
#[error("gix::reference::find::existing error: {0}")]
102+
GixReferenceFindExisting(
103+
#[from] gix::reference::find::existing::Error,
104+
),
105+
106+
///
107+
#[error("gix::head::peel::to_commit error: {0}")]
108+
GixHeadPeelToCommit(#[from] gix::head::peel::to_commit::Error),
109+
110+
///
111+
#[error("gix::revision::walk error: {0}")]
112+
GixRevisionWalk(#[from] gix::revision::walk::Error),
113+
96114
///
97115
#[error("amend error: config commit.gpgsign=true detected.\ngpg signing is not supported for amending non-last commits")]
98116
SignAmendNonLastCommit,
@@ -120,3 +138,9 @@ impl<T> From<crossbeam_channel::SendError<T>> for Error {
120138
Self::Generic(format!("send error: {error}"))
121139
}
122140
}
141+
142+
impl From<gix::open::Error> for Error {
143+
fn from(error: gix::open::Error) -> Self {
144+
Self::GixOpen(Box::new(error))
145+
}
146+
}

asyncgit/src/revlog.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::{
22
error::Result,
33
sync::{
4-
repo, CommitId, LogWalker, RepoPath, SharedCommitFilterFn,
4+
repo, CommitId, LogWalker, LogWalkerWithoutFilter, RepoPath,
5+
SharedCommitFilterFn,
56
},
67
AsyncGitNotification, Error,
78
};
@@ -198,6 +199,34 @@ impl AsyncLog {
198199
arc_background: &Arc<AtomicBool>,
199200
sender: &Sender<AsyncGitNotification>,
200201
filter: Option<SharedCommitFilterFn>,
202+
) -> Result<()> {
203+
filter.map_or_else(
204+
|| {
205+
Self::fetch_helper_without_filter(
206+
repo_path,
207+
arc_current,
208+
arc_background,
209+
sender,
210+
)
211+
},
212+
|filter| {
213+
Self::fetch_helper_with_filter(
214+
repo_path,
215+
arc_current,
216+
arc_background,
217+
sender,
218+
filter,
219+
)
220+
},
221+
)
222+
}
223+
224+
fn fetch_helper_with_filter(
225+
repo_path: &RepoPath,
226+
arc_current: &Arc<Mutex<AsyncLogResult>>,
227+
arc_background: &Arc<AtomicBool>,
228+
sender: &Sender<AsyncGitNotification>,
229+
filter: SharedCommitFilterFn,
201230
) -> Result<()> {
202231
let start_time = Instant::now();
203232

@@ -206,7 +235,50 @@ impl AsyncLog {
206235

207236
let r = repo(repo_path)?;
208237
let mut walker =
209-
LogWalker::new(&r, LIMIT_COUNT)?.filter(filter);
238+
LogWalker::new(&r, LIMIT_COUNT)?.filter(Some(filter));
239+
240+
loop {
241+
entries.clear();
242+
let read = walker.read(&mut entries)?;
243+
244+
let mut current = arc_current.lock()?;
245+
current.commits.extend(entries.iter());
246+
current.duration = start_time.elapsed();
247+
248+
if read == 0 {
249+
break;
250+
}
251+
Self::notify(sender);
252+
253+
let sleep_duration =
254+
if arc_background.load(Ordering::Relaxed) {
255+
SLEEP_BACKGROUND
256+
} else {
257+
SLEEP_FOREGROUND
258+
};
259+
260+
thread::sleep(sleep_duration);
261+
}
262+
263+
log::trace!("revlog visited: {}", walker.visited());
264+
265+
Ok(())
266+
}
267+
268+
fn fetch_helper_without_filter(
269+
repo_path: &RepoPath,
270+
arc_current: &Arc<Mutex<AsyncLogResult>>,
271+
arc_background: &Arc<AtomicBool>,
272+
sender: &Sender<AsyncGitNotification>,
273+
) -> Result<()> {
274+
let start_time = Instant::now();
275+
276+
let mut entries = vec![CommitId::default(); LIMIT_COUNT];
277+
entries.resize(0, CommitId::default());
278+
279+
let mut repo = gix::open(repo_path.gitpath())?;
280+
let mut walker =
281+
LogWalkerWithoutFilter::new(&mut repo, LIMIT_COUNT)?;
210282

211283
loop {
212284
entries.clear();

asyncgit/src/sync/logwalker.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use super::{CommitId, SharedCommitFilterFn};
33
use crate::error::Result;
44
use git2::{Commit, Oid, Repository};
5+
use gix::{revision::Walk, traverse::commit::simple::Sorting};
56
use std::{
67
cmp::Ordering,
78
collections::{BinaryHeap, HashSet},
@@ -108,6 +109,78 @@ impl<'a> LogWalker<'a> {
108109
}
109110
}
110111

112+
/// This is separate from `LogWalker` because filtering currently (June 2024) works through
113+
/// `SharedCommitFilterFn`.
114+
///
115+
/// `SharedCommitFilterFn` requires access to a `git2::repo::Repository` because, under the hood,
116+
/// it calls into functions that work with a `git2::repo::Repository`. It seems unwise to open a
117+
/// repo both through `gix::open` and `Repository::open_ext` at the same time, so there is a
118+
/// separate struct that works with `gix::Repository` only.
119+
///
120+
/// A more long-term option is to refactor filtering to work with a `gix::Repository` and to remove
121+
/// `LogWalker` once this is done, but this is a larger effort.
122+
pub struct LogWalkerWithoutFilter<'a> {
123+
walk: Walk<'a>,
124+
limit: usize,
125+
visited: usize,
126+
}
127+
128+
impl<'a> LogWalkerWithoutFilter<'a> {
129+
///
130+
pub fn new(
131+
repo: &'a mut gix::Repository,
132+
limit: usize,
133+
) -> Result<Self> {
134+
// This seems to be an object cache size that yields optimal performance. There’s no specific
135+
// reason this is 2^14, so benchmarking might reveal that there’s better values.
136+
repo.object_cache_size_if_unset(2_usize.pow(14));
137+
138+
let commit = repo.head()?.peel_to_commit_in_place()?;
139+
140+
let tips = [commit.id];
141+
142+
let platform = repo
143+
.rev_walk(tips)
144+
.sorting(Sorting::ByCommitTimeNewestFirst)
145+
.use_commit_graph(false);
146+
147+
let walk = platform.all()?;
148+
149+
Ok(Self {
150+
walk,
151+
limit,
152+
visited: 0,
153+
})
154+
}
155+
156+
///
157+
pub const fn visited(&self) -> usize {
158+
self.visited
159+
}
160+
161+
///
162+
pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
163+
let mut count = 0_usize;
164+
165+
while let Some(Ok(info)) = self.walk.next() {
166+
let bytes = info.id.as_bytes();
167+
let commit_id: CommitId = Oid::from_bytes(bytes)?.into();
168+
169+
out.push(commit_id);
170+
171+
count += 1;
172+
173+
if count == self.limit {
174+
break;
175+
}
176+
}
177+
178+
self.visited += count;
179+
180+
Ok(count)
181+
}
182+
}
183+
111184
#[cfg(test)]
112185
mod tests {
113186
use super::*;
@@ -183,6 +256,40 @@ mod tests {
183256
Ok(())
184257
}
185258

259+
#[test]
260+
fn test_logwalker_without_filter() -> Result<()> {
261+
let file_path = Path::new("foo");
262+
let (_td, repo) = repo_init_empty().unwrap();
263+
let root = repo.path().parent().unwrap();
264+
let repo_path: &RepoPath =
265+
&root.as_os_str().to_str().unwrap().into();
266+
267+
File::create(root.join(file_path))?.write_all(b"a")?;
268+
stage_add_file(repo_path, file_path).unwrap();
269+
commit(repo_path, "commit1").unwrap();
270+
File::create(root.join(file_path))?.write_all(b"a")?;
271+
stage_add_file(repo_path, file_path).unwrap();
272+
let oid2 = commit(repo_path, "commit2").unwrap();
273+
274+
let mut repo = gix::open(repo_path.gitpath()).unwrap();
275+
let mut walk = LogWalkerWithoutFilter::new(&mut repo, 100)?;
276+
let mut items = Vec::new();
277+
assert!(matches!(walk.read(&mut items), Ok(2)));
278+
279+
let info = get_commits_info(repo_path, &items, 50).unwrap();
280+
dbg!(&info);
281+
282+
assert_eq!(items.len(), 2);
283+
assert_eq!(items[0], oid2);
284+
285+
let mut items = Vec::new();
286+
assert!(matches!(walk.read(&mut items), Ok(0)));
287+
288+
assert_eq!(items.len(), 0);
289+
290+
Ok(())
291+
}
292+
186293
#[test]
187294
fn test_logwalker_with_filter() -> Result<()> {
188295
let file_path = Path::new("foo");

asyncgit/src/sync/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub use hooks::{
7171
};
7272
pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
7373
pub use ignore::add_to_ignore;
74-
pub use logwalker::LogWalker;
74+
pub use logwalker::{LogWalker, LogWalkerWithoutFilter};
7575
pub use merge::{
7676
abort_pending_rebase, abort_pending_state,
7777
continue_pending_rebase, merge_branch, merge_commit, merge_msg,

src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,7 @@ impl App {
11041104
const SIDE_PADS: usize = 2;
11051105
const MARGIN_LEFT_AND_RIGHT: usize = 2;
11061106

1107-
let r = r.inner(&Margin {
1107+
let r = r.inner(Margin {
11081108
vertical: 0,
11091109
horizontal: 1,
11101110
});

src/popups/branchlist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl DrawableComponent for BranchListPopup {
8080
area,
8181
);
8282

83-
let area = area.inner(&Margin {
83+
let area = area.inner(Margin {
8484
vertical: 1,
8585
horizontal: 1,
8686
});

src/popups/fuzzy_find.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ impl DrawableComponent for FuzzyFindPopup {
293293
]
294294
.as_ref(),
295295
)
296-
.split(area.inner(&Margin {
296+
.split(area.inner(Margin {
297297
horizontal: 1,
298298
vertical: 1,
299299
}));

0 commit comments

Comments
 (0)