Skip to content

Rendered diff unavailable for committed changes: git: URIs with a commit ref are stripped, so the command reports "No Markdown changes are available for this file" #59

Description

@wolframarnold

Summary

Rich Markdown Diff works great for uncommitted changes, but it can't render the diff of a committed Markdown file — i.e. when you select a commit in the Source Control Graph (or GitLens), open one of its changed .md files, and get VS Code's text diff editor comparing two commits.

The $(diff) button does appear in the diff editor's title bar, but clicking it shows:

No Markdown changes are available for this file.

This is the one case where the rendered diff would be most valuable — reviewing a document change after the fact — and it's also confusing, because the button's presence promises it will work.

Environment

  • Rich Markdown Diff 1.4.0
  • VS Code 1.129.1, Windows 11 (x64)
  • Built-in Git extension (vscode.git) Source Control Graph view

Steps to reproduce

  1. Commit a change to a .md file.
  2. Open the Source Control Graph view, click the commit, and click the changed .md file. VS Code opens a text diff editor titled file.md (<sha>) ↔ file.md.
  3. Make sure the file has no uncommitted changes in the working tree.
  4. Click the Rich Markdown Diff button in the editor title bar.

Expected: rendered diff of the file as it changed in that commit.
Actual: No Markdown changes are available for this file.

Note that step 3 matters: if the file also has uncommitted edits, the command silently shows the working-tree vs HEAD diff instead of the commit diff you clicked on — arguably a second, quieter form of the same bug.

Root cause (from the 1.4.0 sources)

The commit refs are discarded before the comparison is resolved.

  1. src/commandTarget.tstoFileBackedUri() (L66–81) rewrites any git: URI to a plain file: URI by pulling query.path out, dropping query.ref entirely. In getCommandTarget(), the arg instanceof vscode.Uri branch (L212–218) applies that rewrite and hardcodes comparisonHint: "auto" — it never inspects the ref at all.

  2. src/gitDiffResolver.ts — even on the object-arg path, getComparisonHintFromUris() (L343–367) only recognizes three ref values: "" (index), "~" (working tree), and HEAD. A commit SHA falls through to "auto". And the type itself has nowhere to put a commit:

    export type GitComparisonHint = "auto" | "workingTree" | "index";
  3. src/extension.tsshowDiff() (L1302–1364) reads commandTarget.targetUri and .comparisonHint, but never uses the originalUri / modifiedUri that getCommandTarget already returns (they're populated at commandTarget.ts L246–247). So the commit refs are available and then thrown away.

  4. resolveSingleFileComparison(fileUri, "auto") therefore resolves HEAD ↔ working tree. For a clean file that yields kind: "cleanHeadToWorkingTree", which isActionableSingleFileComparison() rejects when the document isn't dirty → the message.

The button still shows because the editor/title when clause in package.json includes isInDiffEditor, which is true here, while the command's own gate (rich-markdown-diff.canShowRenderedDiff) is false. The menu condition and the command's precondition disagree.

Suggested fix

Most of the machinery already exists. showTwoFilesDiff() (used by the Explorer's "compare 2 selected files" path, extension.ts L1282–1300) already renders an arbitrary URI pair, and readDocumentText() uses vscode.workspace.openTextDocument(), which resolves git: URIs through the Git extension's content provider. So commit-vs-commit content is already readable — it just never gets requested.

A minimal fix:

  1. In getCommandTarget(), stop discarding refs unconditionally: keep the original git: URIs alongside the file-backed targetUri in the vscode.Uri branch too (or at least detect the ref).
  2. In showDiff(), before falling through to resolveSingleFileComparison, check whether commandTarget.originalUri / .modifiedUri carry non-HEAD/non-""/non-~ refs. If so, route straight to the two-URI render path with those URIs.

Two details that would need attention:

  • Labels. showTwoFilesDiff labels each side with path.basename(uri.fsPath). For a commit diff both sides have the same basename, so describeUriPair() walks up parent segments, finds no difference, and falls back to the two full paths — which are identical and unhelpful. Ref-aware labels (file.md (c02e3e4^)file.md (c02e3e4)) would be needed.
  • Watching. showTwoFilesDiff filters watchUris to file: scheme only. That's already the right behavior here — commit blobs are immutable, so there's nothing to watch and no live-refresh needed.

A fuller fix would extend GitComparisonHint / resolveSingleFileComparison with an explicit "commit ref pair" mode so the SCM-context and diff-editor entry points share one resolution path, but the routing shortcut above would cover the common case.

Happy to test a build if that's useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions