144 lines
3.9 KiB
Rust
144 lines
3.9 KiB
Rust
use crate::{
|
|
bare::GitBare,
|
|
cmd::{
|
|
oid::ObjectId,
|
|
tree::{TreeEntry, TreeKind},
|
|
},
|
|
errors::{GitError, GitResult},
|
|
};
|
|
|
|
const LFS_POINTER_PREFIX: &[u8] = b"version https://git-lfs.github.com/spec/v";
|
|
const LFS_OID_MARKER: &[u8] = b"\noid sha256:";
|
|
const LFS_POINTER_MAX_SIZE: usize = 512;
|
|
|
|
impl GitBare {
|
|
fn blob_is_lfs_pointer(&self, id: ObjectId) -> GitResult<bool> {
|
|
let repo = self.gix_repo()?;
|
|
let gix_id: gix::hash::ObjectId = (&id).try_into()?;
|
|
let blob = repo
|
|
.find_blob(gix_id)
|
|
.map_err(|_| GitError::ObjectNotFound(id.as_str().to_string()))?;
|
|
|
|
if blob.data.len() > LFS_POINTER_MAX_SIZE {
|
|
return Ok(false);
|
|
}
|
|
|
|
let data = &blob.data;
|
|
Ok(data.starts_with(LFS_POINTER_PREFIX)
|
|
&& contains_subslice(data, LFS_OID_MARKER))
|
|
}
|
|
|
|
pub fn tree_entries(&self, tree: ObjectId) -> GitResult<Vec<TreeEntry>> {
|
|
let repo = self.gix_repo()?;
|
|
let gix_id: gix::hash::ObjectId = (&tree).try_into()?;
|
|
let gix_tree = repo.find_tree(gix_id)?;
|
|
|
|
let mut entries = Vec::new();
|
|
for entry_result in gix_tree.iter() {
|
|
let entry = entry_result?;
|
|
let name = entry.inner.filename.to_string();
|
|
let oid = ObjectId::new(entry.inner.oid.to_hex().to_string());
|
|
|
|
let is_tree = entry.inner.mode.is_tree();
|
|
let is_lfs = if !is_tree {
|
|
self.blob_is_lfs_pointer(oid.clone()).unwrap_or(false)
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let kind = if is_tree {
|
|
TreeKind::Tree
|
|
} else if is_lfs {
|
|
TreeKind::LfsPointer
|
|
} else {
|
|
TreeKind::Blob
|
|
};
|
|
|
|
let filemode = entry.inner.mode.value() as u32;
|
|
|
|
let is_binary = match kind {
|
|
TreeKind::LfsPointer => false,
|
|
TreeKind::Blob => {
|
|
self.blob_is_binary(oid.clone()).unwrap_or(false)
|
|
}
|
|
TreeKind::Tree => false,
|
|
};
|
|
|
|
entries.push(TreeEntry {
|
|
name,
|
|
oid,
|
|
kind,
|
|
filemode,
|
|
is_binary,
|
|
is_lfs,
|
|
});
|
|
}
|
|
|
|
Ok(entries)
|
|
}
|
|
|
|
pub fn tree_entry_by_path(
|
|
&self,
|
|
tree: ObjectId,
|
|
path: String,
|
|
) -> GitResult<TreeEntry> {
|
|
let repo = self.gix_repo()?;
|
|
let gix_id: gix::hash::ObjectId = (&tree).try_into()?;
|
|
let gix_tree = repo.find_tree(gix_id)?;
|
|
|
|
let entry = gix_tree.find_entry(path.as_str()).ok_or_else(|| {
|
|
GitError::ObjectNotFound(format!(
|
|
"path '{}' not found in tree {}",
|
|
path, tree
|
|
))
|
|
})?;
|
|
|
|
let name = entry.inner.filename.to_string();
|
|
let oid = ObjectId::new(entry.inner.oid.to_hex().to_string());
|
|
|
|
let is_tree = entry.inner.mode.is_tree();
|
|
let is_lfs = if !is_tree {
|
|
self.blob_is_lfs_pointer(oid.clone()).unwrap_or(false)
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let kind = if is_tree {
|
|
TreeKind::Tree
|
|
} else if is_lfs {
|
|
TreeKind::LfsPointer
|
|
} else {
|
|
TreeKind::Blob
|
|
};
|
|
|
|
let filemode = entry.inner.mode.value() as u32;
|
|
|
|
let is_binary = match kind {
|
|
TreeKind::LfsPointer => false,
|
|
TreeKind::Blob => self.blob_is_binary(oid.clone()).unwrap_or(false),
|
|
TreeKind::Tree => false,
|
|
};
|
|
|
|
Ok(TreeEntry {
|
|
name,
|
|
oid,
|
|
kind,
|
|
filemode,
|
|
is_binary,
|
|
is_lfs,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn contains_subslice(data: &[u8], pattern: &[u8]) -> bool {
|
|
if pattern.len() > data.len() {
|
|
return false;
|
|
}
|
|
for window in data.windows(pattern.len()) {
|
|
if window == pattern {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|