Skip to content

Commit

Permalink
Massive performance increase on big repo.
Browse files Browse the repository at this point in the history
git2::Repository::remotes() and find_remote(..) are incredibly slow.
Calling them every time for each branch is unnecessarily slow.

Please note that this is not yet the best way to implement this. We are
still iterating through all remote.refspecs() for each branch. Just that
all Remote structs are cached.

An alternative would be to work on `expand_refspec()` and create a
map/index of (branch name, remote branches) once.

\foriequal0#20
  • Loading branch information
siedentop committed Nov 16, 2020
1 parent 88044cd commit 42a0874
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 3 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Types of Change

- `Added` for new features.
- `Changed` for changes in existing functionality.
- `Deprecated` for soon-to-be removed features.
- `Removed` for now removed features.
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [Unreleased]

### Changed

- Performance increase for big repos. Associating each local branch with all remotes is now multiple orders of magnitude faster. There are still bottlenecks that make the use on big repos impractically slow.
21 changes: 21 additions & 0 deletions src/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,27 @@ impl RemoteTrackingBranch {
}
Err(RemoteBranchError::RemoteNotFound)
}

/// Faster implementation that avoids calling git2::Repository methods remotes() and find_remote().
pub fn to_remote_branch_cached(
&self,
remotes: &Vec<git2::Remote>,
) -> std::result::Result<RemoteBranch, RemoteBranchError> {
for remote in remotes.iter() {
if let Some(expanded) = expand_refspec(
&remote,
&self.refname,
Direction::Fetch,
ExpansionSide::Left,
)? {
return Ok(RemoteBranch {
remote: remote.name().context("non-utf8 remote name")?.to_string(),
refname: expanded,
});
}
}
Err(RemoteBranchError::RemoteNotFound)
}
}

impl Refname for RemoteTrackingBranch {
Expand Down
13 changes: 10 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result<TrimPlan> {
let mut classifier = Classifier::new(git, &merge_tracker);
let mut skipped = HashMap::new();

let mut remotes = vec![];
for remote_name in git.repo.remotes()?.iter() {
let remote_name = remote_name.context("non-utf8 remote name")?;
let remote = git.repo.find_remote(&remote_name)?;
remotes.push(remote);
}

info!("Enqueue classification requests");
if param.delete.scan_tracking() {
for (local, upstream) in &tracking_branches {
Expand Down Expand Up @@ -116,7 +123,7 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result<TrimPlan> {
} else {
for (local, upstream) in &tracking_branches {
if let Some(upstream) = upstream {
let remote = upstream.to_remote_branch(&git.repo)?.remote;
let remote = upstream.to_remote_branch_cached(&remotes)?.remote;
let suggestion = SkipSuggestion::TrackingRemote(remote);
skipped.insert(local.refname.clone(), suggestion.clone());
skipped.insert(upstream.refname.clone(), suggestion.clone());
Expand Down Expand Up @@ -144,14 +151,14 @@ pub fn get_trim_plan(git: &Git, param: &PlanParam) -> Result<TrimPlan> {

for base in &base_upstreams {
for remote_tracking in &non_upstream_branches {
let remote = remote_tracking.to_remote_branch(&git.repo)?;
let remote = remote_tracking.to_remote_branch_cached(&remotes)?;
if param.delete.scan_non_upstream_remote(&remote.remote) {
classifier.queue_request(NonUpstreamBranchClassificationRequest {
base,
remote: remote_tracking,
});
} else {
let remote = remote_tracking.to_remote_branch(&git.repo)?.remote;
let remote = remote_tracking.to_remote_branch_cached(&remotes)?.remote;
skipped.insert(
remote_tracking.refname.clone(),
SkipSuggestion::NonUpstream(remote),
Expand Down

0 comments on commit 42a0874

Please sign in to comment.