wayver's git archive


a simple self-hosted git server
git clone https://git.wayver.dev/bile

src/utils/git/core.rs@bd1a8f79badd439ab7a73e193be91ad175aafa67

raw
Date Commit Message Author Files + -
2026-02-17 21:07 initial mvp wayverd 74 10800 0
...

1use std::path::Path;
2
3use git2::{Reference, Time};
4
5use crate::utils::{
6    error::{Context as _, Result},
7    git::Repository,
8};
9
10impl Repository {
11    #[must_use]
12    pub fn description(&self) -> String {
13        let content = std::fs::read_to_string(self.path().join("description")).unwrap_or_default();
14
15        let first = content.lines().next().unwrap_or_default();
16
17        first.to_string()
18    }
19
20    #[tracing::instrument(skip_all)]
21    pub fn head(&self) -> Result<Reference<'_>> {
22        let head = self.inner.head()?;
23
24        Ok(head)
25    }
26
27    #[tracing::instrument(skip_all)]
28    pub fn is_empty(&self) -> Result<bool> {
29        Ok(self.inner.is_empty()?)
30    }
31
32    #[must_use]
33    pub fn is_shallow(&self) -> bool {
34        self.inner.is_shallow()
35    }
36
37    #[tracing::instrument(skip_all)]
38    pub fn last_modified(&self) -> Result<Time> {
39        let head = self.head()?;
40        let commit = head.peel_to_commit()?;
41        let time = commit.committer().when();
42
43        Ok(time)
44    }
45
46    pub fn name(&self) -> Option<&str> {
47        self.inner
48            .workdir()
49            // use the path for bare repositories
50            .unwrap_or_else(|| self.inner.path())
51            .file_name()
52            .and_then(std::ffi::OsStr::to_str)
53    }
54
55    #[must_use]
56    pub fn owner(&self) -> String {
57        self.inner
58            .config()
59            .and_then(|config| config.get_string("gitweb.owner"))
60            .unwrap_or_default()
61    }
62
63    #[must_use]
64    pub fn path(&self) -> &Path {
65        self.inner.path()
66    }
67
68    #[must_use]
69    pub fn readme(&self) -> String {
70        use askama::filters::Escaper as _;
71
72        enum ReadmeFormat {
73            Plaintext,
74            Html,
75            Markdown,
76        }
77
78        let mut format = ReadmeFormat::Plaintext;
79
80        self.inner
81            .revparse_single("HEAD:readme")
82            .or_else(|_| self.inner.revparse_single("HEAD:README.txt"))
83            .or_else(|_| self.inner.revparse_single("HEAD:readme.txt"))
84            .or_else(|_| self.inner.revparse_single("HEAD:README.txt"))
85            .or_else(|_| {
86                format = ReadmeFormat::Markdown;
87                self.inner.revparse_single("HEAD:readme.md")
88            })
89            .or_else(|_| self.inner.revparse_single("HEAD:README.mdown"))
90            .or_else(|_| self.inner.revparse_single("HEAD:readme.mdown"))
91            .or_else(|_| self.inner.revparse_single("HEAD:README.mdown"))
92            .or_else(|_| self.inner.revparse_single("HEAD:readme.markdown"))
93            .or_else(|_| self.inner.revparse_single("HEAD:README.markdown"))
94            .or_else(|_| {
95                format = ReadmeFormat::Html;
96                self.inner.revparse_single("HEAD:readme.html")
97            })
98            .or_else(|_| self.inner.revparse_single("HEAD:README.html"))
99            .or_else(|_| self.inner.revparse_single("HEAD:readme.htm"))
100            .or_else(|_| self.inner.revparse_single("HEAD:README.htm"))
101            .ok()
102            .and_then(|readme| readme.into_blob().ok())
103            .map(|blob| {
104                let text = str::from_utf8(blob.content()).unwrap_or_default();
105
106                // render the file contents to HTML
107                match format {
108                    // render plaintext as preformatted text
109                    ReadmeFormat::Plaintext => {
110                        let mut output = "<pre>".to_string();
111                        if let Err(err) = askama::filters::Html.write_escaped_str(&mut output, text)
112                        {
113                            tracing::error!(err=?err, "failed to write escaped plaintext readme");
114                        }
115                        output.push_str("</pre>");
116                        output
117                    }
118                    // already is HTML
119                    ReadmeFormat::Html => text.to_string(),
120                    // render Markdown to HTML
121                    ReadmeFormat::Markdown => crate::utils::markdown::render(text),
122                }
123            })
124            .unwrap_or_default()
125    }
126
127    #[tracing::instrument(skip_all)]
128    pub fn ref_or_head_shorthand(&self, r#ref: Option<&str>) -> Result<String> {
129        let head = self.head()?;
130
131        let spec = r#ref
132            .map(str::to_string)
133            .or_else(move || head.shorthand().map(str::to_string))
134            .context("failed to get repo ref spec")?;
135
136        Ok(spec)
137    }
138}
139