add can apply methods

This commit is contained in:
Nikita Galaiko 2023-09-18 09:15:49 +02:00 committed by GitButler
parent c5dc6785a2
commit 2cfe510fc4
7 changed files with 183 additions and 12 deletions

View File

@ -337,6 +337,13 @@ impl Repository {
.map_err(Into::into)
}
pub fn get_wd_tree(&self) -> Result<Tree> {
let mut index = self.0.index()?;
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
let oid = index.write_tree()?;
self.0.find_tree(oid).map(Into::into).map_err(Into::into)
}
#[cfg(test)]
pub fn remote(&self, name: &str, url: &str) -> Result<Remote> {
self.0.remote(name, url).map(Into::into).map_err(Into::into)

View File

@ -682,6 +682,8 @@ async fn main() {
virtual_branches::commands::unapply_branch,
virtual_branches::commands::push_virtual_branch,
virtual_branches::commands::create_virtual_branch_from_branch,
virtual_branches::commands::can_apply_virtual_branch,
virtual_branches::commands::can_apply_remote_branch,
fetch_from_target,
mark_resolved,
git_set_global_config,

View File

@ -46,6 +46,11 @@ impl<'repository> Repository<'repository> {
Ok(head)
}
pub fn get_wd_tree(&self) -> Result<git::Tree> {
let tree = self.git_repository.get_wd_tree()?;
Ok(tree)
}
pub fn is_path_ignored<P: AsRef<std::path::Path>>(&self, path: P) -> Result<bool> {
let path = path.as_ref();
let ignored = self.git_repository.is_path_ignored(path)?;

View File

@ -409,7 +409,9 @@ pub fn update_base_branch(
// ok, now all the problematic branches have been unapplied, so we can try to merge the upstream branch into our current working directory
// first, get a new wd tree
let wd_tree = super::get_wd_tree(repo)?;
let wd_tree = project_repository
.get_wd_tree()
.context("failed to get wd tree")?;
// and try to merge it
let mut merge_index = repo

View File

@ -168,3 +168,31 @@ pub async fn push_virtual_branch(
.await
.map_err(Into::into)
}
#[tauri::command(async)]
#[instrument(skip(handle))]
pub async fn can_apply_virtual_branch(
handle: AppHandle,
project_id: &str,
branch_id: &str,
) -> Result<bool, Error> {
handle
.state::<Controller>()
.can_apply_virtual_branch(project_id, branch_id)
.await
.map_err(Into::into)
}
#[tauri::command(async)]
#[instrument(skip(handle))]
pub async fn can_apply_remote_branch(
handle: AppHandle,
project_id: &str,
branch: git::BranchName,
) -> Result<bool, Error> {
handle
.state::<Controller>()
.can_apply_remote_branch(project_id, &branch)
.await
.map_err(Into::into)
}

View File

@ -85,6 +85,34 @@ impl Controller {
.await
}
pub async fn can_apply_remote_branch(
&self,
project_id: &str,
branch_name: &git::BranchName,
) -> Result<bool, Error> {
self.with_lock(project_id, || {
self.with_verify_branch(project_id, |gb_repository, project_repository| {
super::is_remote_branch_mergeable(gb_repository, project_repository, branch_name)
.map_err(Error::Other)
})
})
.await
}
pub async fn can_apply_virtual_branch(
&self,
project_id: &str,
branch_id: &str,
) -> Result<bool, Error> {
self.with_lock(project_id, || {
self.with_verify_branch(project_id, |gb_repository, project_repository| {
super::is_virtual_branch_mergeable(gb_repository, project_repository, branch_id)
.map_err(Error::Other)
})
})
.await
}
pub async fn list_virtual_branches(
&self,
project_id: &str,

View File

@ -319,7 +319,7 @@ pub fn apply_branch(
}
}
let wd_tree = get_wd_tree(repo)?;
let wd_tree = project_repository.get_wd_tree()?;
// check index for conflicts
let mut merge_index = repo
@ -499,7 +499,7 @@ pub fn list_remote_branches(
.find_commit(main_oid)
.context("failed to find target commit")?;
let wd_tree = get_wd_tree(repo)?;
let wd_tree = project_repository.get_wd_tree()?;
let virtual_branches_names = Iterator::new(&current_session_reader)
.context("failed to create branch iterator")?
@ -687,14 +687,6 @@ fn list_remote_commit_files(
Ok(files)
}
pub fn get_wd_tree(repo: &git::Repository) -> Result<git::Tree> {
let mut index = repo.index()?;
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
let oid = index.write_tree()?;
let tree = repo.find_tree(oid)?;
Ok(tree)
}
fn find_base_tree<'a>(
repo: &'a git::Repository,
branch_commit: &'a git::Commit<'a>,
@ -733,7 +725,7 @@ pub fn list_virtual_branches(
None => return Ok(vec![]),
};
let wd_tree = get_wd_tree(&project_repository.git_repository)?;
let wd_tree = project_repository.get_wd_tree()?;
let statuses = get_status_by_branch(gb_repository, project_repository)?;
for (branch, files) in &statuses {
@ -2085,3 +2077,110 @@ fn is_commit_integrated(
// then the vbranch is fully merged
Ok(merge_tree_oid == upstream_tree_oid)
}
pub fn is_remote_branch_mergeable(
gb_repository: &gb_repository::Repository,
project_repository: &project_repository::Repository,
branch_name: &git::BranchName,
) -> Result<bool> {
// get the current target
let current_session = gb_repository
.get_or_create_current_session()
.context("failed to get or create currnt session")?;
let current_session_reader = sessions::Reader::open(gb_repository, &current_session)
.context("failed to open current session")?;
let default_target =
get_default_target(&current_session_reader)?.context("no default target set")?;
let target_commit = project_repository
.git_repository
.find_commit(default_target.sha)
.context("failed to find target commit")?;
let branch = project_repository.git_repository.find_branch(branch_name)?;
let branch_oid = branch.target().context("detatched head")?;
let branch_commit = project_repository
.git_repository
.find_commit(branch_oid)
.context("failed to find branch commit")?;
let base_tree = find_base_tree(
&project_repository.git_repository,
&branch_commit,
&target_commit,
)?;
let wd_tree = project_repository.get_wd_tree()?;
let branch_tree = branch_commit.tree()?;
let mergeable = !project_repository
.git_repository
.merge_trees(&base_tree, &branch_tree, &wd_tree)?
.has_conflicts();
Ok(mergeable)
}
pub fn is_virtual_branch_mergeable(
gb_repository: &gb_repository::Repository,
project_repository: &project_repository::Repository,
branch_id: &str,
) -> Result<bool> {
let current_session = gb_repository
.get_or_create_current_session()
.context("failed to get or create currnt session")?;
let current_session_reader = sessions::Reader::open(gb_repository, &current_session)
.context("failed to open current session reader")?;
let branch_reader = branch::Reader::new(&current_session_reader);
let branch = branch_reader
.read(branch_id)
.context("failed to read branch")?;
if branch.applied {
bail!("branch {} is applied", branch.name);
}
let default_target = get_default_target(&current_session_reader)
.context("failed to read default target")?
.context("no default target set")?;
// determine if this branch is up to date with the target/base
let merge_base = project_repository
.git_repository
.merge_base(default_target.sha, branch.head)?;
if merge_base != default_target.sha {
return Ok(false);
}
let branch_commit = project_repository
.git_repository
.find_commit(branch.head)
.context("failed to find branch commit")?;
let target_commit = project_repository
.git_repository
.find_commit(default_target.sha)
.context("failed to find target commit")?;
let base_tree = find_base_tree(
&project_repository.git_repository,
&branch_commit,
&target_commit,
)?;
let wd_tree = project_repository.get_wd_tree()?;
// determine if this tree is mergeable
let branch_tree = project_repository
.git_repository
.find_tree(branch.tree)
.context("failed to find branch tree")?;
let is_mergeable = !project_repository
.git_repository
.merge_trees(&base_tree, &branch_tree, &wd_tree)?
.has_conflicts();
Ok(is_mergeable)
}