gdiff: define direct unmerged-file behavior #121

Closed
opened 2026-09-21 18:12:22 +00:00 by barrettruth · 0 comments
Owner

Original issue: barrettruth/diffs.nvim#272
Original author: barrettruth
Original date: 2026-05-09T19:21:38Z

Parent tracker: #270

Problem

Direct :Gdiff on an unmerged worktree file can render a misleading generated diff.

Observed in the local real-repo scenario matrix:

  • Fugitive plain :Gdiffsplit on a conflicted worktree file opens stage :1: against the worktree and warns: Use :Gdiffsplit! for 3 way diff.
  • Fugitive :Gdiffsplit! opens native 3-way diff windows for :2:, worktree, and :3:.
  • diffs.nvim :Gdiff currently tries the normal index -> worktree generated edge. Because an unmerged path has no normal stage-0 index entry, the left side can collapse to empty and the conflict-marker worktree file is rendered as a fake new-file diff.

This is not an acceptable generated-workspace behavior. It makes the user think the whole conflicted file is newly added.

Product decision

Do not implement native :Gdiffsplit! parity here. That belongs to the paired-window track (#252/#253).

For generated :Gdiff, direct invocation on an unmerged file should not render the fake new-file diff. Prefer routing to the existing generated unmerged view:

  • old side: ours, git show :2:path
  • new side: theirs, git show :3:path
  • buffer label: diffs://unmerged:path
  • merge keymaps enabled the same way they are from the Fugitive status-row du/dU path

If the implementation finds a reason this is unsafe, it may instead show an explicit warning telling the user to use the Fugitive status-row unmerged action. It must not silently render an empty-index new-file diff.

Relevant existing code

  • lua/diffs/commands.lua
    • M.gdiff() always parses direct command context as current worktree and renders diffspec.index_to_worktree(path) by default.
    • M.gdiff_file(filepath, { unmerged = true }) already renders :2: vs :3: and calls require("diffs.merge").setup_keymaps(...).
    • replace_combined_diffs() also knows how to replace combined diff output with :2: vs :3: generated diffs.
  • lua/diffs/fugitive.lua
    • diff_file_under_cursor() passes unmerged = status == "U" when invoked from a Fugitive status row.
  • lua/diffs/git.lua
    • already has get_file_content(":2", filepath) and get_file_content(":3", filepath) support through is_index_stage().

Likely missing helper:

  • A Git helper to detect whether a path is unmerged, e.g. git ls-files --unmerged -- path.

Acceptance criteria

  • Direct generated :Gdiff never shows an unmerged file as a fake new-file diff.
  • The chosen generated behavior is documented as intentionally different from Fugitive's native :Gdiffsplit / :Gdiffsplit! behavior.
> Original issue: barrettruth/diffs.nvim#272 > Original author: `barrettruth` > Original date: 2026-05-09T19:21:38Z Parent tracker: #270 ## Problem Direct `:Gdiff` on an unmerged worktree file can render a misleading generated diff. Observed in the local real-repo scenario matrix: - Fugitive plain `:Gdiffsplit` on a conflicted worktree file opens stage `:1:` against the worktree and warns: `Use :Gdiffsplit! for 3 way diff`. - Fugitive `:Gdiffsplit!` opens native 3-way diff windows for `:2:`, worktree, and `:3:`. - `diffs.nvim :Gdiff` currently tries the normal `index -> worktree` generated edge. Because an unmerged path has no normal stage-0 index entry, the left side can collapse to empty and the conflict-marker worktree file is rendered as a fake new-file diff. This is not an acceptable generated-workspace behavior. It makes the user think the whole conflicted file is newly added. ## Product decision Do not implement native `:Gdiffsplit!` parity here. That belongs to the paired-window track (#252/#253). For generated `:Gdiff`, direct invocation on an unmerged file should not render the fake new-file diff. Prefer routing to the existing generated unmerged view: - old side: ours, `git show :2:path` - new side: theirs, `git show :3:path` - buffer label: `diffs://unmerged:path` - merge keymaps enabled the same way they are from the Fugitive status-row `du`/`dU` path If the implementation finds a reason this is unsafe, it may instead show an explicit warning telling the user to use the Fugitive status-row unmerged action. It must not silently render an empty-index new-file diff. ## Relevant existing code - `lua/diffs/commands.lua` - `M.gdiff()` always parses direct command context as current worktree and renders `diffspec.index_to_worktree(path)` by default. - `M.gdiff_file(filepath, { unmerged = true })` already renders `:2:` vs `:3:` and calls `require("diffs.merge").setup_keymaps(...)`. - `replace_combined_diffs()` also knows how to replace combined diff output with `:2:` vs `:3:` generated diffs. - `lua/diffs/fugitive.lua` - `diff_file_under_cursor()` passes `unmerged = status == "U"` when invoked from a Fugitive status row. - `lua/diffs/git.lua` - already has `get_file_content(":2", filepath)` and `get_file_content(":3", filepath)` support through `is_index_stage()`. Likely missing helper: - A Git helper to detect whether a path is unmerged, e.g. `git ls-files --unmerged -- path`. ## Acceptance criteria - Direct generated `:Gdiff` never shows an unmerged file as a fake new-file diff. - The chosen generated behavior is documented as intentionally different from Fugitive's native `:Gdiffsplit` / `:Gdiffsplit!` behavior.
barrettruth 2026-09-21 18:12:22 +00:00
  • closed this issue
  • added the
    bug
    label
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
barrettruth/diffs.nvim#121
No description provided.