From fcd0b7abca6cc2841467dc62e525e2ed2838f16a Mon Sep 17 00:00:00 2001 From: Kuya KOHARA Date: Tue, 29 Sep 2026 22:38:29 +0900 Subject: [PATCH] fix: handle linked worktree correctly --- src/pathname.rs | 4 +++- tests/dah.rs | 33 +++++++++++++++++++++++++++++++++ tests/stale.rs | 33 ++++++++++++++++++++++++++++++++- tests/whose.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/pathname.rs b/src/pathname.rs index c149e62..ea1afee 100644 --- a/src/pathname.rs +++ b/src/pathname.rs @@ -44,7 +44,9 @@ pub fn normalize_paths( repo: &Repository, paths: Vec, ) -> Result, NormalizePathError> { - let repo_root = repo.path().parent().unwrap(); + let repo_root = repo.workdir().ok_or(NormalizePathError::RuntimeError( + "repository has no working directory", + ))?; let mut workdir_paths = Vec::new(); for path in paths { let path = Path::new(&path); diff --git a/tests/dah.rs b/tests/dah.rs index b18aa8c..d7b7146 100644 --- a/tests/dah.rs +++ b/tests/dah.rs @@ -197,6 +197,39 @@ fn git_dah_step_stages_only_one_action() { assert!(!status.contains(Status::WT_MODIFIED)); } +#[test] +fn git_dah_runs_from_linked_worktree() { + let fixture = DahFixture::new(); + let linked_root = fixture.worktree_root.parent().unwrap().join("linked"); + git_command( + &fixture.worktree_repo(), + &[ + "worktree", + "add", + "-b", + "linked", + linked_root.to_str().unwrap(), + ], + ); + fs::write(linked_root.join("tracked.txt"), "linked\n").unwrap(); + + let output = Command::new(git_dah_exe()) + .current_dir(&linked_root) + .env("GIT_EDITOR", &fixture.editor_path) + .env("GIT_DAH_TEST_MESSAGE", "Linked worktree") + .args(["--step", "--no-fetch"]) + .output() + .expect("spawn git-dah"); + + assert_success(&output); + + let repo = Repository::open(linked_root).unwrap(); + assert_eq!("linked", current_branch(&repo)); + let status = repo.status_file(Path::new("tracked.txt")).unwrap(); + assert!(status.contains(Status::INDEX_MODIFIED)); + assert!(!status.contains(Status::WT_MODIFIED)); +} + #[test] fn git_dah_renames_default_branch_commits_and_pushes() { let fixture = DahFixture::new(); diff --git a/tests/stale.rs b/tests/stale.rs index bca0818..de221f2 100644 --- a/tests/stale.rs +++ b/tests/stale.rs @@ -15,7 +15,7 @@ use url::Url; use bin::git_stale_exe; use git_worktree::{ - git_add_remote, git_checkout_branch, git_commit_at, git_init_with_initial_commit, + git_add_remote, git_checkout_branch, git_command, git_commit_at, git_init_with_initial_commit, git_set_config, local_branch_exists, ref_exists, }; @@ -221,6 +221,37 @@ fn git_stale_without_since_lists_only_local_branches_without_upstream() { ); } +#[test] +fn git_stale_lists_branches_from_linked_worktree() { + let fixture = StaleFixture::new(); + let linked_root = fixture.worktree_root.parent().unwrap().join("linked"); + git_command( + &fixture.worktree_repo(), + &[ + "worktree", + "add", + "-b", + "linked", + linked_root.to_str().unwrap(), + ], + ); + + let output = Command::new(git_stale_exe()) + .current_dir(linked_root) + .output() + .expect("spawn git-stale"); + + assert!( + output.status.success(), + "git-stale failed: stderr={}", + stderr_text(&output) + ); + assert_eq!( + vec!["refs/heads/topic/local-only".to_owned()], + sorted_stdout_lines(&output) + ); +} + #[test] fn git_stale_since_lists_only_stale_unprotected_local_branches() { let fixture = StaleFixture::new(); diff --git a/tests/whose.rs b/tests/whose.rs index d253ce0..fb428ae 100644 --- a/tests/whose.rs +++ b/tests/whose.rs @@ -49,6 +49,49 @@ fn git_whose_prints_owners_for_indexed_paths() { ); } +#[test] +fn git_whose_prints_owners_from_linked_worktree() { + let tmpdir = TempDir::new().unwrap(); + let root = tmpdir.path().join("main"); + let linked = tmpdir.path().join("linked"); + + let repo = git_init(&root); + git_set_config(&repo, "user.name", "t"); + git_set_config(&repo, "user.email", "t@example.com"); + + let co_path = root.join(".github/CODEOWNERS"); + mkdir_p(co_path.parent().unwrap()); + write(co_path, b"*.rs @rust-team\n"); + + let src = root.join("src/lib.rs"); + mkdir_p(src.parent().unwrap()); + write(&src, b"fn f() {}\n"); + + git_add(&repo, ".github/CODEOWNERS"); + git_add(&repo, "src/lib.rs"); + git_command(&repo, &["commit", "-m", "init"]); + git_command( + &repo, + &["worktree", "add", "-b", "linked", linked.to_str().unwrap()], + ); + + let exe = git_whose_exe(); + let out = Command::new(exe) + .current_dir(linked) + .args(["src/lib.rs"]) + .output() + .expect("spawn git-whose"); + + assert!( + out.status.success(), + "git-whose failed: stderr={}", + String::from_utf8_lossy(&out.stderr) + ); + + let stdout = String::from_utf8_lossy(&out.stdout); + assert_eq!(stdout, "src/lib.rs: @rust-team\n"); +} + #[test] fn git_whose_prints_owners_for_bare_repo_head_tree() { let tmpdir = TempDir::new().unwrap();