diff --git a/gitoxide-core/src/repository/mod.rs b/gitoxide-core/src/repository/mod.rs index 8158f7cf0b6..bf3f5fe0e6d 100644 --- a/gitoxide-core/src/repository/mod.rs +++ b/gitoxide-core/src/repository/mod.rs @@ -44,6 +44,7 @@ pub mod remote; pub mod revision; pub mod status; pub mod submodule; +pub mod tag; pub mod tree; pub mod verify; pub mod worktree; diff --git a/gitoxide-core/src/repository/tag.rs b/gitoxide-core/src/repository/tag.rs new file mode 100644 index 00000000000..2bc109020e5 --- /dev/null +++ b/gitoxide-core/src/repository/tag.rs @@ -0,0 +1,33 @@ +pub fn list(repo: gix::Repository, out: &mut dyn std::io::Write) -> anyhow::Result<()> { + let platform = repo.references()?; + + for mut reference in platform.tags()?.flatten() { + let tag = reference.peel_to_tag(); + let tag_ref = tag.as_ref().map(gix::Tag::decode); + + // `name` is the name of the file in `refs/tags/`. + // This applies to both lightweight and annotated tags. + let name = reference.name().shorten(); + let mut fields = Vec::new(); + match tag_ref { + Ok(Ok(tag_ref)) => { + // `tag_name` is the name provided by the user via `git tag -a/-s/-u`. + // It is only present for annotated tags. + fields.push(format!( + "tag name: {}", + if name == tag_ref.name { "*".into() } else { tag_ref.name } + )); + if tag_ref.pgp_signature.is_some() { + fields.push("signed".into()); + } + + writeln!(out, "{name} [{fields}]", fields = fields.join(", "))?; + } + _ => { + writeln!(out, "{name}")?; + } + } + } + + Ok(()) +} diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index 3a81447d701..12f2e5e7733 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -17,7 +17,7 @@ use crate::{ plumbing::{ options::{ attributes, commit, commitgraph, config, credential, exclude, free, fsck, index, mailmap, merge, odb, - revision, tree, Args, Subcommands, + revision, tag, tree, Args, Subcommands, }, show_progress, }, @@ -1304,6 +1304,17 @@ pub fn main() -> Result<()> { }, ), }, + Subcommands::Tag(platform) => match platform.cmds { + Some(tag::Subcommands::List) | None => prepare_and_run( + "tag-list", + trace, + auto_verbose, + progress, + progress_keep_open, + None, + move |_progress, out, _err| core::repository::tag::list(repository(Mode::Lenient)?, out), + ), + }, Subcommands::Tree(cmd) => match cmd { tree::Subcommands::Entries { treeish, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index ae1ea443551..33f636b323b 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -100,6 +100,9 @@ pub enum Subcommands { /// Interact with commit objects. #[clap(subcommand)] Commit(commit::Subcommands), + /// Interact with tag objects. + #[clap(visible_alias = "tags")] + Tag(tag::Platform), /// Verify the integrity of the entire repository Verify { #[clap(flatten)] @@ -928,6 +931,20 @@ pub mod commit { } } +pub mod tag { + #[derive(Debug, clap::Parser)] + pub struct Platform { + #[clap(subcommand)] + pub cmds: Option, + } + + #[derive(Debug, clap::Subcommand)] + pub enum Subcommands { + /// List all tags. + List, + } +} + pub mod credential { #[derive(Debug, clap::Subcommand)] pub enum Subcommands {