Skip to content

Commit 904285c

Browse files
holodorumByron
authored andcommitted
Integrate cache to blame with existing algorithm
1 parent 2a63083 commit 904285c

File tree

6 files changed

+72
-14
lines changed

6 files changed

+72
-14
lines changed

Diff for: gitoxide-core/src/repository/blame.rs

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub fn blame_file(
4343
suspect,
4444
cache,
4545
&mut resource_cache,
46+
None,
4647
file.as_bstr(),
4748
range,
4849
)?;

Diff for: gix-blame/src/file/function.rs

+56-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use super::{process_changes, Change, UnblamedHunk};
1+
use super::{process_changes, update_blame_with_changes, Change, UnblamedHunk};
2+
use crate::types::BlameCacheObject;
23
use crate::{BlameEntry, Error, Outcome, Statistics};
34
use gix_diff::blob::intern::TokenSource;
45
use gix_diff::tree::Visit;
56
use gix_hash::ObjectId;
67
use gix_object::{
7-
bstr::{BStr, BString},
8+
bstr::{BStr, BString, ByteSlice},
89
FindExt,
910
};
1011
use gix_traverse::commit::find as find_commit;
@@ -66,17 +67,22 @@ pub fn file(
6667
suspect: ObjectId,
6768
cache: Option<gix_commitgraph::Graph>,
6869
resource_cache: &mut gix_diff::blob::Platform,
70+
blame_cache: Option<BlameCacheObject>,
6971
file_path: &BStr,
7072
range: Option<Range<u32>>,
7173
) -> Result<Outcome, Error> {
7274
let _span = gix_trace::coarse!("gix_blame::file()", ?file_path, ?suspect);
7375

7476
let mut stats = Statistics::default();
7577
let (mut buf, mut buf2, mut buf3) = (Vec::new(), Vec::new(), Vec::new());
76-
let mut file_id = |commit, buf: &mut Vec<u8>, buf2: &mut Vec<u8>| find_path_entry_in_commit(&odb, commit, file_path, cache.as_ref(), buf, buf2, &mut stats)?.ok_or_else(|| Error::FileMissing {
77-
file_path: file_path.to_owned(),
78-
commit_id: suspect,
79-
});
78+
let mut file_id = |commit, buf: &mut Vec<u8>, buf2: &mut Vec<u8>| {
79+
find_path_entry_in_commit(&odb, commit, file_path, cache.as_ref(), buf, buf2, &mut stats)?.ok_or_else(|| {
80+
Error::FileMissing {
81+
file_path: file_path.to_owned(),
82+
commit_id: suspect,
83+
}
84+
})
85+
};
8086
let blamed_file_entry_id = file_id(&suspect, &mut buf, &mut buf2)?;
8187
let blamed_file_blob = odb.find_blob(&blamed_file_entry_id, &mut buf)?.data.to_vec();
8288
let num_lines_in_blamed = tokens_for_diffing(&blamed_file_blob).tokenize().count() as u32;
@@ -87,17 +93,56 @@ pub fn file(
8793
}
8894

8995
let range_in_blamed_file = one_based_inclusive_to_zero_based_exclusive_range(range, num_lines_in_blamed)?;
90-
let mut hunks_to_blame = vec![UnblamedHunk {
91-
range_in_blamed_file: range_in_blamed_file.clone(),
92-
suspects: [(suspect, range_in_blamed_file)].into(),
93-
}];
96+
97+
let (blame_entries, mut hunks_to_blame) = match blame_cache {
98+
Some(blame_cache) => {
99+
// If there is a cache, we first get the diff between the current commit and the commit
100+
// we passed as the cache.
101+
let old_file_id = file_id(&blame_cache.cache_id, &mut buf, &mut buf2)?;
102+
let changes = blob_changes(
103+
&odb,
104+
resource_cache,
105+
blamed_file_entry_id,
106+
old_file_id,
107+
file_path.as_bstr(),
108+
&mut stats,
109+
)?;
110+
111+
// If there are no changes, we can return the cache as is immediately.
112+
if changes.iter().all(|change| matches!(change, Change::Unchanged(_))) {
113+
return Ok(Outcome {
114+
entries: blame_cache.entries.clone(),
115+
blob: blamed_file_blob,
116+
statistics: stats,
117+
});
118+
}
119+
// Otherwise, we update the cache with the new changes.
120+
let (blame_entries, hunks_to_blame) = update_blame_with_changes(blame_cache.entries, changes, suspect);
121+
// If there are no more hunks to blame, we can return the result immediately.
122+
if hunks_to_blame.is_empty() {
123+
return Ok(Outcome {
124+
entries: blame_entries,
125+
blob: blamed_file_blob,
126+
statistics: stats,
127+
});
128+
}
129+
(blame_entries, hunks_to_blame)
130+
}
131+
None => {
132+
let hunks_to_blame = vec![UnblamedHunk {
133+
range_in_blamed_file: range_in_blamed_file.clone(),
134+
suspects: [(suspect, range_in_blamed_file)].into(),
135+
}];
136+
(Vec::new(), hunks_to_blame)
137+
}
138+
};
94139

95140
let (mut buf, mut buf2) = (Vec::new(), Vec::new());
96141
let commit = find_commit(cache.as_ref(), &odb, &suspect, &mut buf)?;
97142
let mut queue: gix_revwalk::PriorityQueue<CommitTime, ObjectId> = gix_revwalk::PriorityQueue::new();
98143
queue.insert(commit_time(commit)?, suspect);
99144

100-
let mut out = Vec::new();
145+
let mut out = blame_entries;
101146
let mut diff_state = gix_diff::tree::State::default();
102147
let mut previous_entry: Option<(ObjectId, ObjectId)> = None;
103148
'outer: while let Some(suspect) = queue.pop_value() {

Diff for: gix-blame/src/file/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ fn process_changes(
357357
new_hunks_to_blame
358358
}
359359

360-
/// Consume `cached_blames` and `changes`. With the changes we update the cached blames.
360+
/// Consume `cached_blames` and `changes`. With the changes we update the cached blames.
361361
/// This function returns the updated blames and the new hunks to blame.
362362
fn update_blame_with_changes(
363363
cached_blames: Vec<BlameEntry>,
@@ -453,7 +453,7 @@ fn update_blame_with_changes(
453453
}
454454
false => {
455455
new_hunks_to_blame.push(new_unblamed_hunk(range, head_id));
456-
456+
457457
blame_assigned
458458
.assigned
459459
.add_assigned(change_assigned.get_remaining(&change));

Diff for: gix-blame/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
mod error;
1818
pub use error::Error;
1919
mod types;
20-
pub use types::{BlameEntry, Outcome, Statistics};
20+
pub use types::{BlameCacheObject, BlameEntry, Outcome, Statistics};
2121

2222
mod file;
2323
pub use file::function::file;

Diff for: gix-blame/src/types.rs

+9
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ impl SubAssign<u32> for Offset {
117117
}
118118
}
119119

120+
#[derive(Debug, PartialEq)]
121+
/// A cache of blame entries that can be used to speed up subsequent blames.
122+
pub struct BlameCacheObject {
123+
/// The entries of the cache.
124+
pub entries: Vec<BlameEntry>,
125+
/// The commit that was blamed to produce these entries.
126+
pub cache_id: ObjectId,
127+
}
128+
120129
/// A mapping of a section of the *Blamed File* to the section in a *Source File* that introduced it.
121130
///
122131
/// Both ranges are of the same size, but may use different [starting points](Range::start). Naturally,

Diff for: gix-blame/tests/blame.rs

+3
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ macro_rules! mktest {
190190
suspect,
191191
None,
192192
&mut resource_cache,
193+
None,
193194
format!("{}.txt", $case).as_str().into(),
194195
None,
195196
)?
@@ -257,6 +258,7 @@ fn diff_disparity() {
257258
suspect,
258259
None,
259260
&mut resource_cache,
261+
None,
260262
format!("{case}.txt").as_str().into(),
261263
None,
262264
)
@@ -285,6 +287,7 @@ fn line_range() {
285287
suspect,
286288
None,
287289
&mut resource_cache,
290+
None,
288291
"simple.txt".into(),
289292
Some(1..2),
290293
)

0 commit comments

Comments
 (0)