noot
This commit is contained in:
parent
9470d7c36b
commit
97f8909001
4
Makefile
4
Makefile
@ -9,7 +9,9 @@ all: dist/repotool
|
|||||||
|
|
||||||
install: dist/repotool shell/zsh/repotool.zsh shell/zsh/repotool.plugin.zsh
|
install: dist/repotool shell/zsh/repotool.zsh shell/zsh/repotool.plugin.zsh
|
||||||
mkdir -p ${REPOTOOL_PATH}/.bin/
|
mkdir -p ${REPOTOOL_PATH}/.bin/
|
||||||
install dist/repotool shell/zsh/repotool.zsh shell/zsh/repotool.plugin.zsh ${REPOTOOL_PATH}/.bin/
|
mkdir -p ${REPOTOOL_PATH}/.shell/
|
||||||
|
install dist/repotool ${REPOTOOL_PATH}/.bin/
|
||||||
|
install shell/zsh/repotool.zsh shell/zsh/repotool.plugin.zsh ${REPOTOOL_PATH}/.shell/
|
||||||
|
|
||||||
dist/repotool: $(SOURCES_SRC) $(LIBS_SRC) main.lua
|
dist/repotool: $(SOURCES_SRC) $(LIBS_SRC) main.lua
|
||||||
@mkdir -p dist
|
@mkdir -p dist
|
||||||
|
|||||||
71
main.lua
71
main.lua
@ -11,7 +11,7 @@ local json = require("json")
|
|||||||
|
|
||||||
-- Load command modules
|
-- Load command modules
|
||||||
local get_command = require("get_command")
|
local get_command = require("get_command")
|
||||||
local worktree_command = require("worktree_command")
|
local worktree = require("worktree")
|
||||||
local open_command = require("open_command")
|
local open_command = require("open_command")
|
||||||
|
|
||||||
-- Create the main command with subcommands
|
-- Create the main command with subcommands
|
||||||
@ -62,42 +62,61 @@ local app = cli.cmd {
|
|||||||
return 0
|
return 0
|
||||||
end),
|
end),
|
||||||
},
|
},
|
||||||
-- Worktree command
|
-- Worktree command with subcommands
|
||||||
cli.cmd {
|
cli.cmd {
|
||||||
'worktree', 'w', 'wt',
|
'worktree', 'w', 'wt',
|
||||||
desc = 'goes to or creates a worktree with the name for the repo you are in',
|
desc = 'manage git worktrees',
|
||||||
|
subs = {
|
||||||
|
-- List subcommand
|
||||||
|
cli.cmd {
|
||||||
|
'list', 'ls',
|
||||||
|
desc = 'list existing worktrees for this repo',
|
||||||
|
term = cli.val():and_then(function()
|
||||||
|
worktree.handle_list()
|
||||||
|
return 0
|
||||||
|
end),
|
||||||
|
},
|
||||||
|
-- Get subcommand
|
||||||
|
cli.cmd {
|
||||||
|
'get',"new","create","n","c",
|
||||||
|
desc = 'create or go to a worktree',
|
||||||
term = cli.all {
|
term = cli.all {
|
||||||
name = cli.arg {
|
name = cli.arg {
|
||||||
'name',
|
'name',
|
||||||
desc = 'Name of the worktree',
|
desc = 'Name of the worktree',
|
||||||
required = false,
|
required = true,
|
||||||
},
|
|
||||||
list = cli.opt {
|
|
||||||
'--list', '-l',
|
|
||||||
desc = 'List existing worktrees for this repo',
|
|
||||||
flag = true,
|
|
||||||
},
|
|
||||||
root = cli.opt {
|
|
||||||
'--root', '-r',
|
|
||||||
desc = 'Return the root directory of the original repo',
|
|
||||||
flag = true,
|
|
||||||
},
|
},
|
||||||
}:and_then(function(args)
|
}:and_then(function(args)
|
||||||
-- Handle special cases: "list" and "ls" as commands
|
worktree.handle_get(args.name)
|
||||||
local name = args.name
|
|
||||||
if name == "list" or name == "ls" then
|
|
||||||
args.list = true
|
|
||||||
args.name = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
worktree_command({
|
|
||||||
name = args.name,
|
|
||||||
list = args.list,
|
|
||||||
root = args.root,
|
|
||||||
})
|
|
||||||
return 0
|
return 0
|
||||||
end),
|
end),
|
||||||
},
|
},
|
||||||
|
-- Root subcommand
|
||||||
|
cli.cmd {
|
||||||
|
'root', 'back', 'r', 'return',
|
||||||
|
desc = 'return to the root directory of the original repo',
|
||||||
|
term = cli.val():and_then(function()
|
||||||
|
worktree.handle_root()
|
||||||
|
return 0
|
||||||
|
end),
|
||||||
|
},
|
||||||
|
-- Remove subcommand
|
||||||
|
cli.cmd {
|
||||||
|
'remove', 'rm', 'delete', 'del',
|
||||||
|
desc = 'remove a worktree',
|
||||||
|
term = cli.all {
|
||||||
|
name = cli.arg {
|
||||||
|
'name',
|
||||||
|
desc = 'Name of the worktree to remove',
|
||||||
|
required = true,
|
||||||
|
},
|
||||||
|
}:and_then(function(args)
|
||||||
|
worktree.handle_remove(args.name)
|
||||||
|
return 0
|
||||||
|
end),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
-- Open command
|
-- Open command
|
||||||
cli.cmd {
|
cli.cmd {
|
||||||
'open', 'o',
|
'open', 'o',
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env zsh
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
[[ -z "$REPOTOOL_PATH" ]] && export REPOTOOL_PATH="$HOME/repo"
|
[[ -z "$REPOTOOL_PATH" ]] && export REPOTOOL_PATH="$HOME/repo"
|
||||||
alias repo=". $REPOTOOL_PATH/.bin/repotool.zsh"
|
alias repo=". $REPOTOOL_PATH/.shell/repotool.zsh"
|
||||||
|
|||||||
36
src/cmd.lua
Normal file
36
src/cmd.lua
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
local cmd = {}
|
||||||
|
|
||||||
|
-- Execute a command, redirecting stdout to stderr to avoid polluting our JSON output
|
||||||
|
function cmd.execute(command)
|
||||||
|
-- Redirect stdout to stderr so only our JSON goes to stdout
|
||||||
|
local redirected_cmd = command .. " 1>&2"
|
||||||
|
return os.execute(redirected_cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Execute a command and capture its output
|
||||||
|
function cmd.read(command)
|
||||||
|
local handle = io.popen(command .. " 2>&1")
|
||||||
|
local result = handle:read("*a")
|
||||||
|
local success = handle:close()
|
||||||
|
return result, success
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Execute a command and capture stdout separately from stderr
|
||||||
|
function cmd.read_stdout(command)
|
||||||
|
local handle = io.popen(command .. " 2>/dev/null")
|
||||||
|
local result = handle:read("*a")
|
||||||
|
local success = handle:close()
|
||||||
|
return result, success
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Execute a command and return only the exit status
|
||||||
|
function cmd.run(command)
|
||||||
|
return os.execute(command .. " >/dev/null 2>&1")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if a command exists
|
||||||
|
function cmd.exists(command_name)
|
||||||
|
return cmd.run("command -v " .. command_name) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return cmd
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
local cmd = require("cmd")
|
||||||
|
|
||||||
local fs = {}
|
local fs = {}
|
||||||
|
|
||||||
-- Check if a file exists
|
-- Check if a file exists
|
||||||
@ -13,17 +15,14 @@ function fs.dir_exists(path)
|
|||||||
if f then
|
if f then
|
||||||
io.close(f)
|
io.close(f)
|
||||||
-- Check if it's actually a directory by trying to list it
|
-- Check if it's actually a directory by trying to list it
|
||||||
local handle = io.popen('test -d "' .. path .. '" && echo "yes" || echo "no"')
|
return cmd.run('test -d "' .. path .. '"') == 0
|
||||||
local result = handle:read("*a"):gsub("\n", "")
|
|
||||||
handle:close()
|
|
||||||
return result == "yes"
|
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create directory with parents
|
-- Create directory with parents
|
||||||
function fs.mkdir_p(path)
|
function fs.mkdir_p(path)
|
||||||
os.execute('mkdir -p "' .. path .. '"')
|
cmd.execute('mkdir -p "' .. path .. '"')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the parent directory of a path
|
-- Get the parent directory of a path
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
local git = require("git")
|
local git = require("git")
|
||||||
local json = require("json")
|
local json = require("json")
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
|
local cmd = require("cmd")
|
||||||
|
|
||||||
local function get_command(args)
|
local function get_command(args)
|
||||||
-- Validate URL
|
-- Validate URL
|
||||||
@ -53,8 +54,7 @@ http_pass: %s
|
|||||||
fs.mkdir_p(target_dir)
|
fs.mkdir_p(target_dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Change to target directory
|
-- Note: cd command doesn't work in os.execute as it runs in a subshell
|
||||||
os.execute("cd '" .. target_dir .. "'")
|
|
||||||
|
|
||||||
-- Construct repo URL based on method
|
-- Construct repo URL based on method
|
||||||
local clone_url
|
local clone_url
|
||||||
@ -73,9 +73,9 @@ http_pass: %s
|
|||||||
local git_dir = fs.join(target_dir, ".git")
|
local git_dir = fs.join(target_dir, ".git")
|
||||||
if not fs.dir_exists(git_dir) then
|
if not fs.dir_exists(git_dir) then
|
||||||
-- Check if remote exists
|
-- Check if remote exists
|
||||||
local check_cmd = "cd '" .. target_dir .. "' && git ls-remote '" .. clone_url .. "' >/dev/null 2>&1"
|
local check_cmd = "cd '" .. target_dir .. "' && git ls-remote '" .. clone_url .. "'"
|
||||||
if os.execute(check_cmd) == 0 then
|
if cmd.run(check_cmd) == 0 then
|
||||||
os.execute("git clone '" .. clone_url .. "' '" .. target_dir .. "' >&2")
|
cmd.execute("git clone '" .. clone_url .. "' '" .. target_dir .. "'")
|
||||||
cloned = "true"
|
cloned = "true"
|
||||||
else
|
else
|
||||||
io.stderr:write("Could not find repo: " .. clone_url .. "\n")
|
io.stderr:write("Could not find repo: " .. clone_url .. "\n")
|
||||||
|
|||||||
20
src/git.lua
20
src/git.lua
@ -1,3 +1,5 @@
|
|||||||
|
local cmd = require("cmd")
|
||||||
|
|
||||||
local git = {}
|
local git = {}
|
||||||
|
|
||||||
-- Parse a git URL into domain and path components
|
-- Parse a git URL into domain and path components
|
||||||
@ -34,9 +36,8 @@ end
|
|||||||
|
|
||||||
-- Get domain and path from current git repository's origin
|
-- Get domain and path from current git repository's origin
|
||||||
function git.parse_origin()
|
function git.parse_origin()
|
||||||
local handle = io.popen("git config --get remote.origin.url 2>/dev/null")
|
local origin_url = cmd.read_stdout("git config --get remote.origin.url")
|
||||||
local origin_url = handle:read("*a"):gsub("\n", "")
|
origin_url = origin_url:gsub("\n", "")
|
||||||
handle:close()
|
|
||||||
|
|
||||||
if origin_url == "" then
|
if origin_url == "" then
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -59,11 +60,8 @@ function git.valid_url(url)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Execute command and return output
|
-- Execute command and return output
|
||||||
function git.execute(cmd)
|
function git.execute(command)
|
||||||
local handle = io.popen(cmd .. " 2>&1")
|
return cmd.read(command)
|
||||||
local result = handle:read("*a")
|
|
||||||
local success = handle:close()
|
|
||||||
return result, success
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we're in a git repository
|
-- Check if we're in a git repository
|
||||||
@ -93,10 +91,10 @@ end
|
|||||||
-- Get list of all worktrees with their properties
|
-- Get list of all worktrees with their properties
|
||||||
function git.worktree_list()
|
function git.worktree_list()
|
||||||
local worktrees = {}
|
local worktrees = {}
|
||||||
local handle = io.popen("git worktree list --porcelain")
|
local output = cmd.read_stdout("git worktree list --porcelain")
|
||||||
local current_worktree = nil
|
local current_worktree = nil
|
||||||
|
|
||||||
for line in handle:lines() do
|
for line in output:gmatch("[^\n]+") do
|
||||||
local worktree_path = line:match("^worktree (.+)$")
|
local worktree_path = line:match("^worktree (.+)$")
|
||||||
if worktree_path then
|
if worktree_path then
|
||||||
-- Save previous worktree if any
|
-- Save previous worktree if any
|
||||||
@ -137,8 +135,6 @@ function git.worktree_list()
|
|||||||
table.insert(worktrees, current_worktree)
|
table.insert(worktrees, current_worktree)
|
||||||
end
|
end
|
||||||
|
|
||||||
handle:close()
|
|
||||||
|
|
||||||
return worktrees
|
return worktrees
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
local git = require("git")
|
local git = require("git")
|
||||||
local json = require("json")
|
local json = require("json")
|
||||||
|
local cmd = require("cmd")
|
||||||
|
|
||||||
local function open_command(args)
|
local function open_command(args)
|
||||||
-- Check if we're in a git repository
|
-- Check if we're in a git repository
|
||||||
@ -9,9 +10,8 @@ local function open_command(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Get the remote URL
|
-- Get the remote URL
|
||||||
local handle = io.popen("git ls-remote --get-url")
|
local raw_url = cmd.read_stdout("git ls-remote --get-url")
|
||||||
local raw_url = handle:read("*a"):gsub("\n", "")
|
raw_url = raw_url:gsub("\n", "")
|
||||||
handle:close()
|
|
||||||
|
|
||||||
if raw_url == "" then
|
if raw_url == "" then
|
||||||
io.stderr:write("Error: No remote URL found\n")
|
io.stderr:write("Error: No remote URL found\n")
|
||||||
@ -30,29 +30,22 @@ local function open_command(args)
|
|||||||
|
|
||||||
-- Detect platform and open URL
|
-- Detect platform and open URL
|
||||||
local open_cmd
|
local open_cmd
|
||||||
local result = os.execute("command -v xdg-open >/dev/null 2>&1")
|
if cmd.exists("xdg-open") then
|
||||||
if result == true or result == 0 then
|
|
||||||
-- Linux
|
-- Linux
|
||||||
open_cmd = "xdg-open"
|
open_cmd = "xdg-open"
|
||||||
else
|
elseif cmd.exists("open") then
|
||||||
result = os.execute("command -v open >/dev/null 2>&1")
|
|
||||||
if result == true or result == 0 then
|
|
||||||
-- macOS
|
-- macOS
|
||||||
open_cmd = "open"
|
open_cmd = "open"
|
||||||
else
|
elseif cmd.exists("start") then
|
||||||
result = os.execute("command -v start >/dev/null 2>&1")
|
|
||||||
if result == true or result == 0 then
|
|
||||||
-- Windows
|
-- Windows
|
||||||
open_cmd = "start"
|
open_cmd = "start"
|
||||||
else
|
else
|
||||||
io.stderr:write("Error: Unable to detect platform open command\n")
|
io.stderr:write("Error: Unable to detect platform open command\n")
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open the URL
|
-- Open the URL
|
||||||
os.execute(open_cmd .. " '" .. https_url .. "' 2>/dev/null")
|
cmd.execute(open_cmd .. " '" .. https_url .. "'")
|
||||||
|
|
||||||
-- Output JSON
|
-- Output JSON
|
||||||
print(json.encode({
|
print(json.encode({
|
||||||
|
|||||||
207
src/worktree.lua
Normal file
207
src/worktree.lua
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
local git = require("git")
|
||||||
|
local json = require("json")
|
||||||
|
local fs = require("fs")
|
||||||
|
local cmd = require("cmd")
|
||||||
|
|
||||||
|
local worktree = {}
|
||||||
|
|
||||||
|
-- Handle the root/return subcommand
|
||||||
|
function worktree.handle_root()
|
||||||
|
-- Check if we're in a git repository
|
||||||
|
if not git.in_repo() then
|
||||||
|
io.stderr:write("Error: Not in a git repository\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get repository root
|
||||||
|
local repo_root = git.get_repo_root()
|
||||||
|
|
||||||
|
-- Check if we're in a worktree
|
||||||
|
local git_common_dir = git.get_common_dir()
|
||||||
|
local cd_path = repo_root
|
||||||
|
|
||||||
|
if git_common_dir ~= ".git" and git_common_dir ~= "" then
|
||||||
|
-- We're in a worktree, get the main repo path
|
||||||
|
cd_path = git_common_dir:match("(.+)/.git")
|
||||||
|
end
|
||||||
|
|
||||||
|
print(json.encode({cd = cd_path, hook = "worktree"}))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle the list subcommand
|
||||||
|
function worktree.handle_list()
|
||||||
|
-- Check if we're in a git repository
|
||||||
|
if not git.in_repo() then
|
||||||
|
io.stderr:write("Error: Not in a git repository\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse origin URL
|
||||||
|
local domain, path = git.parse_origin()
|
||||||
|
if not domain or not path then
|
||||||
|
io.stderr:write("Error: Unable to parse repository origin URL\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get repository root
|
||||||
|
local repo_root = git.get_repo_root()
|
||||||
|
|
||||||
|
-- Calculate worktree base directory
|
||||||
|
local repotool_path = os.getenv("REPOTOOL_PATH") or os.getenv("HOME") .. "/repo"
|
||||||
|
local worktree_base = repotool_path .. "/.worktree/" .. domain .. "/" .. path
|
||||||
|
|
||||||
|
-- Get all worktrees using the git library
|
||||||
|
local worktrees = git.worktree_list()
|
||||||
|
|
||||||
|
-- Filter and display worktrees under our worktree base
|
||||||
|
io.stderr:write("Worktrees for " .. domain .. "/" .. path .. ":\n")
|
||||||
|
local found_any = false
|
||||||
|
|
||||||
|
local any_prunable = false
|
||||||
|
for _, wt in ipairs(worktrees) do
|
||||||
|
if wt.path == repo_root then
|
||||||
|
-- Skip the main repository
|
||||||
|
else
|
||||||
|
found_any = true
|
||||||
|
local wt_name = wt.path:match(".*/([^/]+)$")
|
||||||
|
local exists = fs.dir_exists(wt.path)
|
||||||
|
|
||||||
|
local status = ""
|
||||||
|
if not exists then
|
||||||
|
if wt.prunable then
|
||||||
|
any_prunable = true
|
||||||
|
status = " (prunable)"
|
||||||
|
else
|
||||||
|
status = " (missing)"
|
||||||
|
end
|
||||||
|
elseif wt.locked then
|
||||||
|
status = " (locked)"
|
||||||
|
elseif wt.detached then
|
||||||
|
status = " (detached)"
|
||||||
|
end
|
||||||
|
io.stderr:write(" - " .. wt_name .. status .. "\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if any_prunable then
|
||||||
|
io.stderr:write("Run 'git worktree prune' to remove prunable worktrees\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not found_any then
|
||||||
|
io.stderr:write(" No worktrees found\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
print(json.encode({hook = "worktree.list"}))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle the get subcommand
|
||||||
|
function worktree.handle_get(worktree_name)
|
||||||
|
-- Check if we're in a git repository
|
||||||
|
if not git.in_repo() then
|
||||||
|
io.stderr:write("Error: Not in a git repository\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse origin URL
|
||||||
|
local domain, path = git.parse_origin()
|
||||||
|
if not domain or not path then
|
||||||
|
io.stderr:write("Error: Unable to parse repository origin URL\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Calculate worktree base directory
|
||||||
|
local repotool_path = os.getenv("REPOTOOL_PATH") or os.getenv("HOME") .. "/repo"
|
||||||
|
local worktree_base = repotool_path .. "/.worktree/" .. domain .. "/" .. path
|
||||||
|
|
||||||
|
-- Construct worktree path
|
||||||
|
local worktree_path = worktree_base .. "/" .. worktree_name
|
||||||
|
|
||||||
|
-- Check if a prunable worktree exists at this path
|
||||||
|
local prunable_output = cmd.read_stdout("git worktree list | grep '" .. worktree_path .. ".*prunable'")
|
||||||
|
if prunable_output ~= "" then
|
||||||
|
io.stderr:write("Found prunable worktree at " .. worktree_path .. ", cleaning up...\n")
|
||||||
|
cmd.execute("git worktree prune")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if worktree already exists
|
||||||
|
local created = "false"
|
||||||
|
local exists_output = cmd.read_stdout("git worktree list | grep '" .. worktree_path .. "'")
|
||||||
|
if exists_output == "" then
|
||||||
|
-- Create parent directories if they don't exist
|
||||||
|
fs.mkdir_p(fs.dirname(worktree_path))
|
||||||
|
|
||||||
|
-- Create the worktree (try different methods)
|
||||||
|
local success = cmd.run("git worktree add '" .. worktree_path .. "' -b '" .. worktree_name .. "'") == 0
|
||||||
|
if not success then
|
||||||
|
success = cmd.run("git worktree add '" .. worktree_path .. "' '" .. worktree_name .. "'") == 0
|
||||||
|
end
|
||||||
|
if not success then
|
||||||
|
cmd.execute("git worktree add '" .. worktree_path .. "'")
|
||||||
|
end
|
||||||
|
created = "true"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Output JSON
|
||||||
|
print(json.encode({
|
||||||
|
cd = worktree_path,
|
||||||
|
domain = domain,
|
||||||
|
path = path,
|
||||||
|
worktree_name = worktree_name,
|
||||||
|
created = created,
|
||||||
|
hook = "worktree"
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle the remove/delete subcommand
|
||||||
|
function worktree.handle_remove(worktree_name)
|
||||||
|
-- Check if we're in a git repository
|
||||||
|
if not git.in_repo() then
|
||||||
|
io.stderr:write("Error: Not in a git repository\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse origin URL
|
||||||
|
local domain, path = git.parse_origin()
|
||||||
|
if not domain or not path then
|
||||||
|
io.stderr:write("Error: Unable to parse repository origin URL\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Calculate worktree base directory
|
||||||
|
local repotool_path = os.getenv("REPOTOOL_PATH") or os.getenv("HOME") .. "/repo"
|
||||||
|
local worktree_base = repotool_path .. "/.worktree/" .. domain .. "/" .. path
|
||||||
|
|
||||||
|
-- Construct worktree path
|
||||||
|
local worktree_path = worktree_base .. "/" .. worktree_name
|
||||||
|
|
||||||
|
-- Check if worktree exists
|
||||||
|
local exists_output = cmd.read_stdout("git worktree list | grep '" .. worktree_path .. "'")
|
||||||
|
if exists_output == "" then
|
||||||
|
io.stderr:write("Error: Worktree '" .. worktree_name .. "' not found\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove the worktree
|
||||||
|
io.stderr:write("Removing worktree '" .. worktree_name .. "'...\n")
|
||||||
|
local success = cmd.run("git worktree remove '" .. worktree_path .. "'") == 0
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
-- Try with --force if normal remove fails
|
||||||
|
io.stderr:write("Normal remove failed, trying with --force...\n")
|
||||||
|
success = cmd.run("git worktree remove --force '" .. worktree_path .. "'") == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if success then
|
||||||
|
io.stderr:write("Successfully removed worktree '" .. worktree_name .. "'\n")
|
||||||
|
print(json.encode({
|
||||||
|
removed = worktree_name,
|
||||||
|
path = worktree_path,
|
||||||
|
hook = "worktree.remove"
|
||||||
|
}))
|
||||||
|
else
|
||||||
|
io.stderr:write("Error: Failed to remove worktree '" .. worktree_name .. "'\n")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return worktree
|
||||||
@ -1,139 +0,0 @@
|
|||||||
local git = require("git")
|
|
||||||
local json = require("json")
|
|
||||||
local fs = require("fs")
|
|
||||||
|
|
||||||
local function worktree_command(args)
|
|
||||||
-- Check if we're in a git repository
|
|
||||||
if not git.in_repo() then
|
|
||||||
io.stderr:write("Error: Not in a git repository\n")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get repository root
|
|
||||||
local repo_root = git.get_repo_root()
|
|
||||||
|
|
||||||
-- Handle --root flag
|
|
||||||
if args.root then
|
|
||||||
-- Check if we're in a worktree
|
|
||||||
local git_common_dir = git.get_common_dir()
|
|
||||||
local cd_path = repo_root
|
|
||||||
|
|
||||||
if git_common_dir ~= ".git" and git_common_dir ~= "" then
|
|
||||||
-- We're in a worktree, get the main repo path
|
|
||||||
cd_path = git_common_dir:match("(.+)/.git")
|
|
||||||
end
|
|
||||||
|
|
||||||
print(json.encode({cd = cd_path, hook = "worktree"}))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse origin URL
|
|
||||||
local domain, path = git.parse_origin()
|
|
||||||
if not domain or not path then
|
|
||||||
io.stderr:write("Error: Unable to parse repository origin URL\n")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate worktree base directory
|
|
||||||
local repotool_path = os.getenv("REPOTOOL_PATH") or os.getenv("HOME") .. "/repo"
|
|
||||||
local worktree_base = repotool_path .. "/worktree/" .. domain .. "/" .. path
|
|
||||||
|
|
||||||
-- Handle --list flag
|
|
||||||
if args.list then
|
|
||||||
-- Get all worktrees using the git library
|
|
||||||
local worktrees = git.worktree_list()
|
|
||||||
|
|
||||||
-- Filter and display worktrees under our worktree base
|
|
||||||
io.stderr:write("Worktrees for " .. domain .. "/" .. path .. ":\n")
|
|
||||||
local found_any = false
|
|
||||||
|
|
||||||
local any_prunable = false
|
|
||||||
for _, wt in ipairs(worktrees) do
|
|
||||||
if wt.path == repo_root then
|
|
||||||
else
|
|
||||||
found_any = true
|
|
||||||
local wt_name = wt.path:match(".*/([^/]+)$")
|
|
||||||
local exists = fs.dir_exists(wt.path)
|
|
||||||
|
|
||||||
local status = ""
|
|
||||||
if not exists then
|
|
||||||
if wt.prunable then
|
|
||||||
any_prunable = true
|
|
||||||
status = " (prunable)"
|
|
||||||
else
|
|
||||||
status = " (missing)"
|
|
||||||
end
|
|
||||||
elseif wt.locked then
|
|
||||||
status = " (locked)"
|
|
||||||
elseif wt.detached then
|
|
||||||
status = " (detached)"
|
|
||||||
end
|
|
||||||
io.stderr:write(" - " .. wt_name .. status .. "\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if any_prunable then
|
|
||||||
io.stderr:write("Run 'git worktree prune' to remove prunable worktrees\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
if not found_any then
|
|
||||||
io.stderr:write(" No worktrees found\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
print(json.encode({hook = "worktree.list"}))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get worktree name from arguments
|
|
||||||
local worktree_name = args.name
|
|
||||||
|
|
||||||
-- Check if name is provided
|
|
||||||
if not worktree_name or worktree_name == "" then
|
|
||||||
io.stderr:write([[Error: Missing required argument: name
|
|
||||||
Run 'repo worktree --help' for usage information
|
|
||||||
]])
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Construct worktree path
|
|
||||||
local worktree_path = worktree_base .. "/" .. worktree_name
|
|
||||||
|
|
||||||
-- Check if a prunable worktree exists at this path
|
|
||||||
local check_prunable = io.popen("git worktree list | grep '" .. worktree_path .. ".*prunable'")
|
|
||||||
if check_prunable:read("*a") ~= "" then
|
|
||||||
io.stderr:write("Found prunable worktree at " .. worktree_path .. ", cleaning up...\n")
|
|
||||||
os.execute("git worktree prune")
|
|
||||||
end
|
|
||||||
check_prunable:close()
|
|
||||||
|
|
||||||
-- Check if worktree already exists
|
|
||||||
local created = "false"
|
|
||||||
local check_exists = io.popen("git worktree list | grep '" .. worktree_path .. "'")
|
|
||||||
if check_exists:read("*a") == "" then
|
|
||||||
-- Create parent directories if they don't exist
|
|
||||||
fs.mkdir_p(fs.dirname(worktree_path))
|
|
||||||
|
|
||||||
-- Create the worktree (try different methods)
|
|
||||||
local success = os.execute("git worktree add '" .. worktree_path .. "' -b '" .. worktree_name .. "' 2>/dev/null") == 0
|
|
||||||
if not success then
|
|
||||||
success = os.execute("git worktree add '" .. worktree_path .. "' '" .. worktree_name .. "' 2>/dev/null") == 0
|
|
||||||
end
|
|
||||||
if not success then
|
|
||||||
os.execute("git worktree add '" .. worktree_path .. "'")
|
|
||||||
end
|
|
||||||
created = "true"
|
|
||||||
end
|
|
||||||
check_exists:close()
|
|
||||||
|
|
||||||
-- Output JSON
|
|
||||||
print(json.encode({
|
|
||||||
cd = worktree_path,
|
|
||||||
domain = domain,
|
|
||||||
path = path,
|
|
||||||
worktree_name = worktree_name,
|
|
||||||
created = created,
|
|
||||||
hook = "worktree"
|
|
||||||
}))
|
|
||||||
end
|
|
||||||
|
|
||||||
return worktree_command
|
|
||||||
Loading…
Reference in New Issue
Block a user