Skip to content

Commit dd052e8

Browse files
rework deletion flow (microsoft#256415)
* rework deletion flow * change delete label * Revise deletion of worktree * reopen correct repo * clean up * Refactor deleteWorkspace command * Do not show deleteWorktree command in the command palette --------- Co-authored-by: Ladislau Szomoru <[email protected]>
1 parent 4d63b40 commit dd052e8

File tree

7 files changed

+55
-39
lines changed

7 files changed

+55
-39
lines changed

extensions/git/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@
13071307
},
13081308
{
13091309
"command": "git.deleteWorktree",
1310-
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
1310+
"when": "false"
13111311
},
13121312
{
13131313
"command": "git.deleteRemoteTag",
@@ -2571,6 +2571,7 @@
25712571
"group": "worktrees@1"
25722572
},
25732573
{
2574+
"when": "scmProviderContext == worktree",
25742575
"command": "git.deleteWorktree",
25752576
"group": "worktrees@2"
25762577
}

extensions/git/package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"command.createTag": "Create Tag...",
7777
"command.deleteTag": "Delete Tag...",
7878
"command.createWorktree": "Create Worktree...",
79-
"command.deleteWorktree": "Delete Worktree...",
79+
"command.deleteWorktree": "Delete Worktree",
8080
"command.deleteRemoteTag": "Delete Remote Tag...",
8181
"command.fetch": "Fetch",
8282
"command.fetchPrune": "Fetch (Prune)",

extensions/git/src/api/git.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,5 +440,6 @@ export const enum GitErrorCodes {
440440
BranchNotYetBorn = 'BranchNotYetBorn',
441441
TagConflict = 'TagConflict',
442442
CherryPickEmpty = 'CherryPickEmpty',
443-
CherryPickConflict = 'CherryPickConflict'
443+
CherryPickConflict = 'CherryPickConflict',
444+
WorktreeContainsChanges = 'WorktreeContainsChanges'
444445
}

extensions/git/src/commands.ts

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Command, commands, Disposable, MessageOptions, Position, ProgressLocati
99
import TelemetryReporter from '@vscode/extension-telemetry';
1010
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
1111
import { ForcePushMode, GitErrorCodes, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref } from './api/git';
12-
import { Git, Stash, Worktree } from './git';
12+
import { Git, Stash } from './git';
1313
import { Model } from './model';
1414
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
1515
import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges, compareLineChanges } from './staging';
@@ -57,19 +57,6 @@ class RefItemSeparator implements QuickPickItem {
5757
constructor(private readonly refType: RefType) { }
5858
}
5959

