Skip to content

Commit 39bdc18

Browse files
committed
Retain references across pages
1 parent 3334145 commit 39bdc18

20 files changed

+153
-73
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ moka = { version = "0.12.0", features = ["future"] }
2929
once_cell = "1.18"
3030
path-clean = "1.0.1"
3131
parking_lot = "0.12"
32-
serde = { version = "1.0", features = ["derive"] }
32+
serde = { version = "1.0", features = ["derive", "rc"] }
3333
sha2 = "0.10"
3434
syntect = "5"
3535
sled = { version = "0.34", features = ["compression"] }

src/database/indexer.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
use git2::Sort;
88
use ini::Ini;
99
use time::OffsetDateTime;
10-
use tracing::{info, info_span};
10+
use tracing::{error, info, info_span};
1111

1212
use crate::database::schema::{
1313
commit::Commit,
@@ -89,7 +89,9 @@ fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
8989
let reference = reference.unwrap();
9090

9191
let reference_name = String::from_utf8_lossy(reference.name_bytes());
92-
if !reference_name.starts_with("refs/heads/") {
92+
if !reference_name.starts_with("refs/heads/")
93+
&& !reference_name.starts_with("refs/tags/")
94+
{
9395
continue;
9496
}
9597

@@ -119,7 +121,10 @@ fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
119121
// TODO: only scan revs from the last time we looked
120122
let mut revwalk = git_repository.revwalk().unwrap();
121123
revwalk.set_sorting(Sort::REVERSE).unwrap();
122-
revwalk.push_ref(&reference_name).unwrap();
124+
if let Err(error) = revwalk.push_ref(&reference_name) {
125+
error!(%error, "Failed to revwalk reference");
126+
continue;
127+
}
123128

124129
let mut i = 0;
125130
for rev in revwalk {

src/git.rs

+30-18
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use anyhow::{Context, Result};
1111
use bytes::{Bytes, BytesMut};
1212
use comrak::{ComrakOptions, ComrakPlugins};
1313
use git2::{
14-
BranchType, DiffFormat, DiffLineType, DiffOptions, DiffStatsFormat, Email, EmailCreateOptions,
15-
ObjectType, Oid, Signature,
14+
DiffFormat, DiffLineType, DiffOptions, DiffStatsFormat, Email, EmailCreateOptions, ObjectType,
15+
Oid, Signature,
1616
};
1717
use moka::future::Cache;
1818
use parking_lot::Mutex;
@@ -51,7 +51,11 @@ impl Git {
5151

5252
impl Git {
5353
#[instrument(skip(self))]
54-
pub async fn repo(self: Arc<Self>, repo_path: PathBuf) -> Result<Arc<OpenRepository>> {
54+
pub async fn repo(
55+
self: Arc<Self>,
56+
repo_path: PathBuf,
57+
branch: Option<Arc<str>>,
58+
) -> Result<Arc<OpenRepository>> {
5559
let repo = tokio::task::spawn_blocking({
5660
let repo_path = repo_path.clone();
5761
move || git2::Repository::open(repo_path)
@@ -64,6 +68,7 @@ impl Git {
6468
git: self,
6569
cache_key: repo_path,
6670
repo: Mutex::new(repo),
71+
branch,
6772
}))
6873
}
6974
}
@@ -72,14 +77,14 @@ pub struct OpenRepository {
7277
git: Arc<Git>,
7378
cache_key: PathBuf,
7479
repo: Mutex<git2::Repository>,
80+
branch: Option<Arc<str>>,
7581
}
7682

7783
impl OpenRepository {
7884
pub async fn path(
7985
self: Arc<Self>,
8086
path: Option<PathBuf>,
8187
tree_id: Option<&str>,
82-
branch: Option<String>,
8388
formatted: bool,
8489
) -> Result<PathDestination> {
8590
let tree_id = tree_id
@@ -93,12 +98,11 @@ impl OpenRepository {
9398
let mut tree = if let Some(tree_id) = tree_id {
9499
repo.find_tree(tree_id)
95100
.context("Couldn't find tree with given id")?
96-
} else if let Some(branch) = branch {
97-
let branch = repo.find_branch(&branch, BranchType::Local)?;
98-
branch
99-
.get()
101+
} else if let Some(branch) = &self.branch {
102+
let reference = repo.resolve_reference_from_short_name(branch)?;
103+
reference
100104
.peel_to_tree()
101-
.context("Couldn't find tree for branch")?
105+
.context("Couldn't find tree for reference")?
102106
} else {
103107
let head = repo.head()?;
104108
head.peel_to_tree()
@@ -175,16 +179,14 @@ impl OpenRepository {
175179
}
176180

177181
#[instrument(skip(self))]
178-
pub async fn tag_info(self: Arc<Self>, tag_name: &str) -> Result<DetailedTag> {
179-
let reference = format!("refs/tags/{tag_name}");
180-
let tag_name = tag_name.to_string();
181-
182+
pub async fn tag_info(self: Arc<Self>) -> Result<DetailedTag> {
182183
tokio::task::spawn_blocking(move || {
184+
let tag_name = self.branch.clone().context("no tag given")?;
183185
let repo = self.repo.lock();
184186

185187
let tag = repo
186-
.find_reference(&reference)
187-
.context("Given reference does not exist in repository")?
188+
.find_reference(&format!("refs/tags/{tag_name}"))
189+
.context("Given tag does not exist in repository")?
188190
.peel_to_tag()
189191
.context("Couldn't get to a tag from the given reference")?;
190192
let tag_target = tag.target().context("Couldn't find tagged object")?;
@@ -222,7 +224,12 @@ impl OpenRepository {
222224
tokio::task::spawn_blocking(move || {
223225
let repo = self.repo.lock();
224226

225-
let head = repo.head().context("Couldn't find HEAD of repository")?;
227+
let head = if let Some(reference) = &self.branch {
228+
repo.resolve_reference_from_short_name(reference)?
229+
} else {
230+
repo.head().context("Couldn't find HEAD of repository")?
231+
};
232+
226233
let commit = head.peel_to_commit().context(
227234
"Couldn't find the commit that the HEAD of the repository refers to",
228235
)?;
@@ -268,7 +275,12 @@ impl OpenRepository {
268275
tokio::task::spawn_blocking(move || {
269276
let repo = self.repo.lock();
270277

271-
let head = repo.head().context("Couldn't find HEAD of repository")?;
278+
let head = if let Some(reference) = &self.branch {
279+
repo.resolve_reference_from_short_name(reference)?
280+
} else {
281+
repo.head().context("Couldn't find HEAD of repository")?
282+
};
283+
272284
let commit = head
273285
.peel_to_commit()
274286
.context("Couldn't find commit HEAD of repository refers to")?;
@@ -381,7 +393,7 @@ pub enum TaggedObject {
381393

382394
#[derive(Debug)]
383395
pub struct DetailedTag {
384-
pub name: String,
396+
pub name: Arc<str>,
385397
pub tagger: Option<CommitUser>,
386398
pub message: String,
387399
pub tagged_object: Option<TaggedObject>,

src/main.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ use axum::{
2121
use bat::assets::HighlightingAssets;
2222
use clap::Parser;
2323
use once_cell::sync::{Lazy, OnceCell};
24-
use sha2::digest::FixedOutput;
25-
use sha2::Digest;
24+
use sha2::{digest::FixedOutput, Digest};
2625
use sled::Db;
2726
use syntect::html::ClassStyle;
2827
use tokio::{

src/methods/repo/about.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::sync::Arc;
22

33
use askama::Template;
4-
use axum::{response::Response, Extension};
4+
use axum::{extract::Query, response::Response, Extension};
5+
use serde::Deserialize;
56

67
use crate::{
78
git::ReadmeFormat,
@@ -10,20 +11,35 @@ use crate::{
1011
Git,
1112
};
1213

14+
#[derive(Deserialize)]
15+
pub struct UriQuery {
16+
#[serde(rename = "h")]
17+
pub branch: Option<Arc<str>>,
18+
}
19+
1320
#[derive(Template)]
1421
#[template(path = "repo/about.html")]
1522
pub struct View {
1623
repo: Repository,
1724
readme: Option<(ReadmeFormat, Arc<str>)>,
25+
branch: Option<Arc<str>>,
1826
}
1927

2028
pub async fn handle(
2129
Extension(repo): Extension<Repository>,
2230
Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
2331
Extension(git): Extension<Arc<Git>>,
32+
Query(query): Query<UriQuery>,
2433
) -> Result<Response> {
25-
let open_repo = git.clone().repo(repository_path).await?;
34+
let open_repo = git
35+
.clone()
36+
.repo(repository_path, query.branch.clone())
37+
.await?;
2638
let readme = open_repo.readme().await?;
2739

28-
Ok(into_response(&View { repo, readme }))
40+
Ok(into_response(&View {
41+
repo,
42+
readme,
43+
branch: query.branch,
44+
}))
2945
}

src/methods/repo/commit.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ use crate::{
1616
pub struct View {
1717
pub repo: Repository,
1818
pub commit: Arc<Commit>,
19+
pub branch: Option<Arc<str>>,
1920
}
2021

2122
#[derive(Deserialize)]
2223
pub struct UriQuery {
2324
pub id: Option<String>,
25+
#[serde(rename = "h")]
26+
pub branch: Option<Arc<str>>,
2427
}
2528

2629
pub async fn handle(
@@ -29,12 +32,16 @@ pub async fn handle(
2932
Extension(git): Extension<Arc<Git>>,
3033
Query(query): Query<UriQuery>,
3134
) -> Result<Response> {
32-
let open_repo = git.repo(repository_path).await?;
35+
let open_repo = git.repo(repository_path, query.branch.clone()).await?;
3336
let commit = if let Some(commit) = query.id {
3437
open_repo.commit(&commit).await?
3538
} else {
3639
Arc::new(open_repo.latest_commit().await?)
3740
};
3841

39-
Ok(into_response(&View { repo, commit }))
42+
Ok(into_response(&View {
43+
repo,
44+
commit,
45+
branch: query.branch,
46+
}))
4047
}

src/methods/repo/diff.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::{
2020
pub struct View {
2121
pub repo: Repository,
2222
pub commit: Arc<Commit>,
23+
pub branch: Option<Arc<str>>,
2324
}
2425

2526
pub async fn handle(
@@ -28,22 +29,26 @@ pub async fn handle(
2829
Extension(git): Extension<Arc<Git>>,
2930
Query(query): Query<UriQuery>,
3031
) -> Result<Response> {
31-
let open_repo = git.repo(repository_path).await?;
32+
let open_repo = git.repo(repository_path, query.branch.clone()).await?;
3233
let commit = if let Some(commit) = query.id {
3334
open_repo.commit(&commit).await?
3435
} else {
3536
Arc::new(open_repo.latest_commit().await?)
3637
};
3738

38-
Ok(into_response(&View { repo, commit }))
39+
Ok(into_response(&View {
40+
repo,
41+
commit,
42+
branch: query.branch,
43+
}))
3944
}
4045

4146
pub async fn handle_plain(
4247
Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
4348
Extension(git): Extension<Arc<Git>>,
4449
Query(query): Query<UriQuery>,
4550
) -> Result<Response> {
46-
let open_repo = git.repo(repository_path).await?;
51+
let open_repo = git.repo(repository_path, query.branch).await?;
4752
let commit = if let Some(commit) = query.id {
4853
open_repo.commit(&commit).await?
4954
} else {

src/methods/repo/log.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,22 @@ pub async fn get_branch_commits(
6666
amount: usize,
6767
offset: usize,
6868
) -> Result<Vec<YokedCommit>> {
69-
let reference = branch.map(|branch| format!("refs/heads/{branch}"));
69+
if let Some(reference) = branch {
70+
let commit_tree = repository
71+
.get()
72+
.commit_tree(database, &format!("refs/heads/{reference}"))?;
73+
let commit_tree = commit_tree.fetch_latest(amount, offset).await;
7074

71-
if let Some(reference) = reference {
72-
let commit_tree = repository.get().commit_tree(database, &reference)?;
73-
return Ok(commit_tree.fetch_latest(amount, offset).await);
75+
if !commit_tree.is_empty() {
76+
return Ok(commit_tree);
77+
}
78+
79+
let tag_tree = repository
80+
.get()
81+
.commit_tree(database, &format!("refs/tags/{reference}"))?;
82+
let tag_tree = tag_tree.fetch_latest(amount, offset).await;
83+
84+
return Ok(tag_tree);
7485
}
7586

7687
for branch in DEFAULT_BRANCHES {

src/methods/repo/refs.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::BTreeMap;
1+
use std::{collections::BTreeMap, sync::Arc};
22

33
use anyhow::Context;
44
use askama::Template;
@@ -17,6 +17,7 @@ use crate::{
1717
pub struct View {
1818
repo: Repository,
1919
refs: Refs,
20+
branch: Option<Arc<str>>,
2021
}
2122

2223
#[allow(clippy::unused_async)]
@@ -46,5 +47,6 @@ pub async fn handle(
4647
Ok(into_response(&View {
4748
repo,
4849
refs: Refs { heads, tags },
50+
branch: None,
4951
}))
5052
}

src/methods/repo/smart_git.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ use httparse::Status;
1414
use tokio_util::io::StreamReader;
1515
use tracing::warn;
1616

17-
use crate::methods::repo::{Repository, RepositoryPath, Result};
18-
use crate::StatusCode;
17+
use crate::{
18+
methods::repo::{Repository, RepositoryPath, Result},
19+
StatusCode,
20+
};
1921

2022
#[allow(clippy::unused_async)]
2123
pub async fn handle(

src/methods/repo/summary.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::BTreeMap;
1+
use std::{collections::BTreeMap, sync::Arc};
22

33
use anyhow::Context;
44
use askama::Template;
@@ -20,6 +20,7 @@ pub struct View<'a> {
2020
repo: Repository,
2121
refs: Refs,
2222
commit_list: Vec<&'a crate::database::schema::commit::Commit<'a>>,
23+
branch: Option<Arc<str>>,
2324
}
2425

2526
pub async fn handle(
@@ -51,6 +52,7 @@ pub async fn handle(
5152
repo,
5253
refs: Refs { heads, tags },
5354
commit_list,
55+
branch: None,
5456
}))
5557
}
5658

0 commit comments

Comments
 (0)