|
#!/usr/bin/env bash |
|
# |
|
# wt - utilidad para gestionar git worktrees: permite listarlos, abrirlos |
|
# en VS Code por hash/branch, crear uno a partir de un PR de GitHub, o |
|
# crear una branch y worktree nuevos. |
|
# |
|
# Usage: |
|
# wt list Lista los worktrees del repo actual |
|
# wt open <query> Abre un worktree existente en VS Code |
|
# wt open-pr <PR> Crea (si falta) y abre el worktree de un PR |
|
# wt new <name> Crea una branch y worktree nuevos |
|
# wt help Muestra la ayuda completa |
|
# |
|
# Examples: |
|
# wt list Lista los worktrees del repo actual |
|
# wt open feature/login Abre el worktree de la branch feature/login |
|
# wt open 32 Query numérica: equivale a "wt open-pr 32" |
|
# wt open-pr 482 Crea (si falta) y abre el worktree del PR #482 |
|
# wt new feature/checkout Crea la branch y worktree feature/checkout |
|
# |
|
set -euo pipefail |
|
|
|
# CONFIGS |
|
WORKTREES_ROOT="${WT_ROOT:-$HOME/worktrees}" |
|
WT_EDITOR="${WT_EDITOR:-code}" |
|
|
|
repo_name() { |
|
basename "$(git rev-parse --show-toplevel)" |
|
} |
|
|
|
cmd_list() { |
|
local prs="" |
|
if command -v gh >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then |
|
prs="$(gh pr list --state open --json number,headRefName 2>/dev/null || echo '[]')" |
|
fi |
|
|
|
local c_pr="" c_hash="" c_branch="" c_reset="" |
|
if [[ -t 1 ]]; then |
|
c_pr=$'\033[1;32m' |
|
c_hash=$'\033[33m' |
|
c_branch=$'\033[36m' |
|
c_reset=$'\033[0m' |
|
fi |
|
|
|
git worktree list --porcelain | awk ' |
|
/^worktree / { path=$2; n++ } |
|
/^HEAD / { hash=$2 } |
|
/^branch / { branch=$2; sub("refs/heads/", "", branch) } |
|
/^$/ { |
|
if (path != "") print n "\t" hash "\t" branch |
|
path=""; hash=""; branch="" |
|
} |
|
END { |
|
if (path != "") print n "\t" hash "\t" branch |
|
} |
|
' | while IFS=$'\t' read -r idx hash branch; do |
|
if [[ "$idx" == "1" || -z "$branch" ]]; then |
|
continue |
|
fi |
|
local short="${hash:0:7}" |
|
local pr="" |
|
if [[ -n "$prs" ]]; then |
|
pr="$(jq -r --arg b "$branch" '.[] | select(.headRefName == $b) | "#\(.number)"' <<<"$prs" 2>/dev/null)" |
|
fi |
|
if [[ -n "$pr" ]]; then |
|
printf -- '- %s%s%s %s%s%s %s%s%s\n' "$c_pr" "$pr" "$c_reset" "$c_hash" "$short" "$c_reset" "$c_branch" "$branch" "$c_reset" |
|
else |
|
printf -- '- %s%s%s %s%s%s\n' "$c_hash" "$short" "$c_reset" "$c_branch" "$branch" "$c_reset" |
|
fi |
|
done |
|
} |
|
|
|
cmd_open() { |
|
local query="$1" |
|
local match |
|
|
|
if [[ "$query" =~ ^[0-9]+$ ]]; then |
|
cmd_open_pr "$query" |
|
return |
|
fi |
|
match="$(git worktree list --porcelain | awk -v q="$query" ' |
|
/^worktree / { path=$2 } |
|
/^HEAD / { hash=$2 } |
|
/^branch / { branch=$2; sub("refs/heads/", "", branch) } |
|
/^$/ { |
|
if (path != "" && (index(hash, q) == 1 || branch == q)) { print path; exit } |
|
path=""; hash=""; branch="" |
|
} |
|
END { |
|
if (path != "" && (index(hash, q) == 1 || branch == q)) print path |
|
} |
|
')" |
|
if [[ -z "$match" ]]; then |
|
echo "wt: no worktree found matching '$query'" >&2 |
|
exit 1 |
|
fi |
|
"$WT_EDITOR" "$match" |
|
} |
|
|
|
# |
|
# Sección "Uso" (Usage) del texto de ayuda (cmd_help): documenta cada |
|
# subcomando soportado por el script (list, open, open-pr, new, help) |
|
# junto con sus argumentos y qué hace cada uno. Se imprime tal cual al |
|
# llamar `wt help` o al invocar un comando desconocido. |
|
# |
|
cmd_help() { |
|
cat <<'EOF' |
|
wt - manejo de git worktrees |
|
|
|
Uso: |
|
wt list Lista los worktrees del repo actual |
|
wt open <query> Abre en VS Code el worktree cuyo commit hash (prefijo) |
|
o branch coincida con <query>. Si <query> es un número, |
|
se trata como PR (equivale a "wt open-pr <query>") |
|
wt open-pr <PR> Busca el PR con gh, crea el worktree si no existe |
|
(en $WORKTREES_ROOT/<repo>/<branch>) y lo abre en VS Code |
|
wt new <name> Crea una branch y worktree nuevos a partir de <name> |
|
(en $WORKTREES_ROOT/<repo>/<name>) y lo abre en VS Code |
|
wt help Muestra esta ayuda |
|
|
|
Variables de entorno: |
|
WT_ROOT Carpeta raíz para worktrees creados por open-pr |
|
(default: ~/worktrees) |
|
WT_EDITOR Comando para abrir el worktree en cada subcomando |
|
(default: code) |
|
EOF |
|
} |
|
|
|
cmd_open_pr() { |
|
local pr="$1" |
|
local branch repo target existing |
|
|
|
branch="$(gh pr view "$pr" --json headRefName -q .headRefName)" |
|
repo="$(repo_name)" |
|
target="$WORKTREES_ROOT/$repo/${branch//\//-}" |
|
|
|
existing="$(git worktree list --porcelain | awk -v b="refs/heads/$branch" ' |
|
/^worktree / { path=$2 } |
|
/^branch / { if ($2 == b) print path } |
|
')" |
|
|
|
if [[ -z "$existing" ]]; then |
|
git fetch origin "pull/$pr/head:$branch" |
|
mkdir -p "$WORKTREES_ROOT/$repo" |
|
git worktree add "$target" "$branch" |
|
existing="$target" |
|
fi |
|
|
|
"$WT_EDITOR" "$existing" |
|
} |
|
|
|
cmd_new() { |
|
local name="$1" |
|
local repo target existing |
|
|
|
if [[ -z "$name" ]]; then |
|
echo "wt: se requiere un nombre para 'wt new <name>'" >&2 |
|
exit 1 |
|
fi |
|
|
|
repo="$(repo_name)" |
|
target="$WORKTREES_ROOT/$repo/${name//\//-}" |
|
|
|
existing="$(git worktree list --porcelain | awk -v b="refs/heads/$name" ' |
|
/^worktree / { path=$2 } |
|
/^branch / { if ($2 == b) print path } |
|
')" |
|
|
|
if [[ -n "$existing" ]]; then |
|
echo "wt: ya existe un worktree para la branch '$name' en $existing" >&2 |
|
"$WT_EDITOR" "$existing" |
|
return |
|
fi |
|
|
|
mkdir -p "$WORKTREES_ROOT/$repo" |
|
git worktree add -b "$name" "$target" |
|
"$WT_EDITOR" "$target" |
|
} |
|
|
|
case "${1:-help}" in |
|
list) cmd_list ;; |
|
open) shift; cmd_open "$1" ;; |
|
open-pr) shift; cmd_open_pr "$1" ;; |
|
new) shift; cmd_new "$1" ;; |
|
help|-h|--help) cmd_help ;; |
|
*) echo "wt: unknown command '$1'" >&2; cmd_help >&2; exit 1 ;; |
|
esac |