Skip to content

Add branch-addressed merge with --branch - #4360

Open
liuyinchu wants to merge 2 commits into
max-sixty:mainfrom
liuyinchu:feat/merge-source-branch-1637
Open

liuyinchu wants to merge 2 commits into
max-sixty:mainfrom
liuyinchu:feat/merge-source-branch-1637

Conversation

@liuyinchu

@liuyinchu liuyinchu commented Oct 3, 2026 •

Copy link
Copy Markdown

Status

Draft proposal for #1637. The CLI spelling is proposed for maintainer review, following the existing wt step commit --branch interface introduced by #1750.

Summary

  • Add wt merge [target] --branch/-b <source>. Resolve through the existing worktree selector, including branch names, paths, and @; require a checked-out, non-detached source branch.
  • Carry the selected source environment through commit, squash, rebase, target advancement, approved hooks, and cleanup. Reuse the source's project-config cache so a rewrite cannot silently replace approved commands.
  • Keep caller identity separate. Merging another checkout emits no cd directive and does not approve/run a post-switch hook. Explicitly selecting the current checkout retains normal cleanup navigation, including linked worktrees of bare repositories.
  • Resolve source-sensitive Git operations through WorkingTree and captured object IDs, so inherited GIT_DIR, GIT_WORK_TREE, and GIT_INDEX_FILE cannot turn the caller into the merge source.
  • Generate squash prompts from the selected source index instead of rediscovering the process cwd.
  • Preserve source checkouts containing another registered worktree. Merge cleanup rechecks fresh, canonicalized topology after pre-remove hooks and before directory changes and final ownership/lock/dirty checks; uncertain path resolution fails closed. Ordinary remove, picker, and batch prune retain their upstream staging sequence without the added topology scan.

Why the removal change is necessary

A source may contain the invoking checkout under an ignored .worktrees/ directory. Git's ordinary dirty check does not see that independent checkout. Removing the source could otherwise delete the caller's staged and untracked work. The new guard protects any registered nested checkout, including the target or a checkout created by a pre-remove hook.

The merge-specific registry read can block, so it precedes the final dirty/lock/ownership gates. A deterministic race test writes an untracked source file during that wait and verifies cleanup refuses it. The original shared-staging concurrency regression is restored, preserving the behavior introduced by #3954.

Scope decisions

  • No new checkout is created for a branch that lacks a worktree.
  • No global process cwd/environment mutation, temporary retargeting of an existing checkout, remote fetch, or remote push is introduced.
  • A conflict is left in the source checkout, with its location printed. Existing conflict resolution and source safety rules remain applicable.
  • commit.generation subprocess working-directory behavior remains consistent with existing wt step commit --branch: the configured command inherits the invoking process's cwd. The generated prompt/index is correctly source-scoped. Changing the subprocess cwd is a separate product decision, particularly for relative executable paths.
  • Registered nested-worktree protection follows this repository's Git registry; this is not a recursive scanner for every unrelated nested repository on disk.

Validation

Validated on Linux x86-64 with Rust/Cargo 1.97.0. Git was 2.52.0, below the repository setup's required 2.54.0.

  • cargo test --locked --test integration merge_source: 25 passed
  • Removal-core unit tests: 23 passed; original help tests/snapshots: 56 passed
  • cargo clippy --locked --all-targets --all-features -- -D warnings and cargo fmt --all -- --check: passed
  • Documentation sync, documentation-site build, and warning-free Rust documentation: passed
  • Doctests: 27 passed, 15 ignored
  • Full default-feature nextest run: 4,913 passed, 4 failed, 1 skipped. All four failures are unchanged-baseline Unix-domain socket tests failing at socket creation with Operation not permitted (os error 1); each was reproduced on the unmodified base
  • git diff --check and clean-base patch application: passed

The required aggregate cargo run --locked -- hook pre-merge --yes was attempted but its execution session ended without a final result. The follow-up passed pre-commit run --all-files, including all-target/all-feature Clippy. The complete pre-merge gate and full all-feature/real-shell suite remain unverified locally. Initial-head hosted Linux/macOS tests passed; Windows found four test path-representation mismatches, now corrected with existing path helpers and awaiting new-head CI. zsh, fish, nushell, and PowerShell were unavailable. All-feature Clippy is not a substitute for those checks. Supported exact-head CI and the coverage gate are still needed before merge. The initial patch-coverage gap in fail-closed path resolution now has a real symlink-loop regression; no coverage exclusions or threshold changes were made.

