Skip to content

Commit

Permalink
Use gitoxide for oplog listing
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Nov 4, 2024
1 parent 6ef051e commit bf9ccb1
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 32 deletions.
81 changes: 49 additions & 32 deletions crates/gitbutler-oplog/src/oplog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ use super::{
state::OplogHandle,
};
use anyhow::{anyhow, bail, Context, Result};
use git2::{DiffOptions, FileMode};
use git2::FileMode;
use gitbutler_command_context::RepositoryExtLite;
use gitbutler_diff::{hunks_by_filepath, FileDiff};
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
use gitbutler_oxidize::{git2_to_gix_object_id, gix_time_to_git2, gix_to_git2_oid};
use gitbutler_project::{
access::{WorktreeReadPermission, WorktreeWritePermission},
Project,
};
use gitbutler_repo::SignaturePurpose;
use gitbutler_repo::{GixRepositoryExt, RepositoryExt};
use gitbutler_stack::{Stack, VirtualBranchesHandle, VirtualBranchesState};
use gix::bstr::ByteSlice;
use gix::prelude::Write;
use tracing::instrument;

Expand Down Expand Up @@ -196,53 +197,71 @@ impl OplogExt for Project {
break;
}
let commit_id = commit_id?;
let commit = repo.find_commit(commit_id)?;

if commit.parent_count() > 1 {
let commit = gix_repo.find_commit(git2_to_gix_object_id(commit_id))?;
let mut parents = commit.parent_ids();
let (first_parent, second_parent) = (parents.next(), parents.next());
if second_parent.is_some() {
break;
}

let tree = commit.tree()?;
if tree.get_name("virtual_branches.toml").is_none() {
if tree
.lookup_entry_by_path("virtual_branches.toml")?
.is_none()
{
// We reached a tree that is not a snapshot
tracing::warn!("Commit {commit_id} didn't seem to be an oplog commit - skipping");
continue;
}

// Get tree id from cache or calculate it
let wd_tree = get_workdir_tree(&mut wd_trees_cache, commit_id, &repo, &gix_repo)?;
let wd_tree =
gix_repo.find_tree(get_workdir_tree(&mut wd_trees_cache, commit_id, &gix_repo)?)?;

let details = commit
.message()
.message_raw()?
.to_str()
.ok()
.and_then(|msg| SnapshotDetails::from_str(msg).ok());
let commit_time = gix_time_to_git2(commit.time()?);

if let Ok(parent) = commit.parent(0) {
if let Some(parent_id) = first_parent {
// Get tree id from cache or calculate it
let parent_tree =
get_workdir_tree(&mut wd_trees_cache, parent.id(), &repo, &gix_repo)?;

let mut opts = DiffOptions::new();
opts.include_untracked(true);
opts.ignore_submodules(true);
let diff =
repo.diff_tree_to_tree(Some(&parent_tree), Some(&wd_tree), Some(&mut opts))?;
let parent_tree_id =
get_workdir_tree(&mut wd_trees_cache, gix_to_git2_oid(parent_id), &gix_repo)?;
let parent_tree = gix_repo.find_tree(parent_tree_id)?;

let mut files_changed = Vec::new();
diff.print(git2::DiffFormat::NameOnly, |delta, _, _| {
if let Some(path) = delta.new_file().path() {
files_changed.push(path.to_path_buf());
}
true
})?;
let mut resource_cache = gix_repo.diff_resource_cache_for_tree_diff()?;
let (mut lines_added, mut lines_removed) = (0, 0);
parent_tree
.changes()?
.options(|opts| {
opts.track_rewrites(None).track_path();
})
.for_each_to_obtain_tree(&wd_tree, |change| -> Result<_> {
files_changed.push(gix::path::from_bstr(change.location()).into_owned());
if let Some(counts) = change
.diff(&mut resource_cache)
.ok()
.and_then(|mut platform| platform.line_counts().ok())
.flatten()
{
lines_added += u64::from(counts.insertions);
lines_removed += u64::from(counts.removals);
}
resource_cache.clear_resource_cache_keep_allocation();

Ok(gix::object::tree::diff::Action::Continue)
})?;

let stats = diff.stats()?;
snapshots.push(Snapshot {
commit_id,
details,
lines_added: stats.insertions(),
lines_removed: stats.deletions(),
lines_added: lines_added as usize,
lines_removed: lines_removed as usize,
files_changed,
created_at: commit.time(),
created_at: commit_time,
});
} else {
// this is the very first snapshot
Expand All @@ -252,7 +271,7 @@ impl OplogExt for Project {
lines_added: 0,
lines_removed: 0,
files_changed: Vec::new(),
created_at: commit.time(),
created_at: commit_time,
});
break;
}
Expand Down Expand Up @@ -320,9 +339,8 @@ impl OplogExt for Project {
fn get_workdir_tree<'a>(
wd_trees_cache: &mut HashMap<git2::Oid, git2::Oid>,
commit_id: git2::Oid,
repo: &'a git2::Repository,
gix_repo: &gix::Repository,
) -> Result<git2::Tree<'a>, anyhow::Error> {
) -> Result<gix::ObjectId, anyhow::Error> {
if let Entry::Vacant(e) = wd_trees_cache.entry(commit_id) {
if let Ok(wd_tree_id) = tree_from_applied_vbranches(gix_repo, commit_id) {
e.insert(wd_tree_id);
Expand All @@ -331,8 +349,7 @@ fn get_workdir_tree<'a>(
let wd_tree_id = wd_trees_cache.get(&commit_id).ok_or(anyhow!(
"Could not get a tree of all applied virtual branches merged"
))?;
let wd_tree = repo.find_tree(wd_tree_id.to_owned())?;
Ok(wd_tree)
Ok(git2_to_gix_object_id(*wd_tree_id))
}

fn prepare_snapshot(ctx: &Project, _shared_access: &WorktreeReadPermission) -> Result<git2::Oid> {
Expand Down
4 changes: 4 additions & 0 deletions crates/gitbutler-oxidize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use anyhow::Context;
use gix::bstr::ByteSlice;
use std::borrow::Borrow;

pub fn gix_time_to_git2(time: gix::date::Time) -> git2::Time {
git2::Time::new(time.seconds, time.offset)
}

pub fn git2_to_gix_object_id(id: git2::Oid) -> gix::ObjectId {
gix::ObjectId::try_from(id.as_bytes()).expect("git2 oid is always valid")
}
Expand Down

0 comments on commit bf9ccb1

Please sign in to comment.