108 lines
3.5 KiB
Rust
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(())
|
|
}
|