Skip to content

Commit 59bb3c4

Browse files
committed
feat: add Submodule type to represent a declared submodule.
1 parent a7c0880 commit 59bb3c4

File tree

29 files changed

+934
-61
lines changed

29 files changed

+934
-61
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crate-status.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,16 +726,16 @@ See its [README.md](https://github.com/Byron/gitoxide/blob/main/gix-lock/README.
726726
* [ ] a way to make changes to individual configuration files
727727
* [x] mailmap
728728
* [x] object replacements (`git replace`)
729-
* [ ] configuration
729+
* [x] read git configuration
730730
* [ ] merging
731731
* [ ] stashing
732732
* [ ] Use _Commit Graph_ to speed up certain queries
733733
* [ ] subtree
734734
* [ ] interactive rebase status/manipulation
735735
* **submodules**
736-
* [ ] handle 'old' form for reading
737-
* [ ] list
738-
* [ ] traverse recursively
736+
* [x] handle 'old' form for reading and detect old form
737+
* [x] list
738+
* [ ] edit
739739
* [ ] API documentation
740740
* [ ] Some examples
741741

gix/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ gix-worktree-state = { version = "^0.1.0", path = "../gix-worktree-state" }
166166
gix-hashtable = { version = "^0.2.4", path = "../gix-hashtable" }
167167
gix-commitgraph = { version = "^0.18.2", path = "../gix-commitgraph" }
168168
gix-pathspec = { version = "^0.1.0", path = "../gix-pathspec" }
169+
gix-submodule = { version = "^0.1.0", path = "../gix-submodule" }
169170

170171
gix-worktree-stream = { version = "^0.3.0", path = "../gix-worktree-stream", optional = true }
171172
gix-archive = { version = "^0.3.0", path = "../gix-archive", default-features = false, optional = true }

gix/src/config/cache/access.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,6 @@ impl Cache {
274274
Ok((state, buf))
275275
}
276276

277-
/// // TODO: let caller control if base-case-sensitivity should be inherited from the file-system if it isn't overridden in globals.
278277
pub(crate) fn pathspec_defaults(
279278
&self,
280279
) -> Result<gix_pathspec::Defaults, gix_pathspec::defaults::from_environment::Error> {

gix/src/config/tree/sections/fetch.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ impl Fetch {
1010
&config::Tree::FETCH,
1111
validate::NegotiationAlgorithm,
1212
);
13+
/// The `fetch.recurseSubmodules` key.
14+
pub const RECURSE_SUBMODULES: RecurseSubmodules =
15+
RecurseSubmodules::new_with_validate("recurseSubmodules", &config::Tree::FETCH, validate::RecurseSubmodules);
1316
}
1417

1518
impl Section for Fetch {
@@ -18,21 +21,27 @@ impl Section for Fetch {
1821
}
1922

2023
fn keys(&self) -> &[&dyn Key] {
21-
&[&Self::NEGOTIATION_ALGORITHM]
24+
&[&Self::NEGOTIATION_ALGORITHM, &Self::RECURSE_SUBMODULES]
2225
}
2326
}
2427

2528
/// The `fetch.negotiationAlgorithm` key.
2629
pub type NegotiationAlgorithm = keys::Any<validate::NegotiationAlgorithm>;
2730

31+
/// The `fetch.recurseSubmodules` key.
32+
pub type RecurseSubmodules = keys::Any<validate::RecurseSubmodules>;
33+
2834
mod algorithm {
2935
use std::borrow::Cow;
3036

3137
use gix_object::bstr::ByteSlice;
3238

3339
use crate::{
3440
bstr::BStr,
35-
config::{key::GenericErrorWithValue, tree::sections::fetch::NegotiationAlgorithm},
41+
config::{
42+
key::GenericErrorWithValue,
43+
tree::sections::fetch::{NegotiationAlgorithm, RecurseSubmodules},
44+
},
3645
remote::fetch::negotiate,
3746
};
3847

@@ -50,6 +59,16 @@ mod algorithm {
5059
})
5160
}
5261
}
62+
63+
impl RecurseSubmodules {
64+
/// Obtain the way submodules should be updated.
65+
pub fn try_into_recurse_submodules(
66+
&'static self,
67+
value: Result<bool, gix_config::value::Error>,
68+
) -> Result<gix_submodule::config::FetchRecurse, GenericErrorWithValue> {
69+
gix_submodule::config::FetchRecurse::new(value).map_err(|err| GenericErrorWithValue::from_value(self, err))
70+
}
71+
}
5372
}
5473

5574
mod validate {
@@ -65,4 +84,13 @@ mod validate {
6584
Ok(())
6685
}
6786
}
87+
88+
pub struct RecurseSubmodules;
89+
impl keys::Validate for RecurseSubmodules {
90+
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
91+
let boolean = gix_config::Boolean::try_from(value).map(|b| b.0);
92+
Fetch::RECURSE_SUBMODULES.try_into_recurse_submodules(boolean)?;
93+
Ok(())
94+
}
95+
}
6896
}

gix/src/lib.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,18 @@
5858
//!
5959
//! This section is a 'striving to be complete' mapping from `libgit2` APIs to the respective methods in `gix`.
6060
//!
61-
//! * [`git2::Repository::open()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open) ➡ [`gix::open()`](https://docs.rs/gix/*/gix/fn.open.html)
61+
//! * [`git2::Repository::open()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open) ➡ [`open()`]
6262
//! * [`git2::Repository::open_bare()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_bare) ➡ ❌
63-
//! * [`git2::Repository::open_from_env()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_from_env) ➡ [`gix::ThreadSafeRepository::open_with_environment_overrides()`](https://docs.rs/gix/*/gix/struct.ThreadSafeRepository.html#method.open_with_environment_overrides)
64-
//! * [`git2::Repository::open_ext()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_ext) ➡ [`gix::open_opts()`](https://docs.rs/gix/*/gix/fn.open_opts.html)
65-
//! * [`git2::Repository::revparse()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.revparse) ➡ [`gix::Repository::rev_parse()`](https://docs.rs/gix/*/gix/struct.Repository.html#method.rev_parse)
66-
//! * [`git2::Repository::revparse_single()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.revparse_single) ➡ [`gix::Repository::rev_parse_single()`](https://docs.rs/gix/*/gix/struct.Repository.html#method.rev_parse_single)
67-
//! * [`git2::Repository::revwalk()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.revwalk) ➡ [`gix::Repository::rev_walk()`](https://docs.rs/gix/*/gix/struct.Repository.html#method.rev_walk)
68-
//! * [`git2::build::CheckoutBuilder::disable_filters()](https://docs.rs/git2/0.17.2/git2/build/struct.CheckoutBuilder.html#method.disable_filters) ➡ ❌ *(filters are always applied during checkouts)*
69-
//! * [`git2::Odb::read_header()`](https://docs.rs/git2/0.17.2/git2/struct.Odb.html#method.read_header) ➡ [`gix::Repository::find_header()`](https://docs.rs/gix/*/gix/struct.Repository.html#method.find_header)
63+
//! * [`git2::Repository::open_from_env()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_from_env) ➡ [`ThreadSafeRepository::open_with_environment_overrides()`]
64+
//! * [`git2::Repository::open_ext()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_ext) ➡ [`open_opts()`]
65+
//! * [`git2::Repository::revparse()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.revparse) ➡ [`Repository::rev_parse()`]
66+
//! * [`git2::Repository::revparse_single()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.revparse_single) ➡ [`Repository::rev_parse_single()`]
67+
//! * [`git2::Repository::revwalk()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.revwalk) ➡ [`Repository::rev_walk()`]
68+
//! * [`git2::build::CheckoutBuilder::disable_filters()](https://docs.rs/git2/*/git2/build/struct.CheckoutBuilder.html#method.disable_filters) ➡ ❌ *(filters are always applied during checkouts)*
69+
//! * [`git2::Odb::read_header()`](https://docs.rs/git2/*/git2/struct.Odb.html#method.read_header) ➡ [`Repository::find_header()`]
70+
//! * [`git2::Repository::submodules()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.submodules) ➡ [`Repository::submodules()`]
71+
//! * [`git2::Repository::submodules()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.submodules) ➡ [`Repository::submodules()`]
72+
//! * [`git2::Repository::submodule_status()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.submodule_status) ➡ [`Submodule::state()`] - status provides more information and conveniences though, and an actual worktree status isn't performed.
7073
//!
7174
//! ## Feature Flags
7275
#![cfg_attr(
@@ -132,8 +135,8 @@ pub(crate) type Config = OwnShared<gix_config::File<'static>>;
132135

133136
mod types;
134137
pub use types::{
135-
Commit, Head, Id, Object, ObjectDetached, Pathspec, Reference, Remote, Repository, Tag, ThreadSafeRepository, Tree,
136-
Worktree,
138+
Commit, Head, Id, Object, ObjectDetached, Pathspec, Reference, Remote, Repository, Submodule, Tag,
139+
ThreadSafeRepository, Tree, Worktree,
137140
};
138141

139142
///
@@ -145,6 +148,7 @@ pub mod object;
145148
pub mod pathspec;
146149
pub mod reference;
147150
pub mod repository;
151+
pub mod submodule;
148152
pub mod tag;
149153

150154
///

gix/src/open/repository.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ impl ThreadSafeRepository {
291291
linked_worktree_options: options,
292292
index: gix_fs::SharedFileSnapshotMut::new().into(),
293293
shallow_commits: gix_fs::SharedFileSnapshotMut::new().into(),
294+
modules: gix_fs::SharedFileSnapshotMut::new().into(),
294295
})
295296
}
296297
}

gix/src/pathspec.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ pub mod init {
1010
#[derive(Debug, thiserror::Error)]
1111
#[allow(missing_docs)]
1212
pub enum Error {
13-
#[error("Filesystem configuration could not be obtained")]
14-
FilesystemConfig(#[from] crate::config::boolean::Error),
1513
#[error(transparent)]
1614
MakeAttributes(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
1715
#[error(transparent)]
18-
Defaults(#[from] gix_pathspec::defaults::from_environment::Error),
16+
Defaults(#[from] crate::repository::pathspec_defaults_ignore_case::Error),
1917
#[error(transparent)]
2018
ParseSpec(#[from] gix_pathspec::parse::Error),
2119
#[error(
@@ -45,10 +43,7 @@ impl<'repo> Pathspec<'repo> {
4543
inherit_ignore_case: bool,
4644
make_attributes: impl FnOnce() -> Result<gix_worktree::Stack, Box<dyn std::error::Error + Send + Sync + 'static>>,
4745
) -> Result<Self, init::Error> {
48-
let mut defaults = repo.pathspec_defaults()?;
49-
if inherit_ignore_case && repo.config.fs_capabilities()?.ignore_case {
50-
defaults.signature |= MagicSignature::ICASE;
51-
}
46+
let defaults = repo.pathspec_defaults_inherit_ignore_case(inherit_ignore_case)?;
5247
let patterns = patterns
5348
.into_iter()
5449
.map(move |p| parse(p.as_ref(), defaults))

gix/src/repository/impls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ impl Clone for crate::Repository {
99
self.options.clone(),
1010
self.index.clone(),
1111
self.shallow_commits.clone(),
12+
self.modules.clone(),
1213
)
1314
}
1415
}
@@ -42,6 +43,7 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository {
4243
repo.linked_worktree_options.clone(),
4344
repo.index.clone(),
4445
repo.shallow_commits.clone(),
46+
repo.modules.clone(),
4547
)
4648
}
4749
}
@@ -57,6 +59,7 @@ impl From<crate::ThreadSafeRepository> for crate::Repository {
5759
repo.linked_worktree_options,
5860
repo.index,
5961
repo.shallow_commits,
62+
repo.modules.clone(),
6063
)
6164
}
6265
}
@@ -71,6 +74,7 @@ impl From<crate::Repository> for crate::ThreadSafeRepository {
7174
config: r.config,
7275
linked_worktree_options: r.options,
7376
index: r.index,
77+
modules: r.modules,
7478
shallow_commits: r.shallow_commits,
7579
}
7680
}

gix/src/repository/index.rs

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,35 @@ impl crate::Repository {
3333
///
3434
/// The index file is shared across all clones of this repository.
3535
pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> {
36-
self.index
37-
.recent_snapshot(
38-
|| self.index_path().metadata().and_then(|m| m.modified()).ok(),
39-
|| {
40-
self.open_index().map(Some).or_else(|err| match err {
41-
worktree::open_index::Error::IndexFile(gix_index::file::init::Error::Io(err))
42-
if err.kind() == std::io::ErrorKind::NotFound =>
43-
{
44-
Ok(None)
45-
}
46-
err => Err(err),
47-
})
48-
},
49-
)
50-
.and_then(|opt| match opt {
51-
Some(index) => Ok(index),
52-
None => Err(worktree::open_index::Error::IndexFile(
53-
gix_index::file::init::Error::Io(std::io::Error::new(
54-
std::io::ErrorKind::NotFound,
55-
format!("Could not find index file at {:?} for opening.", self.index_path()),
56-
)),
36+
self.try_index().and_then(|opt| match opt {
37+
Some(index) => Ok(index),
38+
None => Err(worktree::open_index::Error::IndexFile(
39+
gix_index::file::init::Error::Io(std::io::Error::new(
40+
std::io::ErrorKind::NotFound,
41+
format!("Could not find index file at {:?} for opening.", self.index_path()),
5742
)),
58-
})
43+
)),
44+
})
45+
}
46+
47+
/// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file
48+
/// on disk has changed, or `None` if no such file exists.
49+
///
50+
/// The index file is shared across all clones of this repository.
51+
pub fn try_index(&self) -> Result<Option<worktree::Index>, worktree::open_index::Error> {
52+
self.index.recent_snapshot(
53+
|| self.index_path().metadata().and_then(|m| m.modified()).ok(),
54+
|| {
55+
self.open_index().map(Some).or_else(|err| match err {
56+
worktree::open_index::Error::IndexFile(gix_index::file::init::Error::Io(err))
57+
if err.kind() == std::io::ErrorKind::NotFound =>
58+
{
59+
Ok(None)
60+
}
61+
err => Err(err),
62+
})
63+
},
64+
)
5965
}
6066

6167
/// Open the persisted worktree index or generate it from the current `HEAD^{tree}` to live in-memory only.
@@ -69,13 +75,12 @@ impl crate::Repository {
6975
pub fn index_or_load_from_head(
7076
&self,
7177
) -> Result<IndexPersistedOrInMemory, crate::repository::index_or_load_from_head::Error> {
72-
Ok(match self.index() {
73-
Ok(index) => IndexPersistedOrInMemory::Persisted(index),
74-
Err(worktree::open_index::Error::IndexFile(_)) => {
78+
Ok(match self.try_index()? {
79+
Some(index) => IndexPersistedOrInMemory::Persisted(index),
80+
None => {
7581
let tree = self.head_commit()?.tree_id()?;
7682
IndexPersistedOrInMemory::InMemory(self.index_from_tree(&tree)?)
7783
}
78-
Err(err) => return Err(err.into()),
7984
})
8085
}
8186

0 commit comments

Comments
 (0)