Skip to content

Dirtying Large File Indefinitely Suspends Git Line Blame #3066

@gluxon

Description

@gluxon

Description

I love GitLens and get a lot of value out of it. The "GitLens Current Line Blame" is particularly valuable and something I use all the time. Multiple times a day, I've noticed VS Code get into a state where the line blame disappears.

Here's a screenshot of the feature I'm referring to:

Screenshot 2023-12-19 at 9 27 35 PM

Here's a screenshot of what it looks like when the feature mysteriously disappears. When this happens, it completely disappears and is not present as a status bar item when I right click on the bottom status bar.

Screenshot 2023-12-19 at 9 28 25 PM

I usually have to restart the VS Code extension server to get out of this state.

Investigating

I've had this problem for multiple years. It's been very difficult to replicate since it usually happens a few hours into my work day and hadn't appeared in the past when I cloned the GitLens repo and started an Extension Development Host window.

Today, I was feeling particularly motivated to debug and figured out how to attach an interactive debugger with source maps to an already running Extension Host process after it got into this bad state. 😎

I noticed that this.updateState is never called when I get into the bad state due to this.suspended always evaluating to true here.

Screenshot 2023-12-19 at 4 54 34 PM

Theory

I'm not exactly sure how this.suspended became true indefinitely in my editor today, but reading GitLens's source code, I see one reproducible way this could happen:

  1. A user edits a file causing the editor state to become "dirty" and this.suspend() to be called.

private onDirtyStateChanged(e: DocumentDirtyStateChangeEvent<GitDocumentState>) {
if (e.dirty) {
this.suspend();
} else {
this.resume({ force: true });
}
}

  1. After the user stops editing the file for 5 seconds (the advanced.blame.delayAfterEdit time), the onDirtyIdleTriggered callback is fired and this.resume() is supposed to unsuspend GitLineTracker.

private onDirtyIdleTriggered(e: DocumentDirtyIdleTriggerEvent<GitDocumentState>) {
const maxLines = configuration.get('advanced.blame.sizeThresholdAfterEdit');
if (maxLines > 0 && e.document.lineCount > maxLines) return;
this.resume();
}

As the code above shows, this intentionally doesn't resume the GitLineTracker if the dirty file is >5000 lines long (or advanced.blame.sizeThresholdAfterEdit). That part feels correct.

However, what's surprising (and this code doesn't seem to handle), is the case when users switch tabs or open a different file after the idle timeout. The GitLineTracker shared instance is still suspended for the previous large file and never resumes for other smaller files in other tabs.

Steps to Reproduce

I was excited to see the theory above is indeed reproducible. Using the current latest version at the time of writing (v14.6.1) and the GitLens code base itself:

  1. Open src/trackers/gitLineTracker.ts and observe the current line blame updating properly as the cursor moves.
  2. Open yarn.lock (currently at 7582 lines) and make an edit.
  3. Wait 5 seconds.
  4. Close the file and do not save changes.
  5. Observe the current line blame no longer appear in gitLineTracker.ts or any other file.
Screen.Recording.2023-12-19.at.7.24.27.PM.mov

Pull Request

Here's one possible fix. #3067


GitLens Version

v14.6.1

VS Code Version

Version: 1.85.1
Commit: 0ee08df0cf4527e40edc9aa28f4b5bd38bbff2b2
Date: 2023-12-13T09:48:16.874Z
Electron: 25.9.7
ElectronBuildId: 25551756
Chromium: 114.0.5735.289
Node.js: 18.15.0
V8: 11.4.183.29-electron.0
OS: Darwin arm64 23.2.0

Git Version

git version 2.43.0

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions