girt 0.18.0

An incremental, idiomatic Rust implementation of Git
Documentation
//! Read-only staged changes and configured or raw working-tree observations.
//!
//! Call [`Repository::raw_status`](crate::Repository::raw_status) with [`Baseline`] to choose the
//! comparison tree and [`Untracked`] to choose whether raw untracked leaves are listed. It uses
//! default [`Limits`] and never cancels;
//! [`Repository::raw_status_with`](crate::Repository::raw_status_with) takes limits and a
//! cancellation flag. [`Report`]
//! separates staged, unstaged, unmerged, unchecked gitlink, and boundary observations. Empty
//! change lists do not establish Git-default cleanliness or authorize checkout.
//!
//! [`Repository::raw_status`](crate::Repository::raw_status) deliberately compares raw bytes and
//! POSIX executable/symlink modes. It does not apply attributes, filters, EOL conversion, ignore
//! rules, `core.filemode`, `core.symlinks`, or ambient configuration. This is useful for consumers
//! that own normalization policy, and is **not Git-default status**.
//!
//! [`Repository::status_with_filters`](crate::Repository::status_with_filters) converts each
//! working file it reads through a [`filter::Pipeline`](crate::filter::Pipeline) before hashing,
//! as `git status` does for line-ending conversion, `ident` and filter drivers such as Git LFS.
//!
//! [`Repository::status`](crate::Repository::status) and filtered status apply intent-to-add,
//! assume-unchanged and skip-worktree flags, and ignore regular-file executable-bit changes when
//! `core.fileMode=false`. With `core.symlinks=false`, regular files containing target bytes
//! represent indexed symlinks; filtered status still converts these regular files. Status without
//! filters compares literal bytes. Both forms list untracked paths as `git status` does: pass the
//! rules from [`Repository::ignore_rules`](crate::Repository::ignore_rules) and an
//! [`UntrackedFiles`] listing, and read [`Report::untracked`].
//! As in Git, a working file is not read when its `lstat` matches every cached index stat word and
//! the entry is not racily clean; ordinary file comparison does not read index blobs. Submodule
//! metadata can use the index `.gitmodules` blob. Configured status also trusts
//! assume-unchanged and skip-worktree promises; raw status inspects those files. No index
//! refresh, locks, hooks, writes, rename detection, staging or checkout occur.
//!
//! [`Scan`] composes configured status with optional path selection, filters and an explicitly
//! authorized filesystem monitor. Its checkpoint and dirty bits can avoid tracked stat calls;
//! existing status methods never query hooks. Collapsed whole-tree listing can reuse UNTR under
//! exact byte name policy after checking its environment, ignore inputs and directory metadata.
//! [`Report::untracked_cache`] records reused listings or a typed fallback reason. Other listing
//! and name policies retain ordinary traversal. Neither cache makes the report an atomic snapshot.
//!
//! [`Scan::refresh_caches`] explicitly records observed UNTR/FSMN caches under the existing index
//! guard. Ordinary status never writes back the stat words it could have learned, so a file whose
//! stat went stale but whose content did not is read by every call.
//! [`Repository::refresh_index`](crate::Repository::refresh_index) is the explicit write that
//! records them, as `git update-index --refresh` does.
//!
//! Traversal is supported on Linux and macOS. Linux preserves non-UTF-8 filename bytes; macOS
//! accepts ASCII paths only because Unicode normalization aliases are not implemented. Exact
//! directory-name matching prevents case-folded aliases from being mistaken for tracked names.
//! Unsafe components and metadata aliases are rejected before conversion to platform paths.
//! NUL, backslashes, colons, empty/dot/parent components and case-insensitive `.git` components
//! are rejected on both platforms. Raw status never enters gitlinks. Configured status inspects
//! them separately through [`submodule`](crate::submodule), with depth/count bounds and each
//! child's own attributes and filters. Non-gitlink nested repositories remain boundaries.
//!
//! # Example
//!
//! Stage a file in the index, change it in the working tree, and observe a raw unstaged change. The
//! report is an observation; nothing is refreshed or written:
//!
//! ```
//! use girt::index::{Entry, Mode};
//! use girt::status::{Baseline, Change, Untracked};
//! use girt::{InitKind, ObjectFormat, Repository};
//!
//! # if cfg!(not(any(target_os = "linux", target_os = "macos"))) {
//! #     return Ok(());
//! # }
//! let directory = tempfile::tempdir()?;
//! let repository = Repository::init(
//!     ObjectFormat::Sha1,
//!     directory.path().join("w"),
//!     InitKind::Worktree,
//! )?;
//!
//! let id = repository.loose_objects().write_blob(b"staged\n")?;
//! let mut edit = repository.edit_index(Default::default())?;
//! edit.replace_entries(vec![Entry::new(b"hello.txt".to_vec(), Mode::Regular, id)])?;
//! edit.commit()?;
//!
//! let worktree = repository.worktree().expect("ordinary repository");
//! std::fs::write(worktree.join("hello.txt"), b"working\n")?;
//! std::fs::write(worktree.join("scratch.txt"), b"new\n")?;
//!
//! let report = repository.raw_status(Baseline::Head, Untracked::RawFilesWithoutIgnores)?;
//! // HEAD is unborn, so the whole index is staged relative to an empty tree.
//! assert_eq!(report.staged[0].path, b"hello.txt");
//! assert_eq!(report.unstaged[0].path, b"hello.txt");
//! assert!(matches!(report.unstaged[0].change, Change::Modified(_)));
//! assert_eq!(report.raw_untracked, [b"scratch.txt".to_vec()]);
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
mod refresh;
mod scan;
mod types;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod untracked;
mod workflow;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(crate) use workflow::nested_status;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(crate) mod worktree;

pub use refresh::Refresh;
pub use scan::Scan;
pub use types::*;