mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-02 22:42:56 +03:00
move get_applied_status to separate module
This commit is contained in:
parent
587915227d
commit
246dd9c379
@ -19,6 +19,7 @@ use crate::branch_manager::BranchManagerExt;
|
||||
use crate::conflicts::RepoConflictsExt;
|
||||
use crate::integration::update_gitbutler_integration;
|
||||
use crate::remote::{commit_to_remote_commit, RemoteCommit};
|
||||
use crate::status::get_applied_status;
|
||||
use crate::{VirtualBranchHunk, VirtualBranchesExt};
|
||||
use gitbutler_branch::GITBUTLER_INTEGRATION_REFERENCE;
|
||||
use gitbutler_error::error::Marker;
|
||||
@ -365,7 +366,7 @@ pub(crate) fn update_base_branch(
|
||||
let vb_state = project_repository.project().virtual_branches();
|
||||
|
||||
// try to update every branch
|
||||
let updated_vbranches = vb::get_applied_status(project_repository, None)?
|
||||
let updated_vbranches = get_applied_status(project_repository, None)?
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|(branch, _)| branch)
|
||||
|
@ -23,6 +23,8 @@ pub use remote::{list_remote_branches, RemoteBranch, RemoteBranchData, RemoteCom
|
||||
pub mod conflicts;
|
||||
|
||||
mod author;
|
||||
mod status;
|
||||
pub use status::get_applied_status;
|
||||
|
||||
use gitbutler_branch::VirtualBranchesHandle;
|
||||
trait VirtualBranchesExt {
|
||||
|
285
crates/gitbutler-branch-actions/src/status.rs
Normal file
285
crates/gitbutler-branch-actions/src/status.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use gitbutler_branch::{
|
||||
Branch, BranchCreateRequest, BranchId, BranchOwnershipClaims, OwnershipClaim,
|
||||
};
|
||||
use gitbutler_command_context::ProjectRepository;
|
||||
use gitbutler_diff::{diff_files_into_hunks, GitHunk, Hunk, HunkHash};
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_repo::RepositoryExt;
|
||||
use md5::Digest;
|
||||
use std::{collections::HashMap, path::PathBuf, vec};
|
||||
|
||||
use crate::{
|
||||
conflicts::RepoConflictsExt, integration::get_workspace_head, write_tree, BranchManagerExt,
|
||||
HunkLock, VirtualBranchesExt,
|
||||
};
|
||||
|
||||
pub type BranchStatus = HashMap<PathBuf, Vec<gitbutler_diff::GitHunk>>;
|
||||
type AppliedStatuses = Vec<(Branch, BranchStatus)>;
|
||||
|
||||
// Returns branches and their associated file changes, in addition to a list
|
||||
// of skipped files.
|
||||
// TODO(kv): make this side effect free
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn get_applied_status(
|
||||
project_repository: &ProjectRepository,
|
||||
perm: Option<&mut WorktreeWritePermission>,
|
||||
) -> Result<(
|
||||
AppliedStatuses,
|
||||
Vec<gitbutler_diff::FileDiff>,
|
||||
HashMap<Digest, Vec<HunkLock>>,
|
||||
)> {
|
||||
let integration_commit = get_workspace_head(project_repository)?;
|
||||
let mut virtual_branches = project_repository
|
||||
.project()
|
||||
.virtual_branches()
|
||||
.list_branches_in_workspace()?;
|
||||
let base_file_diffs =
|
||||
gitbutler_diff::workdir(project_repository.repo(), &integration_commit.to_owned())
|
||||
.context("failed to diff workdir")?;
|
||||
|
||||
let mut skipped_files: Vec<gitbutler_diff::FileDiff> = Vec::new();
|
||||
for file_diff in base_file_diffs.values() {
|
||||
if file_diff.skipped {
|
||||
skipped_files.push(file_diff.clone());
|
||||
}
|
||||
}
|
||||
let mut base_diffs: HashMap<_, _> = diff_files_into_hunks(base_file_diffs).collect();
|
||||
|
||||
// sort by order, so that the default branch is first (left in the ui)
|
||||
virtual_branches.sort_by(|a, b| a.order.cmp(&b.order));
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
|
||||
if virtual_branches.is_empty() && !base_diffs.is_empty() {
|
||||
if let Some(perm) = perm {
|
||||
virtual_branches = vec![branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), perm)
|
||||
.context("failed to create default branch")?];
|
||||
} else {
|
||||
bail!("Would have to create virtual-branch but write permissions aren't available")
|
||||
}
|
||||
}
|
||||
|
||||
let mut diffs_by_branch: HashMap<BranchId, BranchStatus> = virtual_branches
|
||||
.iter()
|
||||
.map(|branch| (branch.id, HashMap::new()))
|
||||
.collect();
|
||||
|
||||
let locks = new_compute_locks(project_repository.repo(), &base_diffs, &virtual_branches)?;
|
||||
|
||||
for branch in &mut virtual_branches {
|
||||
let old_claims = branch.ownership.claims.clone();
|
||||
let new_claims = old_claims
|
||||
.iter()
|
||||
.filter_map(|claim| {
|
||||
let git_diff_hunks = match base_diffs.get_mut(&claim.file_path) {
|
||||
None => return None,
|
||||
Some(hunks) => hunks,
|
||||
};
|
||||
|
||||
let claimed_hunks: Vec<Hunk> = claim
|
||||
.hunks
|
||||
.iter()
|
||||
.filter_map(|claimed_hunk| {
|
||||
// if any of the current hunks intersects with the owned hunk, we want to keep it
|
||||
for (i, git_diff_hunk) in git_diff_hunks.iter().enumerate() {
|
||||
if claimed_hunk == &Hunk::from(git_diff_hunk)
|
||||
|| claimed_hunk.intersects(git_diff_hunk)
|
||||
{
|
||||
let hash = Hunk::hash_diff(&git_diff_hunk.diff_lines);
|
||||
if locks.contains_key(&hash) {
|
||||
return None; // Defer allocation to unclaimed hunks processing
|
||||
}
|
||||
diffs_by_branch
|
||||
.entry(branch.id)
|
||||
.or_default()
|
||||
.entry(claim.file_path.clone())
|
||||
.or_default()
|
||||
.push(git_diff_hunk.clone());
|
||||
let updated_hunk = Hunk {
|
||||
start: git_diff_hunk.new_start,
|
||||
end: git_diff_hunk.new_start + git_diff_hunk.new_lines,
|
||||
hash: Some(hash),
|
||||
};
|
||||
git_diff_hunks.remove(i);
|
||||
return Some(updated_hunk);
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
if claimed_hunks.is_empty() {
|
||||
// No need for an empty claim
|
||||
None
|
||||
} else {
|
||||
Some(OwnershipClaim {
|
||||
file_path: claim.file_path.clone(),
|
||||
hunks: claimed_hunks,
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
branch.ownership = BranchOwnershipClaims { claims: new_claims };
|
||||
}
|
||||
|
||||
let max_selected_for_changes = virtual_branches
|
||||
.iter()
|
||||
.filter_map(|b| b.selected_for_changes)
|
||||
.max()
|
||||
.unwrap_or(-1);
|
||||
let default_vbranch_pos = virtual_branches
|
||||
.iter()
|
||||
.position(|b| b.selected_for_changes == Some(max_selected_for_changes))
|
||||
.unwrap_or(0);
|
||||
|
||||
// Everything claimed has been removed from `base_diffs`, here we just
|
||||
// process the remaining ones.
|
||||
for (filepath, hunks) in base_diffs {
|
||||
for hunk in hunks {
|
||||
let hash = Hunk::hash_diff(&hunk.diff_lines);
|
||||
let locked_to = locks.get(&hash);
|
||||
|
||||
let vbranch_pos = if let Some(locks) = locked_to {
|
||||
let p = virtual_branches
|
||||
.iter()
|
||||
.position(|vb| vb.id == locks[0].branch_id);
|
||||
match p {
|
||||
Some(p) => p,
|
||||
_ => default_vbranch_pos,
|
||||
}
|
||||
} else {
|
||||
default_vbranch_pos
|
||||
};
|
||||
|
||||
virtual_branches[vbranch_pos].ownership.put(OwnershipClaim {
|
||||
file_path: filepath.clone(),
|
||||
hunks: vec![Hunk::from(&hunk).with_hash(Hunk::hash_diff(&hunk.diff_lines))],
|
||||
});
|
||||
|
||||
diffs_by_branch
|
||||
.entry(virtual_branches[vbranch_pos].id)
|
||||
.or_default()
|
||||
.entry(filepath.clone())
|
||||
.or_default()
|
||||
.push(hunk);
|
||||
}
|
||||
}
|
||||
|
||||
let mut hunks_by_branch = diffs_by_branch
|
||||
.into_iter()
|
||||
.map(|(branch_id, hunks)| {
|
||||
(
|
||||
virtual_branches
|
||||
.iter()
|
||||
.find(|b| b.id.eq(&branch_id))
|
||||
.unwrap()
|
||||
.clone(),
|
||||
hunks,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// write updated state if not resolving
|
||||
if !project_repository.is_resolving() {
|
||||
let vb_state = project_repository.project().virtual_branches();
|
||||
for (vbranch, files) in &mut hunks_by_branch {
|
||||
vbranch.tree = write_tree(project_repository, &vbranch.head, files)?;
|
||||
vb_state
|
||||
.set_branch(vbranch.clone())
|
||||
.context(format!("failed to write virtual branch {}", vbranch.name))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((hunks_by_branch, skipped_files, locks))
|
||||
}
|
||||
|
||||
fn new_compute_locks(
|
||||
repository: &git2::Repository,
|
||||
unstaged_hunks_by_path: &HashMap<PathBuf, Vec<gitbutler_diff::GitHunk>>,
|
||||
virtual_branches: &[Branch],
|
||||
) -> Result<HashMap<HunkHash, Vec<HunkLock>>> {
|
||||
// If we cant find the integration commit and subsequently the target commit, we can't find any locks
|
||||
let target_tree = repository.target_commit()?.tree()?;
|
||||
|
||||
let mut diff_opts = git2::DiffOptions::new();
|
||||
let opts = diff_opts
|
||||
.show_binary(true)
|
||||
.ignore_submodules(true)
|
||||
.context_lines(3);
|
||||
|
||||
let branch_path_diffs = virtual_branches
|
||||
.iter()
|
||||
.filter_map(|branch| {
|
||||
let commit = repository.find_commit(branch.head).ok()?;
|
||||
let tree = commit.tree().ok()?;
|
||||
let diff = repository
|
||||
.diff_tree_to_tree(Some(&target_tree), Some(&tree), Some(opts))
|
||||
.ok()?;
|
||||
let hunks_by_filepath =
|
||||
gitbutler_diff::hunks_by_filepath(Some(repository), &diff).ok()?;
|
||||
|
||||
Some((branch, hunks_by_filepath))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut integration_hunks_by_path =
|
||||
HashMap::<PathBuf, Vec<(gitbutler_diff::GitHunk, &Branch)>>::new();
|
||||
|
||||
for (branch, hunks_by_filepath) in branch_path_diffs {
|
||||
for (path, hunks) in hunks_by_filepath {
|
||||
integration_hunks_by_path.entry(path).or_default().extend(
|
||||
hunks
|
||||
.hunks
|
||||
.iter()
|
||||
.map(|hunk| (hunk.clone(), branch))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let locked_hunks = unstaged_hunks_by_path
|
||||
.iter()
|
||||
.filter_map(|(path, hunks)| {
|
||||
let integration_hunks = integration_hunks_by_path.get(path)?;
|
||||
|
||||
let (unapplied_hunk, branches) = hunks.iter().find_map(|unapplied_hunk| {
|
||||
// Find all branches that have a hunk that intersects with the unapplied hunk
|
||||
let locked_to = integration_hunks
|
||||
.iter()
|
||||
.filter_map(|(integration_hunk, branch)| {
|
||||
if GitHunk::integration_intersects_unapplied(
|
||||
integration_hunk,
|
||||
unapplied_hunk,
|
||||
) {
|
||||
Some(*branch)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if locked_to.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((unapplied_hunk, locked_to))
|
||||
}
|
||||
})?;
|
||||
|
||||
let hash = Hunk::hash_diff(&unapplied_hunk.diff_lines);
|
||||
let locks = branches
|
||||
.iter()
|
||||
.map(|b| HunkLock {
|
||||
branch_id: b.id,
|
||||
commit_id: b.head,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// For now we're returning an array of locks to align with the original type, even though this implementation doesn't give multiple locks for the same hunk
|
||||
Some((hash, locks))
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
Ok(locked_hunks)
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
use gitbutler_branch::{dedup, BranchUpdateRequest, VirtualBranchesHandle};
|
||||
use gitbutler_branch::{dedup_fmt, Branch, BranchCreateRequest, BranchId};
|
||||
use gitbutler_branch::{dedup_fmt, Branch, BranchId};
|
||||
use gitbutler_branch::{reconcile_claims, BranchOwnershipClaims};
|
||||
use gitbutler_branch::{OwnershipClaim, Target};
|
||||
use gitbutler_command_context::ProjectRepository;
|
||||
use gitbutler_commit::commit_ext::CommitExt;
|
||||
use gitbutler_commit::commit_headers::HasCommitHeaders;
|
||||
use gitbutler_diff::{diff_files_into_hunks, trees, FileDiff, GitHunk};
|
||||
use gitbutler_diff::{trees, FileDiff, GitHunk};
|
||||
use gitbutler_diff::{Hunk, HunkHash};
|
||||
use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname};
|
||||
use gitbutler_repo::credentials::Helper;
|
||||
@ -34,6 +34,7 @@ use crate::branch_manager::BranchManagerExt;
|
||||
use crate::conflicts::{self, RepoConflictsExt};
|
||||
use crate::integration::get_workspace_head;
|
||||
use crate::remote::{branch_to_remote_branch, RemoteBranch};
|
||||
use crate::status::get_applied_status;
|
||||
use crate::VirtualBranchesExt;
|
||||
use gitbutler_error::error::Code;
|
||||
use gitbutler_error::error::Marker;
|
||||
@ -41,8 +42,6 @@ use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_repo::rebase::{cherry_rebase, cherry_rebase_group};
|
||||
use gitbutler_time::time::now_since_unix_epoch_ms;
|
||||
|
||||
type AppliedStatuses = Vec<(Branch, BranchStatus)>;
|
||||
|
||||
// this struct is a mapping to the view `Branch` type in Typescript
|
||||
// found in src-tauri/src/routes/repo/[project_id]/types.ts
|
||||
// it holds a materialized view for presentation purposes of the Branch struct in Rust
|
||||
@ -1057,273 +1056,6 @@ pub(crate) fn virtual_hunks_by_file_diffs<'a>(
|
||||
pub type BranchStatus = HashMap<PathBuf, Vec<gitbutler_diff::GitHunk>>;
|
||||
pub type VirtualBranchHunksByPathMap = HashMap<PathBuf, Vec<VirtualBranchHunk>>;
|
||||
|
||||
fn new_compute_locks(
|
||||
repository: &git2::Repository,
|
||||
unstaged_hunks_by_path: &HashMap<PathBuf, Vec<gitbutler_diff::GitHunk>>,
|
||||
virtual_branches: &[Branch],
|
||||
) -> Result<HashMap<HunkHash, Vec<HunkLock>>> {
|
||||
// If we cant find the integration commit and subsequently the target commit, we can't find any locks
|
||||
let target_tree = repository.target_commit()?.tree()?;
|
||||
|
||||
let mut diff_opts = git2::DiffOptions::new();
|
||||
let opts = diff_opts
|
||||
.show_binary(true)
|
||||
.ignore_submodules(true)
|
||||
.context_lines(3);
|
||||
|
||||
let branch_path_diffs = virtual_branches
|
||||
.iter()
|
||||
.filter_map(|branch| {
|
||||
let commit = repository.find_commit(branch.head).ok()?;
|
||||
let tree = commit.tree().ok()?;
|
||||
let diff = repository
|
||||
.diff_tree_to_tree(Some(&target_tree), Some(&tree), Some(opts))
|
||||
.ok()?;
|
||||
let hunks_by_filepath =
|
||||
gitbutler_diff::hunks_by_filepath(Some(repository), &diff).ok()?;
|
||||
|
||||
Some((branch, hunks_by_filepath))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut integration_hunks_by_path =
|
||||
HashMap::<PathBuf, Vec<(gitbutler_diff::GitHunk, &Branch)>>::new();
|
||||
|
||||
for (branch, hunks_by_filepath) in branch_path_diffs {
|
||||
for (path, hunks) in hunks_by_filepath {
|
||||
integration_hunks_by_path.entry(path).or_default().extend(
|
||||
hunks
|
||||
.hunks
|
||||
.iter()
|
||||
.map(|hunk| (hunk.clone(), branch))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let locked_hunks = unstaged_hunks_by_path
|
||||
.iter()
|
||||
.filter_map(|(path, hunks)| {
|
||||
let integration_hunks = integration_hunks_by_path.get(path)?;
|
||||
|
||||
let (unapplied_hunk, branches) = hunks.iter().find_map(|unapplied_hunk| {
|
||||
// Find all branches that have a hunk that intersects with the unapplied hunk
|
||||
let locked_to = integration_hunks
|
||||
.iter()
|
||||
.filter_map(|(integration_hunk, branch)| {
|
||||
if GitHunk::integration_intersects_unapplied(
|
||||
integration_hunk,
|
||||
unapplied_hunk,
|
||||
) {
|
||||
Some(*branch)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if locked_to.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((unapplied_hunk, locked_to))
|
||||
}
|
||||
})?;
|
||||
|
||||
let hash = Hunk::hash_diff(&unapplied_hunk.diff_lines);
|
||||
let locks = branches
|
||||
.iter()
|
||||
.map(|b| HunkLock {
|
||||
branch_id: b.id,
|
||||
commit_id: b.head,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// For now we're returning an array of locks to align with the original type, even though this implementation doesn't give multiple locks for the same hunk
|
||||
Some((hash, locks))
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
Ok(locked_hunks)
|
||||
}
|
||||
|
||||
// Returns branches and their associated file changes, in addition to a list
|
||||
// of skipped files.
|
||||
// TODO(kv): make this side effect free
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn get_applied_status(
|
||||
project_repository: &ProjectRepository,
|
||||
perm: Option<&mut WorktreeWritePermission>,
|
||||
) -> Result<(
|
||||
AppliedStatuses,
|
||||
Vec<gitbutler_diff::FileDiff>,
|
||||
HashMap<Digest, Vec<HunkLock>>,
|
||||
)> {
|
||||
let integration_commit = get_workspace_head(project_repository)?;
|
||||
let mut virtual_branches = project_repository
|
||||
.project()
|
||||
.virtual_branches()
|
||||
.list_branches_in_workspace()?;
|
||||
let base_file_diffs =
|
||||
gitbutler_diff::workdir(project_repository.repo(), &integration_commit.to_owned())
|
||||
.context("failed to diff workdir")?;
|
||||
|
||||
let mut skipped_files: Vec<gitbutler_diff::FileDiff> = Vec::new();
|
||||
for file_diff in base_file_diffs.values() {
|
||||
if file_diff.skipped {
|
||||
skipped_files.push(file_diff.clone());
|
||||
}
|
||||
}
|
||||
let mut base_diffs: HashMap<_, _> = diff_files_into_hunks(base_file_diffs).collect();
|
||||
|
||||
// sort by order, so that the default branch is first (left in the ui)
|
||||
virtual_branches.sort_by(|a, b| a.order.cmp(&b.order));
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
|
||||
if virtual_branches.is_empty() && !base_diffs.is_empty() {
|
||||
if let Some(perm) = perm {
|
||||
virtual_branches = vec![branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), perm)
|
||||
.context("failed to create default branch")?];
|
||||
} else {
|
||||
bail!("Would have to create virtual-branch but write permissions aren't available")
|
||||
}
|
||||
}
|
||||
|
||||
let mut diffs_by_branch: HashMap<BranchId, BranchStatus> = virtual_branches
|
||||
.iter()
|
||||
.map(|branch| (branch.id, HashMap::new()))
|
||||
.collect();
|
||||
|
||||
let locks = new_compute_locks(project_repository.repo(), &base_diffs, &virtual_branches)?;
|
||||
|
||||
for branch in &mut virtual_branches {
|
||||
let old_claims = branch.ownership.claims.clone();
|
||||
let new_claims = old_claims
|
||||
.iter()
|
||||
.filter_map(|claim| {
|
||||
let git_diff_hunks = match base_diffs.get_mut(&claim.file_path) {
|
||||
None => return None,
|
||||
Some(hunks) => hunks,
|
||||
};
|
||||
|
||||
let claimed_hunks: Vec<Hunk> = claim
|
||||
.hunks
|
||||
.iter()
|
||||
.filter_map(|claimed_hunk| {
|
||||
// if any of the current hunks intersects with the owned hunk, we want to keep it
|
||||
for (i, git_diff_hunk) in git_diff_hunks.iter().enumerate() {
|
||||
if claimed_hunk == &Hunk::from(git_diff_hunk)
|
||||
|| claimed_hunk.intersects(git_diff_hunk)
|
||||
{
|
||||
let hash = Hunk::hash_diff(&git_diff_hunk.diff_lines);
|
||||
if locks.contains_key(&hash) {
|
||||
return None; // Defer allocation to unclaimed hunks processing
|
||||
}
|
||||
diffs_by_branch
|
||||
.entry(branch.id)
|
||||
.or_default()
|
||||
.entry(claim.file_path.clone())
|
||||
.or_default()
|
||||
.push(git_diff_hunk.clone());
|
||||
let updated_hunk = Hunk {
|
||||
start: git_diff_hunk.new_start,
|
||||
end: git_diff_hunk.new_start + git_diff_hunk.new_lines,
|
||||
hash: Some(hash),
|
||||
};
|
||||
git_diff_hunks.remove(i);
|
||||
return Some(updated_hunk);
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
if claimed_hunks.is_empty() {
|
||||
// No need for an empty claim
|
||||
None
|
||||
} else {
|
||||
Some(OwnershipClaim {
|
||||
file_path: claim.file_path.clone(),
|
||||
hunks: claimed_hunks,
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
branch.ownership = BranchOwnershipClaims { claims: new_claims };
|
||||
}
|
||||
|
||||
let max_selected_for_changes = virtual_branches
|
||||
.iter()
|
||||
.filter_map(|b| b.selected_for_changes)
|
||||
.max()
|
||||
.unwrap_or(-1);
|
||||
let default_vbranch_pos = virtual_branches
|
||||
.iter()
|
||||
.position(|b| b.selected_for_changes == Some(max_selected_for_changes))
|
||||
.unwrap_or(0);
|
||||
|
||||
// Everything claimed has been removed from `base_diffs`, here we just
|
||||
// process the remaining ones.
|
||||
for (filepath, hunks) in base_diffs {
|
||||
for hunk in hunks {
|
||||
let hash = Hunk::hash_diff(&hunk.diff_lines);
|
||||
let locked_to = locks.get(&hash);
|
||||
|
||||
let vbranch_pos = if let Some(locks) = locked_to {
|
||||
let p = virtual_branches
|
||||
.iter()
|
||||
.position(|vb| vb.id == locks[0].branch_id);
|
||||
match p {
|
||||
Some(p) => p,
|
||||
_ => default_vbranch_pos,
|
||||
}
|
||||
} else {
|
||||
default_vbranch_pos
|
||||
};
|
||||
|
||||
virtual_branches[vbranch_pos].ownership.put(OwnershipClaim {
|
||||
file_path: filepath.clone(),
|
||||
hunks: vec![Hunk::from(&hunk).with_hash(Hunk::hash_diff(&hunk.diff_lines))],
|
||||
});
|
||||
|
||||
diffs_by_branch
|
||||
.entry(virtual_branches[vbranch_pos].id)
|
||||
.or_default()
|
||||
.entry(filepath.clone())
|
||||
.or_default()
|
||||
.push(hunk);
|
||||
}
|
||||
}
|
||||
|
||||
let mut hunks_by_branch = diffs_by_branch
|
||||
.into_iter()
|
||||
.map(|(branch_id, hunks)| {
|
||||
(
|
||||
virtual_branches
|
||||
.iter()
|
||||
.find(|b| b.id.eq(&branch_id))
|
||||
.unwrap()
|
||||
.clone(),
|
||||
hunks,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// write updated state if not resolving
|
||||
if !project_repository.is_resolving() {
|
||||
let vb_state = project_repository.project().virtual_branches();
|
||||
for (vbranch, files) in &mut hunks_by_branch {
|
||||
vbranch.tree = write_tree(project_repository, &vbranch.head, files)?;
|
||||
vb_state
|
||||
.set_branch(vbranch.clone())
|
||||
.context(format!("failed to write virtual branch {}", vbranch.name))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((hunks_by_branch, skipped_files, locks))
|
||||
}
|
||||
|
||||
/// NOTE: There is no use returning an iterator here as this acts like the final product.
|
||||
fn virtual_hunks_into_virtual_files(
|
||||
project_repository: &ProjectRepository,
|
||||
|
Loading…
Reference in New Issue
Block a user