142 lines
5.6 KiB
Rust
142 lines
5.6 KiB
Rust
use crate::GitError;
|
|
use crate::hook::sync::HookMetaDataSync;
|
|
use db::database::AppTransaction;
|
|
use models::repos::repo;
|
|
use models::repos::repo_branch;
|
|
use sea_orm::prelude::Expr;
|
|
use sea_orm::*;
|
|
use std::collections::HashSet;
|
|
|
|
impl HookMetaDataSync {
|
|
pub async fn sync_refs(&self, txn: &AppTransaction) -> Result<(), GitError> {
|
|
let repo_id = self.repo.id;
|
|
let now = chrono::Utc::now();
|
|
|
|
let existing: Vec<repo_branch::Model> = repo_branch::Entity::find()
|
|
.filter(repo_branch::Column::Repo.eq(repo_id))
|
|
.all(txn)
|
|
.await
|
|
.map_err(|e| GitError::IoError(format!("failed to query branches: {}", e)))?;
|
|
let mut existing_names: HashSet<String> = existing.iter().map(|r| r.name.clone()).collect();
|
|
|
|
let references = self
|
|
.domain
|
|
.repo()
|
|
.references()
|
|
.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
|
|
// Auto-detect first local branch when default_branch is empty
|
|
let mut auto_detected_branch: Option<String> = None;
|
|
|
|
for reference in references {
|
|
let reference = reference.map_err(|e| GitError::Internal(e.to_string()))?;
|
|
let name = reference
|
|
.name()
|
|
.ok_or_else(|| GitError::RefNotFound("unnamed ref".into()))?
|
|
.to_string();
|
|
let shorthand = reference.shorthand().unwrap_or("").to_string();
|
|
|
|
let target_oid = match reference.target() {
|
|
Some(oid) => oid.to_string(),
|
|
None => continue,
|
|
};
|
|
|
|
let is_branch = reference.is_branch();
|
|
let is_remote = reference.is_remote();
|
|
|
|
// Detect first local branch if no default is set
|
|
if self.repo.default_branch.is_empty()
|
|
&& is_branch
|
|
&& !is_remote
|
|
&& auto_detected_branch.is_none()
|
|
{
|
|
auto_detected_branch = Some(shorthand.clone());
|
|
}
|
|
|
|
let upstream = if is_branch && !is_remote {
|
|
reference
|
|
.shorthand()
|
|
.map(|short| format!("refs/remotes/{{}}/{}", short))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if existing_names.contains(&name) {
|
|
existing_names.remove(&name);
|
|
repo_branch::Entity::update_many()
|
|
.filter(repo_branch::Column::Repo.eq(repo_id))
|
|
.filter(repo_branch::Column::Name.eq(&name))
|
|
.col_expr(repo_branch::Column::Oid, Expr::value(&target_oid))
|
|
.col_expr(repo_branch::Column::Upstream, Expr::value(upstream))
|
|
.col_expr(
|
|
repo_branch::Column::Head,
|
|
Expr::value(is_branch && shorthand == self.repo.default_branch),
|
|
)
|
|
.col_expr(repo_branch::Column::UpdatedAt, Expr::value(now))
|
|
.exec(txn)
|
|
.await
|
|
.map_err(|e| GitError::IoError(format!("failed to update branch: {}", e)))?;
|
|
} else {
|
|
let new_branch = repo_branch::ActiveModel {
|
|
repo: Set(repo_id),
|
|
name: Set(name),
|
|
oid: Set(target_oid),
|
|
upstream: Set(upstream),
|
|
head: Set(is_branch && shorthand == self.repo.default_branch),
|
|
created_at: Set(now),
|
|
updated_at: Set(now),
|
|
..Default::default()
|
|
};
|
|
new_branch
|
|
.insert(txn)
|
|
.await
|
|
.map_err(|e| GitError::IoError(format!("failed to insert branch: {}", e)))?;
|
|
}
|
|
}
|
|
|
|
if !existing_names.is_empty() {
|
|
repo_branch::Entity::delete_many()
|
|
.filter(repo_branch::Column::Repo.eq(repo_id))
|
|
.filter(repo_branch::Column::Name.is_in(existing_names))
|
|
.exec(txn)
|
|
.await
|
|
.map_err(|e| {
|
|
GitError::IoError(format!("failed to delete stale branches: {}", e))
|
|
})?;
|
|
}
|
|
|
|
// Persist auto-detected default branch and update head flags
|
|
if let Some(ref branch_name) = auto_detected_branch {
|
|
// 1. Update the repo's default_branch
|
|
repo::Entity::update_many()
|
|
.filter(repo::Column::Id.eq(repo_id))
|
|
.col_expr(
|
|
repo::Column::DefaultBranch,
|
|
Expr::value(branch_name.clone()),
|
|
)
|
|
.exec(txn)
|
|
.await
|
|
.map_err(|e| GitError::IoError(format!("failed to set default branch: {}", e)))?;
|
|
|
|
// 2. Clear head on all branches
|
|
repo_branch::Entity::update_many()
|
|
.filter(repo_branch::Column::Repo.eq(repo_id))
|
|
.col_expr(repo_branch::Column::Head, Expr::value(false))
|
|
.exec(txn)
|
|
.await
|
|
.map_err(|e| GitError::IoError(format!("failed to clear head flags: {}", e)))?;
|
|
|
|
// 3. Set head = true for the detected branch (it was inserted above)
|
|
repo_branch::Entity::update_many()
|
|
.filter(repo_branch::Column::Repo.eq(repo_id))
|
|
.filter(repo_branch::Column::Name.eq(branch_name))
|
|
.col_expr(repo_branch::Column::Head, Expr::value(true))
|
|
.exec(txn)
|
|
.await
|
|
.map_err(|e| GitError::IoError(format!("failed to set head flag: {}", e)))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|