Change:
VSCode Version: 1.33.1 OS Version: 10.14.4 When editing and closing a git commit message file very quickly, the contents are not provided to git. Currently I manually save before closing any git commit file but it seems clear this should work properly - autosave should happen, then Code should inform Git that the commit file is ready. Currently it appears the two events are happening in reverse order, causing a bug. Steps to Reproduce: VS Code as editor () a git commit the commit message in Code close the commit message in Code, before autosave occurs Result: git complains about an empty commit message Expected: Autosave should happen before Code releases control back to git, resulting in a properly saved commit message Does this issue occur when all extensions are disabled?: Yes
Good catch. Extra points for using auto save in VSCode :+1:
via

Discussion:
import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { coalesce } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; <ins> import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isEqual } from 'vs/base/common/resources'; </ins> const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))),

@IIntegrityService private readonly integrityService: IIntegrityService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, <del> @IWorkspaceContextService private readonly contextService: IWorkspaceContextService </del> <ins> @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ITextFileService private readonly textFileService: ITextFileService </ins> ) { super();

// Listen to editor closing (if we run with --wait) const filesToWait = this.environmentService.configuration.filesToWait; if (filesToWait) { <del> const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); </del> const waitMarkerFile = filesToWait.waitMarkerFileUri; <del> const listenerDispose = this.editorService.onDidCloseEditor(() => this.onEditorClosed(listenerDispose, resourcesToWaitFor, waitMarkerFile)); </del> <ins> const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); </ins> <del> this._register(listenerDispose); </del> <ins> this._register(this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor)); </ins> } }

} } <del> private onEditorClosed(listenerDispose: IDisposable, resourcesToWaitFor: URI[], waitMarkerFile: URI): void { // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { listenerDispose.dispose(); this.fileService.del(waitMarkerFile); } } </del> private onContextMenu(e: MouseEvent): void { if (e.target instanceof HTMLElement) { const target = <HTMLElement>e.target;

// In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. <del> const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.revive(p.fileUri)); </del> const waitMarkerFile = URI.revive(request.filesToWait.waitMarkerFileUri); <del> const unbind = this.editorService.onDidCloseEditor(() => { if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { unbind.dispose(); this.fileService.del(waitMarkerFile); </del> <ins> const resourcesToWaitFor = coalesce(request.filesToWait.paths.map(p => URI.revive(p.fileUri))); this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor); } } private trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): IDisposable { const listener = this.editorService.onDidCloseEditor(async () => { // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { // If auto save is configured with the default delay (1s) it is possible // to close the editor while the save still continues in the background. As such // we have to also check if the files to wait for are dirty and if so wait // for them to get saved before deleting the wait marker file. const dirtyFilesToWait = this.textFileService.getDirty(resourcesToWaitFor); if (dirtyFilesToWait.length > 0) { await Promise.all(dirtyFilesToWait.map(async dirtyFileToWait => await this.joinResourceSaved(dirtyFileToWait))); } listener.dispose(); await this.fileService.del(waitMarkerFile); } }); return listener; } private joinResourceSaved(resource: URI): Promise<void> { return new Promise(resolve => { if (!this.textFileService.isDirty(resource)) { return resolve(); // return early if resource is not dirty } // Otherwise resolve promise when resource is saved const listener = this.textFileService.models.onModelSaved(e => { if (isEqual(resource, e.resource)) { listener.dispose(); resolve(); </ins> } }); <del> } </del> <ins> }); </ins> } private openResources(resources: Array<IResourceInput | IUntitledResourceInput>, diffMode: boolean): void {