diff --git a/apps/desktop/src/components/BranchesView.svelte b/apps/desktop/src/components/BranchesView.svelte index bdb55ff633c..5c36cf83bff 100644 --- a/apps/desktop/src/components/BranchesView.svelte +++ b/apps/desktop/src/components/BranchesView.svelte @@ -29,12 +29,11 @@ import { focusable } from "@gitbutler/ui/focus/focusable"; import { getTimeAgo } from "@gitbutler/ui/utils/timeAgo"; import type { BranchFilterOption, SidebarEntrySubject } from "$lib/branches/branchListing"; + type Props = { projectId: string; }; - const { projectId }: Props = $props(); - type BranchesSelection = | { type: "branch"; @@ -46,6 +45,9 @@ | { type: "pr"; prNumber: number } | { type: "target"; commitId?: string }; + const { projectId }: Props = $props(); + + const addToLeftmost = persisted(false, "branch-placement-leftmost"); const stackService = inject(STACK_SERVICE); const baseBranchService = inject(BASE_BRANCH_SERVICE); const forge = inject(DEFAULT_FORGE_FACTORY); @@ -80,6 +82,7 @@ branch: branchRef, remote: remoteRef, prNumber, + order: $addToLeftmost ? 0 : undefined, }); handleCreateBranchFromBranchOutcome(outcome); await baseBranchService.refreshBaseBranch(projectId); diff --git a/apps/desktop/src/components/BranchesViewPR.svelte b/apps/desktop/src/components/BranchesViewPR.svelte index fcc630e9d60..4d954a0de75 100644 --- a/apps/desktop/src/components/BranchesViewPR.svelte +++ b/apps/desktop/src/components/BranchesViewPR.svelte @@ -11,6 +11,7 @@ import { STACK_SERVICE } from "$lib/stacks/stackService.svelte"; import { inject } from "@gitbutler/core/context"; + import { persisted } from "@gitbutler/shared/persisted"; import { Button, Modal, TestId, Textbox } from "@gitbutler/ui"; import type { DetailedPullRequest } from "$lib/forge/interface/types"; @@ -34,6 +35,8 @@ const remotesService = inject(REMOTES_SERVICE); const stackService = inject(STACK_SERVICE); + const addToLeftmost = persisted(false, "branch-placement-leftmost"); + let createRemoteModal = $state(); let inputRemoteName = $state(); let loading = $state(false); @@ -71,6 +74,7 @@ branch: remoteRef, remote: remoteRef, prNumber, + order: $addToLeftmost ? 0 : undefined, }); handleCreateBranchFromBranchOutcome(outcome); diff --git a/apps/desktop/src/lib/stacks/stackService.svelte.ts b/apps/desktop/src/lib/stacks/stackService.svelte.ts index 863f105e85d..92660c94b95 100644 --- a/apps/desktop/src/lib/stacks/stackService.svelte.ts +++ b/apps/desktop/src/lib/stacks/stackService.svelte.ts @@ -1727,7 +1727,7 @@ function injectEndpoints(api: ClientState["backendApi"], uiState: UiState) { }), createVirtualBranchFromBranch: build.mutation< CreateBranchFromBranchOutcome, - { projectId: string; branch: string; remote?: string; prNumber?: number } + { projectId: string; branch: string; remote?: string; prNumber?: number; order?: number } >({ extraOptions: { command: "create_virtual_branch_from_branch", diff --git a/crates/but-api/src/legacy/virtual_branches.rs b/crates/but-api/src/legacy/virtual_branches.rs index 30b5605d654..b86c52a24e1 100644 --- a/crates/but-api/src/legacy/virtual_branches.rs +++ b/crates/but-api/src/legacy/virtual_branches.rs @@ -109,9 +109,10 @@ pub fn create_virtual_branch_from_branch( branch: Refname, remote: Option, pr_number: Option, + order: Option, ) -> Result { let outcome = gitbutler_branch_actions::create_virtual_branch_from_branch( - ctx, &branch, remote, pr_number, + ctx, &branch, remote, pr_number, order, )?; Ok(outcome.into()) } diff --git a/crates/but-api/src/legacy/workspace.rs b/crates/but-api/src/legacy/workspace.rs index 96785fe7219..0e6b0c4f463 100644 --- a/crates/but-api/src/legacy/workspace.rs +++ b/crates/but-api/src/legacy/workspace.rs @@ -473,6 +473,7 @@ pub fn split_branch( &refname, None, None, + None, guard.write_permission(), )?; diff --git a/crates/but-testing/src/command/mod.rs b/crates/but-testing/src/command/mod.rs index 5f0ab33e3de..f32705abe85 100644 --- a/crates/but-testing/src/command/mod.rs +++ b/crates/but-testing/src/command/mod.rs @@ -189,6 +189,7 @@ pub mod stacks { &ref_name, Some(remote_ref_name), None, + None, )?; let repo = ctx.repo.get()?; diff --git a/crates/but-tools/src/workspace.rs b/crates/but-tools/src/workspace.rs index 16d1d26e2bd..d22cfc43db2 100644 --- a/crates/but-tools/src/workspace.rs +++ b/crates/but-tools/src/workspace.rs @@ -1155,6 +1155,7 @@ pub fn split_branch( &refname, None, None, + None, guard.write_permission(), )?; diff --git a/crates/but/src/command/legacy/branch/apply.rs b/crates/but/src/command/legacy/branch/apply.rs index d376e12c8e7..ffc5791490b 100644 --- a/crates/but/src/command/legacy/branch/apply.rs +++ b/crates/but/src/command/legacy/branch/apply.rs @@ -33,6 +33,7 @@ pub fn apply(ctx: &mut Context, branch_name: &str, out: &mut OutputChannel) -> a ref_name, remote_ref_name, None, + None, )?; r } else if let Some((remote_ref, r)) = find_remote_reference(&repo, branch_name)? { @@ -48,6 +49,7 @@ pub fn apply(ctx: &mut Context, branch_name: &str, out: &mut OutputChannel) -> a ref_name, Some(remote_ref.clone()), None, + None, )?; r } else { @@ -90,6 +92,7 @@ pub fn apply(ctx: &mut Context, branch_name: &str, out: &mut OutputChannel) -> a ref_name, remote_ref_name, None, + None, )?; r } @@ -110,6 +113,7 @@ pub fn apply(ctx: &mut Context, branch_name: &str, out: &mut OutputChannel) -> a ref_name, Some(remote_ref), None, + None, )?; r } diff --git a/crates/gitbutler-branch-actions/src/actions.rs b/crates/gitbutler-branch-actions/src/actions.rs index f25f2d04c0d..b939b520e82 100644 --- a/crates/gitbutler-branch-actions/src/actions.rs +++ b/crates/gitbutler-branch-actions/src/actions.rs @@ -421,6 +421,7 @@ pub fn create_virtual_branch_from_branch( branch: &Refname, remote: Option, pr_number: Option, + order: Option, ) -> Result<(StackId, Vec, Vec)> { let mut guard = ctx.exclusive_worktree_access(); ctx.verify(guard.write_permission())?; @@ -431,6 +432,7 @@ pub fn create_virtual_branch_from_branch( branch, remote, pr_number, + order, guard.write_permission(), ) } diff --git a/crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs b/crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs index 31dd7372353..90759866912 100644 --- a/crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs +++ b/crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs @@ -101,6 +101,7 @@ impl BranchManager<'_> { target: &Refname, upstream_branch: Option, pr_number: Option, + order: Option, perm: &mut RepoExclusive, ) -> Result<(StackId, Vec, Vec)> { let branch_name = target @@ -129,7 +130,7 @@ impl BranchManager<'_> { OnWorkspaceMergeConflict::MaterializeAndReportConflictingStacks, workspace_reference_naming: WorkspaceReferenceNaming::Default, uncommitted_changes: UncommitedWorktreeChanges::KeepAndAbortOnConflict, - order: None, + order, new_stack_id: None, }, )?; @@ -211,7 +212,7 @@ impl BranchManager<'_> { .peel_to_commit() .context("failed to peel to commit")?; - let order = vb_state.next_order_index()?; + let order = order.unwrap_or(vb_state.next_order_index()?); let mut branch = if let Some(mut branch) = vb_state .find_by_top_reference_name_where_not_in_workspace(&target.to_string())? diff --git a/crates/gitbutler-branch-actions/src/move_branch.rs b/crates/gitbutler-branch-actions/src/move_branch.rs index 0b825067cf0..dabbae244b4 100644 --- a/crates/gitbutler-branch-actions/src/move_branch.rs +++ b/crates/gitbutler-branch-actions/src/move_branch.rs @@ -136,6 +136,7 @@ pub(crate) fn tear_off_branch( &Refname::Local(LocalRefname::new(subject_branch_name, None)), None, None, + None, perm, )?; diff --git a/crates/gitbutler-branch-actions/tests/virtual_branches/apply_virtual_branch.rs b/crates/gitbutler-branch-actions/tests/virtual_branches/apply_virtual_branch.rs index e608d2ad093..1f62134ea0a 100644 --- a/crates/gitbutler-branch-actions/tests/virtual_branches/apply_virtual_branch.rs +++ b/crates/gitbutler-branch-actions/tests/virtual_branches/apply_virtual_branch.rs @@ -106,6 +106,7 @@ fn rebase_commit() { &unapplied_branch, None, None, + None, ) .unwrap(); diff --git a/crates/gitbutler-branch-actions/tests/virtual_branches/create_virtual_branch_from_branch.rs b/crates/gitbutler-branch-actions/tests/virtual_branches/create_virtual_branch_from_branch.rs index cc8970d2d55..4ae32e602b8 100644 --- a/crates/gitbutler-branch-actions/tests/virtual_branches/create_virtual_branch_from_branch.rs +++ b/crates/gitbutler-branch-actions/tests/virtual_branches/create_virtual_branch_from_branch.rs @@ -34,6 +34,7 @@ fn no_conflicts() { &"refs/remotes/origin/branch".parse().unwrap(), None, None, + None, ) .map(|o| o.0) .unwrap(); @@ -90,6 +91,7 @@ fn conflicts_with_uncommited() { &"refs/remotes/origin/branch".parse().unwrap(), None, None, + None, ) .map(|o| o.0) .unwrap(); @@ -148,6 +150,7 @@ fn conflicts_with_commited() { &"refs/remotes/origin/branch".parse().unwrap(), None, None, + None, ) .map(|o| o.0) .unwrap(); @@ -179,6 +182,7 @@ fn from_default_target() { &"refs/remotes/origin/master".parse().unwrap(), None, None, + None, ) .unwrap_err() .to_string(), @@ -207,6 +211,7 @@ fn from_non_existent_branch() { &"refs/remotes/origin/branch".parse().unwrap(), None, None, + None, ) .unwrap_err() .to_string(), @@ -247,6 +252,7 @@ fn from_state_remote_branch() { &"refs/remotes/origin/branch".parse().unwrap(), None, None, + None, ) .unwrap(); diff --git a/crates/gitbutler-cli/src/command/vbranch.rs b/crates/gitbutler-cli/src/command/vbranch.rs index 93d5ba9ba62..4361ea9a399 100644 --- a/crates/gitbutler-cli/src/command/vbranch.rs +++ b/crates/gitbutler-cli/src/command/vbranch.rs @@ -66,6 +66,7 @@ fn apply_by_name(ctx: &mut Context, branch_name: String) -> Result<()> { .context("local reference name was missing")?, None, None, + None, guard.write_permission(), )?, ) @@ -85,6 +86,7 @@ fn apply_from_branch(ctx: &mut Context, branch_name: String) -> Result<()> { &target, None, None, + None, guard.write_permission(), )?) }