Skip to content

Commit 4e8a086

Browse files
committed
use latest gix APIs for more convenience and increased readability.
1 parent 01ce1b7 commit 4e8a086

File tree

2 files changed

+107
-84
lines changed
  • crates

2 files changed

+107
-84
lines changed

crates/gitbutler-branch-actions/src/branch.rs

Lines changed: 105 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::VirtualBranchesExt;
2-
use anyhow::{Context, Result};
2+
use anyhow::{bail, Context, Result};
33
use bstr::{BStr, ByteSlice};
44
use core::fmt;
55
use gitbutler_branch::{
@@ -445,95 +445,120 @@ pub fn get_branch_listing_details(
445445
local_branch.name().as_bstr()
446446
)
447447
})??;
448-
// TODO(ST): implement PartialName from `Cow<'_, FullNameRef>`
449-
let local_tracking_ref = repo.find_reference(local_tracking_ref_name.as_ref())?;
448+
let mut local_tracking_ref = repo.find_reference(local_tracking_ref_name.as_ref())?;
450449
(
451-
// TODO(ST): a way to peel to a specific object type, not just the first one.
452-
gix_to_git2_oid(local_tracking_ref.into_fully_peeled_id()?),
450+
gix_to_git2_oid(local_tracking_ref.peel_to_commit()?.id),
453451
target.sha,
454452
)
455453
};
456454

