From 5495517e6e9cc34ab6b528ceea768a00a258d1c3 Mon Sep 17 00:00:00 2001 From: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com> Date: Sat, 12 Sep 2026 12:10:02 +0000 Subject: [PATCH] docs(hook): say post-merge runs in the destination worktree regardless of removal Closes #4070 --- docs/src/content/docs/hook.md | 2 +- .../skills/worktrunk/reference/hook.md | 2 +- skills/worktrunk/reference/hook.md | 2 +- src/cli/mod.rs | 2 +- tests/integration_tests/merge.rs | 39 +++++++++++++++++++ 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/src/content/docs/hook.md b/docs/src/content/docs/hook.md index 0187107306..31389e78de 100644 --- a/docs/src/content/docs/hook.md +++ b/docs/src/content/docs/hook.md @@ -165,7 +165,7 @@ All hooks share the same perspective — `{{ branch | hash_port }}` produces the - `pre-switch`: hook runs in the source worktree; `worktree_path` is the destination when that worktree already exists — a switch that creates one has no destination directory yet, so `worktree_path` stays on the source (use `pre-start` to work in the new worktree) - `post-remove`: the active worktree is gone, so the hook runs in the primary worktree -- `post-merge` with removal: the active worktree is gone, so the hook runs in the target worktree +- `post-merge`: the hook runs in the target branch's worktree (the primary worktree if the target has none), including under `--no-remove`, where the merged worktree `worktree_path` names is still on disk Undefined variables error — use conditionals or defaults for optional behavior: diff --git a/plugins/worktrunk/skills/worktrunk/reference/hook.md b/plugins/worktrunk/skills/worktrunk/reference/hook.md index 0305761667..2c7314ad0c 100644 --- a/plugins/worktrunk/skills/worktrunk/reference/hook.md +++ b/plugins/worktrunk/skills/worktrunk/reference/hook.md @@ -159,7 +159,7 @@ All hooks share the same perspective — `{{ branch | hash_port }}` produces the - `pre-switch`: hook runs in the source worktree; `worktree_path` is the destination when that worktree already exists — a switch that creates one has no destination directory yet, so `worktree_path` stays on the source (use `pre-start` to work in the new worktree) - `post-remove`: the active worktree is gone, so the hook runs in the primary worktree -- `post-merge` with removal: the active worktree is gone, so the hook runs in the target worktree +- `post-merge`: the hook runs in the target branch's worktree (the primary worktree if the target has none), including under `--no-remove`, where the merged worktree `worktree_path` names is still on disk Undefined variables error — use conditionals or defaults for optional behavior: diff --git a/skills/worktrunk/reference/hook.md b/skills/worktrunk/reference/hook.md index 0305761667..2c7314ad0c 100644 --- a/skills/worktrunk/reference/hook.md +++ b/skills/worktrunk/reference/hook.md @@ -159,7 +159,7 @@ All hooks share the same perspective — `{{ branch | hash_port }}` produces the - `pre-switch`: hook runs in the source worktree; `worktree_path` is the destination when that worktree already exists — a switch that creates one has no destination directory yet, so `worktree_path` stays on the source (use `pre-start` to work in the new worktree) - `post-remove`: the active worktree is gone, so the hook runs in the primary worktree -- `post-merge` with removal: the active worktree is gone, so the hook runs in the target worktree +- `post-merge`: the hook runs in the target branch's worktree (the primary worktree if the target has none), including under `--no-remove`, where the merged worktree `worktree_path` names is still on disk Undefined variables error — use conditionals or defaults for optional behavior: diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 1677cb0a29..6473c3a2c4 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1762,7 +1762,7 @@ All hooks share the same perspective — `{{ branch | hash_port }}` produces the - `pre-switch`: hook runs in the source worktree; `worktree_path` is the destination when that worktree already exists — a switch that creates one has no destination directory yet, so `worktree_path` stays on the source (use `pre-start` to work in the new worktree) - `post-remove`: the active worktree is gone, so the hook runs in the primary worktree -- `post-merge` with removal: the active worktree is gone, so the hook runs in the target worktree +- `post-merge`: the hook runs in the target branch's worktree (the primary worktree if the target has none), including under `--no-remove`, where the merged worktree `worktree_path` names is still on disk Undefined variables error — use conditionals or defaults for optional behavior: diff --git a/tests/integration_tests/merge.rs b/tests/integration_tests/merge.rs index 619f99f738..508d1c7aea 100644 --- a/tests/integration_tests/merge.rs +++ b/tests/integration_tests/merge.rs @@ -866,6 +866,45 @@ fn test_merge_post_merge_runs_with_nothing_to_merge(mut repo: TestRepo) { wait_for_file(&marker_file); } +#[rstest] +fn test_merge_post_merge_runs_in_destination_with_no_remove(mut repo: TestRepo) { + // `--no-remove` keeps the feature worktree, but `post-merge` is anchored on + // the merge destination either way: the hook's cwd is the target worktree, + // not the preserved feature worktree its `worktree_path` names. + let config_dir = repo.root_path().join(".config"); + fs::create_dir_all(&config_dir).unwrap(); + fs::write( + config_dir.join("wt.toml"), + r#"post-merge = "echo ran > post-merge-cwd.txt""#, + ) + .unwrap(); + repo.commit("Add config"); + + let feature_wt = repo.add_worktree("feature"); + fs::write(feature_wt.join("feature.txt"), "feature content").unwrap(); + + let output = repo + .wt_command() + .args(["merge", "main", "--no-remove", "--yes"]) + .current_dir(&feature_wt) + .output() + .unwrap(); + assert!( + output.status.success(), + "merge failed:\n{}", + String::from_utf8_lossy(&output.stderr) + ); + + // The hook wrote its marker relative to its cwd: the destination worktree. + wait_for_file(&repo.root_path().join("post-merge-cwd.txt")); + let feature_marker = feature_wt.join("post-merge-cwd.txt"); + assert!( + !feature_marker.exists(), + "post-merge ran in the preserved feature worktree: {}", + feature_marker.display() + ); +} + #[rstest] fn test_merge_post_merge_runs_from_main_branch(repo: TestRepo) { // Verify post-merge hooks run when merging from main to main (nothing to do)