From 010300faa9421a5e1148387f5dd697c41cf6811d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ozan=20Ka=C5=9F=C4=B1k=C3=A7=C4=B1?= Date: Sun, 28 Sep 2025 22:25:51 +0300 Subject: [PATCH 1/2] update interactive merge command --- src/commands/merge_pr_github/mod.rs | 83 ++++++++++++++++++----------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/src/commands/merge_pr_github/mod.rs b/src/commands/merge_pr_github/mod.rs index 575fb51..e9347ef 100644 --- a/src/commands/merge_pr_github/mod.rs +++ b/src/commands/merge_pr_github/mod.rs @@ -150,6 +150,12 @@ where worktree_path: &Path, pr_number: u64, ) -> color_eyre::Result<()> { + let mut detached_for_deletion = false; + if self.remove_local_branch { + self.detach_worktree_head(worktree_path)?; + detached_for_deletion = true; + } + let mut args = vec![ "pr".to_owned(), "merge".to_owned(), @@ -168,6 +174,9 @@ where let branch_delete_failed = self.remove_local_branch && gh_branch_delete_failure(&output); if !output.success && !branch_delete_failed { + if detached_for_deletion { + let _ = self.restore_worktree_branch(worktree_path, branch); + } return Err(command_failure("gh", &args, &output)); } @@ -187,7 +196,9 @@ where ); } - self.restore_worktree_branch(worktree_path, branch)?; + if !self.remove_local_branch || branch_delete_failed { + self.restore_worktree_branch(worktree_path, branch)?; + } if self.remove_remote_branch { self.delete_remote_branch(repo_path, branch)?; @@ -196,6 +207,24 @@ where Ok(()) } + fn detach_worktree_head(&mut self, worktree_path: &Path) -> color_eyre::Result<()> { + let args = vec![ + "switch".to_owned(), + "--detach".to_owned(), + "HEAD".to_owned(), + ]; + let output = self + .runner + .run("git", worktree_path, &args) + .wrap_err("failed to detach worktree with `git switch --detach`")?; + + if !output.success { + return Err(command_failure("git", &args, &output)); + } + + Ok(()) + } + fn restore_worktree_branch( &mut self, worktree_path: &Path, @@ -449,6 +478,11 @@ mod tests { "1".into(), ], }, + RecordedCall { + program: "git".into(), + dir: worktree_path.clone(), + args: vec!["switch".into(), "--detach".into(), "HEAD".into()], + }, RecordedCall { program: "gh".into(), dir: repo_root, @@ -460,11 +494,6 @@ mod tests { "--delete-branch".into(), ], }, - RecordedCall { - program: "git".into(), - dir: worktree_path.clone(), - args: vec!["switch".into(), "feature/test".into()], - }, ] ); @@ -615,6 +644,11 @@ mod tests { "1".into(), ], }, + RecordedCall { + program: "git".into(), + dir: worktree_path.clone(), + args: vec!["switch".into(), "--detach".into(), "HEAD".into()], + }, RecordedCall { program: "gh".into(), dir: repo_root.clone(), @@ -626,11 +660,6 @@ mod tests { "--delete-branch".into(), ], }, - RecordedCall { - program: "git".into(), - dir: worktree_path.clone(), - args: vec!["switch".into(), "feature/remove".into()], - }, RecordedCall { program: "git".into(), dir: repo_root, @@ -858,6 +887,12 @@ mod tests { success: true, status_code: Some(0), }), + Ok(CommandOutput { + stdout: String::new(), + stderr: String::new(), + success: true, + status_code: Some(0), + }), Ok(CommandOutput { stdout: "Pull request successfully merged".into(), stderr: "failed to delete local branch merge-cmd".into(), @@ -899,6 +934,11 @@ mod tests { "1".into(), ], }, + RecordedCall { + program: "git".into(), + dir: worktree_path.clone(), + args: vec!["switch".into(), "--detach".into(), "HEAD".into()], + }, RecordedCall { program: "gh".into(), dir: repo_root.clone(), @@ -1024,12 +1064,6 @@ mod tests { success: true, status_code: Some(0), }), - Ok(CommandOutput { - stdout: String::new(), - stderr: String::new(), - success: true, - status_code: Some(0), - }), Ok(CommandOutput { stdout: String::new(), stderr: String::from("fatal: not a git repository"), @@ -1040,7 +1074,7 @@ mod tests { let mut command = MergePrGithubCommand::with_runner("feature/test".into(), runner); let err = command.execute(&repo).unwrap_err(); - assert!(err.to_string().contains("git switch")); + assert!(err.to_string().contains("git switch --detach")); assert_eq!( command.runner.calls, @@ -1066,21 +1100,10 @@ mod tests { "1".into(), ], }, - RecordedCall { - program: "gh".into(), - dir: repo_root, - args: vec![ - "pr".into(), - "merge".into(), - "42".into(), - "--merge".into(), - "--delete-branch".into(), - ], - }, RecordedCall { program: "git".into(), dir: worktree_path, - args: vec!["switch".into(), "feature/test".into()], + args: vec!["switch".into(), "--detach".into(), "HEAD".into()], }, ] ); From 04d00908260f37163944bfc23d841f659a2f8d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ozan=20Ka=C5=9F=C4=B1k=C3=A7=C4=B1?= Date: Sun, 28 Sep 2025 22:26:29 +0300 Subject: [PATCH 2/2] update: README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3611e26..e2a988d 100644 --- a/README.md +++ b/README.md @@ -98,3 +98,4 @@ After the binary is on your `PATH`, run `rsworktree --help` to explore the avail ## Environment Set `RSWORKTREE_SHELL` to override the shell used by `rsworktree cd` (falls back to `$SHELL` or `/bin/sh`). +