From 30fa83020763e71ff89bb749a6214ae5cda5bbc0 Mon Sep 17 00:00:00 2001 From: Keith Daulton Date: Thu, 9 Nov 2023 15:01:46 -0500 Subject: [PATCH] Applies patch to branch --- .../webviews/patchDetails/patchDetailsWebview.ts | 37 ++++++++++++++-- src/plus/webviews/patchDetails/protocol.ts | 1 + src/quickpicks/branchPicker.ts | 51 ++++++++++++++++++++++ .../patchDetails/components/gl-draft-details.ts | 20 ++++++++- .../apps/plus/patchDetails/patchDetails.scss | 4 ++ .../apps/plus/patchDetails/patchDetails.ts | 6 ++- .../apps/shared/components/webview-pane.ts | 2 +- 7 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/quickpicks/branchPicker.ts diff --git a/src/plus/webviews/patchDetails/patchDetailsWebview.ts b/src/plus/webviews/patchDetails/patchDetailsWebview.ts index a0a224a..678d14c 100644 --- a/src/plus/webviews/patchDetails/patchDetailsWebview.ts +++ b/src/plus/webviews/patchDetails/patchDetailsWebview.ts @@ -1,7 +1,7 @@ import type { ConfigurationChangeEvent } from 'vscode'; import { Disposable, env, Uri, window } from 'vscode'; import type { CoreConfiguration } from '../../../constants'; -import { Commands } from '../../../constants'; +import { Commands, GlyphChars } from '../../../constants'; import type { Container } from '../../../container'; import { openChanges, openChangesWithWorking, openFile } from '../../../git/actions/commit'; import type { RepositoriesChangeEvent } from '../../../git/gitProviderService'; @@ -13,6 +13,7 @@ import { createReference } from '../../../git/models/reference'; import { isRepository } from '../../../git/models/repository'; import type { CreateDraftChange, Draft, DraftPatch, DraftPatchFileChange, LocalDraft } from '../../../gk/models/drafts'; import type { GkRepositoryId } from '../../../gk/models/repositoryIdentities'; +import { showBranchPicker } from '../../../quickpicks/branchPicker'; import { executeCommand, registerCommand } from '../../../system/command'; import { configuration } from '../../../system/configuration'; import { setContext } from '../../../system/context'; @@ -321,6 +322,8 @@ export class PatchDetailsWebviewProvider const changeset = this._context.draft.changesets?.[0]; if (changeset == null) return; + // TODO: should be overridable with targetRef + const shouldPickBranch = params.target === 'branch'; for (const patch of changeset.patches) { if (!params.selected.includes(patch.id)) continue; @@ -335,9 +338,35 @@ export class PatchDetailsWebviewProvider continue; } - void this.container.git.applyPatchCommit(commit.repoPath, commit.ref, { - branchName: patch.baseBranchName, - }); + let options: + | { + branchName?: string; + createBranchIfNeeded?: boolean; + createWorktreePath?: string; + } + | undefined = undefined; + + if (shouldPickBranch) { + const repo = commit.getRepository(); + const branch = await showBranchPicker( + `Choose a Branch ${GlyphChars.Dot} ${repo?.name}`, + 'Choose a branch to apply the Cloud Patch to', + repo, + ); + + if (branch == null) { + void window.showErrorMessage( + `Unable apply patch to '${patch.repository!.name}': No branch selected`, + ); + continue; + } + options = { + branchName: branch.ref, + createBranchIfNeeded: true, + }; + } + + void this.container.git.applyPatchCommit(commit.repoPath, commit.ref, options); } catch (ex) { void window.showErrorMessage(`Unable apply patch to '${patch.baseRef}': ${ex.message}`); } diff --git a/src/plus/webviews/patchDetails/protocol.ts b/src/plus/webviews/patchDetails/protocol.ts index 5d586a8..ffc43d4 100644 --- a/src/plus/webviews/patchDetails/protocol.ts +++ b/src/plus/webviews/patchDetails/protocol.ts @@ -168,6 +168,7 @@ export type ShowCommitDetailsViewCommandArgs = string[]; export interface ApplyPatchParams { details: DraftDetails; targetRef?: string; // a branch name. default to HEAD if not supplied + target: 'current' | 'branch' | 'worktree'; selected: PatchDetails['id'][]; } export const ApplyPatchCommandType = new IpcCommandType('patch/apply'); diff --git a/src/quickpicks/branchPicker.ts b/src/quickpicks/branchPicker.ts new file mode 100644 index 0000000..a31a73c --- /dev/null +++ b/src/quickpicks/branchPicker.ts @@ -0,0 +1,51 @@ +import type { Disposable } from 'vscode'; +import { window } from 'vscode'; +import { getBranches } from '../commands/quickCommand.steps'; +import type { Repository } from '../git/models/repository'; +import { getQuickPickIgnoreFocusOut } from '../system/utils'; +import type { BranchQuickPickItem } from './items/gitCommands'; + +export async function showBranchPicker( + title: string | undefined, + placeholder?: string, + repository?: Repository, +): Promise { + if (repository == null) { + return undefined; + } + + const items: BranchQuickPickItem[] = await getBranches(repository, {}); + if (items.length === 0) return undefined; + + const quickpick = window.createQuickPick(); + quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut(); + + const disposables: Disposable[] = []; + + try { + const pick = await new Promise(resolve => { + disposables.push( + quickpick.onDidHide(() => resolve(undefined)), + quickpick.onDidAccept(() => { + if (quickpick.activeItems.length !== 0) { + resolve(quickpick.activeItems[0]); + } + }), + ); + + quickpick.title = title; + quickpick.placeholder = placeholder; + quickpick.matchOnDescription = true; + quickpick.matchOnDetail = true; + quickpick.items = items; + + quickpick.show(); + }); + if (pick == null) return undefined; + + return pick; + } finally { + quickpick.dispose(); + disposables.forEach(d => void d.dispose()); + } +} diff --git a/src/webviews/apps/plus/patchDetails/components/gl-draft-details.ts b/src/webviews/apps/plus/patchDetails/components/gl-draft-details.ts index 8db0d2d..0e57c3c 100644 --- a/src/webviews/apps/plus/patchDetails/components/gl-draft-details.ts +++ b/src/webviews/apps/plus/patchDetails/components/gl-draft-details.ts @@ -345,7 +345,20 @@ export class GlDraftDetails extends GlTreeBase {

