refactor(git): move porcelain parsing into repo.lua

This commit is contained in:
2026-05-03 00:46:53 +02:00
parent be1d7ace50
commit 6703b8acba
2 changed files with 136 additions and 120 deletions
+14 -108
View File
@@ -17,19 +17,12 @@ local SECTIONS = {
}
local SIDEBAR_WIDTH = 50
---@class ow.Git.FileEntry
---@field section string
---@field path string
---@field orig string?
---@field x string
---@field y string
---@class ow.Git.CommitEntry
---@field section string
---@field section "Unpushed"|"Unpulled"
---@field sha string
---@field subject string?
---@alias ow.Git.SidebarEntry ow.Git.FileEntry | ow.Git.CommitEntry
---@alias ow.Git.SidebarEntry ow.Git.StatusEntry | ow.Git.CommitEntry
---@class ow.Git.SidebarState
---@field repo ow.Git.Repo
@@ -108,100 +101,13 @@ local function format_entry(entry)
return string.format(" %s %s", char, label), hl, #char
end
---@class ow.Git.BranchInfo
---@field head string?
---@field upstream string?
---@field ahead integer
---@field behind integer
---@param line string
---@return ow.Git.BranchInfo
local function parse_branch_line(line)
local info = { ahead = 0, behind = 0 }
local content = line:sub(4)
local arrow = content:find("...", 1, true)
if not arrow then
info.head = content
return info
end
info.head = content:sub(1, arrow - 1)
local rest = content:sub(arrow + 3)
local bracket = rest:find(" %[")
if not bracket then
info.upstream = rest
return info
end
info.upstream = rest:sub(1, bracket - 1)
local inside = rest:match("%[([^%]]+)%]")
if inside then
info.ahead = (tonumber(inside:match("ahead (%d+)")) or 0) --[[@as integer]]
info.behind = (tonumber(inside:match("behind (%d+)")) or 0) --[[@as integer]]
end
return info
end
---@param stdout string
---@return ow.Git.BranchInfo, table<string, ow.Git.SidebarEntry[]>
local function parse_porcelain(stdout)
local branch = { ahead = 0, behind = 0 }
local groups = {
Untracked = {},
Unstaged = {},
Staged = {},
Unmerged = {},
Unpushed = {},
Unpulled = {},
}
for line in stdout:gmatch("[^\r\n]+") do
if line:sub(1, 2) == "##" then
branch = parse_branch_line(line)
else
local x = line:sub(1, 1)
local y = line:sub(2, 2)
local rest = line:sub(4)
local orig
if x == "R" or x == "C" or y == "R" or y == "C" then
local arrow = rest:find(" -> ", 1, true)
if arrow then
orig = rest:sub(1, arrow - 1)
rest = rest:sub(arrow + 4)
end
end
local entry = {
section = nil,
path = rest,
orig = orig,
x = x,
y = y,
}
if x == "?" and y == "?" then
entry.section = "Untracked"
table.insert(groups.Untracked, entry)
elseif status.UNMERGED[x .. y] then
entry.section = "Unmerged"
table.insert(groups.Unmerged, entry)
else
if x ~= " " then
table.insert(groups.Staged, {
section = "Staged",
path = entry.path,
orig = entry.orig,
x = entry.x,
y = entry.y,
})
end
if y ~= " " then
table.insert(groups.Unstaged, {
section = "Unstaged",
path = entry.path,
orig = entry.orig,
x = entry.x,
y = entry.y,
})
end
end
end
end
local branch, groups = repo.parse_porcelain(stdout)
---@cast groups table<string, ow.Git.SidebarEntry[]>
groups.Unpushed = {}
groups.Unpulled = {}
return branch, groups
end
@@ -476,7 +382,7 @@ local function worktree_pane(r, path)
end
---@param s ow.Git.SidebarState
---@param entry ow.Git.FileEntry
---@param entry ow.Git.StatusEntry
---@return ow.Git.DiffSide
local function index_pane(s, entry)
local rev = Revision.new({ stage = 0, path = entry.path })
@@ -487,7 +393,7 @@ local function index_pane(s, entry)
end
---@param s ow.Git.SidebarState
---@param entry ow.Git.FileEntry
---@param entry ow.Git.StatusEntry
---@return ow.Git.DiffSide?
local function older_pane(s, entry)
if entry.section == "Staged" then
@@ -503,7 +409,7 @@ local function older_pane(s, entry)
end
---@param s ow.Git.SidebarState
---@param entry ow.Git.FileEntry
---@param entry ow.Git.StatusEntry
---@return ow.Git.DiffSide?
local function newer_pane(s, entry)
if entry.section == "Staged" then
@@ -581,7 +487,7 @@ local function adopt_diff_wins(s, sidebar_win)
return left, right
end
---@param entry ow.Git.FileEntry
---@param entry ow.Git.StatusEntry
---@return string
local function entry_key(entry)
return entry.section .. "|" .. entry.path .. "|" .. (entry.orig or "")
@@ -626,7 +532,7 @@ local function view_entry(s, entry, focus_left)
if not entry.path then
return
end
---@cast entry ow.Git.FileEntry
---@cast entry ow.Git.StatusEntry
local sidebar_win = sidebar_win_for(s)
if not sidebar_win then
return
@@ -722,7 +628,7 @@ local function action_stage()
if not s or not entry or not entry.path then
return
end
---@cast entry ow.Git.FileEntry
---@cast entry ow.Git.StatusEntry
if entry.section == "Staged" then
return
end
@@ -742,7 +648,7 @@ local function action_unstage()
if not s or not entry or not entry.path then
return
end
---@cast entry ow.Git.FileEntry
---@cast entry ow.Git.StatusEntry
if entry.section ~= "Staged" then
return
end
@@ -770,7 +676,7 @@ local function action_discard()
if not s or not entry or not entry.path then
return
end
---@cast entry ow.Git.FileEntry
---@cast entry ow.Git.StatusEntry
if entry.section == "Staged" then
util.warning("file has staged changes, unstage first with 'u'")
return