feat(install): adopt an existing checkout into the managed layout

Running scripts/install.sh from a checkout linked `dsh` straight at that
checkout, producing an install that `dsh-upgrade` cannot upgrade (there is
no `current` to repoint), that dangles if the checkout moves, and whose
launcher resolves to an arbitrary working branch.

In-repo mode still never clones and never touches the working tree, but it
now offers to adopt the checkout, and adoption is the default. The container
owns staging worktrees and `current`; the repository is discovered via
`git rev-parse --git-common-dir` rather than owned, so a clone anywhere on
disk converges on the same upgradable layout as a curl install and both
share one worktree/exclude/lock/link sequence.

Declining, or DSH_ADOPT=0, keeps the previous link-in-place behavior with a
warning naming what it costs, preserving the path that makes this script
testable against local source.

All path comparisons run on physical paths: macOS resolves /var through a
symlink to /private/var, and comparing a resolved path against an unresolved
one misclassified an existing managed install as a foreign clone.

Verified manually (no install.spec.ts, per request) with a harness driving
the real script under a stubbed pnpm across 33 assertions, plus both
interactive outcomes under tmux.
This commit is contained in:
Turtle
2026-07-31 20:22:40 +08:00
parent d64b032311
commit 631510f54e
7 changed files with 285 additions and 51 deletions

View File

@@ -26,6 +26,8 @@ The installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm`
The installer keeps every checkout under `~/.dsh/source`: the master clone at `~/.dsh/source/master` and each install's staging checkout as a git worktree `~/.dsh/source/staging-<timestamp>`. The stable symlink `~/.dsh/source/current` points at the active staging worktree, and `dsh` in `~/.local/bin` links to `current/bin/dsh`, so an upgrade repoints one symlink and the `dsh` on PATH never moves. Re-running the command adds a fresh staging worktree from an updated master and repoints `current` at it. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options.
Running the script from an existing clone (`sh scripts/install.sh`) never clones and never modifies that working tree. It offers to *adopt* the clone: the repository behind the checkout becomes the upgrade base, and a staging worktree branched from the checkout's current `HEAD` lands under `~/.dsh/source` with `current` pointing at it, so a clone anywhere on disk gets the same upgradable layout. Adoption carries committed work only — uncommitted changes stay in the clone. Declining (or `DSH_ADOPT=0`) links `dsh` straight at that checkout instead, which is not upgradable and breaks if the checkout moves.
## Use DeepSeek Harness
### Web UI