From d3359e2fa9273e97779b800421807287d3d3089f Mon Sep 17 00:00:00 2001 From: Amolith Date: Sun, 18 Jan 2026 11:07:26 -0700 Subject: [PATCH] feat(remove): cleanup empty parent directories After removing a nested worktree like feature/auth/oauth2, removes the now-empty parent directories up to project root. Assisted-by: Claude Opus 4.5 via Amp --- src/wt/cmd/remove.lua | 14 ++++++++++++++ src/wt/path.lua | 9 +++++++++ src/wt/shell.lua | 7 +++++++ 3 files changed, 30 insertions(+) diff --git a/src/wt/cmd/remove.lua b/src/wt/cmd/remove.lua index ea5bc0ce92717e344806f94a5f3bfa37662708fa..5295cddff46035d3d8572ac6f737599ad38d744e 100644 --- a/src/wt/cmd/remove.lua +++ b/src/wt/cmd/remove.lua @@ -32,6 +32,18 @@ local function get_bare_head(git_dir) return (output:gsub("%s+$", "")) end +---Remove empty parent directories between target and project root +---@param target_path string the removed worktree path +---@param project_root string the project root (stop here) +local function cleanup_empty_parents(target_path, project_root) + project_root = project_root:gsub("/$", "") + local parent = path_mod.parent_dir(target_path) + while parent and parent ~= project_root and path_mod.path_inside(parent, project_root) do + shell.run_cmd_silent("rmdir " .. shell.quote(parent)) + parent = path_mod.parent_dir(parent) + end +end + ---Remove a worktree and optionally its branch ---@param args string[] function M.cmd_remove(args) @@ -106,6 +118,8 @@ function M.cmd_remove(args) shell.die("failed to remove worktree: " .. output, exit.EXIT_SYSTEM_ERROR) end + cleanup_empty_parents(target_path, root) + if delete_branch then local bare_head = get_bare_head(git_dir) if bare_head and bare_head == branch then diff --git a/src/wt/path.lua b/src/wt/path.lua index d21093e80f255ac2284c82c7a529a3051cb6ba86..298a46477778f8bc17bde21b6e5e6eddd55d4e98 100644 --- a/src/wt/path.lua +++ b/src/wt/path.lua @@ -90,4 +90,13 @@ function M.escape_pattern(str) return (str:gsub("([%%%-%+%[%]%(%)%.%^%$%*%?])", "%%%1")) end +---Get the parent directory of a path +---@param path string +---@return string|nil parent or nil if path has no parent +function M.parent_dir(path) + path = path:gsub("/$", "") + local parent = path:match("(.+)/[^/]+$") + return parent +end + return M diff --git a/src/wt/shell.lua b/src/wt/shell.lua index f1cff2117ead5da28c369f5e004c5e706960f9a7..fddecfc589181000d1e4d004626e072e403f2654 100644 --- a/src/wt/shell.lua +++ b/src/wt/shell.lua @@ -52,4 +52,11 @@ function M.die(msg, code) os.exit(code or exit.EXIT_USER_ERROR) end +---Quote a string for safe shell use +---@param str string +---@return string +function M.quote(str) + return "'" .. str:gsub("'", "'\\''") .. "'" +end + return M