Skip to content

Commit 1ccbe16

Browse files
committed
feat: gix submodule subcommand for simple submodule listing and information retrieval
1 parent f4a9a6b commit 1ccbe16

File tree

5 files changed

+104
-4
lines changed

5 files changed

+104
-4
lines changed

gitoxide-core/src/repository/index/entries.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,11 @@ pub(crate) mod function {
4848
recurse_submodules,
4949
}: Options,
5050
) -> anyhow::Result<()> {
51-
use crate::OutputFormat::*;
5251
let mut out = BufWriter::with_capacity(64 * 1024, out);
5352
let mut all_attrs = statistics.then(BTreeSet::new);
5453

5554
#[cfg(feature = "serde")]
56-
if let Json = format {
55+
if let OutputFormat::Json = format {
5756
out.write_all(b"[\n")?;
5857
}
5958

@@ -70,13 +69,14 @@ pub(crate) mod function {
7069
)?;
7170

7271
#[cfg(feature = "serde")]
73-
if format == Json {
72+
if format == OutputFormat::Json {
7473
out.write_all(b"]\n")?;
7574
out.flush()?;
7675
if statistics {
7776
serde_json::to_writer_pretty(&mut err, &stats)?;
7877
}
79-
} else if format == Human && statistics {
78+
}
79+
if format == OutputFormat::Human && statistics {
8080
out.flush()?;
8181
writeln!(err, "{stats:#?}")?;
8282
if let Some(attrs) = all_attrs.filter(|a| !a.is_empty()) {

gitoxide-core/src/repository/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ pub mod mailmap;
4040
pub mod odb;
4141
pub mod remote;
4242
pub mod revision;
43+
pub mod submodule;
4344
pub mod tree;
4445
pub mod verify;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::OutputFormat;
2+
use anyhow::bail;
3+
use gix::commit::describe::SelectRef;
4+
use gix::prelude::ObjectIdExt;
5+
use gix::{Repository, Submodule};
6+
7+
pub fn list(repo: Repository, mut out: impl std::io::Write, format: OutputFormat) -> anyhow::Result<()> {
8+
if format != OutputFormat::Human {
9+
bail!("Only human output is supported for now")
10+
}
11+
12+
let Some(submodules) = repo.submodules()? else { return Ok(()) };
13+
for sm in submodules {
14+
print_sm(sm, &mut out)?;
15+
}
16+
Ok(())
17+
}
18+
19+
fn print_sm(sm: Submodule<'_>, out: &mut impl std::io::Write) -> anyhow::Result<()> {
20+
let _span = gix::trace::coarse!("print_sm", path = ?sm.path());
21+
let state = sm.state()?;
22+
let mut sm_repo = sm.open()?;
23+
if let Some(repo) = sm_repo.as_mut() {
24+
repo.object_cache_size_if_unset(4 * 1024 * 1024);
25+
};
26+
writeln!(
27+
out,
28+
" {is_active} {path} {config} head:{head_id} index:{index_id} ({worktree}) [{url}]",
29+
is_active = if !sm.is_active()? || !state.repository_exists {
30+
"ⅹ"
31+
} else {
32+
"✓"
33+
},
34+
path = sm.path()?,
35+
config = if state.superproject_configuration {
36+
"config:yes"
37+
} else {
38+
"config:no"
39+
},
40+
head_id = submodule_short_hash(sm.head_id()?, sm_repo.as_ref()),
41+
index_id = submodule_short_hash(sm.index_id()?, sm_repo.as_ref()),
42+
worktree = match sm_repo {
43+
Some(repo) => {
44+
// TODO(name-revision): this is the simple version, `git` gives it
45+
// multiple tries https://github.com/git/git/blob/fac96dfbb1c24369ba7d37a5affd8adfe6c650fd/builtin/submodule--helper.c#L161
46+
// and even uses `git name-rev`/`git describe --contains` which we can't do yet.
47+
repo.head_commit()?
48+
.describe()
49+
.names(SelectRef::AllRefs)
50+
.format()?
51+
.to_string()
52+
}
53+
None => {
54+
"no worktree".to_string()
55+
}
56+
},
57+
url = sm.url()?.to_bstring()
58+
)?;
59+
Ok(())
60+
}
61+
62+
fn submodule_short_hash(id: Option<gix::ObjectId>, repo: Option<&Repository>) -> String {
63+
id.map_or_else(
64+
|| "none".to_string(),
65+
|id| repo.map_or_else(|| id.to_string(), |repo| id.attach(repo).shorten_or_id().to_string()),
66+
)
67+
}

src/plumbing/main.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,20 @@ pub fn main() -> Result<()> {
135135
})?;
136136

137137
match cmd {
138+
Subcommands::Submodule(platform) => match platform
139+
.cmds
140+
.unwrap_or(crate::plumbing::options::submodule::Subcommands::List)
141+
{
142+
crate::plumbing::options::submodule::Subcommands::List => prepare_and_run(
143+
"submodule-list",
144+
trace,
145+
verbose,
146+
progress,
147+
progress_keep_open,
148+
None,
149+
move |_progress, out, _err| core::repository::submodule::list(repository(Mode::Lenient)?, out, format),
150+
),
151+
},
138152
#[cfg(feature = "gitoxide-core-tools-archive")]
139153
Subcommands::Archive(crate::plumbing::options::archive::Platform {
140154
format,

src/plumbing/options/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ pub enum Subcommands {
122122
Exclude(exclude::Subcommands),
123123
#[clap(subcommand)]
124124
Index(index::Subcommands),
125+
/// Interact with submodules.
126+
#[clap(alias = "submodules")]
127+
Submodule(submodule::Platform),
125128
/// Show which git configuration values are used or planned.
126129
ConfigTree,
127130
Config(config::Platform),
@@ -708,5 +711,20 @@ pub mod index {
708711
}
709712
}
710713

714+
pub mod submodule {
715+
716+
#[derive(Debug, clap::Parser)]
717+
pub struct Platform {
718+
#[clap(subcommand)]
719+
pub cmds: Option<Subcommands>,
720+
}
721+
722+
#[derive(Debug, clap::Subcommand)]
723+
pub enum Subcommands {
724+
/// Print all direct submodules to standard output
725+
List,
726+
}
727+
}
728+
711729
///
712730
pub mod free;

0 commit comments

Comments
 (0)