From 84807f3e9b4a582ab05a35b37935dc4c55e6a880 Mon Sep 17 00:00:00 2001 From: Mattias Granlund Date: Wed, 24 Jan 2024 10:37:53 +0100 Subject: [PATCH 1/3] Drop unused stashed preview file --- .../stashed/[branchId]/+page.svelte | 132 ------------------ 1 file changed, 132 deletions(-) delete mode 100644 gitbutler-ui/src/routes/[projectId]/stashed/[branchId]/+page.svelte diff --git a/gitbutler-ui/src/routes/[projectId]/stashed/[branchId]/+page.svelte b/gitbutler-ui/src/routes/[projectId]/stashed/[branchId]/+page.svelte deleted file mode 100644 index 65935a5be..000000000 --- a/gitbutler-ui/src/routes/[projectId]/stashed/[branchId]/+page.svelte +++ /dev/null @@ -1,132 +0,0 @@ - - -
- {#if $error$} -

Error...

- {:else if !$branches$} -

Loading...

- {:else if branch} -
- -
- {#await branch.isMergeable then isMergeable} - {#if !isMergeable} - -
- Conflicts with Applied Branches -
-
- {/if} - {/await} - deleteBranchModal.show(branch)} - /> -
-
-
- -
- {:else} -

Branch no longer exists

- {/if} -
- - -

Applying this branch will introduce merge conflicts.

- - - - -
- - -
- Deleting {item.name} cannot be undone. -
- - - - -
From 0cacc62c346aceea0b362c9e2842f1724c8fdbcb Mon Sep 17 00:00:00 2001 From: Mattias Granlund Date: Wed, 24 Jan 2024 10:25:52 +0100 Subject: [PATCH 2/3] Move PR creation logic to service - makes the UI component easier to manage --- gitbutler-ui/src/lib/branches/service.ts | 52 ++++++++++++++++-- .../src/lib/vbranches/branchStoresCache.ts | 23 +++++++- .../src/routes/[projectId]/board/+page.svelte | 2 + .../src/routes/[projectId]/board/Board.svelte | 17 +++--- .../[projectId]/components/BranchCard.svelte | 3 ++ .../components/BranchCommits.svelte | 6 +++ .../[projectId]/components/BranchLane.svelte | 3 ++ .../[projectId]/components/CommitList.svelte | 3 ++ .../components/CommitListFooter.svelte | 54 +++++-------------- 9 files changed, 113 insertions(+), 50 deletions(-) diff --git a/gitbutler-ui/src/lib/branches/service.ts b/gitbutler-ui/src/lib/branches/service.ts index 2d13020fb..321194844 100644 --- a/gitbutler-ui/src/lib/branches/service.ts +++ b/gitbutler-ui/src/lib/branches/service.ts @@ -6,16 +6,18 @@ import { map, startWith, switchMap } from 'rxjs/operators'; import type { RemoteBranchService } from '$lib/stores/remoteBranches'; import type { GitHubService } from '$lib/github/service'; import type { VirtualBranchService } from '$lib/vbranches/branchStoresCache'; +import type { Transaction } from '@sentry/sveltekit'; +import { capture } from '$lib/analytics/posthog'; export class BranchService { public branches$: Observable; constructor( - vbranchService: VirtualBranchService, + private vbranchService: VirtualBranchService, remoteBranchService: RemoteBranchService, - githubService: GitHubService + private githubService: GitHubService ) { - const vbranchesWithEmpty$ = vbranchService.branches$.pipe(startWith([])); + const vbranchesWithEmpty$ = vbranchService.activeBranches$.pipe(startWith([])); const branchesWithEmpty$ = remoteBranchService.branches$.pipe(startWith([])); const prWithEmpty$ = githubService.prs$.pipe(startWith([])); @@ -34,6 +36,50 @@ export class BranchService { map((branches) => branches.filter((b) => !b.vbranch || b.vbranch.active)) ); } + + async createPr( + branch: Branch, + baseBranch: string, + sentryTxn: Transaction + ): Promise { + // Using this mutable variable while investigating why branch variable + // does not seem to update reliably. + // TODO: This needs to be fixed and removed. + let newBranch: Branch | undefined; + + // Push if local commits + if (branch.commits.some((c) => !c.isRemote)) { + const pushBranchSpan = sentryTxn.startChild({ op: 'branch_push' }); + newBranch = await this.vbranchService.pushBranch(branch.id, branch.requiresForce); + pushBranchSpan.finish(); + } else { + newBranch = branch; + } + + if (!newBranch) { + const err = 'branch push failed'; + capture(err, { upstream: branch.upstreamName }); + throw err; + } + + if (!newBranch.upstreamName) { + throw 'Cannot create PR without remote branch name'; + } + + const createPrSpan = sentryTxn.startChild({ op: 'pr_api_create' }); + + try { + return await this.githubService.createPullRequest( + baseBranch, + newBranch.name, + newBranch.notes, + newBranch.id, + newBranch.upstreamName + ); + } finally { + createPrSpan.finish(); + } + } } function mergeBranchesAndPrs( diff --git a/gitbutler-ui/src/lib/vbranches/branchStoresCache.ts b/gitbutler-ui/src/lib/vbranches/branchStoresCache.ts index 951646f91..e77a68697 100644 --- a/gitbutler-ui/src/lib/vbranches/branchStoresCache.ts +++ b/gitbutler-ui/src/lib/vbranches/branchStoresCache.ts @@ -30,7 +30,10 @@ export class VirtualBranchService { private reload$ = new BehaviorSubject(undefined); private fresh$ = new Subject(); - constructor(projectId: string, gbBranchActive$: Observable) { + constructor( + private projectId: string, + gbBranchActive$: Observable + ) { this.branches$ = this.reload$.pipe( switchMap(() => gbBranchActive$), switchMap((gbBranchActive) => @@ -98,6 +101,24 @@ export class VirtualBranchService { ) ); } + + async pushBranch(branchId: string, withForce: boolean): Promise { + try { + await invoke('push_virtual_branch', { + projectId: this.projectId, + branchId, + withForce + }); + await this.reload(); + return await this.getById(branchId); + } catch (err: any) { + if (err.code === 'errors.git.authentication') { + toasts.error('Failed to authenticate. Did you setup GitButler ssh keys?'); + } else { + toasts.error(`Failed to push branch: ${err.message}`); + } + } + } } function subscribeToVirtualBranches(projectId: string, callback: (branches: Branch[]) => void) { diff --git a/gitbutler-ui/src/routes/[projectId]/board/+page.svelte b/gitbutler-ui/src/routes/[projectId]/board/+page.svelte index fb42dd524..ee61ae140 100644 --- a/gitbutler-ui/src/routes/[projectId]/board/+page.svelte +++ b/gitbutler-ui/src/routes/[projectId]/board/+page.svelte @@ -15,6 +15,7 @@ $: base$ = baseBranchService.base$; $: user$ = data.user$; $: githubService = data.githubService; + $: branchService = data.branchService; $: project$ = data.project$; $: branches = vbranchService.branches$; @@ -58,6 +59,7 @@
- import BranchLane from '../components/BranchLane.svelte'; - import NewBranchDropZone from './NewBranchDropZone.svelte'; + import type { User, getCloudApiClient } from '$lib/backend/cloud'; import type { BaseBranch, Branch } from '$lib/vbranches/types'; import type { BranchController } from '$lib/vbranches/branchController'; - import type { User, getCloudApiClient } from '$lib/backend/cloud'; - import { open } from '@tauri-apps/api/shell'; + import type { BranchService } from '$lib/branches/service'; import type { GitHubService } from '$lib/github/service'; - import { cloneWithRotation } from '$lib/utils/draggable'; import type { Project } from '$lib/backend/projects'; - import Icon from '$lib/icons/Icon.svelte'; + + import { cloneWithRotation } from '$lib/utils/draggable'; + import { open } from '@tauri-apps/api/shell'; + + import NewBranchDropZone from './NewBranchDropZone.svelte'; + import BranchLane from '../components/BranchLane.svelte'; import ImgThemed from '$lib/components/ImgThemed.svelte'; + import Icon from '$lib/icons/Icon.svelte'; export let project: Project; export let projectPath: string; @@ -21,6 +24,7 @@ export let cloud: ReturnType; export let branchController: BranchController; + export let branchService: BranchService; export let githubService: GitHubService; export let user: User | undefined; @@ -111,6 +115,7 @@ {base} {cloud} {branchController} + {branchService} branchCount={branches.filter((c) => c.active).length} {projectPath} {user} diff --git a/gitbutler-ui/src/routes/[projectId]/components/BranchCard.svelte b/gitbutler-ui/src/routes/[projectId]/components/BranchCard.svelte index a03beed04..6942bf95e 100644 --- a/gitbutler-ui/src/routes/[projectId]/components/BranchCard.svelte +++ b/gitbutler-ui/src/routes/[projectId]/components/BranchCard.svelte @@ -30,6 +30,7 @@ import DropzoneOverlay from './DropzoneOverlay.svelte'; import ScrollableContainer from '$lib/components/ScrollableContainer.svelte'; + import type { BranchService } from '$lib/branches/service'; export let branch: Branch; export let readonly = false; @@ -37,6 +38,7 @@ export let base: BaseBranch | undefined | null; export let cloud: ReturnType; export let branchController: BranchController; + export let branchService: BranchService; export let branchCount = 1; export let user: User | undefined; export let selectedFiles: Writable; @@ -251,6 +253,7 @@ {project} {githubService} {branchController} + {branchService} {branchCount} {readonly} /> diff --git a/gitbutler-ui/src/routes/[projectId]/components/BranchCommits.svelte b/gitbutler-ui/src/routes/[projectId]/components/BranchCommits.svelte index 9b4df5285..77a543c59 100644 --- a/gitbutler-ui/src/routes/[projectId]/components/BranchCommits.svelte +++ b/gitbutler-ui/src/routes/[projectId]/components/BranchCommits.svelte @@ -1,5 +1,6 @@ @@ -20,6 +22,7 @@ {base} {project} {branchController} + {branchService} {branchCount} {githubService} {readonly} @@ -30,6 +33,7 @@ {base} {project} {branchController} + {branchService} {githubService} {readonly} type="local" @@ -39,6 +43,7 @@ {base} {project} {branchController} + {branchService} {githubService} {readonly} type="remote" @@ -48,6 +53,7 @@ {base} {project} {branchController} + {branchService} {githubService} {readonly} type="integrated" diff --git a/gitbutler-ui/src/routes/[projectId]/components/BranchLane.svelte b/gitbutler-ui/src/routes/[projectId]/components/BranchLane.svelte index 1bdc137a1..3fb3c044c 100644 --- a/gitbutler-ui/src/routes/[projectId]/components/BranchLane.svelte +++ b/gitbutler-ui/src/routes/[projectId]/components/BranchLane.svelte @@ -8,6 +8,7 @@ import { Ownership } from '$lib/vbranches/ownership'; import type { GitHubService } from '$lib/github/service'; import type { Project } from '$lib/backend/projects'; + import type { BranchService } from '$lib/branches/service'; export let branch: Branch; export let readonly = false; @@ -15,6 +16,7 @@ export let base: BaseBranch | undefined | null; export let cloud: ReturnType; export let branchController: BranchController; + export let branchService: BranchService; export let branchCount = 1; export let user: User | undefined; export let projectPath: string; @@ -43,6 +45,7 @@ {base} {cloud} {branchController} + {branchService} {selectedOwnership} bind:commitBoxOpen {branchCount} diff --git a/gitbutler-ui/src/routes/[projectId]/components/CommitList.svelte b/gitbutler-ui/src/routes/[projectId]/components/CommitList.svelte index a3caad451..9d619dfbe 100644 --- a/gitbutler-ui/src/routes/[projectId]/components/CommitList.svelte +++ b/gitbutler-ui/src/routes/[projectId]/components/CommitList.svelte @@ -6,6 +6,7 @@ import CommitListHeader from './CommitListHeader.svelte'; import CommitListFooter from './CommitListFooter.svelte'; import type { Project } from '$lib/backend/projects'; + import type { BranchService } from '$lib/branches/service'; export let branch: Branch; export let base: BaseBranch | undefined | null; @@ -13,6 +14,7 @@ export let branchController: BranchController; export let type: CommitStatus; export let githubService: GitHubService; + export let branchService: BranchService; export let readonly: boolean; export let branchCount: number = 0; @@ -58,6 +60,7 @@
{/if} { - isPushing = true; - const txn = startTransaction({ name: 'pull_request_create' }); - if (!githubService.isEnabled()) { toast.error('Cannot create PR without GitHub credentials'); return; @@ -44,43 +42,19 @@ return; } - let newBranch: Branch | undefined; - // Push if local commits - if (branch.commits.some((c) => !c.isRemote)) { - const pushBranchSpan = txn.startChild({ op: 'branch_push' }); - newBranch = await branchController.pushBranch(branch.id, branch.requiresForce); - pushBranchSpan.finish(); - } else { - newBranch = branch; + // Sentry transaction for measuring pr creation latency + const sentryTxn = startTransaction({ name: 'pull_request_create' }); + + isPushing = true; + try { + return await branchService.createPr(branch, base.shortName, sentryTxn); + } catch (err) { + toast.error(err as string); + console.error(err); + } finally { + sentryTxn.finish(); + isPushing = false; } - - if (newBranch?.upstreamName != branch.upstreamName) { - capture('branch push mismatch', { new: newBranch?.upstreamName, old: branch.upstreamName }); - } - - if (!newBranch) { - console.error('Branch push failed'); - return; - } - - if (!newBranch.upstreamName) { - toast.error('Cannot create PR without remote branch name'); - return; - } - - const createPrSpan = txn.startChild({ op: 'pr_api_create' }); - const resp = await githubService.createPullRequest( - base.shortName, - newBranch.name, - newBranch.notes, - newBranch.id, - newBranch.upstreamName - ); - - createPrSpan.finish(); - txn.finish(); - isPushing = false; - return resp; } From 3e37ffc095d82c642df46af72eb0061ffcdb515f Mon Sep 17 00:00:00 2001 From: Mattias Granlund Date: Wed, 24 Jan 2024 10:51:20 +0100 Subject: [PATCH 3/3] Go to board instead of base on startup --- gitbutler-ui/src/routes/[projectId]/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitbutler-ui/src/routes/[projectId]/+page.svelte b/gitbutler-ui/src/routes/[projectId]/+page.svelte index 338926501..9bf90149f 100644 --- a/gitbutler-ui/src/routes/[projectId]/+page.svelte +++ b/gitbutler-ui/src/routes/[projectId]/+page.svelte @@ -5,6 +5,6 @@ $: projectId = $page.params.projectId; $: if (projectId) { - goto(`/${projectId}/base`, { replaceState: true }); + goto(`/${projectId}/board`, { replaceState: true }); }