This commit is contained in:
2026-02-25 16:39:22 +02:00
parent f84477f9b9
commit cf7f8609ac
8 changed files with 378 additions and 52 deletions

67
config/shared/bin/catfiles Executable file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="."
declare -a SKIP_DIRS=()
declare -a PATTERNS=()
usage() {
cat <<EOF
Usage: $0 [-r root_dir] [-s dir_to_skip]... -p pattern ...
Options:
-r DIR Root directory (default: .)
-s DIR Directory name to skip (can repeat)
-p PATTERN File pattern (e.g. '*.md', '*.txt') (required, can repeat)
Example:
$0 -p '*.md'
$0 -r src -s node_modules -s .git -p '*.md' -p '*.txt'
EOF
exit 1
}
while getopts ":r:s:p:" opt; do
case "$opt" in
r) ROOT="$OPTARG" ;;
s) SKIP_DIRS+=("$OPTARG") ;;
p) PATTERNS+=("$OPTARG") ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
# Require at least one pattern
(( ${#PATTERNS[@]} > 0 )) || usage
# Build prune expression
PRUNE_EXPR=()
if (( ${#SKIP_DIRS[@]} > 0 )); then
PRUNE_EXPR+=( \( )
for i in "${!SKIP_DIRS[@]}"; do
PRUNE_EXPR+=( -type d -name "${SKIP_DIRS[$i]}" -prune )
[[ $i -lt $((${#SKIP_DIRS[@]} - 1)) ]] && PRUNE_EXPR+=( -o )
done
PRUNE_EXPR+=( \) -o )
fi
# Build pattern expression
PATTERN_EXPR=( \( )
for i in "${!PATTERNS[@]}"; do
PATTERN_EXPR+=( -name "${PATTERNS[$i]}" )
[[ $i -lt $((${#PATTERNS[@]} - 1)) ]] && PATTERN_EXPR+=( -o )
done
PATTERN_EXPR+=( \) )
# Execute
find "$ROOT" \
"${PRUNE_EXPR[@]}" \
-type f \
"${PATTERN_EXPR[@]}" \
-print0 |
while IFS= read -r -d '' file; do
printf "\n===== %s =====\n" "$file"
cat "$file"
done

190
config/shared/bin/wt Executable file
View File

@@ -0,0 +1,190 @@
#!/usr/bin/env bash
set -euo pipefail
########################################
# Utilities
########################################
fail() {
echo "Error: $1" >&2
exit 1
}
ensure_repo() {
git rev-parse --is-inside-work-tree >/dev/null 2>&1 ||
fail "Not inside a git repository"
}
repo_root() {
git rev-parse --show-toplevel
}
detect_base() {
if git show-ref --verify --quiet refs/heads/main; then
echo "main"
elif git show-ref --verify --quiet refs/heads/master; then
echo "master"
else
git branch --show-current
fi
}
ensure_clean() {
git diff --quiet || fail "Working tree is dirty"
git diff --cached --quiet || fail "Index is dirty"
}
########################################
# Core Paths
########################################
ROOT=$(repo_root)
WT_DIR="$ROOT/.worktrees"
########################################
# Commands
########################################
cmd_new() {
local name="$1"
local base="${2:-$(detect_base)}"
local path="$WT_DIR/$name"
[ -d "$path" ] && fail "Worktree directory already exists"
git show-ref --verify --quiet "refs/heads/$name" &&
fail "Branch '$name' already exists"
mkdir -p "$WT_DIR"
git worktree add -b "$name" "$path" "$base"
echo "Created worktree:"
echo " branch: $name"
echo " path: $path"
}
cmd_list() {
git worktree list
}
cmd_path() {
echo "$WT_DIR/$1"
}
cmd_rm() {
local name="$1"
local path="$WT_DIR/$name"
[ -d "$path" ] || fail "Worktree not found"
git worktree remove "$path"
git branch -D "$name" >/dev/null 2>&1 || true
echo "Removed worktree '$name'"
}
cmd_finish() {
local name="$1"
shift
local keep_worktree=0
local path="$WT_DIR/$name"
local base
while [[ $# -gt 0 ]]; do
case "$1" in
--keep-worktree)
keep_worktree=1
shift
;;
*)
fail "Unknown option for finish: $1"
;;
esac
done
[ -d "$path" ] || fail "Worktree not found"
git show-ref --verify --quiet "refs/heads/$name" ||
fail "Branch '$name' not found"
base=$(detect_base)
echo "Base branch: $base"
echo "Feature branch: $name"
git checkout "$base" >/dev/null
ensure_clean
if ! git merge --squash "$name"; then
echo
echo "Resolve conflicts, then run:"
echo " git commit"
echo " wt rm $name"
exit 1
fi
git commit -m "$name"
if [ -d "$path" ] && [ "$keep_worktree" -eq 0 ]; then
if ! git worktree remove "$path"; then
git worktree remove --force "$path" || fail "Failed to remove worktree '$name'"
fi
fi
if [ "$keep_worktree" -eq 0 ]; then
git branch -D "$name"
fi
if [ "$keep_worktree" -eq 1 ]; then
echo "Merged (squash) into '$base' and kept worktree '$path'"
else
echo "Merged (squash) and removed '$name'"
fi
}
cmd_prune() {
git worktree prune
echo "Pruned stale worktree metadata"
}
########################################
# Entry
########################################
ensure_repo
case "${1:-}" in
new)
[ $# -lt 2 ] && fail "Usage: wt new <name> [base]"
cmd_new "$2" "${3:-}"
;;
list)
cmd_list
;;
path)
[ $# -lt 2 ] && fail "Usage: wt path <name>"
cmd_path "$2"
;;
finish)
[ $# -lt 2 ] && fail "Usage: wt finish <name>"
cmd_finish "$2" "${@:3}"
;;
rm)
[ $# -lt 2 ] && fail "Usage: wt rm <name>"
cmd_rm "$2"
;;
prune)
cmd_prune
;;
*)
cat <<EOF
Usage:
wt new <name> [base]
wt list
wt path <name>
wt finish <name> [--keep-worktree]
wt rm <name>
wt prune
EOF
exit 1
;;
esac