diff --git a/e2e_tests/test_worktrees.py b/e2e_tests/test_worktrees.py index 7539641..8d9f803 100644 --- a/e2e_tests/test_worktrees.py +++ b/e2e_tests/test_worktrees.py @@ -79,6 +79,36 @@ def test_worktree_add_simple(): assert repo.active_branch.tracking_branch() is None +def test_worktree_add_into_subdirectory(): + with TempGitRepositoryWorktree() as base_dir: + cmd = grm(["wt", "add", "dir/test"], cwd=base_dir) + assert cmd.returncode == 0 + + files = os.listdir(base_dir) + assert len(files) == 2 + assert set(files) == {".git-main-working-tree", "dir"} + + files = os.listdir(os.path.join(base_dir, "dir")) + assert set(files) == {"test"} + + repo = git.Repo(os.path.join(base_dir, "dir", "test")) + assert not repo.bare + assert not repo.is_dirty() + assert repo.active_branch.tracking_branch() is None + + +def test_worktree_add_into_invalid_subdirectory(): + with TempGitRepositoryWorktree() as base_dir: + cmd = grm(["wt", "add", "/dir/test"], cwd=base_dir) + assert cmd.returncode == 1 + assert "dir" not in os.listdir(base_dir) + assert "dir" not in os.listdir("/") + + cmd = grm(["wt", "add", "dir/"], cwd=base_dir) + assert cmd.returncode == 1 + assert "dir" not in os.listdir(base_dir) + + def test_worktree_add_with_tracking(): for remote_branch_already_exists in (True, False): for has_config in (True, False): diff --git a/src/grm/main.rs b/src/grm/main.rs index b2cd8c5..29041c0 100644 --- a/src/grm/main.rs +++ b/src/grm/main.rs @@ -157,7 +157,22 @@ fn main() { None => None, }; - match grm::add_worktree(&cwd, &action_args.name, track, action_args.no_track) { + let mut name: &str = &action_args.name; + let subdirectory; + let split = name.split_once('/'); + match split { + None => subdirectory = None, + Some(split) => { + if split.0.is_empty() || split.1.is_empty() { + print_error("Worktree name cannot start or end with a slash"); + process::exit(1); + } else { + (subdirectory, name) = (Some(Path::new(split.0)), split.1); + } + } + } + + match grm::add_worktree(&cwd, name, subdirectory, track, action_args.no_track) { Ok(_) => print_success(&format!("Worktree {} created", &action_args.name)), Err(error) => { print_error(&format!("Error creating worktree: {}", error)); diff --git a/src/lib.rs b/src/lib.rs index 57fece9..3bc321b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,6 +461,7 @@ pub fn find_in_tree(path: &Path) -> Result<(Tree, Vec), String> { pub fn add_worktree( directory: &Path, name: &str, + subdirectory: Option<&Path>, track: Option<(&str, &str)>, no_track: bool, ) -> Result<(), String> { @@ -473,7 +474,12 @@ pub fn add_worktree( let config = repo::read_worktree_root_config(directory)?; - if repo.find_worktree(name).is_ok() { + let path = match subdirectory { + Some(dir) => dir.join(name), + None => Path::new(name).to_path_buf(), + }; + + if repo.find_worktree(&path).is_ok() { return Err(format!("Worktree {} already exists", &name)); } @@ -605,7 +611,10 @@ pub fn add_worktree( } } - repo.new_worktree(name, &directory.join(&name), &target_branch)?; + if let Some(subdirectory) = subdirectory { + std::fs::create_dir_all(subdirectory).map_err(|error| error.to_string())?; + } + repo.new_worktree(name, &path, &target_branch)?; Ok(()) } diff --git a/src/repo.rs b/src/repo.rs index a96f471..64c2535 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -362,8 +362,10 @@ impl Repo { self.0.config().map_err(convert_libgit2_error) } - pub fn find_worktree(&self, name: &str) -> Result<(), String> { - self.0.find_worktree(name).map_err(convert_libgit2_error)?; + pub fn find_worktree(&self, path: &Path) -> Result<(), String> { + self.0 + .find_worktree(path.to_str().expect("Worktree path is not valid utf-8")) + .map_err(convert_libgit2_error)?; Ok(()) }