wayver's git archive


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

src/git/mod.rs@32c6bed79b26b918b19bcaa6d2f5eaf797bbeb92

raw
Date Commit Message Author Files + -
2026-02-19 17:51 large refactoring wayverd 53 2153 1683
...

1mod branch;
2mod commit;
3mod core;
4mod tag;
5mod tree;
6
7use std::path::{Path, PathBuf};
8
9use git2::{Object, Signature};
10
11use crate::{config::Config, error::Context as _, error::Result, http::extractor::RepoName};
12
13pub(crate) 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(crate) struct Repository {
61    inner: git2::Repository,
62}
63
64impl Repository {
65    #[tracing::instrument(skip_all)]
66    pub(crate) fn open(config: &Config, name: &RepoName) -> Result<Option<Self>> {
67        Self::open_path(config, &PathBuf::from(&name.0))
68    }
69
70    pub(crate) fn open_path(config: &Config, path: &Path) -> Result<Option<Self>> {
71        let path = config.project_root.join(path);
72
73        if !path.exists() {
74            return Ok(None);
75        }
76
77        let path = path.canonicalize().context("failed to canonicalize path")?;
78
79        if !path.starts_with(&config.project_root) {
80            tracing::warn!(
81                root=?config.project_root.display(),
82                requested=?path.display(),
83                "attempted path traversal",
84            );
85
86            return Ok(None);
87        }
88
89        if path == config.project_root {
90            return Ok(None);
91        }
92
93        if !path.exists() {
94            return Ok(None);
95        }
96
97        let inner = git2::Repository::open(path).context("failed to actually open the repo")?;
98
99        if !inner.path().join(&config.export_ok).exists() {
100            tracing::warn!("tried to access private repo");
101
102            return Ok(None);
103        }
104
105        Ok(Some(Self { inner }))
106    }
107
108    #[must_use]
109    pub(crate) const fn as_inner(&self) -> &git2::Repository {
110        &self.inner
111    }
112}
113