60-
class WorktreeItem implements QuickPickItem {
61-
62-
get label(): string {
63-
return `$(list-tree) ${this.worktree.name}`;
64-
}
65-
66-
get description(): string {
67-
return this.worktree.path;
68-
}
69-
70-
constructor(readonly worktree: Worktree) { }
71-
}
72-
7360
class RefItem implements QuickPickItem {
7461

7562
get label(): string {
@@ -228,14 +215,6 @@ class RemoteTagDeleteItem extends RefItem {
228215
}
229216
}
230217

231-
class WorktreeDeleteItem extends WorktreeItem {
232-
async run(repository: Repository): Promise<void> {
233-
if (this.worktree.path) {
234-
await repository.deleteWorktree(this.worktree.path);
235-
}
236-
}
237-
}
238-
239218
class MergeItem extends BranchItem {
240219

241220
async run(repository: Repository): Promise<void> {
@@ -3409,18 +3388,35 @@ export class CommandCenter {
34093388

34103389
@command('git.deleteWorktree', { repository: true })
34113390
async deleteWorktree(repository: Repository): Promise<void> {
3412-
const worktreePicks = async (): Promise<WorktreeDeleteItem[] | QuickPickItem[]> => {
3413-
const worktrees = await repository.getWorktrees();
3414-
return worktrees.length === 0
3415-
? [{ label: l10n.t('$(info) This repository has no worktrees.') }]
3416-
: worktrees.map(worktree => new WorktreeDeleteItem(worktree));
3417-
};
3391+
if (!repository.dotGit.commonPath) {
3392+
return;
3393+
}
3394+
3395+
const mainRepository = this.model.getRepository(path.dirname(repository.dotGit.commonPath));
3396+
if (!mainRepository) {
3397+
return;
3398+
}
34183399

3419-
const placeHolder = l10n.t('Select a worktree to delete');
3420-
const choice = await this.pickRef<WorktreeDeleteItem | QuickPickItem>(worktreePicks(), placeHolder);
3400+
// Dispose worktree repository
3401+
this.model.disposeRepository(repository);
34213402

3422-
if (choice instanceof WorktreeDeleteItem) {
3423-
await choice.run(repository);
3403+
try {
3404+
await mainRepository.deleteWorktree(repository.root);
3405+
} catch (err) {
3406+
if (err.gitErrorCode === GitErrorCodes.WorktreeContainsChanges) {
3407+
const forceDelete = l10n.t('Force Delete');
3408+
const message = l10n.t('The worktree contains modified or untracked files. Do you want to force delete?');
3409+
const choice = await window.showWarningMessage(message, { modal: true }, forceDelete);
3410+
if (choice === forceDelete) {
3411+
await mainRepository.deleteWorktree(repository.root, { force: true });
3412+
} else {
3413+
await this.model.openRepository(repository.root);
3414+
}
3415+
3416+
return;
3417+
}
3418+
3419+
throw err;
34243420
}
34253421
}
34263422

extensions/git/src/git.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ function getGitErrorCode(stderr: string): string | undefined {
349349
return GitErrorCodes.DirtyWorkTree;
350350
} else if (/detected dubious ownership in repository at/.test(stderr)) {
351351
return GitErrorCodes.NotASafeGitRepository;
352+
} else if (/contains modified or untracked files|use --force to delete it/.test(stderr)) {
353+
return GitErrorCodes.WorktreeContainsChanges;
352354
}
353355

354356
return undefined;
@@ -2038,8 +2040,14 @@ export class Repository {
20382040
await this.exec(args);
20392041
}
20402042

2041-
async deleteWorktree(path: string): Promise<void> {
2042-
const args = ['worktree', 'remove', path];
2043+
async deleteWorktree(path: string, options?: { force?: boolean }): Promise<void> {
2044+
const args = ['worktree', 'remove'];
2045+
2046+
if (options?.force) {
2047+
args.push('--force');
2048+
}
2049+
2050+
args.push(path);
20432051
await this.exec(args);
20442052
}
20452053

extensions/git/src/model.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,16 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi
11401140
}
11411141
}
11421142

1143+
disposeRepository(repository: Repository): void {
1144+
const openRepository = this.getOpenRepository(repository);
1145+
if (!openRepository) {
1146+
return;
1147+
}
1148+
1149+
this.logger.info(`[Model][disposeRepository] Repository: ${repository.root}`);
1150+
openRepository.dispose();
1151+
}
1152+
11431153
dispose(): void {
11441154
const openRepositories = [...this.openRepositories];
11451155
openRepositories.forEach(r => r.dispose());

extensions/git/src/repository.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,8 +1721,8 @@ export class Repository implements Disposable {
17211721
await this.run(Operation.Worktree, () => this.repository.worktree(options));
17221722
}
17231723

1724-
async deleteWorktree(path: string): Promise<void> {
1725-
await this.run(Operation.DeleteWorktree, () => this.repository.deleteWorktree(path));
1724+
async deleteWorktree(path: string, options?: { force?: boolean }): Promise<void> {
1725+
await this.run(Operation.DeleteWorktree, () => this.repository.deleteWorktree(path, options));
17261726
}
17271727

17281728
async deleteRemoteRef(remoteName: string, refName: string, options?: { force?: boolean }): Promise<void> {

0 commit comments

Comments
 (0)