@@ -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: <url> [--remote name]... [--own]
@@ -316,170 +318,6 @@ local function cmd_clone(args)
end
end
----@param args string[]
-local function cmd_new(args)
- -- Parse arguments: <project-name> [--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 <project-name> [--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[]
@@ -0,0 +1,181 @@
+-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+--
+-- 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: <project-name> [--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 <project-name> [--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