refactor(git): move porcelain parsing into repo.lua
This commit is contained in:
+122
-12
@@ -19,6 +19,25 @@ end
|
|||||||
---@field log_max_count integer?
|
---@field log_max_count integer?
|
||||||
---@field pending_content string?
|
---@field pending_content string?
|
||||||
|
|
||||||
|
---@class ow.Git.StatusEntry
|
||||||
|
---@field section "Untracked"|"Unstaged"|"Staged"|"Unmerged"
|
||||||
|
---@field x string
|
||||||
|
---@field y string
|
||||||
|
---@field path string
|
||||||
|
---@field orig string?
|
||||||
|
|
||||||
|
---@class ow.Git.BranchInfo
|
||||||
|
---@field head string?
|
||||||
|
---@field upstream string?
|
||||||
|
---@field ahead integer
|
||||||
|
---@field behind integer
|
||||||
|
|
||||||
|
---@class ow.Git.PorcelainGroups
|
||||||
|
---@field Untracked ow.Git.StatusEntry[]
|
||||||
|
---@field Unstaged ow.Git.StatusEntry[]
|
||||||
|
---@field Staged ow.Git.StatusEntry[]
|
||||||
|
---@field Unmerged ow.Git.StatusEntry[]
|
||||||
|
|
||||||
---@class ow.Git.Repo
|
---@class ow.Git.Repo
|
||||||
---@field gitdir string
|
---@field gitdir string
|
||||||
---@field worktree string
|
---@field worktree string
|
||||||
@@ -30,6 +49,23 @@ end
|
|||||||
local Repo = {}
|
local Repo = {}
|
||||||
Repo.__index = Repo
|
Repo.__index = Repo
|
||||||
|
|
||||||
|
---@param line string
|
||||||
|
---@return string x, string y, string path, string? orig
|
||||||
|
local function parse_porcelain_line(line)
|
||||||
|
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
|
||||||
|
return x, y, rest, orig
|
||||||
|
end
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
---@param r ow.Git.Repo
|
||||||
local function do_refresh(r)
|
local function do_refresh(r)
|
||||||
vim.system(
|
vim.system(
|
||||||
@@ -47,18 +83,9 @@ local function do_refresh(r)
|
|||||||
if obj.code == 0 then
|
if obj.code == 0 then
|
||||||
for line in (obj.stdout or ""):gmatch("[^\r\n]+") do
|
for line in (obj.stdout or ""):gmatch("[^\r\n]+") do
|
||||||
if line:sub(1, 2) ~= "##" then
|
if line:sub(1, 2) ~= "##" then
|
||||||
local code = line:sub(1, 2)
|
local x, y, path = parse_porcelain_line(line)
|
||||||
local x = code:sub(1, 1)
|
statuses[vim.fs.joinpath(r.worktree, path)] =
|
||||||
local y = code:sub(2, 2)
|
status.format(x .. y)
|
||||||
local path_part = line:sub(4)
|
|
||||||
if x == "R" or x == "C" or y == "R" or y == "C" then
|
|
||||||
local arrow = path_part:find(" -> ", 1, true)
|
|
||||||
if arrow then
|
|
||||||
path_part = path_part:sub(arrow + 4)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
statuses[vim.fs.joinpath(r.worktree, path_part)] =
|
|
||||||
status.format(code)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -392,4 +419,87 @@ function M.stop_all()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param line string
|
||||||
|
---@return ow.Git.BranchInfo
|
||||||
|
function M.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, ow.Git.PorcelainGroups
|
||||||
|
function M.parse_porcelain(stdout)
|
||||||
|
local branch = { ahead = 0, behind = 0 }
|
||||||
|
---@type ow.Git.PorcelainGroups
|
||||||
|
local groups = {
|
||||||
|
Untracked = {},
|
||||||
|
Unstaged = {},
|
||||||
|
Staged = {},
|
||||||
|
Unmerged = {},
|
||||||
|
}
|
||||||
|
for line in stdout:gmatch("[^\r\n]+") do
|
||||||
|
if line:sub(1, 2) == "##" then
|
||||||
|
branch = M.parse_branch_line(line)
|
||||||
|
else
|
||||||
|
local x, y, path, orig = parse_porcelain_line(line)
|
||||||
|
if x == "?" and y == "?" then
|
||||||
|
table.insert(groups.Untracked, {
|
||||||
|
section = "Untracked",
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
path = path,
|
||||||
|
orig = orig,
|
||||||
|
})
|
||||||
|
elseif status.UNMERGED[x .. y] then
|
||||||
|
table.insert(groups.Unmerged, {
|
||||||
|
section = "Unmerged",
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
path = path,
|
||||||
|
orig = orig,
|
||||||
|
})
|
||||||
|
else
|
||||||
|
if x ~= " " then
|
||||||
|
table.insert(groups.Staged, {
|
||||||
|
section = "Staged",
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
path = path,
|
||||||
|
orig = orig,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
if y ~= " " then
|
||||||
|
table.insert(groups.Unstaged, {
|
||||||
|
section = "Unstaged",
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
path = path,
|
||||||
|
orig = orig,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return branch, groups
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
+14
-108
@@ -17,19 +17,12 @@ local SECTIONS = {
|
|||||||
}
|
}
|
||||||
local SIDEBAR_WIDTH = 50
|
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
|
---@class ow.Git.CommitEntry
|
||||||
---@field section string
|
---@field section "Unpushed"|"Unpulled"
|
||||||
---@field sha string
|
---@field sha string
|
||||||
---@field subject 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
|
---@class ow.Git.SidebarState
|
||||||
---@field repo ow.Git.Repo
|
---@field repo ow.Git.Repo
|
||||||
@@ -108,100 +101,13 @@ local function format_entry(entry)
|
|||||||
return string.format(" %s %s", char, label), hl, #char
|
return string.format(" %s %s", char, label), hl, #char
|
||||||
end
|
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
|
---@param stdout string
|
||||||
---@return ow.Git.BranchInfo, table<string, ow.Git.SidebarEntry[]>
|
---@return ow.Git.BranchInfo, table<string, ow.Git.SidebarEntry[]>
|
||||||
local function parse_porcelain(stdout)
|
local function parse_porcelain(stdout)
|
||||||
local branch = { ahead = 0, behind = 0 }
|
local branch, groups = repo.parse_porcelain(stdout)
|
||||||
local groups = {
|
---@cast groups table<string, ow.Git.SidebarEntry[]>
|
||||||
Untracked = {},
|
groups.Unpushed = {}
|
||||||
Unstaged = {},
|
groups.Unpulled = {}
|
||||||
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
|
|
||||||
return branch, groups
|
return branch, groups
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -476,7 +382,7 @@ local function worktree_pane(r, path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param s ow.Git.SidebarState
|
---@param s ow.Git.SidebarState
|
||||||
---@param entry ow.Git.FileEntry
|
---@param entry ow.Git.StatusEntry
|
||||||
---@return ow.Git.DiffSide
|
---@return ow.Git.DiffSide
|
||||||
local function index_pane(s, entry)
|
local function index_pane(s, entry)
|
||||||
local rev = Revision.new({ stage = 0, path = entry.path })
|
local rev = Revision.new({ stage = 0, path = entry.path })
|
||||||
@@ -487,7 +393,7 @@ local function index_pane(s, entry)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param s ow.Git.SidebarState
|
---@param s ow.Git.SidebarState
|
||||||
---@param entry ow.Git.FileEntry
|
---@param entry ow.Git.StatusEntry
|
||||||
---@return ow.Git.DiffSide?
|
---@return ow.Git.DiffSide?
|
||||||
local function older_pane(s, entry)
|
local function older_pane(s, entry)
|
||||||
if entry.section == "Staged" then
|
if entry.section == "Staged" then
|
||||||
@@ -503,7 +409,7 @@ local function older_pane(s, entry)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param s ow.Git.SidebarState
|
---@param s ow.Git.SidebarState
|
||||||
---@param entry ow.Git.FileEntry
|
---@param entry ow.Git.StatusEntry
|
||||||
---@return ow.Git.DiffSide?
|
---@return ow.Git.DiffSide?
|
||||||
local function newer_pane(s, entry)
|
local function newer_pane(s, entry)
|
||||||
if entry.section == "Staged" then
|
if entry.section == "Staged" then
|
||||||
@@ -581,7 +487,7 @@ local function adopt_diff_wins(s, sidebar_win)
|
|||||||
return left, right
|
return left, right
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param entry ow.Git.FileEntry
|
---@param entry ow.Git.StatusEntry
|
||||||
---@return string
|
---@return string
|
||||||
local function entry_key(entry)
|
local function entry_key(entry)
|
||||||
return entry.section .. "|" .. entry.path .. "|" .. (entry.orig or "")
|
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
|
if not entry.path then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
---@cast entry ow.Git.FileEntry
|
---@cast entry ow.Git.StatusEntry
|
||||||
local sidebar_win = sidebar_win_for(s)
|
local sidebar_win = sidebar_win_for(s)
|
||||||
if not sidebar_win then
|
if not sidebar_win then
|
||||||
return
|
return
|
||||||
@@ -722,7 +628,7 @@ local function action_stage()
|
|||||||
if not s or not entry or not entry.path then
|
if not s or not entry or not entry.path then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
---@cast entry ow.Git.FileEntry
|
---@cast entry ow.Git.StatusEntry
|
||||||
if entry.section == "Staged" then
|
if entry.section == "Staged" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -742,7 +648,7 @@ local function action_unstage()
|
|||||||
if not s or not entry or not entry.path then
|
if not s or not entry or not entry.path then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
---@cast entry ow.Git.FileEntry
|
---@cast entry ow.Git.StatusEntry
|
||||||
if entry.section ~= "Staged" then
|
if entry.section ~= "Staged" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -770,7 +676,7 @@ local function action_discard()
|
|||||||
if not s or not entry or not entry.path then
|
if not s or not entry or not entry.path then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
---@cast entry ow.Git.FileEntry
|
---@cast entry ow.Git.StatusEntry
|
||||||
if entry.section == "Staged" then
|
if entry.section == "Staged" then
|
||||||
util.warning("file has staged changes, unstage first with 'u'")
|
util.warning("file has staged changes, unstage first with 'u'")
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user