Skip to content

Commit 5abe1a7

Browse files
committed
Use gitoxide for oplog listing
1 parent 6ef051e commit 5abe1a7

File tree

2 files changed

+64
-51
lines changed

2 files changed

+64
-51
lines changed

crates/gitbutler-oplog/src/oplog.rs

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@ use super::{
1212
state::OplogHandle,
1313
};
1414
use anyhow::{anyhow, bail, Context, Result};
15-
use git2::{DiffOptions, FileMode};
15+
use git2::FileMode;
1616
use gitbutler_command_context::RepositoryExtLite;
1717
use gitbutler_diff::{hunks_by_filepath, FileDiff};
18-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
18+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_time_to_git2, gix_to_git2_oid};
1919
use gitbutler_project::{
2020
access::{WorktreeReadPermission, WorktreeWritePermission},
2121
Project,
2222
};
2323
use gitbutler_repo::SignaturePurpose;
2424
use gitbutler_repo::{GixRepositoryExt, RepositoryExt};
2525
use gitbutler_stack::{Stack, VirtualBranchesHandle, VirtualBranchesState};
26-
use gix::prelude::Write;
26+
use gix::bstr::ByteSlice;
27+
use gix::prelude::{ObjectIdExt, Write};
2728
use tracing::instrument;
2829

