wok_dev/
repo.rs

1use std::{fmt, path};
2
3use anyhow::*;
4
5pub struct Repo {
6    git_repo: git2::Repository,
7    pub work_dir: path::PathBuf,
8    pub head: String,
9    pub subrepos: Vec<Repo>,
10}
11
12impl Repo {
13    pub fn new(work_dir: &path::Path, head_name: Option<&str>) -> Result<Self> {
14        println!("Reading repo at `{}`", work_dir.display());
15
16        let git_repo = git2::Repository::open(work_dir)
17            .with_context(|| format!("Cannot open repo at `{}`", work_dir.display()))?;
18
19        let head = match head_name {
20            Some(name) => String::from(name),
21            None => {
22                if git_repo.head_detached().with_context(|| {
23                    format!(
24                        "Cannot determine head state for repo at `{}`",
25                        work_dir.display()
26                    )
27                })? {
28                    bail!(
29                        "Cannot operate on a detached head for repo at `{}`",
30                        work_dir.display()
31                    )
32                }
33
34                String::from(git_repo.head().with_context(|| {
35                    format!(
36                        "Cannot find the head branch for repo at `{}`. Is it detached?",
37                        work_dir.display()
38                    )
39                })?.shorthand().with_context(|| {
40                    format!(
41                        "Cannot find a human readable representation of the head ref for repo at `{}`",
42                        work_dir.display(),
43                    )
44                })?)
45            },
46        };
47
48        let subrepos = git_repo
49            .submodules()
50            .with_context(|| {
51                format!(
52                    "Cannot load submodules for repo at `{}`",
53                    work_dir.display()
54                )
55            })?
56            .iter()
57            .map(|submodule| Repo::new(&work_dir.join(submodule.path()), Some(&head)))
58            .collect::<Result<Vec<Repo>>>()?;
59
60        println!("Successfully read repo at `{}`", work_dir.display());
61
62        Ok(Repo {
63            git_repo,
64            work_dir: path::PathBuf::from(work_dir),
65            head,
66            subrepos,
67        })
68    }
69
70    pub fn get_subrepo_by_path(&self, subrepo_path: &path::PathBuf) -> Option<&Repo> {
71        self.subrepos
72            .iter()
73            .find(|subrepo| (subrepo.work_dir == self.work_dir.join(subrepo_path)))
74    }
75
76    pub fn sync(&self) -> Result<()> {
77        self.switch(&self.head)?;
78        Ok(())
79    }
80
81    pub fn switch(&self, head: &str) -> Result<()> {
82        self.git_repo.set_head(&self.resolve_reference(head)?)?;
83        self.git_repo.checkout_head(None)?;
84        Ok(())
85    }
86
87    fn resolve_reference(&self, short_name: &str) -> Result<String> {
88        Ok(self
89            .git_repo
90            .resolve_reference_from_short_name(short_name)?
91            .name()
92            .with_context(|| {
93                format!(
94                    "Cannot resolve head reference for repo at `{}`",
95                    self.work_dir.display()
96                )
97            })?
98            .to_owned())
99    }
100}
101
102impl fmt::Debug for Repo {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        f.debug_struct("Repo")
105            .field("work_dir", &self.work_dir)
106            .field("head", &self.head)
107            .field("subrepos", &self.subrepos)
108            .finish()
109    }
110}