Change:
a merge editor a change, make sure there is one unhandled conflict left the VS Code window (or close and reopen VS Code) the merge editor matter what you do, the changes from step 2 will be written to disk I think this is because step 3 sets the base for the revert operation that takes place at step 5. Thus, when reverting the merge editor changes, it will write the contents of the file after step 3 to disk, no the contents of the file after step 1. I think if we no longer automatically remove conflict markers, we don't need to save and restore the original content anymore: The user does not have auto-save enabled: The user opens a merge editor. The file stays non-dirty. The user modifies the result by clicking on the checkboxes or changing text in the result. This will make the result file dirty. The user closes the file with the "x" on the tab. 1.1) The file contains unhandled conflicts: A prompt shows with the options , and 1.2) The file does not contain unhandled conflicts: A prompt shows with the options , and (same dialog when closing a dirty normal file) The user has auto-save enabled: The user opens a merge editor. The file stays non-dirty. The user modifies the result by clicking on the checkboxes or changing text in the result. The file stays non-dirty and is written to disk. The user closes the file with the "x" on the tab. 2.1) The file contains unhandled conflicts: A prompt shows with the options and 2.2) The file does not contain unhandled conflicts: No prompt is shown Maybe we don't even need the prompt for 2.1). Maybe all prompts could have a checkbox to stage the file after saving it. What do you think?

Discussion:
*--------------------------------------------------------------------------------------------*/ import { DisposableStore } from 'vs/base/common/lifecycle'; <del> import { isEqual } from 'vs/base/common/resources'; </del> <ins> import { basename, isEqual } from 'vs/base/common/resources'; </ins> import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService';

return ConfirmResult.SAVE; } <del> const actions: string[] = [ someAreDirty ? localize('unhandledConflicts.saveAndIgnore', "Save & Continue with Conflicts") : localize('unhandledConflicts.ignore', "Continue with Conflicts"), localize('unhandledConflicts.discard', "Discard Merge Changes"), localize('unhandledConflicts.cancel', "Cancel"), ]; </del> <ins> const result = someAreDirty ? await this._confirmDirty(handler) : await this._confirmNoneDirty(handler); if (result !== ConfirmResult.CANCEL) { // save or ignore: in both cases we tell the inputs to ignore unhandled conflicts // for the dirty state computation. for (const input of handler) { input._ignoreUnhandledConflicts = true; } } return result; } private async _confirmDirty(handler: MergeEditorCloseHandler[]): Promise<ConfirmResult> { const isMany = handler.length > 1; const message = isMany ? localize('messageN', 'Do you want to save the changes you made to {0} files?', handler.length) : localize('message1', 'Do you want to save the changes you made to {0}?', basename(handler[0]._model.resultTextModel.uri)); </ins> const options = { cancelId: 2, <del> detail: handler.length > 1 ? localize('unhandledConflicts.detailN', 'Merge conflicts in {0} editors will remain unhandled.', handler.length) : localize('unhandledConflicts.detail1', 'Merge conflicts in this editor will remain unhandled.') </del> <ins> detail: isMany ? localize('detailN', "The files contain unhandled conflicts. Your changes will be lost if you don't save them.") : localize('detail1', "The file contains unhandled conflicts. Your changes will be lost if you don't save them.") </ins> }; <del> const { choice } = await this._dialogService.show( Severity.Info, localize('unhandledConflicts.msg', 'Do you want to continue with unhandled conflicts?'), // 1 actions, options ); </del> <ins> const actions: string[] = [ localize('saveWithConflict', "Save with Conflicts"), localize('discard', "Don't save"), localize('cancel', "Cancel"), ]; const { choice } = await this._dialogService.show(Severity.Info, message, actions, options); </ins> if (choice === options.cancelId) { // cancel: stay in editor return ConfirmResult.CANCEL; <ins> } else if (choice === 0) { // save with conflicts return ConfirmResult.SAVE; } else { // discard changes return ConfirmResult.DONT_SAVE; </ins> } <ins> } </ins> <del> // save or revert: in both cases we tell the inputs to ignore unhandled conflicts // for the dirty state computation. for (const input of handler) { input._ignoreUnhandledConflicts = true; } </del> <ins> private async _confirmNoneDirty(handler: MergeEditorCloseHandler[]): Promise<ConfirmResult> { const isMany = handler.length > 1; </ins> <del> if (choice === 0) { // conflicts: continue with remaining conflicts return ConfirmResult.SAVE; </del> <ins> const message = isMany ? localize('conflictN', 'Do you want to close with conflicts in {0} files?', handler.length) : localize('conflict1', 'Do you want to close with conflicts in {0}?', basename(handler[0]._model.resultTextModel.uri)); </ins> <del> } else if (choice === 1) { // discard: undo all changes and save original (pre-merge) state for (const input of handler) { input._model.discardMergeChanges(); } return ConfirmResult.SAVE; </del> <ins> const options = { cancelId: 1, detail: isMany ? localize('detailNotDirtyN', "The files contain unhandled conflicts.") : localize('detailNotDirty1', "The file contains unhandled conflicts.") }; const actions = [ localize('closeWithConflicts', "Close with Conflicts"), localize('cancel', "Cancel"), ]; const { choice } = await this._dialogService.show(Severity.Info, message, actions, options); if (choice === options.cancelId) { return ConfirmResult.CANCEL; </ins> } else { <del> // don't save return ConfirmResult.DONT_SAVE; </del> <ins> return ConfirmResult.SAVE; </ins> } } }

return chunks.join(); } <del> public discardMergeChanges(): void { this.resultTextModel.setValue(this.resultSnapshot); } </del> constructor( readonly base: ITextModel, readonly input1: InputData,