Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 40 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Git worktrees let you work on multiple branches in parallel, but IntelliJ treats
This toolkit makes IntelliJ context switching **instant** by:

- **Symlink trick**: IntelliJ always opens the same path; switching worktrees looks like a branch checkout → incremental refresh in seconds, not minutes
- **Metadata vault**: `.ijwb` directories are stored externally and automatically installed into every new worktree—no manual Bazel import needed
- **Metadata vault**: IDE project metadata (`.ijwb`, `.idea`, `.vscode`, etc.) is stored externally and automatically installed into every new worktree—no manual IDE setup needed
- **Safe worktree management**: Automatic stash/restore, branch creation, and cleanup of merged branches
- **Parallel development at scale**: Works for humans and AI agents alike

Expand All @@ -36,8 +36,8 @@ The installer will:
3. Prompt for workspace paths (main repo, worktrees, metadata vault)
4. Create required directories
5. Optionally migrate existing repo to worktree structure
6. Optionally export `.ijwb` metadata to the vault
7. Optionally set up a nightly cron job to refresh `.ijwb` metadata
6. Optionally export project metadata to the vault
7. Optionally set up a nightly cron job to refresh Bazel IDE metadata

## Workflow

Expand All @@ -50,26 +50,26 @@ The directory structure expected (controlled by environment variables, can be ov
├── java -> java-master # Symlink (IntelliJ opens this)
├── java-master/ # Main repository
├── java-worktrees/ # Worktrees go here
└── idea-project-files/ # .ijwb metadata vault
└── idea-project-files/ # Project metadata vault
```

### Full Workflow Diagram

```
┌─────────────────────────────────────────────┐
│ External IntelliJ Metadata Vault │
│ External Project Metadata Vault
│ ~/Development/idea-project-files │
│ (canonical .ijwb directories)
│ (IDE configs: .ijwb, .idea, etc.)
└──────────▲───────────────┬──────────────────┘
│ │
│ │
┌────wt ijwb-export───┘ └───wt ijwb-import──┐
│ │
┌──────────┴───────────────────────┐ ┌───────────▼────────────────────────┐
│ │
┌──wt metadata-export─┘ └──wt metadata-import─┐
│ │
┌──────────┴───────────────────────┐ ┌─────────────▼──────────────────────┐
│ Main Repository │ │ Worktrees │
│ ~/Development/java-master │ wt add │ ~/Development/java-worktrees/... │
│ • master branch │ ──────────────────► │ • feature/foo │
│ • safe stash/pull/restore │ (calls ijwb-import) │ • bugfix/bar │
│ • safe stash/pull/restore │(calls metadata-imp) │ • bugfix/bar │
│ • never removed │ │ • agent-task-123 │
└───────────────┬──────────────────┘ └─────────┬──────────────────────────┘
│ │
Expand Down Expand Up @@ -104,7 +104,7 @@ When creating with `-b`, the script:
1. Stashes uncommitted changes
2. Switches to master, pulls latest
3. Creates branch + worktree
4. Imports .ijwb metadata
4. Imports project metadata from vault
5. Restores original state

### Switching Worktrees
Expand Down Expand Up @@ -167,26 +167,26 @@ Safety features:
- Always prompts for confirmation if uncommitted changes exist, even with `-y`
- `--merged` mode: automatically finds and removes all worktrees whose branches are merged

### Managing IntelliJ Metadata
### Managing Project Metadata

```bash
# Export .ijwb from main repo to vault (run after importing new Bazel projects)
wt ijwb-export
# Export metadata from main repo to vault (run after setting up new IDE projects)
wt metadata-export

# Import .ijwb into a worktree (interactive selection if target omitted)
wt ijwb-import
wt ijwb-import ~/Development/java-worktrees/feature/foo
# Import metadata into a worktree (interactive selection if target omitted)
wt metadata-import
wt metadata-import ~/Development/java-worktrees/feature/foo

# Skip confirmation prompts (useful in scripts)
wt ijwb-export -y
wt ijwb-import -y ~/Development/java-worktrees/feature/foo
wt metadata-export -y
wt metadata-import -y ~/Development/java-worktrees/feature/foo
```

### Refreshing Stale .ijwb Metadata (Cron Job)
### Refreshing Stale Bazel IDE Metadata (Cron Job)

When most development work is done in worktrees, the `.ijwb` directories in the main repository can become stale (targets files don't reflect new Bazel targets).
When most development work is done in worktrees, the Bazel IDE directories (`.ijwb`, `.aswb`, `.clwb`) in the main repository can become stale (targets files don't reflect new Bazel targets).

The `lib/wt-ijwb-refresh` script is designed to run as a cron job to keep metadata current.
The `lib/wt-metadata-refresh` script is designed to run as a cron job to keep metadata current.

**Note:** When IntelliJ has `derive_targets_from_directories: true` in `.bazelproject` (the default), it queries Bazel fresh on every sync. The `targets-*` file serves as a cache for initial project imports and may improve import speed.

Expand All @@ -202,27 +202,28 @@ mkdir -p ~/.wt/logs
crontab -e

# Add this line to run nightly at 2am (uses login shell for full PATH):
0 2 * * * /bin/zsh -lc '~/.wt/lib/wt-ijwb-refresh' >> ~/.wt/logs/ijwb-refresh.log 2>&1
0 2 * * * /bin/zsh -lc '~/.wt/lib/wt-metadata-refresh' >> ~/.wt/logs/metadata-refresh.log 2>&1
```

You can also run the script manually:

```bash
# Refresh all .ijwb directories and re-export to vault
~/.wt/lib/wt-ijwb-refresh
# Refresh all Bazel IDE directories and re-export to vault
~/.wt/lib/wt-metadata-refresh

# Preview what would be refreshed (dry run)
~/.wt/lib/wt-ijwb-refresh --dry-run
~/.wt/lib/wt-metadata-refresh --dry-run

# Refresh targets files only (skip re-export step)
~/.wt/lib/wt-ijwb-refresh --no-export
~/.wt/lib/wt-metadata-refresh --no-export
```

The refresh script:
- Uses `bazel query` to regenerate `targets/targets-*` files in each `.ijwb` directory
- Uses `bazel query` to regenerate `targets/targets-*` files in each Bazel IDE directory
- Supports all Bazel patterns configured in WT_METADATA_PATTERNS (`.ijwb`, `.aswb`, `.clwb`)
- Parses `.bazelproject` to determine which directories to include in the query
- Preserves existing targets file hashes (IntelliJ may reference them)
- Re-exports refreshed metadata to the vault
- Re-exports all metadata to the vault (including non-Bazel patterns)
- Logs timestamped output for monitoring
- Returns exit codes: 0=success, 1=error, 2=partial success

Expand Down Expand Up @@ -269,7 +270,7 @@ export WT_WORKTREES_BASE="$HOME/Development/java-worktrees"


### WT_IDEA_FILES_BASE
Canonical metadata vault storing `.ijwb` directories.
Canonical metadata vault storing project metadata (IDE configs, etc.).

**Default:** `~/Development/idea-project-files`

Expand All @@ -278,9 +279,9 @@ export WT_IDEA_FILES_BASE="$HOME/Development/idea-project-files"
```

Used by:
- wt-ijwb-import
- wt-ijwb-export
- wt-ijwb-refresh
- wt-metadata-import
- wt-metadata-export
- wt-metadata-refresh
- wt-add (when installing metadata)


Expand Down Expand Up @@ -332,14 +333,14 @@ wt/
│ ├── wt-list
│ ├── wt-remove
│ ├── wt-switch
│ ├── wt-ijwb-import
│ └── wt-ijwb-export
│ ├── wt-metadata-import
│ └── wt-metadata-export
├── lib/ # Shared libraries
│ ├── wt-common # Configuration and helpers
│ ├── wt-choose # Interactive worktree selection
│ ├── wt-help # Help text for wt command
│ ├── wt-completion # Shell completion for wt command
│ └── wt-ijwb-refresh # Cron script to refresh .ijwb metadata
│ └── wt-metadata-refresh # Cron script to refresh Bazel IDE metadata
├── completion/ # Shell completions for wt-* scripts
│ ├── wt.zsh
│ └── wt.bash
Expand All @@ -352,12 +353,12 @@ wt/
You can also run the underlying scripts directly:

```bash
wt-add, wt-switch, wt-remove, wt-list, wt-cd, wt-ijwb-export, wt-ijwb-import
wt-add, wt-switch, wt-remove, wt-list, wt-cd, wt-metadata-export, wt-metadata-import
```

These are located in `bin/` and work identically to the `wt` subcommands.

The `lib/wt-ijwb-refresh` script is designed for cron jobs and can be run directly from its location.
The `lib/wt-metadata-refresh` script is designed for cron jobs and can be run directly from its location.

## Project Resources

Expand Down
42 changes: 21 additions & 21 deletions bin/wt-add
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
# 2. If NOT creating a new branch (i.e., no -b/--branch flag), supports:
# a) wt-add <path> <branch> [extra args...]
# → Direct passthrough to `git worktree add <path> <branch> ...`,
# then installs IntelliJ `.ijwb` metadata into the new worktree.
# then installs project metadata into the new worktree.
#
# b) wt-add <branch>
# → Convenience mode:
# path = $WT_WORKTREES_BASE/<branch>
# git worktree add "$path" "<branch>"
# install IntelliJ `.ijwb` metadata into "$path".
# install project metadata into "$path".
#
# 3. If creating a new branch (using -b/--branch):
# a. If there are uncommitted changes in the base repo:
Expand All @@ -28,7 +28,7 @@
# - Switch to that base branch.
# c. Ensure the base branch is up to date by running `git pull`.
# d. Run `git worktree add <args>` to create the new worktree.
# e. Run wt-ijwb-import to import IntelliJ metadata into the new worktree.
# e. Run wt-metadata-import to import project metadata into the new worktree.
# f. Create symlinks for Bazel outputs (bazel-out, bazel-bin, etc.) pointing
# to the same targets as in the main repo, to speed up IntelliJ sync.
# g. After completion:
Expand All @@ -48,18 +48,18 @@
# - `git pull` only happens when the script is truly on the base branch.
# - Detached HEAD states are restored correctly.
# - Worktree creation behaves like `git worktree add`, but with safety
# guardrails and automatic IntelliJ metadata installation.
# guardrails and automatic project metadata installation.
#
# Usage examples:
# ---------------
# wt-add -b feature/foo
# → Creates a worktree at $WT_WORKTREES_BASE/feature/foo and installs .ijwb
# → Creates a worktree at $WT_WORKTREES_BASE/feature/foo and installs metadata
#
# wt-add -b feature/foo /custom/path origin/master
# → Uses the explicit path and extra arguments as-is, then installs .ijwb
# → Uses the explicit path and extra arguments as-is, then installs metadata
#
# wt-add ../path/to/worktree existing-branch
# → No branch creation; simply runs git worktree add and installs .ijwb.
# → No branch creation; simply runs git worktree add and installs metadata.
#

set -euo pipefail
Expand Down Expand Up @@ -136,28 +136,28 @@ while [[ $i -lt ${#ARGS[@]} ]]; do
i=$((i + 1))
done

# Helper: install IntelliJ metadata for a created worktree
install_ijwb_for_worktree() {
# Helper: install project metadata for a created worktree
install_metadata_for_worktree() {
local worktree_path="$1"

# Normalize to absolute (worktree now exists)
local worktree_path_abs
worktree_path_abs="$(cd "$worktree_path" && pwd)"

echo "Installing .ijwb metadata into worktree: $worktree_path_abs"
echo "Installing project metadata into worktree: $worktree_path_abs"

# Find wt-ijwb-import: try script directory first, then PATH
local ijwb_import=""
if [[ -f "$SCRIPT_DIR/wt-ijwb-import" ]]; then
ijwb_import="$SCRIPT_DIR/wt-ijwb-import"
elif command -v wt-ijwb-import >/dev/null 2>&1; then
ijwb_import="wt-ijwb-import"
# Find wt-metadata-import: try script directory first, then PATH
local metadata_import=""
if [[ -f "$SCRIPT_DIR/wt-metadata-import" ]]; then
metadata_import="$SCRIPT_DIR/wt-metadata-import"
elif command -v wt-metadata-import >/dev/null 2>&1; then
metadata_import="wt-metadata-import"
else
error "Cannot find wt-ijwb-import"
error "Cannot find wt-metadata-import"
return 1
fi

"$ijwb_import" -y "$WT_IDEA_FILES_BASE" "$worktree_path_abs"
"$metadata_import" -y "$WT_IDEA_FILES_BASE" "$worktree_path_abs"
}

# Helper: install Bazel output symlinks (bazel-out, bazel-bin, etc.) for a created worktree
Expand Down Expand Up @@ -257,7 +257,7 @@ if [[ $creating_new_branch -eq 0 ]]; then
echo "Creating worktree for existing branch '$local_branch' at: $worktree_path"
git worktree add -- "$worktree_path" "$local_branch"

install_ijwb_for_worktree "$worktree_path"
install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"
success "git worktree add completed successfully."
exit 0
Expand All @@ -279,7 +279,7 @@ if [[ $creating_new_branch -eq 0 ]]; then
info "Not creating a new branch; running: git worktree add ${ARGS[*]}"
git worktree add "${ARGS[@]}"

install_ijwb_for_worktree "$worktree_path"
install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"
success "git worktree add completed successfully."
exit 0
Expand Down Expand Up @@ -466,7 +466,7 @@ else
git worktree add "${FINAL_ARGS[@]}"
fi

install_ijwb_for_worktree "$worktree_path"
install_metadata_for_worktree "$worktree_path"
install_bazel_symlinks_for_worktree "$worktree_path"

success "git worktree add completed successfully."
Expand Down
Loading