repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
nushell/tests/git-worktree-layout.sh
Raw#!/usr/bin/env bash
set -euo pipefail
repo_root="$(git rev-parse --show-toplevel)"
module="$repo_root/nushell/modules/git.nu"
tmp="$(mktemp -d /tmp/dotfiles-wt-test-XXXXXX)"
trap 'rm -rf "$tmp"' EXIT
fail() { echo "FAIL: $*" >&2; exit 1; }
assert_dir() { [ -d "$1" ] || fail "missing dir: $1"; }
assert_file() { [ -f "$1" ] || fail "missing file: $1"; }
assert_no_path() { [ ! -e "$1" ] || fail "unexpected path: $1"; }
run_nu() {
local cwd="$1"
shift
DOTFILES_GIT_TEST_PHASE=nu nu --commands "use '$module' *; cd '$cwd'; $*"
}
expect_fail() {
local cwd="$1"
shift
if run_nu "$cwd" "$*" >/tmp/dotfiles-wt-unexpected-success.log 2>&1; then
cat /tmp/dotfiles-wt-unexpected-success.log >&2
fail "expected failure: $*"
fi
}
expected_exports='co
wt
wt add
wt done
wt go
wt list
wt lock
wt ls
wt migrate
wt move
wt new
wt path
wt prune
wt remove
wt repair
wt repo
wt rm
wt root
wt unlock'
actual_exports="$(perl -ne 'if (/^export def(?: --env)? (?:"([^"]+)"|([^\s\[]+))/) { print (($1 // $2) . "\n") }' "$module" | sort)"
[ "$actual_exports" = "$expected_exports" ] || fail "exported command list mismatch
expected:
$expected_exports
actual:
$actual_exports"
mkdir -p "$tmp/src" "$tmp/ws"
git init --bare "$tmp/origin.git" >/dev/null
git clone "$tmp/origin.git" "$tmp/src" >/dev/null 2>&1
git -C "$tmp/src" config user.email test@example.invalid
git -C "$tmp/src" config user.name test
echo root > "$tmp/src/root.txt"
git -C "$tmp/src" add root.txt
git -C "$tmp/src" commit -m init >/dev/null
git -C "$tmp/src" branch -M trunk
git -C "$tmp/src" push -u origin trunk >/dev/null 2>&1
git --git-dir="$tmp/origin.git" symbolic-ref HEAD refs/heads/trunk
run_nu "$tmp/ws" "co '$tmp/origin.git' default-demo --dir '$tmp/ws' | ignore"
assert_dir "$tmp/ws/default-demo/.bare"
assert_dir "$tmp/ws/default-demo/trunk"
git -C "$tmp/ws/default-demo/trunk" status --short --branch >/dev/null
run_nu "$tmp/ws" "co '$tmp/origin.git' demo --branch trunk --dir '$tmp/ws' | ignore"
assert_dir "$tmp/ws/demo/.bare"
assert_dir "$tmp/ws/demo/trunk"
assert_dir "$tmp/ws/demo/wt"
assert_file "$tmp/ws/demo/trunk/.git"
assert_no_path "$tmp/ws/demo/.git"
assert_no_path "$tmp/ws/demo/demo.trunk"
gitfile="$(cat "$tmp/ws/demo/trunk/.git")"
[[ "$gitfile" == "gitdir: $tmp/ws/demo/.bare/worktrees/"* ]] || fail "gitfile not absolute .bare path: $gitfile"
[ "$(git --git-dir="$tmp/ws/demo/.bare" config --bool core.bare)" = true ] || fail ".bare not bare"
[ -z "$(git --git-dir="$tmp/ws/demo/.bare" config --get extensions.relativeWorktrees || true)" ] || fail "relativeWorktrees set"
[ "$(git --git-dir="$tmp/ws/demo/.bare" config --bool worktree.useRelativePaths)" = false ] || fail "useRelativePaths not false"
git -C "$tmp/ws/demo/trunk" status --short --branch >/dev/null
[ "$(run_nu "$tmp/ws/demo/trunk" "wt root")" = "$tmp/ws/demo" ] || fail "wt root mismatch"
[ "$(run_nu "$tmp/ws/demo/trunk" "wt repo")" = "demo" ] || fail "wt repo mismatch"
[ "$(run_nu "$tmp/ws/demo/trunk" "wt path trunk")" = "$tmp/ws/demo/trunk" ] || fail "wt path trunk mismatch"
run_nu "$tmp/ws/demo/trunk" "wt | ignore"
run_nu "$tmp/ws/demo/trunk" "wt ls | ignore"
run_nu "$tmp/ws/demo/trunk" "wt list | ignore"
run_nu "$tmp/ws/demo/trunk" "wt repair"
run_nu "$tmp/ws/demo/trunk" "wt migrate | ignore"
expect_fail "$tmp/ws" "wt ls | ignore"
expect_fail "$tmp/ws/demo/trunk" "co . | ignore"
expect_fail "$tmp/ws/demo/trunk" "wt new trunk | ignore"
run_nu "$tmp/ws/demo/trunk" "wt new switch/me --switch | ignore; if \$env.PWD != '$tmp/ws/demo/wt/switch-me' { error make {msg: 'wt new --switch did not cd'} }"
assert_dir "$tmp/ws/demo/wt/switch-me"
run_nu "$tmp/ws/demo/wt/switch-me" "wt remove switch/me --force | ignore"
assert_no_path "$tmp/ws/demo/wt/switch-me"
run_nu "$tmp/ws/demo/trunk" "wt go go/create --create | ignore"
assert_dir "$tmp/ws/demo/wt/go-create"
run_nu "$tmp/ws/demo/wt/go-create" "wt remove go/create --force | ignore"
assert_no_path "$tmp/ws/demo/wt/go-create"
run_nu "$tmp/ws/demo/trunk" "wt new feature/x | ignore"
assert_dir "$tmp/ws/demo/wt/feature-x"
git -C "$tmp/ws/demo/wt/feature-x" status --short --branch >/dev/null
run_nu "$tmp/ws/demo/trunk" "wt lock feature/x"
run_nu "$tmp/ws/demo/trunk" "wt unlock feature/x"
run_nu "$tmp/ws/demo/trunk" "wt lock feature/x testing"
run_nu "$tmp/ws/demo/trunk" "wt unlock feature/x"
run_nu "$tmp/ws/demo/trunk" "wt go feature/x | ignore"
run_nu "$tmp/ws/demo/wt/feature-x" "wt rm feature/x --force | ignore"
assert_no_path "$tmp/ws/demo/wt/feature-x"
run_nu "$tmp/ws/demo/trunk" "wt new clean/remove | ignore"
assert_dir "$tmp/ws/demo/wt/clean-remove"
run_nu "$tmp/ws/demo/trunk" "wt remove clean/remove --branch | ignore"
assert_no_path "$tmp/ws/demo/wt/clean-remove"
! git --git-dir="$tmp/ws/demo/.bare" show-ref --verify --quiet refs/heads/clean/remove || fail "wt remove --branch kept branch"
run_nu "$tmp/ws/demo/trunk" "wt add move/me | ignore"
assert_dir "$tmp/ws/demo/wt/move-me"
run_nu "$tmp/ws/demo/trunk" "wt move move/me '$tmp/ws/demo/wt/moved-me' | ignore"
assert_no_path "$tmp/ws/demo/wt/move-me"
assert_dir "$tmp/ws/demo/wt/moved-me"
git -C "$tmp/ws/demo/wt/moved-me" status --short --branch >/dev/null
run_nu "$tmp/ws/demo/trunk" "wt done move/me --force | ignore"
assert_no_path "$tmp/ws/demo/wt/moved-me"
! git --git-dir="$tmp/ws/demo/.bare" show-ref --verify --quiet refs/heads/move/me || fail "wt done kept branch"
run_nu "$tmp/ws/demo/trunk" "wt prune"
# normal checkout migration: dirty/staged/untracked/ignored/stash preserved, original kept as backup.
git clone "$tmp/origin.git" "$tmp/normal" >/dev/null 2>&1
git -C "$tmp/normal" checkout -b local >/dev/null 2>&1
git -C "$tmp/normal" config user.email test@example.invalid
git -C "$tmp/normal" config user.name test
echo stash > "$tmp/normal/stash.txt"
git -C "$tmp/normal" add stash.txt
git -C "$tmp/normal" stash push -m stash-test >/dev/null
echo '*.log' > "$tmp/normal/.gitignore"
git -C "$tmp/normal" add .gitignore
echo dirty >> "$tmp/normal/root.txt"
echo staged > "$tmp/normal/staged.txt"
git -C "$tmp/normal" add staged.txt
echo untracked > "$tmp/normal/untracked.txt"
echo ignored > "$tmp/normal/ignored.log"
normal_status="$(git -C "$tmp/normal" status --porcelain=v1)"
run_nu "$tmp/normal" "wt migrate --dry | ignore"
run_nu "$tmp/normal" "wt migrate | ignore"
assert_dir "$tmp/normal/.bare"
assert_dir "$tmp/normal/local"
assert_dir "$tmp/normal/wt"
assert_file "$tmp/normal/local/.git"
assert_file "$tmp/normal/local/ignored.log"
[ "$(git -C "$tmp/normal/local" status --porcelain=v1)" = "$normal_status" ] || fail "normal migrate status mismatch"
git --git-dir="$tmp/normal/.bare" show-ref --verify --quiet refs/stash || fail "normal migrate lost stash"
compgen -G "$tmp/.normal.pre-wt-migrate-*" >/dev/null || fail "normal migrate missing backup"
# detached migration must fail without replacing checkout.
git clone "$tmp/origin.git" "$tmp/detached" >/dev/null 2>&1
git -C "$tmp/detached" checkout --detach >/dev/null 2>&1
expect_fail "$tmp/detached" "wt migrate | ignore"
assert_dir "$tmp/detached/.git"
# legacy bare-container migration: .git admin repo becomes .bare; worktrees move to trunk + wt/<branch>.
mkdir -p "$tmp/legacy"
git clone --bare "$tmp/origin.git" "$tmp/legacy/.git" >/dev/null 2>&1
git --git-dir="$tmp/legacy/.git" config wt.repo-name legacy
git --git-dir="$tmp/legacy/.git" config wt.main-branch trunk
git --git-dir="$tmp/legacy/.git" -c worktree.useRelativePaths=false worktree add "$tmp/legacy/legacy.trunk" trunk >/dev/null
git --git-dir="$tmp/legacy/.git" -c worktree.useRelativePaths=false worktree add -b feature/x "$tmp/legacy/legacy.feature-x" trunk >/dev/null
run_nu "$tmp/legacy/legacy.trunk" "wt migrate | ignore"
assert_dir "$tmp/legacy/.bare"
assert_no_path "$tmp/legacy/.git"
assert_dir "$tmp/legacy/trunk"
assert_dir "$tmp/legacy/wt/feature-x"
git -C "$tmp/legacy/trunk" status --short --branch >/dev/null
git -C "$tmp/legacy/wt/feature-x" status --short --branch >/dev/null
legacy_gitfile="$(cat "$tmp/legacy/trunk/.git")"
[[ "$legacy_gitfile" == "gitdir: $tmp/legacy/.bare/worktrees/"* ]] || fail "legacy gitfile not absolute .bare path: $legacy_gitfile"
[ -z "$(git --git-dir="$tmp/legacy/.bare" config --get extensions.relativeWorktrees || true)" ] || fail "legacy relativeWorktrees set"
[ "$(git --git-dir="$tmp/legacy/.bare" config --bool worktree.useRelativePaths)" = false ] || fail "legacy useRelativePaths not false"
printf 'ok git-worktree-layout\n'