From 7fd0c44837d24e32f7bf2c6078d3be3592702463 Mon Sep 17 00:00:00 2001 From: Amolith Date: Sun, 18 Jan 2026 09:09:43 -0700 Subject: [PATCH] refactor(wt): extract cmd.remove module Assisted-by: Claude Opus 4.5 via Amp --- src/main.lua | 135 +---------------------------------------- src/wt/cmd/remove.lua | 137 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 132 deletions(-) create mode 100644 src/wt/cmd/remove.lua diff --git a/src/main.lua b/src/main.lua index fea4f540911f2c43e25820042df2a6b447aa0fed..295ba1bf9de20c82483fd54ef057f52a24ea217d 100644 --- a/src/main.lua +++ b/src/main.lua @@ -36,7 +36,6 @@ local detect_cloned_default_branch = git_mod.detect_cloned_default_branch local get_default_branch = git_mod.get_default_branch local parse_branch_remotes = git_mod.parse_branch_remotes local parse_worktree_list = git_mod.parse_worktree_list -local branch_checked_out_at = git_mod.branch_checked_out_at local config_mod = require("wt.config") local resolve_url_template = config_mod.resolve_url_template @@ -60,6 +59,9 @@ local cmd_fetch = fetch_mod.cmd_fetch local list_mod = require("wt.cmd.list") local cmd_list = list_mod.cmd_list +local remove_mod = require("wt.cmd.remove") +local cmd_remove = remove_mod.cmd_remove + ---@param args string[] local function cmd_clone(args) -- Parse arguments: [--remote name]... [--own] @@ -582,137 +584,6 @@ local function cmd_add(args) print(target_path) end ----Check if cwd is inside (or equal to) a given path ----@param target string ----@return boolean -local function cwd_inside_path(target) - local cwd = get_cwd() - if not cwd then - return false - end - return path_inside(cwd, target) -end - ----Get the bare repo's HEAD branch ----@param git_dir string ----@return string|nil branch name, nil on error -local function get_bare_head(git_dir) - local output, code = run_cmd("GIT_DIR=" .. git_dir .. " git symbolic-ref --short HEAD") - if code ~= 0 then - return nil - end - return (output:gsub("%s+$", "")) -end - ----@param args string[] -local function cmd_remove(args) - -- Parse arguments: [-b] [-f] - local branch = nil - local delete_branch = false - local force = false - - for _, a in ipairs(args) do - if a == "-b" then - delete_branch = true - elseif a == "-f" then - force = true - elseif not branch then - branch = a - else - die("unexpected argument: " .. a) - end - end - - if not branch then - die("usage: wt r [-b] [-f]") - return - end - - local root, err = find_project_root() - if not root then - die(err --[[@as string]]) - return - end - - local git_dir = root .. "/.bare" - - -- Find worktree by querying git for actual location (not computed from config) - local wt_output, wt_code = run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain") - if wt_code ~= 0 then - die("failed to list worktrees", EXIT_SYSTEM_ERROR) - return - end - - local worktrees = parse_worktree_list(wt_output) - local target_path = nil - for _, wt in ipairs(worktrees) do - if wt.branch == branch then - target_path = wt.path - break - end - end - - if not target_path then - die("no worktree found for branch '" .. branch .. "'") - return - end - - -- Error if cwd is inside the worktree - if cwd_inside_path(target_path) then - die("cannot remove worktree while inside it") - end - - -- Check for uncommitted changes - if not force then - local status_out = run_cmd("git -C " .. target_path .. " status --porcelain") - if status_out ~= "" then - die("worktree has uncommitted changes (use -f to force)") - end - end - - -- Remove worktree - local remove_cmd = "GIT_DIR=" .. git_dir .. " git worktree remove" - if force then - remove_cmd = remove_cmd .. " --force" - end - remove_cmd = remove_cmd .. " -- " .. target_path - - local output, code = run_cmd(remove_cmd) - if code ~= 0 then - die("failed to remove worktree: " .. output, EXIT_SYSTEM_ERROR) - end - - -- Delete branch if requested - if delete_branch then - -- Check if branch is bare repo's HEAD - local bare_head = get_bare_head(git_dir) - if bare_head and bare_head == branch then - io.stderr:write("warning: cannot delete branch '" .. branch .. "' (it's the bare repo's HEAD)\n") - print("Worktree removed; branch retained") - return - end - - -- Check if branch is checked out elsewhere - local checked_out = branch_checked_out_at(git_dir, branch) - if checked_out then - die("cannot delete branch '" .. branch .. "': checked out at " .. checked_out) - end - - -- Delete branch - local delete_flag = force and "-D" or "-d" - local del_output, del_code = run_cmd("GIT_DIR=" .. git_dir .. " git branch " .. delete_flag .. " " .. branch) - if del_code ~= 0 then - io.stderr:write("warning: failed to delete branch: " .. del_output .. "\n") - print("Worktree removed; branch retained") - return - end - - print("Worktree and branch '" .. branch .. "' removed") - else - print("Worktree removed") - end -end - ---List directory entries (excluding . and ..) ---@param path string ---@return string[] diff --git a/src/wt/cmd/remove.lua b/src/wt/cmd/remove.lua new file mode 100644 index 0000000000000000000000000000000000000000..ea5bc0ce92717e344806f94a5f3bfa37662708fa --- /dev/null +++ b/src/wt/cmd/remove.lua @@ -0,0 +1,137 @@ +-- 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.remove +local M = {} + +---Check if cwd is inside (or equal to) a given path +---@param target string +---@return boolean +local function cwd_inside_path(target) + local cwd = shell.get_cwd() + if not cwd then + return false + end + return path_mod.path_inside(cwd, target) +end + +---Get the bare repo's HEAD branch +---@param git_dir string +---@return string|nil branch name, nil on error +local function get_bare_head(git_dir) + local output, code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git symbolic-ref --short HEAD") + if code ~= 0 then + return nil + end + return (output:gsub("%s+$", "")) +end + +---Remove a worktree and optionally its branch +---@param args string[] +function M.cmd_remove(args) + local branch = nil + local delete_branch = false + local force = false + + for _, a in ipairs(args) do + if a == "-b" then + delete_branch = true + elseif a == "-f" then + force = true + elseif not branch then + branch = a + else + shell.die("unexpected argument: " .. a) + end + end + + if not branch then + shell.die("usage: wt r [-b] [-f]") + return + end + + local root, err = git.find_project_root() + if not root then + shell.die(err --[[@as string]]) + return + end + + local git_dir = root .. "/.bare" + + local wt_output, wt_code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain") + if wt_code ~= 0 then + shell.die("failed to list worktrees", exit.EXIT_SYSTEM_ERROR) + return + end + + local worktrees = git.parse_worktree_list(wt_output) + local target_path = nil + for _, wt in ipairs(worktrees) do + if wt.branch == branch then + target_path = wt.path + break + end + end + + if not target_path then + shell.die("no worktree found for branch '" .. branch .. "'") + return + end + + if cwd_inside_path(target_path) then + shell.die("cannot remove worktree while inside it") + end + + if not force then + local status_out = shell.run_cmd("git -C " .. target_path .. " status --porcelain") + if status_out ~= "" then + shell.die("worktree has uncommitted changes (use -f to force)") + end + end + + local remove_cmd = "GIT_DIR=" .. git_dir .. " git worktree remove" + if force then + remove_cmd = remove_cmd .. " --force" + end + remove_cmd = remove_cmd .. " -- " .. target_path + + local output, code = shell.run_cmd(remove_cmd) + if code ~= 0 then + shell.die("failed to remove worktree: " .. output, exit.EXIT_SYSTEM_ERROR) + end + + if delete_branch then + local bare_head = get_bare_head(git_dir) + if bare_head and bare_head == branch then + io.stderr:write("warning: cannot delete branch '" .. branch .. "' (it's the bare repo's HEAD)\n") + print("Worktree removed; branch retained") + return + end + + local checked_out = git.branch_checked_out_at(git_dir, branch) + if checked_out then + shell.die("cannot delete branch '" .. branch .. "': checked out at " .. checked_out) + end + + local delete_flag = force and "-D" or "-d" + local del_output, del_code = + shell.run_cmd("GIT_DIR=" .. git_dir .. " git branch " .. delete_flag .. " " .. branch) + if del_code ~= 0 then + io.stderr:write("warning: failed to delete branch: " .. del_output .. "\n") + print("Worktree removed; branch retained") + return + end + + print("Worktree and branch '" .. branch .. "' removed") + else + print("Worktree removed") + end +end + +return M