gnostr_asyncgit/sync/
submodules.rs1use std::path::{Path, PathBuf};
2
3pub use git2::SubmoduleStatus;
4use git2::{Repository, RepositoryOpenFlags, Submodule, SubmoduleUpdateOptions};
5use scopetime::scope_time;
6
7use super::{repo, CommitId, RepoPath};
8use crate::{error::Result, sync::utils::work_dir, Error};
9
10#[derive(Debug)]
12pub struct SubmoduleInfo {
13 pub name: String,
15 pub path: PathBuf,
17 pub url: Option<String>,
19 pub id: Option<CommitId>,
21 pub head_id: Option<CommitId>,
23 pub status: SubmoduleStatus,
25}
26
27#[derive(Debug)]
29pub struct SubmoduleParentInfo {
30 pub parent_gitpath: PathBuf,
32 pub submodule_gitpath: PathBuf,
34 pub submodule_info: SubmoduleInfo,
36}
37
38impl SubmoduleInfo {
39 pub fn get_repo_path(&self, repo_path: &RepoPath) -> Result<RepoPath> {
41 let repo = repo(repo_path)?;
42 let wd = repo.workdir().ok_or(Error::NoWorkDir)?;
43
44 Ok(RepoPath::Path(wd.join(self.path.clone())))
45 }
46}
47
48fn submodule_to_info(s: &Submodule, r: &Repository) -> SubmoduleInfo {
49 let status = r
50 .submodule_status(s.name().unwrap_or_default(), git2::SubmoduleIgnore::None)
51 .unwrap_or(SubmoduleStatus::empty());
52
53 SubmoduleInfo {
54 name: s.name().unwrap_or_default().into(),
55 path: s.path().to_path_buf(),
56 id: s.workdir_id().map(CommitId::from),
57 head_id: s.head_id().map(CommitId::from),
58 url: s.url().map(String::from),
59 status,
60 }
61}
62
63pub fn get_submodules(repo_path: &RepoPath) -> Result<Vec<SubmoduleInfo>> {
65 scope_time!("get_submodules");
66
67 let (r, repo2) = (repo(repo_path)?, repo(repo_path)?);
68
69 let res = r
70 .submodules()?
71 .iter()
72 .map(|s| submodule_to_info(s, &repo2))
73 .collect();
74
75 Ok(res)
76}
77
78pub fn update_submodule(repo_path: &RepoPath, name: &str) -> Result<()> {
80 scope_time!("update_submodule");
81
82 let repo = repo(repo_path)?;
83
84 let mut submodule = repo.find_submodule(name)?;
85
86 let mut options = SubmoduleUpdateOptions::new();
87 options.allow_fetch(true);
88
89 submodule.update(true, Some(&mut options))?;
90
91 Ok(())
92}
93
94pub fn submodule_parent_info(repo_path: &RepoPath) -> Result<Option<SubmoduleParentInfo>> {
97 scope_time!("submodule_parent_info");
98
99 let repo = repo(repo_path)?;
100 let repo_wd = work_dir(&repo)?.to_path_buf();
101
102 log::trace!("[sub] repo_wd: {:?}", repo_wd);
103 log::trace!("[sub] repo_path: {:?}", repo.path());
104
105 if let Some(parent_path) = repo_wd.parent() {
106 log::trace!("[sub] parent_path: {:?}", parent_path);
107
108 if let Ok(parent) = Repository::open_ext(
109 parent_path,
110 RepositoryOpenFlags::empty(),
111 Vec::<&Path>::new(),
112 ) {
113 let parent_wd = work_dir(&parent)?.to_path_buf();
114 log::trace!("[sub] parent_wd: {:?}", parent_wd);
115
116 let submodule_name = repo_wd
117 .strip_prefix(parent_wd)?
118 .to_string_lossy()
119 .to_string();
120
121 log::trace!("[sub] submodule_name: {:?}", submodule_name);
122
123 if let Ok(submodule) = parent.find_submodule(&submodule_name) {
124 return Ok(Some(SubmoduleParentInfo {
125 parent_gitpath: parent.path().to_path_buf(),
126 submodule_gitpath: repo.path().to_path_buf(),
127 submodule_info: submodule_to_info(&submodule, &parent),
128 }));
129 }
130 }
131 }
132
133 Ok(None)
134}
135
136#[cfg(test)]
137mod tests {
138 use std::path::Path;
139
140 use git2::Repository;
141 use pretty_assertions::assert_eq;
142
143 use super::get_submodules;
144 use crate::sync::{submodules::submodule_parent_info, tests::repo_init, RepoPath};
145
146 #[test]
147 fn test_smoke() {
148 let (dir, _r) = repo_init().unwrap();
149
150 {
151 let r = Repository::open(dir.path()).unwrap();
152 let mut s = r
153 .submodule(
154 "https://github.com/extrawurst/brewdump.git",
156 Path::new("foo/bar"),
157 false,
158 )
159 .unwrap();
160
161 let _sub_r = s.clone(None).unwrap();
162 s.add_finalize().unwrap();
163 }
164
165 let repo_p = RepoPath::Path(dir.into_path());
166 let subs = get_submodules(&repo_p).unwrap();
167
168 assert_eq!(subs.len(), 1);
169 assert_eq!(&subs[0].name, "foo/bar");
170
171 let info = submodule_parent_info(&subs[0].get_repo_path(&repo_p).unwrap())
172 .unwrap()
173 .unwrap();
174
175 dbg!(&info);
176
177 assert_eq!(&info.submodule_info.name, "foo/bar");
178 }
179}