Git - add worktrees node to the Repositories view (#284224)

* Worktree node - initial implementation

* Wire up various commands
This commit is contained in:
Ladislau Szomoru
2025-12-18 10:48:41 +00:00
committed by GitHub
parent 900e7cb348
commit 150682a14a
8 changed files with 198 additions and 43 deletions

View File

@@ -1110,6 +1110,32 @@
"icon": "$(trash)",
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.repositories.createWorktree",
"title": "%command.createWorktree%",
"icon": "$(plus)",
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.repositories.openWorktree",
"title": "%command.openWorktree2%",
"icon": "$(folder-opened)",
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.repositories.openWorktreeInNewWindow",
"title": "%command.openWorktreeInNewWindow2%",
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.repositories.deleteWorktree",
"title": "%command.deleteRef%",
"category": "Git",
"enablement": "!operationInProgress"
}
],
"continueEditSession": [
@@ -1789,6 +1815,22 @@
{
"command": "git.repositories.stashDrop",
"when": "false"
},
{
"command": "git.repositories.createWorktree",
"when": "false"
},
{
"command": "git.repositories.openWorktree",
"when": "false"
},
{
"command": "git.repositories.openWorktreeInNewWindow",
"when": "false"
},
{
"command": "git.repositories.deleteWorktree",
"when": "false"
}
],
"scm/title": [
@@ -1994,6 +2036,11 @@
"submenu": "git.repositories.stash",
"group": "inline@1",
"when": "scmProvider == git && scmArtifactGroup == stashes"
},
{
"command": "git.repositories.createWorktree",
"group": "inline@1",
"when": "scmProvider == git && scmArtifactGroup == worktrees"
}
],
"scm/artifact/context": [
@@ -2067,6 +2114,26 @@
"command": "git.repositories.compareRef",
"group": "4_compare@1",
"when": "scmProvider == git && (scmArtifactGroupId == branches || scmArtifactGroupId == tags)"
},
{
"command": "git.repositories.openWorktree",
"group": "inline@1",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
},
{
"command": "git.repositories.openWorktree",
"group": "1_open@1",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
},
{
"command": "git.repositories.openWorktreeInNewWindow",
"group": "1_open@2",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
},
{
"command": "git.repositories.deleteWorktree",
"group": "2_modify@1",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
}
],
"scm/resourceGroup/context": [

View File

@@ -11,7 +11,9 @@
"command.close": "Close Repository",
"command.closeOtherRepositories": "Close Other Repositories",
"command.openWorktree": "Open Worktree in Current Window",
"command.openWorktree2": "Open",
"command.openWorktreeInNewWindow": "Open Worktree in New Window",
"command.openWorktreeInNewWindow2": "Open in New Window",
"command.refresh": "Refresh",
"command.compareWithWorkspace": "Compare with Workspace",
"command.openChange": "Open Changes",

View File

@@ -76,6 +76,14 @@ export interface Remote {
readonly isReadOnly: boolean;
}
export interface Worktree {
readonly name: string;
readonly path: string;
readonly ref: string;
readonly detached: boolean;
readonly commitDetails?: Commit;
}
export const enum Status {
INDEX_MODIFIED,
INDEX_ADDED,

View File

@@ -6,16 +6,16 @@
import { LogOutputChannel, SourceControlArtifactProvider, SourceControlArtifactGroup, SourceControlArtifact, Event, EventEmitter, ThemeIcon, l10n, workspace, Uri, Disposable, Command } from 'vscode';
import { dispose, filterEvent, IDisposable } from './util';
import { Repository } from './repository';
import { Ref, RefType } from './api/git';
import { Commit, Ref, RefType } from './api/git';
import { OperationKind } from './operation';
function getArtifactDescription(ref: Ref, shortCommitLength: number): string {
function getArtifactDescription(commit: string | undefined, commitDetails: Commit | undefined, shortCommitLength: number): string {
const segments: string[] = [];
if (ref.commit) {
segments.push(ref.commit.substring(0, shortCommitLength));
if (commit) {
segments.push(commit.substring(0, shortCommitLength));
}
if (ref.commitDetails?.message) {
segments.push(ref.commitDetails.message.split('\n')[0]);
if (commitDetails?.message) {
segments.push(commitDetails.message.split('\n')[0]);
}
return segments.join(' \u2022 ');
@@ -67,6 +67,13 @@ function sortRefByName(refA: Ref, refB: Ref): number {
return 0;
}
function sortByCommitDateDesc(a: { commitDetails?: Commit }, b: { commitDetails?: Commit }): number {
const aCommitDate = a.commitDetails?.commitDate?.getTime() ?? 0;
const bCommitDate = b.commitDetails?.commitDate?.getTime() ?? 0;
return bCommitDate - aCommitDate;
}
export class GitArtifactProvider implements SourceControlArtifactProvider, IDisposable {
private readonly _onDidChangeArtifacts = new EventEmitter<string[]>();
readonly onDidChangeArtifacts: Event<string[]> = this._onDidChangeArtifacts.event;
@@ -81,7 +88,8 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
this._groups = [
{ id: 'branches', name: l10n.t('Branches'), icon: new ThemeIcon('git-branch'), supportsFolders: true },
{ id: 'stashes', name: l10n.t('Stashes'), icon: new ThemeIcon('git-stash'), supportsFolders: false },
{ id: 'tags', name: l10n.t('Tags'), icon: new ThemeIcon('tag'), supportsFolders: true }
{ id: 'tags', name: l10n.t('Tags'), icon: new ThemeIcon('tag'), supportsFolders: true },
{ id: 'worktrees', name: l10n.t('Worktrees'), icon: new ThemeIcon('list-tree'), supportsFolders: false }
];
this._disposables.push(this._onDidChangeArtifacts);
@@ -104,6 +112,8 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
this._disposables.push(onDidRunWriteOperation(result => {
if (result.operation.kind === OperationKind.Stash) {
this._onDidChangeArtifacts.fire(['stashes']);
} else if (result.operation.kind === OperationKind.Worktree) {
this._onDidChangeArtifacts.fire(['worktrees']);
}
}));
}
@@ -124,7 +134,7 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
return refs.sort(sortRefByName).map(r => ({
id: `refs/heads/${r.name}`,
name: r.name ?? r.commit ?? '',
description: getArtifactDescription(r, shortCommitLength),
description: getArtifactDescription(r.commit, r.commitDetails, shortCommitLength),
icon: this.repository.HEAD?.type === RefType.Head && r.name === this.repository.HEAD?.name
? new ThemeIcon('target')
: new ThemeIcon('git-branch'),
@@ -137,7 +147,7 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
return refs.sort(sortRefByName).map(r => ({
id: `refs/tags/${r.name}`,
name: r.name ?? r.commit ?? '',
description: getArtifactDescription(r, shortCommitLength),
description: getArtifactDescription(r.commit, r.commitDetails, shortCommitLength),
icon: this.repository.HEAD?.type === RefType.Tag && r.name === this.repository.HEAD?.name
? new ThemeIcon('target')
: new ThemeIcon('tag'),
@@ -157,6 +167,22 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
command: 'git.repositories.stashView'
} satisfies Command
}));
} else if (group === 'worktrees') {
const worktrees = await this.repository.getWorktreeDetails();
return worktrees.sort(sortByCommitDateDesc).map(w => {
const description = getArtifactDescription(w.commitDetails?.hash, w.commitDetails, shortCommitLength);
return {
id: w.path,
name: w.name,
description: w.detached
? `${l10n.t('detached')} \u2022 ${description}`
: `${w.ref.substring(11)} \u2022 ${description}`,
icon: new ThemeIcon('list-tree'),
timestamp: w.commitDetails?.commitDate?.getTime(),
};
});
}
} catch (err) {
this.logger.error(`[GitArtifactProvider][provideArtifacts] Error while providing artifacts for group '${group}': `, err);

View File

@@ -8,8 +8,8 @@ import * as path from 'path';
import { Command, commands, Disposable, MessageOptions, Position, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, languages, SourceControlArtifact } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import { ForcePushMode, GitErrorCodes, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref } from './api/git';
import { Git, GitError, Stash, Worktree } from './git';
import { ForcePushMode, GitErrorCodes, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref, Worktree } from './api/git';
import { Git, GitError, Stash } from './git';
import { Model } from './model';
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges, compareLineChanges } from './staging';
@@ -3437,6 +3437,10 @@ export class CommandCenter {
return;
}
await this._createWorktree(repository);
}
async _createWorktree(repository: Repository): Promise<void> {
const config = workspace.getConfiguration('git');
const branchPrefix = config.get<string>('branchPrefix')!;
@@ -5083,6 +5087,15 @@ export class CommandCenter {
await this._createTag(repository);
}
@command('git.repositories.createWorktree', { repository: true })
async artifactGroupCreateWorktree(repository: Repository): Promise<void> {
if (!repository) {
return;
}
await this._createWorktree(repository);
}
@command('git.repositories.checkout', { repository: true })
async artifactCheckout(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
if (!repository || !artifact) {
@@ -5293,6 +5306,35 @@ export class CommandCenter {
await this._stashDrop(repository, parseInt(match[1]), artifact.name);
}
@command('git.repositories.openWorktree', { repository: true })
async artifactOpenWorktree(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
if (!repository || !artifact) {
return;
}
const uri = Uri.file(artifact.id);
await commands.executeCommand('vscode.openFolder', uri, { forceReuseWindow: true });
}
@command('git.repositories.openWorktreeInNewWindow', { repository: true })
async artifactOpenWorktreeInNewWindow(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
if (!repository || !artifact) {
return;
}
const uri = Uri.file(artifact.id);
await commands.executeCommand('vscode.openFolder', uri, { forceNewWindow: true });
}
@command('git.repositories.deleteWorktree', { repository: true })
async artifactDeleteWorktree(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
if (!repository || !artifact) {
return;
}
await repository.deleteWorktree(artifact.id);
}
private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => {
let result: Promise<any>;

View File

@@ -13,7 +13,7 @@ import { EventEmitter } from 'events';
import * as filetype from 'file-type';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant, relativePathWithNoFallback, Mutable } from './util';
import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode';
import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery as ApiRefQuery, InitOptions } from './api/git';
import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery as ApiRefQuery, InitOptions, Worktree } from './api/git';
import * as byline from 'byline';
import { StringDecoder } from 'string_decoder';
@@ -867,12 +867,6 @@ export class GitStatusParser {
}
}
export interface Worktree {
readonly name: string;
readonly path: string;
readonly ref: string;
}
export interface Submodule {
name: string;
path: string;
@@ -2826,14 +2820,6 @@ export class Repository {
}
private async getWorktreesFS(): Promise<Worktree[]> {
const config = workspace.getConfiguration('git', Uri.file(this.repositoryRoot));
const shouldDetectWorktrees = config.get<boolean>('detectWorktrees') === true;
if (!shouldDetectWorktrees) {
this.logger.info('[Git][getWorktreesFS] Worktree detection is disabled, skipping worktree detection');
return [];
}
try {
// List all worktree folder names
const worktreesPath = path.join(this.dotGit.commonPath ?? this.dotGit.path, 'worktrees');
@@ -2858,6 +2844,8 @@ export class Repository {
path: gitdirContent.replace(/\/.git.*$/, ''),
// Remove 'ref: ' prefix
ref: headContent.replace(/^ref: /, ''),
// Detached if HEAD does not start with 'ref: '
detached: !headContent.startsWith('ref: ')
});
} catch (err) {
if (/ENOENT/.test(err.message)) {

View File

@@ -32,7 +32,6 @@ export const enum OperationKind {
GetObjectDetails = 'GetObjectDetails',
GetObjectFiles = 'GetObjectFiles',
GetRefs = 'GetRefs',
GetWorktrees = 'GetWorktrees',
GetRemoteRefs = 'GetRemoteRefs',
HashObject = 'HashObject',
Ignore = 'Ignore',
@@ -69,8 +68,8 @@ export const enum OperationKind {
export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchOperation | CheckIgnoreOperation | CherryPickOperation |
CheckoutOperation | CheckoutTrackingOperation | CleanOperation | CommitOperation | ConfigOperation | DeleteBranchOperation |
DeleteRefOperation | DeleteRemoteRefOperation | DeleteTagOperation | DeleteWorktreeOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation |
GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetObjectFilesOperation | GetRefsOperation | GetWorktreesOperation |
DeleteRefOperation | DeleteRemoteRefOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation |
GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetObjectFilesOperation | GetRefsOperation |
GetRemoteRefsOperation | HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation |
MergeBaseOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation |
RemoveOperation | ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | RevertFilesOperation |
@@ -93,7 +92,6 @@ export type DeleteBranchOperation = BaseOperation & { kind: OperationKind.Delete
export type DeleteRefOperation = BaseOperation & { kind: OperationKind.DeleteRef };
export type DeleteRemoteRefOperation = BaseOperation & { kind: OperationKind.DeleteRemoteRef };
export type DeleteTagOperation = BaseOperation & { kind: OperationKind.DeleteTag };
export type DeleteWorktreeOperation = BaseOperation & { kind: OperationKind.DeleteWorktree };
export type DiffOperation = BaseOperation & { kind: OperationKind.Diff };
export type FetchOperation = BaseOperation & { kind: OperationKind.Fetch };
export type FindTrackingBranchesOperation = BaseOperation & { kind: OperationKind.FindTrackingBranches };
@@ -103,7 +101,6 @@ export type GetCommitTemplateOperation = BaseOperation & { kind: OperationKind.G
export type GetObjectDetailsOperation = BaseOperation & { kind: OperationKind.GetObjectDetails };
export type GetObjectFilesOperation = BaseOperation & { kind: OperationKind.GetObjectFiles };
export type GetRefsOperation = BaseOperation & { kind: OperationKind.GetRefs };
export type GetWorktreesOperation = BaseOperation & { kind: OperationKind.GetWorktrees };
export type GetRemoteRefsOperation = BaseOperation & { kind: OperationKind.GetRemoteRefs };
export type HashObjectOperation = BaseOperation & { kind: OperationKind.HashObject };
export type IgnoreOperation = BaseOperation & { kind: OperationKind.Ignore };
@@ -153,7 +150,6 @@ export const Operation = {
DeleteRef: { kind: OperationKind.DeleteRef, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteRefOperation,
DeleteRemoteRef: { kind: OperationKind.DeleteRemoteRef, blocking: false, readOnly: false, remote: true, retry: false, showProgress: true } as DeleteRemoteRefOperation,
DeleteTag: { kind: OperationKind.DeleteTag, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteTagOperation,
DeleteWorktree: { kind: OperationKind.DeleteWorktree, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteWorktreeOperation,
Diff: { kind: OperationKind.Diff, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as DiffOperation,
Fetch: (showProgress: boolean) => ({ kind: OperationKind.Fetch, blocking: false, readOnly: false, remote: true, retry: true, showProgress } as FetchOperation),
FindTrackingBranches: { kind: OperationKind.FindTrackingBranches, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as FindTrackingBranchesOperation,
@@ -163,7 +159,6 @@ export const Operation = {
GetObjectDetails: { kind: OperationKind.GetObjectDetails, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectDetailsOperation,
GetObjectFiles: { kind: OperationKind.GetObjectFiles, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectFilesOperation,
GetRefs: { kind: OperationKind.GetRefs, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetRefsOperation,
GetWorktrees: { kind: OperationKind.GetWorktrees, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetWorktreesOperation,
GetRemoteRefs: { kind: OperationKind.GetRemoteRefs, blocking: false, readOnly: true, remote: true, retry: false, showProgress: false } as GetRemoteRefsOperation,
HashObject: { kind: OperationKind.HashObject, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as HashObjectOperation,
Ignore: { kind: OperationKind.Ignore, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as IgnoreOperation,
@@ -195,7 +190,7 @@ export const Operation = {
SubmoduleUpdate: { kind: OperationKind.SubmoduleUpdate, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as SubmoduleUpdateOperation,
Sync: { kind: OperationKind.Sync, blocking: true, readOnly: false, remote: true, retry: true, showProgress: true } as SyncOperation,
Tag: { kind: OperationKind.Tag, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as TagOperation,
Worktree: { kind: OperationKind.Worktree, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as WorktreeOperation
Worktree: (readOnly: boolean) => ({ kind: OperationKind.Worktree, blocking: false, readOnly, remote: false, retry: false, showProgress: true } as WorktreeOperation)
};
export interface OperationResult {

View File

@@ -10,11 +10,11 @@ import picomatch from 'picomatch';
import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, FileDecoration, FileType, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, QuickDiffProvider, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, ThemeIcon, Uri, window, workspace, WorkspaceEdit } from 'vscode';
import { ActionButton } from './actionButton';
import { ApiRepository } from './api/api1';
import { Branch, BranchQuery, Change, CommitOptions, FetchOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } from './api/git';
import { Branch, BranchQuery, Change, CommitOptions, FetchOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, Worktree } from './api/git';
import { AutoFetcher } from './autofetch';
import { GitBranchProtectionProvider, IBranchProtectionProviderRegistry } from './branchProtection';
import { debounce, memoize, sequentialize, throttle } from './decorators';
import { Repository as BaseRepository, BlameInformation, Commit, CommitShortStat, GitError, LogFileOptions, LsTreeElement, PullOptions, RefQuery, Stash, Submodule, Worktree } from './git';
import { Repository as BaseRepository, BlameInformation, Commit, CommitShortStat, GitError, LogFileOptions, LsTreeElement, PullOptions, RefQuery, Stash, Submodule } from './git';
import { GitHistoryProvider } from './historyProvider';
import { Operation, OperationKind, OperationManager, OperationResult } from './operation';
import { CommitCommandsCenter, IPostCommitCommandsProviderRegistry } from './postCommitCommands';
@@ -1760,7 +1760,37 @@ export class Repository implements Disposable {
}
async getWorktrees(): Promise<Worktree[]> {
return await this.run(Operation.GetWorktrees, () => this.repository.getWorktrees());
return await this.run(Operation.Worktree(true), () => this.repository.getWorktrees());
}
async getWorktreeDetails(): Promise<Worktree[]> {
return this.run(Operation.Worktree(true), async () => {
const worktrees = await this.repository.getWorktrees();
if (worktrees.length === 0) {
return [];
}
// Get refs for worktrees that point to a ref
const worktreeRefs = worktrees
.filter(worktree => !worktree.detached)
.map(worktree => worktree.ref);
// Get the commit details for worktrees that point to a ref
const refs = await this.getRefs({ pattern: worktreeRefs, includeCommitDetails: true });
// Get the commit details for detached worktrees
const commits = await Promise.all(worktrees
.filter(worktree => worktree.detached)
.map(worktree => this.repository.getCommit(worktree.ref)));
return worktrees.map(worktree => {
const commitDetails = worktree.detached
? commits.find(commit => commit.hash === worktree.ref)
: refs.find(ref => `refs/heads/${ref.name}` === worktree.ref)?.commitDetails;
return { ...worktree, commitDetails } satisfies Worktree;
});
});
}
async getRemoteRefs(remote: string, opts?: { heads?: boolean; tags?: boolean }): Promise<Ref[]> {
@@ -1796,7 +1826,7 @@ export class Repository implements Disposable {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const branchPrefix = config.get<string>('branchPrefix', '');
return await this.run(Operation.Worktree, async () => {
return await this.run(Operation.Worktree(false), async () => {
let worktreeName: string | undefined;
let { path: worktreePath, commitish, branch } = options || {};
@@ -1835,15 +1865,12 @@ export class Repository implements Disposable {
}
async deleteWorktree(path: string, options?: { force?: boolean }): Promise<void> {
await this.run(Operation.DeleteWorktree, async () => {
await this.run(Operation.Worktree(false), async () => {
const worktree = this.repositoryResolver.getRepository(path);
if (!worktree || worktree.kind !== 'worktree') {
return;
}
const deleteWorktree = async (options?: { force?: boolean }): Promise<void> => {
await this.repository.deleteWorktree(path, options);
worktree.dispose();
worktree?.dispose();
};
try {