Skip to content

Commit a89d2a4

Browse files
committed
Skip uninteresting commits for blame
1 parent c2d1a5d commit a89d2a4

File tree

4 files changed

+120
-10
lines changed

4 files changed

+120
-10
lines changed

Cargo.lock

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

gix-blame/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ rust-version = "1.70"
1414
doctest = false
1515

1616
[dependencies]
17+
gix-commitgraph = { version = "^0.25.1", path = "../gix-commitgraph" }
18+
gix-revwalk = { version = "^0.17.0", path = "../gix-revwalk" }
1719
gix-trace = { version = "^0.1.11", path = "../gix-trace" }
1820
gix-diff = { version = "^0.49.0", path = "../gix-diff", default-features = false, features = ["blob"] }
1921
gix-object = { version = "^0.46.0", path = "../gix-object" }
2022
gix-hash = { version = "^0.15.0", path = "../gix-hash" }
2123
gix-worktree = { version = "^0.38.0", path = "../gix-worktree", default-features = false, features = ["attributes"] }
2224
gix-traverse = { version = "^0.43.0", path = "../gix-traverse" }
2325

26+
smallvec = "1.10.0"
2427
thiserror = "2.0.0"
2528

2629
[dev-dependencies]

gix-blame/src/file/function.rs

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use gix_object::{
77
bstr::{BStr, BString},
88
FindExt,
99
};
10+
use gix_traverse::commit::find;
11+
use smallvec::SmallVec;
12+
use std::collections::HashSet;
1013
use std::num::NonZeroU32;
1114
use std::ops::Range;
1215

@@ -103,20 +106,115 @@ where
103106
suspects: [(suspect, range_in_blamed_file)].into(),
104107
}];
105108

109+
// TODO
110+
// Get `cache` as an argument to `file`.
111+
let cache: Option<gix_commitgraph::Graph> = None;
112+
113+
let mut buf = Vec::new();
114+
let commit = find(cache.as_ref(), &odb, &suspect, &mut buf)?;
115+
116+
// TODO
117+
// This is a simplified version of `GenAndCommitTime` in
118+
// `gix-traverse/src/commit/topo/iter.rs`. There, generation is also part of the key. It’s
119+
// possible we need generation, but I have too little context to know.
120+
type CommitTime = i64;
121+
122+
let mut queue: gix_revwalk::PriorityQueue<CommitTime, ObjectId> = gix_revwalk::PriorityQueue::new();
123+
let mut seen: HashSet<ObjectId> = HashSet::new();
124+
125+
// TODO
126+
// This is a simplified version of `gen_and_commit_time` in
127+
// `gix-traverse/src/commit/topo/iter.rs`. It can probably be extracted.
128+
let commit_time = match commit {
129+
gix_traverse::commit::Either::CommitRefIter(commit_ref_iter) => {
130+
let mut commit_time = 0;
131+
for token in commit_ref_iter {
132+
use gix_object::commit::ref_iter::Token as T;
133+
match token {
134+
Ok(T::Tree { .. }) => continue,
135+
Ok(T::Parent { .. }) => continue,
136+
Ok(T::Author { .. }) => continue,
137+
Ok(T::Committer { signature }) => {
138+
commit_time = signature.time.seconds;
139+
break;
140+
}
141+
Ok(_unused_token) => break,
142+
Err(_err) => todo!(),
143+
}
144+
}
145+
commit_time
146+
}
147+
gix_traverse::commit::Either::CachedCommit(commit) => commit.committer_timestamp() as i64,
148+
};
149+
150+
queue.insert(commit_time, suspect);
151+
106152
let mut out = Vec::new();
107153
let mut diff_state = gix_diff::tree::State::default();
108154
let mut previous_entry: Option<(ObjectId, ObjectId)> = None;
109-
'outer: while let Some(item) = traverse.next() {
155+
'outer: while let Some(suspect) = queue.pop_value() {
110156
if hunks_to_blame.is_empty() {
111157
break;
112158
}
113-
let commit = item.map_err(|err| Error::Traverse(err.into()))?;
114-
let suspect = commit.id;
159+
160+
let was_inserted = seen.insert(suspect);
161+
162+
if !was_inserted {
163+
// We have already visited `suspect` and can continue with the next one.
164+
continue 'outer;
165+
}
166+
115167
stats.commits_traversed += 1;
116168

