From a6f99dccf46b2bf374fe03727fa9cad366ba481b Mon Sep 17 00:00:00 2001 From: Amolith Date: Sun, 18 Jan 2026 09:15:55 -0700 Subject: [PATCH] refactor(wt): extract cmd.new module Assisted-by: Claude Opus 4.5 via Amp --- src/main.lua | 168 +---------------------------------------- src/wt/cmd/new.lua | 181 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 165 deletions(-) create mode 100644 src/wt/cmd/new.lua diff --git a/src/main.lua b/src/main.lua index bb867ed4bde0b7b39823b4b4df07f91d224cd68f..b2dd9ef203891eb174ad455bbc91ff839d3b340b 100644 --- a/src/main.lua +++ b/src/main.lua @@ -31,7 +31,6 @@ local git_mod = require("wt.git") local find_project_root = git_mod.find_project_root local detect_source_worktree = git_mod.detect_source_worktree 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 @@ -63,6 +62,9 @@ local cmd_remove = remove_mod.cmd_remove local add_mod = require("wt.cmd.add") local cmd_add = add_mod.cmd_add +local new_mod = require("wt.cmd.new") +local cmd_new = new_mod.cmd_new + ---@param args string[] local function cmd_clone(args) -- Parse arguments: [--remote name]... [--own] @@ -316,170 +318,6 @@ local function cmd_clone(args) end end ----@param args string[] -local function cmd_new(args) - -- Parse arguments: [--remote name]... - local project_name = nil - ---@type string[] - local remote_flags = {} - - local i = 1 - while i <= #args do - local a = args[i] - if a == "--remote" then - if not args[i + 1] then - die("--remote requires a name") - end - table.insert(remote_flags, args[i + 1]) - i = i + 1 - elseif not project_name then - project_name = a - else - die("unexpected argument: " .. a) - end - i = i + 1 - end - - if not project_name then - die("usage: wt n [--remote name]...") - return - end - - -- Check if project directory already exists - local cwd = get_cwd() - if not cwd then - die("failed to get current directory", EXIT_SYSTEM_ERROR) - end - local project_path = cwd .. "/" .. project_name - local check = io.open(project_path, "r") - if check then - check:close() - die("directory already exists: " .. project_path) - end - - -- Load global config - local global_config = load_global_config() - - -- Determine which remotes to use - ---@type string[] - local selected_remotes = {} - - if #remote_flags > 0 then - -- Use explicitly provided remotes - selected_remotes = remote_flags - elseif global_config.default_remotes then - if type(global_config.default_remotes) == "table" then - selected_remotes = global_config.default_remotes - elseif global_config.default_remotes == "prompt" then - -- Prompt with gum choose - if global_config.remotes then - local keys = {} - for k in pairs(global_config.remotes) do - table.insert(keys, k) - end - table.sort(keys) - if #keys > 0 then - local input = table.concat(keys, "\n") - local cmd = "echo '" .. input .. "' | gum choose --no-limit" - local output, code = run_cmd(cmd) - if code == 0 and output ~= "" then - for line in output:gmatch("[^\n]+") do - table.insert(selected_remotes, line) - end - end - end - end - end - elseif global_config.remotes then - -- No default_remotes configured, prompt if remotes exist - local keys = {} - for k in pairs(global_config.remotes) do - table.insert(keys, k) - end - table.sort(keys) - if #keys > 0 then - local input = table.concat(keys, "\n") - local cmd = "echo '" .. input .. "' | gum choose --no-limit" - local output, code = run_cmd(cmd) - if code == 0 and output ~= "" then - for line in output:gmatch("[^\n]+") do - table.insert(selected_remotes, line) - end - end - end - end - - -- Create project structure - local bare_path = project_path .. "/.bare" - local output, code = run_cmd("mkdir -p " .. bare_path) - if code ~= 0 then - die("failed to create directory: " .. output, EXIT_SYSTEM_ERROR) - end - - output, code = run_cmd("git init --bare " .. bare_path) - if code ~= 0 then - die("failed to init bare repo: " .. output, EXIT_SYSTEM_ERROR) - end - - -- Write .git file pointing to .bare - local git_file_handle = io.open(project_path .. "/.git", "w") - if not git_file_handle then - die("failed to create .git file", EXIT_SYSTEM_ERROR) - return - end - git_file_handle:write("gitdir: ./.bare\n") - git_file_handle:close() - - -- Add remotes - local git_dir = bare_path - for _, remote_name in ipairs(selected_remotes) do - local template = global_config.remotes and global_config.remotes[remote_name] - if template then - local url = resolve_url_template(template, project_name) - output, code = run_cmd("GIT_DIR=" .. git_dir .. " git remote add " .. remote_name .. " " .. url) - if code ~= 0 then - io.stderr:write("warning: failed to add remote '" .. remote_name .. "': " .. output .. "\n") - else - -- Configure fetch refspec for the remote - run_cmd( - "GIT_DIR=" - .. git_dir - .. " git config remote." - .. remote_name - .. ".fetch '+refs/heads/*:refs/remotes/" - .. remote_name - .. "/*'" - ) - end - else - io.stderr:write("warning: remote '" .. remote_name .. "' not found in config\n") - end - end - - -- Detect default branch - local default_branch = get_default_branch() - - -- Load config for path style - local style = global_config.branch_path_style or "nested" - local separator = global_config.flat_separator - local worktree_path = branch_to_path(project_path, default_branch, style, separator) - - -- Create orphan worktree - output, code = - run_cmd("GIT_DIR=" .. git_dir .. " git worktree add --orphan -b " .. default_branch .. " -- " .. worktree_path) - if code ~= 0 then - die("failed to create worktree: " .. output, EXIT_SYSTEM_ERROR) - end - - -- Print summary - print("Created project: " .. project_path) - print("Default branch: " .. default_branch) - print("Worktree: " .. worktree_path) - if #selected_remotes > 0 then - print("Remotes: " .. table.concat(selected_remotes, ", ")) - end -end - ---List directory entries (excluding . and ..) ---@param path string ---@return string[] diff --git a/src/wt/cmd/new.lua b/src/wt/cmd/new.lua new file mode 100644 index 0000000000000000000000000000000000000000..f9208b303c6a2b08152d65defe694725006ad6eb --- /dev/null +++ b/src/wt/cmd/new.lua @@ -0,0 +1,181 @@ +-- 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") +local config = require("wt.config") + +---@class wt.cmd.new +local M = {} + +---Create a new project with bare repo structure +---@param args string[] +function M.cmd_new(args) + -- Parse arguments: [--remote name]... + local project_name = nil + ---@type string[] + local remote_flags = {} + + local i = 1 + while i <= #args do + local a = args[i] + if a == "--remote" then + if not args[i + 1] then + shell.die("--remote requires a name") + end + table.insert(remote_flags, args[i + 1]) + i = i + 1 + elseif not project_name then + project_name = a + else + shell.die("unexpected argument: " .. a) + end + i = i + 1 + end + + if not project_name then + shell.die("usage: wt n [--remote name]...") + return + end + + -- Check if project directory already exists + local cwd = shell.get_cwd() + if not cwd then + shell.die("failed to get current directory", exit.EXIT_SYSTEM_ERROR) + end + ---@cast cwd string + local project_path = cwd .. "/" .. project_name + local check = io.open(project_path, "r") + if check then + check:close() + shell.die("directory already exists: " .. project_path) + end + + -- Load global config + local global_config = config.load_global_config() + + -- Determine which remotes to use + ---@type string[] + local selected_remotes = {} + + if #remote_flags > 0 then + -- Use explicitly provided remotes + selected_remotes = remote_flags + elseif global_config.default_remotes then + if type(global_config.default_remotes) == "table" then + selected_remotes = global_config.default_remotes --[[@as string[] ]] + elseif global_config.default_remotes == "prompt" then + -- Prompt with gum choose + if global_config.remotes then + local keys = {} + for k in pairs(global_config.remotes) do + table.insert(keys, k) + end + table.sort(keys) + if #keys > 0 then + local input = table.concat(keys, "\n") + local cmd = "echo '" .. input .. "' | gum choose --no-limit" + local output, code = shell.run_cmd(cmd) + if code == 0 and output ~= "" then + for line in output:gmatch("[^\n]+") do + table.insert(selected_remotes, line) + end + end + end + end + end + elseif global_config.remotes then + -- No default_remotes configured, prompt if remotes exist + local keys = {} + for k in pairs(global_config.remotes) do + table.insert(keys, k) + end + table.sort(keys) + if #keys > 0 then + local input = table.concat(keys, "\n") + local cmd = "echo '" .. input .. "' | gum choose --no-limit" + local output, code = shell.run_cmd(cmd) + if code == 0 and output ~= "" then + for line in output:gmatch("[^\n]+") do + table.insert(selected_remotes, line) + end + end + end + end + + -- Create project structure + local bare_path = project_path .. "/.bare" + local output, code = shell.run_cmd("mkdir -p " .. bare_path) + if code ~= 0 then + shell.die("failed to create directory: " .. output, exit.EXIT_SYSTEM_ERROR) + end + + output, code = shell.run_cmd("git init --bare " .. bare_path) + if code ~= 0 then + shell.die("failed to init bare repo: " .. output, exit.EXIT_SYSTEM_ERROR) + end + + -- Write .git file pointing to .bare + local git_file_handle = io.open(project_path .. "/.git", "w") + if not git_file_handle then + shell.die("failed to create .git file", exit.EXIT_SYSTEM_ERROR) + return + end + git_file_handle:write("gitdir: ./.bare\n") + git_file_handle:close() + + -- Add remotes + local git_dir = bare_path + for _, remote_name in ipairs(selected_remotes) do + local template = global_config.remotes and global_config.remotes[remote_name] + if template then + local url = config.resolve_url_template(template, project_name) + output, code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git remote add " .. remote_name .. " " .. url) + if code ~= 0 then + io.stderr:write("warning: failed to add remote '" .. remote_name .. "': " .. output .. "\n") + else + -- Configure fetch refspec for the remote + shell.run_cmd( + "GIT_DIR=" + .. git_dir + .. " git config remote." + .. remote_name + .. ".fetch '+refs/heads/*:refs/remotes/" + .. remote_name + .. "/*'" + ) + end + else + io.stderr:write("warning: remote '" .. remote_name .. "' not found in config\n") + end + end + + -- Detect default branch + local default_branch = git.get_default_branch() + + -- Load config for path style + local style = global_config.branch_path_style or "nested" + local separator = global_config.flat_separator + local worktree_path = path_mod.branch_to_path(project_path, default_branch, style, separator) + + -- Create orphan worktree + output, code = shell.run_cmd( + "GIT_DIR=" .. git_dir .. " git worktree add --orphan -b " .. default_branch .. " -- " .. worktree_path + ) + if code ~= 0 then + shell.die("failed to create worktree: " .. output, exit.EXIT_SYSTEM_ERROR) + end + + -- Print summary + print("Created project: " .. project_path) + print("Default branch: " .. default_branch) + print("Worktree: " .. worktree_path) + if #selected_remotes > 0 then + print("Remotes: " .. table.concat(selected_remotes, ", ")) + end +end + +return M