mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-24 01:51:57 +03:00
Enforce locking by making locks part of the public oplog
API.
This way, all methods that care about the `oplog` also have to care about choosing the right lock.
This commit is contained in:
parent
09ca2d0284
commit
822fd92b9d
@ -40,7 +40,10 @@ impl VirtualBranchActions {
|
||||
run_hooks: bool,
|
||||
) -> Result<git2::Oid> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let snapshot_tree = project_repository.project().prepare_snapshot();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let snapshot_tree = project_repository
|
||||
.project()
|
||||
.prepare_snapshot(guard.read_permission());
|
||||
let result = branch::commit(
|
||||
&project_repository,
|
||||
branch_id,
|
||||
@ -55,6 +58,7 @@ impl VirtualBranchActions {
|
||||
result.as_ref().err(),
|
||||
message.to_owned(),
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)
|
||||
});
|
||||
result
|
||||
@ -73,7 +77,11 @@ impl VirtualBranchActions {
|
||||
&self,
|
||||
project: &Project,
|
||||
) -> Result<(Vec<branch::VirtualBranch>, Vec<diff::FileDiff>)> {
|
||||
branch::list_virtual_branches(&open_with_verify(project)?).map_err(Into::into)
|
||||
branch::list_virtual_branches(
|
||||
&open_with_verify(project)?,
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn create_virtual_branch(
|
||||
@ -82,8 +90,11 @@ impl VirtualBranchActions {
|
||||
create: &BranchCreateRequest,
|
||||
) -> Result<BranchId> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch_id = branch_manager.create_virtual_branch(create)?.id;
|
||||
let branch_id = branch_manager
|
||||
.create_virtual_branch(create, guard.write_permission())?
|
||||
.id;
|
||||
Ok(branch_id)
|
||||
}
|
||||
|
||||
@ -109,9 +120,11 @@ impl VirtualBranchActions {
|
||||
target_branch: &RemoteRefname,
|
||||
) -> Result<BaseBranch> {
|
||||
let project_repository = ProjectRepository::open(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::SetBaseBranch));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::SetBaseBranch),
|
||||
guard.write_permission(),
|
||||
);
|
||||
set_base_branch(&project_repository, target_branch)
|
||||
}
|
||||
|
||||
@ -126,18 +139,22 @@ impl VirtualBranchActions {
|
||||
branch_id: BranchId,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::MergeUpstream));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::MergeUpstream),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::integrate_upstream_commits(&project_repository, branch_id).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn update_base_branch(&self, project: &Project) -> Result<Vec<ReferenceName>> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::UpdateWorkspaceBase));
|
||||
update_base_branch(&project_repository).map_err(Into::into)
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::UpdateWorkspaceBase),
|
||||
guard.write_permission(),
|
||||
);
|
||||
update_base_branch(&project_repository, guard.write_permission()).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn update_virtual_branch(
|
||||
@ -146,7 +163,10 @@ impl VirtualBranchActions {
|
||||
branch_update: BranchUpdateRequest,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let snapshot_tree = project_repository.project().prepare_snapshot();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let snapshot_tree = project_repository
|
||||
.project()
|
||||
.prepare_snapshot(guard.read_permission());
|
||||
let old_branch = project_repository
|
||||
.project()
|
||||
.virtual_branches()
|
||||
@ -158,6 +178,7 @@ impl VirtualBranchActions {
|
||||
&old_branch,
|
||||
&branch_update,
|
||||
result.as_ref().err(),
|
||||
guard.write_permission(),
|
||||
)
|
||||
});
|
||||
result?;
|
||||
@ -170,7 +191,8 @@ impl VirtualBranchActions {
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
branch_manager.delete_branch(branch_id)
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
branch_manager.delete_branch(branch_id, guard.write_permission())
|
||||
}
|
||||
|
||||
pub async fn unapply_ownership(
|
||||
@ -179,17 +201,22 @@ impl VirtualBranchActions {
|
||||
ownership: &BranchOwnershipClaims,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::DiscardHunk));
|
||||
branch::unapply_ownership(&project_repository, ownership).map_err(Into::into)
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::DiscardHunk),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::unapply_ownership(&project_repository, ownership, guard.write_permission())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn reset_files(&self, project: &Project, files: &Vec<String>) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::DiscardFile));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::DiscardFile),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::reset_files(&project_repository, files).map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -201,10 +228,18 @@ impl VirtualBranchActions {
|
||||
ownership: &BranchOwnershipClaims,
|
||||
) -> Result<git2::Oid> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::AmendCommit));
|
||||
branch::amend(&project_repository, branch_id, commit_oid, ownership)
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::AmendCommit),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::amend(
|
||||
&project_repository,
|
||||
branch_id,
|
||||
commit_oid,
|
||||
ownership,
|
||||
guard.write_permission(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn move_commit_file(
|
||||
@ -216,9 +251,11 @@ impl VirtualBranchActions {
|
||||
ownership: &BranchOwnershipClaims,
|
||||
) -> Result<git2::Oid> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::MoveCommitFile));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::MoveCommitFile),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::move_commit_file(
|
||||
&project_repository,
|
||||
branch_id,
|
||||
@ -236,7 +273,10 @@ impl VirtualBranchActions {
|
||||
commit_oid: git2::Oid,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let snapshot_tree = project_repository.project().prepare_snapshot();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let snapshot_tree = project_repository
|
||||
.project()
|
||||
.prepare_snapshot(guard.read_permission());
|
||||
let result: Result<()> =
|
||||
branch::undo_commit(&project_repository, branch_id, commit_oid).map_err(Into::into);
|
||||
let _ = snapshot_tree.and_then(|snapshot_tree| {
|
||||
@ -244,6 +284,7 @@ impl VirtualBranchActions {
|
||||
snapshot_tree,
|
||||
result.as_ref(),
|
||||
commit_oid,
|
||||
guard.write_permission(),
|
||||
)
|
||||
});
|
||||
result
|
||||
@ -257,9 +298,11 @@ impl VirtualBranchActions {
|
||||
offset: i32,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::InsertBlankCommit));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::InsertBlankCommit),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::insert_blank_commit(&project_repository, branch_id, commit_oid, offset)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -272,9 +315,11 @@ impl VirtualBranchActions {
|
||||
offset: i32,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::ReorderCommit));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::ReorderCommit),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::reorder_commit(&project_repository, branch_id, commit_oid, offset)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -286,9 +331,11 @@ impl VirtualBranchActions {
|
||||
target_commit_oid: git2::Oid,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::UndoCommit));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::UndoCommit),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::reset_branch(&project_repository, branch_id, target_commit_oid).map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -299,14 +346,23 @@ impl VirtualBranchActions {
|
||||
name_conflict_resolution: branch::NameConflictResolution,
|
||||
) -> Result<ReferenceName> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let snapshot_tree = project_repository.project().prepare_snapshot();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let snapshot_tree = project_repository
|
||||
.project()
|
||||
.prepare_snapshot(guard.read_permission());
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let result = branch_manager.convert_to_real_branch(branch_id, name_conflict_resolution);
|
||||
let result = branch_manager.convert_to_real_branch(
|
||||
branch_id,
|
||||
name_conflict_resolution,
|
||||
guard.write_permission(),
|
||||
);
|
||||
|
||||
let _ = snapshot_tree.and_then(|snapshot_tree| {
|
||||
project_repository
|
||||
.project()
|
||||
.snapshot_branch_unapplied(snapshot_tree, result.as_ref())
|
||||
project_repository.project().snapshot_branch_unapplied(
|
||||
snapshot_tree,
|
||||
result.as_ref(),
|
||||
guard.write_permission(),
|
||||
)
|
||||
});
|
||||
|
||||
result
|
||||
@ -345,9 +401,11 @@ impl VirtualBranchActions {
|
||||
commit_oid: git2::Oid,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::SquashCommit));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::SquashCommit),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::squash(&project_repository, branch_id, commit_oid).map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -359,9 +417,11 @@ impl VirtualBranchActions {
|
||||
message: &str,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::UpdateCommitMessage));
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::UpdateCommitMessage),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::update_commit_message(&project_repository, branch_id, commit_oid, message)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -408,10 +468,18 @@ impl VirtualBranchActions {
|
||||
commit_oid: git2::Oid,
|
||||
) -> Result<()> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let _ = project_repository
|
||||
.project()
|
||||
.create_snapshot(SnapshotDetails::new(OperationKind::MoveCommit));
|
||||
branch::move_commit(&project_repository, target_branch_id, commit_oid).map_err(Into::into)
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let _ = project_repository.project().create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::MoveCommit),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch::move_commit(
|
||||
&project_repository,
|
||||
target_branch_id,
|
||||
commit_oid,
|
||||
guard.write_permission(),
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn create_virtual_branch_from_branch(
|
||||
@ -421,14 +489,16 @@ impl VirtualBranchActions {
|
||||
) -> Result<BranchId> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(branch)
|
||||
.create_virtual_branch_from_branch(branch, guard.write_permission())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn open_with_verify(project: &Project) -> Result<ProjectRepository> {
|
||||
let project_repository = ProjectRepository::open(project)?;
|
||||
crate::integration::verify_branch(&project_repository)?;
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
crate::integration::verify_branch(&project_repository, guard.write_permission())?;
|
||||
Ok(project_repository)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ use crate::remote::{commit_to_remote_commit, RemoteCommit};
|
||||
use crate::{VirtualBranchHunk, VirtualBranchesExt};
|
||||
use gitbutler_branch::GITBUTLER_INTEGRATION_REFERENCE;
|
||||
use gitbutler_error::error::Marker;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_repo::rebase::cherry_rebase;
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||
@ -329,6 +330,7 @@ fn _print_tree(repo: &git2::Repository, tree: &git2::Tree) -> Result<()> {
|
||||
// update the target sha
|
||||
pub(crate) fn update_base_branch(
|
||||
project_repository: &ProjectRepository,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<Vec<ReferenceName>> {
|
||||
project_repository.assure_resolved()?;
|
||||
|
||||
@ -418,8 +420,11 @@ pub(crate) fn update_base_branch(
|
||||
if branch_tree_merge_index.has_conflicts() {
|
||||
// branch tree conflicts with new target, unapply branch for now. we'll handle it later, when user applies it back.
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let unapplied_real_branch =
|
||||
branch_manager.convert_to_real_branch(branch.id, Default::default())?;
|
||||
let unapplied_real_branch = branch_manager.convert_to_real_branch(
|
||||
branch.id,
|
||||
Default::default(),
|
||||
perm,
|
||||
)?;
|
||||
|
||||
unapplied_branch_names.push(unapplied_real_branch);
|
||||
|
||||
@ -452,8 +457,11 @@ pub(crate) fn update_base_branch(
|
||||
// branch commits conflict with new target, make sure the branch is
|
||||
// unapplied. conflicts witll be dealt with when applying it back.
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let unapplied_real_branch =
|
||||
branch_manager.convert_to_real_branch(branch.id, Default::default())?;
|
||||
let unapplied_real_branch = branch_manager.convert_to_real_branch(
|
||||
branch.id,
|
||||
Default::default(),
|
||||
perm,
|
||||
)?;
|
||||
unapplied_branch_names.push(unapplied_real_branch);
|
||||
|
||||
return Ok(None);
|
||||
|
@ -12,14 +12,18 @@ use gitbutler_branch::{
|
||||
use gitbutler_commit::commit_headers::HasCommitHeaders;
|
||||
use gitbutler_error::error::Marker;
|
||||
use gitbutler_oplog::SnapshotExt;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_reference::Refname;
|
||||
use gitbutler_repo::{rebase::cherry_rebase, RepoActionsExt, RepositoryExt};
|
||||
use gitbutler_time::time::now_since_unix_epoch_ms;
|
||||
|
||||
impl BranchManager<'_> {
|
||||
pub fn create_virtual_branch(&self, create: &BranchCreateRequest) -> Result<Branch> {
|
||||
pub fn create_virtual_branch(
|
||||
&self,
|
||||
create: &BranchCreateRequest,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<Branch> {
|
||||
let vb_state = self.project_repository.project().virtual_branches();
|
||||
|
||||
let default_target = vb_state.get_default_target()?;
|
||||
|
||||
let commit = self
|
||||
@ -50,7 +54,7 @@ impl BranchManager<'_> {
|
||||
_ = self
|
||||
.project_repository
|
||||
.project()
|
||||
.snapshot_branch_creation(name.clone());
|
||||
.snapshot_branch_creation(name.clone(), perm);
|
||||
|
||||
all_virtual_branches.sort_by_key(|branch| branch.order);
|
||||
|
||||
@ -118,7 +122,11 @@ impl BranchManager<'_> {
|
||||
Ok(branch)
|
||||
}
|
||||
|
||||
pub fn create_virtual_branch_from_branch(&self, upstream: &Refname) -> Result<BranchId> {
|
||||
pub fn create_virtual_branch_from_branch(
|
||||
&self,
|
||||
upstream: &Refname,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<BranchId> {
|
||||
// only set upstream if it's not the default target
|
||||
let upstream_branch = match upstream {
|
||||
Refname::Other(_) | Refname::Virtual(_) => {
|
||||
@ -137,7 +145,7 @@ impl BranchManager<'_> {
|
||||
let _ = self
|
||||
.project_repository
|
||||
.project()
|
||||
.snapshot_branch_creation(branch_name.clone());
|
||||
.snapshot_branch_creation(branch_name.clone(), perm);
|
||||
|
||||
let vb_state = self.project_repository.project().virtual_branches();
|
||||
|
||||
@ -249,7 +257,7 @@ impl BranchManager<'_> {
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
self.project_repository.add_branch_reference(&branch)?;
|
||||
|
||||
match self.apply_branch(branch.id) {
|
||||
match self.apply_branch(branch.id, perm) {
|
||||
Ok(_) => Ok(branch.id),
|
||||
Err(err)
|
||||
if err
|
||||
@ -266,7 +274,11 @@ impl BranchManager<'_> {
|
||||
|
||||
/// Holding private methods associated to branch creation
|
||||
impl BranchManager<'_> {
|
||||
fn apply_branch(&self, branch_id: BranchId) -> Result<String> {
|
||||
fn apply_branch(
|
||||
&self,
|
||||
branch_id: BranchId,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<String> {
|
||||
self.project_repository.assure_resolved()?;
|
||||
self.project_repository.assure_unconflicted()?;
|
||||
let repo = self.project_repository.repo();
|
||||
@ -313,7 +325,7 @@ impl BranchManager<'_> {
|
||||
.iter()
|
||||
.filter(|branch| branch.id != branch_id)
|
||||
{
|
||||
self.convert_to_real_branch(branch.id, Default::default())?;
|
||||
self.convert_to_real_branch(branch.id, Default::default(), perm)?;
|
||||
}
|
||||
|
||||
// apply the branch
|
||||
|
@ -9,6 +9,7 @@ use git2::build::TreeUpdateBuilder;
|
||||
use gitbutler_branch::{Branch, BranchExt, BranchId};
|
||||
use gitbutler_commit::commit_headers::CommitHeadersV2;
|
||||
use gitbutler_oplog::SnapshotExt;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_reference::ReferenceName;
|
||||
use gitbutler_reference::{normalize_branch_name, Refname};
|
||||
use gitbutler_repo::{RepoActionsExt, RepositoryExt};
|
||||
@ -21,6 +22,7 @@ impl BranchManager<'_> {
|
||||
&self,
|
||||
branch_id: BranchId,
|
||||
name_conflict_resolution: NameConflictResolution,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<ReferenceName> {
|
||||
let vb_state = self.project_repository.project().virtual_branches();
|
||||
|
||||
@ -29,7 +31,7 @@ impl BranchManager<'_> {
|
||||
// Convert the vbranch to a real branch
|
||||
let real_branch = self.build_real_branch(&mut target_branch, name_conflict_resolution)?;
|
||||
|
||||
self.delete_branch(branch_id)?;
|
||||
self.delete_branch(branch_id, perm)?;
|
||||
|
||||
// If we were conflicting, it means that it was the only branch applied. Since we've now unapplied it we can clear all conflicts
|
||||
if conflicts::is_conflicting(self.project_repository, None)? {
|
||||
@ -46,7 +48,11 @@ impl BranchManager<'_> {
|
||||
real_branch.reference_name()
|
||||
}
|
||||
|
||||
pub(crate) fn delete_branch(&self, branch_id: BranchId) -> Result<()> {
|
||||
pub(crate) fn delete_branch(
|
||||
&self,
|
||||
branch_id: BranchId,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<()> {
|
||||
let vb_state = self.project_repository.project().virtual_branches();
|
||||
let Some(branch) = vb_state.try_branch(branch_id)? else {
|
||||
return Ok(());
|
||||
@ -60,7 +66,7 @@ impl BranchManager<'_> {
|
||||
_ = self
|
||||
.project_repository
|
||||
.project()
|
||||
.snapshot_branch_deletion(branch.name.clone());
|
||||
.snapshot_branch_deletion(branch.name.clone(), perm);
|
||||
|
||||
let repo = self.project_repository.repo();
|
||||
|
||||
@ -76,6 +82,7 @@ impl BranchManager<'_> {
|
||||
self.project_repository,
|
||||
&integration_commit.id(),
|
||||
virtual_branches,
|
||||
Some(perm),
|
||||
)
|
||||
.context("failed to get status by branch")?;
|
||||
|
||||
|
@ -12,6 +12,7 @@ use gitbutler_branch::{
|
||||
use gitbutler_command_context::ProjectRepository;
|
||||
use gitbutler_commit::commit_ext::CommitExt;
|
||||
use gitbutler_error::error::Marker;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_repo::{LogUntil, RepoActionsExt, RepositoryExt};
|
||||
|
||||
use crate::branch_manager::BranchManagerExt;
|
||||
@ -281,17 +282,17 @@ pub fn update_gitbutler_integration(
|
||||
Ok(final_commit)
|
||||
}
|
||||
|
||||
pub fn verify_branch(ctx: &ProjectRepository) -> Result<()> {
|
||||
pub fn verify_branch(ctx: &ProjectRepository, perm: &mut WorktreeWritePermission) -> Result<()> {
|
||||
verify_current_branch_name(ctx)
|
||||
.and_then(verify_head_is_set)
|
||||
.and_then(verify_head_is_clean)
|
||||
.and_then(|()| verify_head_is_clean(ctx, perm))
|
||||
.context(Marker::VerificationFailure)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_head_is_set(ctx: &ProjectRepository) -> Result<&ProjectRepository> {
|
||||
fn verify_head_is_set(ctx: &ProjectRepository) -> Result<()> {
|
||||
match ctx.repo().head().context("failed to get head")?.name() {
|
||||
Some(refname) if *refname == GITBUTLER_INTEGRATION_REFERENCE.to_string() => Ok(ctx),
|
||||
Some(refname) if *refname == GITBUTLER_INTEGRATION_REFERENCE.to_string() => Ok(()),
|
||||
Some(head_name) => Err(invalid_head_err(head_name)),
|
||||
None => Err(anyhow!(
|
||||
"project in detached head state. Please checkout {} to continue",
|
||||
@ -314,7 +315,8 @@ fn verify_current_branch_name(ctx: &ProjectRepository) -> Result<&ProjectReposit
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_head_is_clean(ctx: &ProjectRepository) -> Result<&ProjectRepository> {
|
||||
// TODO(ST): Probably there should not be an implicit vbranch creation here.
|
||||
fn verify_head_is_clean(ctx: &ProjectRepository, perm: &mut WorktreeWritePermission) -> Result<()> {
|
||||
let head_commit = ctx
|
||||
.repo()
|
||||
.head()
|
||||
@ -340,7 +342,7 @@ fn verify_head_is_clean(ctx: &ProjectRepository) -> Result<&ProjectRepository> {
|
||||
|
||||
if extra_commits.is_empty() {
|
||||
// no extra commits found, so we're good
|
||||
return Ok(ctx);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ctx.repo()
|
||||
@ -353,12 +355,15 @@ fn verify_head_is_clean(ctx: &ProjectRepository) -> Result<&ProjectRepository> {
|
||||
|
||||
let branch_manager = ctx.branch_manager();
|
||||
let mut new_branch = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest {
|
||||
name: extra_commits
|
||||
.last()
|
||||
.map(|commit| commit.message_bstr().to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest {
|
||||
name: extra_commits
|
||||
.last()
|
||||
.map(|commit| commit.message_bstr().to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
perm,
|
||||
)
|
||||
.context("failed to create virtual branch")?;
|
||||
|
||||
// rebasing the extra commits onto the new branch
|
||||
@ -400,7 +405,7 @@ fn verify_head_is_clean(ctx: &ProjectRepository) -> Result<&ProjectRepository> {
|
||||
|
||||
head = rebased_commit.id();
|
||||
}
|
||||
Ok(ctx)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn invalid_head_err(head_name: &str) -> anyhow::Error {
|
||||
|
@ -35,6 +35,7 @@ use crate::remote::{branch_to_remote_branch, RemoteBranch};
|
||||
use crate::VirtualBranchesExt;
|
||||
use gitbutler_error::error::Code;
|
||||
use gitbutler_error::error::Marker;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_repo::rebase::{cherry_rebase, cherry_rebase_group};
|
||||
use gitbutler_time::time::now_since_unix_epoch_ms;
|
||||
|
||||
@ -200,6 +201,7 @@ pub enum NameConflictResolution {
|
||||
pub fn unapply_ownership(
|
||||
project_repository: &ProjectRepository,
|
||||
ownership: &BranchOwnershipClaims,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<()> {
|
||||
project_repository.assure_resolved()?;
|
||||
|
||||
@ -211,9 +213,13 @@ pub fn unapply_ownership(
|
||||
|
||||
let integration_commit_id = get_workspace_head(&vb_state, project_repository)?;
|
||||
|
||||
let (applied_statuses, _) =
|
||||
get_applied_status(project_repository, &integration_commit_id, virtual_branches)
|
||||
.context("failed to get status by branch")?;
|
||||
let (applied_statuses, _) = get_applied_status(
|
||||
project_repository,
|
||||
&integration_commit_id,
|
||||
virtual_branches,
|
||||
Some(perm),
|
||||
)
|
||||
.context("failed to get status by branch")?;
|
||||
|
||||
let hunks_to_unapply = applied_statuses
|
||||
.iter()
|
||||
@ -346,6 +352,7 @@ fn find_base_tree<'a>(
|
||||
fn resolve_old_applied_state(
|
||||
project_repository: &ProjectRepository,
|
||||
vb_state: &VirtualBranchesHandle,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<()> {
|
||||
let branches = vb_state.list_all_branches()?;
|
||||
|
||||
@ -353,7 +360,7 @@ fn resolve_old_applied_state(
|
||||
|
||||
for mut branch in branches {
|
||||
if branch.is_old_unapplied() {
|
||||
branch_manager.convert_to_real_branch(branch.id, Default::default())?;
|
||||
branch_manager.convert_to_real_branch(branch.id, Default::default(), perm)?;
|
||||
} else {
|
||||
branch.applied = branch.in_workspace;
|
||||
vb_state.set_branch(branch)?;
|
||||
@ -365,12 +372,15 @@ fn resolve_old_applied_state(
|
||||
|
||||
pub fn list_virtual_branches(
|
||||
ctx: &ProjectRepository,
|
||||
// TODO(ST): this should really only shared access, but there is some internals
|
||||
// that conditionally write things.
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<(Vec<VirtualBranch>, Vec<diff::FileDiff>)> {
|
||||
let mut branches: Vec<VirtualBranch> = Vec::new();
|
||||
|
||||
let vb_state = ctx.project().virtual_branches();
|
||||
|
||||
resolve_old_applied_state(ctx, &vb_state)?;
|
||||
resolve_old_applied_state(ctx, &vb_state, perm)?;
|
||||
|
||||
let default_target = vb_state
|
||||
.get_default_target()
|
||||
@ -1045,6 +1055,7 @@ pub fn get_status_by_branch(
|
||||
// TODO: Keep this optional or update lots of tests?
|
||||
integration_commit.unwrap_or(&default_target.sha),
|
||||
virtual_branches,
|
||||
None,
|
||||
)?;
|
||||
|
||||
Ok((applied_status, skipped_files))
|
||||
@ -1128,6 +1139,7 @@ pub(crate) fn get_applied_status(
|
||||
project_repository: &ProjectRepository,
|
||||
integration_commit: &git2::Oid,
|
||||
mut virtual_branches: Vec<Branch>,
|
||||
perm: Option<&mut WorktreeWritePermission>,
|
||||
) -> Result<(AppliedStatuses, Vec<diff::FileDiff>)> {
|
||||
let base_file_diffs = diff::workdir(project_repository.repo(), &integration_commit.to_owned())
|
||||
.context("failed to diff workdir")?;
|
||||
@ -1146,9 +1158,13 @@ pub(crate) fn get_applied_status(
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
|
||||
if virtual_branches.is_empty() && !base_diffs.is_empty() {
|
||||
virtual_branches = vec![branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.context("failed to create default branch")?];
|
||||
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 wanted to create a virtual branch but wasn't allowed to make changes")
|
||||
}
|
||||
}
|
||||
|
||||
let mut diffs_by_branch: HashMap<BranchId, BranchStatus> = virtual_branches
|
||||
@ -2115,6 +2131,7 @@ pub(crate) fn amend(
|
||||
branch_id: BranchId,
|
||||
commit_oid: git2::Oid,
|
||||
target_ownership: &BranchOwnershipClaims,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<git2::Oid> {
|
||||
project_repository.assure_resolved()?;
|
||||
let vb_state = project_repository.project().virtual_branches();
|
||||
@ -2129,11 +2146,14 @@ pub(crate) fn amend(
|
||||
|
||||
let default_target = vb_state.get_default_target()?;
|
||||
|
||||
let integration_commit_id =
|
||||
crate::integration::get_workspace_head(&vb_state, project_repository)?;
|
||||
let integration_commit_id = get_workspace_head(&vb_state, project_repository)?;
|
||||
|
||||
let (mut applied_statuses, _) =
|
||||
get_applied_status(project_repository, &integration_commit_id, virtual_branches)?;
|
||||
let (mut applied_statuses, _) = get_applied_status(
|
||||
project_repository,
|
||||
&integration_commit_id,
|
||||
virtual_branches,
|
||||
Some(perm),
|
||||
)?;
|
||||
|
||||
let (ref mut target_branch, target_status) = applied_statuses
|
||||
.iter_mut()
|
||||
@ -2601,6 +2621,7 @@ pub(crate) fn move_commit(
|
||||
project_repository: &ProjectRepository,
|
||||
target_branch_id: BranchId,
|
||||
commit_id: git2::Oid,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<()> {
|
||||
project_repository.assure_resolved()?;
|
||||
let vb_state = project_repository.project().virtual_branches();
|
||||
@ -2613,11 +2634,14 @@ pub(crate) fn move_commit(
|
||||
bail!("branch {target_branch_id} is not among applied branches")
|
||||
}
|
||||
|
||||
let integration_commit_id =
|
||||
crate::integration::get_workspace_head(&vb_state, project_repository)?;
|
||||
let integration_commit_id = get_workspace_head(&vb_state, project_repository)?;
|
||||
|
||||
let (mut applied_statuses, _) =
|
||||
get_applied_status(project_repository, &integration_commit_id, applied_branches)?;
|
||||
let (mut applied_statuses, _) = get_applied_status(
|
||||
project_repository,
|
||||
&integration_commit_id,
|
||||
applied_branches,
|
||||
Some(perm),
|
||||
)?;
|
||||
|
||||
let (ref mut source_branch, source_status) = applied_statuses
|
||||
.iter_mut()
|
||||
|
@ -43,9 +43,10 @@ fn commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
||||
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = project_repository
|
||||
.branch_manager()
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -54,7 +55,7 @@ fn commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
||||
"line0\nline1\nline2\nline3\nline4\n",
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches[0];
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
assert_eq!(branch.commits.len(), 0);
|
||||
@ -63,7 +64,7 @@ fn commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
||||
commit(project_repository, branch1_id, "test commit", None, false)?;
|
||||
|
||||
// status (no files)
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches[0];
|
||||
assert_eq!(branch.files.len(), 0);
|
||||
assert_eq!(branch.commits.len(), 1);
|
||||
@ -74,7 +75,7 @@ fn commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
||||
)?;
|
||||
|
||||
// should have just the last change now, the other line is committed
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches[0];
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
assert_eq!(branch.commits.len(), 1);
|
||||
@ -114,9 +115,10 @@ fn track_binary_files() -> Result<()> {
|
||||
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = project_repository
|
||||
.branch_manager()
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -136,7 +138,7 @@ fn track_binary_files() -> Result<()> {
|
||||
let mut file = std::fs::File::create(Path::new(&project.path).join("image.bin"))?;
|
||||
file.write_all(&image_data)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches[0];
|
||||
assert_eq!(branch.files.len(), 2);
|
||||
let img_file = &branch
|
||||
@ -156,7 +158,8 @@ fn track_binary_files() -> Result<()> {
|
||||
commit(project_repository, branch1_id, "test commit", None, false)?;
|
||||
|
||||
// status (no files)
|
||||
let (branches, _) = list_virtual_branches(project_repository).unwrap();
|
||||
let (branches, _) =
|
||||
list_virtual_branches(project_repository, guard.write_permission()).unwrap();
|
||||
let commit_id = &branches[0].commits[0].id;
|
||||
let commit_obj = project_repository
|
||||
.repo()
|
||||
@ -181,7 +184,8 @@ fn track_binary_files() -> Result<()> {
|
||||
// commit
|
||||
commit(project_repository, branch1_id, "test commit", None, false)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository).unwrap();
|
||||
let (branches, _) =
|
||||
list_virtual_branches(project_repository, guard.write_permission()).unwrap();
|
||||
let commit_id = &branches[0].commits[0].id;
|
||||
// get tree from commit_id
|
||||
let commit_obj = project_repository
|
||||
@ -212,7 +216,10 @@ fn create_branch_with_ownership() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch0 = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
get_status_by_branch(project_repository, None).expect("failed to get status");
|
||||
@ -221,10 +228,13 @@ fn create_branch_with_ownership() -> Result<()> {
|
||||
let branch0 = vb_state.get_branch_in_workspace(branch0.id).unwrap();
|
||||
|
||||
let branch1 = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest {
|
||||
ownership: Some(branch0.ownership),
|
||||
..Default::default()
|
||||
})
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest {
|
||||
ownership: Some(branch0.ownership),
|
||||
..Default::default()
|
||||
},
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
let statuses = get_status_by_branch(project_repository, None)
|
||||
@ -247,23 +257,34 @@ fn create_branch_with_ownership() -> Result<()> {
|
||||
fn create_branch_in_the_middle() -> Result<()> {
|
||||
let suite = Suite::default();
|
||||
let Case {
|
||||
project_repository, ..
|
||||
project_repository,
|
||||
project,
|
||||
..
|
||||
} = &suite.new_case();
|
||||
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest {
|
||||
order: Some(1),
|
||||
..Default::default()
|
||||
})
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest {
|
||||
order: Some(1),
|
||||
..Default::default()
|
||||
},
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
let vb_state = VirtualBranchesHandle::new(project_repository.project().gb_dir());
|
||||
@ -283,14 +304,19 @@ fn create_branch_in_the_middle() -> Result<()> {
|
||||
fn create_branch_no_arguments() -> Result<()> {
|
||||
let suite = Suite::default();
|
||||
let Case {
|
||||
project_repository, ..
|
||||
project_repository,
|
||||
project,
|
||||
..
|
||||
} = &suite.new_case();
|
||||
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
let vb_state = VirtualBranchesHandle::new(project_repository.project().gb_dir());
|
||||
@ -321,11 +347,17 @@ fn hunk_expantion() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -415,11 +447,17 @@ fn get_status_files_by_branch() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -454,15 +492,24 @@ fn move_hunks_multiple_sources() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch3_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -557,12 +604,18 @@ fn move_hunks_partial_explicitly() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -642,7 +695,10 @@ fn add_new_hunk_to_the_end() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
let statuses = get_status_by_branch(project_repository, None)
|
||||
@ -813,15 +869,16 @@ fn merge_vbranch_upstream_clean_rebase() -> Result<()> {
|
||||
|
||||
let remote_branch: RemoteRefname = "refs/remotes/origin/master".parse().unwrap();
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let mut branch = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch");
|
||||
branch.upstream = Some(remote_branch.clone());
|
||||
branch.head = last_push;
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
// create the branch
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches[0];
|
||||
assert_eq!(branch1.files.len(), 1);
|
||||
assert_eq!(branch1.commits.len(), 1);
|
||||
@ -829,7 +886,7 @@ fn merge_vbranch_upstream_clean_rebase() -> Result<()> {
|
||||
|
||||
integrate_upstream_commits(project_repository, branch1.id)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches[0];
|
||||
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||
@ -912,8 +969,9 @@ async fn merge_vbranch_upstream_conflict() -> Result<()> {
|
||||
|
||||
let remote_branch: RemoteRefname = "refs/remotes/origin/master".parse().unwrap();
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let mut branch = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch");
|
||||
branch.upstream = Some(remote_branch.clone());
|
||||
branch.head = last_push;
|
||||
@ -930,7 +988,7 @@ async fn merge_vbranch_upstream_conflict() -> Result<()> {
|
||||
.unwrap();
|
||||
|
||||
// create the branch
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches[0];
|
||||
|
||||
assert_eq!(branch1.files.len(), 1);
|
||||
@ -939,7 +997,7 @@ async fn merge_vbranch_upstream_conflict() -> Result<()> {
|
||||
|
||||
integrate_upstream_commits(project_repository, branch1.id)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches[0];
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||
|
||||
@ -959,7 +1017,7 @@ async fn merge_vbranch_upstream_conflict() -> Result<()> {
|
||||
)?;
|
||||
|
||||
// make gb see the conflict resolution
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
assert!(branches[0].conflicted);
|
||||
|
||||
// commit the merge resolution
|
||||
@ -971,7 +1029,7 @@ async fn merge_vbranch_upstream_conflict() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches[0];
|
||||
assert!(!branch1.conflicted);
|
||||
assert_eq!(branch1.files.len(), 0);
|
||||
@ -1005,11 +1063,12 @@ fn unapply_ownership_partial() -> Result<()> {
|
||||
)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
assert_eq!(branches.len(), 1);
|
||||
assert_eq!(branches[0].files.len(), 1);
|
||||
assert_eq!(branches[0].ownership.claims.len(), 1);
|
||||
@ -1020,9 +1079,14 @@ fn unapply_ownership_partial() -> Result<()> {
|
||||
"line1\nline2\nline3\nline4\nbranch1\n"
|
||||
);
|
||||
|
||||
unapply_ownership(project_repository, &"test.txt:2-6".parse().unwrap()).unwrap();
|
||||
unapply_ownership(
|
||||
project_repository,
|
||||
&"test.txt:2-6".parse().unwrap(),
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
assert_eq!(branches.len(), 1);
|
||||
assert_eq!(branches[0].files.len(), 0);
|
||||
assert_eq!(branches[0].ownership.claims.len(), 0);
|
||||
@ -1061,12 +1125,13 @@ fn unapply_branch() -> Result<()> {
|
||||
std::fs::write(Path::new(&project.path).join(file_path2), "line5\nline6\n")?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1087,25 +1152,31 @@ fn unapply_branch() -> Result<()> {
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path2))?;
|
||||
assert_eq!("line5\nline6\n", String::from_utf8(contents)?);
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
assert!(branch.active);
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let real_branch = branch_manager.convert_to_real_branch(branch1_id, Default::default())?;
|
||||
let real_branch = branch_manager.convert_to_real_branch(
|
||||
branch1_id,
|
||||
Default::default(),
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||
assert_eq!("line1\nline2\nline3\nline4\n", String::from_utf8(contents)?);
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path2))?;
|
||||
assert_eq!("line5\nline6\n", String::from_utf8(contents)?);
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
assert!(!branches.iter().any(|b| b.id == branch1_id));
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id =
|
||||
branch_manager.create_virtual_branch_from_branch(&Refname::from_str(&real_branch)?)?;
|
||||
let branch1_id = branch_manager.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch)?,
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||
assert_eq!(
|
||||
"line1\nline2\nline3\nline4\nbranch1\n",
|
||||
@ -1114,7 +1185,7 @@ fn unapply_branch() -> Result<()> {
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path2))?;
|
||||
assert_eq!("line5\nline6\n", String::from_utf8(contents)?);
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
// TODO: expect there to be 0 branches
|
||||
assert_eq!(branch.files.len(), 0);
|
||||
@ -1147,12 +1218,13 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
||||
std::fs::write(Path::new(&project.path).join(file_path3), "file3\n")?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch3_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1173,28 +1245,42 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
||||
},
|
||||
)?;
|
||||
|
||||
list_virtual_branches(project_repository).unwrap();
|
||||
list_virtual_branches(project_repository, guard.write_permission()).unwrap();
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let real_branch_2 = branch_manager.convert_to_real_branch(branch2_id, Default::default())?;
|
||||
let real_branch_2 = branch_manager.convert_to_real_branch(
|
||||
branch2_id,
|
||||
Default::default(),
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
|
||||
// check that file2 is back
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path2))?;
|
||||
assert_eq!("file2\n", String::from_utf8(contents)?);
|
||||
|
||||
let real_branch_3 = branch_manager.convert_to_real_branch(branch3_id, Default::default())?;
|
||||
let real_branch_3 = branch_manager.convert_to_real_branch(
|
||||
branch3_id,
|
||||
Default::default(),
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
// check that file3 is gone
|
||||
assert!(!Path::new(&project.path).join(file_path3).exists());
|
||||
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(&Refname::from_str(&real_branch_2).unwrap())
|
||||
.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch_2).unwrap(),
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// check that file2 is gone
|
||||
assert!(!Path::new(&project.path).join(file_path2).exists());
|
||||
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(&Refname::from_str(&real_branch_3).unwrap())
|
||||
.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch_3).unwrap(),
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// check that file3 is back
|
||||
@ -1232,12 +1318,13 @@ fn detect_mergeable_branch() -> Result<()> {
|
||||
std::fs::write(Path::new(&project.path).join(file_path4), "line5\nline6\n")?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1253,8 +1340,16 @@ fn detect_mergeable_branch() -> Result<()> {
|
||||
|
||||
// unapply both branches and create some conflicting ones
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
branch_manager.convert_to_real_branch(branch1_id, Default::default())?;
|
||||
branch_manager.convert_to_real_branch(branch2_id, Default::default())?;
|
||||
branch_manager.convert_to_real_branch(
|
||||
branch1_id,
|
||||
Default::default(),
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
branch_manager.convert_to_real_branch(
|
||||
branch2_id,
|
||||
Default::default(),
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
|
||||
project_repository.repo().set_head("refs/heads/master")?;
|
||||
project_repository
|
||||
@ -1303,10 +1398,10 @@ fn detect_mergeable_branch() -> Result<()> {
|
||||
// create branches that conflict with our earlier branches
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch");
|
||||
let branch4_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1405,16 +1500,17 @@ fn upstream_integrated_vbranch() -> Result<()> {
|
||||
|
||||
// create vbranches, one integrated, one not
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch2_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
let branch3_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1474,7 +1570,7 @@ fn upstream_integrated_vbranch() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
|
||||
let branch1 = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
assert!(branch1.commits.iter().any(|c| c.is_integrated));
|
||||
@ -1509,8 +1605,9 @@ fn commit_same_hunk_twice() -> Result<()> {
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1519,7 +1616,7 @@ fn commit_same_hunk_twice() -> Result<()> {
|
||||
"line1\npatch1\nline2\nline3\nline4\nline5\nmiddle\nmiddle\nmiddle\nmiddle\nline6\nline7\nline8\nline9\nline10\nmiddle\nmiddle\nmiddle\nline11\nline12\n",
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
@ -1535,7 +1632,7 @@ fn commit_same_hunk_twice() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 0, "no files expected");
|
||||
@ -1555,7 +1652,7 @@ fn commit_same_hunk_twice() -> Result<()> {
|
||||
"line1\nPATCH1\nline2\nline3\nline4\nline5\nmiddle\nmiddle\nmiddle\nmiddle\nline6\nline7\nline8\nline9\nline10\nmiddle\nmiddle\nmiddle\nline11\nline12\n",
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 1, "one file should be changed");
|
||||
@ -1569,7 +1666,7 @@ fn commit_same_hunk_twice() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@ -1602,8 +1699,9 @@ fn commit_same_file_twice() -> Result<()> {
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1612,7 +1710,7 @@ fn commit_same_file_twice() -> Result<()> {
|
||||
"line1\npatch1\nline2\nline3\nline4\nline5\nmiddle\nmiddle\nmiddle\nmiddle\nline6\nline7\nline8\nline9\nline10\nmiddle\nmiddle\nmiddle\nline11\nline12\n",
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
@ -1628,7 +1726,7 @@ fn commit_same_file_twice() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 0, "no files expected");
|
||||
@ -1648,7 +1746,7 @@ fn commit_same_file_twice() -> Result<()> {
|
||||
"line1\npatch1\nline2\nline3\nline4\nline5\nmiddle\nmiddle\nmiddle\nmiddle\nline6\nline7\nline8\nline9\nline10\nmiddle\nmiddle\nmiddle\npatch2\nline11\nline12\n",
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 1, "one file should be changed");
|
||||
@ -1662,7 +1760,7 @@ fn commit_same_file_twice() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@ -1695,8 +1793,9 @@ fn commit_partial_by_hunk() -> Result<()> {
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1705,7 +1804,7 @@ fn commit_partial_by_hunk() -> Result<()> {
|
||||
"line1\npatch1\nline2\nline3\nline4\nline5\nmiddle\nmiddle\nmiddle\nmiddle\nline6\nline7\nline8\nline9\nline10\nmiddle\nmiddle\nmiddle\npatch2\nline11\nline12\n",
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
@ -1721,7 +1820,7 @@ fn commit_partial_by_hunk() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
@ -1738,7 +1837,7 @@ fn commit_partial_by_hunk() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
assert_eq!(branch.files.len(), 0);
|
||||
@ -1775,8 +1874,9 @@ fn commit_partial_by_file() -> Result<()> {
|
||||
std::fs::write(Path::new(&project.path).join(file_path3), "file3\n")?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1789,7 +1889,7 @@ fn commit_partial_by_file() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
// branch one test.txt has just the 1st and 3rd hunks applied
|
||||
@ -1835,8 +1935,9 @@ fn commit_add_and_delete_files() -> Result<()> {
|
||||
std::fs::write(Path::new(&project.path).join(file_path3), "file3\n")?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1849,7 +1950,7 @@ fn commit_add_and_delete_files() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
// branch one test.txt has just the 1st and 3rd hunks applied
|
||||
@ -1901,8 +2002,9 @@ fn commit_executable_and_symlinks() -> Result<()> {
|
||||
std::fs::set_permissions(&exec, new_permissions)?;
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -1915,7 +2017,7 @@ fn commit_executable_and_symlinks() -> Result<()> {
|
||||
false,
|
||||
)?;
|
||||
|
||||
let (branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (branches, _) = list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
let branch1 = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
|
||||
let commit = &branch1.commits[0].id;
|
||||
@ -2001,7 +2103,8 @@ fn verify_branch_commits_to_integration() -> Result<()> {
|
||||
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
verify_branch(project_repository).unwrap();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
verify_branch(project_repository, guard.write_permission()).unwrap();
|
||||
|
||||
// write two commits
|
||||
let file_path2 = Path::new("test2.txt");
|
||||
@ -2011,10 +2114,11 @@ fn verify_branch_commits_to_integration() -> Result<()> {
|
||||
commit_all(project_repository.repo());
|
||||
|
||||
// verify puts commits onto the virtual branch
|
||||
verify_branch(project_repository).unwrap();
|
||||
verify_branch(project_repository, guard.write_permission()).unwrap();
|
||||
|
||||
// one virtual branch with two commits was created
|
||||
let (virtual_branches, _) = list_virtual_branches(project_repository)?;
|
||||
let (virtual_branches, _) =
|
||||
list_virtual_branches(project_repository, guard.write_permission())?;
|
||||
assert_eq!(virtual_branches.len(), 1);
|
||||
|
||||
let branch = &virtual_branches.first().unwrap();
|
||||
@ -2028,16 +2132,19 @@ fn verify_branch_commits_to_integration() -> Result<()> {
|
||||
fn verify_branch_not_integration() -> Result<()> {
|
||||
let suite = Suite::default();
|
||||
let Case {
|
||||
project_repository, ..
|
||||
project_repository,
|
||||
project,
|
||||
..
|
||||
} = &suite.new_case();
|
||||
|
||||
set_test_target(project_repository)?;
|
||||
|
||||
verify_branch(project_repository).unwrap();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
verify_branch(project_repository, guard.write_permission()).unwrap();
|
||||
|
||||
project_repository.repo().set_head("refs/heads/master")?;
|
||||
|
||||
let verify_result = verify_branch(project_repository);
|
||||
let verify_result = verify_branch(project_repository, guard.write_permission());
|
||||
assert!(verify_result.is_err());
|
||||
assert_eq!(
|
||||
format!("{:#}", verify_result.unwrap_err()),
|
||||
@ -2063,7 +2170,10 @@ fn pre_commit_hook_rejection() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -2103,7 +2213,10 @@ fn post_commit_hook() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
@ -2154,7 +2267,10 @@ fn commit_msg_hook_rejection() -> Result<()> {
|
||||
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager
|
||||
.create_virtual_branch(&BranchCreateRequest::default())
|
||||
.create_virtual_branch(
|
||||
&BranchCreateRequest::default(),
|
||||
project.exclusive_worktree_access().write_permission(),
|
||||
)
|
||||
.expect("failed to create virtual branch")
|
||||
.id;
|
||||
|
||||
|
@ -47,7 +47,7 @@ pub trait OplogExt {
|
||||
/// Prepares a snapshot of the current state of the working directory as well as GitButler data.
|
||||
/// Returns a tree hash of the snapshot. The snapshot is not discoverable until it is committed with [`commit_snapshot`](Self::commit_snapshot())
|
||||
/// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring.
|
||||
fn prepare_snapshot(&self) -> Result<git2::Oid>;
|
||||
fn prepare_snapshot(&self, perm: &WorktreeReadPermission) -> Result<git2::Oid>;
|
||||
|
||||
/// Commits the snapshot tree that is created with the [`prepare_snapshot`](Self::prepare_snapshot) method,
|
||||
/// which yielded the `snapshot_tree_id` for the entire snapshot state.
|
||||
@ -62,6 +62,7 @@ pub trait OplogExt {
|
||||
&self,
|
||||
snapshot_tree_id: git2::Oid,
|
||||
details: SnapshotDetails,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<Option<git2::Oid>>;
|
||||
|
||||
/// Creates a snapshot of the current state of the working directory as well as GitButler data.
|
||||
@ -72,7 +73,11 @@ pub trait OplogExt {
|
||||
/// commit and the current one (after comparing trees).
|
||||
///
|
||||
/// Note that errors in snapshot creation is typically ignored, so we want to learn about them.
|
||||
fn create_snapshot(&self, details: SnapshotDetails) -> Result<Option<git2::Oid>>;
|
||||
fn create_snapshot(
|
||||
&self,
|
||||
details: SnapshotDetails,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<Option<git2::Oid>>;
|
||||
|
||||
/// Lists the snapshots that have been created for the given repository, up to the given limit,
|
||||
/// and with the most recent snapshot first, and at the end of the vec.
|
||||
@ -129,25 +134,27 @@ pub trait OplogExt {
|
||||
}
|
||||
|
||||
impl OplogExt for Project {
|
||||
fn prepare_snapshot(&self) -> Result<git2::Oid> {
|
||||
let guard = self.shared_worktree_access();
|
||||
prepare_snapshot(self, guard.read_permission())
|
||||
fn prepare_snapshot(&self, perm: &WorktreeReadPermission) -> Result<git2::Oid> {
|
||||
prepare_snapshot(self, perm)
|
||||
}
|
||||
|
||||
fn commit_snapshot(
|
||||
&self,
|
||||
snapshot_tree_id: git2::Oid,
|
||||
details: SnapshotDetails,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<Option<git2::Oid>> {
|
||||
let mut guard = self.exclusive_worktree_access();
|
||||
commit_snapshot(self, snapshot_tree_id, details, guard.write_permission())
|
||||
commit_snapshot(self, snapshot_tree_id, details, perm)
|
||||
}
|
||||
|
||||
#[instrument(skip(details), err(Debug))]
|
||||
fn create_snapshot(&self, details: SnapshotDetails) -> Result<Option<git2::Oid>> {
|
||||
let mut guard = self.exclusive_worktree_access();
|
||||
let tree_id = prepare_snapshot(self, guard.read_permission())?;
|
||||
commit_snapshot(self, tree_id, details, guard.write_permission())
|
||||
#[instrument(skip(details, perm), err(Debug))]
|
||||
fn create_snapshot(
|
||||
&self,
|
||||
details: SnapshotDetails,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<Option<git2::Oid>> {
|
||||
let tree_id = prepare_snapshot(self, perm.read_permission())?;
|
||||
commit_snapshot(self, tree_id, details, perm)
|
||||
}
|
||||
|
||||
fn list_snapshots(
|
||||
|
@ -1,13 +1,13 @@
|
||||
use anyhow::Result;
|
||||
use gitbutler_branch::{Branch, BranchUpdateRequest};
|
||||
use gitbutler_project::Project;
|
||||
use gitbutler_reference::ReferenceName;
|
||||
use std::vec;
|
||||
|
||||
use crate::{
|
||||
entry::{OperationKind, SnapshotDetails},
|
||||
oplog::OplogExt,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gitbutler_branch::{Branch, BranchUpdateRequest};
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_project::Project;
|
||||
use gitbutler_reference::ReferenceName;
|
||||
use std::vec;
|
||||
|
||||
use super::entry::Trailer;
|
||||
|
||||
@ -16,6 +16,7 @@ pub trait SnapshotExt {
|
||||
&self,
|
||||
snapshot_tree: git2::Oid,
|
||||
result: Result<&ReferenceName, &anyhow::Error>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
fn snapshot_commit_undo(
|
||||
@ -23,6 +24,7 @@ pub trait SnapshotExt {
|
||||
snapshot_tree: git2::Oid,
|
||||
result: Result<&(), &anyhow::Error>,
|
||||
commit_sha: git2::Oid,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
fn snapshot_commit_creation(
|
||||
@ -31,16 +33,26 @@ pub trait SnapshotExt {
|
||||
error: Option<&anyhow::Error>,
|
||||
commit_message: String,
|
||||
sha: Option<git2::Oid>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()>;
|
||||
fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()>;
|
||||
fn snapshot_branch_creation(
|
||||
&self,
|
||||
branch_name: String,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()>;
|
||||
fn snapshot_branch_deletion(
|
||||
&self,
|
||||
branch_name: String,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()>;
|
||||
fn snapshot_branch_update(
|
||||
&self,
|
||||
snapshot_tree: git2::Oid,
|
||||
old_branch: &Branch,
|
||||
update: &BranchUpdateRequest,
|
||||
error: Option<&anyhow::Error>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
@ -50,11 +62,12 @@ impl SnapshotExt for Project {
|
||||
&self,
|
||||
snapshot_tree: git2::Oid,
|
||||
result: Result<&ReferenceName, &anyhow::Error>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = result.map(|s| Some(s.to_string()));
|
||||
let details = SnapshotDetails::new(OperationKind::UnapplyBranch)
|
||||
.with_trailers(result_trailer(result, "name".to_string()));
|
||||
self.commit_snapshot(snapshot_tree, details)?;
|
||||
self.commit_snapshot(snapshot_tree, details, perm)?;
|
||||
Ok(())
|
||||
}
|
||||
fn snapshot_commit_undo(
|
||||
@ -62,11 +75,12 @@ impl SnapshotExt for Project {
|
||||
snapshot_tree: git2::Oid,
|
||||
result: Result<&(), &anyhow::Error>,
|
||||
commit_sha: git2::Oid,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()> {
|
||||
let result = result.map(|_| Some(commit_sha.to_string()));
|
||||
let details = SnapshotDetails::new(OperationKind::UndoCommit)
|
||||
.with_trailers(result_trailer(result, "sha".to_string()));
|
||||
self.commit_snapshot(snapshot_tree, details)?;
|
||||
self.commit_snapshot(snapshot_tree, details, perm)?;
|
||||
Ok(())
|
||||
}
|
||||
fn snapshot_commit_creation(
|
||||
@ -75,6 +89,7 @@ impl SnapshotExt for Project {
|
||||
error: Option<&anyhow::Error>,
|
||||
commit_message: String,
|
||||
sha: Option<git2::Oid>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()> {
|
||||
let details = SnapshotDetails::new(OperationKind::CreateCommit).with_trailers(
|
||||
[
|
||||
@ -92,26 +107,34 @@ impl SnapshotExt for Project {
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
self.commit_snapshot(snapshot_tree, details)?;
|
||||
self.commit_snapshot(snapshot_tree, details, perm)?;
|
||||
Ok(())
|
||||
}
|
||||
fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()> {
|
||||
fn snapshot_branch_creation(
|
||||
&self,
|
||||
branch_name: String,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()> {
|
||||
let details =
|
||||
SnapshotDetails::new(OperationKind::CreateBranch).with_trailers(vec![Trailer {
|
||||
key: "name".to_string(),
|
||||
value: branch_name,
|
||||
}]);
|
||||
self.create_snapshot(details)?;
|
||||
self.create_snapshot(details, perm)?;
|
||||
Ok(())
|
||||
}
|
||||
fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()> {
|
||||
fn snapshot_branch_deletion(
|
||||
&self,
|
||||
branch_name: String,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()> {
|
||||
let details =
|
||||
SnapshotDetails::new(OperationKind::DeleteBranch).with_trailers(vec![Trailer {
|
||||
key: "name".to_string(),
|
||||
value: branch_name.to_string(),
|
||||
}]);
|
||||
|
||||
self.create_snapshot(details)?;
|
||||
self.create_snapshot(details, perm)?;
|
||||
Ok(())
|
||||
}
|
||||
fn snapshot_branch_update(
|
||||
@ -120,6 +143,7 @@ impl SnapshotExt for Project {
|
||||
old_branch: &Branch,
|
||||
update: &BranchUpdateRequest,
|
||||
error: Option<&anyhow::Error>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> anyhow::Result<()> {
|
||||
let details = if update.ownership.is_some() {
|
||||
SnapshotDetails::new(OperationKind::MoveHunk).with_trailers(
|
||||
@ -212,7 +236,7 @@ impl SnapshotExt for Project {
|
||||
} else {
|
||||
SnapshotDetails::new(OperationKind::GenericBranchUpdate)
|
||||
};
|
||||
self.commit_snapshot(snapshot_tree, details)?;
|
||||
self.commit_snapshot(snapshot_tree, details, perm)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -44,16 +44,6 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a guard for shared (read) worktree access, and block while waiting for writers to disappear.
|
||||
/// There can be multiple readers, but only a single writer. Waiting writers will be handled with priority,
|
||||
/// thus block readers to prevent writer starvation.
|
||||
/// The guard can be upgraded to allow for writes, which is useful if a mutation is prepared by various reads
|
||||
/// first, followed by conclusive writes.
|
||||
pub fn shared_upgradable_worktree_access(&self) -> UpgradableWorkspaceReadGuard {
|
||||
let mut map = WORKTREE_LOCKS.lock();
|
||||
UpgradableWorkspaceReadGuard(map.entry(self.id).or_default().upgradable_read_arc())
|
||||
}
|
||||
|
||||
/// Return a guard for shared (read) worktree access, and block while waiting for writers to disappear.
|
||||
/// There can be multiple readers, but only a single writer. Waiting writers will be handled with priority,
|
||||
/// thus block readers to prevent writer starvation.
|
||||
@ -82,26 +72,6 @@ impl WriteWorkspaceGuard {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UpgradableWorkspaceReadGuard(parking_lot::ArcRwLockUpgradableReadGuard<RawRwLock, ()>);
|
||||
|
||||
impl UpgradableWorkspaceReadGuard {
|
||||
/// Wait until a write-lock for exclusive access can be acquired, and return a handle to it.
|
||||
/// It must be kept alive until the write operation completes.
|
||||
pub fn upgrade_to_exclusive_worktree_access(self) -> WriteWorkspaceGuard {
|
||||
WriteWorkspaceGuard {
|
||||
_inner: parking_lot::ArcRwLockUpgradableReadGuard::upgrade(self.0),
|
||||
perm: WorktreeWritePermission(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Signal that a read-permission is available - useful as API-marker to assure these
|
||||
/// can only be called when the respective protection/permission is present.
|
||||
pub fn read_permission(&self) -> &WorktreeReadPermission {
|
||||
static READ: WorktreeReadPermission = WorktreeReadPermission(());
|
||||
&READ
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkspaceReadGuard(#[allow(dead_code)] parking_lot::ArcRwLockReadGuard<RawRwLock, ()>);
|
||||
|
||||
impl WorkspaceReadGuard {
|
||||
|
@ -122,13 +122,8 @@ impl Handler {
|
||||
paths: Vec<PathBuf>,
|
||||
project_id: ProjectId,
|
||||
) -> Result<()> {
|
||||
// Create a snapshot every time there are more than a configurable number of new lines of code (default 20)
|
||||
let handle_snapshots = tokio::task::spawn_blocking({
|
||||
let this = self.clone();
|
||||
move || this.maybe_create_snapshot(project_id)
|
||||
});
|
||||
self.maybe_create_snapshot(project_id).ok();
|
||||
self.calculate_virtual_branches(project_id).await?;
|
||||
let _ = handle_snapshots.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -141,7 +136,11 @@ impl Handler {
|
||||
.should_auto_snapshot(std::time::Duration::from_secs(300))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
project.create_snapshot(SnapshotDetails::new(OperationKind::FileChanges))?;
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
project.create_snapshot(
|
||||
SnapshotDetails::new(OperationKind::FileChanges),
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user