Merge pull request #4663 from gitbutlerapp/create-delete-local-branch

create-delete-local-branch
This commit is contained in:
Kiril Videlov 2024-08-09 16:07:54 +02:00 committed by GitHub
commit ed7a8c4027
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 126 additions and 0 deletions

View File

@ -8,6 +8,7 @@
import { BranchController } from '$lib/vbranches/branchController';
import Icon from '@gitbutler/ui/icon/Icon.svelte';
import Button from '@gitbutler/ui/inputs/Button.svelte';
import Modal from '@gitbutler/ui/modal/Modal.svelte';
import { tooltip } from '@gitbutler/ui/utils/tooltip';
import type { PullRequest } from '$lib/gitHost/interface/types';
import type { Branch } from '$lib/vbranches/types';
@ -26,6 +27,8 @@
$: gitHostBranch = upstream ? $gitHost?.branch(upstream) : undefined;
let isApplying = false;
let isDeleting = false;
let deleteBranchModal: Modal;
</script>
<div class="header__wrapper">
@ -110,12 +113,56 @@
>
Apply
</Button>
<Button
style="ghost"
outline
help="Deletes the local branch. If this branch is also present on a remote, it will not be deleted there."
icon="bin-small"
loading={isDeleting}
disabled={!localBranch}
onclick={async () => {
if (localBranch) {
console.log(JSON.stringify(localBranch));
deleteBranchModal.show(branch);
}
}}
>
Delete locally
</Button>
</div>
</div>
</div>
<div class="header__top-overlay" data-tauri-drag-region></div>
</div>
<Modal width="small" title="Delete branch" bind:this={deleteBranchModal}>
{#snippet children(branch)}
Are you sure you want to delete <code class="code-string">{branch.name}</code>?
{/snippet}
{#snippet controls(close)}
<Button style="ghost" outline onclick={close}>Cancel</Button>
<Button
style="error"
kind="solid"
onclick={async () => {
try {
await branchController.deleteLocalBranch(branch.name, branch.givenName);
} catch (e) {
const err = 'Failed to delete local branch';
error(err);
console.error(err, e);
} finally {
isDeleting = false;
close();
}
goto(`/${project.id}/board`);
}}
>
Delete
</Button>
{/snippet}
</Modal>
<style lang="postcss">
.header__wrapper {
z-index: var(--z-lifted);

View File

@ -293,6 +293,25 @@ You can find them in the 'Branches' sidebar in order to resolve conflicts.`;
}
}
/**
* Removes a branch local reference and any associated virtual branch if applicable and updates the list of branches know to the UI.
* @param branch The reference name of the branch to delete (including the `refs/heads/` prefix).
*/
async deleteLocalBranch(refname: string, givenName: string) {
try {
await invoke<void>('delete_local_branch', {
projectId: this.projectId,
refname,
givenName
});
} catch (err) {
showError('Failed to delete local branch', err);
} finally {
this.remoteBranchService.refresh();
this.baseBranchService.refresh();
}
}
async markResolved(path: string) {
try {
await invoke<void>('mark_resolved', { projectId: this.projectId, path });

View File

@ -94,6 +94,43 @@ impl VirtualBranchActions {
Ok(branch_id)
}
/// Deletes a local branch reference and it's associated virtual branch.
/// If there is a virtual branch and it is applied, this function will return an error.
/// If there is no such local reference, this function will return an error.
pub fn delete_local_branch(
&self,
project: &Project,
refname: &Refname,
given_name: String,
) -> Result<()> {
let ctx = open_with_verify(project)?;
let repo = ctx.repository();
let handle = ctx.project().virtual_branches();
let vbranch = handle.list_all_branches()?.into_iter().find(|branch| {
branch
.source_refname
.as_ref()
.map_or(false, |source_refname| source_refname == refname)
});
if let Some(vbranch) = vbranch {
// Disallow deletion of branches that are applied in workspace
if vbranch.in_workspace {
return Err(anyhow::anyhow!(
"Cannot delete a branch that is applied in workspace"
));
}
// Deletes the virtual branch entry from the application state
handle.delete_branch_entry(&vbranch.id)?;
}
// If a branch reference for this can be found, delete it
if let Ok(mut branch) = repo.find_branch(&given_name, git2::BranchType::Local) {
branch.delete()?;
};
Ok(())
}
#[instrument(skip(project), err(Debug))]
pub fn get_base_branch_data(project: &Project) -> Result<BaseBranch> {
let ctx = CommandContext::open(project)?;

View File

@ -237,6 +237,13 @@ impl VirtualBranchesHandle {
Ok(order)
}
pub fn delete_branch_entry(&self, branch_id: &BranchId) -> Result<()> {
let mut virtual_branches = self.read_file()?;
virtual_branches.branches.remove(branch_id);
self.write_file(&virtual_branches)?;
Ok(())
}
}
fn write<P: AsRef<Path>>(file_path: P, virtual_branches: &VirtualBranches) -> Result<()> {

View File

@ -149,6 +149,7 @@ fn main() {
repo::commands::git_clone_repository,
virtual_branches::commands::list_virtual_branches,
virtual_branches::commands::create_virtual_branch,
virtual_branches::commands::delete_local_branch,
virtual_branches::commands::commit_virtual_branch,
virtual_branches::commands::get_base_branch_data,
virtual_branches::commands::set_base_branch,

View File

@ -78,6 +78,21 @@ pub mod commands {
Ok(branch_id)
}
#[tauri::command(async)]
#[instrument(skip(projects, windows), err(Debug))]
pub fn delete_local_branch(
windows: State<'_, WindowState>,
projects: State<'_, projects::Controller>,
project_id: ProjectId,
refname: Refname,
given_name: String,
) -> Result<(), Error> {
let project = projects.get(project_id)?;
VirtualBranchActions.delete_local_branch(&project, &refname, given_name)?;
emit_vbranches(&windows, project_id);
Ok(())
}
#[tauri::command(async)]
#[instrument(skip(projects, windows), err(Debug))]
pub fn create_virtual_branch_from_branch(