Skip to content

Follow vim-fugitive's flows in diff and merge views - #32

Open
CoreyKaylor wants to merge 1 commit into
mainfrom
fugitive-support
Open

CoreyKaylor wants to merge 1 commit into
mainfrom
fugitive-support

Conversation

@CoreyKaylor

@CoreyKaylor CoreyKaylor commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

Fixes #31.

Problem

Asked in a comment on #1: can you walk fugitive's :Git mergetool list through :DiffBanditMerge with :cnext and :cprev? You couldn't, and trying could overwrite conflicted files with base text. #31 lists the rest of the gaps.

What changes

Quickfix following. Merge and repository diff views follow the list in place, from any pane or the quickfix window. Neovim has no event for these jumps, and one within a file changes no buffer, so host/quickfix compares the list position on SafeState. It restores the panes and the view switches files. Plain :DiffBandit diffs don't follow.

Comparisons and commits. Fugitive's list context records both sides of each entry, and those map onto DiffBandit's modes:

Fugitive command View
:Git difftool unstaged
:Git difftool --cached staged
:Git difftool <commit> all against that commit
:Git difftool A B, A..B, A...B rev, read-only
:Gclog, :0Gclog, -L That commit's review

Fugitive's buffers. Git commands no longer fail on fugitive:// names, including in linked worktrees and submodules. In the status buffer they use the file or commit under the cursor and its section.

fugitive.auto_open. Off by default. true enables everything, or { mergetool = true, difftool = true, dd = true } picks. On lines with no file, dd runs fugitive's own mapping.

Fixes that don't need fugitive

  • Closing an untouched merge left base text in an already open buffer, so :wall replaced the conflicted file. The buffer is now restored, including its unsaved changes and cursor. Edits are kept per file and resume on return.
  • Leaving a file wiped its buffer, so the next jump failed with E92. Existing buffers now stay with their owner, and DiffBandit's own are unloaded instead of wiped.
  • A jump from a diff view's left pane ended the session. Those scratch panes now stay alive until dispose.

Worth a close look

  • Merge entry mapping. The Nth entry for a file selects its Nth conflict, because entry lines count marker lines the result doesn't have. The tests confirm git and the model agree on conflict counts across the fixtures. If they differ on a real file, the jump clamps to the last conflict and could land on the wrong one.
  • A list overrides an explicit mode once you walk it. --staged wins when a view opens, but the first jump adopts the list's comparison.
  • Fugitive's internals. git/fugitive and git/fugitive_auto read its status lines, list context and event name. Unknown shapes fall back to plain behavior. The pin won't catch format changes until it's bumped.
  • :edit on the current entry's file counts as a jump, because it looks the same as :cc.

Known limits

  • :Git difftool --cached <commit> with a commit other than HEAD keeps the view's comparison, because staged only compares against HEAD.
  • :DiffBanditGitCurrent on :Gedit HEAD~1:a.txt shows the file's current changes, not that revision.
  • :Gdiffsplit and :Git difftool -y still use vimdiff.
  • After a switch, an untouched merge's header reads saved rather than modified. The panel's switching already behaved this way, but following makes it easier to see.
  • Views opened from a range show the full commit hash in the left header.
  • Location lists aren't followed, and :DiffBanditMerge inside a merge tab still opens another tab.

Testing

nvim -l tests/run_all.lua: all 23 jobs pass on three local runs, and tests/smoke/tmux.sh passes. CI passes on Linux too, including the fugitive tests. It now also fetches fugitive at a pinned commit through tests/deps.sh.

  • test_fugitive_merge.lua and test_fugitive_diff.lua drive real vim-fugitive rather than a simulated list. Most of them fail against the code from before each change. The rest pass there by design, because they check that something stays unchanged.
  • I walked the main flows by hand in a real terminal under tmux, with and without auto_open. Those were one-off runs on macOS with Neovim 0.12.6, and I didn't try the Kitty connector.

Merge and repository diff views now follow the quickfix list, so :cnext and
:cprev move them in place from any pane. There is no event for these jumps, and
one within a file changes no buffer, so host/quickfix compares the list position
on SafeState. Merge selects the Nth conflict for a file's Nth entry, because
entry lines count marker lines the result lacks. Diff views use the line number.

Entries from :Git difftool [range] and :Gclog move the view to the comparison or
commit they describe. Git commands now work from fugitive's buffers instead of
failing on the fugitive:// name, and in the status buffer they use the line under
the cursor. Everything that reads fugitive's formats is in git/fugitive.

fugitive.auto_open, off by default, opens DiffBandit from :Git mergetool,
:Git difftool, and dd in the status buffer.

Three bugs fixed on the way do not need fugitive. Closing an untouched merge left
base text in an already open buffer, so :wall replaced the conflicted file; it is
now restored, and edits are kept per file. Leaving a file wiped its buffer, which
broke quickfix entries with E92; existing buffers now stay with their owner, and
DiffBandit's own are unloaded instead. A jump from a diff view's left pane wiped
its scratch buffer and ended the session.

Tests run against real vim-fugitive, pinned in tests/deps.sh.

Fixes #31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow vim-fugitive's quickfix flows in diff and merge views

1 participant