<!--
Deleting a local branch in one repository.
The branches are read from the repository rather than typed: a mistyped name
is a refusal at best and the wrong branch at worst. The one that is checked
out is named and left out of the list — git will not delete it and neither
will this, and saying so where the names are beats a refusal after the click.
Unmerged work needs force, stated as what it is: git's refusal is the only
thing standing between a branch nobody merged and losing what is on it.
-->
<script lang="ts">
import { onMount } from "svelte";
import { git } from "../../../stores/git.svelte.js";
import AppIcon from "../../layout/AppIcon.svelte";
let {
repoPath,
onclose,
}: {
repoPath?: string;
onclose: () => void;
} = $props();
let branches = $state<string[]>([]);
let current = $state<string | null>(null);
let selected = $state("");
let force = $state(false);
let loading = $state(true);
let error = $state<string | null>(null);
onMount(async () => {
try {
const answer = await git.getBranches(repoPath);
branches = answer.branches;
current = answer.current;
selected = branches.find((branch) => branch !== answer.current) ?? "";
} catch (failure) {
error = failure instanceof Error ? failure.message : String(failure);
} finally {
loading = false;
}
});
const deletable = $derived(branches.filter((branch) => branch !== current));
function remove() {
if (!selected) return;
if (git.deleteBranch(selected, repoPath, force)) onclose();
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === "Escape") onclose();
}
</script>
<div class="modal-overlay" onclick={onclose} onkeydown={handleKeydown} role="presentation">
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<div
class="modal-panel"
onclick={(event) => event.stopPropagation()}
onkeydown={handleKeydown}
role="dialog"
aria-label="Delete branch"
tabindex="0"
>
<div class="modal-header">
<h2 class="modal-title">Delete branch</h2>
<button class="modal-close" onclick={onclose} aria-label="Close">
<AppIcon name="x" size={14} />
</button>
</div>
<div class="modal-body">
{#if error}
<div class="modal-error">{error}</div>
{/if}
{#if loading}
<p class="empty">Reading branches…</p>
{:else if deletable.length === 0}
<p class="empty">
This repository has only {current ?? "the branch it is on"}, and the branch that is
checked out cannot be deleted from under itself.
</p>
{:else}
<label class="field">
<span class="field-label">Branch</span>
<select class="field-input" bind:value={selected}>
{#each deletable as branch (branch)}
<option value={branch}>{branch}</option>
{/each}
</select>
</label>
{#if current}
<p class="empty">On {current} — that one stays.</p>
{/if}
<label class="check">
<input type="checkbox" bind:checked={force} />
delete even if its work is not merged anywhere
</label>
{/if}
</div>
<div class="modal-footer">
<button class="secondary" onclick={onclose}>Cancel</button>
<button class="danger" disabled={!selected} onclick={remove}>
Delete {selected}
</button>
</div>
</div>
</div>
<style>
.modal-body {
display: flex;
flex-direction: column;
gap: var(--wb-spacing-8);
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: var(--wb-spacing-6);
padding: var(--wb-spacing-10) var(--wb-spacing-14) var(--wb-spacing-12);
}
</style>