use crate::git_metadata::path_from_git_bytes;
use crate::transaction_git::{GitContext, GitRequest, run_git};
use crate::worktree_add_binding::bind_registration;
use crate::worktree_copy_error::{WorktreeCopyError, raw_failure};
use crate::worktree_copy_selection::CopySelection;
use crate::worktree_identity::WorktreeIdentity;
use std::path::{Path, PathBuf};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WorktreeCopyObservation {
pub common_dir: PathBuf,
pub effective_cwd: PathBuf,
pub source_root: Option<PathBuf>,
pub selection: CopySelection,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CreatedWorktree {
pub root: PathBuf,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CreatedWorktrees {
pub created: Vec<CreatedWorktree>,
pub registered_roots: Vec<PathBuf>,
}
pub fn observe_worktree_repository(
identity: &WorktreeIdentity,
effective_cwd: &Path,
) -> Result<Option<WorktreeCopyObservation>, WorktreeCopyError> {
match identity {
WorktreeIdentity::OutsideWorktree | WorktreeIdentity::MainWorktree { .. } => {
return Ok(None);
}
WorktreeIdentity::BareRepository { common_dir, .. } => {
return Ok(Some(observation(common_dir.as_path(), None, effective_cwd, CopySelection::Ignored)));
}
WorktreeIdentity::LinkedWorktree {
common_dir,
worktree_root,
..
} => {
return Ok(Some(observation(
common_dir.as_path(),
Some(worktree_root.as_path()),
effective_cwd,
CopySelection::Ignored,
)));
}
}
}
pub fn observe_copy_source(
identity: &WorktreeIdentity,
effective_cwd: &Path,
) -> Result<Option<WorktreeCopyObservation>, WorktreeCopyError> {
match identity {
WorktreeIdentity::MainWorktree {
common_dir,
worktree_root,
..
} => {
return Ok(Some(observation(
common_dir.as_path(),
Some(worktree_root.as_path()),
effective_cwd,
CopySelection::MainCheckout,
)));
}
_ => return observe_worktree_repository(identity, effective_cwd),
}
}
fn observation(
common_dir: &Path,
source_root: Option<&Path>,
effective_cwd: &Path,
selection: CopySelection,
) -> WorktreeCopyObservation {
return WorktreeCopyObservation {
common_dir: common_dir.to_path_buf(),
effective_cwd: effective_cwd.to_path_buf(),
source_root: source_root.map(Path::to_path_buf),
selection,
};
}
pub fn destination_is_registered(common_dir: &Path, destination: &Path) -> bool {
return bind_registration(common_dir, destination).is_ok();
}
fn canonical_registered_root(root: &Path) -> Result<Option<PathBuf>, WorktreeCopyError> {
match std::fs::canonicalize(root) {
Ok(canonical) => return Ok(Some(canonical)),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(error) => return Err(raw_failure(&error)),
}
}
fn read_registered_roots(
context: &GitContext,
observation: &WorktreeCopyObservation,
) -> Result<Vec<PathBuf>, WorktreeCopyError> {
let request: GitRequest = GitRequest {
cwd: observation.effective_cwd.clone(),
arguments: vec![
"--git-dir".into(),
observation.common_dir.as_os_str().to_os_string(),
"worktree".into(),
"list".into(),
"--porcelain".into(),
"-z".into(),
],
without_prefix: true,
..GitRequest::default()
};
let output = run_git(context, &request).map_err(|error| return raw_failure(&error))?;
if !output.succeeded() {
return Err(raw_failure(&String::from_utf8_lossy(
output.stderr.as_slice(),
)));
}
let mut roots: Vec<PathBuf> = Vec::new();
for path in output
.stdout
.split(|byte| return *byte == 0)
.filter_map(|field| return field.strip_prefix(b"worktree "))
.filter_map(path_from_git_bytes)
{
if let Some(canonical) = canonical_registered_root(path.as_path())? {
roots.push(canonical);
}
}
return Ok(roots);
}
pub fn find_created_worktree(
context: &GitContext,
observation: &WorktreeCopyObservation,
destination: &Path,
registered_before: bool,
) -> Result<CreatedWorktrees, WorktreeCopyError> {
if registered_before || !destination_is_registered(observation.common_dir.as_path(), destination) {
return Ok(CreatedWorktrees {
created: Vec::new(),
registered_roots: Vec::new(),
});
}
return Ok(CreatedWorktrees {
created: vec![CreatedWorktree { root: destination.to_path_buf() }],
registered_roots: read_registered_roots(context, observation)?,
});
}
#[cfg(test)]
#[path = "worktree_copy_registry_tests.rs"]
mod tests;