gitdataai/lib/git/sync/lfs.rs

108 lines
3.5 KiB
Rust

use std::collections::HashSet;
use db::{database::AppDatabase, sqlx};
use model::repos::RepoLfsObjectModel;
use uuid::Uuid;
use crate::{bare::GitBare, errors::GitError};
#[tracing::instrument(skip(db, bare), fields(repo_id = %repo_id))]
pub async fn sync_lfs_objects(
db: &AppDatabase,
bare: &GitBare,
repo_id: Uuid,
) -> Result<(), GitError> {
let pool = db.writer();
let existing: Vec<RepoLfsObjectModel> = sqlx::query_as::<_, RepoLfsObjectModel>(
"SELECT repo, oid, size_bytes, storage_key, created_at FROM repo_lfs_object WHERE repo = $1"
)
.bind(repo_id)
.fetch_all(pool)
.await
.map_err(|e| GitError::Internal(format!("failed to query lfs objects: {}", e)))?;
let mut existing_oids: HashSet<String> =
existing.into_iter().map(|o| o.oid).collect();
let lfs_dir = bare.bare_dir.join(".lfs").join("objects");
if !lfs_dir.exists() {
if !existing_oids.is_empty() {
let oids_vec: Vec<String> = existing_oids.into_iter().collect();
sqlx::query(
"DELETE FROM repo_lfs_object WHERE repo = $1 AND oid = ANY($2)",
)
.bind(repo_id)
.bind(&oids_vec)
.execute(pool)
.await
.map_err(|e| {
GitError::Internal(format!(
"failed to delete stale lfs objects: {}",
e
))
})?;
}
return Ok(());
}
let now = chrono::Utc::now();
let mut new_objects: Vec<(String, i64, String)> = Vec::new();
if let Ok(prefix_entries) = std::fs::read_dir(&lfs_dir) {
for prefix_entry in prefix_entries.flatten() {
if let Ok(oid_entries) = std::fs::read_dir(prefix_entry.path()) {
for oid_entry in oid_entries.flatten() {
let oid_str =
oid_entry.file_name().to_string_lossy().to_string();
if existing_oids.contains(&oid_str) {
existing_oids.remove(&oid_str);
continue;
}
let path = oid_entry.path();
let size_bytes = match std::fs::metadata(&path) {
Ok(meta) => meta.len() as i64,
Err(_) => continue,
};
let storage_key = path.to_string_lossy().to_string();
new_objects.push((oid_str, size_bytes, storage_key));
}
}
}
}
for (oid, size_bytes, storage_key) in &new_objects {
sqlx::query(
"INSERT INTO repo_lfs_object (repo, oid, size_bytes, storage_key, created_at) VALUES ($1, $2, $3, $4, $5)"
)
.bind(repo_id)
.bind(oid)
.bind(*size_bytes)
.bind(storage_key)
.bind(now)
.execute(pool)
.await
.map_err(|e| GitError::Internal(format!("failed to insert lfs object: {}", e)))?;
}
if !existing_oids.is_empty() {
let oids_vec: Vec<String> = existing_oids.into_iter().collect();
sqlx::query(
"DELETE FROM repo_lfs_object WHERE repo = $1 AND oid = ANY($2)",
)
.bind(repo_id)
.bind(&oids_vec)
.execute(pool)
.await
.map_err(|e| {
GitError::Internal(format!(
"failed to delete stale lfs objects: {}",
e
))
})?;
}
Ok(())
}