refactor(git): unify around the Repo abstraction

This commit is contained in:
2026-05-02 22:45:44 +02:00
parent 8bd674622e
commit be1d7ace50
14 changed files with 671 additions and 586 deletions
+32 -45
View File
@@ -32,14 +32,13 @@ local SIDEBAR_WIDTH = 50
---@alias ow.Git.SidebarEntry ow.Git.FileEntry | ow.Git.CommitEntry
---@class ow.Git.SidebarState
---@field gitdir string
---@field worktree string
---@field repo ow.Git.Repo
---@field lines table<integer, ow.Git.SidebarEntry>
---@field sidebar_win integer?
---@field invocation_win integer?
---@field diff_left_win integer?
---@field diff_right_win integer?
---@field user_aucmd integer?
---@field unsubscribe fun()?
---@field last_shown_key string?
---@field last_render_key string?
@@ -407,7 +406,7 @@ local function refresh(bufnr, prefetched_stdout)
end
end
fetch_status(s.worktree, prefetched_stdout, function(branch, groups)
fetch_status(s.repo.worktree, prefetched_stdout, function(branch, groups)
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end
@@ -456,22 +455,22 @@ end
---@field left ow.Git.DiffSide
---@field right ow.Git.DiffSide
---@param worktree string
---@param r ow.Git.Repo
---@param path string
---@return ow.Git.DiffSide
local function head_pane(worktree, path)
local function head_pane(r, path)
local rev = Revision.new({ base = "HEAD", path = path })
return {
buf = object.buf_for(worktree, rev),
name = rev:uri(),
buf = object.buf_for(r, rev),
name = object.format_uri(rev),
}
end
---@param worktree string
---@param r ow.Git.Repo
---@param path string
---@return ow.Git.DiffSide
local function worktree_pane(worktree, path)
local buf = vim.fn.bufadd(vim.fs.joinpath(worktree, path))
local function worktree_pane(r, path)
local buf = vim.fn.bufadd(vim.fs.joinpath(r.worktree, path))
vim.fn.bufload(buf)
return { buf = buf, name = nil }
end
@@ -482,8 +481,8 @@ end
local function index_pane(s, entry)
local rev = Revision.new({ stage = 0, path = entry.path })
return {
buf = object.buf_for(s.worktree, rev),
name = rev:uri(),
buf = object.buf_for(s.repo, rev),
name = object.format_uri(rev),
}
end
@@ -495,7 +494,7 @@ local function older_pane(s, entry)
if entry.x == "A" then
return nil
end
return head_pane(s.worktree, entry.orig or entry.path)
return head_pane(s.repo, entry.orig or entry.path)
end
if entry.section == "Unstaged" then
return index_pane(s, entry)
@@ -517,10 +516,10 @@ local function newer_pane(s, entry)
if entry.y == "D" then
return nil
end
return worktree_pane(s.worktree, entry.path)
return worktree_pane(s.repo, entry.path)
end
if entry.section == "Untracked" then
return worktree_pane(s.worktree, entry.path)
return worktree_pane(s.repo, entry.path)
end
return nil
end
@@ -695,8 +694,9 @@ local function view_entry(s, entry, focus_left)
+ vim.api.nvim_win_get_width(right_win)
vim.api.nvim_win_set_width(left_win, math.floor(combined / 2))
end
---@cast left_win -nil
---@cast right_win -nil
assert(left_win and right_win, "diff windows must be set")
vim.w[left_win].git_diff_role = "left"
vim.w[right_win].git_diff_role = "right"
s.diff_left_win = left_win
@@ -728,7 +728,7 @@ local function action_stage()
end
vim.system(
{ "git", "add", "--", entry.path },
{ cwd = s.worktree },
{ cwd = s.repo.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
util.error("git add failed: %s", vim.trim(obj.stderr or ""))
@@ -753,7 +753,7 @@ local function action_unstage()
table.insert(cmd, entry.path)
vim.system(
cmd,
{ cwd = s.worktree },
{ cwd = s.repo.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
util.error(
@@ -785,7 +785,7 @@ local function action_discard()
entry.path
)
action = function()
local target = vim.fs.joinpath(s.worktree, entry.path)
local target = vim.fs.joinpath(s.repo.worktree, entry.path)
local rc = vim.fn.delete(target, is_dir and "rf" or "")
if rc ~= 0 then
util.error("failed to delete %s", entry.path)
@@ -797,7 +797,7 @@ local function action_discard()
action = function()
vim.system(
{ "git", "checkout", "--", entry.path },
{ cwd = s.worktree },
{ cwd = s.repo.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
util.error(
@@ -829,20 +829,14 @@ local function action_help()
}, "\n"))
end
---@param worktree string
local function open(worktree)
---@param r ow.Git.Repo
local function open(r)
local existing = find_sidebar()
if existing then
vim.api.nvim_set_current_win(existing)
return
end
local gitdir, worktree = repo.resolve(worktree)
if not gitdir then
return
end
---@cast worktree -nil
local previous_win = vim.api.nvim_get_current_win()
local bufnr, win = util.new_scratch({ split = "left" })
vim.bo[bufnr].filetype = "gitsidebar"
@@ -856,8 +850,7 @@ local function open(worktree)
vim.api.nvim_win_set_width(win, SIDEBAR_WIDTH)
state[bufnr] = {
gitdir = gitdir,
worktree = worktree,
repo = r,
lines = {},
sidebar_win = win,
invocation_win = previous_win,
@@ -882,15 +875,9 @@ local function open(worktree)
k("X", action_discard, "Discard worktree changes")
k("g?", action_help, "Help")
state[bufnr].user_aucmd = vim.api.nvim_create_autocmd("User", {
pattern = "GitRefresh",
group = group,
callback = function(args)
if args.data and args.data.gitdir == gitdir then
refresh(bufnr, args.data.porcelain_stdout)
end
end,
})
state[bufnr].unsubscribe = r:on_refresh(function(_, porcelain_stdout)
refresh(bufnr, porcelain_stdout)
end)
vim.api.nvim_create_autocmd({ "BufWipeout", "BufDelete" }, {
buffer = bufnr,
group = group,
@@ -899,8 +886,8 @@ local function open(worktree)
if not s then
return
end
if s.user_aucmd then
pcall(vim.api.nvim_del_autocmd, s.user_aucmd)
if s.unsubscribe then
s.unsubscribe()
end
state[bufnr] = nil
end,
@@ -916,12 +903,12 @@ function M.toggle()
vim.api.nvim_win_close(sidebar_win, false)
return
end
local _, worktree = repo.current_repo()
if not worktree then
local r = repo.find()
if not r then
util.warning("not in a git repository")
return
end
open(worktree)
open(r)
end
return M