diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 24315fcf2327..d26f8ed8e356 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -12,8 +12,12 @@ import { memoize, throttle, debounce } from './decorators'; import { watch } from './watch'; import { Askpass } from './askpass'; import * as path from 'path'; +import * as fs from 'fs'; import * as nls from 'vscode-nls'; +const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); +const exists = (path: string) => new Promise(c => fs.exists(path, c)); + const localize = nls.loadMessageBundle(); const iconsRootPath = path.join(path.dirname(__dirname), 'resources', 'icons'); @@ -367,6 +371,21 @@ export class Model implements Disposable { } } + /** + * Returns promise which resolves when there is no `.git/index.lock` file, + * or when it has attempted way too many times. Back off mechanism. + */ + async whenUnlocked(): Promise { + let millis = 100; + let retries = 0; + + while (retries < 10 && await exists(path.join(this.repository.root, '.git', 'index.lock'))) { + retries += 1; + millis *= 1.4; + await timeout(millis); + } + } + @throttle async init(): Promise { if (this.state !== State.NotAGitRepository) { @@ -512,6 +531,7 @@ export class Model implements Disposable { try { await this.assertIdleState(); + await this.whenUnlocked(); const result = await runOperation(); if (!isReadOnly(operation)) { @@ -657,7 +677,7 @@ export class Model implements Disposable { private async updateWhenIdleAndWait(): Promise { await this.whenIdle(); await this.status(); - await new Promise(c => setTimeout(c, 5000)); + await timeout(5000); } dispose(): void {