e9a538e7
- refs
- commit
- e9a538e7599fcfe0f2ae9db7aa073210a51e327f
- parent
- author
- Christopher K. Schmitt <me@shmish.dev>
- date
- 2026-08-11
- committer
- Christopher K. Schmitt <me@shmish.dev>
- date
- 2026-08-11
- changes
- 14
- insertions
- 74
- deletions
- 32
Msrc/filters.rs
~ })~}~+/// Returns the first 8 characters of `s`, or the whole string if shorter.+///+/// Unlike a raw `s[..8]` slice, this never panics on short input, so it is+/// safe to apply to SHAs and refs taken directly from request paths.+fn short_chars(s: &str) -> String {+ s.chars().take(8).collect()+}++/// Askama filter `{{ value|short }}`: the first 8 characters of the input.+#[askama::filter_fn]+pub fn short<T: std::fmt::Display>(s: T, _: &dyn askama::Values) -> askama::Result<String> {+ Ok(short_chars(&s.to_string()))+}+~#[cfg(test)]~mod tests {~ use super::*;
~ #[test]~ fn pluralize_multiple() {~ assert_eq!(pluralize(&3, "item", "items").unwrap(), "items");+ }++ #[test]+ fn short_full() {+ assert_eq!(short_chars("0123456789abcdef"), "01234567");+ }++ #[test]+ fn short_abbreviated() {+ assert_eq!(short_chars("863a47b"), "863a47b");+ }++ #[test]+ fn short_branch_name() {+ assert_eq!(short_chars("main"), "main");+ }++ #[test]+ fn short_empty() {+ assert_eq!(short_chars(""), "");~ }~}
Mtemplates/blame.html
~<ul>~ <li><a href="{{ site.base_url }}/">{{ site.site_name }}</a></li>~ <li><a href="{{ site.base_url }}/{{ repo }}">{{ repo }}</a></li>- <li><a href="{{ site.base_url }}/{{ repo }}/commits/{{ sha }}">{{ sha[..8] }}</a></li>+ <li><a href="{{ site.base_url }}/{{ repo }}/commits/{{ sha }}">{{ sha|short }}</a></li>~ {% for crumb in breadcrumbs %}~ <li>{% if loop.last %}{{ crumb.label }}{% else %}<a href="{{ crumb.url }}">{{ crumb.label }}</a>{% endif %}</li>~ {% endfor %}
Mtemplates/branch_detail.html
~ <a href="{{ site.base_url }}/{{ repo }}/commits/{{ tip_sha }}/tree" class="repo-nav-item">~ <svg class="summary-icon"><use href="#icon-tree"/></svg>~ <span>Tree</span>- <span class="repo-nav-item-meta">[{{ tip_sha[..8] }}]</span>+ <span class="repo-nav-item-meta">[{{ tip_sha|short }}]</span>~ <span class="repo-nav-item-arrow">[>]</span>~ </a>~</div>
~<div class="commit-log">~ {% for commit in commits %}~ <a href="{{ site.base_url }}/{{ repo }}/commits/{{ commit.sha }}" class="commit-row">- <span class="commit-sha">{{ commit.sha[..8] }}</span>+ <span class="commit-sha">{{ commit.sha|short }}</span>~ <span class="commit-message">{{ commit.message }}</span>~ <span class="commit-meta"><span>{{ commit.author }}</span><span>{{ commit.date }}</span></span>~ </a>
Mtemplates/commit.html
~{% extends "base.html" %}~-{% block title %}{{ sha[..8] }} - {{ repo }}{% endblock %}-{% block og_title %}{{ sha[..8] }} - {{ repo }}{% endblock %}+{% block title %}{{ sha|short }} - {{ repo }}{% endblock %}+{% block og_title %}{{ sha|short }} - {{ repo }}{% endblock %}~{% block og_description %}{{ message }}{% endblock %}~~{% block breadcrumbs %}
~ <li><a href="{{ site.base_url }}/">{{ site.site_name }}</a></li>~ <li><a href="{{ site.base_url }}/{{ repo }}">{{ repo }}</a></li>~ <li><a href="{{ site.base_url }}/{{ repo }}/commits">commits</a></li>- <li>{{ sha[..8] }}</li>+ <li>{{ sha|short }}</li>~</ul>~{% endblock %}~~{% block content %}-<h1>{{ sha[..8] }}</h1>+<h1>{{ sha|short }}</h1>~~<details>~ <summary><svg class="summary-icon"><use href="#icon-archive"/></svg>Archive</summary>~ <ul>- <li><a href="{{ site.base_url }}/{{ repo }}/archive/tar.gz/{{ sha }}" download="{{ repo }}-{{ sha[..8] }}.tar.gz"><span class="archive-link">{{ repo }}-{{ sha[..8] }}.tar.gz</span><span>tar.gz</span></a></li>- <li><a href="{{ site.base_url }}/{{ repo }}/archive/tar.xz/{{ sha }}" download="{{ repo }}-{{ sha[..8] }}.tar.xz"><span class="archive-link">{{ repo }}-{{ sha[..8] }}.tar.xz</span><span>tar.xz</span></a></li>- <li><a href="{{ site.base_url }}/{{ repo }}/archive/zip/{{ sha }}" download="{{ repo }}-{{ sha[..8] }}.zip"><span class="archive-link">{{ repo }}-{{ sha[..8] }}.zip</span><span>zip</span></a></li>+ <li><a href="{{ site.base_url }}/{{ repo }}/archive/tar.gz/{{ sha }}" download="{{ repo }}-{{ sha|short }}.tar.gz"><span class="archive-link">{{ repo }}-{{ sha|short }}.tar.gz</span><span>tar.gz</span></a></li>+ <li><a href="{{ site.base_url }}/{{ repo }}/archive/tar.xz/{{ sha }}" download="{{ repo }}-{{ sha|short }}.tar.xz"><span class="archive-link">{{ repo }}-{{ sha|short }}.tar.xz</span><span>tar.xz</span></a></li>+ <li><a href="{{ site.base_url }}/{{ repo }}/archive/zip/{{ sha }}" download="{{ repo }}-{{ sha|short }}.zip"><span class="archive-link">{{ repo }}-{{ sha|short }}.zip</span><span>zip</span></a></li>~ </ul>~</details>~
~ <a href="{{ site.base_url }}/{{ repo }}/commits/{{ sha }}/tree" class="repo-nav-item">~ <svg class="summary-icon"><use href="#icon-tree"/></svg>~ <span>Tree</span>- <span class="repo-nav-item-meta">[{{ sha[..8] }}]</span>+ <span class="repo-nav-item-meta">[{{ sha|short }}]</span>~ <span class="repo-nav-item-arrow">[>]</span>~ </a>~</div>
Mtemplates/commit_list.html
~ {% for commit in commits %}~ <a href="{{ site.base_url }}/{{ repo }}/commits/{{ commit.sha }}" class="commit-graph-row" style="border-left: 3px solid {{ commit.color }}">~ <span class="commit-message">{% for r in commit.refs %}<span class="commit-ref commit-ref-{{ r.kind }}">{{ r.name }}</span> {% endfor %}{{ commit.message }}</span>- <span class="commit-sha">{{ commit.sha[..8] }}</span>+ <span class="commit-sha">{{ commit.sha|short }}</span>~ <span class="commit-date">{{ commit.date }}</span>~ </a>~ {% endfor %}
Mtemplates/repo.html
~ <a href="{{ site.base_url }}/{{ name }}/commits/{{ sha }}/tree" class="repo-nav-item">~ <svg class="summary-icon"><use href="#icon-tree"/></svg>~ <span>Tree</span>- <span class="repo-nav-item-meta">[{{ sha[..8] }}]</span>+ <span class="repo-nav-item-meta">[{{ sha|short }}]</span>~ <span class="repo-nav-item-arrow">[>]</span>~ </a>~ {% endif %}
Mtemplates/tag_detail.html
~ <a href="{{ site.base_url }}/{{ repo }}/commits/{{ commit_sha }}/tree" class="repo-nav-item">~ <svg class="summary-icon"><use href="#icon-tree"/></svg>~ <span>Tree</span>- <span class="repo-nav-item-meta">[{{ commit_sha[..8] }}]</span>+ <span class="repo-nav-item-meta">[{{ commit_sha|short }}]</span>~ <span class="repo-nav-item-arrow">[>]</span>~ </a>~</div>
Mtemplates/tree.html
~<ul>~ <li><a href="{{ site.base_url }}/">{{ site.site_name }}</a></li>~ <li><a href="{{ site.base_url }}/{{ repo }}">{{ repo }}</a></li>- <li><a href="{{ site.base_url }}/{{ repo }}/commits/{{ sha }}">{{ sha[..8] }}</a></li>+ <li><a href="{{ site.base_url }}/{{ repo }}/commits/{{ sha }}">{{ sha|short }}</a></li>~ {% for crumb in breadcrumbs %}~ <li>{% if loop.last %}{{ crumb.label }}{% else %}<a href="{{ crumb.url }}">{{ crumb.label }}</a>{% endif %}</li>~ {% endfor %}
Msrc/routes/blame.rs
~~use crate::config::SiteConfig;~use crate::error::Result;+use crate::filters;~use crate::git;~use crate::routes::{AppState, Breadcrumb, RepoName};~
Msrc/routes/branch.rs
~~use crate::config::SiteConfig;~use crate::error::Result;+use crate::filters;~use crate::git;~use crate::routes::{AppState, RepoName};~
Msrc/routes/commit.rs
~~use crate::config::SiteConfig;~use crate::error::{Error, Result};+use crate::filters;~use crate::git::{self, GitResultExt as _, commit_date, committer_date};~use crate::routes::{AppState, RepoName};~
Msrc/routes/repo.rs
~~use crate::config::SiteConfig;~use crate::error::Result;+use crate::filters;~use crate::git::{self, commit_date};~use crate::routes::{AppState, RepoName};~
Msrc/routes/tag.rs
~~use crate::config::SiteConfig;~use crate::error::Result;+use crate::filters;~use crate::git;~use crate::routes::{AppState, RepoName};~
Msrc/routes/tree.rs
~~use crate::config::SiteConfig;~use crate::error::{Error, Result};+use crate::filters;~use crate::git::{self, GitResultExt as _};~use crate::routes::{AppState, Breadcrumb, RepoName};~
~~ let git_repo = git::open_repo(&state.root, &name)?;~- let (object, resolved_path) = if path.is_empty() {+ let (object, resolved_path, sha) = if path.is_empty() {~ // When path is empty, resolve the sha to a commit and get its tree.~ let oid = gix::ObjectId::from_hex(params.sha.as_bytes())~ .map_err(|_| Error::BadRequest(format!("invalid sha: {}", params.sha)))?;+ let sha = oid.to_string();~ let commit = git_repo~ .find_object(oid)~ .map_err(|_| Error::NotFound(params.sha.clone()))?
~ .map_err(|_| Error::BadRequest(format!("{} is not a commit", params.sha)))?;~ let tree_id = commit.tree_id().corrupt()?;~ let tree = git_repo.find_object(tree_id).corrupt()?;- (tree, String::new())+ (tree, String::new(), sha)~ } else {- let spec = format!("{}:{}", params.sha, path);+ let oid = git_repo+ .rev_parse_single(params.sha.as_str())+ .map_err(|_| Error::NotFound(params.sha.clone()))?;+ let sha = oid.to_string();+ let spec = format!("{sha}:{path}");~ let obj = git_repo~ .rev_parse_single(spec.as_str())~ .map_err(|_| Error::NotFound(spec.clone()))?~ .object()~ .corrupt()?;- (obj, path)+ (obj, path, sha)~ };~~ let b = &state.site.base_url;- let breadcrumbs = crate::routes::build_breadcrumbs(b, &name, ¶ms.sha, &resolved_path);+ let breadcrumbs = crate::routes::build_breadcrumbs(b, &name, &sha, &resolved_path);~ let raw_url = if resolved_path.is_empty() {~ String::new()~ } else {- format!("{b}/{name}/commits/{}/raw/{}", params.sha, resolved_path)+ format!("{b}/{name}/commits/{sha}/raw/{resolved_path}")~ };~ let parent_url = if resolved_path.is_empty() {~ String::new()~ } else {~ let parent_path = resolved_path.rsplit_once('/').map_or("", |(p, _)| p);~ if parent_path.is_empty() {- format!("{b}/{name}/commits/{}/tree", params.sha)+ format!("{b}/{name}/commits/{sha}/tree")~ } else {- format!("{b}/{name}/commits/{}/tree/{parent_path}", params.sha)+ format!("{b}/{name}/commits/{sha}/tree/{parent_path}")~ }~ };~
~ .unwrap_or_default()~ };~ let url = if resolved_path.is_empty() {- format!("{b}/{name}/commits/{}/tree/{entry_name}", params.sha)+ format!("{b}/{name}/commits/{sha}/tree/{entry_name}")~ } else {- format!(- "{b}/{name}/commits/{}/tree/{resolved_path}/{entry_name}",- params.sha- )+ format!("{b}/{name}/commits/{sha}/tree/{resolved_path}/{entry_name}")~ };~ Some(TreeEntry {~ name: entry_name,
~~ Ok(TreeTemplate {~ repo: name,- sha: params.sha,+ sha,~ path: resolved_path,~ breadcrumbs,~ is_file: false,
~~ Ok(TreeTemplate {~ repo: name,- sha: params.sha,+ sha,~ path: resolved_path,~ breadcrumbs,~ is_file: true,
~ }~ _ => {~ let spec = if resolved_path.is_empty() {- format!("{}:", params.sha)+ format!("{sha}:")~ } else {- format!("{}:{}", params.sha, resolved_path)+ format!("{sha}:{resolved_path}")~ };~ Err(Error::NotFound(spec))~ }