From ac940cb2af7e92c529f772216c2df05efc9108fe Mon Sep 17 00:00:00 2001 From: Amolith Date: Sun, 18 Jan 2026 09:05:32 -0700 Subject: [PATCH] refactor(wt): extract cmd.list module Assisted-by: Claude Opus 4.5 via Amp --- dist/wt | 162 ++++++++++++++++++++++++-------------------- src/main.lua | 78 +-------------------- src/wt/cmd/list.lua | 82 ++++++++++++++++++++++ 3 files changed, 172 insertions(+), 150 deletions(-) create mode 100644 src/wt/cmd/list.lua diff --git a/dist/wt b/dist/wt index 3fe33e4ee362adde90e03e3141c17597a631032d..62ee513599ba33c6c92a9ba1138cc8643e8637e5 100755 --- a/dist/wt +++ b/dist/wt @@ -861,6 +861,90 @@ end return M ]] +_EMBEDDED_MODULES["wt.cmd.list"] = [[-- SPDX-FileCopyrightText: Amolith +-- +-- SPDX-License-Identifier: GPL-3.0-or-later + +local exit = require("wt.exit") +local shell = require("wt.shell") +local git = require("wt.git") +local path_mod = require("wt.path") + +---@class wt.cmd.list +local M = {} + +---List all worktrees with status +function M.cmd_list() + local root, err = git.find_project_root() + if not root then + shell.die(err --[[@as string]]) + return + end + + local git_dir = root .. "/.bare" + local output, code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain") + if code ~= 0 then + shell.die("failed to list worktrees: " .. output, exit.EXIT_SYSTEM_ERROR) + end + + ---@type {path: string, head: string, branch: string}[] + local worktrees = {} + local current = {} + + for line in output:gmatch("[^\n]+") do + local key, value = line:match("^(%S+)%s*(.*)$") + if key == "worktree" and value then + if current.path then + table.insert(worktrees, current) + end + if value:match("/%.bare$") then + current = {} + else + current = { path = value, head = "", branch = "(detached)" } + end + elseif key == "HEAD" and value then + current.head = value:sub(1, 7) + elseif key == "branch" and value then + current.branch = value:gsub("^refs/heads/", "") + elseif key == "bare" then + current = {} + end + end + if current.path then + table.insert(worktrees, current) + end + + if #worktrees == 0 then + print("No worktrees found") + return + end + + local cwd = shell.get_cwd() or "" + + local rows = {} + for _, wt in ipairs(worktrees) do + local rel_path = path_mod.relative_path(cwd, wt.path) + + local status_out = shell.run_cmd("git -C " .. wt.path .. " status --porcelain") + local status = status_out == "" and "clean" or "dirty" + + table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status) + end + + local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n") + table_input = table_input:gsub("EOF", "eof") + local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF" + local table_handle = io.popen(table_cmd, "r") + if not table_handle then + return + end + io.write(table_handle:read("*a") or "") + table_handle:close() +end + +return M +]] + _EMBEDDED_MODULES["wt.cmd.fetch"] = [[-- SPDX-FileCopyrightText: Amolith -- -- SPDX-License-Identifier: GPL-3.0-or-later @@ -945,6 +1029,9 @@ local show_command_help = help_mod.show_command_help local fetch_mod = require("wt.cmd.fetch") local cmd_fetch = fetch_mod.cmd_fetch +local list_mod = require("wt.cmd.list") +local cmd_list = list_mod.cmd_list + ---@param args string[] local function cmd_clone(args) -- Parse arguments: [--remote name]... [--own] @@ -1598,81 +1685,6 @@ local function cmd_remove(args) end end -local function cmd_list() - local root, err = find_project_root() - if not root then - die(err --[[@as string]]) - return - end - - local git_dir = root .. "/.bare" - local output, code = run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain") - if code ~= 0 then - die("failed to list worktrees: " .. output, EXIT_SYSTEM_ERROR) - end - - -- Parse porcelain output into worktree entries - ---@type {path: string, head: string, branch: string}[] - local worktrees = {} - local current = {} - - for line in output:gmatch("[^\n]+") do - local key, value = line:match("^(%S+)%s*(.*)$") - if key == "worktree" and value then - if current.path then - table.insert(worktrees, current) - end - -- Skip .bare directory - if value:match("/%.bare$") then - current = {} - else - current = { path = value, head = "", branch = "(detached)" } - end - elseif key == "HEAD" and value then - current.head = value:sub(1, 7) - elseif key == "branch" and value then - current.branch = value:gsub("^refs/heads/", "") - elseif key == "bare" then - -- Skip bare repo entry - current = {} - end - end - if current.path then - table.insert(worktrees, current) - end - - if #worktrees == 0 then - print("No worktrees found") - return - end - - -- Get current working directory - local cwd = get_cwd() or "" - - -- Build table rows with status - local rows = {} - for _, wt in ipairs(worktrees) do - local rel_path = relative_path(cwd, wt.path) - - -- Check dirty status - local status_out = run_cmd("git -C " .. wt.path .. " status --porcelain") - local status = status_out == "" and "clean" or "dirty" - - table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status) - end - - -- Output via gum table - local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n") - table_input = table_input:gsub("EOF", "eof") - local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF" - local table_handle = io.popen(table_cmd, "r") - if not table_handle then - return - end - io.write(table_handle:read("*a") or "") - table_handle:close() -end - ---List directory entries (excluding . and ..) ---@param path string ---@return string[] diff --git a/src/main.lua b/src/main.lua index 9ee4770b3086b3cdb173ee446b2bc5c872df7382..fea4f540911f2c43e25820042df2a6b447aa0fed 100644 --- a/src/main.lua +++ b/src/main.lua @@ -57,6 +57,9 @@ local show_command_help = help_mod.show_command_help local fetch_mod = require("wt.cmd.fetch") local cmd_fetch = fetch_mod.cmd_fetch +local list_mod = require("wt.cmd.list") +local cmd_list = list_mod.cmd_list + ---@param args string[] local function cmd_clone(args) -- Parse arguments: [--remote name]... [--own] @@ -710,81 +713,6 @@ local function cmd_remove(args) end end -local function cmd_list() - local root, err = find_project_root() - if not root then - die(err --[[@as string]]) - return - end - - local git_dir = root .. "/.bare" - local output, code = run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain") - if code ~= 0 then - die("failed to list worktrees: " .. output, EXIT_SYSTEM_ERROR) - end - - -- Parse porcelain output into worktree entries - ---@type {path: string, head: string, branch: string}[] - local worktrees = {} - local current = {} - - for line in output:gmatch("[^\n]+") do - local key, value = line:match("^(%S+)%s*(.*)$") - if key == "worktree" and value then - if current.path then - table.insert(worktrees, current) - end - -- Skip .bare directory - if value:match("/%.bare$") then - current = {} - else - current = { path = value, head = "", branch = "(detached)" } - end - elseif key == "HEAD" and value then - current.head = value:sub(1, 7) - elseif key == "branch" and value then - current.branch = value:gsub("^refs/heads/", "") - elseif key == "bare" then - -- Skip bare repo entry - current = {} - end - end - if current.path then - table.insert(worktrees, current) - end - - if #worktrees == 0 then - print("No worktrees found") - return - end - - -- Get current working directory - local cwd = get_cwd() or "" - - -- Build table rows with status - local rows = {} - for _, wt in ipairs(worktrees) do - local rel_path = relative_path(cwd, wt.path) - - -- Check dirty status - local status_out = run_cmd("git -C " .. wt.path .. " status --porcelain") - local status = status_out == "" and "clean" or "dirty" - - table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status) - end - - -- Output via gum table - local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n") - table_input = table_input:gsub("EOF", "eof") - local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF" - local table_handle = io.popen(table_cmd, "r") - if not table_handle then - return - end - io.write(table_handle:read("*a") or "") - table_handle:close() -end - ---List directory entries (excluding . and ..) ---@param path string ---@return string[] diff --git a/src/wt/cmd/list.lua b/src/wt/cmd/list.lua new file mode 100644 index 0000000000000000000000000000000000000000..cbec797e771b74600928f26a0f48d61217bc5a27 --- /dev/null +++ b/src/wt/cmd/list.lua @@ -0,0 +1,82 @@ +-- SPDX-FileCopyrightText: Amolith +-- +-- SPDX-License-Identifier: GPL-3.0-or-later + +local exit = require("wt.exit") +local shell = require("wt.shell") +local git = require("wt.git") +local path_mod = require("wt.path") + +---@class wt.cmd.list +local M = {} + +---List all worktrees with status +function M.cmd_list() + local root, err = git.find_project_root() + if not root then + shell.die(err --[[@as string]]) + return + end + + local git_dir = root .. "/.bare" + local output, code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain") + if code ~= 0 then + shell.die("failed to list worktrees: " .. output, exit.EXIT_SYSTEM_ERROR) + end + + ---@type {path: string, head: string, branch: string}[] + local worktrees = {} + local current = {} + + for line in output:gmatch("[^\n]+") do + local key, value = line:match("^(%S+)%s*(.*)$") + if key == "worktree" and value then + if current.path then + table.insert(worktrees, current) + end + if value:match("/%.bare$") then + current = {} + else + current = { path = value, head = "", branch = "(detached)" } + end + elseif key == "HEAD" and value then + current.head = value:sub(1, 7) + elseif key == "branch" and value then + current.branch = value:gsub("^refs/heads/", "") + elseif key == "bare" then + current = {} + end + end + if current.path then + table.insert(worktrees, current) + end + + if #worktrees == 0 then + print("No worktrees found") + return + end + + local cwd = shell.get_cwd() or "" + + local rows = {} + for _, wt in ipairs(worktrees) do + local rel_path = path_mod.relative_path(cwd, wt.path) + + local status_out = shell.run_cmd("git -C " .. wt.path .. " status --porcelain") + local status = status_out == "" and "clean" or "dirty" + + table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status) + end + + local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n") + table_input = table_input:gsub("EOF", "eof") + local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF" + local table_handle = io.popen(table_cmd, "r") + if not table_handle then + return + end + io.write(table_handle:read("*a") or "") + table_handle:close() +end + +return M