Skip to content

Commit fb4750f

Browse files
committed
fix: Use revwalk to determine consecutive range
1 parent 8de1bf9 commit fb4750f

File tree

3 files changed

+87
-13
lines changed

3 files changed

+87
-13
lines changed

asyncgit/src/sync/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod rebase;
2424
pub mod remotes;
2525
mod repository;
2626
mod reset;
27+
mod revwalk;
2728
mod reword;
2829
pub mod sign;
2930
mod staging;
@@ -65,6 +66,7 @@ pub use config::{
6566
};
6667
pub use diff::get_diff_commit;
6768
pub use git2::BranchType;
69+
pub use git2::Sort;
6870
pub use hooks::{
6971
hooks_commit_msg, hooks_post_commit, hooks_pre_commit,
7072
hooks_prepare_commit_msg, HookResult, PrepareCommitMsgSource,
@@ -87,6 +89,7 @@ pub use remotes::{
8789
pub(crate) use repository::repo;
8890
pub use repository::{RepoPath, RepoPathRef};
8991
pub use reset::{reset_repo, reset_stage, reset_workdir};
92+
pub use revwalk::revwalk;
9093
pub use reword::reword;
9194
pub use staging::{discard_lines, stage_lines};
9295
pub use stash::{

asyncgit/src/sync/revwalk.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use std::ops::Bound;
2+
3+
use crate::Result;
4+
use git2::{Commit, Oid};
5+
6+
use super::{repo, CommitId, RepoPath};
7+
8+
/// Performs a Git revision walk on `repo_path`, optionally bounded by `start` and `end` commits,
9+
/// sorted according to `sort`. The revwalk iterator bound by repository's lifetime is exposed through
10+
/// the `iter_fn`.
11+
///
12+
///
13+
pub fn revwalk<R>(
14+
repo_path: &RepoPath,
15+
start: Bound<&CommitId>,
16+
end: Bound<&CommitId>,
17+
sort: git2::Sort,
18+
iter_fn: impl FnOnce(
19+
&mut (dyn Iterator<Item = Result<Oid>>),
20+
) -> Result<R>,
21+
) -> Result<R> {
22+
let repo = repo(repo_path)?;
23+
let mut revwalk = repo.revwalk()?;
24+
revwalk.set_sorting(sort)?;
25+
let start = resolve(&repo, start)?;
26+
let end = resolve(&repo, end)?;
27+
28+
if let Some(s) = start {
29+
revwalk.hide(s.id())?;
30+
}
31+
if let Some(e) = end {
32+
revwalk.push(e.id())?;
33+
}
34+
let ret = iter_fn(&mut revwalk.map(|r| {
35+
r.map_err(|x| crate::Error::Generic(x.to_string()))
36+
}));
37+
ret
38+
}
39+
40+
fn resolve<'r>(
41+
repo: &'r git2::Repository,
42+
commit: Bound<&CommitId>,
43+
) -> Result<Option<Commit<'r>>> {
44+
match commit {
45+
Bound::Included(c) => {
46+
let commit = repo.find_commit(c.get_oid())?;
47+
Ok(Some(commit))
48+
}
49+
Bound::Excluded(s) => {
50+
let commit = repo.find_commit(s.get_oid())?;
51+
let res = (commit.parent_count() == 1)
52+
.then(|| commit.parent(0))
53+
.transpose()?;
54+
Ok(res)
55+
}
56+
Bound::Unbounded => Ok(None),
57+
}
58+
}

src/components/commitlist.rs

+26-13
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use crate::{
1414
};
1515
use anyhow::Result;
1616
use asyncgit::sync::{
17-
self, checkout_commit, BranchDetails, BranchInfo, CommitId,
18-
RepoPathRef, Tags,
17+
self, checkout_commit, revwalk, BranchDetails, BranchInfo,
18+
CommitId, RepoPathRef, Sort, Tags,
1919
};
2020
use chrono::{DateTime, Local};
2121
use crossterm::event::Event;
@@ -29,8 +29,8 @@ use ratatui::{
2929
Frame,
3030
};
3131
use std::{
32-
borrow::Cow, cell::Cell, cmp, collections::BTreeMap, rc::Rc,
33-
time::Instant,
32+
borrow::Cow, cell::Cell, cmp, collections::BTreeMap, ops::Bound,
33+
rc::Rc, time::Instant,
3434
};
3535

3636
const ELEMENTS_PER_LINE: usize = 9;
@@ -134,9 +134,7 @@ impl CommitList {
134134

135135
///
136136
pub fn copy_commit_hash(&self) -> Result<()> {
137-
let marked = self.marked.iter().rev().cloned().collect_vec();
138-
let marked = marked.as_slice();
139-
137+
let marked = self.marked.as_slice();
140138
let yank: Option<String> = match marked {
141139
[] => self
142140
.items
@@ -147,12 +145,27 @@ impl CommitList {
147145
)
148146
.map(|e| e.id.to_string()),
149147
[(_idx, commit)] => Some(commit.to_string()),
150-
[first, .., last] => {
151-
let marked_consecutive =
152-
marked.windows(2).all(|w| w[0].0 - 1 == w[1].0);
153-
154-
let yank = if marked_consecutive {
155-
format!("{}^..{}", first.1, last.1)
148+
[latest, .., earliest] => {
149+
let marked_rev = marked.iter().rev();
150+
let marked_topo_consecutive = revwalk(
151+
&self.repo.borrow(),
152+
Bound::Excluded(&earliest.1),
153+
Bound::Included(&latest.1),
154+
Sort::TOPOLOGICAL | Sort::REVERSE,
155+
|revwalk| {
156+
revwalk.zip(marked_rev).try_fold(
157+
true,
158+
|acc, (r, m)| {
159+
let revwalked = CommitId::new(r?);
160+
let marked = m.1;
161+
log::trace!("comparing revwalk with marked: {} <-> {}",revwalked,marked);
162+
Ok(acc && (revwalked == marked))
163+
},
164+
)
165+
},
166+
)?;
167+
let yank = if marked_topo_consecutive {
168+
format!("{}^..{}", earliest.1, latest.1)
156169
} else {
157170
marked
158171
.iter()

0 commit comments

Comments
 (0)