summaryrefslogtreecommitdiff
path: root/src/utils.rs
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-03-30 21:28:48 -0400
committerMica White <botahamec@outlook.com>2026-03-30 21:28:48 -0400
commit96f63ec38fcdc8194a52c2b34b32f6e88ae64c34 (patch)
treedb669dec8823dd18099af3a28020e235ef238e4a /src/utils.rs
parentb464fb60ac8295ef3678f59898c89e0a912457d2 (diff)
Refactor magic strings
Diffstat (limited to 'src/utils.rs')
-rw-r--r--src/utils.rs105
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)?)
+}