gitscope
← rgit / Diff · 7d804b9 → 7d804b9
Changes 7d804b9..7d804b9
Patch commit
diff --git a/src/database/indexer.rs b/src/database/indexer.rs
index 11ba789..c79ecba 100644
--- a/src/database/indexer.rs
+++ b/src/database/indexer.rs
@@ -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()
}
diff --git a/src/database/schema/mod.rs b/src/database/schema/mod.rs
index 9609e4f..d977149 100644
--- a/src/database/schema/mod.rs
+++ b/src/database/schema/mod.rs
@@ -9,3 +9,5 @@ pub mod repository;
pub mod tag;
pub type Yoked<T> = Yoke<T, Box<IVec>>;
+
+pub const SCHEMA_VERSION: &str = "1";
diff --git a/src/database/schema/prefixes.rs b/src/database/schema/prefixes.rs
index 8cfd627..2646915 100644
--- a/src/database/schema/prefixes.rs
+++ b/src/database/schema/prefixes.rs
@@ -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]
+ }
}
diff --git a/src/database/schema/repository.rs b/src/database/schema/repository.rs
index a8c8c9b..8eb48e5 100644
--- a/src/database/schema/repository.rs
+++ b/src/database/schema/repository.rs
@@ -25,6 +25,9 @@ pub struct Repository<'a> {
pub owner: Option<Cow<'a, str>>,
/// The last time this repository was updated, currently read from the directory mtime
pub last_modified: OffsetDateTime,
+ /// The default branch for Git operations
+ #[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))
diff --git a/src/main.rs b/src/main.rs
index 75f0f66..eea8226 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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,
diff --git a/src/methods/repo/log.rs b/src/methods/repo/log.rs
index 6b07a4c..e43a190 100644
--- a/src/methods/repo/log.rs
+++ b/src/methods/repo/log.rs
@@ -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;
diff --git a/src/methods/repo/summary.rs b/src/methods/repo/summary.rs
index 4302c51..0d4bb07 100644
--- a/src/methods/repo/summary.rs
+++ b/src/methods/repo/summary.rs
@@ -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;