Skip to content

Commit c82945f

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

File tree

2 files changed

+72
-51
lines changed

2 files changed

+72
-51
lines changed

crates/gitbutler-oplog/src/oplog.rs

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ 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::object::tree::diff::Change;
28+
use gix::prelude::{ObjectIdExt, Write};
2729
use tracing::instrument;
2830

2931
const SNAPSHOT_FILE_LIMIT_BYTES: u64 = 32 * 1024 * 1024;
@@ -167,10 +169,9 @@ impl OplogExt for Project {
167169
oplog_commit_id: Option<git2::Oid>,
168170
) -> Result<Vec<Snapshot>> {
169171
let worktree_dir = self.path.as_path();
170-
let repo = git2::Repository::open(worktree_dir)?;
171172
let gix_repo = gitbutler_command_context::gix_repository_for_merging(worktree_dir)?;
172173

173-
let traversal_root_id = match oplog_commit_id {
174+
let traversal_root_id = git2_to_gix_object_id(match oplog_commit_id {
174175
Some(id) => id,
175176
None => {
176177
let oplog_state = OplogHandle::new(&self.gb_dir());
@@ -180,69 +181,86 @@ impl OplogExt for Project {
180181
return Ok(vec![]);
181182
}
182183
}
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())?;
184+
})
185+
.attach(&gix_repo);
189186

190187
let mut snapshots = Vec::new();
188+
let mut wd_trees_cache: HashMap<gix::ObjectId, gix::ObjectId> = HashMap::new();
191189

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

205202
let tree = commit.tree()?;
206-
if tree.get_name("virtual_branches.toml").is_none() {
203+
if tree
204+
.lookup_entry_by_path("virtual_branches.toml")?
205+
.is_none()
206+
{
207207
// We reached a tree that is not a snapshot
208208
tracing::warn!("Commit {commit_id} didn't seem to be an oplog commit - skipping");
209209
continue;
210210
}
211211

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

215+
let commit_id = gix_to_git2_oid(commit_id);
215216
let details = commit
216-
.message()
217+
.message_raw()?
218+
.to_str()
219+
.ok()
217220
.and_then(|msg| SnapshotDetails::from_str(msg).ok());
221+
let commit_time = gix_time_to_git2(commit.time()?);
218222

219-
if let Ok(parent) = commit.parent(0) {
223+
if let Some(parent_id) = first_parent {
220224
// 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))?;
229225

230226
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-
})?;
227+
let mut resource_cache = gix_repo.diff_resource_cache_for_tree_diff()?;
228+
let (mut lines_added, mut lines_removed) = (0, 0);
229+
let parent_tree = get_workdir_tree(&mut wd_trees_cache, parent_id, &gix_repo)?;
230+
parent_tree
231+
.changes()?
232+
.options(|opts| {
233+
opts.track_rewrites(None).track_path();
234+
})
235+
.for_each_to_obtain_tree(&wd_tree, |change| -> Result<_> {
236+
match change {
237+
Change::Addition { location, .. } => {
238+
files_changed.push(gix::path::from_bstr(location).into_owned());
239+
}
240+
Change::Deletion { .. }
241+
| Change::Modification { .. }
242+
| Change::Rewrite { .. } => {}
243+
}
244+
if let Some(counts) = change
245+
.diff(&mut resource_cache)
246+
.ok()
247+
.and_then(|mut platform| platform.line_counts().ok().flatten())
248+
{
249+
lines_added += u64::from(counts.insertions);
250+
lines_removed += u64::from(counts.removals);
251+
}
252+
resource_cache.clear_resource_cache_keep_allocation();
253+
254+
Ok(gix::object::tree::diff::Action::Continue)
255+
})?;
237256

238-
let stats = diff.stats()?;
239257
snapshots.push(Snapshot {
240258
commit_id,
241259
details,
242-
lines_added: stats.insertions(),
243-
lines_removed: stats.deletions(),
260+
lines_added: lines_added as usize,
261+
lines_removed: lines_removed as usize,
244262
files_changed,
245-
created_at: commit.time(),
263+
created_at: commit_time,
246264
});
247265
} else {
248266
// this is the very first snapshot
@@ -252,7 +270,7 @@ impl OplogExt for Project {
252270
lines_added: 0,
253271
lines_removed: 0,
254272
files_changed: Vec::new(),
255-
created_at: commit.time(),
273+
created_at: commit_time,
256274
});
257275
break;
258276
}
@@ -318,21 +336,20 @@ impl OplogExt for Project {
318336

319337
/// Get a tree of the working dir (applied branches merged)
320338
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> {
339+
wd_trees_cache: &mut HashMap<gix::ObjectId, gix::ObjectId>,
340+
commit_id: impl Into<gix::ObjectId>,
341+
repo: &'a gix::Repository,
342+
) -> Result<gix::Tree<'a>, anyhow::Error> {
343+
let commit_id = commit_id.into();
326344
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);
345+
if let Ok(wd_tree_id) = tree_from_applied_vbranches(repo, gix_to_git2_oid(commit_id)) {
346+
e.insert(git2_to_gix_object_id(wd_tree_id));
329347
}
330348
}
331-
let wd_tree_id = wd_trees_cache.get(&commit_id).ok_or(anyhow!(
349+
let id = wd_trees_cache.get(&commit_id).copied().ok_or(anyhow!(
332350
"Could not get a tree of all applied virtual branches merged"
333351
))?;
334-
let wd_tree = repo.find_tree(wd_tree_id.to_owned())?;
335-
Ok(wd_tree)
352+
Ok(repo.find_tree(id)?)
336353
}
337354

338355
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)