Skip to content

Find for gix-object #1088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gitoxide-core/src/hours/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ where
let mut skipped_merge_commits = 0;
const CHUNK_SIZE: usize = 50;
let mut chunk = Vec::with_capacity(CHUNK_SIZE);
let mut commit_iter = commit_id.ancestors(|oid, buf| repo.objects.find_commit_iter(oid, buf));
let mut commit_iter = commit_id.ancestors(&repo.objects);
let mut is_shallow = false;
while let Some(c) = commit_iter.next() {
progress.inc();
Expand Down Expand Up @@ -175,7 +175,7 @@ where
}
commit_idx += 1;
}
Err(gix::traverse::commit::ancestors::Error::FindExisting { .. }) => {
Err(gix::traverse::commit::ancestors::Error::Find { .. }) => {
is_shallow = true;
break;
}
Expand Down
63 changes: 44 additions & 19 deletions gitoxide-core/src/index/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::{
};

use anyhow::bail;
use gix::{odb::FindExt, worktree::state::checkout, NestedProgress, Progress};
use gix::objs::find::Error;
use gix::{worktree::state::checkout, NestedProgress, Progress};

use crate::{
index,
Expand Down Expand Up @@ -89,20 +90,9 @@ pub fn checkout_exclusive(
Some(repo) => gix::worktree::state::checkout(
&mut index,
dest_directory,
{
let objects = repo.objects.into_arc()?;
move |oid, buf| {
objects.find_blob(oid, buf).ok();
if empty_files {
// We always want to query the ODB here…
objects.find_blob(oid, buf)?;
buf.clear();
// …but write nothing
Ok(gix::objs::BlobRef { data: buf })
} else {
objects.find_blob(oid, buf)
}
}
EmptyOrDb {
empty_files,
db: repo.objects.into_arc()?,
},
&files,
&bytes,
Expand All @@ -112,10 +102,7 @@ pub fn checkout_exclusive(
None => gix::worktree::state::checkout(
&mut index,
dest_directory,
|_, buf| {
buf.clear();
Ok(gix::objs::BlobRef { data: buf })
},
Empty,
&files,
&bytes,
should_interrupt,
Expand Down Expand Up @@ -184,3 +171,41 @@ pub fn checkout_exclusive(
}
Ok(())
}

#[derive(Clone)]
struct EmptyOrDb<Find> {
empty_files: bool,
db: Find,
}

impl<Find> gix::objs::Find for EmptyOrDb<Find>
where
Find: gix::objs::Find,
{
fn try_find<'a>(&self, id: &gix::oid, buf: &'a mut Vec<u8>) -> Result<Option<gix::objs::Data<'a>>, Error> {
if self.empty_files {
// We always want to query the ODB here…
let Some(kind) = self.db.try_find(id, buf)?.map(|d| d.kind) else {
return Ok(None);
};
buf.clear();
// …but write nothing
Ok(Some(gix::objs::Data { kind, data: buf }))
} else {
self.db.try_find(id, buf)
}
}
}

#[derive(Clone)]
struct Empty;

impl gix::objs::Find for Empty {
fn try_find<'a>(&self, _id: &gix::oid, buffer: &'a mut Vec<u8>) -> Result<Option<gix::objs::Data<'a>>, Error> {
buffer.clear();
Ok(Some(gix::objs::Data {
kind: gix::object::Kind::Blob,
data: buffer,
}))
}
}
2 changes: 1 addition & 1 deletion gitoxide-core/src/index/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn verify(
let file = parse_file(index_path, object_hash)?;
file.verify_integrity()?;
file.verify_entries()?;
file.verify_extensions(false, gix::index::verify::extensions::no_find)?;
file.verify_extensions(false, gix::objs::find::Never)?;
#[cfg_attr(not(feature = "serde"), allow(irrefutable_let_patterns))]
if let crate::OutputFormat::Human = format {
writeln!(out, "OK").ok();
Expand Down
17 changes: 4 additions & 13 deletions gitoxide-core/src/pack/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@ use std::{ffi::OsStr, io, path::Path, str::FromStr, time::Instant};

use anyhow::anyhow;
use gix::{
hash,
hash::ObjectId,
interrupt,
objs::bstr::ByteVec,
odb::{pack, pack::FindExt},
parallel::InOrderIter,
prelude::Finalize,
hash, hash::ObjectId, interrupt, objs::bstr::ByteVec, odb::pack, parallel::InOrderIter, prelude::Finalize,
progress, traverse, Count, NestedProgress, Progress,
};

Expand Down Expand Up @@ -136,12 +130,9 @@ where
.collect::<Result<Vec<_>, _>>()?;
let handle = repo.objects.into_shared_arc().to_cache_arc();
let iter = Box::new(
traverse::commit::Ancestors::new(tips, traverse::commit::ancestors::State::default(), {
let handle = handle.clone();
move |oid, buf| handle.find_commit_iter(oid, buf).map(|t| t.0)
})
.map(|res| res.map_err(|err| Box::new(err) as Box<_>).map(|c| c.id))
.inspect(move |_| progress.inc()),
traverse::commit::Ancestors::new(tips, traverse::commit::ancestors::State::default(), handle.clone())
.map(|res| res.map_err(|err| Box::new(err) as Box<_>).map(|c| c.id))
.inspect(move |_| progress.inc()),
);
(handle, iter)
}
Expand Down
4 changes: 2 additions & 2 deletions gitoxide-core/src/pack/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub fn from_pack(
directory,
&mut progress,
ctx.should_interrupt,
None,
None::<gix::objs::find::Never>,
options,
)
}
Expand All @@ -105,7 +105,7 @@ pub fn from_pack(
directory,
&mut progress,
ctx.should_interrupt,
None,
None::<gix::objs::find::Never>,
options,
),
}
Expand Down
2 changes: 1 addition & 1 deletion gitoxide-core/src/pack/receive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ fn receive_pack_blocking<W: io::Write>(
directory.take().as_deref(),
&mut progress,
&ctx.should_interrupt,
None,
None::<gix::objs::find::Never>,
options,
)
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
Expand Down
103 changes: 78 additions & 25 deletions gitoxide-core/src/query/engine/update.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::cell::RefCell;
use std::{
convert::Infallible,
sync::atomic::{AtomicUsize, Ordering},
time::Instant,
};

use anyhow::{anyhow, bail};
use gix::objs::find::Error;
use gix::{
bstr::{BStr, BString, ByteSlice},
features::progress,
object::tree::diff::rewrites::CopySource,
odb::FindExt,
parallel::{InOrderIter, SequenceId},
prelude::ObjectIdExt,
Count, Progress,
Expand Down Expand Up @@ -150,13 +151,18 @@ pub fn update(
});
r
};

