Use correct default branch on log & summary pages
Diff
src/database/indexer.rs | 49 ++++++++++++++++++++++++++++++++++++----
src/database/schema/mod.rs | 2 ++-
src/database/schema/prefixes.rs | 5 ++++-
src/database/schema/repository.rs | 14 +++++++++++-
src/main.rs | 44 ++++++++++++++++++++++++++++--------
src/methods/repo/log.rs | 8 ++++++-
src/methods/repo/summary.rs | 8 ++++++-
7 files changed, 115 insertions(+), 15 deletions(-)
@@ -4,10 +4,10 @@ use std::{
path::{Path, PathBuf},
};
use git2::Sort;
use git2::{ErrorCode, Sort};
use ini::Ini;
use time::OffsetDateTime;
use tracing::{error, info, info_span};
use tracing::{error, info, info_span, instrument, warn};
use crate::database::schema::{
commit::Commit,
@@ -56,11 +56,19 @@ fn update_repository_metadata(scan_path: &Path, db: &sled::Db) {
owner: find_gitweb_owner(repository_path.as_path()),
last_modified: find_last_committed_time(&git_repository)
.unwrap_or(OffsetDateTime::UNIX_EPOCH),
default_branch: find_default_branch(&git_repository)
.ok()
.flatten()
.map(Cow::Owned),
}
.insert(db, relative);
}
}
fn find_default_branch(repo: &git2::Repository) -> Result<Option<String>, git2::Error> {
Ok(repo.head()?.name().map(ToString::to_string))
}
fn find_last_committed_time(repo: &git2::Repository) -> Result<OffsetDateTime, git2::Error> {
let mut timestamp = OffsetDateTime::UNIX_EPOCH;
@@ -81,9 +89,13 @@ fn find_last_committed_time(repo: &git2::Repository) -> Result<OffsetDateTime, g
Ok(timestamp)
}
#[instrument(skip(db))]
fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
for (relative_path, db_repository) in Repository::fetch_all(db).unwrap() {
let git_repository = git2::Repository::open(scan_path.join(&relative_path)).unwrap();
let Some(git_repository) = open_repo(scan_path, &relative_path, db_repository.get(), db)
else {
continue;
};
for reference in git_repository.references().unwrap() {
let reference = reference.unwrap();
@@ -145,9 +157,13 @@ fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
}
}
#[instrument(skip(db))]
fn update_repository_tags(scan_path: &Path, db: &sled::Db) {
for (relative_path, db_repository) in Repository::fetch_all(db).unwrap() {
let git_repository = git2::Repository::open(scan_path.join(&relative_path)).unwrap();
let Some(git_repository) = open_repo(scan_path, &relative_path, db_repository.get(), db)
else {
continue;
};
let tag_tree = db_repository.get().tag_tree(db).unwrap();
@@ -195,6 +211,31 @@ fn update_repository_tags(scan_path: &Path, db: &sled::Db) {
}
}
#[instrument(skip(scan_path, db_repository, db))]
fn open_repo(
scan_path: &Path,
relative_path: &str,
db_repository: &Repository<'_>,
db: &sled::Db,
) -> Option<git2::Repository> {
match git2::Repository::open(scan_path.join(relative_path)) {
Ok(v) => Some(v),
Err(e) if e.code() == ErrorCode::NotFound => {
warn!("Repository gone from disk, removing from db");
if let Err(error) = db_repository.delete(db, relative_path) {
warn!(%error, "Failed to delete dangling index");
}
None
}
Err(error) => {
warn!(%error, "Failed to reindex {relative_path}, skipping");
None
}
}
}
fn get_relative_path<'a>(relative_to: &Path, full_path: &'a Path) -> &'a Path {
full_path.strip_prefix(relative_to).unwrap()
}
@@ -9,3 +9,5 @@ pub mod repository;
pub mod tag;
pub type Yoked<T> = Yoke<T, Box<IVec>>;
pub const SCHEMA_VERSION: &str = "1";
@@ -5,6 +5,7 @@ use crate::database::schema::repository::RepositoryId;
#[repr(u8)]
pub enum TreePrefix {
Repository = 0,
SchemaVersion = 1,
Commit = 100,
Tag = 101,
}
@@ -45,4 +46,8 @@ impl TreePrefix {
prefixed
}
pub fn schema_version() -> &'static [u8] {
&[TreePrefix::SchemaVersion as u8]
}
}
@@ -25,6 +25,9 @@ pub struct Repository<'a> {
pub owner: Option<Cow<'a, str>>,
pub last_modified: OffsetDateTime,
#[serde(borrow)]
pub default_branch: Option<Cow<'a, str>>,
}
pub type YokedRepository = Yoked<Repository<'static>>;
@@ -60,6 +63,17 @@ impl Repository<'_> {
.unwrap();
}
pub fn delete<P: AsRef<Path>>(&self, database: &sled::Db, path: P) -> Result<()> {
for reference in self.heads(database) {
database.drop_tree(TreePrefix::commit_id(self.id, &reference))?;
}
database.drop_tree(TreePrefix::tag_id(self.id))?;
database.remove(TreePrefix::repository_id(path))?;
Ok(())
}
pub fn open<P: AsRef<Path>>(database: &sled::Db, path: P) -> Result<Option<YokedRepository>> {
database
.get(TreePrefix::repository_id(path))
@@ -1,6 +1,7 @@
#![deny(clippy::pedantic)]
use std::{
borrow::Cow,
fmt::{Display, Formatter},
net::SocketAddr,
path::PathBuf,
@@ -9,6 +10,7 @@ use std::{
time::Duration,
};
use anyhow::Context;
use askama::Template;
use axum::{
body::Body,
@@ -20,6 +22,7 @@ use axum::{
};
use bat::assets::HighlightingAssets;
use clap::Parser;
use database::schema::{prefixes::TreePrefix, SCHEMA_VERSION};
use once_cell::sync::{Lazy, OnceCell};
use sha2::{digest::FixedOutput, Digest};
use sled::Db;
@@ -30,7 +33,7 @@ use tokio::{
};
use tower_http::cors::CorsLayer;
use tower_layer::layer_fn;
use tracing::{error, info, instrument};
use tracing::{error, info, instrument, warn};
use crate::{git::Git, layers::logger::LoggingMiddleware};
@@ -95,7 +98,7 @@ impl FromStr for RefreshInterval {
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), anyhow::Error> {
let args: Args = Args::parse();
let subscriber = tracing_subscriber::fmt();
@@ -103,11 +106,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let subscriber = subscriber.pretty();
subscriber.init();
let db = sled::Config::default()
.use_compression(true)
.path(&args.db_store)
.open()
.unwrap();
let db = open_db(&args)?;
let indexer_wakeup_task =
run_indexer(db.clone(), args.scan_path.clone(), args.refresh_interval);
@@ -190,8 +189,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.serve(app.into_make_service_with_connect_info::<SocketAddr>());
tokio::select! {
res = server => res.map_err(Box::from),
res = indexer_wakeup_task => res.map_err(Box::from),
res = server => res.context("failed to run server"),
res = indexer_wakeup_task => res.context("failed to run indexer"),
_ = tokio::signal::ctrl_c() => {
info!("Received ctrl-c, shutting down");
Ok(())
@@ -199,6 +198,33 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
fn open_db(args: &Args) -> Result<Db, anyhow::Error> {
let db = sled::Config::default()
.use_compression(true)
.path(&args.db_store)
.open()
.context("Failed to open database")?;
let needs_schema_regen = match db.get(TreePrefix::schema_version())? {
Some(v) if v != SCHEMA_VERSION.as_bytes() => Some(Some(v)),
Some(_) => None,
None => Some(None),
};
if let Some(version) = needs_schema_regen {
let old_version = version
.as_deref()
.map_or(Cow::Borrowed("unknown"), String::from_utf8_lossy);
warn!("Clearing outdated database ({old_version} != {SCHEMA_VERSION})");
db.clear()?;
db.insert(TreePrefix::schema_version(), SCHEMA_VERSION)?;
}
Ok(db)
}
async fn run_indexer(
db: Db,
scan_path: PathBuf,
@@ -84,7 +84,13 @@ pub async fn get_branch_commits(
return Ok(tag_tree);
}
for branch in DEFAULT_BRANCHES {
for branch in repository
.get()
.default_branch
.as_deref()
.into_iter()
.chain(DEFAULT_BRANCHES.into_iter())
{
let commit_tree = repository.get().commit_tree(database, branch)?;
let commits = commit_tree.fetch_latest(amount, offset).await;
@@ -60,7 +60,13 @@ pub async fn get_default_branch_commits(
repository: &YokedRepository,
database: &sled::Db,
) -> Result<Vec<YokedCommit>> {
for branch in DEFAULT_BRANCHES {
for branch in repository
.get()
.default_branch
.as_deref()
.into_iter()
.chain(DEFAULT_BRANCHES.into_iter())
{
let commit_tree = repository.get().commit_tree(database, branch)?;
let commits = commit_tree.fetch_latest(11, 0).await;