dotfiles/neovim/config/plugins/codediff.lua

96 lines
3.6 KiB
Lua
Raw Normal View History

vim.pack.add({ 'https://github.com/esmuellert/codediff.nvim' })
require('codediff').setup({
keymaps = {
-- q closes the diff tab from inside it; <leader>gc is here too so the
-- same key that opened things from the g/diff group also shuts them.
view = { quit = { 'q', '<leader>gc' } },
},
})
local function map(lhs, rhs, desc)
vim.keymap.set('n', lhs, rhs, { desc = desc })
end
map('<leader>gd', '<Cmd>CodeDiff<CR>', 'Diff working tree')
map('<leader>gh', '<Cmd>CodeDiff history %<CR>', 'File history')
map('<leader>gH', '<Cmd>CodeDiff history<CR>', 'Branch history')
Add git sync, and stop trusting the cached default branch BuildEmpire/Totara renamed its default branch from main to totara-20. Nothing local noticed, because refs/remotes/origin/HEAD is written once at clone time and remote.<name>.followRemoteHEAD defaults to `create`, which only fills the ref in when it is missing. So `git default-branch` still said main, as did nvim's diff-against-branch prompt. bin/git-sync does the start-of-work sequence for a fork - fast-forward the default branch from upstream, push it to origin - and works out which branch that is by asking the server, not the cache. It sets the cached HEADs from the answer, so everything reading that ref agrees afterwards. It lives in bin/ rather than as an alias because git picks up git-<name> on the $PATH as a subcommand, and this is more shell than a gitconfig alias should hold. followRemoteHEAD = always is set for origin as well, so an ordinary fetch keeps the ref current without running the script. The nvim prompt now prefers origin, then upstream, then the remaining remotes. It took the first remote alphabetically before, which in be-edition means kdog - a colleague's fork - rather than anything authoritative. Verified against a fixture of bare repos whose default branch is totara-20 and whose cached HEAD is stale: the branch is created tracking origin when absent, fast-forwarded when behind, pushed to the fork, refuses to merge when the local copy has diverged, and is a no-op on a second run. The nvim prompt was checked in three real repos.
2026-08-27 16:05:49 +01:00
-- A remote's cached HEAD, or nil if it has none. Kept current by
-- remote.origin.followRemoteHEAD in the gitconfig and by `git sync`; without
-- those this is whatever the default branch was at clone time.
local function remote_head(remote)
local prefix = 'refs/remotes/' .. remote .. '/'
-- --quiet so a remote with no cached HEAD stays silent rather than having its
-- error text come back as the branch name.
local ref = vim.fn.systemlist('git symbolic-ref --quiet ' .. prefix .. 'HEAD')[1]
if not ref or ref:sub(1, #prefix) ~= prefix then
return nil
end
return ref:sub(#prefix + 1)
end
-- The base branch name varies per repo (main, master, totara-20, ...), so ask a
-- remote rather than hardcoding one. origin first: in the fork workflow that is
-- my fork, and `git sync` keeps its default branch level with upstream's. Only
-- then upstream, then whatever remotes exist - `git remote` sorts them
-- alphabetically, so first-available on its own can hand back a colleague's fork.
local function default_branch()
Add git sync, and stop trusting the cached default branch BuildEmpire/Totara renamed its default branch from main to totara-20. Nothing local noticed, because refs/remotes/origin/HEAD is written once at clone time and remote.<name>.followRemoteHEAD defaults to `create`, which only fills the ref in when it is missing. So `git default-branch` still said main, as did nvim's diff-against-branch prompt. bin/git-sync does the start-of-work sequence for a fork - fast-forward the default branch from upstream, push it to origin - and works out which branch that is by asking the server, not the cache. It sets the cached HEADs from the answer, so everything reading that ref agrees afterwards. It lives in bin/ rather than as an alias because git picks up git-<name> on the $PATH as a subcommand, and this is more shell than a gitconfig alias should hold. followRemoteHEAD = always is set for origin as well, so an ordinary fetch keeps the ref current without running the script. The nvim prompt now prefers origin, then upstream, then the remaining remotes. It took the first remote alphabetically before, which in be-edition means kdog - a colleague's fork - rather than anything authoritative. Verified against a fixture of bare repos whose default branch is totara-20 and whose cached HEAD is stale: the branch is created tracking origin when absent, fast-forwarded when behind, pushed to the fork, refuses to merge when the local copy has diverged, and is a no-op on a second run. The nvim prompt was checked in three real repos.
2026-08-27 16:05:49 +01:00
local candidates = { 'origin', 'upstream' }
vim.list_extend(candidates, vim.fn.systemlist('git remote'))
for _, remote in ipairs(candidates) do
local branch = remote_head(remote)
if branch then
return branch
end
end
Add git sync, and stop trusting the cached default branch BuildEmpire/Totara renamed its default branch from main to totara-20. Nothing local noticed, because refs/remotes/origin/HEAD is written once at clone time and remote.<name>.followRemoteHEAD defaults to `create`, which only fills the ref in when it is missing. So `git default-branch` still said main, as did nvim's diff-against-branch prompt. bin/git-sync does the start-of-work sequence for a fork - fast-forward the default branch from upstream, push it to origin - and works out which branch that is by asking the server, not the cache. It sets the cached HEADs from the answer, so everything reading that ref agrees afterwards. It lives in bin/ rather than as an alias because git picks up git-<name> on the $PATH as a subcommand, and this is more shell than a gitconfig alias should hold. followRemoteHEAD = always is set for origin as well, so an ordinary fetch keeps the ref current without running the script. The nvim prompt now prefers origin, then upstream, then the remaining remotes. It took the first remote alphabetically before, which in be-edition means kdog - a colleague's fork - rather than anything authoritative. Verified against a fixture of bare repos whose default branch is totara-20 and whose cached HEAD is stale: the branch is created tracking origin when absent, fast-forwarded when behind, pushed to the fork, refuses to merge when the local copy has diverged, and is a no-op on a second run. The nvim prompt was checked in three real repos.
2026-08-27 16:05:49 +01:00
return ''
end
map('<leader>gm', function()
vim.ui.input({ prompt = 'Diff against: ', default = default_branch() }, function(rev)
if rev and rev ~= '' then
-- rev...HEAD is merge-base semantics: what this branch added, not what
-- the base branch has moved on to since.
vim.cmd('CodeDiff ' .. rev .. '...HEAD')
end
end)
end, 'Diff against branch')
-- A commit-ish under the cursor, or nil. Lets <leader>gr be pressed straight on
-- a sha pasted in a commit message, a code comment or a fugitive/log buffer.
-- ^{commit} so a tag or tree does not pass as something diffable, and --quiet
-- so a cword like 'function' fails silently instead of shouting.
local function commit_under_cursor()
local cword = vim.fn.expand('<cword>')
if cword == '' then
return nil
end
vim.fn.systemlist('git rev-parse --verify --quiet ' .. vim.fn.shellescape(cword) .. '^{commit}')
if vim.v.shell_error ~= 0 then
return nil
end
return cword
end
map('<leader>gr', function()
local default = commit_under_cursor() or 'HEAD'
vim.ui.input({ prompt = 'Review commit: ', default = default }, function(rev)
if rev and rev ~= '' then
-- rev^ rev, not `history`, so the explorer opens on that one commit's
-- files. A merge commit diffs against its first parent, as git does.
vim.cmd('CodeDiff ' .. rev .. '^ ' .. rev)
end
end)
end, 'Review a commit')
map('<leader>gR', function()
vim.ui.input({ prompt = 'Commits since: ', default = default_branch() }, function(rev)
if rev and rev ~= '' then
-- Commit-by-commit rather than squashed: the history panel lists each
-- commit the branch is missing, oldest first, so they read in the order
-- they were made.
vim.cmd('CodeDiff history ' .. rev .. '..HEAD --reverse')
end
end)
end, 'Review commits since branch')