Skip to content

Commit 682bc71

Browse files
Use git oid as filter id also internally
This means that persisting filters as tree now does not need to recurse multiple times anymore.
1 parent aa89848 commit 682bc71

File tree

3 files changed

+56
-45
lines changed

3 files changed

+56
-45
lines changed

josh-core/src/cache_notes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl CacheBackend for NotesCacheBackend {
6060
return Ok(None);
6161
}
6262

63-
let key = crate::filter::as_tree(&*repo, filter)?;
63+
let key = filter.id();
6464

6565
if let Ok(note) = repo.find_note(Some(&note_path(key, sequence_number)), from) {
6666
let message = note.message().unwrap_or("");
@@ -88,7 +88,7 @@ impl CacheBackend for NotesCacheBackend {
8888
return Ok(());
8989
}
9090

91-
let key = crate::filter::as_tree(&*repo, filter)?;
91+
let key = filter.id();
9292
let signature = crate::cache::josh_commit_signature()?;
9393

9494
repo.note(

josh-core/src/filter/mod.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ use op::{LazyRef, Op};
1313

1414
pub use persist::as_tree;
1515
pub use persist::from_tree;
16+
pub(crate) use persist::{to_filter, to_op};
1617

1718
pub use opt::invert;
1819
pub use parse::get_comments;
1920
pub use parse::parse;
20-
21-
static FILTERS: LazyLock<std::sync::Mutex<std::collections::HashMap<Filter, Op>>> =
22-
LazyLock::new(|| Default::default());
2321
static WORKSPACES: LazyLock<std::sync::Mutex<std::collections::HashMap<git2::Oid, Filter>>> =
2422
LazyLock::new(|| Default::default());
2523
static ANCESTORS: LazyLock<
@@ -236,27 +234,6 @@ pub fn squash(ids: Option<&[(git2::Oid, Filter)]>) -> Filter {
236234
}
237235
}
238236

239-
fn to_filter(op: Op) -> Filter {
240-
let s = format!("{:?}", op);
241-
let f = Filter(
242-
git2::Oid::hash_object(git2::ObjectType::Blob, s.as_bytes()).expect("hash_object filter"),
243-
);
244-
FILTERS.lock().unwrap().insert(f, op);
245-
f
246-
}
247-
248-
fn to_op(filter: Filter) -> Op {
249-
if filter == sequence_number() {
250-
return Op::Nop;
251-
}
252-
FILTERS
253-
.lock()
254-
.unwrap()
255-
.get(&filter)
256-
.expect("unknown filter")
257-
.clone()
258-
}
259-
260237
/// Pretty print the filter on multiple lines with initial indentation level.
261238
/// Nested filters will be indented with additional 4 spaces per nesting level.
262239
pub fn pretty(filter: Filter, indent: usize) -> String {

josh-core/src/filter/persist.rs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
use gix_object::WriteTo;
22
use gix_object::bstr::BString;
33
use std::collections::HashMap;
4+
use std::sync::LazyLock;
45

5-
use crate::filter::{Filter, LazyRef, Op, to_filter, to_op};
6+
use crate::filter::{Filter, LazyRef, Op, sequence_number};
67
use crate::{JoshResult, josh_error};
78

9+
static FILTERS: LazyLock<std::sync::Mutex<std::collections::HashMap<Filter, Op>>> =
10+
LazyLock::new(|| Default::default());
11+
12+
pub(crate) fn to_op(filter: Filter) -> Op {
13+
if filter == sequence_number() {
14+
return Op::Nop;
15+
}
16+
FILTERS
17+
.lock()
18+
.unwrap()
19+
.get(&filter)
20+
.expect("unknown filter")
21+
.clone()
22+
}
23+
824
fn push_blob_entries(
925
entries: &mut Vec<gix_object::tree::Entry>,
1026
items: impl IntoIterator<Item = (impl AsRef<str>, gix_hash::ObjectId)>,
@@ -79,7 +95,7 @@ impl InMemoryBuilder {
7995
fn build_filter_params(&mut self, params: &[Filter]) -> JoshResult<gix_hash::ObjectId> {
8096
let mut entries = Vec::new();
8197
for (i, filter) in params.iter().enumerate() {
82-
let child = self.build_filter(*filter)?;
98+
let child = gix_hash::ObjectId::from_bytes_or_panic(filter.id().as_bytes());
8399
entries.push(gix_object::tree::Entry {
84100
mode: gix_object::tree::EntryKind::Tree.into(),
85101
filename: BString::from(i.to_string()),
@@ -94,7 +110,7 @@ impl InMemoryBuilder {
94110
let mut outer_entries = Vec::new();
95111
for (i, (key, filter)) in params.iter().enumerate() {
96112
let key_blob = self.write_blob(key.as_bytes());
97-
let filter_tree = self.build_filter(*filter)?;
113+
let filter_tree = gix_hash::ObjectId::from_bytes_or_panic(filter.id().as_bytes());
98114

99115
let inner_entries = vec![
100116
gix_object::tree::Entry {
@@ -156,11 +172,6 @@ impl InMemoryBuilder {
156172
self.write_tree(outer_tree)
157173
}
158174

159-
fn build_filter(&mut self, filter: Filter) -> JoshResult<gix_hash::ObjectId> {
160-
let op = to_op(filter);
161-
self.build_op(&op)
162-
}
163-
164175
fn build_op(&mut self, op: &Op) -> JoshResult<gix_hash::ObjectId> {
165176
let mut entries = Vec::new();
166177

@@ -332,32 +343,55 @@ impl InMemoryBuilder {
332343
}
333344
}
334345

346+
pub(crate) fn to_filter(op: Op) -> Filter {
347+
let mut builder = InMemoryBuilder::new();
348+
let tree_id = builder.build_op(&op).unwrap();
349+
let oid = git2::Oid::from_bytes(tree_id.as_bytes()).unwrap();
350+
351+
let f = Filter(oid);
352+
FILTERS.lock().unwrap().insert(f, op);
353+
f
354+
}
355+
335356
pub fn as_tree(repo: &git2::Repository, filter: crate::filter::Filter) -> JoshResult<git2::Oid> {
357+
let odb = repo.odb()?;
358+
359+
// If the tree exists in the ODB it means all children must already exist as
360+
// well so we can just return it.
361+
if odb.exists(filter.id()) {
362+
return Ok(filter.id());
363+
}
364+
365+
// We don't try to figure out what to write exactly, just write all
366+
// filters we know about to the ODB
367+
let filters = FILTERS.lock().unwrap().clone();
336368
let mut builder = InMemoryBuilder::new();
337-
let tree_id = builder.build_filter(filter)?;
369+
for (f, op) in filters.into_iter() {
370+
if !odb.exists(f.id()) {
371+
builder.build_op(&op)?;
372+
}
373+
}
338374

339375
// Write all pending objects to the git2 repository
340-
let odb = repo.odb()?;
341376
for (oid, (kind, data)) in builder.pending_writes {
342-
let git2_type = match kind {
343-
gix_object::Kind::Tree => git2::ObjectType::Tree,
344-
gix_object::Kind::Blob => git2::ObjectType::Blob,
345-
gix_object::Kind::Commit => git2::ObjectType::Commit,
346-
gix_object::Kind::Tag => git2::ObjectType::Tag,
347-
};
348-
349377
let oid = git2::Oid::from_bytes(oid.as_bytes())?;
350378

351379
// On some platforms, .exists() is cheaper in terms of i/o
352380
// than .write(), because .write() updates file access time
353381
// in loose object backend
354382
if !odb.exists(oid) {
383+
let git2_type = match kind {
384+
gix_object::Kind::Tree => git2::ObjectType::Tree,
385+
gix_object::Kind::Blob => git2::ObjectType::Blob,
386+
gix_object::Kind::Commit => git2::ObjectType::Commit,
387+
gix_object::Kind::Tag => git2::ObjectType::Tag,
388+
};
355389
odb.write(git2_type, &data)?;
356390
}
357391
}
358392

359-
// Return the root tree OID
360-
Ok(git2::Oid::from_bytes(tree_id.as_bytes())?)
393+
// Now the tree should really be in the ODB
394+
Ok(filter.id())
361395
}
362396

363397
pub fn from_tree(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Filter> {

0 commit comments

Comments
 (0)