use std::path::Path;
use crate::error::Result;
use crate::repo::Repository;
use crate::worktree::{
add_linked_worktree, list_worktrees, remove_linked_worktree, AddLinkedWorktreeOptions,
WorktreeEntry,
};
#[derive(Debug, Clone, Default)]
pub struct LockWorktreeOptions {
pub reason: Option<String>,
}
#[derive(Debug, Copy, Clone)]
pub struct RepositoryWorktrees<'repo> {
repo: &'repo Repository,
}
impl<'repo> RepositoryWorktrees<'repo> {
pub(crate) fn new(repo: &'repo Repository) -> Self {
Self { repo }
}
#[must_use]
pub fn repository(&self) -> &'repo Repository {
self.repo
}
pub fn list(&self) -> Result<Vec<WorktreeEntry>> {
list_worktrees(self.repo)
}
pub fn add(&self, options: &AddLinkedWorktreeOptions) -> Result<WorktreeEntry> {
add_linked_worktree(self.repo, options)
}
pub fn remove(&self, path: &Path, force: bool) -> Result<()> {
remove_linked_worktree(self.repo, path, force)
}
pub fn lock(&self, path: &Path, options: &LockWorktreeOptions) -> Result<()> {
lock_linked_worktree(self.repo, path, options.reason.as_deref())
}
pub fn unlock(&self, path: &Path) -> Result<()> {
unlock_linked_worktree(self.repo, path)
}
}
pub fn lock_linked_worktree(repo: &Repository, path: &Path, reason: Option<&str>) -> Result<()> {
use crate::git_path::path_for_disk_compare;
use crate::worktree::common_git_dir;
use std::fs;
let common = common_git_dir(repo.git_dir());
let target = path_for_disk_compare(path);
let entry = list_worktrees(repo)?
.into_iter()
.find(|e| e.path == target)
.ok_or_else(|| {
crate::error::Error::Message(format!("'{target:?}' is not a registered worktree"))
})?;
if entry.admin_dir == common {
return Err(crate::error::Error::Message(
"cannot lock the main working tree".into(),
));
}
if entry.is_locked {
return Err(crate::error::Error::Message(
entry
.lock_reason
.map(|r| format!("worktree is already locked: {r}"))
.unwrap_or_else(|| "worktree is already locked".into()),
));
}
let body = reason.unwrap_or("");
fs::write(entry.admin_dir.join("locked"), body).map_err(crate::error::Error::Io)?;
Ok(())
}
pub fn unlock_linked_worktree(repo: &Repository, path: &Path) -> Result<()> {
use crate::git_path::path_for_disk_compare;
let target = path_for_disk_compare(path);
let entry = list_worktrees(repo)?
.into_iter()
.find(|e| e.path == target)
.ok_or_else(|| {
crate::error::Error::Message(format!("'{target:?}' is not a registered worktree"))
})?;
if entry.admin_dir == crate::worktree::common_git_dir(repo.git_dir()) {
return Err(crate::error::Error::Message(
"cannot unlock the main working tree".into(),
));
}
if !entry.is_locked {
return Err(crate::error::Error::Message(
"worktree is not locked".into(),
));
}
std::fs::remove_file(entry.admin_dir.join("locked")).map_err(crate::error::Error::Io)?;
Ok(())
}