2930
const SNAPSHOT_FILE_LIMIT_BYTES: u64 = 32 * 1024 * 1024;
@@ -167,10 +168,9 @@ impl OplogExt for Project {
167168
oplog_commit_id: Option<git2::Oid>,
168169
) -> Result<Vec<Snapshot>> {
169170
let worktree_dir = self.path.as_path();
170-
let repo = git2::Repository::open(worktree_dir)?;
171171
let gix_repo = gitbutler_command_context::gix_repository_for_merging(worktree_dir)?;
172172

173-
let traversal_root_id = match oplog_commit_id {
173+
let traversal_root_id = git2_to_gix_object_id(match oplog_commit_id {
174174
Some(id) => id,
175175
None => {
176176
let oplog_state = OplogHandle::new(&self.gb_dir());
@@ -180,69 +180,79 @@ impl OplogExt for Project {
180180
return Ok(vec![]);
181181
}
182182
}
183-
};
184-
185-
let oplog_head_commit = repo.find_commit(traversal_root_id)?;
186-
187-
let mut revwalk = repo.revwalk()?;
188-
revwalk.push(oplog_head_commit.id())?;
183+
})
184+
.attach(&gix_repo);
189185

190186
let mut snapshots = Vec::new();
187+
let mut wd_trees_cache: HashMap<gix::ObjectId, gix::ObjectId> = HashMap::new();
191188

192-
let mut wd_trees_cache: HashMap<git2::Oid, git2::Oid> = HashMap::new();
193-
194-
for commit_id in revwalk {
189+
for commit_info in traversal_root_id.ancestors().all()? {
195190
if snapshots.len() == limit {
196191
break;
197192
}
198-
let commit_id = commit_id?;
199-
let commit = repo.find_commit(commit_id)?;
200-
201-
if commit.parent_count() > 1 {
193+
let commit_id = commit_info?.id();
194+
let commit = commit_id.object()?.into_commit();
195+
let mut parents = commit.parent_ids();
196+
let (first_parent, second_parent) = (parents.next(), parents.next());
197+
if second_parent.is_some() {
202198
break;
203199
}
204200

205201
let tree = commit.tree()?;
206-
if tree.get_name("virtual_branches.toml").is_none() {
202+
if tree
203+
.lookup_entry_by_path("virtual_branches.toml")?
204+
.is_none()
205+
{
207206
// We reached a tree that is not a snapshot
208207
tracing::warn!("Commit {commit_id} didn't seem to be an oplog commit - skipping");
209208
continue;
210209
}
211210

212211
// Get tree id from cache or calculate it
213-
let wd_tree = get_workdir_tree(&mut wd_trees_cache, commit_id, &repo, &gix_repo)?;
212+
let wd_tree = get_workdir_tree(&mut wd_trees_cache, commit_id, &gix_repo)?;
214213

214+
let commit_id = gix_to_git2_oid(commit_id);
215215
let details = commit
216-
.message()
216+
.message_raw()?
217+
.to_str()
218+
.ok()
217219
.and_then(|msg| SnapshotDetails::from_str(msg).ok());
220+
let commit_time = gix_time_to_git2(commit.time()?);
218221

219-
if let Ok(parent) = commit.parent(0) {
222+
if let Some(parent_id) = first_parent {
220223
// Get tree id from cache or calculate it
221-
let parent_tree =
222-
get_workdir_tree(&mut wd_trees_cache, parent.id(), &repo, &gix_repo)?;
223-
224-
let mut opts = DiffOptions::new();
225-
opts.include_untracked(true);
226-
opts.ignore_submodules(true);
227-
let diff =
228-
repo.diff_tree_to_tree(Some(&parent_tree), Some(&wd_tree), Some(&mut opts))?;
229224

230225
let mut files_changed = Vec::new();
231-
diff.print(git2::DiffFormat::NameOnly, |delta, _, _| {
232-
if let Some(path) = delta.new_file().path() {
233-
files_changed.push(path.to_path_buf());
234-
}
235-
true
236-
})?;
226+
let mut resource_cache = gix_repo.diff_resource_cache_for_tree_diff()?;
227+
let (mut lines_added, mut lines_removed) = (0, 0);
228+
let parent_tree = get_workdir_tree(&mut wd_trees_cache, parent_id, &gix_repo)?;
229+
parent_tree
230+
.changes()?
231+
.options(|opts| {
232+
opts.track_rewrites(None).track_path();
233+
})
234+
.for_each_to_obtain_tree(&wd_tree, |change| -> Result<_> {
235+
files_changed.push(gix::path::from_bstr(change.location()).into_owned());
236+
if let Some(counts) = change
237+
.diff(&mut resource_cache)
238+
.ok()
239+
.and_then(|mut platform| platform.line_counts().ok().flatten())
240+
{
241+
lines_added += u64::from(counts.insertions);
242+
lines_removed += u64::from(counts.removals);
243+
}
244+
resource_cache.clear_resource_cache_keep_allocation();
245+
246+
Ok(gix::object::tree::diff::Action::Continue)
247+
})?;
237248

238-
let stats = diff.stats()?;
239249
snapshots.push(Snapshot {
240250
commit_id,
241251
details,
242-
lines_added: stats.insertions(),
243-
lines_removed: stats.deletions(),
252+
lines_added: lines_added as usize,
253+
lines_removed: lines_removed as usize,
244254
files_changed,
245-
created_at: commit.time(),
255+
created_at: commit_time,
246256
});
247257
} else {
248258
// this is the very first snapshot
@@ -252,7 +262,7 @@ impl OplogExt for Project {
252262
lines_added: 0,
253263
lines_removed: 0,
254264
files_changed: Vec::new(),
255-
created_at: commit.time(),
265+
created_at: commit_time,
256266
});
257267
break;
258268
}
@@ -318,21 +328,20 @@ impl OplogExt for Project {
318328

319329
/// Get a tree of the working dir (applied branches merged)
320330
fn get_workdir_tree<'a>(
321-
wd_trees_cache: &mut HashMap<git2::Oid, git2::Oid>,
322-
commit_id: git2::Oid,
323-
repo: &'a git2::Repository,
324-
gix_repo: &gix::Repository,
325-
) -> Result<git2::Tree<'a>, anyhow::Error> {
331+
wd_trees_cache: &mut HashMap<gix::ObjectId, gix::ObjectId>,
332+
commit_id: impl Into<gix::ObjectId>,
333+
repo: &'a gix::Repository,
334+
) -> Result<gix::Tree<'a>, anyhow::Error> {
335+
let commit_id = commit_id.into();
326336
if let Entry::Vacant(e) = wd_trees_cache.entry(commit_id) {
327-
if let Ok(wd_tree_id) = tree_from_applied_vbranches(gix_repo, commit_id) {
328-
e.insert(wd_tree_id);
337+
if let Ok(wd_tree_id) = tree_from_applied_vbranches(repo, gix_to_git2_oid(commit_id)) {
338+
e.insert(git2_to_gix_object_id(wd_tree_id));
329339
}
330340
}
331-
let wd_tree_id = wd_trees_cache.get(&commit_id).ok_or(anyhow!(
341+
let id = wd_trees_cache.get(&commit_id).copied().ok_or(anyhow!(
332342
"Could not get a tree of all applied virtual branches merged"
333343
))?;
334-
let wd_tree = repo.find_tree(wd_tree_id.to_owned())?;
335-
Ok(wd_tree)
344+
Ok(repo.find_tree(id)?)
336345
}
337346

338347
fn prepare_snapshot(ctx: &Project, _shared_access: &WorktreeReadPermission) -> Result<git2::Oid> {

crates/gitbutler-oxidize/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ use anyhow::Context;
44
use gix::bstr::ByteSlice;
55
use std::borrow::Borrow;
66

7+
pub fn gix_time_to_git2(time: gix::date::Time) -> git2::Time {
8+
git2::Time::new(time.seconds, time.offset)
9+
}
10+
711
pub fn git2_to_gix_object_id(id: git2::Oid) -> gix::ObjectId {
812
gix::ObjectId::try_from(id.as_bytes()).expect("git2 oid is always valid")
913
}

0 commit comments

Comments
 (0)