crate-seq-git

Overview

crate-seq-git provides a pure-Rust git interface via gix for tag discovery, tree checkout, annotated tag creation/deletion, and tag date extraction. It is consumed primarily by crate-seq-core for version discovery and source resolution.

Tag discovery — discover.rs

discover_tags

pub fn discover_tags(
    repo_path: &Path,
    pattern: &str,
) -> Result<Vec<TagRef>, Error>

Discovers all git tags in repo_path matching pattern and parses their SemVer. Tags that don't match or whose suffix fails semver::Version::parse are silently skipped. Results are sorted ascending by SemVer.

TagRef

pub struct TagRef {
    pub name: String,           // e.g. "v1.0.3" or "foo-v1.0.3"
    pub semver: semver::Version,
}

Tree checkout — checkout.rs

checkout_tag

pub fn checkout_tag(
    repo_path: &Path,
    tag_name: &str,
) -> Result<tempfile::TempDir, Error>

Checks out the tree of tag_name into a fresh temporary directory. Only git-tracked files at the tagged commit's tree are written. The TempDir is cleaned up on drop.

Symlinks and submodules are skipped (unsupported in v1). Blobs and executable blobs are written as regular files; subtrees become directories.

Tag creation & deletion — tag_create.rs

create_annotated_tag

pub fn create_annotated_tag(
    repo_path: &Path,
    tag_name: &str,
    version: &semver::Version,
) -> Result<(), Error>

Creates an annotated git tag at HEAD via the git subprocess. Tag message is "crate-seq: version {version}". Returns Error::TagCreate if the subprocess exits non-zero, Error::TempDir if it cannot be spawned.

delete_tag

pub fn delete_tag(
    repo_path: &Path,
    tag_name: &str,
) -> Result<(), Error>

Deletes a tag from the repository. Used for rollback if a ledger write succeeds but subsequent tag creation fails. Same error behavior as create_annotated_tag.

Why subprocess?

Tag creation and deletion use the git CLI rather than gix because annotated tag creation with custom messages is simpler via the subprocess. Discovery and checkout use gix for performance and no-subprocess safety.

Tag date extraction — date.rs

tag_date

pub fn tag_date(
    repo_path: &Path,
    tag_name: &str,
) -> Result<chrono::DateTime<chrono::Utc>, Error>

Resolves the author/tagger timestamp for tag_name:

  • Annotated tags → returns the tagger date from the tag object's tagger field
  • Lightweight tags (pointing directly to a commit) → returns the committer date
  • Other object types → peels to commit and uses committer time as fallback

Returns Error::TagNotFound if the tag doesn't exist, Error::Object if the timestamp cannot be decoded or produces an invalid timestamp_opt.

Error type — error.rs

#[derive(Debug, thiserror::Error)]
pub enum Error {
    OpenRepo { path: PathBuf, source: Box<gix::discover::Error> },
    References(#[from] gix::reference::iter::Error),
    ReferencesInit(#[from] gix::reference::iter::init::Error),
    Peel(String),
    TagObject(String),
    TagNotFound(String),
    Object(String),
    TreeTraversal(String),
    TempDir(#[from] std::io::Error),
    TagCreate(String),
}

Key design choices:

  • OpenRepo wraps the gix::discover::Error in a Box to keep the enum size small
  • References and ReferencesInit use #[from] for automatic conversion
  • String-based variants (Peel, Object, TreeTraversal, TagCreate) are used where the upstream error types are not Send/Sync or are too complex to wrap

Source files

File Responsibility
discover.rs Tag discovery with glob-pattern filtering and SemVer parsing
checkout.rs Temp-directory tree checkout via recursive tree walking
tag_create.rs Annotated tag creation and deletion via git subprocess
date.rs Tag date extraction from annotated and lightweight tags
error.rs Crate error enum
lib.rs Module declarations and re-exports