Skip to content

feat: introduce wt adopt as core worktree management primitive#29

Merged
guodong-sq merged 5 commits intoblock:mainfrom
bezhermoso:bezhermoso/adopt-refactor
Mar 11, 2026
Merged

feat: introduce wt adopt as core worktree management primitive#29
guodong-sq merged 5 commits intoblock:mainfrom
bezhermoso:bezhermoso/adopt-refactor

Conversation

@bezhermoso
Copy link
Copy Markdown
Contributor

@bezhermoso bezhermoso commented Mar 10, 2026

Implements major parts of #18


Add wt adopt command that gives any existing git worktree the wt treatment — metadata import, Bazel symlink installation, and a marker file at $(git rev-parse --git-dir)/wt/adopted to track adoption state.

This decouples worktree creation from wt management: worktrees created via plain git worktree add can now be retroactively adopted, and they don't need to live in the managed worktrees base directory.

Refactor wt add to use adoption internally — it now does git worktree add followed by wt_adopt_worktree(), replacing the inline install_metadata_for_worktree and
install_bazel_symlinks_for_worktree helpers that were moved to the new lib/wt-adopt library.

New files:

  • lib/wt-adopt: marker helpers (is/mark/unmark_adopted, is_main_repo), Bazel symlink installation, conflict detection, and full adoption treatment function
  • bin/wt-adopt: user-facing command with CWD/path/branch-name modes, main-repo guard, and --force/--redo flags

Overwrite protection

Adoption now detects pre-existing metadata and Bazel directories before overwriting. The conflict check scans the vault using the same find logic as wt-metadata-import, so it detects the exact same set of files that would be imported.

Interactive sessions get a 3-option prompt:

  • [O]verwrite — replace with vault contents
  • [K]eep — adopt without importing metadata (Bazel symlinks still installed)
  • [A]bort — cancel adoption

Non-interactive sessions abort with an error listing the conflicts.

Bazel symlinks (bazel-out, etc.) are only flagged as conflicts when they are real directories — existing symlinks are always safe to replace.

Flags

Flag Effect
--force Skip conflict checks, overwrite without prompting
--redo Re-run adoption treatment on already-adopted worktrees

--force on an already-adopted worktree also triggers re-run (implies --redo).

Also: shell completions, dispatch wiring, test helpers (assert_is_adopted/refute_is_adopted), unit + integration tests (36 adopt-specific tests).

TODO

  • Auto-adopt on first switch
  • Adoption indicators in wt list

Add `wt adopt` command that gives any existing git worktree the "wt
treatment" — metadata import, Bazel symlink installation, and a marker
file at $(git rev-parse --git-dir)/wt/adopted to track adoption state.

This decouples worktree creation from wt management: worktrees created
via plain `git worktree add` can now be retroactively adopted, and they
don't need to live in the managed worktrees base directory.

Refactor `wt add` to use adoption internally — it now does
`git worktree add` followed by `wt_adopt_worktree()`, replacing the
inline `install_metadata_for_worktree` and
`install_bazel_symlinks_for_worktree` helpers that were moved to the
new `lib/wt-adopt` library.

New files:
- lib/wt-adopt: marker helpers (is/mark/unmark_adopted, is_main_repo),
  Bazel symlink installation, and full adoption treatment function
- bin/wt-adopt: user-facing command with CWD/path/branch-name modes,
  main-repo guard, and idempotent re-adopt prompt

Also: shell completions, dispatch wiring, test helpers
(assert_is_adopted/refute_is_adopted), unit + integration tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Ai-assisted: true
Adoption now checks for pre-existing metadata and Bazel directories
before overwriting. The conflict check mirrors wt-metadata-import's
vault-scanning logic so it detects the exact same set of files.

Interactive sessions get a 3-option prompt:
  [O]verwrite — replace with vault contents
  [K]eep     — adopt without importing metadata (Bazel symlinks still installed)
  [A]bort    — cancel adoption

Non-interactive sessions abort with an error listing the conflicts.

New flags for bin/wt-adopt:
  --force  skip conflict checks (overwrite without prompting)
  --redo   re-run adoption treatment on already-adopted worktrees

bin/wt-add now passes --force to wt_adopt_worktree since freshly
created worktrees have no conflicts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Ai-assisted: true
Comment thread lib/wt-adopt
Comment on lines +266 to +279
if [[ "$keep_existing" != true ]]; then
local metadata_import=""
if [[ -n "${SCRIPT_DIR:-}" && -x "$SCRIPT_DIR/wt-metadata-import" ]]; then
metadata_import="$SCRIPT_DIR/wt-metadata-import"
elif [[ -x "$HOME/.wt/bin/wt-metadata-import" ]]; then
metadata_import="$HOME/.wt/bin/wt-metadata-import"
elif command -v wt-metadata-import >/dev/null 2>&1; then
metadata_import="wt-metadata-import"
fi

if [[ -n "$metadata_import" && -d "${WT_IDEA_FILES_BASE:-}" ]]; then
info "Importing metadata into worktree: $worktree_path"
"$metadata_import" -y "$WT_IDEA_FILES_BASE" "$worktree_path"
fi
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failures here should get bubbled up so that a worktree is not stamped as "adopted" with metadata never imported and no user feedback in cases where import fails.

(I had this happened to me before where I deleted the symlink src of the metadata)

Comment thread bin/wt-adopt Outdated

# Resolve target worktree
if [[ -z "$TARGET_WORKTREE" ]]; then
TARGET_WORKTREE="$(pwd -P)"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When wt adopt is run with no arguments from a subdirectory inside a worktree (e.g., /repo-java/orders/src/main), TARGET_WORKTREE is set to the current physical directory via pwd -P, not to the worktree's root. All downstream treatment then operates on that subdirectory: wt-metadata-import installs vault contents into the subdirectory, and wt_install_bazel_symlinks creates bazel-out/bazel-bin symlinks inside it. Only wt_mark_adopted is unaffected because it uses git -C "$worktree_path" rev-parse --git-dir.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Comment thread lib/wt-adopt Outdated
fi

ln -s "$target" "$dst_link"
echo " ✓ $symlink_name -> $target"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use log utilities

Comment thread bin/wt-add Outdated

install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"
wt_adopt_worktree --force "$(cd "$worktree_path" && pwd)"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these be pwd -P as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah def

bezhermoso and others added 3 commits March 10, 2026 16:58
1. Metadata import failure now prevents adoption marker from being
   written. If wt-metadata-import exits non-zero, wt_adopt_worktree
   returns 1 and the worktree is not stamped as adopted.

2. CWD mode (`wt adopt` with no args) now resolves to the worktree
   root via `git rev-parse --show-toplevel`, not `pwd -P`. Running
   from a subdirectory no longer installs metadata/symlinks into the
   wrong location.

3. Use `info` log utility in wt_install_bazel_symlinks instead of
   raw `echo` for consistency with the rest of the codebase.

4. Use `pwd -P` (physical path) in all wt_adopt_worktree call sites
   in bin/wt-add to match codebase conventions and avoid macOS
   symlink issues (e.g. /var -> /private/var).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Ai-assisted: true
The interactive "keep" prompt test requires expect(1) to simulate a
TTY. Skip gracefully in CI environments where expect is not installed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Ai-assisted: true
Extract TTY check into _wt_is_interactive() function in lib/wt-adopt
so tests can override it. Removes the expect(1) dependency that broke
CI (not installed on GitHub Actions runners).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Ai-assisted: true
@guodong-sq guodong-sq merged commit efe2093 into block:main Mar 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants