Skip to content

Commit 986d34a

Browse files
authored
support opening submodule (#1298)
1 parent aa9ed33 commit 986d34a

18 files changed

+392
-51
lines changed

Cargo.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
- Browse commit log, diff committed changes
5252
- Scalable terminal UI layout
5353
- Async git API for fluid control
54+
- Submodule support
5455

5556
## 2. <a name="motivation"></a> Motivation <small><sup>[Top ▲](#table-of-contents)</sup></small>
5657

asyncgit/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ unicode-truncate = "0.2.0"
2828
url = "2.2"
2929

3030
[dev-dependencies]
31+
env_logger = "0.9"
3132
invalidstring = { path = "../invalidstring", version = "0.1" }
3233
pretty_assertions = "1.3"
3334
serial_test = "0.9"

asyncgit/src/error.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#![allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
22

3-
use std::{num::TryFromIntError, string::FromUtf8Error};
3+
use std::{
4+
num::TryFromIntError, path::StripPrefixError,
5+
string::FromUtf8Error,
6+
};
47
use thiserror::Error;
58

69
///
@@ -50,6 +53,10 @@ pub enum Error {
5053
#[error("git error:{0}")]
5154
Git(#[from] git2::Error),
5255

56+
///
57+
#[error("strip prefix error: {0}")]
58+
StripPrefix(#[from] StripPrefixError),
59+
5360
///
5461
#[error("utf8 error:{0}")]
5562
Utf8Conversion(#[from] FromUtf8Error),

asyncgit/src/sync/mod.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ pub use stash::{
8282
pub use state::{repo_state, RepoState};
8383
pub use status::is_workdir_clean;
8484
pub use submodules::{
85-
get_submodules, update_submodule, SubmoduleInfo, SubmoduleStatus,
85+
get_submodules, submodule_parent_info, update_submodule,
86+
SubmoduleInfo, SubmoduleParentInfo, SubmoduleStatus,
8687
};
8788
pub use tags::{
8889
delete_tag, get_tags, get_tags_with_metadata, CommitTags, Tag,
@@ -209,6 +210,8 @@ mod tests {
209210

210211
///
211212
pub fn repo_init_empty() -> Result<(TempDir, Repository)> {
213+
init_log();
214+
212215
sandbox_config_files();
213216

214217
let td = TempDir::new()?;
@@ -223,6 +226,8 @@ mod tests {
223226

224227
///
225228
pub fn repo_init() -> Result<(TempDir, Repository)> {
229+
init_log();
230+
226231
sandbox_config_files();
227232

228233
let td = TempDir::new()?;
@@ -266,8 +271,18 @@ mod tests {
266271
Ok((td, repo))
267272
}
268273

274+
// init log
275+
fn init_log() {
276+
let _ = env_logger::builder()
277+
.is_test(true)
278+
.filter_level(log::LevelFilter::Trace)
279+
.try_init();
280+
}
281+
269282
/// Same as repo_init, but the repo is a bare repo (--bare)
270283
pub fn repo_init_bare() -> Result<(TempDir, Repository)> {
284+
init_log();
285+
271286
let tmp_repo_dir = TempDir::new()?;
272287
let bare_repo = Repository::init_bare(tmp_repo_dir.path())?;
273288
Ok((tmp_repo_dir, bare_repo))

asyncgit/src/sync/repository.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::error::Result;
1111
pub type RepoPathRef = RefCell<RepoPath>;
1212

1313
///
14-
#[derive(Clone)]
14+
#[derive(Clone, Debug)]
1515
pub enum RepoPath {
1616
///
1717
Path(PathBuf),

asyncgit/src/sync/submodules.rs

+136-19
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
use std::path::PathBuf;
1+
//TODO:
2+
// #![allow(unused_variables, dead_code)]
23

3-
use git2::SubmoduleUpdateOptions;
4+
use std::path::{Path, PathBuf};
5+
6+
use git2::{
7+
Repository, RepositoryOpenFlags, Submodule,
8+
SubmoduleUpdateOptions,
9+
};
410
use scopetime::scope_time;
511

612
use super::{repo, CommitId, RepoPath};
7-
use crate::{error::Result, Error};
13+
use crate::{error::Result, sync::utils::work_dir, Error};
814

915
pub use git2::SubmoduleStatus;
1016

1117
///
18+
#[derive(Debug)]
1219
pub struct SubmoduleInfo {
20+
///
21+
pub name: String,
1322
///
1423
pub path: PathBuf,
1524
///
@@ -22,6 +31,17 @@ pub struct SubmoduleInfo {
2231
pub status: SubmoduleStatus,
2332
}
2433

34+
///
35+
#[derive(Debug)]
36+
pub struct SubmoduleParentInfo {
37+
/// where to find parent repo
38+
pub parent_gitpath: PathBuf,
39+
/// where to find submodule git path
40+
pub submodule_gitpath: PathBuf,
41+
/// `submodule_info` from perspective of parent repo
42+
pub submodule_info: SubmoduleInfo,
43+
}
44+
2545
impl SubmoduleInfo {
2646
///
2747
pub fn get_repo_path(
@@ -35,6 +55,24 @@ impl SubmoduleInfo {
3555
}
3656
}
3757

58+
fn submodule_to_info(s: &Submodule, r: &Repository) -> SubmoduleInfo {
59+
let status = r
60+
.submodule_status(
61+
s.name().unwrap_or_default(),
62+
git2::SubmoduleIgnore::None,
63+
)
64+
.unwrap_or(SubmoduleStatus::empty());
65+
66+
SubmoduleInfo {
67+
name: s.name().unwrap_or_default().into(),
68+
path: s.path().to_path_buf(),
69+
id: s.workdir_id().map(CommitId::from),
70+
head_id: s.head_id().map(CommitId::from),
71+
url: s.url().map(String::from),
72+
status,
73+
}
74+
}
75+
3876
///
3977
pub fn get_submodules(
4078
repo_path: &RepoPath,
@@ -46,22 +84,7 @@ pub fn get_submodules(
4684
let res = r
4785
.submodules()?
4886
.iter()
49-
.map(|s| {
50-
let status = repo2
51-
.submodule_status(
52-
s.name().unwrap_or_default(),
53-
git2::SubmoduleIgnore::None,
54-
)
55-
.unwrap_or(SubmoduleStatus::empty());
56-
57-
SubmoduleInfo {
58-
path: s.path().to_path_buf(),
59-
id: s.workdir_id().map(CommitId::from),
60-
head_id: s.head_id().map(CommitId::from),
61-
url: s.url().map(String::from),
62-
status,
63-
}
64-
})
87+
.map(|s| submodule_to_info(s, &repo2))
6588
.collect();
6689

6790
Ok(res)
@@ -82,3 +105,97 @@ pub fn update_submodule(
82105

83106
Ok(())
84107
}
108+
109+
/// query whether `repo_path` points to a repo that is part of a parent git which contains it as a submodule
110+
pub fn submodule_parent_info(
111+
repo_path: &RepoPath,
112+
) -> Result<Option<SubmoduleParentInfo>> {
113+
scope_time!("submodule_parent_info");
114+
115+
let repo = repo(repo_path)?;
116+
let repo_wd = work_dir(&repo)?.to_path_buf();
117+
118+
log::trace!("[sub] repo_wd: {:?}", repo_wd);
119+
log::trace!("[sub] repo_path: {:?}", repo.path());
120+
121+
if let Some(parent_path) = repo_wd.parent() {
122+
log::trace!("[sub] parent_path: {:?}", parent_path);
123+
124+
if let Ok(parent) = Repository::open_ext(
125+
parent_path,
126+
RepositoryOpenFlags::empty(),
127+
Vec::<&Path>::new(),
128+
) {
129+
let parent_wd = work_dir(&parent)?.to_path_buf();
130+
log::trace!("[sub] parent_wd: {:?}", parent_wd);
131+
132+
let submodule_name = repo_wd
133+
.strip_prefix(parent_wd)?
134+
.to_string_lossy()
135+
.to_string();
136+
137+
log::trace!("[sub] submodule_name: {:?}", submodule_name);
138+
139+
if let Ok(submodule) =
140+
parent.find_submodule(&submodule_name)
141+
{
142+
return Ok(Some(SubmoduleParentInfo {
143+
parent_gitpath: parent.path().to_path_buf(),
144+
submodule_gitpath: repo.path().to_path_buf(),
145+
submodule_info: submodule_to_info(
146+
&submodule, &parent,
147+
),
148+
}));
149+
}
150+
}
151+
}
152+
153+
Ok(None)
154+
}
155+
156+
#[cfg(test)]
157+
mod tests {
158+
use super::get_submodules;
159+
use crate::sync::{
160+
submodules::submodule_parent_info, tests::repo_init, RepoPath,
161+
};
162+
use git2::Repository;
163+
use pretty_assertions::assert_eq;
164+
use std::path::Path;
165+
166+
#[test]
167+
fn test_smoke() {
168+
let (dir, _r) = repo_init().unwrap();
169+
170+
{
171+
let r = Repository::open(dir.path()).unwrap();
172+
let mut s = r
173+
.submodule(
174+
//TODO: use local git
175+
"https://github.com/extrawurst/brewdump.git",
176+
Path::new("foo/bar"),
177+
false,
178+
)
179+
.unwrap();
180+
181+
let _sub_r = s.clone(None).unwrap();
182+
s.add_finalize().unwrap();
183+
}
184+
185+
let repo_p = RepoPath::Path(dir.into_path());
186+
let subs = get_submodules(&repo_p).unwrap();
187+
188+
assert_eq!(subs.len(), 1);
189+
assert_eq!(&subs[0].name, "foo/bar");
190+
191+
let info = submodule_parent_info(
192+
&subs[0].get_repo_path(&repo_p).unwrap(),
193+
)
194+
.unwrap()
195+
.unwrap();
196+
197+
dbg!(&info);
198+
199+
assert_eq!(&info.submodule_info.name, "foo/bar");
200+
}
201+
}

0 commit comments

Comments
 (0)