From 9ea98fda75fbef339647a0ca03776060356d1206 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 23 Jan 2022 12:15:31 +0800 Subject: [PATCH 01/13] frame for printing index information (#298) --- Cargo.lock | 1 + git-bitmap/src/ewah.rs | 2 +- git-repository/Cargo.toml | 5 ++++- git-repository/src/lib.rs | 6 +++++- gitoxide-core/src/index.rs | 18 ++++++++++++++++++ gitoxide-core/src/lib.rs | 1 + src/plumbing/main.rs | 14 ++++++++++++++ src/plumbing/options.rs | 25 +++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 gitoxide-core/src/index.rs diff --git a/Cargo.lock b/Cargo.lock index 6bac2ccc1ea..58cd70f8830 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1526,6 +1526,7 @@ dependencies = [ "git-diff 0.13.0", "git-features 0.19.0", "git-hash 0.9.0", + "git-index", "git-lock 1.0.1", "git-object 0.17.0", "git-odb 0.26.0", diff --git a/git-bitmap/src/ewah.rs b/git-bitmap/src/ewah.rs index e504050da2c..7575a59e2d6 100644 --- a/git-bitmap/src/ewah.rs +++ b/git-bitmap/src/ewah.rs @@ -119,6 +119,6 @@ mod access { pub struct Vec { num_bits: u32, bits: std::vec::Vec, - /// RLW is an offset into the `bits` buffer, so `1` translates into &bits[1] essentially. + /// RLW is an offset into the `bits` buffer, so `1` translates into &bits\[1] essentially. rlw: usize, } diff --git a/git-repository/Cargo.toml b/git-repository/Cargo.toml index 8cd8801819b..5777f6e22da 100644 --- a/git-repository/Cargo.toml +++ b/git-repository/Cargo.toml @@ -14,7 +14,7 @@ test = true [features] default = ["max-performance", "one-stop-shop"] -unstable = [] +unstable = ["git-index"] serde1 = ["git-pack/serde1", "git-object/serde1", "git-protocol/serde1", "git-transport/serde1", "git-ref/serde1", "git-odb/serde1"] # enable when https://github.com/RustCrypto/asm-hashes/issues/17 is fixed # max-performance = ["git-features/parallel", "git-features/zlib-ng-compat", "git-features/fast-sha1"] @@ -54,6 +54,9 @@ git-transport = { version = "^0.15.0", path = "../git-transport", optional = tru git-diff = { version = "^0.13.0", path = "../git-diff", optional = true } git-features = { version = "^0.19.0", path = "../git-features", features = ["progress"] } +# unstable only +git-index = { version ="^0.1.0", path = "../git-index", optional = true } + signal-hook = { version = "0.3.9", default-features = false } thiserror = "1.0.26" clru = "0.5.0" diff --git a/git-repository/src/lib.rs b/git-repository/src/lib.rs index 8f47c687b0c..fca7e00eb02 100644 --- a/git-repository/src/lib.rs +++ b/git-repository/src/lib.rs @@ -86,6 +86,7 @@ //! * [`url`] //! * [`actor`] //! * [`bstr`][bstr] +//! * [`index`] //! * [`objs`] //! * [`odb`] //! * [`pack`][odb::pack] @@ -118,6 +119,8 @@ pub use git_features::{parallel, progress, progress::Progress, threading}; pub use git_hash as hash; #[doc(inline)] pub use git_hash::{oid, ObjectId}; +#[cfg(all(feature = "unstable", feature = "git-index"))] +pub use git_index as index; pub use git_lock as lock; pub use git_object as objs; pub use git_object::bstr; @@ -151,9 +154,10 @@ pub mod prelude { pub mod path; mod repository; -use git_features::threading::OwnShared; pub use repository::{discover, init, open}; +use git_features::threading::OwnShared; + /// The standard type for a store to handle git references. pub type RefStore = git_ref::file::Store; /// A handle for finding objects in an object database, abstracting away caches for thread-local use. diff --git a/gitoxide-core/src/index.rs b/gitoxide-core/src/index.rs new file mode 100644 index 00000000000..c565d5eb77e --- /dev/null +++ b/gitoxide-core/src/index.rs @@ -0,0 +1,18 @@ +use git_repository as git; +use std::path::Path; + +#[allow(unused)] +pub fn entries( + index_path: impl AsRef, + out: impl std::io::Write, + object_hash: git::hash::Kind, +) -> anyhow::Result<()> { + let file = git::index::File::at( + index_path.as_ref(), + git::index::decode::Options { + object_hash, + ..Default::default() + }, + )?; + todo!("print entries") +} diff --git a/gitoxide-core/src/lib.rs b/gitoxide-core/src/lib.rs index 6977191ee70..0f89d1d0f19 100644 --- a/gitoxide-core/src/lib.rs +++ b/gitoxide-core/src/lib.rs @@ -40,6 +40,7 @@ pub mod net; pub mod commitgraph; #[cfg(feature = "estimate-hours")] pub mod hours; +pub mod index; #[cfg(feature = "organize")] pub mod organize; pub mod pack; diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index 6824f0abdbf..b26f62ca537 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -12,6 +12,7 @@ use clap::Parser; use gitoxide_core as core; use gitoxide_core::pack::verify; +use crate::plumbing::options::index; #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))] use crate::plumbing::options::remote; use crate::{ @@ -73,6 +74,19 @@ pub fn main() -> Result<()> { })?; match cmd { + Subcommands::Index(subcommands) => match subcommands { + index::Subcommands::Entries { + object_hash, + index_path, + } => prepare_and_run( + "index-entries", + verbose, + progress, + progress_keep_open, + None, + move |_progress, out, _err| core::index::entries(index_path, out, object_hash), + ), + }, Subcommands::Repository(subcommands) => match subcommands { repo::Subcommands::Verify { args: diff --git a/src/plumbing/options.rs b/src/plumbing/options.rs index e241c5b99d2..bdb3fcce5b3 100644 --- a/src/plumbing/options.rs +++ b/src/plumbing/options.rs @@ -56,6 +56,9 @@ pub enum Subcommands { /// Subcommands for interacting with commit-graphs #[clap(subcommand)] CommitGraph(commitgraph::Subcommands), + /// Subcommands for interacting with a worktree index, typically at .git/index + #[clap(subcommand)] + Index(index::Subcommands), /// Subcommands for interacting with entire git repositories #[clap(subcommand)] Repository(repo::Subcommands), @@ -344,6 +347,28 @@ pub mod repo { } } +/// +pub mod index { + use std::path::PathBuf; + + use clap::AppSettings; + + #[derive(Debug, clap::Parser)] + #[clap(alias = "index")] + pub enum Subcommands { + /// Print all entries to standard output + #[clap(setting = AppSettings::DisableVersionFlag)] + Entries { + /// The object format to assume when reading files that don't inherently know about it, or when writing files. + #[clap(long, default_value = "sha1", possible_values(&["sha1"]))] + object_hash: git_repository::hash::Kind, + + /// The path too the index file. + index_path: PathBuf, + }, + } +} + /// pub mod commitgraph { use std::path::PathBuf; From 329538b9c3f44bb8e70a4567ba90dc3b594c2dfc Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 23 Jan 2022 13:32:07 +0800 Subject: [PATCH 02/13] simple printing of basic entry information (#298) --- git-index/src/entry.rs | 4 ++++ gitoxide-core/src/index.rs | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/git-index/src/entry.rs b/git-index/src/entry.rs index 6710d845e7d..863fcbdf0fb 100644 --- a/git-index/src/entry.rs +++ b/git-index/src/entry.rs @@ -88,11 +88,15 @@ bitflags! { } } +#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] pub struct Time { pub secs: u32, pub nsecs: u32, } +#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] pub struct Stat { pub mtime: Time, pub ctime: Time, diff --git a/gitoxide-core/src/index.rs b/gitoxide-core/src/index.rs index c565d5eb77e..8a4e2d0a7b8 100644 --- a/gitoxide-core/src/index.rs +++ b/gitoxide-core/src/index.rs @@ -4,7 +4,7 @@ use std::path::Path; #[allow(unused)] pub fn entries( index_path: impl AsRef, - out: impl std::io::Write, + mut out: impl std::io::Write, object_hash: git::hash::Kind, ) -> anyhow::Result<()> { let file = git::index::File::at( @@ -14,5 +14,19 @@ pub fn entries( ..Default::default() }, )?; - todo!("print entries") + for entry in file.entries() { + writeln!( + out, + "{}{:?} {} {}", + if entry.flags.is_empty() { + "".to_string() + } else { + format!("{:?} ", entry.flags) + }, + entry.mode, + entry.id, + entry.path(&file.state) + )?; + } + Ok(()) } From 003515f3c90a49fbe9db9b84987233486711beb8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 23 Jan 2022 20:43:21 +0800 Subject: [PATCH 03/13] Also print stage of entries (#298) --- git-index/src/entry.rs | 7 +++++++ gitoxide-core/src/index.rs | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/git-index/src/entry.rs b/git-index/src/entry.rs index 863fcbdf0fb..5d155859712 100644 --- a/git-index/src/entry.rs +++ b/git-index/src/entry.rs @@ -61,6 +61,7 @@ pub(crate) mod at_rest { bitflags! { /// In-memory flags pub struct Flags: u32 { + const STAGE_MASK = 0x3000; // TODO: could we use the pathlen ourselves to save 8 bytes? And how to handle longer paths than that? 0 as sentinel maybe? const PATH_LEN = 0x0fff; const UPDATE = 1 << 16; @@ -88,6 +89,12 @@ bitflags! { } } +impl Flags { + pub fn stage(&self) -> u32 { + (*self & Flags::STAGE_MASK).bits >> 12 + } +} + #[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)] #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] pub struct Time { diff --git a/gitoxide-core/src/index.rs b/gitoxide-core/src/index.rs index 8a4e2d0a7b8..369ad9f6c2b 100644 --- a/gitoxide-core/src/index.rs +++ b/gitoxide-core/src/index.rs @@ -17,7 +17,13 @@ pub fn entries( for entry in file.entries() { writeln!( out, - "{}{:?} {} {}", + "{} {}{:?} {} {}", + match entry.flags.stage() { + 0 => "BASE ", + 1 => "OURS ", + 2 => "THEIRS ", + _ => "UNKNOWN", + }, if entry.flags.is_empty() { "".to_string() } else { From bc89fc77354f7d8af6628364be18550c4a45c789 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 23 Jan 2022 20:50:04 +0800 Subject: [PATCH 04/13] feat: Implement Display for hash kind (#298) This helps 'clap' and allows for a little more type-safety during declaration. --- git-hash/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/git-hash/src/lib.rs b/git-hash/src/lib.rs index ac101385fd0..c4f5b2495a2 100644 --- a/git-hash/src/lib.rs +++ b/git-hash/src/lib.rs @@ -96,6 +96,14 @@ impl FromStr for Kind { } } +impl std::fmt::Display for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Kind::Sha1 => f.write_str("SHA1"), + } + } +} + impl Kind { /// Returns the shortest hash we support #[inline] From f790a55ff4263bea9b9476137bac3824912044ac Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 23 Jan 2022 20:52:41 +0800 Subject: [PATCH 05/13] Simplify command-line options declaration (#298) --- src/plumbing/options.rs | 44 ++++++++++------------------------------ src/porcelain/options.rs | 9 +++----- 2 files changed, 14 insertions(+), 39 deletions(-) diff --git a/src/plumbing/options.rs b/src/plumbing/options.rs index bdb3fcce5b3..919d9d19029 100644 --- a/src/plumbing/options.rs +++ b/src/plumbing/options.rs @@ -37,14 +37,14 @@ pub struct Args { pub format: core::OutputFormat, /// The object format to assume when reading files that don't inherently know about it, or when writing files. - #[clap(long, default_value = "sha1", possible_values(&["sha1"]))] + #[clap(long, default_value_t = git_repository::hash::Kind::default(), possible_values(&["SHA1"]))] pub object_hash: git_repository::hash::Kind, #[clap(subcommand)] pub cmd: Subcommands, } -#[derive(Debug, clap::Parser)] +#[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Subcommands for interacting with packs and their indices. #[clap(subcommand)] @@ -68,10 +68,9 @@ pub enum Subcommands { pub mod pack { use std::{ffi::OsString, path::PathBuf}; - use clap::AppSettings; use gitoxide_core as core; - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Subcommands for interacting with pack indices (.idx) #[clap(subcommand)] @@ -80,7 +79,6 @@ pub mod pack { #[clap(subcommand)] MultiIndex(multi_index::Subcommands), /// Create a new pack with a set of objects. - #[clap(setting = AppSettings::DisableVersionFlag)] Create { #[clap(long, short = 'r')] /// the directory containing the '.git' repository from which objects should be read. @@ -140,7 +138,6 @@ pub mod pack { tips: Vec, }, /// Use the git-protocol to receive a pack, emulating a clone. - #[clap(setting = AppSettings::DisableVersionFlag)] #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))] Receive { /// The protocol version to use. Valid values are 1 and 2 @@ -173,7 +170,6 @@ pub mod pack { /// /// Note that this effectively removes delta compression for an average compression of 2x, creating one file per object in the process. /// Thus this should only be done to dissolve small packs after a fetch. - #[clap(setting = AppSettings::DisableVersionFlag)] Explode { #[clap(long)] /// Read written objects back and assert they match their source. Fail the operation otherwise. @@ -211,7 +207,6 @@ pub mod pack { object_path: Option, }, /// Verify the integrity of a pack, index or multi-index file - #[clap(setting = AppSettings::DisableVersionFlag)] Verify { #[clap(flatten)] args: VerifyOptions, @@ -257,18 +252,14 @@ pub mod pack { pub mod multi_index { use std::path::PathBuf; - use clap::AppSettings; - - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Verify a multi-index quickly without inspecting objects themselves - #[clap(setting = AppSettings::DisableVersionFlag)] Verify { /// The path to the multi-pack-index to verify. multi_index_path: PathBuf, }, /// Create a multi-pack index from one or more pack index files - #[clap(setting = AppSettings::DisableVersionFlag)] Create { /// The path to which the multi-index file should be written, overwriting any possibly existing file. /// @@ -287,13 +278,11 @@ pub mod pack { pub mod index { use std::path::PathBuf; - use clap::AppSettings; use gitoxide_core as core; - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// create a pack index from a pack data file. - #[clap(setting = AppSettings::DisableVersionFlag)] Create { /// Specify how to iterate the pack, defaults to 'verify' /// @@ -331,13 +320,10 @@ pub mod pack { pub mod repo { use std::path::PathBuf; - use clap::AppSettings; - - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] #[clap(alias = "repo")] pub enum Subcommands { /// Verify the integrity of the entire repository - #[clap(setting = AppSettings::DisableVersionFlag)] Verify { #[clap(flatten)] args: super::pack::VerifyOptions, @@ -351,19 +337,16 @@ pub mod repo { pub mod index { use std::path::PathBuf; - use clap::AppSettings; - - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] #[clap(alias = "index")] pub enum Subcommands { /// Print all entries to standard output - #[clap(setting = AppSettings::DisableVersionFlag)] Entries { /// The object format to assume when reading files that don't inherently know about it, or when writing files. - #[clap(long, default_value = "sha1", possible_values(&["sha1"]))] + #[clap(long, default_value_t = git_repository::hash::Kind::default(), possible_values(&["SHA1"]))] object_hash: git_repository::hash::Kind, - /// The path too the index file. + /// The path to the index file. index_path: PathBuf, }, } @@ -373,12 +356,9 @@ pub mod index { pub mod commitgraph { use std::path::PathBuf; - use clap::AppSettings; - - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Verify the integrity of a commit graph - #[clap(setting = AppSettings::DisableVersionFlag)] Verify { /// The path to '.git/objects/info/', '.git/objects/info/commit-graphs/', or '.git/objects/info/commit-graph' to validate. #[clap(parse(from_os_str))] @@ -393,16 +373,14 @@ pub mod commitgraph { /// #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))] pub mod remote { - use clap::AppSettings; use gitoxide_core as core; - #[derive(Debug, clap::Parser)] + #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// List remote references from a remote identified by a url. /// /// This is the plumbing equivalent of `git ls-remote`. /// Supported URLs are documented here: - #[clap(setting = AppSettings::DisableVersionFlag)] RefList { /// The protocol version to use. Valid values are 1 and 2 #[clap(long, short = 'p')] diff --git a/src/porcelain/options.rs b/src/porcelain/options.rs index a49cd0f369c..3fb403b68f1 100644 --- a/src/porcelain/options.rs +++ b/src/porcelain/options.rs @@ -24,11 +24,10 @@ pub struct Args { pub cmd: Subcommands, } -#[derive(Debug, clap::Parser)] +#[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Initialize the repository in the current directory. #[clap(visible_alias = "initialize")] - #[clap(setting = AppSettings::DisableVersionFlag)] Init { /// The directory in which to initialize a new git repository. /// @@ -44,12 +43,11 @@ pub enum Subcommands { } #[cfg(feature = "gitoxide-core-tools")] -#[derive(Debug, clap::Parser)] -#[clap(setting = AppSettings::DisableVersionFlag, setting = AppSettings::SubcommandRequired)] +#[derive(Debug, clap::Subcommand)] +#[clap(setting = AppSettings::SubcommandRequired)] #[clap(visible_alias = "t")] pub enum ToolCommands { /// Find all repositories in a given directory. - #[clap(setting = AppSettings::DisableVersionFlag)] Find { /// The directory in which to find all git repositories. /// @@ -57,7 +55,6 @@ pub enum ToolCommands { root: Option, }, /// Move all repositories found in a directory into a structure matching their clone URLs. - #[clap(setting = AppSettings::DisableVersionFlag)] Organize { #[clap(long)] /// The operation will be in dry-run mode unless this flag is set. From 3fc1622488054c6ab655eb9d2f941b68cc3ccf18 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 10:09:13 +0800 Subject: [PATCH 06/13] JSON output for index entries (#293) Mainly intended for seeing all the values, at the cost of readability. --- git-repository/Cargo.toml | 2 +- gitoxide-core/src/index.rs | 107 ++++++++++++++++++++++++++++++------- src/plumbing/main.rs | 4 +- 3 files changed, 91 insertions(+), 22 deletions(-) diff --git a/git-repository/Cargo.toml b/git-repository/Cargo.toml index 5777f6e22da..a48b6e27787 100644 --- a/git-repository/Cargo.toml +++ b/git-repository/Cargo.toml @@ -15,7 +15,7 @@ test = true [features] default = ["max-performance", "one-stop-shop"] unstable = ["git-index"] -serde1 = ["git-pack/serde1", "git-object/serde1", "git-protocol/serde1", "git-transport/serde1", "git-ref/serde1", "git-odb/serde1"] +serde1 = ["git-pack/serde1", "git-object/serde1", "git-protocol/serde1", "git-transport/serde1", "git-ref/serde1", "git-odb/serde1", "git-index/serde1"] # enable when https://github.com/RustCrypto/asm-hashes/issues/17 is fixed # max-performance = ["git-features/parallel", "git-features/zlib-ng-compat", "git-features/fast-sha1"] max-performance = ["git-features/parallel", "git-features/zlib-ng-compat", "git-pack/pack-cache-lru-static", "git-pack/pack-cache-lru-dynamic"] diff --git a/gitoxide-core/src/index.rs b/gitoxide-core/src/index.rs index 369ad9f6c2b..a3f5287dc65 100644 --- a/gitoxide-core/src/index.rs +++ b/gitoxide-core/src/index.rs @@ -1,12 +1,24 @@ use git_repository as git; +use git_repository::bstr::ByteSlice; +use std::io::Write; use std::path::Path; +pub mod entries { + use git_repository as git; + + pub struct Options { + pub object_hash: git::hash::Kind, + pub format: crate::OutputFormat, + } +} + #[allow(unused)] pub fn entries( index_path: impl AsRef, mut out: impl std::io::Write, - object_hash: git::hash::Kind, + entries::Options { object_hash, format }: entries::Options, ) -> anyhow::Result<()> { + use crate::OutputFormat::*; let file = git::index::File::at( index_path.as_ref(), git::index::decode::Options { @@ -14,25 +26,80 @@ pub fn entries( ..Default::default() }, )?; - for entry in file.entries() { - writeln!( - out, - "{} {}{:?} {} {}", - match entry.flags.stage() { - 0 => "BASE ", - 1 => "OURS ", - 2 => "THEIRS ", - _ => "UNKNOWN", - }, - if entry.flags.is_empty() { - "".to_string() - } else { - format!("{:?} ", entry.flags) - }, - entry.mode, - entry.id, - entry.path(&file.state) - )?; + + #[cfg(feature = "serde1")] + if let Json = format { + out.write_all(b"[\n")?; + } + + let mut entries = file.entries().iter().peekable(); + while let Some(entry) = entries.next() { + match format { + Human => human_entry(&mut out, &file, entry)?, + #[cfg(feature = "serde1")] + Json => json_entry_oneline(&mut out, &file, entry, entries.peek().is_none())?, + } + } + + #[cfg(feature = "serde1")] + if let Json = format { + out.write_all(b"]\n")?; } Ok(()) } + +#[cfg(feature = "serde1")] +fn json_entry_oneline( + mut out: &mut impl Write, + file: &git::index::File, + entry: &git::index::Entry, + is_last: bool, +) -> anyhow::Result<()> { + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + struct Entry<'a> { + stat: &'a git::index::entry::Stat, + hex_id: String, + flags: u32, + mode: u32, + path: std::borrow::Cow<'a, str>, + } + + serde_json::to_writer( + &mut out, + &Entry { + stat: &entry.stat, + hex_id: entry.id.to_hex().to_string(), + flags: entry.flags.bits(), + mode: entry.mode.bits(), + path: entry.path(&file.state).to_str_lossy(), + }, + )?; + + if is_last { + out.write_all(b"\n")?; + } else { + out.write_all(b",\n")?; + } + Ok(()) +} + +fn human_entry(out: &mut impl Write, file: &git::index::File, entry: &git::index::Entry) -> std::io::Result<()> { + writeln!( + out, + "{} {}{:?} {} {}", + match entry.flags.stage() { + 0 => "BASE ", + 1 => "OURS ", + 2 => "THEIRS ", + _ => "UNKNOWN", + }, + if entry.flags.is_empty() { + "".to_string() + } else { + format!("{:?} ", entry.flags) + }, + entry.mode, + entry.id, + entry.path(&file.state) + ) +} diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index b26f62ca537..e482e2d5568 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -84,7 +84,9 @@ pub fn main() -> Result<()> { progress, progress_keep_open, None, - move |_progress, out, _err| core::index::entries(index_path, out, object_hash), + move |_progress, out, _err| { + core::index::entries(index_path, out, core::index::entries::Options { object_hash, format }) + }, ), }, Subcommands::Repository(subcommands) => match subcommands { From 8bf585d67cd67b168d819ba05858cef7d9b90894 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 10:16:02 +0800 Subject: [PATCH 07/13] refactor (#293) --- git-repository/src/lib.rs | 3 +- gitoxide-core/src/index.rs | 105 ----------------------------- gitoxide-core/src/index/entries.rs | 67 ++++++++++++++++++ gitoxide-core/src/index/mod.rs | 40 +++++++++++ src/plumbing/main.rs | 3 +- 5 files changed, 109 insertions(+), 109 deletions(-) delete mode 100644 gitoxide-core/src/index.rs create mode 100644 gitoxide-core/src/index/entries.rs create mode 100644 gitoxide-core/src/index/mod.rs diff --git a/git-repository/src/lib.rs b/git-repository/src/lib.rs index fca7e00eb02..935897a75a1 100644 --- a/git-repository/src/lib.rs +++ b/git-repository/src/lib.rs @@ -154,9 +154,8 @@ pub mod prelude { pub mod path; mod repository; -pub use repository::{discover, init, open}; - use git_features::threading::OwnShared; +pub use repository::{discover, init, open}; /// The standard type for a store to handle git references. pub type RefStore = git_ref::file::Store; diff --git a/gitoxide-core/src/index.rs b/gitoxide-core/src/index.rs deleted file mode 100644 index a3f5287dc65..00000000000 --- a/gitoxide-core/src/index.rs +++ /dev/null @@ -1,105 +0,0 @@ -use git_repository as git; -use git_repository::bstr::ByteSlice; -use std::io::Write; -use std::path::Path; - -pub mod entries { - use git_repository as git; - - pub struct Options { - pub object_hash: git::hash::Kind, - pub format: crate::OutputFormat, - } -} - -#[allow(unused)] -pub fn entries( - index_path: impl AsRef, - mut out: impl std::io::Write, - entries::Options { object_hash, format }: entries::Options, -) -> anyhow::Result<()> { - use crate::OutputFormat::*; - let file = git::index::File::at( - index_path.as_ref(), - git::index::decode::Options { - object_hash, - ..Default::default() - }, - )?; - - #[cfg(feature = "serde1")] - if let Json = format { - out.write_all(b"[\n")?; - } - - let mut entries = file.entries().iter().peekable(); - while let Some(entry) = entries.next() { - match format { - Human => human_entry(&mut out, &file, entry)?, - #[cfg(feature = "serde1")] - Json => json_entry_oneline(&mut out, &file, entry, entries.peek().is_none())?, - } - } - - #[cfg(feature = "serde1")] - if let Json = format { - out.write_all(b"]\n")?; - } - Ok(()) -} - -#[cfg(feature = "serde1")] -fn json_entry_oneline( - mut out: &mut impl Write, - file: &git::index::File, - entry: &git::index::Entry, - is_last: bool, -) -> anyhow::Result<()> { - #[cfg_attr(feature = "serde1", derive(serde::Serialize))] - struct Entry<'a> { - stat: &'a git::index::entry::Stat, - hex_id: String, - flags: u32, - mode: u32, - path: std::borrow::Cow<'a, str>, - } - - serde_json::to_writer( - &mut out, - &Entry { - stat: &entry.stat, - hex_id: entry.id.to_hex().to_string(), - flags: entry.flags.bits(), - mode: entry.mode.bits(), - path: entry.path(&file.state).to_str_lossy(), - }, - )?; - - if is_last { - out.write_all(b"\n")?; - } else { - out.write_all(b",\n")?; - } - Ok(()) -} - -fn human_entry(out: &mut impl Write, file: &git::index::File, entry: &git::index::Entry) -> std::io::Result<()> { - writeln!( - out, - "{} {}{:?} {} {}", - match entry.flags.stage() { - 0 => "BASE ", - 1 => "OURS ", - 2 => "THEIRS ", - _ => "UNKNOWN", - }, - if entry.flags.is_empty() { - "".to_string() - } else { - format!("{:?} ", entry.flags) - }, - entry.mode, - entry.id, - entry.path(&file.state) - ) -} diff --git a/gitoxide-core/src/index/entries.rs b/gitoxide-core/src/index/entries.rs new file mode 100644 index 00000000000..b324f859b08 --- /dev/null +++ b/gitoxide-core/src/index/entries.rs @@ -0,0 +1,67 @@ +use git_repository as git; +use git_repository::bstr::ByteSlice; + +pub struct Options { + pub object_hash: git::hash::Kind, + pub format: crate::OutputFormat, +} + +#[cfg(feature = "serde1")] +pub(crate) fn to_json( + mut out: &mut impl std::io::Write, + file: &git::index::File, + entry: &git::index::Entry, + is_last: bool, +) -> anyhow::Result<()> { + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + struct Entry<'a> { + stat: &'a git::index::entry::Stat, + hex_id: String, + flags: u32, + mode: u32, + path: std::borrow::Cow<'a, str>, + } + + serde_json::to_writer( + &mut out, + &Entry { + stat: &entry.stat, + hex_id: entry.id.to_hex().to_string(), + flags: entry.flags.bits(), + mode: entry.mode.bits(), + path: entry.path(&file.state).to_str_lossy(), + }, + )?; + + if is_last { + out.write_all(b"\n")?; + } else { + out.write_all(b",\n")?; + } + Ok(()) +} + +pub(crate) fn to_human( + out: &mut impl std::io::Write, + file: &git::index::File, + entry: &git::index::Entry, +) -> std::io::Result<()> { + writeln!( + out, + "{} {}{:?} {} {}", + match entry.flags.stage() { + 0 => "BASE ", + 1 => "OURS ", + 2 => "THEIRS ", + _ => "UNKNOWN", + }, + if entry.flags.is_empty() { + "".to_string() + } else { + format!("{:?} ", entry.flags) + }, + entry.mode, + entry.id, + entry.path(&file.state) + ) +} diff --git a/gitoxide-core/src/index/mod.rs b/gitoxide-core/src/index/mod.rs new file mode 100644 index 00000000000..2cafeac5060 --- /dev/null +++ b/gitoxide-core/src/index/mod.rs @@ -0,0 +1,40 @@ +use std::path::Path; + +use git_repository as git; + +pub mod entries; + +pub fn entries( + index_path: impl AsRef, + mut out: impl std::io::Write, + entries::Options { object_hash, format }: entries::Options, +) -> anyhow::Result<()> { + use crate::OutputFormat::*; + let file = git::index::File::at( + index_path.as_ref(), + git::index::decode::Options { + object_hash, + ..Default::default() + }, + )?; + + #[cfg(feature = "serde1")] + if let Json = format { + out.write_all(b"[\n")?; + } + + let mut entries = file.entries().iter().peekable(); + while let Some(entry) = entries.next() { + match format { + Human => entries::to_human(&mut out, &file, entry)?, + #[cfg(feature = "serde1")] + Json => entries::to_json(&mut out, &file, entry, entries.peek().is_none())?, + } + } + + #[cfg(feature = "serde1")] + if let Json = format { + out.write_all(b"]\n")?; + } + Ok(()) +} diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index e482e2d5568..e6451b4dcec 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -12,11 +12,10 @@ use clap::Parser; use gitoxide_core as core; use gitoxide_core::pack::verify; -use crate::plumbing::options::index; #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))] use crate::plumbing::options::remote; use crate::{ - plumbing::options::{commitgraph, pack, repo, Args, Subcommands}, + plumbing::options::{commitgraph, index, pack, repo, Args, Subcommands}, shared::pretty::prepare_and_run, }; From 239e7b291297d6d49ebdf3d4986fb9fb86480e9a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 11:31:44 +0800 Subject: [PATCH 08/13] Basic entry information (#293) --- gitoxide-core/src/index/mod.rs | 127 +++++++++++++++++++++++++++++++-- src/plumbing/main.rs | 21 ++++-- src/plumbing/options.rs | 30 +++++--- 3 files changed, 155 insertions(+), 23 deletions(-) diff --git a/gitoxide-core/src/index/mod.rs b/gitoxide-core/src/index/mod.rs index 2cafeac5060..c7c65e83cd5 100644 --- a/gitoxide-core/src/index/mod.rs +++ b/gitoxide-core/src/index/mod.rs @@ -1,22 +1,124 @@ +use std::convert::TryFrom; use std::path::Path; use git_repository as git; pub mod entries; +mod information { + use git_repository as git; + use std::convert::TryFrom; + + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + pub(crate) struct EntryKind { + dir: usize, + file: usize, + executable: usize, + symlink: usize, + submodule: usize, + other: usize, + } + + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + pub(crate) struct EntryFlag { + intent_to_add: usize, + skip_worktree: usize, + } + + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + pub(crate) struct Entries { + stage_0: usize, + stage_1: usize, + stage_2: usize, + kind: EntryKind, + flags: EntryFlag, + } + + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + pub(crate) struct Collection { + version: u8, + entries: Entries, + } + + impl TryFrom for Collection { + type Error = anyhow::Error; + + fn try_from(f: git::index::File) -> Result { + Ok(Collection { + version: f.version() as u8, + entries: { + let (mut stage_0, mut stage_1, mut stage_2) = (0, 0, 0); + let (mut dir, mut file, mut executable, mut symlink, mut submodule, mut other) = (0, 0, 0, 0, 0, 0); + let (mut intent_to_add, mut skip_worktree) = (0, 0); + for entry in f.entries() { + match entry.flags.stage() { + 0 => stage_0 += 1, + 1 => stage_1 += 1, + 2 => stage_2 += 1, + invalid => anyhow::bail!("Invalid stage {} encountered", invalid), + } + match entry.mode { + git::index::entry::Mode::DIR => dir += 1, + git::index::entry::Mode::FILE => file += 1, + git::index::entry::Mode::FILE_EXECUTABLE => executable += 1, + git::index::entry::Mode::SYMLINK => symlink += 1, + git::index::entry::Mode::COMMIT => submodule += 1, + _ => other += 1, + } + if entry.flags.contains(git::index::entry::Flags::INTENT_TO_ADD) { + intent_to_add += 1; + } + if entry.flags.contains(git::index::entry::Flags::SKIP_WORKTREE) { + skip_worktree += 1; + } + } + Entries { + stage_0, + stage_1, + stage_2, + kind: EntryKind { + dir, + file, + executable, + symlink, + submodule, + other, + }, + flags: EntryFlag { + intent_to_add, + skip_worktree, + }, + } + }, + }) + } + } +} + +pub fn information( + index_path: impl AsRef, + out: impl std::io::Write, + entries::Options { object_hash, format }: entries::Options, +) -> anyhow::Result<()> { + use crate::OutputFormat::*; + let info = information::Collection::try_from(parse_file(index_path, object_hash)?)?; + match format { + Human => { + anyhow::bail!("Only JSON output is implemented"); + } + #[cfg(feature = "serde1")] + Json => serde_json::to_writer_pretty(out, &info)?, + } + Ok(()) +} + pub fn entries( index_path: impl AsRef, mut out: impl std::io::Write, entries::Options { object_hash, format }: entries::Options, ) -> anyhow::Result<()> { use crate::OutputFormat::*; - let file = git::index::File::at( - index_path.as_ref(), - git::index::decode::Options { - object_hash, - ..Default::default() - }, - )?; + let file = parse_file(index_path, object_hash)?; #[cfg(feature = "serde1")] if let Json = format { @@ -38,3 +140,14 @@ pub fn entries( } Ok(()) } + +fn parse_file(index_path: impl AsRef, object_hash: git::hash::Kind) -> anyhow::Result { + git::index::File::at( + index_path.as_ref(), + git::index::decode::Options { + object_hash, + ..Default::default() + }, + ) + .map_err(Into::into) +} diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index e6451b4dcec..c05209c82c1 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -73,11 +73,22 @@ pub fn main() -> Result<()> { })?; match cmd { - Subcommands::Index(subcommands) => match subcommands { - index::Subcommands::Entries { - object_hash, - index_path, - } => prepare_and_run( + Subcommands::Index(index::Platform { + object_hash, + index_path, + cmd, + }) => match cmd { + index::Subcommands::Info => prepare_and_run( + "index-entries", + verbose, + progress, + progress_keep_open, + None, + move |_progress, out, _err| { + core::index::information(index_path, out, core::index::entries::Options { object_hash, format }) + }, + ), + index::Subcommands::Entries => prepare_and_run( "index-entries", verbose, progress, diff --git a/src/plumbing/options.rs b/src/plumbing/options.rs index 919d9d19029..59de45761b0 100644 --- a/src/plumbing/options.rs +++ b/src/plumbing/options.rs @@ -57,8 +57,7 @@ pub enum Subcommands { #[clap(subcommand)] CommitGraph(commitgraph::Subcommands), /// Subcommands for interacting with a worktree index, typically at .git/index - #[clap(subcommand)] - Index(index::Subcommands), + Index(index::Platform), /// Subcommands for interacting with entire git repositories #[clap(subcommand)] Repository(repo::Subcommands), @@ -337,18 +336,27 @@ pub mod repo { pub mod index { use std::path::PathBuf; + #[derive(Debug, clap::Parser)] + pub struct Platform { + /// The object format to assume when reading files that don't inherently know about it, or when writing files. + #[clap(long, default_value_t = git_repository::hash::Kind::default(), possible_values(&["SHA1"]))] + pub object_hash: git_repository::hash::Kind, + + /// The path to the index file. + #[clap(short = 'i', long, default_value = ".git/index")] + pub index_path: PathBuf, + + /// Subcommands + #[clap(subcommand)] + pub cmd: Subcommands, + } + #[derive(Debug, clap::Subcommand)] - #[clap(alias = "index")] pub enum Subcommands { /// Print all entries to standard output - Entries { - /// The object format to assume when reading files that don't inherently know about it, or when writing files. - #[clap(long, default_value_t = git_repository::hash::Kind::default(), possible_values(&["SHA1"]))] - object_hash: git_repository::hash::Kind, - - /// The path to the index file. - index_path: PathBuf, - }, + Entries, + /// Print information about the index structure + Info, } } From 9277cf877e1f2276dcad1efdeb97e0e3d96ed3f0 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 12:02:22 +0800 Subject: [PATCH 09/13] Print basic index information, including the tree extension (#293) The latter is the one we have to maintain/create when creating and index from a tree. --- git-index/Cargo.toml | 2 +- git-index/src/extension/mod.rs | 6 +- git-index/src/extension/tree.rs | 4 +- git-index/src/lib.rs | 19 +++- gitoxide-core/src/index/entries.rs | 5 - gitoxide-core/src/index/information.rs | 152 +++++++++++++++++++++++++ gitoxide-core/src/index/mod.rs | 99 ++-------------- src/plumbing/main.rs | 4 +- 8 files changed, 185 insertions(+), 106 deletions(-) create mode 100644 gitoxide-core/src/index/information.rs diff --git a/git-index/Cargo.toml b/git-index/Cargo.toml index 9bbcfb5df7f..2abc92005e6 100644 --- a/git-index/Cargo.toml +++ b/git-index/Cargo.toml @@ -22,7 +22,7 @@ path = "tests/index-single-threaded.rs" required-features = ["internal-testing-to-avoid-being-run-by-cargo-test-all"] [features] -serde1 = ["serde"] +serde1 = ["serde", "smallvec/serde", "git-hash/serde1"] internal-testing-git-features-parallel = ["git-features/parallel"] internal-testing-to-avoid-being-run-by-cargo-test-all = [] diff --git a/git-index/src/extension/mod.rs b/git-index/src/extension/mod.rs index 7959a692b16..e30a41621af 100644 --- a/git-index/src/extension/mod.rs +++ b/git-index/src/extension/mod.rs @@ -15,10 +15,10 @@ pub struct Iter<'a> { /// It allows to more quickly build trees by avoiding as it can quickly re-use portions of the index and its associated tree ids /// if there was no change to them. Portions of this tree are invalidated as the index is changed. pub struct Tree { - name: SmallVec<[u8; 23]>, + pub name: SmallVec<[u8; 23]>, /// Only set if there are any entries in the index we are associated with. - id: Option, - children: Vec, + pub id: Option, + pub children: Vec, } pub struct Link { diff --git a/git-index/src/extension/tree.rs b/git-index/src/extension/tree.rs index f7d97fbefe2..f26c8cc785c 100644 --- a/git-index/src/extension/tree.rs +++ b/git-index/src/extension/tree.rs @@ -9,9 +9,9 @@ pub const SIGNATURE: Signature = *b"TREE"; pub struct NodeId { /// The id of the directory tree of the associated tree object. - id: git_hash::ObjectId, + pub id: git_hash::ObjectId, /// The amount of non-tree entries contained within, and definitely not zero. - entry_count: u32, + pub entry_count: u32, } /// A recursive data structure diff --git a/git-index/src/lib.rs b/git-index/src/lib.rs index f1beb940933..ef2237b1edc 100644 --- a/git-index/src/lib.rs +++ b/git-index/src/lib.rs @@ -7,12 +7,12 @@ use filetime::FileTime; pub mod file; -pub(crate) mod extension; +pub mod extension; pub mod entry; mod access { - use crate::{Entry, State, Version}; + use crate::{extension, Entry, State, Version}; impl State { pub fn version(&self) -> Version { @@ -22,6 +22,21 @@ mod access { pub fn entries(&self) -> &[Entry] { &self.entries } + pub fn tree(&self) -> Option<&extension::Tree> { + self.tree.as_ref() + } + pub fn link(&self) -> Option<&extension::Link> { + self.link.as_ref() + } + pub fn resolve_undo(&self) -> Option<&extension::resolve_undo::Paths> { + self.resolve_undo.as_ref() + } + pub fn untracked(&self) -> Option<&extension::UntrackedCache> { + self.untracked.as_ref() + } + pub fn fs_monitor(&self) -> Option<&extension::FsMonitor> { + self.fs_monitor.as_ref() + } } } diff --git a/gitoxide-core/src/index/entries.rs b/gitoxide-core/src/index/entries.rs index b324f859b08..df1d7e2e39f 100644 --- a/gitoxide-core/src/index/entries.rs +++ b/gitoxide-core/src/index/entries.rs @@ -1,11 +1,6 @@ use git_repository as git; use git_repository::bstr::ByteSlice; -pub struct Options { - pub object_hash: git::hash::Kind, - pub format: crate::OutputFormat, -} - #[cfg(feature = "serde1")] pub(crate) fn to_json( mut out: &mut impl std::io::Write, diff --git a/gitoxide-core/src/index/information.rs b/gitoxide-core/src/index/information.rs new file mode 100644 index 00000000000..cce5106b9a8 --- /dev/null +++ b/gitoxide-core/src/index/information.rs @@ -0,0 +1,152 @@ +use git_repository as git; +use std::convert::TryFrom; + +mod ext { + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] + pub(crate) struct Tree { + name: String, + /// Only set if there are any entries in the index we are associated with. + id: Option, + children: Vec, + } + + mod tree { + use git_repository as git; + use git_repository::bstr::ByteSlice; + + impl<'a> From<&'a git::index::extension::Tree> for super::Tree { + fn from(t: &'a git_repository::index::extension::Tree) -> Self { + super::Tree { + name: t.name.as_bstr().to_string(), + id: t.id.as_ref().map(|id| NodeId { + entry_count: id.entry_count, + id: id.id.to_hex().to_string(), + }), + children: t.children.iter().map(|t| t.into()).collect(), + } + } + } + + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + pub struct NodeId { + /// The id of the directory tree of the associated tree object. + id: String, + /// The amount of non-tree entries contained within, and definitely not zero. + entry_count: u32, + } + } +} + +#[cfg_attr(feature = "serde1", derive(serde::Serialize))] +pub(crate) struct EntryKind { + dir: usize, + file: usize, + executable: usize, + symlink: usize, + submodule: usize, + other: usize, +} + +#[cfg_attr(feature = "serde1", derive(serde::Serialize))] +pub(crate) struct EntryFlag { + intent_to_add: usize, + skip_worktree: usize, +} + +#[cfg_attr(feature = "serde1", derive(serde::Serialize))] +pub(crate) struct Entries { + stage_0: usize, + stage_1: usize, + stage_2: usize, + kind: EntryKind, + flags: EntryFlag, +} + +#[cfg_attr(feature = "serde1", derive(serde::Serialize))] +pub(crate) struct Extensions { + count: usize, + tree: Option, +} + +#[cfg_attr(feature = "serde1", derive(serde::Serialize))] +pub(crate) struct Collection { + version: u8, + checksum: String, + entries: Entries, + extensions: Extensions, +} + +impl TryFrom for Collection { + type Error = anyhow::Error; + + fn try_from(f: git::index::File) -> Result { + Ok(Collection { + version: f.version() as u8, + checksum: f.checksum.to_hex().to_string(), + extensions: { + let mut count = 0; + let tree = f.tree().map(|tree| { + count += 1; + tree.into() + }); + if let Some(_) = f.link() { + count += 1 + }; + if let Some(_) = f.resolve_undo() { + count += 1 + }; + if let Some(_) = f.untracked() { + count += 1 + }; + if let Some(_) = f.fs_monitor() { + count += 1 + }; + Extensions { count, tree } + }, + entries: { + let (mut stage_0, mut stage_1, mut stage_2) = (0, 0, 0); + let (mut dir, mut file, mut executable, mut symlink, mut submodule, mut other) = (0, 0, 0, 0, 0, 0); + let (mut intent_to_add, mut skip_worktree) = (0, 0); + for entry in f.entries() { + match entry.flags.stage() { + 0 => stage_0 += 1, + 1 => stage_1 += 1, + 2 => stage_2 += 1, + invalid => anyhow::bail!("Invalid stage {} encountered", invalid), + } + match entry.mode { + git::index::entry::Mode::DIR => dir += 1, + git::index::entry::Mode::FILE => file += 1, + git::index::entry::Mode::FILE_EXECUTABLE => executable += 1, + git::index::entry::Mode::SYMLINK => symlink += 1, + git::index::entry::Mode::COMMIT => submodule += 1, + _ => other += 1, + } + if entry.flags.contains(git::index::entry::Flags::INTENT_TO_ADD) { + intent_to_add += 1; + } + if entry.flags.contains(git::index::entry::Flags::SKIP_WORKTREE) { + skip_worktree += 1; + } + } + Entries { + stage_0, + stage_1, + stage_2, + kind: EntryKind { + dir, + file, + executable, + symlink, + submodule, + other, + }, + flags: EntryFlag { + intent_to_add, + skip_worktree, + }, + } + }, + }) + } +} diff --git a/gitoxide-core/src/index/mod.rs b/gitoxide-core/src/index/mod.rs index c7c65e83cd5..0fa62902e99 100644 --- a/gitoxide-core/src/index/mod.rs +++ b/gitoxide-core/src/index/mod.rs @@ -3,102 +3,19 @@ use std::path::Path; use git_repository as git; -pub mod entries; - -mod information { - use git_repository as git; - use std::convert::TryFrom; - - #[cfg_attr(feature = "serde1", derive(serde::Serialize))] - pub(crate) struct EntryKind { - dir: usize, - file: usize, - executable: usize, - symlink: usize, - submodule: usize, - other: usize, - } - - #[cfg_attr(feature = "serde1", derive(serde::Serialize))] - pub(crate) struct EntryFlag { - intent_to_add: usize, - skip_worktree: usize, - } - - #[cfg_attr(feature = "serde1", derive(serde::Serialize))] - pub(crate) struct Entries { - stage_0: usize, - stage_1: usize, - stage_2: usize, - kind: EntryKind, - flags: EntryFlag, - } - - #[cfg_attr(feature = "serde1", derive(serde::Serialize))] - pub(crate) struct Collection { - version: u8, - entries: Entries, - } +pub struct Options { + pub object_hash: git::hash::Kind, + pub format: crate::OutputFormat, +} - impl TryFrom for Collection { - type Error = anyhow::Error; +mod entries; - fn try_from(f: git::index::File) -> Result { - Ok(Collection { - version: f.version() as u8, - entries: { - let (mut stage_0, mut stage_1, mut stage_2) = (0, 0, 0); - let (mut dir, mut file, mut executable, mut symlink, mut submodule, mut other) = (0, 0, 0, 0, 0, 0); - let (mut intent_to_add, mut skip_worktree) = (0, 0); - for entry in f.entries() { - match entry.flags.stage() { - 0 => stage_0 += 1, - 1 => stage_1 += 1, - 2 => stage_2 += 1, - invalid => anyhow::bail!("Invalid stage {} encountered", invalid), - } - match entry.mode { - git::index::entry::Mode::DIR => dir += 1, - git::index::entry::Mode::FILE => file += 1, - git::index::entry::Mode::FILE_EXECUTABLE => executable += 1, - git::index::entry::Mode::SYMLINK => symlink += 1, - git::index::entry::Mode::COMMIT => submodule += 1, - _ => other += 1, - } - if entry.flags.contains(git::index::entry::Flags::INTENT_TO_ADD) { - intent_to_add += 1; - } - if entry.flags.contains(git::index::entry::Flags::SKIP_WORKTREE) { - skip_worktree += 1; - } - } - Entries { - stage_0, - stage_1, - stage_2, - kind: EntryKind { - dir, - file, - executable, - symlink, - submodule, - other, - }, - flags: EntryFlag { - intent_to_add, - skip_worktree, - }, - } - }, - }) - } - } -} +mod information; pub fn information( index_path: impl AsRef, out: impl std::io::Write, - entries::Options { object_hash, format }: entries::Options, + Options { object_hash, format }: Options, ) -> anyhow::Result<()> { use crate::OutputFormat::*; let info = information::Collection::try_from(parse_file(index_path, object_hash)?)?; @@ -115,7 +32,7 @@ pub fn information( pub fn entries( index_path: impl AsRef, mut out: impl std::io::Write, - entries::Options { object_hash, format }: entries::Options, + Options { object_hash, format }: Options, ) -> anyhow::Result<()> { use crate::OutputFormat::*; let file = parse_file(index_path, object_hash)?; diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index c05209c82c1..d410daea355 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -85,7 +85,7 @@ pub fn main() -> Result<()> { progress_keep_open, None, move |_progress, out, _err| { - core::index::information(index_path, out, core::index::entries::Options { object_hash, format }) + core::index::information(index_path, out, core::index::Options { object_hash, format }) }, ), index::Subcommands::Entries => prepare_and_run( @@ -95,7 +95,7 @@ pub fn main() -> Result<()> { progress_keep_open, None, move |_progress, out, _err| { - core::index::entries(index_path, out, core::index::entries::Options { object_hash, format }) + core::index::entries(index_path, out, core::index::Options { object_hash, format }) }, ), }, From 3aba4b4877a11b720a02f4a246e6fa6ac6327119 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 12:04:08 +0800 Subject: [PATCH 10/13] thanks clippy --- gitoxide-core/src/index/entries.rs | 3 ++- gitoxide-core/src/index/information.rs | 8 ++++---- gitoxide-core/src/index/mod.rs | 12 ++++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/gitoxide-core/src/index/entries.rs b/gitoxide-core/src/index/entries.rs index df1d7e2e39f..01e718824c5 100644 --- a/gitoxide-core/src/index/entries.rs +++ b/gitoxide-core/src/index/entries.rs @@ -1,5 +1,4 @@ use git_repository as git; -use git_repository::bstr::ByteSlice; #[cfg(feature = "serde1")] pub(crate) fn to_json( @@ -8,6 +7,8 @@ pub(crate) fn to_json( entry: &git::index::Entry, is_last: bool, ) -> anyhow::Result<()> { + use git_repository::bstr::ByteSlice; + #[cfg_attr(feature = "serde1", derive(serde::Serialize))] struct Entry<'a> { stat: &'a git::index::entry::Stat, diff --git a/gitoxide-core/src/index/information.rs b/gitoxide-core/src/index/information.rs index cce5106b9a8..8649aae5e87 100644 --- a/gitoxide-core/src/index/information.rs +++ b/gitoxide-core/src/index/information.rs @@ -89,16 +89,16 @@ impl TryFrom for Collection { count += 1; tree.into() }); - if let Some(_) = f.link() { + if f.link().is_some() { count += 1 }; - if let Some(_) = f.resolve_undo() { + if f.resolve_undo().is_some() { count += 1 }; - if let Some(_) = f.untracked() { + if f.untracked().is_some() { count += 1 }; - if let Some(_) = f.fs_monitor() { + if f.fs_monitor().is_some() { count += 1 }; Extensions { count, tree } diff --git a/gitoxide-core/src/index/mod.rs b/gitoxide-core/src/index/mod.rs index 0fa62902e99..6b31dadb358 100644 --- a/gitoxide-core/src/index/mod.rs +++ b/gitoxide-core/src/index/mod.rs @@ -1,4 +1,3 @@ -use std::convert::TryFrom; use std::path::Path; use git_repository as git; @@ -10,23 +9,28 @@ pub struct Options { mod entries; +#[cfg(feature = "serde1")] mod information; +#[cfg_attr(not(feature = "serde1"), allow(unused_variables))] pub fn information( index_path: impl AsRef, out: impl std::io::Write, Options { object_hash, format }: Options, ) -> anyhow::Result<()> { use crate::OutputFormat::*; - let info = information::Collection::try_from(parse_file(index_path, object_hash)?)?; match format { Human => { anyhow::bail!("Only JSON output is implemented"); } #[cfg(feature = "serde1")] - Json => serde_json::to_writer_pretty(out, &info)?, + Json => { + use std::convert::TryFrom; + let info = information::Collection::try_from(parse_file(index_path, object_hash)?)?; + serde_json::to_writer_pretty(out, &info)?; + Ok(()) + } } - Ok(()) } pub fn entries( From 34ea001fafa93b6453513cf458fe24327a13ff28 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 13:27:37 +0800 Subject: [PATCH 11/13] Flag to hide extension details (#293) That way understanding which extension is present is a bit easier --- gitoxide-core/src/index/information.rs | 17 ++++++++++------- gitoxide-core/src/index/mod.rs | 10 ++++++---- src/plumbing/main.rs | 11 +++++++++-- src/plumbing/options.rs | 6 +++++- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/gitoxide-core/src/index/information.rs b/gitoxide-core/src/index/information.rs index 8649aae5e87..238f04c2ce0 100644 --- a/gitoxide-core/src/index/information.rs +++ b/gitoxide-core/src/index/information.rs @@ -1,5 +1,10 @@ use git_repository as git; -use std::convert::TryFrom; + +pub struct Options { + pub index: super::Options, + /// If true, show exstension in detail. + pub extension_details: bool, +} mod ext { #[cfg_attr(feature = "serde1", derive(serde::Serialize))] @@ -76,18 +81,16 @@ pub(crate) struct Collection { extensions: Extensions, } -impl TryFrom for Collection { - type Error = anyhow::Error; - - fn try_from(f: git::index::File) -> Result { +impl Collection { + pub fn try_from_file(f: git::index::File, extension_details: bool) -> anyhow::Result { Ok(Collection { version: f.version() as u8, checksum: f.checksum.to_hex().to_string(), extensions: { let mut count = 0; - let tree = f.tree().map(|tree| { + let tree = f.tree().and_then(|tree| { count += 1; - tree.into() + extension_details.then(|| tree.into()) }); if f.link().is_some() { count += 1 diff --git a/gitoxide-core/src/index/mod.rs b/gitoxide-core/src/index/mod.rs index 6b31dadb358..d8f2e8c2275 100644 --- a/gitoxide-core/src/index/mod.rs +++ b/gitoxide-core/src/index/mod.rs @@ -10,13 +10,16 @@ pub struct Options { mod entries; #[cfg(feature = "serde1")] -mod information; +pub mod information; #[cfg_attr(not(feature = "serde1"), allow(unused_variables))] pub fn information( index_path: impl AsRef, out: impl std::io::Write, - Options { object_hash, format }: Options, + information::Options { + index: Options { object_hash, format }, + extension_details, + }: information::Options, ) -> anyhow::Result<()> { use crate::OutputFormat::*; match format { @@ -25,8 +28,7 @@ pub fn information( } #[cfg(feature = "serde1")] Json => { - use std::convert::TryFrom; - let info = information::Collection::try_from(parse_file(index_path, object_hash)?)?; + let info = information::Collection::try_from_file(parse_file(index_path, object_hash)?, extension_details)?; serde_json::to_writer_pretty(out, &info)?; Ok(()) } diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index d410daea355..2fff55607bd 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -78,14 +78,21 @@ pub fn main() -> Result<()> { index_path, cmd, }) => match cmd { - index::Subcommands::Info => prepare_and_run( + index::Subcommands::Info { no_details } => prepare_and_run( "index-entries", verbose, progress, progress_keep_open, None, move |_progress, out, _err| { - core::index::information(index_path, out, core::index::Options { object_hash, format }) + core::index::information( + index_path, + out, + core::index::information::Options { + index: core::index::Options { object_hash, format }, + extension_details: !no_details, + }, + ) }, ), index::Subcommands::Entries => prepare_and_run( diff --git a/src/plumbing/options.rs b/src/plumbing/options.rs index 59de45761b0..df5b4d1769e 100644 --- a/src/plumbing/options.rs +++ b/src/plumbing/options.rs @@ -356,7 +356,11 @@ pub mod index { /// Print all entries to standard output Entries, /// Print information about the index structure - Info, + Info { + /// Do not extract specific extension information to gain only a superficial idea of the index's composition. + #[clap(long)] + no_details: bool, + }, } } From 1cc07e0cfdae74e388abb29d7acb9c6f643278b4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 13:32:38 +0800 Subject: [PATCH 12/13] Print extension names instead of count (#293) More helpful if no details are provided. --- git-index/src/extension/fs_monitor.rs | 37 ++++++++++++++++++++++++ git-index/src/extension/mod.rs | 40 +------------------------- gitoxide-core/src/index/information.rs | 16 +++++------ 3 files changed, 46 insertions(+), 47 deletions(-) create mode 100644 git-index/src/extension/fs_monitor.rs diff --git a/git-index/src/extension/fs_monitor.rs b/git-index/src/extension/fs_monitor.rs new file mode 100644 index 00000000000..a58885bdb6a --- /dev/null +++ b/git-index/src/extension/fs_monitor.rs @@ -0,0 +1,37 @@ +use bstr::BString; + +use crate::{ + extension::{FsMonitor, Signature}, + util::{read_u32, read_u64, split_at_byte_exclusive}, +}; + +pub enum Token { + V1 { nanos_since_1970: u64 }, + V2 { token: BString }, +} + +pub const SIGNATURE: Signature = *b"FSMN"; + +pub fn decode(data: &[u8]) -> Option { + let (version, data) = read_u32(data)?; + let (token, data) = match version { + 1 => { + let (nanos_since_1970, data) = read_u64(data)?; + (Token::V1 { nanos_since_1970 }, data) + } + 2 => { + let (token, data) = split_at_byte_exclusive(data, 0)?; + (Token::V2 { token: token.into() }, data) + } + _ => return None, + }; + + let (ewah_size, data) = read_u32(data)?; + let (entry_dirty, data) = git_bitmap::ewah::decode(&data[..ewah_size as usize]).ok()?; + + if !data.is_empty() { + return None; + } + + FsMonitor { token, entry_dirty }.into() +} diff --git a/git-index/src/extension/mod.rs b/git-index/src/extension/mod.rs index e30a41621af..ba0b48b10a4 100644 --- a/git-index/src/extension/mod.rs +++ b/git-index/src/extension/mod.rs @@ -50,45 +50,7 @@ pub struct FsMonitor { mod iter; -pub(crate) mod fs_monitor { - use bstr::BString; - - use crate::{ - extension::{FsMonitor, Signature}, - util::{read_u32, read_u64, split_at_byte_exclusive}, - }; - - pub enum Token { - V1 { nanos_since_1970: u64 }, - V2 { token: BString }, - } - - pub const SIGNATURE: Signature = *b"FSMN"; - - pub fn decode(data: &[u8]) -> Option { - let (version, data) = read_u32(data)?; - let (token, data) = match version { - 1 => { - let (nanos_since_1970, data) = read_u64(data)?; - (Token::V1 { nanos_since_1970 }, data) - } - 2 => { - let (token, data) = split_at_byte_exclusive(data, 0)?; - (Token::V2 { token: token.into() }, data) - } - _ => return None, - }; - - let (ewah_size, data) = read_u32(data)?; - let (entry_dirty, data) = git_bitmap::ewah::decode(&data[..ewah_size as usize]).ok()?; - - if !data.is_empty() { - return None; - } - - FsMonitor { token, entry_dirty }.into() - } -} +pub(crate) mod fs_monitor; pub(crate) mod decode; diff --git a/gitoxide-core/src/index/information.rs b/gitoxide-core/src/index/information.rs index 238f04c2ce0..ce93fbc8444 100644 --- a/gitoxide-core/src/index/information.rs +++ b/gitoxide-core/src/index/information.rs @@ -69,7 +69,7 @@ pub(crate) struct Entries { #[cfg_attr(feature = "serde1", derive(serde::Serialize))] pub(crate) struct Extensions { - count: usize, + names: Vec<&'static str>, tree: Option, } @@ -87,24 +87,24 @@ impl Collection { version: f.version() as u8, checksum: f.checksum.to_hex().to_string(), extensions: { - let mut count = 0; + let mut names = Vec::new(); let tree = f.tree().and_then(|tree| { - count += 1; + names.push("tree (TREE)"); extension_details.then(|| tree.into()) }); if f.link().is_some() { - count += 1 + names.push("link"); }; if f.resolve_undo().is_some() { - count += 1 + names.push("resolve-undo (REUC)"); }; if f.untracked().is_some() { - count += 1 + names.push("untracked (UNTR)"); }; if f.fs_monitor().is_some() { - count += 1 + names.push("fs-monitor (FSMN)"); }; - Extensions { count, tree } + Extensions { names, tree } }, entries: { let (mut stage_0, mut stage_1, mut stage_2) = (0, 0, 0); From 3541e3329574fbb694450bead62b71a9af1d336e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 24 Jan 2022 13:54:44 +0800 Subject: [PATCH 13/13] refactor (#293) --- gitoxide-core/src/index/information.rs | 268 +++++++++++++------------ gitoxide-core/src/index/mod.rs | 1 - 2 files changed, 136 insertions(+), 133 deletions(-) diff --git a/gitoxide-core/src/index/information.rs b/gitoxide-core/src/index/information.rs index ce93fbc8444..189facf709d 100644 --- a/gitoxide-core/src/index/information.rs +++ b/gitoxide-core/src/index/information.rs @@ -1,155 +1,159 @@ -use git_repository as git; - pub struct Options { pub index: super::Options, /// If true, show exstension in detail. pub extension_details: bool, } -mod ext { - #[cfg_attr(feature = "serde1", derive(serde::Serialize))] - pub(crate) struct Tree { - name: String, - /// Only set if there are any entries in the index we are associated with. - id: Option, - children: Vec, - } +#[cfg(feature = "serde1")] +mod serde_only { + use git_repository as git; + mod ext { + #[derive(serde::Serialize)] + pub(crate) struct Tree { + name: String, + /// Only set if there are any entries in the index we are associated with. + id: Option, + children: Vec, + } - mod tree { - use git_repository as git; - use git_repository::bstr::ByteSlice; + mod tree { + use git_repository as git; + use git_repository::bstr::ByteSlice; - impl<'a> From<&'a git::index::extension::Tree> for super::Tree { - fn from(t: &'a git_repository::index::extension::Tree) -> Self { - super::Tree { - name: t.name.as_bstr().to_string(), - id: t.id.as_ref().map(|id| NodeId { - entry_count: id.entry_count, - id: id.id.to_hex().to_string(), - }), - children: t.children.iter().map(|t| t.into()).collect(), + impl<'a> From<&'a git::index::extension::Tree> for super::Tree { + fn from(t: &'a git_repository::index::extension::Tree) -> Self { + super::Tree { + name: t.name.as_bstr().to_string(), + id: t.id.as_ref().map(|id| NodeId { + entry_count: id.entry_count, + id: id.id.to_hex().to_string(), + }), + children: t.children.iter().map(|t| t.into()).collect(), + } } } - } - #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] - pub struct NodeId { - /// The id of the directory tree of the associated tree object. - id: String, - /// The amount of non-tree entries contained within, and definitely not zero. - entry_count: u32, + #[derive(serde::Serialize, serde::Deserialize)] + pub struct NodeId { + /// The id of the directory tree of the associated tree object. + id: String, + /// The amount of non-tree entries contained within, and definitely not zero. + entry_count: u32, + } } } -} -#[cfg_attr(feature = "serde1", derive(serde::Serialize))] -pub(crate) struct EntryKind { - dir: usize, - file: usize, - executable: usize, - symlink: usize, - submodule: usize, - other: usize, -} + #[derive(serde::Serialize)] + pub(crate) struct EntryKind { + dir: usize, + file: usize, + executable: usize, + symlink: usize, + submodule: usize, + other: usize, + } -#[cfg_attr(feature = "serde1", derive(serde::Serialize))] -pub(crate) struct EntryFlag { - intent_to_add: usize, - skip_worktree: usize, -} + #[derive(serde::Serialize)] + pub(crate) struct EntryFlag { + intent_to_add: usize, + skip_worktree: usize, + } -#[cfg_attr(feature = "serde1", derive(serde::Serialize))] -pub(crate) struct Entries { - stage_0: usize, - stage_1: usize, - stage_2: usize, - kind: EntryKind, - flags: EntryFlag, -} + #[derive(serde::Serialize)] + pub struct Entries { + stage_0: usize, + stage_1: usize, + stage_2: usize, + kind: EntryKind, + flags: EntryFlag, + } -#[cfg_attr(feature = "serde1", derive(serde::Serialize))] -pub(crate) struct Extensions { - names: Vec<&'static str>, - tree: Option, -} + #[derive(serde::Serialize)] + pub struct Extensions { + names: Vec<&'static str>, + tree: Option, + } -#[cfg_attr(feature = "serde1", derive(serde::Serialize))] -pub(crate) struct Collection { - version: u8, - checksum: String, - entries: Entries, - extensions: Extensions, -} + #[derive(serde::Serialize)] + pub struct Collection { + version: u8, + checksum: String, + entries: Entries, + extensions: Extensions, + } -impl Collection { - pub fn try_from_file(f: git::index::File, extension_details: bool) -> anyhow::Result { - Ok(Collection { - version: f.version() as u8, - checksum: f.checksum.to_hex().to_string(), - extensions: { - let mut names = Vec::new(); - let tree = f.tree().and_then(|tree| { - names.push("tree (TREE)"); - extension_details.then(|| tree.into()) - }); - if f.link().is_some() { - names.push("link"); - }; - if f.resolve_undo().is_some() { - names.push("resolve-undo (REUC)"); - }; - if f.untracked().is_some() { - names.push("untracked (UNTR)"); - }; - if f.fs_monitor().is_some() { - names.push("fs-monitor (FSMN)"); - }; - Extensions { names, tree } - }, - entries: { - let (mut stage_0, mut stage_1, mut stage_2) = (0, 0, 0); - let (mut dir, mut file, mut executable, mut symlink, mut submodule, mut other) = (0, 0, 0, 0, 0, 0); - let (mut intent_to_add, mut skip_worktree) = (0, 0); - for entry in f.entries() { - match entry.flags.stage() { - 0 => stage_0 += 1, - 1 => stage_1 += 1, - 2 => stage_2 += 1, - invalid => anyhow::bail!("Invalid stage {} encountered", invalid), + impl Collection { + pub fn try_from_file(f: git::index::File, extension_details: bool) -> anyhow::Result { + Ok(Collection { + version: f.version() as u8, + checksum: f.checksum.to_hex().to_string(), + extensions: { + let mut names = Vec::new(); + let tree = f.tree().and_then(|tree| { + names.push("tree (TREE)"); + extension_details.then(|| tree.into()) + }); + if f.link().is_some() { + names.push("link"); + }; + if f.resolve_undo().is_some() { + names.push("resolve-undo (REUC)"); + }; + if f.untracked().is_some() { + names.push("untracked (UNTR)"); + }; + if f.fs_monitor().is_some() { + names.push("fs-monitor (FSMN)"); + }; + Extensions { names, tree } + }, + entries: { + let (mut stage_0, mut stage_1, mut stage_2) = (0, 0, 0); + let (mut dir, mut file, mut executable, mut symlink, mut submodule, mut other) = (0, 0, 0, 0, 0, 0); + let (mut intent_to_add, mut skip_worktree) = (0, 0); + for entry in f.entries() { + match entry.flags.stage() { + 0 => stage_0 += 1, + 1 => stage_1 += 1, + 2 => stage_2 += 1, + invalid => anyhow::bail!("Invalid stage {} encountered", invalid), + } + match entry.mode { + git::index::entry::Mode::DIR => dir += 1, + git::index::entry::Mode::FILE => file += 1, + git::index::entry::Mode::FILE_EXECUTABLE => executable += 1, + git::index::entry::Mode::SYMLINK => symlink += 1, + git::index::entry::Mode::COMMIT => submodule += 1, + _ => other += 1, + } + if entry.flags.contains(git::index::entry::Flags::INTENT_TO_ADD) { + intent_to_add += 1; + } + if entry.flags.contains(git::index::entry::Flags::SKIP_WORKTREE) { + skip_worktree += 1; + } } - match entry.mode { - git::index::entry::Mode::DIR => dir += 1, - git::index::entry::Mode::FILE => file += 1, - git::index::entry::Mode::FILE_EXECUTABLE => executable += 1, - git::index::entry::Mode::SYMLINK => symlink += 1, - git::index::entry::Mode::COMMIT => submodule += 1, - _ => other += 1, + Entries { + stage_0, + stage_1, + stage_2, + kind: EntryKind { + dir, + file, + executable, + symlink, + submodule, + other, + }, + flags: EntryFlag { + intent_to_add, + skip_worktree, + }, } - if entry.flags.contains(git::index::entry::Flags::INTENT_TO_ADD) { - intent_to_add += 1; - } - if entry.flags.contains(git::index::entry::Flags::SKIP_WORKTREE) { - skip_worktree += 1; - } - } - Entries { - stage_0, - stage_1, - stage_2, - kind: EntryKind { - dir, - file, - executable, - symlink, - submodule, - other, - }, - flags: EntryFlag { - intent_to_add, - skip_worktree, - }, - } - }, - }) + }, + }) + } } } +#[cfg(feature = "serde1")] +pub(crate) use serde_only::Collection; diff --git a/gitoxide-core/src/index/mod.rs b/gitoxide-core/src/index/mod.rs index d8f2e8c2275..a1a9f1fa4d0 100644 --- a/gitoxide-core/src/index/mod.rs +++ b/gitoxide-core/src/index/mod.rs @@ -9,7 +9,6 @@ pub struct Options { mod entries; -#[cfg(feature = "serde1")] pub mod information; #[cfg_attr(not(feature = "serde1"), allow(unused_variables))]