diff options
| author | Mica White <botahamec@outlook.com> | 2026-03-30 21:28:48 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-03-30 21:28:48 -0400 |
| commit | 96f63ec38fcdc8194a52c2b34b32f6e88ae64c34 (patch) | |
| tree | db669dec8823dd18099af3a28020e235ef238e4a /src/utils.rs | |
| parent | b464fb60ac8295ef3678f59898c89e0a912457d2 (diff) | |
Refactor magic strings
Diffstat (limited to 'src/utils.rs')
| -rw-r--r-- | src/utils.rs | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..dc0c082 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,105 @@ +use std::path::Path; + +use git2::{Commit, MergeOptions, Oid, Repository, Tree}; +use is_executable::is_executable; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum TreeError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Git(#[from] git2::Error), +} + +pub fn current_branch_ref(repository: &Repository) -> Result<String, git2::Error> { + let head = repository.head()?; + let ref_name = match head.kind() { + Some(git2::ReferenceType::Symbolic) => head.symbolic_target(), + _ => head.name(), + } + .ok_or(git2::Error::new( + git2::ErrorCode::Invalid, + git2::ErrorClass::Reference, + "The current branch's name is not valid UTF-8", + ))? + .to_string(); + Ok(ref_name) +} + +pub fn current_branch(repository: &Repository) -> Result<String, git2::Error> { + let ref_name = current_branch_ref(repository)?; + let branch_name = ref_name + .strip_prefix("refs/heads/") + .unwrap_or(&ref_name) + .to_string(); + Ok(branch_name) +} + +pub fn filemode_for_dir_entry(path: &Path) -> std::io::Result<i32> { + let metadata = path.metadata()?; + Ok(if metadata.is_dir() { + 0o040000 + } else if metadata.is_symlink() { + 0o120000 + } else if is_executable(path) { + 0o100755 + } else { + 0o100644 + }) +} + +pub fn path_to_tree(repository: &Repository, path: &Path) -> Result<Oid, TreeError> { + let workdir = repository.workdir().expect("a non-bare repo"); + if path.is_dir() { + let mut tree = repository.treebuilder(None)?; + for entry in path.read_dir()? { + let entry = entry?; + let full_path = entry.path(); + let relative_path = full_path.strip_prefix(workdir).unwrap_or(path); + if repository.is_path_ignored(relative_path)? { + continue; + } + + let filemode = filemode_for_dir_entry(&entry.path())?; + let oid = path_to_tree(repository, &entry.path())?; + let filename = entry.file_name(); + tree.insert(filename, oid, filemode)?; + } + Ok(tree.write()?) + } else { + Ok(repository.blob_path(path)?) + } +} + +pub fn workdir_to_tree(repository: &Repository) -> Result<Oid, TreeError> { + let Some(workdir) = repository.workdir() else { + return Err(TreeError::Git(git2::Error::new( + git2::ErrorCode::BareRepo, + git2::ErrorClass::Repository, + "git-autosave does not support bare repositories", + ))); + }; + + path_to_tree(repository, workdir) +} + +pub fn merge_commit_with_tree( + repository: &Repository, + commit: &Commit<'_>, + workdir: &Tree<'_>, +) -> Result<Oid, TreeError> { + Ok(repository + .merge_trees( + &repository + .find_commit( + repository + .merge_base(repository.head()?.peel_to_commit()?.id(), commit.id())?, + )? + .tree()?, + workdir, + &commit.tree()?, + Some(MergeOptions::new().find_renames(true).patience(true)), + )? + .write_tree_to(repository)?) +} |