457455
let mut enriched_branches = Vec::new();
458-
for branch in branches {
459-
let other_branch_commit_id = if let Some(virtual_branch) = branch.virtual_branch {
460-
if virtual_branch.in_workspace {
461-
default_target_seen_at_last_update
456+
let diffstats = {
457+
let (start, start_rx) = std::sync::mpsc::channel::<(
458+
std::sync::mpsc::Receiver<gix::object::tree::diff::ChangeDetached>,
459+
std::sync::mpsc::Sender<(usize, usize, usize)>,
460+
)>();
461+
let diffstats = std::thread::Builder::new()
462+
.name("gitbutler-diff-stats".into())
463+
.spawn({
464+
let repo = repo.clone();
465+
move || -> Result<()> {
466+
let mut resource_cache = repo.diff_resource_cache_for_tree_diff()?;
467+
for (change_rx, res_tx) in start_rx {
468+
let (mut number_of_files, mut lines_added, mut lines_removed) = (0, 0, 0);
469+
for change in change_rx {
470+
if let Some(counts) = change
471+
.attach(&repo, &repo)
472+
.diff(&mut resource_cache)
473+
.ok()
474+
.and_then(|mut platform| platform.line_counts().ok())
475+
.flatten()
476+
{
477+
number_of_files += 1;
478+
lines_added += counts.insertions as usize;
479+
lines_removed += counts.removals as usize;
480+
}
481+
// Let's not attempt to reuse the cache as it's only useful if we know the diff repeats
482+
// over different objects, like when doing rename tracking.
483+
resource_cache.clear_resource_cache_keep_allocation();
484+
}
485+
if res_tx
486+
.send((number_of_files, lines_added, lines_removed))
487+
.is_err()
488+
{
489+
break;
490+
}
491+
}
492+
Ok(())
493+
}
494+
})?;
495+
for branch in branches {
496+
let other_branch_commit_id = if let Some(virtual_branch) = branch.virtual_branch {
497+
if virtual_branch.in_workspace {
498+
default_target_seen_at_last_update
499+
} else {
500+
default_target_current_upstream_commit_id
501+
}
462502
} else {
463503
default_target_current_upstream_commit_id
504+
};
505+
let Ok(base) = git2_repo.merge_base(other_branch_commit_id, branch.head) else {
506+
continue;
507+
};
508+
509+
let branch_head = git2_to_gix_object_id(branch.head);
510+
let gix_base = git2_to_gix_object_id(base);
511+
let base_commit = repo.find_object(gix_base)?.try_into_commit()?;
512+
let base_tree = base_commit.tree()?;
513+
let head_tree = repo.find_object(branch_head)?.peel_to_tree()?;
514+
515+
let ((change_tx, change_rx), (res_tx, rex_rx)) =
516+
(std::sync::mpsc::channel(), std::sync::mpsc::channel());
517+
if start.send((change_rx, res_tx)).is_err() {
518+
bail!("diffing-thread crashed");
519+
};
520+
base_tree
521+
.changes()?
522+
.track_rewrites(None)
523+
// NOTE: `stats(head_tree)` is also possible, but we have a separate thread for that.
524+
.for_each_to_obtain_tree(&head_tree, move |change| -> anyhow::Result<Action> {
525+
change_tx.send(change.detach()).ok();
526+
Ok(Action::Continue)
527+
})?;
528+
let (number_of_files, lines_added, lines_removed) = rex_rx.recv()?;
529+
// TODO(ST): make this API nicer, maybe have one that is not based on `ancestors()` but
530+
// similar to revwalk because it's so common?
531+
let revwalk = branch_head
532+
.attach(&repo)
533+
.ancestors()
534+
// When allowing to skip branches without a filter, make sure it automatically skips by date!
535+
.sorting(
536+
gix::traverse::commit::simple::Sorting::ByCommitTimeNewestFirstCutoffOlderThan {
537+
seconds: base_commit.time()?.seconds,
538+
},
539+
)
540+
.selected(|id| id != gix_base)?;
541+
let mut num_commits = 0;
542+
let mut authors = HashSet::new();
543+
for commit_info in revwalk {
544+
let commit_info = commit_info?;
545+
let commit = repo.find_commit(commit_info.id)?;
546+
authors.insert(commit.author()?.into());
547+
num_commits += 1;
464548
}
465-
} else {
466-
default_target_current_upstream_commit_id
467-
};
468-
let Ok(base) = git2_repo.merge_base(other_branch_commit_id, branch.head) else {
469-
continue;
470-
};
471-
472-
let branch_head = git2_to_gix_object_id(branch.head);
473-
let gix_base = git2_to_gix_object_id(base);
474-
let base_commit = repo.find_object(gix_base)?.try_into_commit()?;
475-
let base_tree = base_commit.tree()?;
476-
let head_tree = repo.find_object(branch_head)?.peel_to_tree()?;
477-
// TODO(ST): make it easier to get a resource cache preconfigured for different purposes,
478-
// like tree-tree. Should probably be on the platform and separate?
479-
let mut resource_cache = repo.diff_resource_cache(
480-
gix::diff::blob::pipeline::Mode::ToGit,
481-
gix::diff::blob::pipeline::WorktreeRoots::default(),
482-
)?;
483-
let (mut number_of_files, mut lines_added, mut lines_removed) = (0, 0, 0);
484-
base_tree
485-
.changes()?
486-
.track_rewrites(None)
487-
// TODO(ST): definitely have `stats()` just like `git2`.
488-
.for_each_to_obtain_tree(&head_tree, |change| -> anyhow::Result<Action> {
489-
if let Some(counts) = change
490-
.diff(&mut resource_cache)
491-
.ok()
492-
.and_then(|mut platform| platform.line_counts().ok())
493-
.flatten()
494-
{
495-
number_of_files += 1;
496-
lines_added += counts.insertions as usize;
497-
lines_removed += counts.removals as usize;
498-
}
499-
// Let's not attempt to reuse the cache as it's only useful if we know the diff repeats
500-
// over different objects, like when doing rename tracking.
501-
// TODO(ST): consider not using it unless it's for rename tracking. However, there should
502-
// be a way to re-use memory for the two objects to compare, at least.
503-
resource_cache.clear_resource_cache();
504-
Ok(Action::Continue)
505-
})?;
506-
// TODO(ST): make this API nicer, maybe have one that is not based on `ancestors()` but
507-
// similar to revwalk because it's so common?
508-
let revwalk = branch_head
509-
.attach(&repo)
510-
.ancestors()
511-
// When allowing to skip branches without a filter, make sure it automatically skips by date!
512-
.sorting(
513-
gix::traverse::commit::simple::Sorting::ByCommitTimeNewestFirstCutoffOlderThan {
514-
seconds: base_commit.time()?.seconds,
515-
},
516-
)
517-
.selected(|id| id != gix_base)?;
518-
let mut num_commits = 0;
519-
let mut authors = HashSet::new();
520-
for commit_info in revwalk {
521-
let commit_info = commit_info?;
522-
// TODO(ST): offer direct `find_<kind>` methods.
523-
let commit = repo.find_object(commit_info.id)?.try_into_commit()?;
524-
authors.insert(commit.author()?.into());
525-
num_commits += 1;
549+
let branch_data = BranchListingDetails {
550+
name: branch.name,
551+
lines_added,
552+
lines_removed,
553+
number_of_files,
554+
authors: authors.into_iter().collect(),
555+
number_of_commits: num_commits,
556+
};
557+
enriched_branches.push(branch_data);
526558
}
527-
let branch_data = BranchListingDetails {
528-
name: branch.name,
529-
lines_added,
530-
lines_removed,
531-
number_of_files,
532-
authors: authors.into_iter().collect(),
533-
number_of_commits: num_commits,
534-
};
535-
enriched_branches.push(branch_data);
536-
}
559+
diffstats
560+
};
561+
diffstats.join().expect("no panic")?;
537562
Ok(enriched_branches)
538563
}
539564

crates/gitbutler-command-context/src/lib.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,8 @@ pub trait GixRepositoryExt: Sized {
108108

109109
impl GixRepositoryExt for gix::Repository {
110110
fn for_tree_diffing(mut self) -> anyhow::Result<Self> {
111-
let num_tracked = self.index_or_empty()?.entries().len();
112-
let ten_mb_for_every_10k_files =
113-
(num_tracked as f32 / 10_000.0) * (10 * 1024 * 1024) as f32;
114-
self.object_cache_size_if_unset((ten_mb_for_every_10k_files as usize).max(4 * 1024));
111+
let bytes = self.compute_object_cache_size_for_tree_diffs(&***self.index_or_empty()?);
112+
self.object_cache_size_if_unset(bytes);
115113
Ok(self)
116114
}
117115
}

0 commit comments

Comments
 (0)