#[derive(Clone)]
struct Task {
commit: gix::hash::ObjectId,
parent_commit: Option<gix::hash::ObjectId>,
compute_stats: bool,
}

type Packet = (SequenceId, Vec<Task>);

let (tx_tree_ids, stat_threads) = {
let (tx, rx) = crossbeam_channel::unbounded::<(SequenceId, Vec<Task>)>();
let (tx, rx) = crossbeam_channel::unbounded::<Packet>();
let stat_workers = (0..threads)
.map(|_| {
scope.spawn({
Expand Down Expand Up @@ -304,46 +310,94 @@ pub fn update(
};
drop(tx_stats);

const CHUNK_SIZE: usize = 50;
let mut chunk = Vec::with_capacity(CHUNK_SIZE);
let mut chunk_id: SequenceId = 0;
let commit_iter = gix::interrupt::Iter::new(
commit_id.ancestors(|oid, buf| -> Result<_, gix::object::find::existing::Error> {
let obj = repo.objects.find(oid, buf)?;
traverse_progress.inc();
if known_commits.binary_search(&oid.to_owned()).is_err() {
#[derive(Clone)]
struct Db<'a, Find: Clone> {
inner: &'a Find,
progress: &'a dyn gix::progress::Count,
chunk: std::cell::RefCell<Vec<Task>>,
chunk_id: std::cell::RefCell<SequenceId>,
chunk_size: usize,
tx: crossbeam_channel::Sender<Packet>,
known_commits: &'a [gix::ObjectId],
}

impl<'a, Find> Db<'a, Find>
where
Find: gix::prelude::Find + Clone,
{
fn new(
inner: &'a Find,
progress: &'a dyn gix::progress::Count,
chunk_size: usize,
tx: crossbeam_channel::Sender<Packet>,
known_commits: &'a [gix::ObjectId],
) -> Self {
Self {
inner,
progress,
known_commits,
tx,
chunk_size,
chunk_id: 0.into(),
chunk: RefCell::new(Vec::with_capacity(chunk_size)),
}
}

fn send_last_chunk(self) {
self.tx.send((self.chunk_id.into_inner(), self.chunk.into_inner())).ok();
}
}

impl<'a, Find> gix::prelude::Find for Db<'a, Find>
where
Find: gix::prelude::Find + Clone,
{
fn try_find<'b>(&self, id: &gix::oid, buf: &'b mut Vec<u8>) -> Result<Option<gix::objs::Data<'b>>, Error> {
let obj = self.inner.try_find(id, buf)?;
let Some(obj) = obj else { return Ok(None) };
if !obj.kind.is_commit() {
return Ok(None);
}

self.progress.inc();
if self.known_commits.binary_search(&id.to_owned()).is_err() {
let res = {
let mut parents = gix::objs::CommitRefIter::from_bytes(obj.data).parent_ids();
let res = parents.next().map(|first_parent| (Some(first_parent), oid.to_owned()));
let res = parents.next().map(|first_parent| (Some(first_parent), id.to_owned()));
match parents.next() {
Some(_) => None,
None => res,
}
};
if let Some((first_parent, commit)) = res {
chunk.push(Task {
self.chunk.borrow_mut().push(Task {
parent_commit: first_parent,
commit,
compute_stats: true,
});
} else {
chunk.push(Task {
self.chunk.borrow_mut().push(Task {
parent_commit: None,
commit: oid.to_owned(),
commit: id.to_owned(),
compute_stats: false,
});
}
if chunk.len() == CHUNK_SIZE {
tx_tree_ids
.send((chunk_id, std::mem::replace(&mut chunk, Vec::with_capacity(CHUNK_SIZE))))
if self.chunk.borrow().len() == self.chunk_size {
self.tx
.send((
*self.chunk_id.borrow(),
std::mem::replace(&mut self.chunk.borrow_mut(), Vec::with_capacity(self.chunk_size)),
))
.ok();
chunk_id += 1;
*self.chunk_id.borrow_mut() += 1;
}
}
Ok(gix::objs::CommitRefIter::from_bytes(obj.data))
}),
|| anyhow!("Cancelled by user"),
);
Ok(Some(obj))
}
}

let db = Db::new(&repo.objects, &traverse_progress, 50, tx_tree_ids, &known_commits);
let commit_iter = gix::interrupt::Iter::new(commit_id.ancestors(&db), || anyhow!("Cancelled by user"));
let mut commits = Vec::new();
for c in commit_iter {
match c?.map(|c| c.id) {
Expand All @@ -354,15 +408,14 @@ pub fn update(
break;
}
}
Err(gix::traverse::commit::ancestors::Error::FindExisting { .. }) => {
Err(gix::traverse::commit::ancestors::Error::Find { .. }) => {
writeln!(err, "shallow repository - commit history is truncated").ok();
break;
}
Err(err) => return Err(err.into()),
};
}
tx_tree_ids.send((chunk_id, chunk)).ok();
drop(tx_tree_ids);
db.send_last_chunk();
let saw_new_commits = !commits.is_empty();
if saw_new_commits {
traverse_progress.show_throughput(start);
Expand Down
6 changes: 1 addition & 5 deletions gitoxide-core/src/repository/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::OutputFormat;
use anyhow::{bail, Context};
use gix::bstr::{BStr, BString};
use gix::index::Entry;
use gix::prelude::FindExt;
use gix::Progress;
use gix_status::index_as_worktree::traits::FastEq;
use gix_status::index_as_worktree::{Change, Conflict, EntryStatus};
Expand Down Expand Up @@ -80,10 +79,7 @@ pub fn show(
&mut printer,
FastEq,
Submodule,
{
let odb = repo.objects.clone().into_arc()?;
move |id, buf| odb.find_blob(id, buf)
},
repo.objects.clone().into_arc()?,
&mut progress,
pathspec.detach()?,
repo.filter_pipeline(Some(gix::hash::ObjectId::empty_tree(repo.object_hash())))?
Expand Down
6 changes: 1 addition & 5 deletions gitoxide-core/src/repository/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ pub fn integrity(
if let Some(index) = repo.worktree().map(|wt| wt.index()).transpose()? {
index.verify_integrity()?;
index.verify_entries()?;
index.verify_extensions(true, {
use gix::odb::FindExt;
let objects = repo.objects;
move |oid, buf: &mut Vec<u8>| objects.find_tree_iter(oid, buf).ok()
})?;
index.verify_extensions(true, repo.objects)?;
progress.info(format!("Index at '{}' OK", index.path().display()));
}
match output_statistics {
Expand Down
Loading