diff --git a/src/git.rs b/src/git.rs
@@ -7,25 +7,35 @@ use std::{
};
use arc_swap::ArcSwapOption;
-use git2::{Oid, Repository, Signature, Sort};
+use git2::{ObjectType, Oid, Repository, Signature};
+use moka::future::Cache;
use time::OffsetDateTime;
pub type RepositoryMetadataList = BTreeMap<Option<String>, Vec<RepositoryMetadata>>;
#[derive(Clone)]
pub struct Git {
- commits: moka::future::Cache<Oid, Arc<Commit>>,
- readme_cache: moka::future::Cache<PathBuf, Arc<str>>,
- refs: moka::future::Cache<PathBuf, Arc<Refs>>,
+ commits: Cache<Oid, Arc<Commit>>,
+ readme_cache: Cache<PathBuf, Arc<str>>,
+ refs: Cache<PathBuf, Arc<Refs>>,
repository_metadata: Arc<ArcSwapOption<RepositoryMetadataList>>,
}
impl Default for Git {
fn default() -> Self {
Self {
- commits: moka::future::Cache::new(100),
- readme_cache: moka::future::Cache::new(100),
- refs: moka::future::Cache::new(100),
+ commits: Cache::builder()
+ .time_to_live(Duration::from_secs(10))
+ .max_capacity(100)
+ .build(),
+ readme_cache: Cache::builder()
+ .time_to_live(Duration::from_secs(10))
+ .max_capacity(100)
+ .build(),
+ refs: Cache::builder()
+ .time_to_live(Duration::from_secs(10))
+ .max_capacity(100)
+ .build(),
repository_metadata: Arc::new(ArcSwapOption::default()),
}
}
@@ -69,11 +79,11 @@ impl Git {
commit: commit.into(),
});
} else if ref_.is_tag() {
- let commit = ref_.peel_to_commit().unwrap();
+ let tag = ref_.peel_to_tag().unwrap();
built_refs.tag.push(Tag {
name: ref_.shorthand().unwrap().to_string(),
- commit: commit.into(),
+ tagger: tag.tagger().map(Into::into),
});
}
}
@@ -143,22 +153,27 @@ impl Git {
repos
}
- pub async fn get_commits(&self, repo: PathBuf) -> Vec<Commit> {
+ pub async fn get_commits(&self, repo: PathBuf, offset: usize) -> (Vec<Commit>, Option<usize>) {
+ const AMOUNT: usize = 200;
+
tokio::task::spawn_blocking(move || {
let repo = Repository::open_bare(repo).unwrap();
let mut revs = repo.revwalk().unwrap();
- revs.set_sorting(Sort::TIME).unwrap();
revs.push_head().unwrap();
- let mut commits = Vec::with_capacity(200);
+ let mut commits: Vec<Commit> = revs
+ .skip(offset)
+ .take(AMOUNT + 1)
+ .map(|rev| {
+ let rev = rev.unwrap();
+ repo.find_commit(rev).unwrap().into()
+ })
+ .collect();
- for rev in revs.skip(0).take(200) {
- let rev = rev.unwrap();
- let commit = repo.find_commit(rev).unwrap();
- commits.push(commit.into());
- }
+ // TODO: avoid having to take + 1 and popping the last commit off
+ let next_offset = commits.pop().is_some().then(|| offset + commits.len());
- commits
+ (commits, next_offset)
})
.await
.unwrap()
@@ -185,7 +200,7 @@ pub struct Remote {
#[derive(Debug)]
pub struct Tag {
pub name: String,
- pub commit: Commit,
+ pub tagger: Option<CommitUser>,
}
#[derive(Debug)]
diff --git a/src/methods/repo.rs b/src/methods/repo.rs
@@ -89,22 +89,40 @@ pub async fn handle_summary(Extension(repo): Extension<Repository>) -> Html<Stri
Html(View { repo }.render().unwrap())
}
+#[derive(Deserialize)]
+pub struct LogQuery {
+ #[serde(rename = "ofs")]
+ offset: Option<usize>,
+}
+
#[allow(clippy::unused_async)]
pub async fn handle_log(
Extension(repo): Extension<Repository>,
Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
Extension(git): Extension<Git>,
+ Query(query): Query<LogQuery>,
) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/log.html")]
pub struct View {
repo: Repository,
commits: Vec<Commit>,
+ next_offset: Option<usize>,
}
- let commits = git.get_commits(repository_path).await;
+ let (commits, next_offset) = git
+ .get_commits(repository_path, query.offset.unwrap_or(0))
+ .await;
- Html(View { repo, commits }.render().unwrap())
+ Html(
+ View {
+ repo,
+ commits,
+ next_offset,
+ }
+ .render()
+ .unwrap(),
+ )
}
#[allow(clippy::unused_async)]
diff --git a/statics/style.css b/statics/style.css
@@ -33,6 +33,10 @@ a:hover {
margin-top: 2rem;
}
+.text-center {
+ text-align: center;
+}
+
.no-hover:hover {
text-decoration: none;
}
@@ -52,6 +56,11 @@ table tbody tr.has-parent td:first-of-type {
padding-left: 1rem;
}
+tr.separator {
+ background: white !important;
+ height: 1rem;
+}
+
table pre { margin: 0; }
table.commit-info td, table.commit-info th { padding: 0.1em 1em 0.1em 0.1em; }
diff --git a/templates/repo/log.html b/templates/repo/log.html
@@ -25,4 +25,10 @@
{% endfor %}
</tbody>
</table>
+
+{% if let Some(next_offset) = next_offset %}
+<div class="mt-2 text-center">
+ <a href="?ofs={{ next_offset }}">[next]</a>
+</div>
+{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/repo/refs.html b/templates/repo/refs.html
@@ -26,28 +26,37 @@
</tr>
{% endfor %}
</tbody>
-</table>
-<table class="repositories mt-2">
- <thead>
+ <tbody>
+ <tr class="separator">
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+
<tr>
<th>Tag</th>
<th>Download</th>
<th>Author</th>
<th>Age</th>
</tr>
- </thead>
- <tbody>
- {% for tag in refs.tag %}
+ {% for tag in refs.tag.iter().rev() %}
<tr>
<td><a href="/{{ repo.display() }}/tag/?h={{ tag.name }}">{{ tag.name }}</a></td>
<td></td>
<td>
- <img src="https://www.gravatar.com/avatar/{{ tag.commit.author().email_md5() }}?s=13&d=retro" width="13" height="13">
- {{ tag.commit.author().name() }}
+ {% if let Some(tagger) = tag.tagger %}
+ <img src="https://www.gravatar.com/avatar/{{ tagger.email_md5() }}?s=13&d=retro" width="13" height="13">
+ {{ tagger.name() }}
+ {% endif %}
+ </td>
+ <td>
+ {% if let Some(tagger) = tag.tagger %}
+ {{ tagger.time() }}
+ {% endif %}
</td>
- <td>{{ tag.commit.author().time() }}</td>
</tr>
{% endfor %}
</tbody>