diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 15532bfc28b..e8c155102a3 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -62,21 +62,18 @@ class CheckoutRemoteHeadItem extends CheckoutItem { class BranchDeleteItem implements QuickPickItem { - protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } - protected get treeish(): string | undefined { return this.ref.name; } - get label(): string { return this.ref.name || this.shortCommit; } + private get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } + get branchName(): string | undefined { return this.ref.name; } + get label(): string { return this.branchName || ''; } get description(): string { return this.shortCommit; } - constructor(protected ref: Ref) { } + constructor(private ref: Ref) { } - async run(model: Model): Promise { - const ref = this.treeish; - - if (!ref) { + async run(model: Model, force?: boolean): Promise { + if (!this.branchName) { return; } - - await model.deleteBranch(ref); + await model.deleteBranch(this.branchName, force); } } @@ -720,22 +717,40 @@ export class CommandCenter { } @command('git.deleteBranch') - async deleteBranch(branchName: string): Promise { - if (typeof branchName === 'string') { - return await this.model.deleteBranch(branchName); - } - const currentHead = this.model.HEAD && this.model.HEAD.name; - const heads = this.model.refs.filter(ref => ref.type === RefType.Head && ref.name !== currentHead) - .map(ref => new BranchDeleteItem(ref)); + async deleteBranch(name: string, force?: boolean): Promise { + let run: (force?: boolean) => Promise; + if (typeof name === 'string') { + run = force => this.model.deleteBranch(name, force); + } else { + const currentHead = this.model.HEAD && this.model.HEAD.name; + const heads = this.model.refs.filter(ref => ref.type === RefType.Head && ref.name !== currentHead) + .map(ref => new BranchDeleteItem(ref)); - const placeHolder = 'Select a branch to delete'; - const choice = await window.showQuickPick(heads, { placeHolder }); + const placeHolder = 'Select a branch to delete'; + const choice = await window.showQuickPick(heads, { placeHolder }); - if (!choice) { - return; + if (!choice) { + return; + } + name = choice.branchName || ''; + run = force => choice.run(this.model, force); } - await choice.run(this.model); + try { + await run(force); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.BranchNotFullyMerged) { + throw err; + } + + const message = localize('confirm force delete branch', "The branch '{0}' is not fully merged. Delete anyway?", name); + const yes = localize('delete branch', "Delete Branch"); + const pick = await window.showWarningMessage(message, yes); + + if (pick === yes) { + await run(true); + } + } } @command('git.pull') diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index edba69ad2ed..9ef482def93 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -273,7 +273,8 @@ export const GitErrorCodes = { CantCreatePipe: 'CantCreatePipe', CantAccessRemote: 'CantAccessRemote', RepositoryNotFound: 'RepositoryNotFound', - RepositoryIsLocked: 'RepositoryIsLocked' + RepositoryIsLocked: 'RepositoryIsLocked', + BranchNotFullyMerged: 'BranchNotFullyMerged' }; function getGitErrorCode(stderr: string): string | undefined { @@ -291,6 +292,8 @@ function getGitErrorCode(stderr: string): string | undefined { return GitErrorCodes.RepositoryNotFound; } else if (/unable to access/.test(stderr)) { return GitErrorCodes.CantAccessRemote; + } else if (/branch '.+' is not fully merged/.test(stderr)) { + return GitErrorCodes.BranchNotFullyMerged; } return void 0; @@ -650,8 +653,8 @@ export class Repository { await this.run(args); } - async deleteBranch(name: string): Promise { - const args = ['branch', '-d', name]; + async deleteBranch(name: string, force?: boolean): Promise { + const args = ['branch', force ? '-D' : '-d', name]; await this.run(args); } diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 1ce7f8287a9..aba4a4251d6 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -454,8 +454,8 @@ export class Model implements Disposable { await this.run(Operation.Branch, () => this.repository.branch(name, true)); } - async deleteBranch(name: string): Promise { - await this.run(Operation.DeleteBranch, () => this.repository.deleteBranch(name)); + async deleteBranch(name: string, force?: boolean): Promise { + await this.run(Operation.DeleteBranch, () => this.repository.deleteBranch(name, force)); } async checkout(treeish: string): Promise {