diff --git a/src/commands/discardAtPointCommands.ts b/src/commands/discardAtPointCommands.ts index 070f78d..f3c99ec 100644 --- a/src/commands/discardAtPointCommands.ts +++ b/src/commands/discardAtPointCommands.ts @@ -18,6 +18,7 @@ import { RemoteBranchListingView } from '../views/remotes/remoteBranchListingVie import * as Constants from '../common/constants'; import ViewUtils from '../utils/viewUtils'; import { View } from '../views/general/view'; +import { IExecutionResult } from '../utils/commandRunner/command'; export async function magitDiscardAtPoint(repository: MagitRepository, currentView: DocumentView): Promise { @@ -54,7 +55,47 @@ async function discard(repository: MagitRepository, selection: Selection, select if (changeView.section === Section.Unstaged) { const args = ['checkout', '--', change.uri.fsPath]; - return gitRun(repository.gitRepository, args); + try { + let result = await gitRun(repository.gitRepository, args); + return result; + } catch (err: unknown) { + const error = err as IExecutionResult; + // Ask to checkout our stage, their stage, or to leave conflicts for manual resolving + if (error.stderr.includes(`is unmerged`)) { + let selection = await MagitUtils.selectAction(`For ${change.relativePath} checkout`, [ + { key: 'o', description: '[o]ur stage' }, + { key: 't', description: '[t]heir stage' }, + { key: 'c', description: '[c]onflict' } + ]); + let which = ''; + // Also run `git add` when taking our or their stage + let addAfter = false; + switch (selection) { + case undefined: + return error; + case 'o': + which = '--ours'; + addAfter = true; + break; + case 't': + which = '--theirs'; + addAfter = true; + break; + case 'c': + which = '--merge'; + break; + } + const args = ['checkout', which, '--', change.uri.fsPath]; + await gitRun(repository.gitRepository, args); + + if (addAfter) { + const addArgs = ['add', '-u', '--', change.uri.fsPath]; + return gitRun(repository.gitRepository, addArgs); + } else { + return; + } + } + } } else { if (!changeView.change.diff) { return; diff --git a/src/utils/magitUtils.ts b/src/utils/magitUtils.ts index 42bbf1f..1830cdc 100644 --- a/src/utils/magitUtils.ts +++ b/src/utils/magitUtils.ts @@ -10,6 +10,11 @@ import { PickMenuItem, PickMenuUtil } from '../menu/pickMenu'; import GitTextUtils from '../utils/gitTextUtils'; import * as Constants from '../common/constants'; +export interface Selection { + key: string; + description: string; +} + export default class MagitUtils { public static getMagitRepoThatContainsFile(uri: Uri): MagitRepository | undefined { @@ -231,5 +236,51 @@ export default class MagitUtils { _inputBox.show(); }); } + + public static async selectAction(prompt: string, options: Selection[]): Promise { + + const optionsStr = options.reduce((acc, current) => { + const { description } = current; + if (acc !== '') { + return acc.concat(', ', description); + } else { + return acc.concat(description); + } + }, ''); + let renderedPrompt = `${prompt}: Select one of ${optionsStr} or [q] to abort`; + + return new Promise(resolve => { + + let resolveOnHide = true; + let selection = options[0].key; + + const _inputBox = window.createInputBox(); + _inputBox.validationMessage = renderedPrompt; + + let changeListener = _inputBox.onDidChangeValue(e => { + const input = e.toLocaleLowerCase(); + let found = options.find((selection) => selection.key === input); + if (found) { + resolveOnHide = false; + _inputBox.hide(); + resolve(found.key); + } else if (e.toLowerCase().includes('q')) { + _inputBox.hide(); + } + }); + + let onHideListener = _inputBox.onDidHide(() => { + _inputBox.dispose(); + changeListener.dispose(); + onHideListener.dispose(); + if (resolveOnHide) { + window.setStatusBarMessage('Abort', Constants.StatusMessageDisplayTimeout); + resolve(undefined); + } + }); + + _inputBox.show(); + }); + } }