Use correct default branch on log & summary pages
Diff
src/main.rs | 44 +++++++++++++++++++++++++++++++++++++++++---
src/database/indexer.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
src/database/schema/mod.rs | 2 ++
src/database/schema/prefixes.rs | 5 +++++
src/database/schema/repository.rs | 14 ++++++++++++++
src/methods/repo/log.rs | 8 +++++++-
src/methods/repo/summary.rs | 8 +++++++-
7 files changed, 115 insertions(+), 15 deletions(-)
@@ -1,6 +1,7 @@
#![deny(clippy::pedantic)]
use std::{
borrow::Cow,
fmt::{Display, Formatter},
net::SocketAddr,
path::PathBuf,
@@ -9,6 +10,7 @@
time::Duration,
};
use anyhow::Context;
use askama::Template;
use axum::{
body::Body,
@@ -20,6 +22,7 @@
};
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 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 @@
}
#[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 @@
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,13 +189,40 @@
.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(())
}
}
}
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(
@@ -1,13 +1,13 @@
use std::{
borrow::Cow,
collections::HashSet,
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 @@
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 @@
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 @@
}
}
#[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();
@@ -191,6 +207,31 @@
info!("Removing stale tag from index");
tag_tree.remove(tag_name);
}
}
}
#[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
}
}
}
@@ -9,3 +9,5 @@
pub mod tag;
pub type Yoked<T> = Yoke<T, Box<IVec>>;
pub const SCHEMA_VERSION: &str = "1";
@@ -5,6 +5,7 @@
#[repr(u8)]
pub enum TreePrefix {
Repository = 0,
SchemaVersion = 1,
Commit = 100,
Tag = 101,
}
@@ -44,5 +45,9 @@
prefixed.extend_from_slice(&repository.to_ne_bytes());
prefixed
}
pub fn schema_version() -> &'static [u8] {
&[TreePrefix::SchemaVersion as u8]
}
}
@@ -25,6 +25,9 @@
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>>;
@@ -58,6 +61,17 @@
bincode::serialize(self).unwrap(),
)
.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>> {
@@ -84,7 +84,13 @@
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 @@
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;