wayver's git archive


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

src/utils/git/mod.rs@375565f690b958e08f589a7fee998ad5f47a70d0

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

1mod branch;
2mod commit;
3mod core;
4mod tag;
5mod tree;
6
7use std::path::Path;
8
9use git2::{Object, Signature};
10
11use crate::utils::error::{Context as _, Result};
12
13pub struct TagEntry {
14    pub link: String,
15    pub tag: String,
16    pub message: String,
17    pub signature: Signature<'static>,
18}
19
20impl TagEntry {
21    #[tracing::instrument(skip_all)]
22    fn try_from_commit(name: String, obj: &Object<'_>) -> Result<Self> {
23        Ok(Self {
24            link: format!("refs/{name}"),
25            tag: name,
26            message: String::new(),
27            signature: obj
28                .as_commit()
29                .context("git object is not a commit")?
30                .committer()
31                .to_owned(),
32        })
33    }
34
35    #[tracing::instrument(skip_all)]
36    fn try_from_tag(name: String, obj: &Object<'_>) -> Result<Self> {
37        let tag = obj.as_tag().context("git object is not a tag")?;
38
39        Ok(Self {
40            link: format!("refs/{name}"),
41            tag: name,
42            message: tag.message().unwrap_or("").to_string(),
43            signature: tag
44                .tagger()
45                .context("git tag does not have a tagger")
46                .or_else(|_| -> Result<Signature<'_>> {
47                    let signature = obj
48                        .peel_to_commit()
49                        .context("failed to peel object to commit")?
50                        .committer()
51                        .to_owned();
52
53                    Ok(signature)
54                })?
55                .to_owned(),
56        })
57    }
58}
59
60pub struct Repository {
61    inner: git2::Repository,
62}
63
64impl Repository {
65    #[tracing::instrument(skip_all)]
66    pub fn open<P>(path: P) -> Result<Option<Self>>
67    where
68        P: AsRef<Path>,
69    {
70        let config = crate::config();
71
72        let path = path.as_ref();
73
74        let path = config.project_root.join(path);
75
76        if !path.exists() {
77            return Ok(None);
78        }
79
80        let path = path.canonicalize().context("failed to canonicalize path")?;
81
82        if !path.starts_with(&config.project_root) {
83            tracing::warn!(
84                root=?config.project_root.display(),
85                requested=?path.display(),
86                "attempted path traversal",
87            );
88
89            return Ok(None);
90        }
91
92        if path == config.project_root {
93            return Ok(None);
94        }
95
96        if !path.exists() {
97            return Ok(None);
98        }
99
100        let inner = git2::Repository::open(path).context("failed to actually open the repo")?;
101
102        if !inner.path().join(&config.export_ok).exists() {
103            tracing::warn!("tried to access private repo");
104
105            return Ok(None);
106        }
107
108        Ok(Some(Self { inner }))
109    }
110
111    #[must_use]
112    pub const fn as_inner(&self) -> &git2::Repository {
113        &self.inner
114    }
115}
116