New automated integration cases cover the full pipeline, hooks/config selection, caller staging and untracked data, source/target failures, inherited Git context, paths/current aliases, nested worktrees, late hooks, and bare-repository navigation. Independent review reproduced and then retested the data-safety failures.

Disclosure

This patch, tests, and PR draft were produced with OpenAI Codex assistance and independently reviewed by a second AI reviewer. A human maintainer has not reviewed or approved this proposal. The complete upstream pre-merge gate has not been established as passing in this environment; do not represent the patch as ready to merge on that basis.

Allow wt merge --branch to select an existing source worktree while
preserving caller identity, approved hooks, and nested worktree data.
Include integration regressions and generated documentation updates.

Related to max-sixty#1637.
Developed with OpenAI Codex assistance.

@worktrunk-bot worktrunk-bot left a comment

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.

This is draft feedback on work in progress, not a merge verdict. Mark the PR ready when you want the full review.

The nested-worktree guard undoes the concurrent removal staging from #3954. nested_worktree builds a fresh Repository and calls list_worktrees(), which takes worktree_registry_read() around git worktree list. Since stage_worktree_removal now runs that check first, every removal's staging (dirty check, fsmonitor stop, rename into trash) waits behind any other removal's registry teardown, which holds the write side. That is the serialization #3954 removed from wt step prune (a 41% improvement on prune_e2e/live). The PR also replaces the test that pinned that behavior (stages_worktree_before_waiting_for_registry_lock) with staging_waits_for_registry_snapshot, which asserts the opposite. Every removal on every path also gets an extra git worktree list plus a canonicalize of each registered worktree path, which makes a prune of N worktrees O(N²) in filesystem calls. Changing a property a maintainer deliberately restored needs their decision. If the guard stays in the shared removal core, an option is to keep it off the registry lock, for example by checking the cached snapshot at planning time and doing a lock-free live recheck. Another is to scope the guard to the case that motivates it, wt merge --branch removing a worktree other than the invoking one, and land the general wt remove/prune guard as its own PR.

This needs a maintainer to sign off on data safety. --branch lets wt merge remove a worktree the user isn't standing in, chosen by name, path, or @. Its cleanup also deletes that worktree's branch. That widens what a merge can delete, so per this repo's review policy, @max-sixty needs to review it whatever the automated verdict.

Comment thread src/git/remove.rs Outdated
Scope fresh nested-worktree checks to merge cleanup after approved hooks,
restore upstream prune staging concurrency, and retain late-write safety.
Cover failed path canonicalization and normalize Windows test expectations.

Developed with OpenAI Codex assistance.
@liuyinchu
liuyinchu marked this pull request as ready for review October 3, 2026 19:13

@worktrunk-bot worktrunk-bot left a comment

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.

The registry-lock concern from the earlier review is resolved. The live nested_worktree read now runs only in merge cleanup (handle_merge_remove_output), and stage_worktree_removal matches main again, including stages_worktree_before_waiting_for_registry_lock. I found nothing new in this push.

I'm leaving this as a comment, not an approval, for two reasons. First, the PR body asks that the patch not be treated as ready to merge until the full pre-merge gate is established. Second, the data-safety sign-off from @max-sixty, which the earlier review asked for, still stands.

@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot how many of the issues above are still present? publish an fresh list

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

One of the two issues from my earlier reviews is still open, and it is a decision for you rather than a code defect. I checked against the current head, 9b89b9cd, which is the head my last review covered.

Still open

  • Data-safety sign-off. --branch lets wt merge remove a worktree the user isn't standing in, chosen by name, path, or @, and delete that worktree's branch. That widens what a merge can delete, so it still needs your review.

Resolved

  • Registry-lock serialization from Restore concurrent worktree staging in step prune #3954. stage_worktree_removal is identical to the PR's merge base, and stages_worktree_before_waiting_for_registry_lock is back. The live nested_worktree registry read runs only when protect_nested_worktrees is set. Only handle_merge_remove_output sets it, so wt remove, the picker, and wt step prune don't pay for the extra git worktree list.
  • Unverified gate. The PR body warns that the full pre-merge gate and the Windows fixes were unverified locally. At this head, CI is green: test (linux|macos|windows), lint, check-docs, and codecov/patch all pass.

The branch is behind main by f844d09 (#4361), which also touched src/git/remove.rs. GitHub still reports it as mergeable.

This branch has not been deployed

No deployments
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.

3 participants