michal/tit
Browse tree · Show commit · Download archive
Diff
ffbc13328b68 → cd74ae809117
assets/style.css
Mode 100644 → 100644; object 6591942e96a5 → 3a8e737bef64
@@ -55,9 +55,21 @@
font-weight: 700;
}
-.repository-header nav,
+.repository-header {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 0.5rem 2rem;
+}
+
.repository-header h1 {
margin-block: 0.5rem;
+}
+
+.repository-header nav {
+ justify-content: flex-end;
+ font-size: 0.875rem;
}
.repository-toolbar {
@@ -73,7 +85,7 @@
.repository-summary-layout {
display: grid;
- grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr);
+ grid-template-columns: minmax(0, 1.7fr) minmax(23rem, 1fr);
gap: 2rem;
align-items: start;
}
@@ -83,18 +95,56 @@
margin-bottom: 2rem;
}
-.repository-sidebar > section {
+.repository-panel {
padding: 1rem;
border: 1px solid GrayText;
border-radius: 0.25rem;
}
-.repository-sidebar h2 {
+.repository-panel > h2:first-child {
margin-top: 0;
+}
+
+.repository-file-panel {
+ padding: 0;
+ overflow: hidden;
+}
+
+.repository-file-panel > h2 {
+ margin: 0;
+ padding: 1rem;
+ border-bottom: 1px solid GrayText;
+}
+
+.repository-file-panel th,
+.repository-file-panel td {
+ padding: 0.6rem 1rem;
+}
+
+.repository-file-panel tbody tr:last-child > * {
+ border-bottom: 0;
}
.repository-file-list th:first-child {
width: 100%;
+}
+
+.repository-sidebar dd {
+ overflow-x: auto;
+ overflow-wrap: normal;
+ white-space: nowrap;
+}
+
+.repository-recent-list {
+ padding-left: 1.5rem;
+}
+
+.repository-recent-list li {
+ margin-block: 0.5rem;
+}
+
+.repository-readme {
+ margin-block: 2rem;
}
.site-header nav,
src/http/public.rs
Mode 100644 → 100644; object 55bf5641ed98 → f1ddda9ecb4e
@@ -38,7 +38,8 @@
use super::{PublicWebConfig, RequestActor, RequestId, WebState, render_error_with_auth};
const MAX_HISTORY_COMMITS: usize = 10_000;
-const MAX_SUMMARY_COMMITS: usize = 10;
+const MAX_SUMMARY_COMMITS: usize = 3;
+const MAX_SUMMARY_TAGS: usize = 3;
const COMMITS_PER_PAGE: usize = 100;
const MAX_SEARCH_QUERY_BYTES: usize = 256;
@@ -436,6 +437,26 @@
.iter()
.find(|reference| reference.name == default_branch.as_bytes())
.map(|reference| reference.target);
+ let mut tags = references
+ .iter()
+ .filter(|reference| reference.name.starts_with(b"refs/tags/"))
+ .filter_map(|reference| {
+ let target = reference.peeled.unwrap_or(reference.target);
+ let commit = service.commit(target, &cancellation).ok()?;
+ Some(RepositorySummaryTag {
+ name: reference.name[b"refs/tags/".len()..].to_vec(),
+ target,
+ committed_at: commit.committed_at,
+ })
+ })
+ .collect::<Vec<_>>();
+ tags.sort_by(|left, right| {
+ right
+ .committed_at
+ .cmp(&left.committed_at)
+ .then_with(|| right.name.cmp(&left.name))
+ });
+ tags.truncate(MAX_SUMMARY_TAGS);
let (history, readme, entries) = match head {
Some(head) => (
service.history(head, &cancellation)?,
@@ -452,6 +473,7 @@
default_branch,
head,
history,
+ tags,
readme: readme.map(|readme| (readme.path, readme.blob.data)),
entries,
},
@@ -1393,6 +1415,7 @@
readme_html: RenderedMarkdown,
readme_binary: bool,
history: Vec<CommitView>,
+ tags: Vec<TagView>,
entries: Vec<TreeView>,
blob_content: String,
blob_binary: bool,
@@ -1420,6 +1443,7 @@
default_branch: String,
head: Option<ObjectId>,
history: Vec<CommitInfo>,
+ tags: Vec<RepositorySummaryTag>,
readme: Option<(Vec<u8>, Vec<u8>)>,
entries: Vec<TreeEntryInfo>,
}
@@ -1448,6 +1472,7 @@
readme_html: RenderedMarkdown::default(),
readme_binary: false,
history: Vec::new(),
+ tags: Vec::new(),
entries: Vec::new(),
blob_content: String::new(),
blob_binary: false,
@@ -1488,6 +1513,15 @@
.into_iter()
.take(MAX_SUMMARY_COMMITS)
.map(CommitView::from)
+ .collect();
+ page.tags = summary
+ .tags
+ .into_iter()
+ .map(|tag| TagView {
+ name: display_bytes(&tag.name),
+ id: tag.target.to_string(),
+ committed_at: tag.committed_at,
+ })
.collect();
if let Some(head) = summary.head {
page.entries = summary
@@ -1692,6 +1726,18 @@
}
page
}
+}
+
+struct RepositorySummaryTag {
+ name: Vec<u8>,
+ target: ObjectId,
+ committed_at: i64,
+}
+
+struct TagView {
+ name: String,
+ id: String,
+ committed_at: i64,
}
#[derive(Default)]
templates/repository.html
Mode 100644 → 100644; object 525b73c4631b → c9b763fe942c
@@ -22,7 +22,7 @@
<div class="repository-summary-layout">
<div class="repository-summary-main">
{% if has_head %}
- <section aria-labelledby="files-heading">
+ <section class="repository-panel repository-file-panel" aria-labelledby="files-heading">
<h2 id="files-heading">Files</h2>
<div class="table-scroll"><table class="repository-file-list">
<thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Object</th></tr></thead>
@@ -37,31 +37,12 @@
</tbody>
</table></div>
</section>
- <section aria-labelledby="history-heading">
- <h2 id="history-heading">Recent commits</h2>
- <ol class="commit-list">
-{% for item in history %}
- <li><a href="/{{ owner }}/{{ repository }}/commit/{{ item.id }}" aria-label="Commit {{ item.id }}"><code title="{{ item.id }}">{{ item.id|short_id }}</code></a> {{ item.summary }} — {{ item.author_name }}</li>
-{% endfor %}
- </ol>
- <p><a href="/{{ owner }}/{{ repository }}/commits">View all commits</a></p>
- </section>
-{% if has_readme %}
- <section aria-labelledby="readme-heading">
- <h2 id="readme-heading">{{ readme_path }}</h2>
-{% if readme_binary %}
- <p>Binary README content cannot be shown.</p>
-{% else %}
- <div class="markdown">{{ readme_html|safe }}</div>
-{% endif %}
- </section>
-{% endif %}
{% else %}
<p>This repository has no commits.</p>
{% endif %}
</div>
<aside class="repository-sidebar" aria-label="Repository information">
- <section aria-labelledby="about-heading">
+ <section class="repository-panel" aria-labelledby="about-heading">
<h2 id="about-heading">About</h2>
{% if description != "" %}
<p class="repository-description">{{ description }}</p>
@@ -70,15 +51,50 @@
{% endif %}
<p>Created <time datetime="{{ created_at }}">{{ created_at|human_time }}</time></p>
</section>
- <section aria-labelledby="clone-heading">
+ <section class="repository-panel" aria-labelledby="clone-heading">
<h2 id="clone-heading">Clone</h2>
<dl>
<dt>HTTP</dt><dd><code>{{ http_clone_url }}</code></dd>
<dt>SSH</dt><dd><code>{{ ssh_clone_url }}</code></dd>
</dl>
</section>
+ <section class="repository-panel" aria-labelledby="recent-heading">
+ <h2 id="recent-heading">Recent</h2>
+ <h3>Commits</h3>
+{% if history.is_empty() %}
+ <p>No commits are available.</p>
+{% else %}
+ <ol class="repository-recent-list">
+{% for item in history %}
+ <li><a href="/{{ owner }}/{{ repository }}/commit/{{ item.id }}" aria-label="Commit {{ item.id }}"><code title="{{ item.id }}">{{ item.id|short_id }}</code></a> {{ item.summary }}</li>
+{% endfor %}
+ </ol>
+ <p><a href="/{{ owner }}/{{ repository }}/commits">View all commits</a></p>
+{% endif %}
+ <h3>Tags</h3>
+{% if tags.is_empty() %}
+ <p>No tags are available.</p>
+{% else %}
+ <ul class="repository-recent-list">
+{% for tag in tags %}
+ <li><a href="/{{ owner }}/{{ repository }}/commit/{{ tag.id }}">{{ tag.name }}</a> · <time datetime="{{ tag.committed_at }}">{{ tag.committed_at|human_time }}</time></li>
+{% endfor %}
+ </ul>
+ <p><a href="/{{ owner }}/{{ repository }}/refs">View all refs</a></p>
+{% endif %}
+ </section>
</aside>
</div>
+{% if has_readme %}
+ <section class="repository-panel repository-readme" aria-labelledby="readme-heading">
+ <h2 id="readme-heading">{{ readme_path }}</h2>
+{% if readme_binary %}
+ <p>Binary README content cannot be shown.</p>
+{% else %}
+ <div class="markdown">{{ readme_html|safe }}</div>
+{% endif %}
+ </section>
+{% endif %}
{% endif %}
{% if page_kind == "commits" %}
tests/public_routes.rs
Mode 100644 → 100644; object 2205df793b20 → 393a5b099ec0
@@ -116,6 +116,9 @@
assert!(summary_text.contains("ssh://tit.example:2222/alice/example"));
assert!(summary_text.contains(&fixture.head));
assert!(summary_text.contains("<div class=\"repository-summary-layout\">"));
+ assert!(summary_text.contains(
+ "</aside>\n </div>\n\n <section class=\"repository-panel repository-readme\""
+ ));
assert!(summary_text.contains("<strong>main</strong>"));
assert!(summary_text.contains(&format!(
"href=\"/alice/example/blob/{}/README.md\"",
@@ -146,12 +149,11 @@
assert!(summary_text.contains("/alice/example/rss.xml"));
assert!(summary_text.contains("/alice/example/search"));
assert!(summary_text.contains("/alice/example/commits\">View all commits</a>"));
- assert_eq!(
- summary_text
- .matches("<li><a href=\"/alice/example/commit/")
- .count(),
- 10
- );
+ assert!(summary_text.contains(">v0.4</a>"));
+ assert!(summary_text.contains(">v0.3</a>"));
+ assert!(summary_text.contains(">v0.2</a>"));
+ assert!(!summary_text.contains(">v0.1</a>"));
+ assert_eq!(summary_text.matches("aria-label=\"Commit ").count(), 3);
assert!(!summary_text.contains("Object format"));
let commits = request(server.address(), "GET", "/alice/example/commits", &[], &[]);
@@ -1688,6 +1690,20 @@
.expect("update the text file");
commit_all(&worktree, "<script>alert(3)</script>");
let head = rev_parse(&worktree, "HEAD");
+ for (tag, target) in [
+ ("v0.1", parent.as_str()),
+ ("v0.2", "HEAD~2"),
+ ("v0.3", "HEAD~1"),
+ ("v0.4", "HEAD"),
+ ] {
+ run(Command::new("git").arg("-C").arg(&worktree).args([
+ "-c",
+ "tag.gpgSign=false",
+ "tag",
+ tag,
+ target,
+ ]));
+ }
run(Command::new("git")
.arg("-C")
.arg(&worktree)
@@ -1710,6 +1726,12 @@
.args(["push", "-q"])
.arg(&bare)
.args(["main", "feature"]));
+ run(Command::new("git")
+ .arg("-C")
+ .arg(&worktree)
+ .args(["push", "-q"])
+ .arg(&bare)
+ .arg("--tags"));
let database = instance.path().join(store::DATABASE_FILE);
let mut store = Store::open(&database).expect("open the fixture database");