- Apply Cloud Patch + Apply Patch + + + + Apply to new branch + + +

@@ -577,7 +590,10 @@ export class GlDraftDetails extends GlTreeBase { } onSelectApplyOption(e: CustomEvent<{ target: MenuItem }>) { - if (this.canSubmit === false) return; + if (this.canSubmit === false) { + this.validityMessage = 'Please select changes to apply'; + return; + } const target = e.detail?.target; if (target?.dataset?.value != null) { diff --git a/src/webviews/apps/plus/patchDetails/patchDetails.scss b/src/webviews/apps/plus/patchDetails/patchDetails.scss index bd10a38..27a07ae 100644 --- a/src/webviews/apps/plus/patchDetails/patchDetails.scss +++ b/src/webviews/apps/plus/patchDetails/patchDetails.scss @@ -160,5 +160,9 @@ gl-patch-create { &__group-fixed { flex: none; + + webview-pane::part(content) { + overflow: visible; + } } } diff --git a/src/webviews/apps/plus/patchDetails/patchDetails.ts b/src/webviews/apps/plus/patchDetails/patchDetails.ts index b909ac5..f3a9a68 100644 --- a/src/webviews/apps/plus/patchDetails/patchDetails.ts +++ b/src/webviews/apps/plus/patchDetails/patchDetails.ts @@ -264,7 +264,11 @@ export class PatchDetailsApp extends App> { private onApplyPatch(e: ApplyPatchDetail) { console.log('onApplyPatch', e); if (e.selectedPatches == null || e.selectedPatches.length === 0) return; - this.sendCommand(ApplyPatchCommandType, { details: e.draft, selected: e.selectedPatches }); + this.sendCommand(ApplyPatchCommandType, { + details: e.draft, + target: e.target ?? 'current', + selected: e.selectedPatches, + }); } private onChangePatchBase(e: ChangePatchBaseDetail) { diff --git a/src/webviews/apps/shared/components/webview-pane.ts b/src/webviews/apps/shared/components/webview-pane.ts index c13a22e..4f78bee 100644 --- a/src/webviews/apps/shared/components/webview-pane.ts +++ b/src/webviews/apps/shared/components/webview-pane.ts @@ -140,7 +140,7 @@ export class WebviewPane extends LitElement { -
+
`;