117-
let parent_ids = commit.parent_ids;
169+
let commit = find(cache.as_ref(), &odb, &suspect, &mut buf)?;
170+
171+
type ParentIds = SmallVec<[(gix_hash::ObjectId, i64); 2]>;
172+
let mut parent_ids: ParentIds = Default::default();
173+
174+
// TODO
175+
// This is a simplified version of `collect_parents` in
176+
// `gix-traverse/src/commit/topo/iter.rs`. It can probably be extracted.
177+
match commit {
178+
gix_traverse::commit::Either::CachedCommit(commit) => {
179+
let cache = cache
180+
.as_ref()
181+
.expect("find returned a cached commit, so we expect cache to be present");
182+
for parent_id in commit.iter_parents() {
183+
match parent_id {
184+
Ok(pos) => {
185+
let parent = cache.commit_at(pos);
186+
187+
parent_ids.push((parent.id().to_owned(), parent.committer_timestamp() as i64));
188+
}
189+
Err(_) => todo!(),
190+
}
191+
}
192+
}
193+
gix_traverse::commit::Either::CommitRefIter(commit_ref_iter) => {
194+
for token in commit_ref_iter {
195+
match token {
196+
Ok(gix_object::commit::ref_iter::Token::Tree { .. }) => continue,
197+
Ok(gix_object::commit::ref_iter::Token::Parent { id }) => {
198+
let mut buf = Vec::new();
199+
let parent = odb.find_commit_iter(id.as_ref(), &mut buf).ok();
200+
let parent_commit_time = parent
201+
.and_then(|parent| parent.committer().ok().map(|committer| committer.time.seconds))
202+
.unwrap_or_default();
203+
204+
parent_ids.push((id, parent_commit_time));
205+
}
206+
Ok(_unused_token) => break,
207+
Err(_err) => todo!(),
208+
}
209+
}
210+
}
211+
};
212+
118213
if parent_ids.is_empty() {
119-
if traverse.peek().is_none() {
214+
if queue.is_empty() {
215+
// TODO
216+
// Adapt comment. Also all other comments that mention `traverse`.
217+
//
120218
// I’m not entirely sure if this is correct yet. `suspect`, at this point, is the `id` of
121219
// the last `item` that was yielded by `traverse`, so it makes sense to assign the
122220
// remaining lines to it, even though we don’t explicitly check whether that is true
@@ -143,7 +241,7 @@ where
143241
continue;
144242
};
145243

146-
for (pid, parent_id) in parent_ids.iter().enumerate() {
244+
for (pid, (parent_id, parent_commit_time)) in parent_ids.iter().enumerate() {
147245
if let Some(parent_entry_id) =
148246
find_path_entry_in_commit(&odb, parent_id, file_path, &mut buf, &mut buf2, &mut stats)?
149247
{
@@ -153,17 +251,19 @@ where
153251
}
154252
if no_change_in_entry {
155253
pass_blame_from_to(suspect, *parent_id, &mut hunks_to_blame);
254+
queue.insert(*parent_commit_time, *parent_id);
156255
continue 'outer;
157256
}
158257
}
159258
}
160259

161260
let more_than_one_parent = parent_ids.len() > 1;
162-
for parent_id in parent_ids {
261+
for (parent_id, parent_commit_time) in parent_ids {
262+
queue.insert(parent_commit_time, parent_id);
163263
let changes_for_file_path = tree_diff_at_file_path(
164264
&odb,
165265
file_path,
166-
commit.id,
266+
suspect,
167267
parent_id,
168268
&mut stats,
169269
&mut diff_state,

gix-traverse/src/commit/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,16 @@ pub struct Info {
6767
pub commit_time: Option<gix_date::SecondsSinceUnixEpoch>,
6868
}
6969

70-
enum Either<'buf, 'cache> {
70+
/// TODO
71+
pub enum Either<'buf, 'cache> {
72+
/// TODO
7173
CommitRefIter(gix_object::CommitRefIter<'buf>),
74+
/// TODO
7275
CachedCommit(gix_commitgraph::file::Commit<'cache>),
7376
}
7477

75-
fn find<'cache, 'buf, Find>(
78+
/// TODO
79+
pub fn find<'cache, 'buf, Find>(
7680
cache: Option<&'cache gix_commitgraph::Graph>,
7781
objects: Find,
7882
id: &gix_hash::oid,

0 commit comments

Comments
 (0)