From 259b38e629807e887442ca6140a14cefcd672699 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Mon, 4 Mar 2024 17:31:17 +0100 Subject: [PATCH 01/26] fix usage of tracing span (which now is just a message) --- gix-index/src/access/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gix-index/src/access/mod.rs b/gix-index/src/access/mod.rs index 431a47e5805..d5da3c9934a 100644 --- a/gix-index/src/access/mod.rs +++ b/gix-index/src/access/mod.rs @@ -173,7 +173,7 @@ impl State { } } } - gix_features::trace::detail!("stored directories", directories = out.icase_dirs.len()); + gix_features::trace::debug!(directories = out.icase_dirs.len(), "stored directories"); out } From 57cf83b57b0de01bd69f63ec3637859ccd757272 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Tue, 27 Feb 2024 09:44:49 +0100 Subject: [PATCH 02/26] feat!: `diff::resource_cache()` now takes the attribute stack directly. That way, the constructor becaomes more versatile as the user can chose to pass attribute stacks that have more functionality, and thus can be used in more places. --- gix/src/diff.rs | 18 ++++-------------- gix/src/repository/diff.rs | 18 ++++++++++++------ gix/tests/diff/mod.rs | 5 +++-- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/gix/src/diff.rs b/gix/src/diff.rs index af3c987048a..5d15193b270 100644 --- a/gix/src/diff.rs +++ b/gix/src/diff.rs @@ -56,8 +56,6 @@ mod utils { DiffPipelineOptions(#[from] crate::config::diff::pipeline_options::Error), #[error(transparent)] CommandContext(#[from] crate::config::command_context::Error), - #[error(transparent)] - AttributeStack(#[from] crate::config::attribute_stack::Error), } } @@ -102,18 +100,16 @@ mod utils { /// Return a low-level utility to efficiently prepare a the blob-level diff operation between two resources, /// and cache these diffable versions so that matrix-like MxN diffs are efficient. /// - /// `repo` is used to obtain the needed configuration values, and `index` is used to potentially read `.gitattributes` - /// files from which may affect the diff operation. + /// `repo` is used to obtain the needed configuration values. /// `mode` determines how the diffable files will look like, and also how fast, in average, these conversions are. - /// `attribute_source` controls where `.gitattributes` will be read from, and it's typically adjusted based on the + /// `attr_stack` is for accessing `.gitattributes` for knowing how to apply filters. Noow that it's typically adjusted based on the /// `roots` - if there are no worktree roots, `.gitattributes` are also not usually read from worktrees. /// `roots` provide information about where to get diffable data from, so source and destination can either be sourced from /// a worktree, or from the object database, or both. pub fn resource_cache( repo: &Repository, - index: &gix_index::State, mode: gix_diff::blob::pipeline::Mode, - attribute_source: gix_worktree::stack::state::attributes::Source, + attr_stack: gix_worktree::Stack, roots: gix_diff::blob::pipeline::WorktreeRoots, ) -> Result<gix_diff::blob::Platform, resource_cache::Error> { let diff_algo = repo.config.diff_algorithm()?; @@ -129,13 +125,7 @@ mod utils { repo.config.diff_pipeline_options()?, ), mode, - repo.attributes_only( - // TODO(perf): this could benefit from not having to build an intermediate index, - // and traverse the a tree directly. - index, - attribute_source, - )? - .inner, + attr_stack, ); Ok(diff_cache) } diff --git a/gix/src/repository/diff.rs b/gix/src/repository/diff.rs index cb1d070a2f4..8ce53ec3410 100644 --- a/gix/src/repository/diff.rs +++ b/gix/src/repository/diff.rs @@ -10,6 +10,8 @@ pub mod resource_cache { ResourceCache(#[from] crate::diff::resource_cache::Error), #[error(transparent)] Index(#[from] crate::repository::index_or_load_from_head::Error), + #[error(transparent)] + AttributeStack(#[from] crate::config::attribute_stack::Error), } } @@ -30,15 +32,19 @@ impl Repository { mode: gix_diff::blob::pipeline::Mode, worktree_roots: gix_diff::blob::pipeline::WorktreeRoots, ) -> Result<gix_diff::blob::Platform, resource_cache::Error> { + let index = self.index_or_load_from_head()?; Ok(crate::diff::resource_cache( self, - &*self.index_or_load_from_head()?, mode, - if worktree_roots.new_root.is_some() || worktree_roots.old_root.is_some() { - gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping - } else { - gix_worktree::stack::state::attributes::Source::IdMapping - }, + self.attributes_only( + &index, + if worktree_roots.new_root.is_some() || worktree_roots.old_root.is_some() { + gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping + } else { + gix_worktree::stack::state::attributes::Source::IdMapping + }, + )? + .inner, worktree_roots, )?) } diff --git a/gix/tests/diff/mod.rs b/gix/tests/diff/mod.rs index 2fa681ae981..2b9fa79401b 100644 --- a/gix/tests/diff/mod.rs +++ b/gix/tests/diff/mod.rs @@ -5,11 +5,12 @@ use crate::util::named_repo; #[test] fn resource_cache() -> crate::Result { let repo = named_repo("make_diff_repo.sh")?; + let index = repo.index()?; let cache = gix::diff::resource_cache( &repo, - &*repo.index()?, gix::diff::blob::pipeline::Mode::ToWorktreeAndBinaryToText, - gix_worktree::stack::state::attributes::Source::IdMapping, + repo.attributes_only(&index, gix_worktree::stack::state::attributes::Source::IdMapping)? + .detach(), Default::default(), )?; assert_eq!( From e20b6b1b9e767a07c5bc135c45cf7dccc878b99c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Tue, 27 Feb 2024 10:46:49 +0100 Subject: [PATCH 03/26] feat!: Provide more source information when emitting rewrites. Previously, the source was entirely missing, now it's also made available. Further, all the cloning of these resources is now left to the user, which should safe time. --- Cargo.lock | 1 + gix-diff/src/rewrites/tracker.rs | 19 +++++++--- gix-diff/tests/Cargo.toml | 1 + .../generated-archives/make_diff_repo.tar.xz | Bin 19236 -> 18440 bytes gix-diff/tests/rewrites/mod.rs | 2 +- gix-diff/tests/rewrites/tracker.rs | 35 ++++++++++++++---- 6 files changed, 44 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index deac531b0d6..0b1c07ad9ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1671,6 +1671,7 @@ dependencies = [ "gix-testtools", "gix-traverse 0.37.0", "gix-worktree 0.31.0", + "pretty_assertions", "shell-words", ] diff --git a/gix-diff/src/rewrites/tracker.rs b/gix-diff/src/rewrites/tracker.rs index 4caefcd34d4..6652efb25e7 100644 --- a/gix-diff/src/rewrites/tracker.rs +++ b/gix-diff/src/rewrites/tracker.rs @@ -83,7 +83,7 @@ pub mod visit { /// The source of a rewrite, rename or copy. #[derive(Debug, Clone, PartialEq, PartialOrd)] - pub struct Source<'a> { + pub struct Source<'a, T> { /// The kind of entry. pub entry_mode: EntryMode, /// The hash of the state of the source as seen in the object database. @@ -92,6 +92,8 @@ pub mod visit { pub kind: SourceKind, /// The repository-relative location of this entry. pub location: &'a BStr, + /// The change that was registered as source. + pub change: &'a T, /// If this is a rewrite, indicate how many lines would need to change to turn this source into the destination. pub diff: Option<DiffLineStats>, } @@ -193,7 +195,7 @@ impl<T: Change> Tracker<T> { /// will panic if `change` is not a modification, and it's valid to not call `push` at all. pub fn emit<PushSourceTreeFn, E>( &mut self, - mut cb: impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_>>) -> crate::tree::visit::Action, + mut cb: impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_, T>>) -> crate::tree::visit::Action, diff_cache: &mut crate::blob::Platform, objects: &impl gix_object::FindObjectOrHeader, mut push_source_tree: PushSourceTreeFn, @@ -283,7 +285,7 @@ impl<T: Change> Tracker<T> { fn match_pairs_of_kind( &mut self, kind: visit::SourceKind, - cb: &mut impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_>>) -> crate::tree::visit::Action, + cb: &mut impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_, T>>) -> crate::tree::visit::Action, percentage: Option<f32>, out: &mut Outcome, diff_cache: &mut crate::blob::Platform, @@ -326,7 +328,7 @@ impl<T: Change> Tracker<T> { fn match_pairs( &mut self, - cb: &mut impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_>>) -> crate::tree::visit::Action, + cb: &mut impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_, T>>) -> crate::tree::visit::Action, percentage: Option<f32>, kind: visit::SourceKind, stats: &mut Outcome, @@ -360,6 +362,7 @@ impl<T: Change> Tracker<T> { id, kind, location, + change: &src.change, diff, }, src_idx, @@ -371,11 +374,15 @@ impl<T: Change> Tracker<T> { let location = dest.location(&self.path_backing); let change = dest.change.clone(); let dest = visit::Destination { change, location }; + let src_idx = src.as_ref().map(|t| t.1); + let res = cb(dest, src.map(|t| t.0)); + self.items[dest_idx].emitted = true; - if let Some(src_idx) = src.as_ref().map(|t| t.1) { + if let Some(src_idx) = src_idx { self.items[src_idx].emitted = true; } - if cb(dest, src.map(|t| t.0)) == crate::tree::visit::Action::Cancel { + + if res == crate::tree::visit::Action::Cancel { return Ok(crate::tree::visit::Action::Cancel); } } diff --git a/gix-diff/tests/Cargo.toml b/gix-diff/tests/Cargo.toml index e63c3d5a320..695508cf1aa 100644 --- a/gix-diff/tests/Cargo.toml +++ b/gix-diff/tests/Cargo.toml @@ -25,3 +25,4 @@ gix-filter = { path = "../../gix-filter" } gix-traverse = { path = "../../gix-traverse" } gix-testtools = { path = "../../tests/tools" } shell-words = "1" +pretty_assertions = "1.4.0" diff --git a/gix-diff/tests/fixtures/generated-archives/make_diff_repo.tar.xz b/gix-diff/tests/fixtures/generated-archives/make_diff_repo.tar.xz index 18e3c75e65181b1088c48b8976097795f286ff8a..64826afc64692511672ac00cd1f81136c61d64ab 100644 GIT binary patch literal 18440 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 literal 19236 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 L0ssI200dcDIbm0i diff --git a/gix-diff/tests/rewrites/mod.rs b/gix-diff/tests/rewrites/mod.rs index be44ef39eda..ddcb12dfc86 100644 --- a/gix-diff/tests/rewrites/mod.rs +++ b/gix-diff/tests/rewrites/mod.rs @@ -4,7 +4,7 @@ use gix_object::tree::{EntryKind, EntryMode}; mod tracker; -#[derive(Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Change { id: ObjectId, kind: ChangeKind, diff --git a/gix-diff/tests/rewrites/tracker.rs b/gix-diff/tests/rewrites/tracker.rs index f522fbec312..2cc5d1b6527 100644 --- a/gix-diff/tests/rewrites/tracker.rs +++ b/gix-diff/tests/rewrites/tracker.rs @@ -12,6 +12,7 @@ use gix_diff::{ Rewrites, }; use gix_object::tree::EntryKind; +use pretty_assertions::assert_eq; use crate::{ hex_to_id, @@ -51,6 +52,7 @@ fn rename_by_id() -> crate::Result { id: NULL_ID, kind: SourceKind::Rename, location: "b".into(), + change: &Change::deletion(), diff: None, } ); @@ -146,11 +148,16 @@ fn copy_by_id() -> crate::Result { let out = util::assert_emit_with_objects( &mut track, |dst, src| { + let id = hex_to_id("2e65efe2a145dda7ee51d1741299f848e5bf752e"); let source_a = Source { entry_mode: EntryKind::Blob.into(), - id: hex_to_id("2e65efe2a145dda7ee51d1741299f848e5bf752e"), + id: id, kind: SourceKind::Copy, location: "a".into(), + change: &Change { + id, + ..Change::modification() + }, diff: None, }; match calls { @@ -219,6 +226,10 @@ fn copy_by_id_search_in_all_sources() -> crate::Result { id: content_id, kind: SourceKind::Copy, location: "a-src".into(), + change: &Change { + id: content_id, + ..Change::modification() + }, diff: None, }; match calls { @@ -289,11 +300,16 @@ fn copy_by_50_percent_similarity() -> crate::Result { let out = util::assert_emit_with_objects( &mut track, |dst, src| { + let id = hex_to_id("78981922613b2afb6025042ff6bd878ac1994e85"); let source_a = Source { entry_mode: EntryKind::Blob.into(), - id: hex_to_id("78981922613b2afb6025042ff6bd878ac1994e85"), + id: id, kind: SourceKind::Copy, location: "a".into(), + change: &Change { + id, + ..Change::modification() + }, diff: Some(DiffLineStats { removals: 0, insertions: 1, @@ -459,13 +475,18 @@ fn rename_by_50_percent_similarity() -> crate::Result { |dst, src| { match calls { 0 => { + let id = hex_to_id("66a52ee7a1d803dc57859c3e95ac9dcdc87c0164"); assert_eq!( src.unwrap(), Source { entry_mode: EntryKind::Blob.into(), - id: hex_to_id("66a52ee7a1d803dc57859c3e95ac9dcdc87c0164"), + id: id, kind: SourceKind::Rename, location: "a".into(), + change: &Change { + id, + ..Change::deletion() + }, diff: Some(DiffLineStats { removals: 1, insertions: 1, @@ -532,7 +553,7 @@ fn add_only() -> crate::Result { let out = util::assert_emit(&mut track, |dst, src| { assert!(!called); called = true; - assert_eq!(src, None, "there is just a single deletion, no pair"); + assert!(src.is_none(), "there is just a single deletion, no pair"); assert_eq!(dst.location, "a"); assert_eq!(dst.change.kind, ChangeKind::Addition); Action::Continue @@ -569,14 +590,14 @@ mod util { pub fn assert_emit( tracker: &mut rewrites::Tracker<Change>, - cb: impl FnMut(Destination<'_, Change>, Option<Source<'_>>) -> Action, + cb: impl FnMut(Destination<'_, Change>, Option<Source<'_, Change>>) -> Action, ) -> rewrites::Outcome { assert_emit_with_objects(tracker, cb, gix_object::find::Never) } pub fn assert_emit_with_objects( tracker: &mut rewrites::Tracker<Change>, - cb: impl FnMut(Destination<'_, Change>, Option<Source<'_>>) -> Action, + cb: impl FnMut(Destination<'_, Change>, Option<Source<'_, Change>>) -> Action, objects: impl gix_object::FindObjectOrHeader, ) -> rewrites::Outcome { assert_emit_with_objects_and_sources(tracker, cb, objects, None) @@ -584,7 +605,7 @@ mod util { pub fn assert_emit_with_objects_and_sources<'a>( tracker: &mut rewrites::Tracker<Change>, - cb: impl FnMut(Destination<'_, Change>, Option<Source<'_>>) -> Action, + cb: impl FnMut(Destination<'_, Change>, Option<Source<'_, Change>>) -> Action, objects: impl gix_object::FindObjectOrHeader, sources: impl IntoIterator<Item = (Change, &'a str)>, ) -> rewrites::Outcome { From 80a6cb80fdb3a49c3461d286d62baf68755aa733 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 15 Feb 2024 17:08:07 +0100 Subject: [PATCH 04/26] feat: add index-to-worktree status with rename tracking --- Cargo.lock | 9 + crate-status.md | 8 +- gix-diff/tests/rewrites/tracker.rs | 6 +- gix-status/Cargo.toml | 14 +- gix-status/src/index_as_worktree/mod.rs | 2 +- gix-status/src/index_as_worktree/recorder.rs | 2 +- gix-status/src/index_as_worktree/types.rs | 5 +- .../src/index_as_worktree_with_renames/mod.rs | 564 ++++++++++++++++++ .../recorder.rs | 17 + .../index_as_worktree_with_renames/types.rs | 278 +++++++++ gix-status/src/lib.rs | 12 + gix-status/tests/Cargo.toml | 9 +- .../generated-archives/status_many.tar.xz | Bin 0 -> 11216 bytes gix-status/tests/fixtures/status_many.sh | 40 ++ gix-status/tests/status/index_as_worktree.rs | 12 +- .../status/index_as_worktree_with_renames.rs | 330 ++++++++++ gix-status/tests/status/mod.rs | 1 + justfile | 2 + 18 files changed, 1293 insertions(+), 18 deletions(-) create mode 100644 gix-status/src/index_as_worktree_with_renames/mod.rs create mode 100644 gix-status/src/index_as_worktree_with_renames/recorder.rs create mode 100644 gix-status/src/index_as_worktree_with_renames/types.rs create mode 100644 gix-status/tests/fixtures/generated-archives/status_many.tar.xz create mode 100644 gix-status/tests/fixtures/status_many.sh create mode 100644 gix-status/tests/status/index_as_worktree_with_renames.rs diff --git a/Cargo.lock b/Cargo.lock index 0b1c07ad9ff..efe227eb3dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2511,7 +2511,10 @@ name = "gix-status" version = "0.6.0" dependencies = [ "bstr", + "document-features", "filetime", + "gix-diff", + "gix-dir", "gix-features 0.38.0", "gix-filter", "gix-fs 0.10.0", @@ -2530,15 +2533,21 @@ version = "0.0.0" dependencies = [ "bstr", "filetime", + "gix-diff", + "gix-dir", "gix-features 0.38.0", + "gix-filter", "gix-fs 0.10.0", "gix-hash 0.14.1", "gix-index 0.30.0", "gix-object 0.41.1", + "gix-odb", + "gix-path 0.10.6", "gix-pathspec", "gix-status", "gix-testtools", "gix-worktree 0.31.0", + "pretty_assertions", ] [[package]] diff --git a/crate-status.md b/crate-status.md index 6a43168f4e7..00a8d8e13d0 100644 --- a/crate-status.md +++ b/crate-status.md @@ -552,12 +552,12 @@ Make it the best-performing implementation and the most convenient one. ### gix-status * [x] differences between index and worktree to turn index into worktree - - [ ] rename tracking + - [x] rename tracking + - [x] untracked files + - [ ] support for fs-monitor for modification checks * [ ] differences between index and index to learn what changed - [ ] rename tracking -* [ ] untracked files -* [ ] fast answer to 'is it dirty'. -* + ### gix-worktree-state * handle the working **tree/checkout** - [x] checkout an index of files, executables and symlinks just as fast as git diff --git a/gix-diff/tests/rewrites/tracker.rs b/gix-diff/tests/rewrites/tracker.rs index 2cc5d1b6527..9b3f4f5a928 100644 --- a/gix-diff/tests/rewrites/tracker.rs +++ b/gix-diff/tests/rewrites/tracker.rs @@ -151,7 +151,7 @@ fn copy_by_id() -> crate::Result { let id = hex_to_id("2e65efe2a145dda7ee51d1741299f848e5bf752e"); let source_a = Source { entry_mode: EntryKind::Blob.into(), - id: id, + id, kind: SourceKind::Copy, location: "a".into(), change: &Change { @@ -303,7 +303,7 @@ fn copy_by_50_percent_similarity() -> crate::Result { let id = hex_to_id("78981922613b2afb6025042ff6bd878ac1994e85"); let source_a = Source { entry_mode: EntryKind::Blob.into(), - id: id, + id, kind: SourceKind::Copy, location: "a".into(), change: &Change { @@ -480,7 +480,7 @@ fn rename_by_50_percent_similarity() -> crate::Result { src.unwrap(), Source { entry_mode: EntryKind::Blob.into(), - id: id, + id, kind: SourceKind::Rename, location: "a".into(), change: &Change { diff --git a/gix-status/Cargo.toml b/gix-status/Cargo.toml index 0d75b7dc3c0..6d36fd1c85e 100644 --- a/gix-status/Cargo.toml +++ b/gix-status/Cargo.toml @@ -13,17 +13,29 @@ autotests = false [lib] doctest = false +[features] +## Add support for tracking rewrites along with checking for worktree modifications. +worktree-rewrites = ["dep:gix-dir", "dep:gix-diff"] + [dependencies] gix-index = { version = "^0.30.0", path = "../gix-index" } gix-fs = { version = "^0.10.0", path = "../gix-fs" } gix-hash = { version = "^0.14.1", path = "../gix-hash" } gix-object = { version = "^0.41.1", path = "../gix-object" } gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-features = { version = "^0.38.0", path = "../gix-features" } +gix-features = { version = "^0.38.0", path = "../gix-features", features = ["progress"] } gix-filter = { version = "^0.10.0", path = "../gix-filter" } gix-worktree = { version = "^0.31.0", path = "../gix-worktree", default-features = false, features = ["attributes"] } gix-pathspec = { version = "^0.7.0", path = "../gix-pathspec" } +gix-dir = { version = "^0.1.0", path = "../gix-dir", optional = true } +gix-diff = { version = "^0.41.0", path = "../gix-diff", default-features = false, features = ["blob"], optional = true } + thiserror = "1.0.26" filetime = "0.2.15" bstr = { version = "1.3.0", default-features = false } + +document-features = { version = "0.2.0", optional = true } + +[package.metadata.docs.rs] +features = ["document-features", "worktree-rewrites"] diff --git a/gix-status/src/index_as_worktree/mod.rs b/gix-status/src/index_as_worktree/mod.rs index 96694078bb8..7dce0c43306 100644 --- a/gix-status/src/index_as_worktree/mod.rs +++ b/gix-status/src/index_as_worktree/mod.rs @@ -6,6 +6,6 @@ pub use types::{Change, Conflict, Context, EntryStatus, Error, Options, Outcome, mod recorder; pub use recorder::{Record, Recorder}; -pub(crate) mod function; +pub(super) mod function; /// pub mod traits; diff --git a/gix-status/src/index_as_worktree/recorder.rs b/gix-status/src/index_as_worktree/recorder.rs index 0cf1aa6f367..407abfd557c 100644 --- a/gix-status/src/index_as_worktree/recorder.rs +++ b/gix-status/src/index_as_worktree/recorder.rs @@ -6,7 +6,7 @@ use crate::index_as_worktree::{EntryStatus, VisitEntry}; /// A record of a change. /// /// It's created either if there is a conflict or a change, or both. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Record<'index, T, U> { /// The index entry that is changed. pub entry: &'index index::Entry, diff --git a/gix-status/src/index_as_worktree/types.rs b/gix-status/src/index_as_worktree/types.rs index 6d93f784f12..818ee5840ea 100644 --- a/gix-status/src/index_as_worktree/types.rs +++ b/gix-status/src/index_as_worktree/types.rs @@ -21,7 +21,7 @@ pub enum Error { } /// Options that control how the index status with a worktree is computed. -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)] pub struct Options { /// Capabilities of the file system which affect the status computation. pub fs: gix_fs::Capabilities, @@ -37,6 +37,9 @@ pub struct Options { #[derive(Clone)] pub struct Context<'a> { /// The pathspec to limit the amount of paths that are checked. Can be empty to allow all paths. + /// + /// Note that these are expected to have a [commont_prefix()](gix_pathspec::Search::common_prefix()) according + /// to the prefix of the repository to efficiently limit the scope of the paths we process. pub pathspec: gix_pathspec::Search, /// A stack pre-configured to allow accessing attributes for each entry, as required for `filter` /// and possibly pathspecs. diff --git a/gix-status/src/index_as_worktree_with_renames/mod.rs b/gix-status/src/index_as_worktree_with_renames/mod.rs new file mode 100644 index 00000000000..6185e05cd53 --- /dev/null +++ b/gix-status/src/index_as_worktree_with_renames/mod.rs @@ -0,0 +1,564 @@ +//! Changes between the index and the worktree along with optional rename tracking. +mod types; +pub use types::{Context, DirwalkContext, Entry, Error, Options, Outcome, RewriteSource, Sorting, VisitEntry}; + +mod recorder; +pub use recorder::Recorder; + +pub(super) mod function { + use crate::index_as_worktree::traits::{CompareBlobs, SubmoduleStatus}; + use crate::index_as_worktree_with_renames::function::rewrite::ModificationOrDirwalkEntry; + use crate::index_as_worktree_with_renames::{Context, Entry, Error, Options, Outcome, RewriteSource, VisitEntry}; + use bstr::ByteSlice; + use gix_worktree::stack::State; + use std::borrow::Cow; + use std::path::Path; + + /// Similar to [`index_as_worktree(…)`](crate::index_as_worktree()), except that it will automatically + /// track renames if enabled, while additionally providing information about untracked files + /// (or more, depending on the configuration). + /// + /// * `index` + /// - used for checking modifications, and also for knowing which files are tracked during + /// the working-dir traversal. + /// * `worktree` + /// - The root of the worktree, in a format that respects `core.precomposeUnicode`. + /// * `collector` + /// - A [`VisitEntry`] implementation that sees the results of this operation. + /// * `compare` + /// - An implementation to compare two blobs for equality, used during index modification checks. + /// * `submodule` + /// - An implementation to determine the status of a submodule when encountered during + /// index modification checks. + /// * `objects` + /// - A way to obtain objects from the git object database. + /// * `progress` + /// - A way to send progress information for the index modification checks. + /// * `ctx` + /// - Additional information that will be accessed during index modification checks and traversal. + /// * `options` + /// - a way to configure both paths of the operation. + #[allow(clippy::too_many_arguments)] + pub fn index_as_worktree_with_renames<'index, T, U, Find, E>( + index: &'index gix_index::State, + worktree: &Path, + collector: &mut impl VisitEntry<'index, ContentChange = T, SubmoduleStatus = U>, + compare: impl CompareBlobs<Output = T> + Send + Clone, + submodule: impl SubmoduleStatus<Output = U, Error = E> + Send + Clone, + objects: Find, + progress: &mut dyn gix_features::progress::Progress, + mut ctx: Context<'_>, + options: Options, + ) -> Result<Outcome, Error> + where + T: Send + Clone, + U: Send + Clone, + E: std::error::Error + Send + Sync + 'static, + Find: gix_object::Find + gix_object::FindHeader + Send + Clone, + { + gix_features::parallel::threads(|scope| -> Result<Outcome, Error> { + let (tx, rx) = std::sync::mpsc::channel(); + let walk_outcome = options + .dirwalk + .map(|options| { + gix_features::parallel::build_thread() + .name("gix_status::dirwalk".into()) + .spawn_scoped(scope, { + let tx = tx.clone(); + let mut collect = dirwalk::Delegate { + tx, + should_interrupt: ctx.should_interrupt, + }; + let dirwalk_ctx = ctx.dirwalk; + let objects = objects.clone(); + let mut excludes = match ctx.resource_cache.attr_stack.state() { + State::CreateDirectoryAndAttributesStack { .. } | State::AttributesStack(_) => None, + State::AttributesAndIgnoreStack { .. } | State::IgnoreStack(_) => { + Some(ctx.resource_cache.attr_stack.clone()) + } + }; + let mut pathspec_attr_stack = ctx + .pathspec + .patterns() + .any(|p| !p.attributes.is_empty()) + .then(|| ctx.resource_cache.attr_stack.clone()); + let mut pathspec = ctx.pathspec.clone(); + move || -> Result<_, Error> { + gix_dir::walk( + worktree, + gix_dir::walk::Context { + git_dir_realpath: dirwalk_ctx.git_dir_realpath, + current_dir: dirwalk_ctx.current_dir, + index, + ignore_case_index_lookup: dirwalk_ctx.ignore_case_index_lookup, + pathspec: &mut pathspec, + pathspec_attributes: &mut |relative_path, case, is_dir, out| { + let stack = pathspec_attr_stack + .as_mut() + .expect("can only be called if attributes are used in patterns"); + stack + .set_case(case) + .at_entry(relative_path, Some(is_dir), &objects) + .map_or(false, |platform| platform.matching_attributes(out)) + }, + excludes: excludes.as_mut(), + objects: &objects, + explicit_traversal_root: Some(worktree), + }, + options, + &mut collect, + ) + .map_err(Error::DirWalk) + } + }) + .map_err(Error::SpawnThread) + }) + .transpose()?; + + let entries = &index.entries()[index + .prefixed_entries_range(ctx.pathspec.common_prefix()) + .unwrap_or(0..index.entries().len())]; + + let filter = options.rewrites.is_some().then(|| { + ( + ctx.resource_cache.filter.worktree_filter.clone(), + ctx.resource_cache.attr_stack.clone(), + ) + }); + let tracked_modifications_outcome = gix_features::parallel::build_thread() + .name("gix_status::index_as_worktree".into()) + .spawn_scoped(scope, { + let mut collect = tracked_modifications::Delegate { tx }; + let objects = objects.clone(); + let stack = ctx.resource_cache.attr_stack.clone(); + let filter = ctx.resource_cache.filter.worktree_filter.clone(); + move || -> Result<_, Error> { + crate::index_as_worktree( + index, + worktree, + &mut collect, + compare, + submodule, + objects, + progress, + crate::index_as_worktree::Context { + pathspec: ctx.pathspec, + stack, + filter, + should_interrupt: ctx.should_interrupt, + }, + options.tracked_file_modifications, + ) + .map_err(Error::TrackedFileModifications) + } + }) + .map_err(Error::SpawnThread)?; + + let tracker = options + .rewrites + .map(gix_diff::rewrites::Tracker::<rewrite::ModificationOrDirwalkEntry<'index, T, U>>::new) + .zip(filter); + let rewrite_outcome = match tracker { + Some((mut tracker, (mut filter, mut attrs))) => { + let mut entries_for_sorting = options.sorting.map(|_| Vec::new()); + let mut buf = Vec::new(); + for event in rx { + let (change, location) = match event { + Event::IndexEntry(record) => { + let location = Cow::Borrowed(record.relative_path); + (rewrite::ModificationOrDirwalkEntry::Modification(record), location) + } + Event::DirEntry(entry, collapsed_directory_status) => { + let location = Cow::Owned(entry.rela_path.clone()); + ( + rewrite::ModificationOrDirwalkEntry::DirwalkEntry { + id: rewrite::calculate_worktree_id( + options.object_hash, + worktree, + entry.disk_kind, + entry.rela_path.as_bstr(), + &mut filter, + &mut attrs, + &objects, + &mut buf, + ctx.should_interrupt, + )?, + entry, + collapsed_directory_status, + }, + location, + ) + } + }; + if let Some(v) = entries_for_sorting.as_mut() { + v.push((change, location)); + } else if let Some(change) = tracker.try_push_change(change, location.as_ref()) { + collector.visit_entry(rewrite::change_to_entry(change, entries)) + } + } + + let mut entries_for_sorting = entries_for_sorting.map(|mut v| { + v.sort_by(|a, b| a.1.cmp(&b.1)); + let mut remaining = Vec::new(); + for (change, location) in v { + if let Some(change) = tracker.try_push_change(change, location.as_ref()) { + remaining.push(rewrite::change_to_entry(change, entries)); + } + } + remaining + }); + + let outcome = tracker.emit( + |dest, src| { + match src { + None => { + let entry = rewrite::change_to_entry(dest.change, entries); + if let Some(v) = entries_for_sorting.as_mut() { + v.push(entry); + } else { + collector.visit_entry(entry) + } + } + Some(src) => { + let rewrite::ModificationOrDirwalkEntry::DirwalkEntry { + id, + entry, + collapsed_directory_status, + } = dest.change + else { + unreachable!("BUG: only possible destinations are dirwalk entries (additions)"); + }; + let source = match src.change { + ModificationOrDirwalkEntry::Modification(record) => { + RewriteSource::RewriteFromIndex { + index_entries: entries, + source_entry: record.entry, + source_entry_index: record.entry_index, + source_rela_path: record.relative_path, + source_status: record.status.clone(), + } + } + ModificationOrDirwalkEntry::DirwalkEntry { + id, + entry, + collapsed_directory_status, + } => RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry: entry.clone(), + source_dirwalk_entry_collapsed_directory_status: + *collapsed_directory_status, + source_dirwalk_entry_id: *id, + }, + }; + + let entry = Entry::Rewrite { + source, + dirwalk_entry: entry, + dirwalk_entry_collapsed_directory_status: collapsed_directory_status, + dirwalk_entry_id: id, + diff: src.diff, + copy: src.kind == gix_diff::rewrites::tracker::visit::SourceKind::Copy, + }; + if let Some(v) = entries_for_sorting.as_mut() { + v.push(entry); + } else { + collector.visit_entry(entry); + } + } + } + gix_diff::tree::visit::Action::Continue + }, + &mut ctx.resource_cache, + &objects, + |_cb| { + // NOTE: to make this work, we'd want to wait the index modification check to complete. + // Then it's possible to efficiently emit the tracked files along with what we already sent, + // i.e. untracked and ignored files. + gix_features::trace::debug!("full-tree copy tracking isn't currently supported"); + Ok::<_, std::io::Error>(()) + }, + )?; + + if let Some(mut v) = entries_for_sorting { + v.sort_by(|a, b| a.destination_rela_path().cmp(b.destination_rela_path())); + for entry in v { + collector.visit_entry(entry); + } + } + Some(outcome) + } + None => { + let mut entries_for_sorting = options.sorting.map(|_| Vec::new()); + for event in rx { + let entry = match event { + Event::IndexEntry(record) => Entry::Modification { + entries, + entry: record.entry, + entry_index: record.entry_index, + rela_path: record.relative_path, + status: record.status, + }, + Event::DirEntry(entry, collapsed_directory_status) => Entry::DirectoryContents { + entry, + collapsed_directory_status, + }, + }; + + if let Some(v) = entries_for_sorting.as_mut() { + v.push(entry); + } else { + collector.visit_entry(entry); + } + } + + if let Some(mut v) = entries_for_sorting { + v.sort_by(|a, b| a.destination_rela_path().cmp(b.destination_rela_path())); + for entry in v { + collector.visit_entry(entry); + } + } + None + } + }; + + let walk_outcome = walk_outcome + .map(|handle| handle.join().expect("no panic")) + .transpose()?; + let tracked_modifications_outcome = tracked_modifications_outcome.join().expect("no panic")?; + Ok(Outcome { + dirwalk: walk_outcome.map(|t| t.0), + tracked_file_modification: tracked_modifications_outcome, + rewrites: rewrite_outcome, + }) + }) + } + + enum Event<'index, T, U> { + IndexEntry(crate::index_as_worktree::Record<'index, T, U>), + DirEntry(gix_dir::Entry, Option<gix_dir::entry::Status>), + } + + mod tracked_modifications { + use crate::index_as_worktree::{EntryStatus, Record}; + use crate::index_as_worktree_with_renames::function::Event; + use bstr::BStr; + use gix_index::Entry; + + pub(super) struct Delegate<'index, T, U> { + pub(super) tx: std::sync::mpsc::Sender<Event<'index, T, U>>, + } + + impl<'index, T, U> crate::index_as_worktree::VisitEntry<'index> for Delegate<'index, T, U> { + type ContentChange = T; + type SubmoduleStatus = U; + + fn visit_entry( + &mut self, + _entries: &'index [Entry], + entry: &'index Entry, + entry_index: usize, + rela_path: &'index BStr, + status: EntryStatus<Self::ContentChange, Self::SubmoduleStatus>, + ) { + self.tx + .send(Event::IndexEntry(Record { + entry, + entry_index, + relative_path: rela_path, + status, + })) + .ok(); + } + } + } + + mod dirwalk { + use super::Event; + use gix_dir::entry::Status; + use gix_dir::walk::Action; + use gix_dir::EntryRef; + use std::sync::atomic::{AtomicBool, Ordering}; + + pub(super) struct Delegate<'index, 'a, T, U> { + pub(super) tx: std::sync::mpsc::Sender<Event<'index, T, U>>, + pub(super) should_interrupt: &'a AtomicBool, + } + + impl<'index, 'a, T, U> gix_dir::walk::Delegate for Delegate<'index, 'a, T, U> { + fn emit(&mut self, entry: EntryRef<'_>, collapsed_directory_status: Option<Status>) -> Action { + let entry = entry.to_owned(); + self.tx.send(Event::DirEntry(entry, collapsed_directory_status)).ok(); + + if self.should_interrupt.load(Ordering::Relaxed) { + Action::Cancel + } else { + Action::Continue + } + } + } + } + + mod rewrite { + use crate::index_as_worktree::{Change, EntryStatus}; + use crate::index_as_worktree_with_renames::{Entry, Error}; + use bstr::BStr; + use gix_diff::rewrites::tracker::ChangeKind; + use gix_dir::entry::Kind; + use gix_filter::pipeline::convert::ToGitOutcome; + use gix_hash::oid; + use gix_object::tree::EntryMode; + use std::io::Read; + use std::path::Path; + + #[derive(Clone)] + pub enum ModificationOrDirwalkEntry<'index, T, U> + where + T: Clone, + U: Clone, + { + Modification(crate::index_as_worktree::Record<'index, T, U>), + DirwalkEntry { + id: gix_hash::ObjectId, + entry: gix_dir::Entry, + collapsed_directory_status: Option<gix_dir::entry::Status>, + }, + } + + impl<'index, T, U> gix_diff::rewrites::tracker::Change for ModificationOrDirwalkEntry<'index, T, U> + where + T: Clone, + U: Clone, + { + fn id(&self) -> &oid { + match self { + ModificationOrDirwalkEntry::Modification(m) => &m.entry.id, + ModificationOrDirwalkEntry::DirwalkEntry { id, .. } => id, + } + } + + fn kind(&self) -> ChangeKind { + match self { + ModificationOrDirwalkEntry::Modification(m) => match &m.status { + EntryStatus::Conflict(_) | EntryStatus::IntentToAdd | EntryStatus::NeedsUpdate(_) => { + ChangeKind::Modification + } + EntryStatus::Change(c) => match c { + Change::Removed => ChangeKind::Deletion, + Change::Type | Change::Modification { .. } | Change::SubmoduleModification(_) => { + ChangeKind::Modification + } + }, + }, + ModificationOrDirwalkEntry::DirwalkEntry { .. } => ChangeKind::Addition, + } + } + + fn entry_mode(&self) -> EntryMode { + match self { + ModificationOrDirwalkEntry::Modification(c) => c.entry.mode.to_tree_entry_mode(), + ModificationOrDirwalkEntry::DirwalkEntry { entry, .. } => entry.disk_kind.map(|kind| { + match kind { + Kind::File => gix_object::tree::EntryKind::Blob, + Kind::Symlink => gix_object::tree::EntryKind::Link, + Kind::Repository | Kind::Directory => gix_object::tree::EntryKind::Tree, + } + .into() + }), + } + .unwrap_or(gix_object::tree::EntryKind::Blob.into()) + } + + fn id_and_entry_mode(&self) -> (&oid, EntryMode) { + (self.id(), self.entry_mode()) + } + } + + /// Note that for non-files, we always return a null-sha and assume that the rename-tracking + /// does nothing for these anyway. + #[allow(clippy::too_many_arguments)] + pub(super) fn calculate_worktree_id( + object_hash: gix_hash::Kind, + worktree_root: &Path, + disk_kind: Option<gix_dir::entry::Kind>, + rela_path: &BStr, + filter: &mut gix_filter::Pipeline, + attrs: &mut gix_worktree::Stack, + objects: &dyn gix_object::Find, + buf: &mut Vec<u8>, + should_interrupt: &std::sync::atomic::AtomicBool, + ) -> Result<gix_hash::ObjectId, Error> { + let Some(kind) = disk_kind else { + return Ok(object_hash.null()); + }; + + Ok(match kind { + Kind::File => { + let platform = attrs + .at_entry(rela_path, Some(false), objects) + .map_err(Error::SetAttributeContext)?; + let rela_path = gix_path::from_bstr(rela_path); + let file_path = worktree_root.join(rela_path.as_ref()); + let file = std::fs::File::open(&file_path).map_err(Error::OpenWorktreeFile)?; + let out = filter.convert_to_git( + file, + rela_path.as_ref(), + &mut |_path, attrs| { + platform.matching_attributes(attrs); + }, + &mut |_buf| Ok(None), + )?; + match out { + ToGitOutcome::Unchanged(mut file) => gix_object::compute_stream_hash( + object_hash, + gix_object::Kind::Blob, + &mut file, + file_path.metadata().map_err(Error::OpenWorktreeFile)?.len(), + &mut gix_features::progress::Discard, + should_interrupt, + ) + .map_err(Error::HashFile)?, + ToGitOutcome::Buffer(buf) => gix_object::compute_hash(object_hash, gix_object::Kind::Blob, buf), + ToGitOutcome::Process(mut stream) => { + buf.clear(); + stream.read_to_end(buf).map_err(Error::HashFile)?; + gix_object::compute_hash(object_hash, gix_object::Kind::Blob, buf) + } + } + } + Kind::Symlink => { + let path = worktree_root.join(gix_path::from_bstr(rela_path)); + let target = gix_path::into_bstr(std::fs::read_link(path).map_err(Error::ReadLink)?); + gix_object::compute_hash(object_hash, gix_object::Kind::Blob, &target) + } + Kind::Directory | Kind::Repository => object_hash.null(), + }) + } + + #[inline] + pub(super) fn change_to_entry<'index, T, U>( + change: ModificationOrDirwalkEntry<'index, T, U>, + entries: &'index [gix_index::Entry], + ) -> Entry<'index, T, U> + where + T: Clone, + U: Clone, + { + match change { + ModificationOrDirwalkEntry::Modification(r) => Entry::Modification { + entries, + entry: r.entry, + entry_index: r.entry_index, + rela_path: r.relative_path, + status: r.status, + }, + ModificationOrDirwalkEntry::DirwalkEntry { + id: _, + entry, + collapsed_directory_status, + } => Entry::DirectoryContents { + entry, + collapsed_directory_status, + }, + } + } + } +} diff --git a/gix-status/src/index_as_worktree_with_renames/recorder.rs b/gix-status/src/index_as_worktree_with_renames/recorder.rs new file mode 100644 index 00000000000..81d05f1b064 --- /dev/null +++ b/gix-status/src/index_as_worktree_with_renames/recorder.rs @@ -0,0 +1,17 @@ +use crate::index_as_worktree_with_renames::{Entry, VisitEntry}; + +/// Convenience implementation of [`VisitEntry`] that collects all changes into a `Vec`. +#[derive(Debug, Default)] +pub struct Recorder<'index, T = (), U = ()> { + /// The collected changes. + pub records: Vec<Entry<'index, T, U>>, +} + +impl<'index, T: Send, U: Send> VisitEntry<'index> for Recorder<'index, T, U> { + type ContentChange = T; + type SubmoduleStatus = U; + + fn visit_entry(&mut self, entry: Entry<'index, Self::ContentChange, Self::SubmoduleStatus>) { + self.records.push(entry) + } +} diff --git a/gix-status/src/index_as_worktree_with_renames/types.rs b/gix-status/src/index_as_worktree_with_renames/types.rs new file mode 100644 index 00000000000..c46462f5d0a --- /dev/null +++ b/gix-status/src/index_as_worktree_with_renames/types.rs @@ -0,0 +1,278 @@ +use crate::index_as_worktree::EntryStatus; +use bstr::{BStr, ByteSlice}; +use std::sync::atomic::AtomicBool; + +/// The error returned by [index_as_worktree_with_renames()`](crate::index_as_worktree_with_renames()). +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + TrackedFileModifications(#[from] crate::index_as_worktree::Error), + #[error(transparent)] + DirWalk(gix_dir::walk::Error), + #[error(transparent)] + SpawnThread(std::io::Error), + #[error("Failed to change the context for querying gitattributes to the respective path")] + SetAttributeContext(std::io::Error), + #[error("Could not open worktree file for reading")] + OpenWorktreeFile(std::io::Error), + #[error(transparent)] + HashFile(std::io::Error), + #[error("Could not read worktree link content")] + ReadLink(std::io::Error), + #[error(transparent)] + ConvertToGit(#[from] gix_filter::pipeline::convert::to_git::Error), + #[error(transparent)] + RewriteTracker(#[from] gix_diff::rewrites::tracker::emit::Error), +} + +/// The way all output should be sorted. +#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum Sorting { + /// The entries are sorted by their path in a case-sensitive fashion. + #[default] + ByPathCaseSensitive, +} + +/// Provide additional information collected during the runtime of [`index_as_worktree_with_renames()`](crate::index_as_worktree_with_renames()). +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Outcome { + /// The outcome of the modification check of tracked files. + pub tracked_file_modification: crate::index_as_worktree::Outcome, + /// The outcome of the directory walk, or `None` if its [options](Options::dirwalk) also weren't present which means + /// the dirwalk never ran. + pub dirwalk: Option<gix_dir::walk::Outcome>, + /// The result of the rewrite operation, if [rewrites were configured](Options::rewrites). + pub rewrites: Option<gix_diff::rewrites::Outcome>, +} + +/// Either an index entry for renames or another directory entry in case of copies. +#[derive(Clone, PartialEq, Debug)] +pub enum RewriteSource<'index, ContentChange, SubmoduleStatus> { + /// The source originates in the index and is detected as missing in the working tree. + /// This can also happen for copies. + RewriteFromIndex { + /// All entries in the index. + index_entries: &'index [gix_index::Entry], + /// The entry that is the source of the rewrite, which means it was removed on disk, + /// equivalent to [Change::Removed](crate::index_as_worktree::Change::Removed). + /// + /// Note that the [entry-id](gix_index::Entry::id) is the content-id of the source of the rewrite. + source_entry: &'index gix_index::Entry, + /// The index of the `source_entry` for lookup in `index_entries` - useful to look at neighbors. + source_entry_index: usize, + /// The repository-relative path of the `source_entry`. + source_rela_path: &'index BStr, + /// The computed status of the `source_entry`. + source_status: EntryStatus<ContentChange, SubmoduleStatus>, + }, + /// This source originates in the directory tree and is always the source of copies. + CopyFromDirectoryEntry { + /// The source of the copy operation, which is also an entry of the directory walk. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the source of the rewrite. + source_dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `source_dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `source_dirwalk_entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + source_dirwalk_entry_collapsed_directory_status: Option<gix_dir::entry::Status>, + /// The object id as it would appear if the entry was written to the object database. + /// It's the same as `dirwalk_entry_id`, or `diff` is `Some(_)` to indicate that the copy was determined by similarity. + source_dirwalk_entry_id: gix_hash::ObjectId, + }, +} + +/// An 'entry' in the sense of a merge of modified tracked files and results from a directory walk. +#[derive(Clone, PartialEq, Debug)] +pub enum Entry<'index, ContentChange, SubmoduleStatus> { + /// A tracked file was modified, and index-specific information is passed. + Modification { + /// All entries in the index. + entries: &'index [gix_index::Entry], + /// The entry with modifications. + entry: &'index gix_index::Entry, + /// The index of the `entry` for lookup in `entries` - useful to look at neighbors. + entry_index: usize, + /// The repository-relative path of the entry. + rela_path: &'index BStr, + /// The computed status of the entry. + status: EntryStatus<ContentChange, SubmoduleStatus>, + }, + /// An entry returned by the directory walk, without any relation to the index. + /// + /// This can happen if ignored files are returned as well, or if rename-tracking is disabled. + DirectoryContents { + /// The entry found during the disk traversal. + entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + collapsed_directory_status: Option<gix_dir::entry::Status>, + }, + /// The rewrite tracking discovered a match between a deleted and added file, and considers them equal enough, + /// depending on the tracker settings. + /// + /// Note that the source of the rewrite is always the index as it detects the absence of entries, something that + /// can't be done during a directory walk. + Rewrite { + /// The source of the rewrite operation. + source: RewriteSource<'index, ContentChange, SubmoduleStatus>, + /// The untracked entry found during the disk traversal, the destination of the rewrite. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the destination of the rewrite, and the current + /// location of the entry. + dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `dirwalk_entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + dirwalk_entry_collapsed_directory_status: Option<gix_dir::entry::Status>, + /// The object id after the rename, specifically hashed in order to determine equality. + dirwalk_entry_id: gix_hash::ObjectId, + /// It's `None` if the 'source.id' is equal to `dirwalk_entry_id`, as identity made an actual diff computation unnecessary. + /// Otherwise, and if enabled, it's `Some(stats)` to indicate how similar both entries were. + diff: Option<gix_diff::blob::DiffLineStats>, + /// If true, this rewrite is created by copy, and 'source.id' is pointing to its source. + /// Otherwise, it's a rename, and 'source.id' points to a deleted object, + /// as renames are tracked as deletions and additions of the same or similar content. + copy: bool, + }, +} + +/// Access +impl<ContentChange, SubmoduleStatus> RewriteSource<'_, ContentChange, SubmoduleStatus> { + /// The repository-relative path of this source. + pub fn rela_path(&self) -> &BStr { + match self { + RewriteSource::RewriteFromIndex { source_rela_path, .. } => source_rela_path, + RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, .. + } => source_dirwalk_entry.rela_path.as_bstr(), + } + } +} + +/// Access +impl<ContentChange, SubmoduleStatus> Entry<'_, ContentChange, SubmoduleStatus> { + /// The repository-relative path at which the source of a rewrite is located. + /// + /// If this isn't a rewrite, the path is the location of the entry itself. + pub fn source_rela_path(&self) -> &BStr { + match self { + Entry::Modification { rela_path, .. } => rela_path, + Entry::DirectoryContents { entry, .. } => entry.rela_path.as_bstr(), + Entry::Rewrite { source, .. } => source.rela_path(), + } + } + + /// The repository-relative path at which the destination of a rewrite is located. + /// + /// If this isn't a rewrite, the path is the location of the entry itself. + pub fn destination_rela_path(&self) -> &BStr { + match self { + Entry::Modification { rela_path, .. } => rela_path, + Entry::DirectoryContents { entry, .. } => entry.rela_path.as_bstr(), + Entry::Rewrite { dirwalk_entry, .. } => dirwalk_entry.rela_path.as_bstr(), + } + } +} + +/// Options for use in [index_as_worktree_with_renames()](crate::index_as_worktree_with_renames()). +#[derive(Clone, Default)] +pub struct Options { + /// The way all output should be sorted. + /// + /// If `None`, and depending on the `rewrites` field, output will be immediate but the output order + /// isn't determined, and may differ between two runs. `rewrites` also depend on the order of entries that + /// are presented to it, hence for deterministic results, sorting needs to be enabled. + /// + /// If `Some(_)`, all entries are collected beforehand, so they can be sorted before outputting any of them + /// to the user. + /// + /// If immediate output of entries in any order is desired, this should be `None`, + /// along with `rewrites` being `None` as well. + pub sorting: Option<Sorting>, + /// The kind of hash to create when hashing worktree entries. + pub object_hash: gix_hash::Kind, + /// Options to configure how modifications to tracked files should be obtained. + pub tracked_file_modifications: crate::index_as_worktree::Options, + /// Options to control the directory walk that informs about untracked files. + /// + /// Note that we forcefully disable emission of tracked files to avoid any overlap + /// between emissions to indicate modifications, and those that are obtained by + /// the directory walk. + /// + /// If `None`, the directory walk portion will not run at all, yielding data similar + /// to a bare [index_as_worktree()](crate::index_as_worktree()) call. + pub dirwalk: Option<gix_dir::walk::Options>, + /// The configuration for the rewrite tracking. Note that if set, the [`dirwalk`](Self::dirwalk) should be configured + /// to *not* collapse untracked and ignored entries, as rewrite tracking is on a file-by-file basis. + /// Also note that when `Some(_)`, it will collect certain changes depending on the exact configuration, which typically increases + /// the latency until the first entries are received. Note that some entries are never candidates for renames, which means + /// they are forwarded to the caller right away. + /// + /// If `None`, no tracking will occour, which means that all output becomes visible to the delegate immediately. + pub rewrites: Option<gix_diff::Rewrites>, +} + +/// The context for [index_as_worktree_with_renames()`](crate::index_as_worktree_with_renames()). +pub struct Context<'a> { + /// The pathspec to limit the amount of paths that are checked. Can be empty to allow all paths. + /// + /// Note that these are expected to have a [commont_prefix()](gix_pathspec::Search::common_prefix()) according + /// to the prefix of the repository to efficiently limit the scope of the paths we process, both for the + /// index modifications as well as for the directory walk. + pub pathspec: gix_pathspec::Search, + /// A fully-configured platform capable of producing diffable buffers similar to what Git would do, for use + /// with rewrite tracking. + /// + /// Note that it contains resources that are additionally used here: + /// + /// * `attr_stack` + /// - A stack pre-configured to allow accessing attributes for each entry, as required for `filter` + /// and possibly pathspecs. + /// It *may* also allow accessing `.gitignore` information for use in the directory walk. + /// If no excludes information is present, the directory walk will identify ignored files as untracked, which + /// might be desirable under certain circumstances. + /// * `filter` + /// - A filter to be able to perform conversions from and to the worktree format. + /// It is needed to potentially refresh the index with data read from the worktree, which needs to be converted back + /// to the form stored in Git. + pub resource_cache: gix_diff::blob::Platform, + /// A flag to query to learn if cancellation is requested. + pub should_interrupt: &'a AtomicBool, + /// The context for the directory walk. + pub dirwalk: DirwalkContext<'a>, +} + +/// All information that is required to perform a [dirwalk](gix_dir::walk()). +pub struct DirwalkContext<'a> { + /// The `git_dir` of the parent repository, after a call to [`gix_path::realpath()`]. + /// + /// It's used to help us differentiate our own `.git` directory from nested unrelated repositories, + /// which is needed if `core.worktree` is used to nest the `.git` directory deeper within. + pub git_dir_realpath: &'a std::path::Path, + /// The current working directory as returned by `gix_fs::current_dir()` to assure it respects `core.precomposeUnicode`. + /// It's used to produce the realpath of the git-dir of a repository candidate to assure it's not our own repository. + pub current_dir: &'a std::path::Path, + /// A utility to lookup index entries faster, and deal with ignore-case handling. + /// + /// Must be set if [`ignore_case`](gix_dir::walk::Options::ignore_case) is `true`, or else some entries won't be found if their case is different. + /// + /// [Read more in `gix-dir`](gix_dir::walk::Context::ignore_case_index_lookup). + pub ignore_case_index_lookup: Option<&'a gix_index::AccelerateLookup<'a>>, +} + +/// Observe the status of an entry by comparing an index entry to the worktree, along +/// with potential directory walk results. +pub trait VisitEntry<'a> { + /// Data generated by comparing an entry with a file. + type ContentChange; + /// Data obtained when checking the submodule status. + type SubmoduleStatus; + /// Observe the `status` of `entry` at the repository-relative `rela_path` at `entry_index` + /// (for accessing `entry` and surrounding in the complete list of `entries`). + fn visit_entry(&mut self, entry: Entry<'a, Self::ContentChange, Self::SubmoduleStatus>); +} diff --git a/gix-status/src/lib.rs b/gix-status/src/lib.rs index 0749c5bd6cb..a2dbf6a4c51 100644 --- a/gix-status/src/lib.rs +++ b/gix-status/src/lib.rs @@ -6,11 +6,23 @@ //! * find untracked files //! //! While also being able to check check if the working tree is dirty, quickly. +//! +//! ### Feature Flags +#![cfg_attr( + all(doc, feature = "document-features"), + doc = ::document_features::document_features!() +)] +#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs, rust_2018_idioms, unsafe_code)] pub mod index_as_worktree; pub use index_as_worktree::function::index_as_worktree; +#[cfg(feature = "worktree-rewrites")] +pub mod index_as_worktree_with_renames; +#[cfg(feature = "worktree-rewrites")] +pub use index_as_worktree_with_renames::function::index_as_worktree_with_renames; + /// A stack that validates we are not going through a symlink in a way that is read-only. /// /// It can efficiently validate paths when these are queried in sort-order, which leads to each component diff --git a/gix-status/tests/Cargo.toml b/gix-status/tests/Cargo.toml index 2e138b9a184..576a81829c4 100644 --- a/gix-status/tests/Cargo.toml +++ b/gix-status/tests/Cargo.toml @@ -17,10 +17,15 @@ path = "worktree.rs" gix-features-parallel = ["gix-features/parallel"] [dev-dependencies] -gix-status = { path = ".." } +gix-status = { path = "..", features = ["worktree-rewrites"] } gix-testtools = { path = "../../tests/tools" } gix-index = { path = "../../gix-index" } gix-fs = { path = "../../gix-fs" } +gix-diff = { path = "../../gix-diff" } +gix-filter = { path = "../../gix-filter" } +gix-path = { path = "../../gix-path" } +gix-dir = { path = "../../gix-dir" } +gix-odb = { path = "../../gix-odb" } gix-hash = { path = "../../gix-hash" } gix-object = { path = "../../gix-object" } gix-features = { path = "../../gix-features" } @@ -28,4 +33,4 @@ gix-pathspec = { path = "../../gix-pathspec" } gix-worktree = { path = "../../gix-worktree" } filetime = "0.2.15" bstr = { version = "1.3.0", default-features = false } - +pretty_assertions = "1.4.0" diff --git a/gix-status/tests/fixtures/generated-archives/status_many.tar.xz b/gix-status/tests/fixtures/generated-archives/status_many.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..0ba30dc3e292de0feadeb9dd9511318e5c35d54e GIT binary patch literal 11216 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 literal 0 HcmV?d00001 diff --git a/gix-status/tests/fixtures/status_many.sh b/gix-status/tests/fixtures/status_many.sh new file mode 100644 index 00000000000..2315ddc2a96 --- /dev/null +++ b/gix-status/tests/fixtures/status_many.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -eu -o pipefail + +git init -q changed-and-untracked +(cd changed-and-untracked + touch empty + echo "content" > executable + chmod +x executable + + mkdir dir + echo "other content" > dir/content + echo "different content" > dir/content2 + + git add -A + git commit -m "Commit" + echo "change" >> executable + + + mkdir dir/empty + >dir/untracked + >untracked + + git status +) + +cp -R changed-and-untracked changed-and-untracked-and-renamed +(cd changed-and-untracked-and-renamed + # it has a local change compared to the indexed version, hence it's rewritten + mv executable rewritten-executable + + cp dir/content content-copy + cp dir/content content-copy-with-rewrite + echo change >> content-copy-with-rewrite + + mv dir/content plainly-renamed-content + + mv dir/content2 content-with-rewrite + echo change >> content-with-rewrite + +) diff --git a/gix-status/tests/status/index_as_worktree.rs b/gix-status/tests/status/index_as_worktree.rs index d6753e26943..e485f0759c7 100644 --- a/gix-status/tests/status/index_as_worktree.rs +++ b/gix-status/tests/status/index_as_worktree.rs @@ -22,7 +22,7 @@ use crate::fixture_path; // changes when extracting the data so we need to disable all advanced stat // changes and only look at mtime seconds and file size to properly // test all code paths (and to trigger racy git). -const TEST_OPTIONS: index::entry::stat::Options = index::entry::stat::Options { +pub(super) const TEST_OPTIONS: index::entry::stat::Options = index::entry::stat::Options { trust_ctime: false, check_stat: false, use_nsec: false, @@ -128,7 +128,9 @@ fn fixture_filtered_detailed( } /// Note that we also reset certain information to assure there is no flakiness - everything regarding race-detection otherwise can cause failures. -fn records_to_tuple<'index>(records: impl IntoIterator<Item = Record<'index, (), ()>>) -> Vec<Expectation<'index>> { +pub(super) fn records_to_tuple<'index>( + records: impl IntoIterator<Item = Record<'index, (), ()>>, +) -> Vec<Expectation<'index>> { records .into_iter() .filter_map(|r| deracify_status(r.status).map(|status| (r.relative_path, r.entry_index, status))) @@ -159,8 +161,8 @@ fn deracify_status(status: EntryStatus) -> Option<EntryStatus> { } #[derive(Clone)] -struct SubmoduleStatusMock { - dirty: bool, +pub(super) struct SubmoduleStatusMock { + pub(super) dirty: bool, } impl SubmoduleStatus for SubmoduleStatusMock { @@ -172,7 +174,7 @@ impl SubmoduleStatus for SubmoduleStatusMock { } } -fn to_pathspecs(input: &[&str]) -> Vec<gix_pathspec::Pattern> { +pub(super) fn to_pathspecs(input: &[&str]) -> Vec<gix_pathspec::Pattern> { input .iter() .map(|pattern| gix_pathspec::parse(pattern.as_bytes(), Default::default()).expect("known to be valid")) diff --git a/gix-status/tests/status/index_as_worktree_with_renames.rs b/gix-status/tests/status/index_as_worktree_with_renames.rs new file mode 100644 index 00000000000..08414a9687f --- /dev/null +++ b/gix-status/tests/status/index_as_worktree_with_renames.rs @@ -0,0 +1,330 @@ +use crate::status::fixture_path; +use bstr::ByteSlice; +use gix_diff::blob::pipeline::WorktreeRoots; +use gix_diff::rewrites::CopySource; +use gix_status::index_as_worktree::traits::FastEq; +use gix_status::index_as_worktree::{Change, EntryStatus}; +use gix_status::index_as_worktree_with_renames; +use gix_status::index_as_worktree_with_renames::{Context, DirwalkContext, Entry, Options, Outcome, Recorder, Sorting}; +use pretty_assertions::assert_eq; + +#[test] +fn changed_and_untracked_and_renamed() { + let expectations_with_dirwalk = [ + // Not always will we match the right source to destinations, there is ambiguity. + Expectation::Rewrite { + source_rela_path: "dir/content", + dest_rela_path: "content-copy", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: None, + copy: false, + }, + Expectation::DirwalkEntry { + rela_path: "content-copy-with-rewrite", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + Expectation::Rewrite { + source_rela_path: "dir/content2", + dest_rela_path: "content-with-rewrite", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: Some(gix_diff::blob::DiffLineStats { + removals: 0, + insertions: 1, + before: 1, + after: 2, + similarity: 0.72, + }), + copy: false, + }, + Expectation::Rewrite { + source_rela_path: "empty", + dest_rela_path: "dir/untracked", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: None, + copy: true, + }, + // This is just detected as untracked, related to how the rename-tracker matches pairs + Expectation::DirwalkEntry { + rela_path: "plainly-renamed-content", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + Expectation::Rewrite { + source_rela_path: "executable", + dest_rela_path: "rewritten-executable", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: Some(gix_diff::blob::DiffLineStats { + removals: 0, + insertions: 1, + before: 1, + after: 2, + similarity: 0.53333336, + }), + copy: false, + }, + Expectation::Rewrite { + source_rela_path: "empty", + dest_rela_path: "untracked", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: None, + copy: true, + }, + ]; + let rewrites = gix_diff::Rewrites { + copies: Some(gix_diff::rewrites::Copies { + source: CopySource::FromSetOfModifiedFiles, + percentage: Some(0.3), + }), + percentage: Some(0.3), + limit: 0, + }; + let out = fixture_filtered_detailed( + "changed-and-untracked-and-renamed", + &[], + &expectations_with_dirwalk, + Some(rewrites), + Some(Default::default()), + ); + assert_eq!( + out.rewrites, + Some(gix_diff::rewrites::Outcome { + options: rewrites, + num_similarity_checks: 11, + num_similarity_checks_skipped_for_rename_tracking_due_to_limit: 0, + num_similarity_checks_skipped_for_copy_tracking_due_to_limit: 0, + }) + ) +} + +#[test] +fn changed_and_untracked() { + let out = fixture_filtered_detailed( + "changed-and-untracked", + &[], + &[Expectation::Modification { + rela_path: "executable", + status: EntryStatus::Change(Change::Modification { + executable_bit_changed: false, + content_change: Some(()), + set_entry_stat_size_zero: false, + }), + }], + None, + None, + ); + assert_eq!(out.tracked_file_modification.entries_processed, 4); + assert_eq!( + out.dirwalk, None, + "we didn't configure the dirwalk, so it's just like a modification check" + ); + assert_eq!(out.rewrites, None, "rewrite checking isn't configured either"); + + let expectations_with_dirwalk = [ + Expectation::DirwalkEntry { + rela_path: "dir/untracked", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + Expectation::Modification { + rela_path: "executable", + status: EntryStatus::Change(Change::Modification { + executable_bit_changed: false, + content_change: Some(()), + set_entry_stat_size_zero: false, + }), + }, + Expectation::DirwalkEntry { + rela_path: "untracked", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + ]; + let out = fixture_filtered_detailed( + "changed-and-untracked", + &[], + &expectations_with_dirwalk, + None, + Some(gix_dir::walk::Options::default()), + ); + + let dirwalk = out.dirwalk.expect("configured thus has output"); + assert_eq!( + dirwalk, + gix_dir::walk::Outcome { + read_dir_calls: 3, + returned_entries: 2, + seen_entries: 8, + } + ); + assert_eq!(out.rewrites, None, "rewrites are still not configured"); + + let out = fixture_filtered_detailed( + "changed-and-untracked", + &[], + &expectations_with_dirwalk, + Some(Default::default()), + Some(gix_dir::walk::Options::default()), + ); + + let rewrites = out.rewrites.expect("configured thus has output"); + assert_eq!( + rewrites, + gix_diff::rewrites::Outcome::default(), + "there actually is no candidates pairs as there are no deletions" + ); +} + +fn fixture_filtered_detailed( + subdir: &str, + pathspecs: &[&str], + expected: &[Expectation<'_>], + rewrites: Option<gix_diff::Rewrites>, + dirwalk: Option<gix_dir::walk::Options>, +) -> Outcome { + fn cleanup(mut out: Outcome) -> Outcome { + out.tracked_file_modification.worktree_bytes = 0; + out.tracked_file_modification.worktree_files_read = 0; + out.tracked_file_modification.entries_to_update = 0; + out.tracked_file_modification.racy_clean = 0; + out + } + + let worktree = fixture_path("status_many.sh").join(subdir); + let git_dir = worktree.join(".git"); + let index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default()).unwrap(); + let search = gix_pathspec::Search::from_specs( + crate::status::index_as_worktree::to_pathspecs(pathspecs), + None, + std::path::Path::new(""), + ) + .expect("valid specs can be normalized"); + let stack = gix_worktree::Stack::from_state_and_ignore_case( + worktree.clone(), + false, + gix_worktree::stack::State::AttributesAndIgnoreStack { + attributes: Default::default(), + ignore: Default::default(), + }, + &index, + index.path_backing(), + ); + let capabilities = gix_fs::Capabilities::probe(&git_dir); + let resource_cache = gix_diff::blob::Platform::new( + Default::default(), + gix_diff::blob::Pipeline::new( + WorktreeRoots { + old_root: None, + new_root: Some(worktree.to_owned()), + }, + gix_filter::Pipeline::new(Default::default(), Default::default()), + vec![], + gix_diff::blob::pipeline::Options { + large_file_threshold_bytes: 0, + fs: capabilities, + }, + ), + gix_diff::blob::pipeline::Mode::ToGit, + stack, + ); + + let git_dir_real = gix_path::realpath(&git_dir).unwrap(); + let cwd = gix_fs::current_dir(capabilities.precompose_unicode).unwrap(); + let context = Context { + pathspec: search, + resource_cache, + should_interrupt: &Default::default(), + dirwalk: DirwalkContext { + git_dir_realpath: &git_dir_real, + current_dir: &cwd, + ignore_case_index_lookup: None, + }, + }; + let options = Options { + object_hash: gix_hash::Kind::Sha1, + tracked_file_modifications: gix_status::index_as_worktree::Options { + fs: capabilities, + stat: crate::status::index_as_worktree::TEST_OPTIONS, + ..Default::default() + }, + dirwalk, + sorting: Some(Sorting::ByPathCaseSensitive), + rewrites, + }; + + let mut recorder = Recorder::default(); + let objects = gix_odb::at(git_dir.join("objects")).unwrap().into_arc().unwrap(); + let outcome = index_as_worktree_with_renames( + &index, + &worktree, + &mut recorder, + FastEq, + crate::status::index_as_worktree::SubmoduleStatusMock { dirty: false }, + objects, + &mut gix_features::progress::Discard, + context, + options, + ) + .unwrap(); + + assert_eq!(records_to_expectations(&recorder.records), expected); + cleanup(outcome) +} + +fn records_to_expectations<'a>(recs: &'a [Entry<'_, (), ()>]) -> Vec<Expectation<'a>> { + recs.iter() + .filter(|r| { + !matches!( + r, + Entry::Modification { + status: EntryStatus::NeedsUpdate(..), + .. + } + ) + }) + .map(|r| match r { + Entry::Modification { rela_path, status, .. } => Expectation::Modification { + rela_path: rela_path.to_str().unwrap(), + status: status.clone(), + }, + Entry::DirectoryContents { entry, .. } => Expectation::DirwalkEntry { + rela_path: entry.rela_path.to_str().unwrap(), + status: entry.status, + disk_kind: entry.disk_kind, + }, + Entry::Rewrite { + source, + dirwalk_entry, + diff, + copy, + .. + } => Expectation::Rewrite { + source_rela_path: source.rela_path().to_str().unwrap(), + dest_rela_path: dirwalk_entry.rela_path.to_str().unwrap(), + dest_dirwalk_status: dirwalk_entry.status, + diff: *diff, + copy: *copy, + }, + }) + .collect() +} + +#[derive(Debug, Clone, PartialEq)] +enum Expectation<'a> { + Modification { + rela_path: &'a str, + status: EntryStatus<(), ()>, + }, + DirwalkEntry { + rela_path: &'a str, + status: gix_dir::entry::Status, + disk_kind: Option<gix_dir::entry::Kind>, + }, + Rewrite { + source_rela_path: &'a str, + dest_rela_path: &'a str, + dest_dirwalk_status: gix_dir::entry::Status, + diff: Option<gix_diff::blob::DiffLineStats>, + copy: bool, + }, +} diff --git a/gix-status/tests/status/mod.rs b/gix-status/tests/status/mod.rs index e758770cf90..4da345b7a8d 100644 --- a/gix-status/tests/status/mod.rs +++ b/gix-status/tests/status/mod.rs @@ -1,4 +1,5 @@ mod index_as_worktree; +mod index_as_worktree_with_renames; pub fn fixture_path(name: &str) -> std::path::PathBuf { let dir = gix_testtools::scripted_fixture_read_only_standalone(std::path::Path::new(name).with_extension("sh")) diff --git a/justfile b/justfile index c43c6c94826..38294a5070f 100755 --- a/justfile +++ b/justfile @@ -92,6 +92,8 @@ check: cargo check -p gix-revision --no-default-features --features describe cargo check -p gix-mailmap --features serde cargo check -p gix-url --all-features + cargo check -p gix-status + cargo check -p gix-status --all-features cargo check -p gix-features --all-features cargo check -p gix-features --features parallel cargo check -p gix-features --features fs-walkdir-parallel From 0330ad77edab88e14812c57f812c96c5e4561045 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 5 Oct 2023 15:38:26 +0200 Subject: [PATCH 05/26] feat: add `Status` iterator. We also move the `IndexPersistedOrInMemory` type to the `worktree` module as its more widely useful. --- gix/Cargo.toml | 16 +- gix/src/config/cache/access.rs | 2 +- gix/src/lib.rs | 6 +- gix/src/pathspec.rs | 7 + gix/src/repository/dirwalk.rs | 9 +- gix/src/repository/filter.rs | 2 +- gix/src/repository/index.rs | 2 +- gix/src/repository/mod.rs | 11 - gix/src/status/index_worktree.rs | 620 +++++++++++++++++++++++++++++++ gix/src/status/mod.rs | 93 +++++ gix/src/status/platform.rs | 65 ++++ gix/src/submodule/mod.rs | 10 +- gix/src/worktree/mod.rs | 11 + 13 files changed, 822 insertions(+), 32 deletions(-) create mode 100644 gix/src/status/index_worktree.rs create mode 100644 gix/src/status/mod.rs create mode 100644 gix/src/status/platform.rs diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 7bac0835b57..583d800f87f 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -65,7 +65,7 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human command = ["dep:gix-command"] ## Obtain information similar to `git status`. -status = ["gix-status"] +status = ["gix-status", "dirwalk", "index", "blob-diff"] ## Utilities for interrupting computations and cleaning up tempfiles. interrupt = ["dep:signal-hook", "gix-tempfile/signals"] @@ -131,12 +131,12 @@ blocking-http-transport-curl-rustls = ["blocking-http-transport-curl", "dep:curl ## Stacks with `blocking-network-client` to provide support for HTTP/S using **reqwest**, and implies blocking networking as a whole, making the `https://` transport available. blocking-http-transport-reqwest = ["blocking-network-client", "gix-transport/http-client-reqwest"] ## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `rustls` crate. -blocking-http-transport-reqwest-rust-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/rustls-tls" ] +blocking-http-transport-reqwest-rust-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/rustls-tls"] ## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `rustls` crate. ## This also makes use of `trust-dns` to avoid `getaddrinfo`, but note it comes with its own problems. blocking-http-transport-reqwest-rust-tls-trust-dns = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/rustls-tls", "reqwest-for-configuration-only/trust-dns"] ## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `native-tls` crate. -blocking-http-transport-reqwest-native-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/default-tls" ] +blocking-http-transport-reqwest-native-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/default-tls"] #! #### Performance @@ -186,11 +186,11 @@ pack-cache-lru-dynamic = ["gix-pack/pack-cache-lru-dynamic"] ## Activate other features that maximize performance, like usage of threads, `zlib-ng` and access to caching in object databases. ## Note that some platforms might suffer from compile failures, which is when `max-performance-safe` should be used. -max-performance = [ "max-performance-safe", "zlib-ng", "fast-sha1" ] +max-performance = ["max-performance-safe", "zlib-ng", "fast-sha1"] ## If enabled, use assembly versions of sha1 on supported platforms. ## This might cause compile failures as well which is why it can be turned off separately. -fast-sha1 = [ "gix-features/fast-sha1" ] +fast-sha1 = ["gix-features/fast-sha1"] ## Use the C-based zlib-ng backend, which can compress and decompress significantly faster. ## Note that this will cause duplicate symbol errors if the application also depends on `zlib` - use `zlib-ng-compat` in that case. @@ -215,7 +215,7 @@ zlib-stock = ["gix-features/zlib-stock"] verbose-object-parsing-errors = ["gix-object/verbose-object-parsing-errors"] ## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde = [ "dep:serde", +serde = ["dep:serde", "gix-pack/serde", "gix-object/serde", "gix-protocol?/serde", @@ -286,7 +286,7 @@ gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } gix-commitgraph = { version = "^0.24.1", path = "../gix-commitgraph" } gix-pathspec = { version = "^0.7.0", path = "../gix-pathspec", optional = true } gix-submodule = { version = "^0.9.0", path = "../gix-submodule", optional = true } -gix-status = { version = "^0.6.0", path = "../gix-status", optional = true } +gix-status = { version = "^0.6.0", path = "../gix-status", optional = true, features = ["worktree-rewrites"] } gix-command = { version = "^0.3.5", path = "../gix-command", optional = true } gix-worktree-stream = { version = "^0.10.0", path = "../gix-worktree-stream", optional = true } @@ -301,7 +301,7 @@ prodash = { workspace = true, optional = true, features = ["progress-tree"] } once_cell = "1.14.0" signal-hook = { version = "0.3.9", default-features = false, optional = true } thiserror = "1.0.26" -serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} +serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } smallvec = "1.9.0" async-std = { version = "1.12.0", optional = true } diff --git a/gix/src/config/cache/access.rs b/gix/src/config/cache/access.rs index 464a0bf4dc3..660ab771a7a 100644 --- a/gix/src/config/cache/access.rs +++ b/gix/src/config/cache/access.rs @@ -112,7 +112,7 @@ impl Cache { } #[cfg(feature = "blob-diff")] - pub(crate) fn diff_renames(&self) -> Result<Option<crate::diff::Rewrites>, crate::diff::new_rewrites::Error> { + pub(crate) fn diff_renames(&self) -> Result<Option<gix_diff::Rewrites>, crate::diff::new_rewrites::Error> { self.diff_renames .get_or_try_init(|| crate::diff::new_rewrites(&self.resolved, self.lenient_config)) .copied() diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 8c2124270e8..689a0a8e732 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -133,8 +133,6 @@ pub use gix_ref as refs; pub use gix_refspec as refspec; pub use gix_revwalk as revwalk; pub use gix_sec as sec; -#[cfg(feature = "status")] -pub use gix_status as status; pub use gix_tempfile as tempfile; pub use gix_trace as trace; pub use gix_traverse as traverse; @@ -317,6 +315,10 @@ pub mod init; /// Not to be confused with 'status'. pub mod state; +/// +#[cfg(feature = "status")] +pub mod status; + /// pub mod shallow; diff --git a/gix/src/pathspec.rs b/gix/src/pathspec.rs index 08ac5e5230a..1123b604c94 100644 --- a/gix/src/pathspec.rs +++ b/gix/src/pathspec.rs @@ -67,6 +67,13 @@ impl<'repo> Pathspec<'repo> { )?, )?; let cache = needs_cache.then(make_attributes).transpose()?; + + gix_trace::debug!( + longest_prefix = ?search.longest_common_directory(), + prefix_dir = ?search.prefix_directory(), + patterns = ?search.patterns().map(gix_pathspec::Pattern::path).collect::<Vec<_>>() + ); + Ok(Self { repo, search, diff --git a/gix/src/repository/dirwalk.rs b/gix/src/repository/dirwalk.rs index 2495b544925..e0141613e7f 100644 --- a/gix/src/repository/dirwalk.rs +++ b/gix/src/repository/dirwalk.rs @@ -9,7 +9,7 @@ pub enum Error { #[error(transparent)] Walk(#[from] gix_dir::walk::Error), #[error("A working tree is required to perform a directory walk")] - MissinWorkDir, + MissingWorkDir, #[error(transparent)] Excludes(#[from] config::exclude_stack::Error), #[error(transparent)] @@ -57,7 +57,7 @@ impl Repository { delegate: &mut dyn gix_dir::walk::Delegate, ) -> Result<Outcome<'_>, Error> { let _span = gix_trace::coarse!("gix::dirwalk"); - let workdir = self.work_dir().ok_or(Error::MissinWorkDir)?; + let workdir = self.work_dir().ok_or(Error::MissingWorkDir)?; let mut excludes = self.excludes( index, None, @@ -70,11 +70,6 @@ impl Repository { index, crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, )?; - gix_trace::debug!( - longest_prefix = ?pathspec.search.longest_common_directory(), - prefix_dir = ?pathspec.search.prefix_directory(), - patterns = ?pathspec.search.patterns().map(gix_pathspec::Pattern::path).collect::<Vec<_>>() - ); let git_dir_realpath = crate::path::realpath_opts(self.git_dir(), self.current_dir(), crate::path::realpath::MAX_SYMLINKS)?; diff --git a/gix/src/repository/filter.rs b/gix/src/repository/filter.rs index 0ad7c0ef609..9c0cdcfa42f 100644 --- a/gix/src/repository/filter.rs +++ b/gix/src/repository/filter.rs @@ -1,4 +1,4 @@ -use crate::{filter, repository::IndexPersistedOrInMemory, Id, Repository}; +use crate::{filter, worktree::IndexPersistedOrInMemory, Id, Repository}; /// pub mod pipeline { diff --git a/gix/src/repository/index.rs b/gix/src/repository/index.rs index 85a1a664bb0..18899d09fba 100644 --- a/gix/src/repository/index.rs +++ b/gix/src/repository/index.rs @@ -1,4 +1,4 @@ -use crate::{config::cache::util::ApplyLeniencyDefault, repository::IndexPersistedOrInMemory, worktree}; +use crate::{config::cache::util::ApplyLeniencyDefault, worktree, worktree::IndexPersistedOrInMemory}; /// Index access impl crate::Repository { diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index b60a4bc870c..f635e8b2653 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -104,17 +104,6 @@ pub mod branch_remote_tracking_ref_name { } } -/// A type to represent an index which either was loaded from disk as it was persisted there, or created on the fly in memory. -#[cfg(feature = "index")] -pub enum IndexPersistedOrInMemory { - /// The index as loaded from disk, and shared across clones of the owning `Repository`. - Persisted(crate::worktree::Index), - /// A temporary index as created from the `HEAD^{tree}`, with the file path set to the place where it would be stored naturally. - /// - /// Note that unless saved explicitly, it will not persist. - InMemory(gix_index::File), -} - /// #[cfg(feature = "attributes")] pub mod pathspec_defaults_ignore_case { diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs new file mode 100644 index 00000000000..4739544daea --- /dev/null +++ b/gix/src/status/index_worktree.rs @@ -0,0 +1,620 @@ +use crate::bstr::BStr; +use crate::{config, Repository}; +use gix_status::index_as_worktree::traits::{CompareBlobs, SubmoduleStatus}; +use std::sync::atomic::AtomicBool; + +/// The error returned by [Repository::index_worktree_status()]. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error("A working tree is required to perform a directory walk")] + MissingWorkDir, + #[error(transparent)] + AttributesAndExcludes(#[from] crate::repository::attributes::Error), + #[error(transparent)] + Pathspec(#[from] crate::pathspec::init::Error), + #[error(transparent)] + Prefix(#[from] gix_path::realpath::Error), + #[error(transparent)] + FilesystemOptions(#[from] config::boolean::Error), + #[error(transparent)] + IndexAsWorktreeWithRenames(#[from] gix_status::index_as_worktree_with_renames::Error), + #[error(transparent)] + StatOptions(#[from] config::stat_options::Error), + #[error(transparent)] + ResourceCache(#[from] crate::diff::resource_cache::Error), +} + +/// Options for use with [Repository::index_worktree_status()]. +#[derive(Default, Debug, Clone, Copy, PartialEq)] +pub struct Options { + /// The way all output should be sorted. + /// + /// If `None`, and depending on the `rewrites` field, output will be immediate but the output order + /// isn't determined, and may differ between two runs. `rewrites` also depend on the order of entries that + /// are presented to it, hence for deterministic results, sorting needs to be enabled. + /// + /// If `Some(_)`, all entries are collected beforehand, so they can be sorted before outputting any of them + /// to the user. + /// + /// If immediate output of entries in any order is desired, this should be `None`, + /// along with `rewrites` being `None` as well. + pub sorting: Option<gix_status::index_as_worktree_with_renames::Sorting>, + /// If not `None`, the options to configure the directory walk, determining how its results will look like. + /// + /// If `None`, only modification checks are performed. + /// + /// Can be instantiated with [Repository::dirwalk_options()]. + pub dirwalk_options: Option<crate::dirwalk::Options>, + /// If `Some(_)`, along with `Some(_)` in `dirwalk_options`, rewrite tracking will be performed between the + /// index and the working tree. + /// Note that there is no git-configuration specific to index-worktree rename tracking. + /// When rewrite tracking is enabled, there will be a delay for some entries as they partake in the rename-analysis. + pub rewrites: Option<gix_diff::Rewrites>, + /// If set, don't use more than this amount of threads for the tracked modification check. + /// Otherwise, usually use as many threads as there are logical cores. + /// A value of 0 is interpreted as no-limit + pub thread_limit: Option<usize>, +} + +impl Repository { + /// Obtain the status between the index and the worktree, involving modification checks + /// for all tracked files along with information about untracked (and posisbly ignored) files (if configured). + /// + /// * `index` + /// - The index to use for modification checks, and to know which files are tacked when applying the dirwalk. + /// * `patterns` + /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered. + /// * `delegate` + /// - The sink for receiving all status data. + /// * `compare` + /// - The implementations for fine-grained control over what happens if a hash must be recalculated. + /// * `submodule` + /// - Control what kind of information to retrieve when a submodule is encountered while traversing the index. + /// * `progress` + /// - A progress indication for index modification checks. + /// * `should_interrupt` + /// - A flag to stop the whole operation. + /// * `options` + /// - Additional configuration for all parts of the operation. + /// + /// ### Note + /// + /// This is a lower-level method, prefer the [`status`](Repository::status()) method for greater ease of use. + #[allow(clippy::too_many_arguments)] + pub fn index_worktree_status<'index, T, U, E>( + &self, + index: &'index gix_index::State, + patterns: impl IntoIterator<Item = impl AsRef<BStr>>, + delegate: &mut impl gix_status::index_as_worktree_with_renames::VisitEntry< + 'index, + ContentChange = T, + SubmoduleStatus = U, + >, + compare: impl CompareBlobs<Output = T> + Send + Clone, + submodule: impl SubmoduleStatus<Output = U, Error = E> + Send + Clone, + progress: &mut dyn gix_features::progress::Progress, + should_interrupt: &AtomicBool, + options: Options, + ) -> Result<gix_status::index_as_worktree_with_renames::Outcome, Error> + where + T: Send + Clone, + U: Send + Clone, + E: std::error::Error + Send + Sync + 'static, + { + let _span = gix_trace::coarse!("gix::index_worktree_status"); + let workdir = self.work_dir().ok_or(Error::MissingWorkDir)?; + let attrs_and_excludes = self.attributes( + index, + crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, + crate::worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped, + None, + )?; + let pathspec = crate::Pathspec::new( + self, + options + .dirwalk_options + .as_ref() + .map_or(false, |opts| opts.empty_patterns_match_prefix), + patterns, + true, /* inherit ignore case */ + || Ok(attrs_and_excludes.clone()), + )?; + + let cwd = self.current_dir(); + let git_dir_realpath = crate::path::realpath_opts(self.git_dir(), cwd, crate::path::realpath::MAX_SYMLINKS)?; + let fs_caps = self.filesystem_options()?; + let accelerate_lookup = fs_caps.ignore_case.then(|| index.prepare_icase_backing()); + let resource_cache = crate::diff::resource_cache( + self, + gix_diff::blob::pipeline::Mode::ToGit, + attrs_and_excludes.inner, + gix_diff::blob::pipeline::WorktreeRoots { + old_root: None, + new_root: Some(workdir.to_owned()), + }, + )?; + + let out = gix_status::index_as_worktree_with_renames( + index, + workdir, + delegate, + compare, + submodule, + self.objects.clone().into_arc().expect("arc conversion always works"), + progress, + gix_status::index_as_worktree_with_renames::Context { + pathspec: pathspec.search, + resource_cache, + should_interrupt, + dirwalk: gix_status::index_as_worktree_with_renames::DirwalkContext { + git_dir_realpath: git_dir_realpath.as_path(), + current_dir: cwd, + ignore_case_index_lookup: accelerate_lookup.as_ref(), + }, + }, + gix_status::index_as_worktree_with_renames::Options { + sorting: options.sorting, + object_hash: self.object_hash(), + tracked_file_modifications: gix_status::index_as_worktree::Options { + fs: fs_caps, + thread_limit: options.thread_limit, + stat: self.stat_options()?, + }, + dirwalk: options.dirwalk_options.map(Into::into), + rewrites: options.rewrites, + }, + )?; + Ok(out) + } +} + +/// An iterator for changes between the index and the worktree. +/// +/// Note that depending on the underlying configuration, there might be a significant delay until the first +/// item is received due to the buffering necessary to perform rename tracking and/or sorting. +/// +/// ### Index Changes +/// +/// Changes to the index are collected and it's possible to write the index back using [iter::Outcome::write_changes()]. +/// Note that these changes are not observable, they will always be kept. +#[cfg(feature = "parallel")] +pub struct Iter { + #[allow(clippy::type_complexity)] + rx_and_join: Option<( + std::sync::mpsc::Receiver<iter::Item>, + std::thread::JoinHandle<Result<iter::Outcome, crate::status::index_worktree::Error>>, + )>, + should_interrupt: std::sync::Arc<AtomicBool>, + /// The outcome of the operation, only available once the operation has ended. + out: Option<iter::Outcome>, + /// The set of `(entry_index, change)` we extracted in order to potentially write back the index with the changes applied. + changes: Vec<(usize, iter::ApplyChange)>, +} + +/// +#[cfg(feature = "parallel")] +pub mod iter { + use crate::bstr::BString; + use crate::config::cache::util::ApplyLeniencyDefault; + use crate::status::index_worktree::iter; + use crate::status::{index_worktree, Platform}; + use crate::worktree::IndexPersistedOrInMemory; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + + pub(super) enum ApplyChange { + SetSizeToZero, + NewStat(crate::index::entry::Stat), + } + + /// The data the thread sends over to the receiving iterator. + pub struct Outcome { + /// The outcome of the index-to-worktree comparison operation. + pub index_worktree: gix_status::index_as_worktree_with_renames::Outcome, + /// The index that was used for the operation. + pub index: crate::worktree::IndexPersistedOrInMemory, + skip_hash: bool, + changes: Option<Vec<(usize, iter::ApplyChange)>>, + } + + impl Outcome { + /// Returns `true` if the index has received currently unapplied changes that *should* be written back. + /// + /// If they are not written back, subsequent `status` operations will take longer to complete, whereas the + /// additional work can be prevented by writing the changes back to the index. + pub fn has_changes(&self) -> bool { + self.changes.as_ref().map_or(false, |changes| !changes.is_empty()) + } + + /// Write the changes if there are any back to the index file. + /// This can only be done once as the changes are consumed in the process, if there were any. + pub fn write_changes(&mut self) -> Option<Result<(), gix_index::file::write::Error>> { + let _span = gix_features::trace::coarse!("gix::status::index_worktree::iter::Outcome::write_changes()"); + let changes = self.changes.take()?; + let mut index = match &self.index { + IndexPersistedOrInMemory::Persisted(persisted) => (***persisted).clone(), + IndexPersistedOrInMemory::InMemory(index) => index.clone(), + }; + + let entries = index.entries_mut(); + for (entry_index, change) in changes { + let entry = &mut entries[entry_index]; + match change { + ApplyChange::SetSizeToZero => { + entry.stat.size = 0; + } + ApplyChange::NewStat(new_stat) => { + entry.stat = new_stat; + } + } + } + + Some(index.write(crate::index::write::Options { + extensions: Default::default(), + skip_hash: self.skip_hash, + })) + } + } + + /// Either an index entry for renames or another directory entry in case of copies. + #[derive(Clone, PartialEq, Debug)] + pub enum RewriteSource { + /// The source originates in the index and is detected as missing in the working tree. + /// This can also happen for copies. + RewriteFromIndex { + /// The entry that is the source of the rewrite, which means it was removed on disk, + /// equivalent to [Change::Removed](gix_status::index_as_worktree::Change::Removed). + /// + /// Note that the [entry-id](gix_index::Entry::id) is the content-id of the source of the rewrite. + source_entry: gix_index::Entry, + /// The index of the `source_entry` for lookup in [`gix_index::State::entries()`] - useful to look at neighbors. + source_entry_index: usize, + /// The repository-relative path of the `source_entry`. + source_rela_path: BString, + /// The computed status of the `source_entry`. + source_status: gix_status::index_as_worktree::EntryStatus<(), SubmoduleStatus>, + }, + /// This source originates in the directory tree and is always the source of copies. + CopyFromDirectoryEntry { + /// The source of the copy operation, which is also an entry of the directory walk. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the source of the rewrite. + source_dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `source_dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `source_dirwalk_entry` and + /// if [gix_dir::walk::Options::emit_collapsed] was [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + source_dirwalk_entry_collapsed_directory_status: Option<gix_dir::entry::Status>, + /// The object id as it would appear if the entry was written to the object database. + /// It's the same as [`dirwalk_entry_id`](Item::Rewrite), or `diff` is `Some(_)` to indicate that the copy + /// was determined by similarity, not by content equality. + source_dirwalk_entry_id: gix_hash::ObjectId, + }, + } + + impl<'index> From<gix_status::index_as_worktree_with_renames::RewriteSource<'index, (), SubmoduleStatus>> + for RewriteSource + { + fn from(value: gix_status::index_as_worktree_with_renames::RewriteSource<'index, (), SubmoduleStatus>) -> Self { + match value { + gix_status::index_as_worktree_with_renames::RewriteSource::RewriteFromIndex { + index_entries: _, + source_entry, + source_entry_index, + source_rela_path, + source_status, + } => RewriteSource::RewriteFromIndex { + source_entry: source_entry.clone(), + source_entry_index, + source_rela_path: source_rela_path.to_owned(), + source_status, + }, + gix_status::index_as_worktree_with_renames::RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, + source_dirwalk_entry_collapsed_directory_status, + source_dirwalk_entry_id, + } => RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, + source_dirwalk_entry_collapsed_directory_status, + source_dirwalk_entry_id, + }, + } + } + } + + /// The item produced by the iterator + #[derive(Clone, PartialEq, Debug)] + pub enum Item { + /// A tracked file was modified, and index-specific information is passed. + Modification { + /// The entry with modifications. + entry: gix_index::Entry, + /// The index of the `entry` for lookup in [`gix_index::State::entries()`] - useful to look at neighbors. + entry_index: usize, + /// The repository-relative path of the entry. + rela_path: BString, + /// The computed status of the entry. + status: gix_status::index_as_worktree::EntryStatus<(), SubmoduleStatus>, + }, + /// An entry returned by the directory walk, without any relation to the index. + /// + /// This can happen if ignored files are returned as well, or if rename-tracking is disabled. + DirectoryContents { + /// The entry found during the disk traversal. + entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + collapsed_directory_status: Option<gix_dir::entry::Status>, + }, + /// The rewrite tracking discovered a match between a deleted and added file, and considers them equal enough, + /// depending on the tracker settings. + /// + /// Note that the source of the rewrite is always the index as it detects the absence of entries, something that + /// can't be done during a directory walk. + Rewrite { + /// The source of the rewrite operation. + source: RewriteSource, + /// The untracked entry found during the disk traversal, the destination of the rewrite. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the destination of the rewrite, and the current + /// location of the entry. + dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `dirwalk_entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + dirwalk_entry_collapsed_directory_status: Option<gix_dir::entry::Status>, + /// The object id after the rename, specifically hashed in order to determine equality. + dirwalk_entry_id: gix_hash::ObjectId, + /// It's `None` if the 'source.id' is equal to `dirwalk_entry_id`, as identity made an actual diff computation unnecessary. + /// Otherwise, and if enabled, it's `Some(stats)` to indicate how similar both entries were. + diff: Option<gix_diff::blob::DiffLineStats>, + /// If true, this rewrite is created by copy, and 'source.id' is pointing to its source. + /// Otherwise, it's a rename, and 'source.id' points to a deleted object, + /// as renames are tracked as deletions and additions of the same or similar content. + copy: bool, + }, + } + + impl<'index> From<gix_status::index_as_worktree_with_renames::Entry<'index, (), SubmoduleStatus>> for Item { + fn from(value: gix_status::index_as_worktree_with_renames::Entry<'index, (), SubmoduleStatus>) -> Self { + match value { + gix_status::index_as_worktree_with_renames::Entry::Modification { + entries: _, + entry, + entry_index, + rela_path, + status, + } => Item::Modification { + entry: entry.clone(), + entry_index, + rela_path: rela_path.to_owned(), + status, + }, + gix_status::index_as_worktree_with_renames::Entry::DirectoryContents { + entry, + collapsed_directory_status, + } => Item::DirectoryContents { + entry, + collapsed_directory_status, + }, + gix_status::index_as_worktree_with_renames::Entry::Rewrite { + source, + dirwalk_entry, + dirwalk_entry_collapsed_directory_status, + dirwalk_entry_id, + diff, + copy, + } => Item::Rewrite { + source: source.into(), + dirwalk_entry, + dirwalk_entry_collapsed_directory_status, + dirwalk_entry_id, + diff, + copy, + }, + } + } + } + + /// The status of a submodule + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] + pub struct SubmoduleStatus {} + + /// The error returned by [Platform::into_index_worktree_iter()](crate::status::Platform::into_index_worktree_iter()). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + Index(#[from] crate::worktree::open_index::Error), + #[error("Failed to spawn producer thread")] + SpawnThread(#[source] std::io::Error), + #[error(transparent)] + ConfigSkipHash(#[from] crate::config::boolean::Error), + } + + /// Lifecycle + impl<'repo, Progress> Platform<'repo, Progress> + where + Progress: gix_features::progress::Progress, + { + /// Turn the platform into an iterator for changes between the index and the working tree. + /// + /// * `patterns` + /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered. + pub fn into_index_worktree_iter(self, patterns: Vec<BString>) -> Result<index_worktree::Iter, Error> { + let index = match self.index { + None => IndexPersistedOrInMemory::Persisted(self.repo.index_or_empty()?), + Some(index) => index, + }; + + let should_interrupt = Arc::new(AtomicBool::default()); + let (tx, rx) = std::sync::mpsc::channel(); + let mut collect = Collect { tx }; + let submodule = ComputeSubmoduleStatus { _mode: self.submodules }; + let skip_hash = self + .repo + .config + .resolved + .boolean("index", None, "skipHash") + .map(|res| crate::config::tree::Index::SKIP_HASH.enrich_error(res)) + .transpose() + .with_lenient_default(self.repo.config.lenient_config)? + .unwrap_or_default(); + let join = std::thread::Builder::new() + .name("gix::status::index_worktree::iter::producer".into()) + .spawn({ + let repo = self.repo.clone().into_sync(); + let options = self.index_worktree_options; + let should_interrupt = should_interrupt.clone(); + let mut progress = self.progress; + move || -> Result<_, crate::status::index_worktree::Error> { + let repo = repo.to_thread_local(); + let out = repo.index_worktree_status( + &index, + patterns, + &mut collect, + gix_status::index_as_worktree::traits::FastEq, + submodule, + &mut progress, + &should_interrupt, + options, + )?; + Ok(Outcome { + index_worktree: out, + index, + changes: None, + skip_hash, + }) + } + }) + .map_err(Error::SpawnThread)?; + + Ok(super::Iter { + rx_and_join: Some((rx, join)), + should_interrupt, + changes: Vec::new(), + out: None, + }) + } + } + + impl Iterator for super::Iter { + type Item = Result<Item, crate::status::index_worktree::Error>; + + fn next(&mut self) -> Option<Self::Item> { + loop { + let (rx, _join) = self.rx_and_join.as_ref()?; + match rx.recv().ok() { + Some(item) => { + if let Some(item) = self.maybe_keep_index_change(item) { + break Some(Ok(item)); + } + continue; + } + None => { + let (_rx, handle) = self.rx_and_join.take()?; + break match handle.join().expect("no panic") { + Ok(out) => { + self.out = Some(out); + None + } + Err(err) => Some(Err(err)), + }; + } + } + } + } + } + + impl super::Iter { + fn maybe_keep_index_change(&mut self, item: Item) -> Option<Item> { + let change = match item { + Item::Modification { + status: gix_status::index_as_worktree::EntryStatus::NeedsUpdate(stat), + entry_index, + .. + } => (entry_index, ApplyChange::NewStat(stat)), + Item::Modification { + status: + gix_status::index_as_worktree::EntryStatus::Change( + gix_status::index_as_worktree::Change::Modification { + set_entry_stat_size_zero, + .. + }, + ), + entry_index, + .. + } if set_entry_stat_size_zero => (entry_index, ApplyChange::SetSizeToZero), + _ => return Some(item), + }; + + self.changes.push(change); + None + } + } + + impl Drop for super::Iter { + fn drop(&mut self) { + self.should_interrupt.store(true, Ordering::Relaxed); + // Allow to temporarily 'leak' the producer to not block on drop, nobody + // is interested in the result of the thread anymore. + drop(self.rx_and_join.take()); + } + } + + #[derive(Clone)] + struct ComputeSubmoduleStatus { + _mode: crate::status::Submodule, + } + + mod submodule_status { + use crate::bstr::BStr; + use crate::status::index_worktree::iter::{ComputeSubmoduleStatus, SubmoduleStatus}; + + /// The error returned by the submodule status implementation - it will be turned into a box, hence it doesn't have to + /// be private. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + #[error("TBD")] + pub enum Error {} + + impl gix_status::index_as_worktree::traits::SubmoduleStatus for ComputeSubmoduleStatus { + type Output = SubmoduleStatus; + type Error = Error; + + fn status( + &mut self, + _entry: &gix_index::Entry, + _rela_path: &BStr, + ) -> Result<Option<Self::Output>, Self::Error> { + todo!("impl in Submodule itself, it will be calling this exact implementation under the hood, maybe with reduced threads") + } + } + } + + struct Collect { + tx: std::sync::mpsc::Sender<Item>, + } + + impl<'index> gix_status::index_as_worktree_with_renames::VisitEntry<'index> for Collect { + type ContentChange = <gix_status::index_as_worktree::traits::FastEq as gix_status::index_as_worktree::traits::CompareBlobs>::Output; + type SubmoduleStatus = + <ComputeSubmoduleStatus as gix_status::index_as_worktree::traits::SubmoduleStatus>::Output; + + fn visit_entry( + &mut self, + entry: gix_status::index_as_worktree_with_renames::Entry< + 'index, + Self::ContentChange, + Self::SubmoduleStatus, + >, + ) { + // NOTE: we assume that the receiver triggers interruption so the operation will stop if the receiver is down. + self.tx.send(entry.into()).ok(); + } + } +} diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs new file mode 100644 index 00000000000..f3cf1abce54 --- /dev/null +++ b/gix/src/status/mod.rs @@ -0,0 +1,93 @@ +use crate::{config, Repository}; +pub use gix_status as plumbing; + +/// A structure to hold options configuring the status request, which can then be turned into an iterator. +pub struct Platform<'repo, Progress> +where + Progress: gix_features::progress::Progress + 'static, +{ + #[cfg_attr(not(feature = "parallel"), allow(dead_code))] + repo: &'repo Repository, + #[cfg_attr(not(feature = "parallel"), allow(dead_code))] + progress: Progress, + index: Option<crate::worktree::IndexPersistedOrInMemory>, + submodules: Submodule, + index_worktree_options: index_worktree::Options, +} + +/// How to obtain a submodule's status. +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub enum Submodule { + /// Use the ['ignore' value](crate::Submodule::ignore) to determine which submodules + /// participate in the status query, and to which extent. + AsConfigured { + /// If `true`, default `false`, the computation will stop once the first in a ladder operations + /// ordered from cheap to expensive shows that the submodule is dirty. + /// Thus, submodules that are clean will still impose the complete set of computation, as configured. + check_dirty: bool, + }, + /// Instead of the configuration, use the given ['ignore' value](crate::submodule::config::Ignore). + /// This makes it possible to fine-tune the amount of work invested in this status, while allowing + /// to turn off all submodule status information. + Given { + /// The portion of the submodule status to ignore. + ignore: crate::submodule::config::Ignore, + /// If `true`, default `false`, the computation will stop once the first in a ladder operations + /// ordered from cheap to expensive shows that the submodule is dirty. + /// Thus, submodules that are clean will still impose the complete set of computation, as given. + check_dirty: bool, + }, +} + +impl Default for Submodule { + fn default() -> Self { + Submodule::AsConfigured { check_dirty: false } + } +} + +/// Status +impl Repository { + /// Obtain a platform for configuring iterators for traversing git repository status information. + /// + /// By default, this is set to the fastest and most immediate way of obtaining a status, + /// which is most similar to + /// + /// `git status --untracked=all --ignored=no --no-renames` + /// + /// which implies that submodule information is provided by default. + /// + /// Pass `progress` to receive progress information on file modifications on this repository. + /// Use [`progress::Discard`](crate::progress::Discard) to discard all progress information. + /// + /// ### Deviation + /// + /// Whereas Git runs the index-modified check before the directory walk to set entries + /// as up-to-date to (potentially) safe some disk-access, we run both in parallel which + /// ultimately is much faster. + // TODO: if untracked and ignored entries are disabled, don't run a dirwalk at all. + // TODO: submodule support with reasonable configurability. + pub fn status<P>(&self, progress: P) -> Result<Platform<'_, P>, config::boolean::Error> + where + P: gix_features::progress::Progress + 'static, + { + Ok(Platform { + repo: self, + progress, + index: None, + submodules: Submodule::default(), + index_worktree_options: index_worktree::Options { + sorting: None, + dirwalk_options: Some(self.dirwalk_options()?), + rewrites: None, + thread_limit: None, + }, + }) + } + + // TODO: submodule status, where the base of the operation is the list of submodules in the .gitmodules file. +} + +mod platform; + +/// +pub mod index_worktree; diff --git a/gix/src/status/platform.rs b/gix/src/status/platform.rs new file mode 100644 index 00000000000..14d170551d1 --- /dev/null +++ b/gix/src/status/platform.rs @@ -0,0 +1,65 @@ +use crate::status::{index_worktree, Platform, Submodule}; + +/// Builder +impl<'repo, Progress> Platform<'repo, Progress> +where + Progress: gix_features::progress::Progress, +{ + /// Call `cb` on dirwalk options if these are set (which is the default). The directory walk is used to find + /// untracked files or ignored files. + /// `cb` will be able to run builder-methods on the passed dirwalk options. + pub fn dirwalk_options(mut self, cb: impl FnOnce(crate::dirwalk::Options) -> crate::dirwalk::Options) -> Self { + if let Some(opts) = self.index_worktree_options.dirwalk_options.take() { + self.index_worktree_options.dirwalk_options = Some(cb(opts)); + } + self + } + + /// Configure how the `submodule_status` is obtained when looking at submodules that are still mentioned in the index. + // If `None` is given, no submodule status check is performed. + pub fn index_worktree_submodules(mut self, submodules: impl Into<Option<Submodule>>) -> Self { + let submodules = submodules.into(); + self.submodules = match submodules { + None => Submodule::Given { + ignore: crate::submodule::config::Ignore::All, + check_dirty: false, + }, + Some(status) => status, + }; + self + } + + /// Set the `index` to use when making comparisons to the worktree and the head revision. + /// + /// Defaults to the current index, or an empty one if it doesn't exist (yet). + pub fn index(mut self, index: crate::worktree::IndexPersistedOrInMemory) -> Self { + self.index = Some(index); + self + } + + /// Configure the index-to-worktree rename tracking with `rewrites`, which is `None` by default. + /// + /// Note that Git does not have configuration related to rename tracking of changes between the index + /// and the worktree. The closest there is can be obtained using [`crate::diff::new_rewrites()`], which refers + /// to rename tracking between trees. + /// + /// Also note that if `rewrites` are `Some()`, [`sorting`](index_worktree::Options::sorting) will automatically be + /// configured to assure deterministic outcomes for rewrite solutions. + pub fn index_worktree_rewrites(mut self, rewrites: impl Into<Option<gix_diff::Rewrites>>) -> Self { + let rewrites = rewrites.into(); + self.index_worktree_options.rewrites = rewrites; + if rewrites.is_some() && self.index_worktree_options.sorting.is_none() { + self.index_worktree_options.sorting = + Some(gix_status::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + } + self + } + + /// Adjust all options related to the index-worktree status. + /// This is a catch-all in case there are no more specific methods that could be used instead to change + /// the respective option. + pub fn index_worktree_options_mut(mut self, cb: impl FnOnce(&mut index_worktree::Options)) -> Self { + cb(&mut self.index_worktree_options); + self + } +} diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index fcfffd26f16..b2ca0d0bef6 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -9,7 +9,7 @@ use std::{ pub use gix_submodule::*; -use crate::{bstr::BStr, repository::IndexPersistedOrInMemory, Repository, Submodule}; +use crate::{bstr::BStr, worktree::IndexPersistedOrInMemory, Repository, Submodule}; pub(crate) type ModulesFileStorage = gix_features::threading::OwnShared<gix_fs::SharedFileSnapshotMut<File>>; /// A lazily loaded and auto-updated worktree index. @@ -96,16 +96,22 @@ impl<'repo> Submodule<'repo> { } /// Return the url from which to clone or update the submodule. + /// + /// This method takes into consideration submodule configuration overrides. pub fn url(&self) -> Result<gix_url::Url, config::url::Error> { self.state.modules.url(self.name()) } /// Return the `update` field from this submodule's configuration, if present, or `None`. + /// + /// This method takes into consideration submodule configuration overrides. pub fn update(&self) -> Result<Option<config::Update>, config::update::Error> { self.state.modules.update(self.name()) } /// Return the `branch` field from this submodule's configuration, if present, or `None`. + /// + /// This method takes into consideration submodule configuration overrides. pub fn branch(&self) -> Result<Option<config::Branch>, config::branch::Error> { self.state.modules.branch(self.name()) } @@ -126,6 +132,8 @@ impl<'repo> Submodule<'repo> { } /// Return the `ignore` field from this submodule's configuration, if present, or `None`. + /// + /// This method takes into consideration submodule configuration overrides. pub fn ignore(&self) -> Result<Option<config::Ignore>, config::Error> { self.state.modules.ignore(self.name()) } diff --git a/gix/src/worktree/mod.rs b/gix/src/worktree/mod.rs index f0a0c44a780..2a336b1e539 100644 --- a/gix/src/worktree/mod.rs +++ b/gix/src/worktree/mod.rs @@ -20,6 +20,17 @@ pub(crate) type IndexStorage = gix_features::threading::OwnShared<gix_fs::Shared #[cfg(feature = "index")] pub type Index = gix_fs::SharedFileSnapshot<gix_index::File>; +/// A type to represent an index which either was loaded from disk as it was persisted there, or created on the fly in memory. +#[cfg(feature = "index")] +pub enum IndexPersistedOrInMemory { + /// The index as loaded from disk, and shared across clones of the owning `Repository`. + Persisted(crate::worktree::Index), + /// A temporary index as created from the `HEAD^{tree}`, with the file path set to the place where it would be stored naturally. + /// + /// Note that unless saved explicitly, it will not persist. + InMemory(gix_index::File), +} + /// A stand-in to a worktree as result of a worktree iteration. /// /// It provides access to typical worktree state, but may not actually point to a valid checkout as the latter has been moved or From 2d40bdf325ed5b09b0ef6fd21e8b3da47d710451 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Mon, 4 Mar 2024 17:34:55 +0100 Subject: [PATCH 06/26] adapt to changes in `gix` --- Cargo.lock | 4 ++-- gitoxide-core/src/repository/attributes/query.rs | 2 +- gitoxide-core/src/repository/index/entries.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efe227eb3dd..5dd0701ddd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3475,9 +3475,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", diff --git a/gitoxide-core/src/repository/attributes/query.rs b/gitoxide-core/src/repository/attributes/query.rs index 09d1b0a0b48..b3443cdec95 100644 --- a/gitoxide-core/src/repository/attributes/query.rs +++ b/gitoxide-core/src/repository/attributes/query.rs @@ -1,4 +1,4 @@ -use gix::repository::IndexPersistedOrInMemory; +use gix::worktree::IndexPersistedOrInMemory; use crate::OutputFormat; diff --git a/gitoxide-core/src/repository/index/entries.rs b/gitoxide-core/src/repository/index/entries.rs index b4f335e6ae3..7126849ad4d 100644 --- a/gitoxide-core/src/repository/index/entries.rs +++ b/gitoxide-core/src/repository/index/entries.rs @@ -25,7 +25,7 @@ pub(crate) mod function { use gix::{ bstr::{BStr, BString}, - repository::IndexPersistedOrInMemory, + worktree::IndexPersistedOrInMemory, Repository, }; From 434f5434d7242f7f3d6b595f767195c51a3acd86 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 7 Mar 2024 15:29:16 +0100 Subject: [PATCH 07/26] fix: make it possible to use a submodule root for a full walk. Previously, it would not allow to enter the repository, making a walk impossible. --- gix-dir/src/entry.rs | 12 +++- gix-dir/src/walk/classify.rs | 36 +++++----- gix-dir/src/walk/function.rs | 19 +++-- gix-dir/src/walk/mod.rs | 20 ++++-- gix-dir/src/walk/readdir.rs | 8 ++- gix-dir/tests/fixtures/many.sh | 7 ++ gix-dir/tests/walk/mod.rs | 127 ++++++++++++++++++++++++++++++++- 7 files changed, 199 insertions(+), 30 deletions(-) diff --git a/gix-dir/src/entry.rs b/gix-dir/src/entry.rs index 81c6fae5204..4bfc02e02a4 100644 --- a/gix-dir/src/entry.rs +++ b/gix-dir/src/entry.rs @@ -168,6 +168,9 @@ impl Status { /// This implements the default rules of `git status`, which is good for a minimal traversal through /// tracked and non-ignored portions of a worktree. /// `for_deletion` is used to determine if recursion into a directory is allowed even though it otherwise wouldn't be. + /// If `worktree_root_is_repository` is `true`, then this status is part of the root of an iteration, and the corresponding + /// worktree root is a repository itself. This typically happens for submodules. In this case, recursion rules are relaxed + /// to allow traversing submodule worktrees. /// /// Use `pathspec_match` to determine if a pathspec matches in any way, affecting the decision to recurse. pub fn can_recurse( @@ -175,8 +178,15 @@ impl Status { file_type: Option<Kind>, pathspec_match: Option<PathspecMatch>, for_deletion: Option<ForDeletionMode>, + worktree_root_is_repository: bool, ) -> bool { - let is_dir_on_disk = file_type.map_or(false, |ft| ft.is_recursable_dir()); + let is_dir_on_disk = file_type.map_or(false, |ft| { + if worktree_root_is_repository { + ft.is_dir() + } else { + ft.is_recursable_dir() + } + }); if !is_dir_on_disk { return false; } diff --git a/gix-dir/src/walk/classify.rs b/gix-dir/src/walk/classify.rs index 4b89911d3d1..3d5f5c3f913 100644 --- a/gix-dir/src/walk/classify.rs +++ b/gix-dir/src/walk/classify.rs @@ -4,8 +4,7 @@ use std::borrow::Cow; use crate::entry::PathspecMatch; use crate::walk::{Context, Error, ForDeletionMode, Options}; use bstr::{BStr, BString, ByteSlice}; -use std::ffi::OsStr; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; /// Classify the `worktree_relative_root` path and return the first `PathKind` that indicates that /// it isn't a directory, leaving `buf` with the path matching the returned `PathKind`, @@ -16,20 +15,18 @@ pub fn root( worktree_relative_root: &Path, options: Options, ctx: &mut Context<'_>, -) -> Result<Outcome, Error> { +) -> Result<(Outcome, bool), Error> { buf.clear(); let mut last_length = None; let mut path_buf = worktree_root.to_owned(); // These initial values kick in if worktree_relative_root.is_empty(); - let mut out = None; - for component in worktree_relative_root - .components() - .chain(if worktree_relative_root.as_os_str().is_empty() { - Some(Component::Normal(OsStr::new(""))) - } else { - None - }) - { + let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok(); + let mut out = path(&mut path_buf, buf, 0, file_kind, || None, options, ctx)?; + let worktree_root_is_repository = out + .disk_kind + .map_or(false, |kind| matches!(kind, entry::Kind::Repository)); + + for component in worktree_relative_root.components() { if last_length.is_some() { buf.push(b'/'); } @@ -37,7 +34,7 @@ pub fn root( buf.extend_from_slice(gix_path::os_str_into_bstr(component.as_os_str()).expect("no illformed UTF8")); let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok(); - let res = path( + out = path( &mut path_buf, buf, last_length.map(|l| l + 1 /* slash */).unwrap_or_default(), @@ -46,16 +43,17 @@ pub fn root( options, ctx, )?; - out = Some(res); - if !res - .status - .can_recurse(res.disk_kind, res.pathspec_match, options.for_deletion) - { + if !out.status.can_recurse( + out.disk_kind, + out.pathspec_match, + options.for_deletion, + worktree_root_is_repository, + ) { break; } last_length = Some(buf.len()); } - Ok(out.expect("One iteration of the loop at least")) + Ok((out, worktree_root_is_repository)) } /// The product of [`path()`] calls. #[derive(Debug, Copy, Clone)] diff --git a/gix-dir/src/walk/function.rs b/gix-dir/src/walk/function.rs index a7ec42f7a3d..5d75311eaf3 100644 --- a/gix-dir/src/walk/function.rs +++ b/gix-dir/src/walk/function.rs @@ -60,15 +60,21 @@ pub fn walk( let (mut current, worktree_root_relative) = assure_no_symlink_in_root(worktree_root, &root)?; let mut out = Outcome::default(); let mut buf = BString::default(); - let root_info = classify::root( + let (root_info, worktree_root_is_repository) = classify::root( worktree_root, &mut buf, worktree_root_relative.as_ref(), options, &mut ctx, )?; - if !can_recurse(buf.as_bstr(), root_info, options.for_deletion, delegate) { - if buf.is_empty() && !matches!(root_info.disk_kind, Some(entry::Kind::Directory { .. })) { + if !can_recurse( + buf.as_bstr(), + root_info, + options.for_deletion, + worktree_root_is_repository, /* is root */ + delegate, + ) { + if buf.is_empty() && !root_info.disk_kind.map_or(false, |kind| kind.is_dir()) { return Err(Error::WorktreeRootIsFile { root: root.to_owned() }); } if options.precompose_unicode { @@ -141,12 +147,17 @@ pub(super) fn can_recurse( rela_path: &BStr, info: classify::Outcome, for_deletion: Option<ForDeletionMode>, + is_root: bool, delegate: &mut dyn Delegate, ) -> bool { if info.disk_kind.map_or(true, |k| !k.is_dir()) { return false; } - delegate.can_recurse(EntryRef::from_outcome(Cow::Borrowed(rela_path), info), for_deletion) + delegate.can_recurse( + EntryRef::from_outcome(Cow::Borrowed(rela_path), info), + for_deletion, + is_root, + ) } /// Possibly emit an entry to `for_each` in case the provided information makes that possible. diff --git a/gix-dir/src/walk/mod.rs b/gix-dir/src/walk/mod.rs index 3f593c40d9b..026edd485c3 100644 --- a/gix-dir/src/walk/mod.rs +++ b/gix-dir/src/walk/mod.rs @@ -69,12 +69,24 @@ pub trait Delegate { /// Use `for_deletion` to specify if the seen entries should ultimately be deleted, which may affect the decision /// of whether to resource or not. /// + /// If `worktree_root_is_repository` is `true`, then this status is part of the root of an iteration, and the corresponding + /// worktree root is a repository itself. This typically happens for submodules. In this case, recursion rules are relaxed + /// to allow traversing submodule worktrees. + /// /// Note that this method will see all directories, even though not all of them may end up being [emitted](Self::emit()). /// If this method returns `false`, the `entry` will always be emitted. - fn can_recurse(&mut self, entry: EntryRef<'_>, for_deletion: Option<ForDeletionMode>) -> bool { - entry - .status - .can_recurse(entry.disk_kind, entry.pathspec_match, for_deletion) + fn can_recurse( + &mut self, + entry: EntryRef<'_>, + for_deletion: Option<ForDeletionMode>, + worktree_root_is_repository: bool, + ) -> bool { + entry.status.can_recurse( + entry.disk_kind, + entry.pathspec_match, + for_deletion, + worktree_root_is_repository, + ) } } diff --git a/gix-dir/src/walk/readdir.rs b/gix-dir/src/walk/readdir.rs index 05bc5a93301..cda368a05fc 100644 --- a/gix-dir/src/walk/readdir.rs +++ b/gix-dir/src/walk/readdir.rs @@ -63,7 +63,13 @@ pub(super) fn recursive( ctx, )?; - if can_recurse(current_bstr.as_bstr(), info, opts.for_deletion, delegate) { + if can_recurse( + current_bstr.as_bstr(), + info, + opts.for_deletion, + false, /* is root */ + delegate, + ) { let subdir_may_collapse = state.may_collapse(current); let (action, subdir_prevent_collapse) = recursive( subdir_may_collapse, diff --git a/gix-dir/tests/fixtures/many.sh b/gix-dir/tests/fixtures/many.sh index 0c17c4d3a3b..ee4250dbb97 100644 --- a/gix-dir/tests/fixtures/many.sh +++ b/gix-dir/tests/fixtures/many.sh @@ -29,6 +29,13 @@ git init dir-with-tracked-file git commit -m "init" ) +git init repo-with-submodule +(cd repo-with-submodule + git submodule add ../dir-with-tracked-file submodule + git commit -m "add submodule" + touch submodule/untracked +) + git init ignored-dir (cd ignored-dir mkdir dir diff --git a/gix-dir/tests/walk/mod.rs b/gix-dir/tests/walk/mod.rs index 823bf360370..34e8ec7d42f 100644 --- a/gix-dir/tests/walk/mod.rs +++ b/gix-dir/tests/walk/mod.rs @@ -2631,6 +2631,130 @@ fn root_may_not_go_through_dot_git() -> crate::Result { Ok(()) } +#[test] +fn root_at_submodule_repository_allows_walk() -> crate::Result { + let root = fixture("repo-with-submodule"); + let troot = root.join("submodule"); + let ((out, _root), entries) = try_collect_filtered_opts_collect_with_root( + &troot, + None, + Some(&troot), + |keep, ctx| { + walk( + &troot, + ctx, + walk::Options { + emit_tracked: true, + emit_untracked: Matching, + ..options() + }, + keep, + ) + }, + None::<&str>, + Options::git_dir("../.git/modules/submodule"), + )?; + + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 2, + returned_entries: entries.len(), + seen_entries: 3, + } + ); + + assert_eq!( + entries, + [entry("dir/file", Tracked, File), entry("untracked", Untracked, File)], + "this is a special case to allow walking submodules specifically, like a normal repository" + ); + Ok(()) +} + +#[test] +fn root_in_submodule_repository_allows_walk() -> crate::Result { + let root = fixture("repo-with-submodule"); + let troot = root.join("submodule"); + let ((out, _root), entries) = try_collect_filtered_opts_collect_with_root( + &troot, + None, + Some(&troot.join("dir")), + |keep, ctx| { + walk( + &troot, + ctx, + walk::Options { + emit_tracked: true, + emit_untracked: Matching, + ..options() + }, + keep, + ) + }, + None::<&str>, + Options::git_dir("../.git/modules/submodule"), + )?; + + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 1, + returned_entries: entries.len(), + seen_entries: 1, + } + ); + + assert_eq!( + entries, + [entry("dir/file", Tracked, File)], + "it's also working if the traversal root is inside the subdmodule" + ); + Ok(()) +} + +#[test] +fn root_in_submodule_from_superproject_repository_allows_walk() -> crate::Result { + let root = fixture("repo-with-submodule"); + let troot = root.join("submodule").join("dir"); + let ((out, _root), entries) = try_collect_filtered_opts_collect_with_root( + &root, + None, + Some(&troot), + |keep, ctx| { + walk( + &troot, + ctx, + walk::Options { + emit_tracked: true, + emit_untracked: Matching, + ..options() + }, + keep, + ) + }, + None::<&str>, + Default::default(), + )?; + + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 1, + returned_entries: entries.len(), + seen_entries: 1, + } + ); + + assert_eq!( + entries, + [entry("file", Untracked, File)], + "there is no index that has 'file' in it (it's 'dir/file'), hence it's untracked.\ + But the traversal is possible, even though it might not make the most sense." + ); + Ok(()) +} + #[test] fn root_enters_directory_with_dot_git_in_reconfigured_worktree_tracked() -> crate::Result { let root = fixture("nonstandard-worktree"); @@ -2797,7 +2921,8 @@ fn root_may_not_go_through_submodule() -> crate::Result { assert_eq!( entries, [entry("submodule", Tracked, Repository)], - "it refuses to start traversal in a submodule, thus it ends in the directory that is the submodule" + "it refuses to start traversal in a submodule, thus it ends in the directory that is the submodule, \ + if the root is another repository" ); Ok(()) } From a29fa00d0727baffcba10c8f2f09115a362a2baf Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Wed, 6 Mar 2024 09:13:16 +0100 Subject: [PATCH 08/26] feat: Add `Submodule::status()` method. That way it's possible to obtain submodule status information, with enough information to implement `git status`-like commands. --- gix-submodule/src/config.rs | 2 - gix/src/status/platform.rs | 5 +- gix/src/submodule/mod.rs | 172 ++++++++++++++++++ .../generated-archives/make_submodules.tar.xz | Bin 23128 -> 26856 bytes gix/tests/fixtures/make_submodules.sh | 30 +++ gix/tests/submodule/mod.rs | 160 +++++++++++++++- 6 files changed, 364 insertions(+), 5 deletions(-) diff --git a/gix-submodule/src/config.rs b/gix-submodule/src/config.rs index 2026966917c..12194f4595f 100644 --- a/gix-submodule/src/config.rs +++ b/gix-submodule/src/config.rs @@ -4,8 +4,6 @@ use bstr::{BStr, BString, ByteSlice}; #[derive(Default, Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum Ignore { /// Submodule changes won't be considered at all, which is the fastest option. - /// - /// Note that changes to the submodule hash in the superproject will still be observable. All, /// Ignore any changes to the submodule working tree, only show committed differences between the `HEAD` of the submodule /// compared to the recorded commit in the superproject. diff --git a/gix/src/status/platform.rs b/gix/src/status/platform.rs index 14d170551d1..7a8a1fc844e 100644 --- a/gix/src/status/platform.rs +++ b/gix/src/status/platform.rs @@ -5,8 +5,9 @@ impl<'repo, Progress> Platform<'repo, Progress> where Progress: gix_features::progress::Progress, { - /// Call `cb` on dirwalk options if these are set (which is the default). The directory walk is used to find - /// untracked files or ignored files. + /// Call `cb` on dirwalk options if these are set (which is the default when created through [`Repository::status()`](crate::Repository::status())). + /// The directory walk is used to find untracked files or ignored files. + /// /// `cb` will be able to run builder-methods on the passed dirwalk options. pub fn dirwalk_options(mut self, cb: impl FnOnce(crate::dirwalk::Options) -> crate::dirwalk::Options) -> Self { if let Some(opts) = self.index_worktree_options.dirwalk_options.take() { diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index b2ca0d0bef6..b9e6976a7f8 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -275,6 +275,178 @@ impl<'repo> Submodule<'repo> { } } +/// +#[cfg(all(feature = "status", feature = "parallel"))] +pub mod status { + use super::{head_id, index_id, open, Status}; + use crate::Submodule; + use gix_submodule::config; + + /// The error returned by [Submodule::status()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + State(#[from] config::path::Error), + #[error(transparent)] + HeadId(#[from] head_id::Error), + #[error(transparent)] + IndexId(#[from] index_id::Error), + #[error(transparent)] + OpenRepository(#[from] open::Error), + #[error(transparent)] + IgnoreConfiguration(#[from] config::Error), + #[error(transparent)] + StatusPlatform(#[from] crate::config::boolean::Error), + #[error(transparent)] + Status(#[from] crate::status::index_worktree::iter::Error), + } + + impl<'repo> Submodule<'repo> { + /// Return the status of the submodule. + /// + /// Use `ignore` to control the portion of the submodule status to ignore. It can be obtained from + /// submodule configuration using the [`ignore()`](Submodule::ignore()) method. + /// If `check_dirty` is `true`, the computation will stop once the first in a ladder operations + /// ordered from cheap to expensive shows that the submodule is dirty. + /// Thus, submodules that are clean will still impose the complete set of computation, as given. + #[doc(alias = "submodule_status", alias = "git2")] + pub fn status( + &self, + ignore: config::Ignore, + check_dirty: bool, + ) -> Result<crate::submodule::status::types::Status, Error> { + self.status_opts(ignore, check_dirty, &mut |s| s) + } + /// Return the status of the submodule, just like [`status`](Self::status), but allows to adjust options + /// for more control over how the status is performed. + /// + /// Use `&mut std::convert::identity` for `adjust_options` if no specific options are desired. + /// A reason to change them might be to enable sorting to enjoy deterministic order of changes. + /// + /// The status allows to easily determine if a submodule [has changes](Status::is_dirty). + /// + /// ### Incomplete Implementation Warning + /// + /// Currently, changes between the head and the index aren't computed. + // TODO: Run the full status, including tree->index once available. + #[doc(alias = "submodule_status", alias = "git2")] + pub fn status_opts( + &self, + ignore: config::Ignore, + check_dirty: bool, + adjust_options: &mut dyn for<'a> FnMut( + crate::status::Platform<'a, gix_features::progress::Discard>, + ) + -> crate::status::Platform<'a, gix_features::progress::Discard>, + ) -> Result<Status, Error> { + let mut state = self.state()?; + if ignore == config::Ignore::All { + return Ok(Status { + state, + ..Default::default() + }); + } + + let index_id = self.index_id()?; + if !state.repository_exists { + return Ok(Status { + state, + index_id, + ..Default::default() + }); + } + let sm_repo = match self.open()? { + None => { + state.repository_exists = false; + return Ok(Status { + state, + index_id, + ..Default::default() + }); + } + Some(repo) => repo, + }; + + let checked_out_head_id = sm_repo.head_id().ok().map(crate::Id::detach); + let mut status = Status { + state, + index_id, + checked_out_head_id, + ..Default::default() + }; + if ignore == config::Ignore::Dirty || check_dirty && status.is_dirty() == Some(true) { + return Ok(status); + } + + status.changes = Some( + adjust_options(sm_repo.status(gix_features::progress::Discard)?) + .index_worktree_options_mut(|opts| { + assert!(opts.dirwalk_options.is_some(), "BUG: it's supposed to be the default"); + if ignore == config::Ignore::Untracked { + opts.dirwalk_options = None; + } + }) + .into_index_worktree_iter(Vec::new())? + .filter_map(Result::ok) + .collect(), + ); + + Ok(status) + } + } + + impl Status { + /// Return `Some(true)` if the submodule status could be determined sufficiently and + /// if there are changes that would render this submodule dirty. + /// + /// Return `Some(false)` if the submodule status could be determined and it has no changes + /// at all. + /// + /// Return `None` if the repository clone or the worktree are missing entirely, which would leave + /// it to the caller to determine if that's considered dirty or not. + pub fn is_dirty(&self) -> Option<bool> { + if !self.state.worktree_checkout && !self.state.repository_exists { + return None; + } + let is_dirty = + self.checked_out_head_id != self.index_id || self.changes.as_ref().map_or(false, |c| !c.is_empty()); + Some(is_dirty) + } + } + + pub(super) mod types { + use crate::submodule::State; + + /// A simplified status of the Submodule. + /// + /// As opposed to the similar-sounding [`State`], it is more exhaustive and potentially expensive to compute, + /// particularly for submodules without changes. + /// + /// It's produced by [Submodule::status()](crate::Submodule::status()). + #[derive(Default, Clone, PartialEq, Debug)] + pub struct Status { + /// The cheapest part of the status that is always performed, to learn if the repository is cloned + /// and if there is a worktree checkout. + pub state: State, + /// The commit at which the submodule is supposed to be according to the super-project's index. + /// `None` means the computation wasn't performed, or the submodule didn't exist in the super-project's index anymore. + pub index_id: Option<gix_hash::ObjectId>, + /// The commit-id of the `HEAD` at which the submodule is currently checked out. + /// `None` if the computation wasn't performed as it was skipped early, or if no repository was available or + /// if the HEAD could not be obtained or wasn't born. + pub checked_out_head_id: Option<gix_hash::ObjectId>, + /// The set of changes obtained from running something akin to `git status` in the submodule working tree. + /// + /// `None` if the computation wasn't performed as the computation was skipped early, or if no working tree was + /// available or repository was available. + pub changes: Option<Vec<crate::status::index_worktree::iter::Item>>, + } + } +} +#[cfg(all(feature = "status", feature = "parallel"))] +pub use status::types::Status; + /// A summary of the state of all parts forming a submodule, which allows to answer various questions about it. /// /// Note that expensive questions about its presence in the `HEAD` or the `index` are left to the caller. diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index 18cca6675812070f27c089a5d8a567dd3e0989ba..13238821c5177ab2f7008a84d00b08c105e158a9 100644 GIT binary patch delta 26792 zcmV(hK={Adv;pYR0gxC4_znMPr?DLe0e|KrMO>iWHQm2x?QQGmGjcPMTqaa%?2L8k zA+KIHxB#V^@U9Fg#<I4YGI-$)t1fSLCvsnCU*c~U083GB=!@>|xKowO9zClbDdi|} zxW;4}b`vLr+p(*Xjk%Ib6{gG~be{`W`;Qu=Z|@}>&u@LtXXu6eW#_Vg``1wC-G4{U znceUUuTyKxLsh^Wm_hb7IJcjc!y98WS4{)NV7z*gv@oB<3YdyrxEd5cFvX-<tm-){ zs54OScUqT-L}23;pPaMq@>O^Bow$RHtmepkVKSP@zrb0bjpg5fe3w1`G4ilAT2G1s ziX>9q256vGXy)eCMvpnoJlLqACV!iJVTRFVPDrRvsLPK!)OSQSVp*M%=@HLK-Yu!N z?FI35Mz8$*Pv&%_!+Aqlhe}F}K9z1^<^2gYKXf{e{;I^LJXV>??8~~??-7Y<BDCAL zY*JW^$?_(`(Yw+a+5j2a#V6BOUhyih@-J9KWt}Nv5y@9jC{p!U5NfRn41WeSSr}lQ z`o6POX8WTaApQ<tzraO5ZCHuqeFC84Toln$X@VHhO=(o8?`>^A__gRqmAE}xB6>*C z{bunYIO!rR4H$Xw%|F<{OgDjcNxkf~Zq)CuW5?PT2&qxE5g+QD5T@%pkus_yCA<v3 zN3$oeh3^d@!f=&fjGg}t#eYwUOr1b+d77UJ;Ir@MSSPaQhWVxd_r4gOh6ohON9z4J zH_snJh=H`9aKv_M99MXHqNusHIS#OHZ$SJ^>$8KBr+^5-vM+@Hoc-cL#-r8{78xL+ zXirGF0p#|GxH0toksvE;BjS1g2R67y9FhY44vkXZXj+fYOFV3>DSsFS0P2?q!1<*u zFi1#ymN(K=XFgcgZ6DReJ|YfdCsNg>rW*U+*YWCZV#B?wX!W}sI|iJfb*qXtV~u2< zrn7oIl_eFL&2%h!!=gw31o`Gd!BvP6p*>B{20v#GTe_+RbLRZt|5esQs4mJq*=F;> zDBPYP!_u;1&CZZ&qkl+xW{3*J`A%gIe9#?-{<Y8~5CL}?XLhRgJjbKf!5AroxIk9D zR@15Vhj^naCC*139CPT_uMP}^`I{ht`Xl>Wk!4t)AH>Tp9lZT3wbJf;9_<5rDm#iw za-Xl*BF4~!#b|<wBUnw2)uv6TS~Dv52OgcU0vPZ<OFKl**MA5d)ORJx{hK~rSmIZ` z-a`6p`)OH@_M;Ba9xpYgY?$}cIz8EwsJ5!0q3S%9r;6@<`yMKYNd0u_LxnFTLcC&O zxSI7biQjETAZ4Dy=;xWW#i|jg<=J^K{oF5u1;3F+1K0QrkPNvieENkcB?;)G`c&wH zH^Eu5-5l^tB!BSz1#QnVV)Q@<$pbL%2@KXhGbbLp65{ueM`k=FXx`K!<~fVRB&|E) zAY`FbQ_T!83<H4G%$tMhn%d)ayTuD<bGQhYp$tFF(v<C-5`xBAEWmV?|4ZI9f#Im* zyJ&gNo)<9*k17_QxDoBDyld7`?CFObslrFK>bS0$aDNj3<8B#FF4EN+SRFN9WN5e9 z5>$$7!_p|AbC#Ke6w-xz3;*K0Q`>pp+)Y|H9wla@q!oRy%((Ep#z5Ce=p=G*S6_w& zaCTtS0!>CH5o@&Rn7oiy;5@Ye+~3+CVN`4Fv1xhp289Jzz-kE;53o1s)W)+5OkE$& zFH4>o0)PEXA;tCZR_3y4FV0yYQ|*LLog4&j^i5cd(6$Rz`X=%eg6w`J?*Avhwzedv z-@fnx*CTL)5I=9ac#XHD6W})@S`Ed_)-eEY#k93;A(PF!Cj;xd1!pn3I9}zv=!b`g zl@jNr4NcKpTmSg*R3AggVaJZE-IoF!e!3I*vwy|6ZdVA}MNofIZom!HY9E(TDGeCs zyg@ifz2h16h|rb@=zHCLH_8K-g&hO?{p`Rn?2l<lcj(R)V8*v{V^3ba;{e@VRQkhl zIMJ4?3Ka^05Oy%WvEEUj+jmXk<ffB`T(V)vZ6dUEWB~f<?b>N-F8xXspYE%n)aR_X zQGd?y*^11iHBlsGa_0zuzNJrnxLFfCn^BPw>Tt)$dT<FI1H+RhRO_7^<o}DPLWjsv z<)}4yd1`1ceQ5#*b>lm7L23cPG#S_;7=pBDkCP3(FnJMw-zXF7<d28i3M?(U7dkNH z3^>Md_pemAt>A>~c*Q>3JH_Ho+yXRY&VQ4Q&jtatSlWH=e#>ZEY%qoxPHfUA+~o0Y z1W%;Mn+icEAxr}>r1*kx<zSniLkS8Nup9s5jLZ=+TLlNx7)%C0jx(gD1@r>Q?6|N` zGKFAakO(%)P!1ax%?yvt@C2G1Mki!)zS3UqaA3n)ucUHuLQD{57(HP6>&<InF@KK1 znl+Q+PMdD|{`wftMpNauoJe{%(#`Y)k08#s;SFD+m3N~U6h6K+s^^W4w`X$U|2@z* zmX~x`mE)?Xa!tR6(^lT@9m`oBe^(y61sEdz<Y;<}2iZcvm|O-D{MKE@wx*+B4e4ei zUW$v9lU@nPxX0ECzE&j5KSctZKYvogHS9=h9u@vw#}JG)ezNWHp`Go?#oMUknR<5R zf(z~xu0PU(rIJRvSzB8YQi0cAKi$o3L(XH<`(v`9^c<bFmz_)j#uXmw*HIabkw|&3 zArxeE-`3xy0D&ylS<3GplsaqT!W-sGc0Y>L{ep$^Ez1F2@d_}o%-y@5;eTRUsT&$} z6qm%h8GSfr8Q6&=(WtH`_?X7EwW^?q0kMWYsMun{hDqPkoFfNn1wn52#qyf8($GJ) zu<TdIfPd^UUbsA3iDt|#d)Dz3(P!X9tEm4bg?xj}Gdk#n<Z`5(scryZXU1J0EMEOg z&eOx8my}Go>;(`@>&&qBynkpkY@e9mWjIvwCA7K?{|m8Ef(2eJGEobEUuvsV3ps~u zT2Oh<nUHR2<|qVr%oVwvGDsWtoW;`W{><fMhaoRUB$uMdbnW>elr3$!5Suzctt^!% zy)etTEIwWO-x*)RHm~&8<P5I9`$FSVv`tW9WRjnb=chjwD8tD|-+uyCQZ7X&sHPhh z<9&9Ln?*HHtW(n&%mpR;3jPa#f27%bbo|YT1zyrvm$R?YB~8pukP*C}{`>`IrDu>z zww<o~H{bB$Ux!v0*R7uJC;7J(<kFCQbX3Ei0IZ2AfE~Z+)M~vUP=O`Q3;LTU4G9)C z_T`}w6SbO}74BQIz<=cmbm2g8q~7zZ`0O`oiso?%0bX=~ndP$F_>umVJt9WfclDed zmd@X~qYF(flG8PVll;f?sQb%bi$JUnmBrP+XD2$Qyn*aKUh>Kvy|Xn6PJVbjy{buo zP_E5IzMFGhioX~J&Pz<J!K_rqkrrR#+-LC8eFHYj-Ij~y(0@o#TVnw<=ThJk=E*l_ z6O4{1Y*N<Z6??{t1#jKJdkeUQ+NW#9EfY>SpRlKdoyC+8+ctQ(ezOBa-kI!spm~pQ zA-W_NxR~OUt%A!<TO2C_KA~76J?c3nf9TUhm7%mARA)j0zUl^%USqn=5WaP19*P+) zPopJi>BmPK4S&qWC@<T4NH5NI#dW7RdLD6<OCMc)xqBWKEfzsn+=;rs6wT1DnNR9{ zfwsjN0Wv8bHChLk%^kJ#m^-q2VZDD)9>O`!aLl9g$t=rEz7BnPD#cb5!?$&NParN- zTtV$O6KUU|iT3_$U}zE`d~Ddq`_43}PGMfTeiB?vSAT(;RRS0D!eQm(9)_ml3c&nQ zDsVx_jf0JWX;c(MOB!3C8<x!8dFDAw_jwQb%kJ-ha^hAmVahmDV7=1Al75=5T4ARB z^OE5ytR~IZ)G~4aIh{KnH6n{(xdf8KL4T4dbCnn$>EvRBMh;r@C_<#%l;f3eGNp{{ zYsl>>AAeV6l?Hq)+T!L60W|=k!T6U(Irk!>7vNd9fgV^nHMFBgEMJ<Iix%ko+TY1Q z4^C&eY6n&miqT86R8j|fe@2p0-^9_xox@Y#TZu*!q!#LQ8{Ua2JCE(ZBX-(*ZB#x! z4Y5Bb8$~Hm(*Trq!>dSxWRXieMK#z1>@q7l5`QboDU+312%vi{6B`DBk8I`S&9xb~ zMN9z1o{f2J6c_DM#rv3$szR0xetyhPR=9%2$T}?mQU^V1gQ9@eTBU+%9H=KF+F=QZ zX~C1$DF#J=J5De`Ramuo$!fra`SQ=pcYf@3jD6(8B3r{-1pWQ$o<ES@^VlVTvo^}_ z4}S&SzaR<**LReMoDG*(@2H9cUxU(v8|4Zpz3FZA*Ui5lDR8y}NRrKhh(~)ggJCLD zoROrvAJHfK`X}tDoXRwyG1*Udkc7&!^*SdX@<p1g>()WA*sQ>_S>Z)`u0&)&x6g)O zx3J2?JA~>5h)?oNXPtKMHh9b7NSz__oqq~esBP3lb)99Zc@-3wvgEtoMtZy~<+FD# zYitv4x-Id|>9rJBYGPhN`VX50mnUX~%s@g6$Fi5nB(Vic25mQXWHxJ}26OuXkyUy9 z63qqTYeDUPJ1AjmSlrOT#LMfdRu)5VKJ;JD#H$P9JH9-Qvt;!wndS~wdzW|1wSPDb zXQ6EaD$i{=Vb6A**#K4CyR4IRtvJw4E`~|(%T8xOWmDiBb;oW6_HMGT<EWpjJ@!;0 z%q@a$giqEm`?gvQ{_me~e;7_XHwr10VTygUjzB#%Wcq=e<nZNsM;FmAu0z@xK47zQ z7;WQAJ?lYL;l_FTArAk24jX^70)K2&j+cC^`#P+q?9co4;($VDVoY5nT!o0XE5|$w zaj*;Gb9hX(DLk?`Tu!VBUQa$%%Yk%9Dc5m*AKE{kXE|V|g#CJ7GIU5tU81A1;_n8p z?YsX7_%1BU!=>z;`Ui}z#ZL%Z#1#Y&nzV^<!kN>vwBJ!GDi4vsf=*~Tcz<+dxNS{s z#Dn(T5DPzE#pQAARstDZ;y4%){Uvcl&5*r>SD!{#?m{51of^ahTnh7BqgjAwr|L{W zbf}j=uR&Dh+9SX1^Hf_)NU_Q{vA%^jm-0Dt2v5NjFs%D-vspk2B|Iym(xTPz^AYpm z5^$P6ZO|t55lLK(n>iPg%76Z^b6?xM!!rScm4L+a_~>k1?j7_N+=iXd;@v1P#tQRW zd3^?0&lLnddNs~f@Dl@$Q6zjiC+51$?9qNPn1bzgaD^^UW8NCJn5J4j=N2SBL#CIl zd_F$MpT7J{R=gvS)Hps+s7)eGt~a*0kWmt{9POavPQ|!}o(7En`hSP#LfjTk*8K%% z+LBvxN72r43X(&;llx(roTwvApv4HoV_JPfM6j83D-a3fr>$f*n%rF)XTrTNk3#&L z2_E<TkwxY|$#us3$Q};E%s*X))Nhw!A?n7@5q7Xg>>xLan)YgqWE4~Dj^hf)&ndE? zTFfd9oAw0x%$^yfl7CG!i-3PpV^PuW9x&)}tW<l&MGLo>P|C4yo?h&v)1mD00#!li z7$IfMjYz;M>t!<CQUOsLTL~0APA%>bzj%&QVj$cd7p3c$>s-}G)rJu3;LTZk2*eQ& z*)^jw;%=w=RHX2qhRC&lNEcJAR{SfJEnLLdq8WK!JRn==t$(4kSWD#V!f(<NAN$W} z?;SlA2uaN-4qtlDA{3W;>rW#Gfkxc4V|@{_P(VW)1H>K()$e36^B>wJORuMc>4dzU zTb5IrQgj6gj8$tZRWtGoqe)e;=LI*JaX{VS--ZdU)2NTzuTL)8k_KGztV0NRR|8T0 ztZD{fZ8=uN27lV!fqmvJg(CSq=PzxJ5?@a-6txB^5S>J`Jg5Xd$~Y|wx^kac3@c~v z8CKVtY3ETcX>vv{kywKu#oh39_vhZ~zIlziPkYO`$Kc4LYOnVfv{CZ{BJrs;U`b9K zRug+EI2_soFuY|SU^;o2_Nz)pZmE;;&@y9ybPBPR*ne{f*RW~fX__i^K`=L`e!JU} zx|PH<D^sn(I4LH<p>51-cq>LoCe;H=BXh6m2Y*<P-v|^O^Oi>jPy{_xed@aaaGGa> zWXW|bSEItxiyq9BG>P~oVWgYMZh0-@QKIT59oKr>*w?nbYC+lQNIu%_9<ll&OkLKw zYwPr4E`PQ5`C4^hE-xk%1=1@`jTL*0z+dFd|LCMgBwS1}xt3&!-V3_X_NAVmS{dEO zaxYT;sQF+2X5;x7u#Ts-oWT*30r?4RLH2rOyjjt}CsF-)CmT^Qu@`b+7a<f6PBE08 zPYMyqlOxi^V3h0kSdg_9ZeT>;>b+p8K;;h@A%Axyo+CFE=yg`J-Yy6VbqZ3eQyfPt z-kKprFj*Bx-2S}q(E*X(6a}-OT$#sGDliJePQh6SF}ZAh1PeArEmj;*S_3CrH>Fu% zYc*!~dg+vu>D}S>DiI&+zGG<Pa2K?u9VCZRqDIX*)0N_&Jx&VNIS#=d6iy9Rw7+z; z@qaX4pCXG@|KAqjW&`ZcAUXg49*&@xDj-r8J_L&aX>c^M<!PiU5enMchsUZ{40u_@ zt@XfuKM?jTM=3G=z)e3J#I*2QCCPD_CXqFct)X{w=p5u#IfYNco`Q<$O=Fx#S@f%X z-dkEd5o$O40DhL}2N7t}b3_%-fFYAgOn>F(=@O}^U0>ewKDRfqMNB<DezD~ZX{uKi zecX_T^%ph&TL7=-%>y{-uMP5~s@`ZUG>SFOw8>TKNP;AV8#iBx?5{#Jk*&>Tr)A0w zg&6SiwlXHHSiBUpQuKK%t**8MdS-iJE7Y62xxfXbADzHSpS$|YOU2#q)gim!kbkx_ zInX^NKFfb}8Ai9Jw*vqn&M~$9iBzDe%bis=D3@!k#(Fqs<V8_r6`qyNtaH%`z4k;f z@U)QyZKOVnWe>~JM+2FuBajbwv#6!%JPOzGo&+?gnxNQN$aQtoP)Kg+f6TFNrYc@Z zDU)o{OS3Bn3ymHz6;+ge4-2hRbARM!`IK;~J>Sj2&Y!e5md0DP$M^=H$a()skwZEy zHbV^LLM_^zo4R3$gZj3~07$TvAy?E^u0k3weTSn$*La33jCn<jc#dzxj+n+2E8~V- z&W8;#B-M4Sb`TdCK_$OK?P;yyJPS2B2ia9nAxl<43QW|44V6%u49P779)F6WBY`n2 zWxB2`lGjz-66t%}TDPs2&4Gx{cXwhOFKcZzky^D;v`6*ZuK<UGj%PD7%N;J7iWCv* z>*#isRlVQ!AowZWu|Ev>X$I6ujsnBc>6b5fW9$e!j0m-wU#I%Px_^Ft9jhqvJ_bi# zP=nmG7^6M85>?0M9A7fhFMpUMlvza`jwp_Bk7po2LT(y9Nt?>*LrQZNOCLse&d&}& zc!K^LHFL}s^TjY3u%SSR${ZkUH07=p5nnafy3Sdus5LMOAbB{M5(uy50$ljspKw{< zp!NH#>w>gJ*akVtjI6;f0RNSo-h=4=nw5<=wkK(lYCJ?<HHl^Unt#VpxsA1OsB(rG zf5-uniKKEE8Y1o4zXo91KxVCV=Y2b6FvQ8e2Y3E&8%DCo2tC^_r<BKFtx_#jm4FGz zIV(~qJg*`Q2gFR-g&|ue=##+Uw-CkV321_Z+QHr>=YcZ`&^Wyk+8q>xLt387YQoxa zh6*xA1F<yfihc_>WPf}to%?3!+lBTe*Ao+Y9TBOX#kMklu3Dm`rS_eY!(FPXH9lB9 zOZzg!ue>Q4b<4@spXt9uiaY<6#T3Yinp`2@h&pa$8LR2{^To5fr2T1#Kce9G;Kz#P z>|d+zj2c0&s7tX36`$!aB(#WF2(g)*8T+-SOn74`Qo7z-6o30!HMJOTtr-ye0JZu& z%|!w`GRwl{?GP*BuRr3zoK{F(V+IEL(_;0XLn_~1=|Q4@LNsF^IEUwsf5RZpl<Uw8 zd$ysRfjiq#RfS5VrdN|;?0L(eVE$xRQ!$D!Fj07LQo!_r3+>5pbCbo_JS13S_ga|> zg4SGVB;46Px_@_``av^+C%+qCgJ@gG!TBM`ory(%W26<^lY-dLpS;8{-a8#TE3+=G zuB;$}TIj~#=wzKx40C9HTFKmRggF#4Vj|<ST3sOxNU4AnO~ok9K5hXVf^7dH#)s{~ zbreX#O`Y1uychG`<1<EwffGuhboh%#phidO$$Ej-`hPih>Uqr0o#x9`cmJ~Nz-uIb zrUi~CA*P?|zL=rjl_>X1F`%nU;A?MTaJRe7hhT0;b7aLZSuJa3MvdeLI>K-)<_`O? z2O$ax=dynI#X>f-{a-Li6KpMj%nqMy?31-<kIIcH0mtG*8hF5)efa5;c$1J~sp5Q8 za!7fa+<(Cn6jq5#-2lX@oXc8VyYo`e>4gEXTH>1<jTLzl5PS_#my$QMlGo{VZ4?7A zJ5;{rSh$}=?_+i|0_O0x21r*~1bSnbwQ7vGEh%Yxd3cQtN5#OLZw0W#AVd5krjQ|% zbK}r-TSuW_tFns6j5Sb!1qlD%Gmw)k>Yk;|Z+~8jN56=JG4l!OQyCtQ#*<PNqz9X$ z*n5%`1?dP_Zj2|raYe2`>z1E2LQTjKf*f%lHgJJr46(P!ZOSBg&I<mL?j)rSYgqv% z%O#%2<$7-*;I&$)*PfR3S+y6m%#4wm4rxI+OBOjg)Ql<`Pi`mZi@XW=)2JiEo$d+x z2!G`9f~5x#_++WmlF?shT;lbwRRtTL7~3N6;flc58MkN5Q-u1Zcz^OerSj1ZM!2{v zb4t_1@Fkw9PbpELOFaGIJNB8G<CbM)fIh8u+{jek{5{c3KpCHF_y4_W4FFC}nNM5t zHLMBAjWy}ClVAxc#<RInRp9QDi<Jp!nSb%Na1{?@9%_CDsY(7<g(QG6kSj5->PVPv zI;+%Kl*U{ll&c69t4`yr$IiMLlH+`*jEY!eh3-vq-0RI?>q{rb197!lgbE{SyKGt+ zbdSyY`^fi{X`Bdb!gaUQ2I`eqp#vHq3|#Y$4uzGSGcjX)S=>&)_}}`O>JM8OLw}CD zCc7kOvT^18z2vjk?#@NwzC%GoxK-gs7mVG?wLocUqsIcIZke2%4MDpMNVQd-EIT(E zl3s?z^wb8)SFbV}C&Vk-)LG;#?k?^MCxfJphSG+wufNa|`vvuGwHvFNH__+EceuH( zPXdup5%HEk-NoXStv7q-HJB?yCVwH_=Zs|X6-y`Uqoio$G3K|{|1ruhZBrx_n2)8Y zq8?nSLMRiK=Lm7GKC97XB-G(Uo`K4%{`$)02+fm9XFg0hx?erv4@WxD>mg$}9i&#O zO{5CH)iwMzGVYy!&=1lbc^k#vexc-dRgWSgSosIT%(cdz560p@9^#eAUw`Un<^qeX z@ZQX4#&D`jp*)QB$TC|sgt)ppsC6mhP2Pr|4qx-^aD8@I(pACsqffKe*kPlr0uUuu zR71q<bn(K1IhBEA>9I$p?{vN_9l|OeeOI57C5Z%&!nLg^k%VdO?wW=6xik9-E^B6B zUE<D5oEIP<%-9c1<b%XPXMa>%uEs@{sQ3AieiL&fR1T247i75!<LDyr#9jMEw!%B? zD~2!qm=X0Q?b}kks9T+IBXhaCdo{p&Nh3T?B}er*t?xb|#dT|nc%mqi;K=HvNZzob zP`}R_oR_BN_M?`7&gld?j#x4#B`j7J5&_L8eJL@r)UyX8@&wZ2wttY=_0=D#(oMG> zTS-FABL>eIAFVad%_p)M!A%&({39K!4ils{-2c+tSWaI$@6|w7(^>_KzF24d@e7)B zq1hFU(EzOiD4494<M9bssE)l2?}}c6Ks@~|XF?J}r=}r-OSN#?u*=(shjjOAx^oO5 zomlo-@LuG{&#$t3hJUvSc@JDUn-qd&+@fc(|HnRBV*lkl027gP5Sn?0!ty|Z$?ElN zO1Sm)VR&s6{4`0@$C76~;<kc|mS5_5BlvJxrGckrz|ygc$AQ7H&P4iwo{U8gEBKLy zNi-6B(kR-)6(WGh2zz2gad~&FK9wY|N|sPNpOMvA`=XQ@(|>@V0*^4GQ9BrI?{M>c zyJ~VokW_(N<`nY5(q8<+9e(u>N*(PN=|^NZd5>YHbHx$dBWA<=(ZgTNk4$a4$C#~9 zO5F@fVB=VQfYwfH=ap2Ah+)JH?^XU9h?s-@E=)MW2<J8@8!k&Df8vaQaJS;gZz~O! zO=b-421Ztj34ibJ+@mA$)xJ;Nhs#;ijMIvkz#0p{#76gS$m24nhvirzPaV^(c%lvb zH63$!ZZX?-XG2sUBFqr}^3>y_J(kLdVHxH7CRQXa`P4lnW@3g<K0AXL#f>VSsdS83 zAtAeEzE%x3aE&T0+C7R`62o5gSHCR@v>~;mHh@YtIe$**%e9$J*9u<f1kO<Mzel@N z;mAE7BwvEXA!~Ib^|qlkoez7UO{LI-d%TMhRJk>}<<6LvdeMWd+uU{=qaITL0KqW9 zP+dvFi@Y%V8?%r(FK%+2O32hpY}~*cW$TyOn94A2ZxgATZFU)Lco_l$VFCWd*>+1J z-R>8Vn}1y9icE(h@1wy3K}JZLz_QFYXClgdGfY4)Fp=Z_CfsPY?EXgu<)c0afOok` zUg}i66O-9`UwW=Qh3-qq%!L7Tfl+#<g28>U)Y~a{m+%(xWH`uzN@0L4sl5S7`>rE9 zQh~^0Y`FPe<Nv#JgY;xZylMKZ`5w&qFIpfnAb;1-Z<*8jVp<*8>Xw_L)!(zD*HJVF zHW_(3%Vb-V4vUwh3Rv0lS3PM?-luQwM~F!g&D%H3PTa-(1t~?rNa?dmt}Eh-g*?m( zyoPVe$~z#-8hqW#k11G@kjoA)Q%2b8F2}D7W9|r=q-cJY|5ZfGYlnXYx_F`ElBFAX zJ%3vv*b!W-X6}lLridQ-85kor8tV>O1z1PJDz>CwWOCqaUj7dbx?d(79Y*kzQ~^@> zqssC?k&leIeJjh@QuBR0R)JdZ%id$IRWGlxIJzjbb60q|k1~P%1q+@)p(VcFhY$l8 zO}8yWol|r`)wO`MtvCn1pb5Bapyp02?SHXB`4f1$whO2WaiD1*3aT+d!*!@@QI>05 z&?>i$j#t@eopZtuPJKXWvza1Df+hlWL5Q_qbmp(O#EZs4&{dUd(ttfcyzN7Musf}% z_!F?6{nKwlw?5}>#zC561ywKfKm+2g?M9{y#ZEdqVa%fey6Jkm)0vdj+EE>wseh@9 zevDTuhn{pg*N}NkXJU-^bId@{YB92|e`{<Z8#ynAv{Bj9T{#A&yW&FvO;?ReQR-LG zn)er{>8T!VsW*s~UDep097B`END^;eKZj0O$@5iyy19~i4^HmS$POBuBb{Z>v%%j( z@&}|$f4b#ILdRn@y*%CRsJ6)pw0~Y*rG-fplfwm4Qkb{u2_}wirRly_9b`<q>a`o# zpXV~$7u{TUqDgP3)0Jb~31WQ2^eqoemAP!TH*V!h;)_S7?+v+BGt2qoTc&UO8!CuJ z>&mLPOWqC@XMS&ADSaAhO@TeD9TJ!YynnsIQ<RDPpfsADFKCFb^!X0aEPtjwO1A2+ zbgH<(DVJd>@Aa>jGkRneRkVIGv1~-W?;Pz@(*FS=SVN#v%z6x83_${^VJJ<|CIFBr z1-UGnTK$hAuzN;>`2ny|i_oi~h-;qfkH+0M8kK8Ar>*Uo_cjzZ9w1zB!SSgjDJ3)4 zzI8jm2SjB+vIVuS^H^Dg4u9$t!`eP-gfD4E+=k<OKLG?}2j{$7vSC3G2zx=UJ-=0+ zw?GL!cmvS@ffc9OA#LN92+hW|Y|f|-{`9|C1-4z-Pk5J$0N#X<r2~y05a?JP{}gj` zxrCm}PL(%E#d9B2fn7s+pl|-P=UY^VvZl4-r9lmUPvI{o9SqTJxqm?06z2_xnOC^E zzYpz#QfT_ZhfPKSO&L<#8>sunxfv2-;gz%L&}N%wJ#yK7oZugkJ-E;OGH!=0+3$jw z`-bcD=i}9}4G8Nu{)!;r{z}B9(n-8ftb1(j&wAvPTu<0%4Lvc1yeJD0INXZ6Fg#4f zl?QFkEzX^?BV0J*ynj<ifhi-@HZ4GE1iaJ_pq!>zOI%fo<-Gp#L;Itx)4W3m<D<m1 z2(`eZD{)3Pace=}a1YpgGaO3tc<8|@V0r}J=XKk9^^<&TY<;T7LU`}qG|Kw0;tpYM z*z%q}E~i*3c7In5v6cU_n$2<3_IuCw*23tpma6~WmTwnMr+?xiT?Il~ntz>D)%VB| zMZQCF5pwy=u`xid%%*(svbj$35Ss?7FoYb#LQ|KUy>`@_2sN~sm2noLxs3}&w}2ia zmke_;!jGSK6F-&rli1ePcD~?=XqMevhvRze8hB)p@W%PC0V&Kf@x|s;GYA~o9@SW& zFsgF12PCh>zJGFJv@w)tSrLBlYSW5?OBQ-2K2r95;2D=HD>9WOR|UovNXc{|pbZRl zX(b%T|8TF@dAb9<#M*OMgKaM|dp$ISi>MS@a#+b7(GM@sXRqRW1jMpbf_1T^0pxh& z`3Yl1MpEO}u@3(soyV39C$%b@S*{Bt%Afq<0XW?4lz)KRv>*xNLRE&am$Fx?)^7cW zo`u8fKth_I{Vn4u#KpDM@(dsGHLiml;iS9h@Tc@(VZk58^;cH9E+nksYF#EBA?dx# zS!_Y0uc2PIikFfuD^$IyRfUE|x{CU!VcQgN`+zV42^HY#1m(v|c$`uV_noG%cj9%^ zbr<&duz%i@1LuzPKXK`jPHX)CTY?E+x-B;iN7$UbIO~bYvF3h0-<NxuGGA<L%4a5y zajRR^Bb%g4vZE$p5Vh(O%$7B13O6OvMZo2K5bRl4jC$jJx-hEgOGK-lLIvL8x!vyc z7Xc+jseP&W-WA~E$K`^OG+skH#la}8*W<Lzuz!-299IcEXdhm@b(Dsk*)B*6kDY5I zJ&D<#0jH{Vo~u;^Fvh(|&|m3Zx4rASn7sCR7!?@G8a)cd6jl#k>hTj=EMlm3-F_6q zXwSEeZ$oGW9N$NVWNJgYEb#-xM+7-Q-*G5&xUT!Zg*Chh@3xN`Q$6hN1C%P>Wahc| zgMZ(6R{h~ZF#(qz(b)ICvi$1U6<o_maWn8U#g|E?KA%t2$u;b<$QL#qWPuY15*xNY z5ieDm5C>2|U1<bx@~%jJaLc}l9Br2!Tnso{umCb3t0zBr3`bvI8zVHzYh`LQuo-ZM zuU6{hhFPU<rt8GZx1-||cfxP=<WA9VJAdwXjiXiWio`d$LQ5A|XAt@?=_vhv$^AXf zT0w~uAFg@(tO+~Ncr~iK`%Ps$=9B_R<NkWMTl7!X&t?`FWcQF`6FG_R<nL8y#KV!b zNAFz#<B=4P^{nB2=1pxxxiffFC|LbK#ey-XOWRE)Ig8eTI(dmy4bo}y82p+$sDG(& zJ}AYS_6+UZK8#MFl5ZP_zlIAelyC&uL~g$Rz1gQ?7`SYmR`*v{cT0(8%!4*SVS8UC zd!^ZYN<AXYP+4TtlJbL>yE_PL_HP(82>S6wm?XX#FOQ83e^fgS1)xm#FaAqzVP@1u zY<zX2snNIhi|B-8P^Pzhss+B|Nq=}!k{-#DV(Yw<A6c9mExbnY*EiJ0o~ZJ#?mvsP zftCR8x9A<WgLUGZX7#U!1J`HbmzwFx{JpX>XYRtWSF0#OuZ1fGeu;bm^X;2LnS_gb zlzqt5Amu0abWGu2?wp@+lrZVS!#7B7%`4)|aF#{e8sk!dJt}h;xIGL{B!9TSunmJL zu{^`&`fgK&?iLGv4_B??_{Dx;V<hWK92kZQx8$5?k{Q)WlqZ^&`eVh#$q$#snq>8f z9zvSrxu?einZrsbG>INv;cLF9@8G0LLPxfe*G&<!5D@kr=cuf@03^DN465xu+r!x* zwpQMW0BbL^ExY(D0|2ZI^M5&P#!sAMfZ;+=!KCk_Nt*N|^51*F(G<Wk$+TCR70Go+ zZWyG6<7<>QK3Jh#0Mum)h!9E$EmP>FjhjQos<t$;IwAi&!}EVko1^bkUE$j53cKLf zKE4{zU)q;H{%^Fq?)g^YXR`=Ug9z&y4|KufR{~+bqi;PiMexx!j(^da%!i076AMZ( z#kb7{ewUEPPjf&p>esfkvoiLiyG(K*p+^rfwW@X)Wv7v7jsz-~c|pfQ7QG*|Dt=Cx zk571;6i2e7x(P&KRv{K|L}EgLxAi$e-!r-*-lI@lLu{R(r5Fv@=nhh_fHG1nr<Y~f z)#mmud=wwu7PP(j6@N2pX8ozlVm$ilQRMUlh!|A~$I->gP=>W07t=1iV<d0j)|#W< zMB%!TAbDwe+9m#V88~DvibyN3v5`@1!CQp!r(bz4p}WG09MStt%mq2i;NJOH)G)qe zoW?Xhf)Yg8?iC4TtNhd?e>}W3MSL=&OUz8!Y0DlDO38r$(|_#|O>#Vu=&c15nEvu( z*<+R{X+2*zBf?eMAs4Z8)TY9jwO*Z9j2Yq6N48lP{`2(5mB7BWYp8=>Xm|dr(iHZg z4l@EC;P=XBAbbRcK$2O6IX-?>)10B@UQ;ue2vGq3gVInsGw{L$An0)N)c3_fe}`Il zWrR_;vtQbA+kdf6|8hm8y-d|DaT&;Rn$11FrB@w_y~nW=uvL~)c7*$v=-NxACX`!; zj`QMwRQ>WhSP(LH%k*rM0B@5f`YThcdIJ2e33c{a%}9L<w;7-Yw??(WOmDQ@Zq28V zY>h~PkEHm1E6cgT-wxE5SbY+#UY&S<j0NwS7OLV`Eq}%`y||fj!k~Dv*W5_u5-ong z-yk^`!~?0vn0j2=5;{brFl|BJtiAxrt)Jwvx#lGfLlrlCSNmcdI+EPJcG7Jt*yRs! z*~xTFbWa>Y1m`sIM_M08oPVvo=M9IHXfvgo<1A9E$Swi!HERZbH6X0hig;f6v(?P= zV4>)Qmw%(!O1oOsKUh~cyY=~xIH%!_crRqB7yLXO39MV4X5@8{n@2sp`?-2A_Keqe z=+j7P^#rnd<5Cmi#+$t?+|!lU+19M5<PmP)@o|(-!!hU+W@VsiEz-^;3dGH_w%Et2 zntJ3}_p2=xWP|AQe;^iwOeB+gMbW&>T2!K0T7L&+yL&muV$mkx>Zx&kpOQ4cOzG<G zip>^g8DEV2Du0P39cbR=$%NQFYD;k{W6r_V-IFtwhu<|y(KP>DA=Sn|l_Fr`mr-#U z8xv4JKKFGoDWT($G*KoP%bH&9m`B}c6GJFKBBk%Jc|(x+Hu{LbyX?ZwnPl=Q?6(5x zs()27ad7f%)J;)|R5K9rMj%c!V2A=o+H;qq88aPmSA1_8?B`x<OkVg3lzwpsbYj7j ze{3M#h_-3i?lUuO;N;%|L;njZ$P;X*W5##sK*!v;TBvtguZtR`_oFLQC;*Qm6t#bY zv+SFkjo|WHrFiqz3F<{2F)+Dbx=}vgjep(C;}r7?A%c{K_4d%z9dw}Ha+(rd2g~7S z<7o+~4LG68E!Pt9|G7?#{=m2*!!^W%3nxZU{!f3+D&2Yk(u!!DI78Q-k<pFj@%W{R zTEFbTAq_(E((kickUL#8wllx{#Y!Bagpmt^oA@#H!!0W|G#Equ-K0JH?5j8^YJVy7 zN=T#Sf?{II)HtQe=4dacZl~Rtk}5odO4P}Y0nbzB#Ql=`IYsJ2tHFtL%lfWWaw3Cd zc-s}%w1Y>HX-B<IbB81&eUdkAYN9P)D7ys0biFN%(I6WqUXma48&zW_;~;5yOr=Fw zy30;5$Fv!3uUqVuh9pcL5<41imw!$DX8sGLVE}`3Yw1m-D45}?FO%GZ@wHzK0a0fN zf<*fK;}I&KIugV^fW}Zj!e-f%c|<y%$Mc&)#53E`6a}2z1vG^Pc_7a;sF&6H!IBr8 z!FbCI+j+e8y7>dKiQU|vz*aske|B(HxvuHxi@3*ce8{<Qp@NctXf1v#Re$|nTifZ8 zQw{djjH<1t(4QcIVzL}5YT$vKM8NCqHojQOxuD=>?~3a_xJ7zn^ML3$iWs)Cn)DG- zC0fJJ>*5D&^^l@*&A?DBu!pr!s}eNEczmq)`QkYZx~s&+Q?Zd=tzpHTA;Q#gT4+*h zubiv!+`w?#pMUC@Bhh)i{C`Vjrs!BSPwbC>U2~b_s}|wNCj|?L6Fq#<wXRBCKk>f8 zDyKxgS6jhC3p-)1l)P;|kd;XyL&n(EL?aEYY~qL%>qYCOJO@j6n~|{Q29ySgxf>g3 zvcdv;?1l?wTiys)v9a9Vpe|j?Pf6{!eWe`hZ|fzcYe~n*Nf{ElL4W#|>nZkId=VAP z+$;VFHr&-@thmRR8ln*iu+DN}{03LmL}@`5Uos<;3cW|3Dm;kD-D2JJ12Dq$9{TKr z$yrj%qEb=Se$eF^HH}1~MWw=1+VuiLVS2)LvPvnR51&M;jKPG{jqa-G*m(zn%u_|3 zpV$6TjOvKjzZGLV<bRb@|8zW(V$5qf4$->c^&EU>=W9auZ6n5dB=6dmv9)Np8W&JU z5|RpZmNha>O>MZ@=!eSz$ot}Tm<xSf%TaPfT@OKka*-1(Y`>{#ihKB3BA05<svi26 z4bCg0d1yREZT4=dbu~&sg#Q7HtnYVEyu3sSlL)nClvP))-+#WHoHKjg3=QAU1X9yH z;!47n;Eecmr6I37a)-}51yxX=(Lq+LpFEzmnh(MYa`vZ+u&kDcaMMn?W%S7-G|4as zrzNZ!lQ^j=CZf&_BD+aj^7d#S8IfEX>bfNU5tGHByXS!zM;X7H3wd5xGjCL<g_z)K z&UVa2OjNj+Qhy1)5Vnz8bB(wZ*dN#VRNzG#>g`f+OOBY(zH+}UP1qj@Fu#uUJ37ct zMH71~VyJ<g^Jk%d=DiEt#)IWiS}E#Rc+~4QIUWClb9-r}1^1~sgL|1=jwYZtCebeo zSz}QQ_90bRr=Xm%SLh?Ue2jfMR{sa1jt!$G;_^7(pnr^(B|Ja`suS$R?cnIgnvc<J zzn}2fB9Lk&+ONRno1C52A}p9$Au8KyaI!^0$&}AdtC;ZL#KLV89lVM2L|#tu#oW1W ze1G1UDL27_I4@utGdlccO|ssL4@UKt8<HN5@_k{X(`My0XAu<)o2nbu=Dmrdu$TN9 zD@u~-@_&|zpX9GD=IO&uwu$fUtT#%`#ar;Z**jc-{|H|))-EUz9B3aWN@p#e6@i+Z zGx#z;_|mja#-kR&B9w%)U~zM`>B~@oqLGDiSl?*;aB<ij8CMMJgB4QVr8~rHrJ-M& zuhr3p8h6HwEO{nh)K6@J)3qKIcUfW-DON=U^?!>+ZFNWWu|!Fu#Go4PD+O8Sr;>E9 zL>uB6e#B%==4XDM3?i5u;2rOolR7&s_8bScm8U>R5u?NzXNRNW&4;?Lv;A!SsF6Ld zl}p}gaA}~jT+6kGad5*;K}LpOCZ2JE+5W-!sqEb0c+YfValosQiCjr+KNy<@!?As7 z<bUj{A5&>cX@qzzNQWx0iw*@STJ}zmbNFHw2nMwJ+-7(TW-j00y{JX!MS}ReJ?c>T z4YLm=3%zNzR*-P*tdRmJOFPW9u3t!MfWKn7J;Xw`@#GUW7+;a0RYb0@c{)gkxU=*9 zfI&E@1cU-KC3LH@W9Ai+6C%=Fl0Ke+U4OZIS?$I?XgO~VM(Kk?q1vAo_(SmUYnQS6 zLiD}|hW^cl_+2{?fR1mFuy1)JCWW28+CF(PR|c*{fB;Xy>oQ7-S*aHMRS+=wn@wJw z4+<3GB{v_HVX0CNX8f&&Icva_<rKS3Q~xpqi!nc!+y!}LQy$wdv3SEn2A6x!Wq%q8 zYv6G@<uJ=_I3%O{#$XOZHMEmw&aJV-PVrqO)@?S|@vq3v>YR5mBu6fCw(FU~Ur(@s z9fm0<22wl};wKaQ_PKpjoluWc8<ghh+{Z#<h~ac<#fA4of084A<K{4T_@QwV;tW(6 z#hr_J>lYdWY@&-5ESUL*pA@ty8-GUhgn0F$PB%7}=idmx>f@KUpZ)BlGYkN!{E(F? z!7a27VqtAGy{~4j{AtlT)?Jred>3BA^m%P%0LA=H?Z3vKPG)C@g#tN^?_^7XEWwpy z2t}zx77m-mG_sHGiWk3g2M)dPeO~PsMzCBpQDG;x`-Q6q@Q#EzE|sO56n{Q8N17cd zVX{xVYON(GdVSFrlHG@tx>M1F|K(%Q`F72zxaXw2g3aX5d5S}mYM~VX!{P*d7W*He zk^<6DVB=BsQ2!B|0H1l32z=sJ;dnm-Ot57PjTn7H&>S@o66}6xyE}2Qz<4k4sr}Vp z`{{_E@6ZR4LL6skYclg<(SJ~rEYgNI4mAM90+$Gi{j6N1_<$r0MFew#={N#5_xtk{ z^={#{lcSCy;ewI9So49AMUlq=;0j4Hu@rv{yk&zTQ~UHE79Zn<p!F37rg%y)Ez#U1 zKuQjbHeS75(oD)u_(YPph#j}}xZG@n;t1U7?mZw2Q3c-fDsDs+Hh=t9s3Je<?Ey(H zY@D>eywoXub!y+qD5*H;JkY4Y%<Gy|qCGGT*I5J{{<*9E-gFw@V(Paa0d&`8vl_c# zI-vaS&~533qp?m*I>H3QEYVu2-y6Jr=2j=j_N>0t!*wRL9sYZ!P3VEP1p;k(QF~zY zCFHRyY!v0~Y8W}vw0|XYW&H|Y5ga9AHObHu{y&DEIC$d{kig#b%Vvk!t~%lCndqQC z8In0)!+&oYeS)5`V-g7G2&Dg=so=Pk!sYdSz}FF7AtRJMxwro#;rF27%#grD$B|># z!D!H|5aw-&@NOnPe7kyj;spLv!L};p6D=VBnHoqHMj*W{8-EuePC%Dzoklj~k!U|W z7bxi2N==)&0N8scD(F7}1E^Cu<{^}4^~QlPQ5oBiw_~f^Ef)w31boqhHz7+xdWhUP zk{Zn|@;pc$CIRK`%VzlPN_6-xn@-%2-H0*Ac~oFbn_x5>jMZ3CW%qgh?Oy_I-h7jA zOHkG1vhAp>=6_=M)P|fqv1Nxww)O5b_*O!LeP<fUF-wFO*-|iqHT>&D8)pFgk8QJ| z8e-?IXY+3a?3-*hVQXFx=n<P6PaKd6oBBET%v5$pN$x@6o>TUr@lNGg-rb5i^e;)y zkjJ~vKM|>r!uojLN1M?SMzs<egLMy=8tQ!4lY4B*l7BIHNJYbtx9_GaDtyN(qr!+- zx%ziyX55HSs4_9wv@Tt9id$NeRdt1a{p$4^s}&83mB$QL5^6k5th0`lGmka8YGF*1 zJ9U87n$%Y&GnX(<ZV`N9S|0S>Mw7ir%15`24SeN?e+o~}v$vod4wZ_hwD#d)X}9G? zw3{}a#eW5?DggMhWEs&-W@F0DwzN#hH~asbY?r#mlC+5jA;;xkxtcAA)kWymsh%{P ztA}<MkzzKfeu|a&sB>M@fPC9DNt(0kRKwDQF#Lc6*k(0ooSg;Hg#x;wI%Uu|NuB0n zR8~j1{=kw&D-$^mRA{^GU@6|doH8@n;FRnC+<zf^U&w63oCct`VEd!1s2GrIgJo)_ zG~EMGDPv@4*Tmd4R**@WgVo}mgC0`OLNh)#&ob}LO(3gD2mP%VcOz>JWG5v2R>z=4 zOyV`*WOU*j-f+0M7vwTo>x?kg&;yMO)hAA1Va*~wfsF?x{nC02&t%3Md;w;MztofI zk$-kBzoMgKhzgo&bstIJ*4y2~Re3l1n5QA)4<!Lln*fl^LF<5(J803N|K+83cmj!N zF!IFB>RLoc++9eZm38o%#Z*NA4fYgnzhEa`FM_rS?s|o6TATt31;&k8#@JnShpv`n z3|JJqdwh{=l1>Yf6#Lf9I^sKpdb9ixCV!@T#;8fO<i7EQPhkmj#DJleA|Pl?*iZxt z)Y`WU{94_h@`um!Mzn*Nn`DeQe7fH^M~jAo4aAeNr4Dy|im`?jy~?ijyMUX1l5I-U zZ=k+)8OT`uQYjfZ<wf*10IC|Y`NfKu)|z}Ek|OE7>p(1}=JwhS-Jv~Yk9h(4Ie)yO za-(Rf9Wxk%oBO+(HevzR*+P2w=}dtc?axP(#JoeX^O`=3sKmjx$}Br8w(z7B-ve!z z2U{xwg1ukK>OJdXK)ZyxF_5d$3BPgIz)M!X<CyQz??pV}&BKKQ918$+FTJ?{Ea*aB z<vD|5>?|1`(q5Z)7T;BO!sGalmVevo_|M50akCD1zG%t?%%WJvbF3-x)@DRPGMFxp zwuvWU3)9+@ka4{eGPsAVD@Wgt!*js*SVGH8p|YKFDh9fi%OkyfuvRqtR$x=W;$tFU zuO~1a@gzz~FnWWoArL-e)hV5qBib5RW7eH31!SB1pB<`kdq4?vJkoOks((xh=`;K{ zSyQKu^%^xxxvjRT<<Iyv>yx753>$gs#1fKLUUJujr~x}6+AdJC)pWOvEQI@+4((`6 z{Vhu&+w57r0U3m9;zoEc5ebdX&>WL^a#;rLut^v!Q|dU_tRkiAqYQ?=;zZNDp{stv zRaI9L7MYz0cF@?ulZ&=y7Jr{+GqoGp7Dbu&JpW;)9Z`k|4;T2}@uoN}6i<19!n|?3 z_GE<90QAwXvwyq55_G#lD8y;=@72$yaVK*qoNm1jY(Si+U5@SNcHAJq+oj#)Q1d@3 zz`z{t^cwGhbh!oAn6MBODAuxqM3X7lteUi9bxOV)egY&R6;%KwOMl1Wr$j_F-E|y6 zmMcJ1*9Kq%X0BNs$!`PG2l39&@UVECh)a@)_y@I(5IcSB2cUn*L~$sNwo*Cu6A$GW zS8(yeOL+qb#X=(!cEd@FY!nuS!3yx%z0`Xeveq-qyICmI`J^^A@8O8#@V-dg{muAN zwyR1|2%qVqu=Z|~y?^leY<oC<*os@<&3|*6wKi(?-Mez}mh^mMF`775cc!Fdekh<m z=V+^S6Dj?auwt>E{{qPuOZF(mWMs>tGpFdL9C-3~QKYY|5nvcbNuS8QXZ%MD&Y_2w z#UxvBYTXQuVVW`spg#y+Q-RCdIXOD3h`CZoI9r}?@j&Hh=YPa;Vl=-XpAi5D7YR)T zVwRUgcwieodjO}Ct+~d|Mi74|1gF>WFNIN6h*^zDa*LVJNyts70|<ad92~g-PpW8f zS;*w0D%!Yo=1MKgVCW<AaK7d8VKvAW{$Ucz?BBrWkn5-Xa2%CIrGUxy;!WeWk^CMW z#Hg`P25qG9(SKv;x`p4W2!*J<s6`xa%%|UM{^eCh3*JduHV-O5w2#lwFxE|e*X|wn z;of_;yG|{CtAI>*3Vyrh7&r*V1IRi@2VN+TkR`ZMug^mE(>TH8pwKOceCemO_l&9e z3bv~il)PCJ4V4VeP+X;}>0t5NaBOg}c4!kO_Hx97`hVloHj}?ljK^<^2|&dPnT(y6 zkb9nop+-r`(|sa7)L$b{Y`>O-2)r@jo=aUgbnUD3WLeWm%7DM&k6Qs{?u`>E${{E6 zLNlPh&s-m8+10{6q*UB~p-N3RHgB{NlneRVsP|^{w*Bj-@=!s^A8MKK@fX);=d&0K z=2cS)RDaX<xS*sUjET?M9?1aJ?e{bGrMj7|u%N;_rMAKE6jk1AjpY!Nl4@a9c=4<+ zUVa1uDJK`4Lbe(K4db9S*PHQvdVh~pedC4pE`awm#c{>>*jZPcZ8*Pq%0?4`EO~kb z&b1n0Uc%$TFdaeJQQZYm6V?HtC|=iszKFN5hFgC-t~!6XE?x=q3gm>{NRllZOW$4v zv;VD0mu<p}z*TErdEnn`#rCMwg)#%!7w();L5;a6f6ALL=(Onv>MxX40x=T|TuJeH zYYIqcH;z2ErSt28Alx?4AxK?kDFEWyB@~GyQbU$zhGB^=)iJhvm3^aO>1uHz`+>t{ zm;gh`{PTZwZs6+!?r`;_^N@Wr=_i$jHk)Go!{z8Z$U>sztS~aBM|Utr740@2ZBAz| zz>bt$?~%Mj8fZ90Ii<qUWeD2<t15Mc_2iDxF{oz?m>mD8@!v!8JK>j%rwb@DFTq1D zPnz)AaY*o$w%{as&y|-dLLq+ZV|)oxtLKB`4~Kshpkt~l#mYGGMn>p|AcGT3>Xp?> z-(^IF5)Ypr&l(8N?Tb|!d@`IekW?!UD-lXO)siaf+Aq17A8qu$J>s5HFVfh(xOT+G zz23dLr?UK11E2v)P6D%0K?EnwOh7B7&ox>JahD>nNjKT>Vja=6@cXoWV<CI<IO$XA z5|V#L6LYT;=3b-4gU1WFl@QTr^L^W6?^9cjbmIAN#(aY6hoMqos@tAoXw;`l1C@`F z63$Gu-B@@OAjo-ZpE=-lS4aU4J2M|`|GQH9@4@2`J?+k0_lIxU4(@hOu^%O*;kx?P zb^Q|^K=?MHW`*yX@-7k$Uh`S5yq*`fEn<Hx0zl7%I6}3ND#%hd1k($pykiCV!e`=l zAI8vig1KX7HzX|la>4CQyx)v)8(M7$e<Q-LtIr&C+JNE6H;7SJXXZ%_JTYe*v&6jm zE5!)ERn13SYQr=K{+o{fgC|lW*iKEGi!&J_Oh36p)+qz*^!^QLe#ZIX|ITT98AyK< z@3h*(*Ha?<cKI@7Q0Z_xr?;IcVmyfoB2GNJd-(O$;0;Gc97T&vH^bUexvi_SeogBG zTZf!YawFbJU(n$TQfyX4ESFG+JtuBg(B>cnZagL^!=4@8Al#r^8hDAWsXfxf1KP~A zFm`uA*1oQ7>~dR+U{q~b(vs#)ey)GiqnbC;l+J(=tHdd5HVOL3<$9+ns?8nL|K~rP z)J8HyH@*TifPx4c&<)~|d%DAONKh+3lPDbY-;d?~NDe7k!aGrR^P2!>DW7tj=@T2% zpm6bm65z2T1EFFpW)XlTYoUMA9>ieXywm{D9ON)2n=H^dX$L&E?VuKC_Sk=|W=czP zbGM>FAw<yr*8neffrG_HzECv`fqUxL75wT_0(IP>{}p3&Y3~c-rn6-`bvE~OmR&Q% zZazu@DoV6a5_t9ie<HnSdc@lfdYMxPPHM?!MzD!i=P9c^Pzs0yH&<Y>0%W#!w{!a! z|1do_L%Ragd0#(tXI!is8$^GyrmQ28!JDPtJY9!o2?rE)FdFnH@*CG=_nN)#9J_c{ z>C$(rOfC<_@044BW-~eeMqyU^?St<)9CeXqG1HYb(u6S0Kqbe$EMNs9<8G=G=O*ym z23sQitQY_aQ9){QqbZR;o<;9Eb%eAsb29Q74{()nm3t5SZ*vYu<NJR{2y)Qj9&Arf z{o_(=#N&}E@AT6?31(HyJ3>0RJ5Pl*5O5+1#R*>$2#8*tD{IqUMNhVmTN_Ba{WQ-h z#gMS^7Bv`oKQB_3BHN${x4sfW^SeMbBrc@?BQs1Ms`>OS6FU46?wVF5CH~yfID^rO z9&Me)X?vlrzu?(Q@(F*aiao(`kEwApw?qf7Aa}Zjk~BSpYxS?~9_W+Q*bTNQ^?HT6 z++p~ZPHhIlHNp#l3S}>z8Ma2b+a13#N2cW8l?MKs)P+uWoUr5{zziU$T{>G=6If{W zn5=NcNd6|RpyHSoM(cKZ&LIdhaj%^N%}S<tWqY%R)B%daFD!pS_q`ph4?(j?ZZ!{T zsO)nW+Ev{^7^CNFzO&&&K-07#BlkbXE!;3o8(N$vWjm?24i<A_d|72%#UGzNj#i7I zodHV4O)Yr9U<;0fKEwbGkM!p^%8DL5S^}NE-In=@V3f)zb1hQKf4|yiW-@NPCM2!K zSB3N{<#j|>-0goxZ1J#v0<PjbODIX+?9ui<sz=3bdSr>j)3Q|t`ZFIJLlWpcYC`G9 zl&-IR%eR+OB~D4jrrL(F*0TeV$@Lg_C8p}=0vL#LOJ_hO-&ajU<OKR#ApA`Zfbd1O z0E7XL8p_lCZ~ucV6z9V3*ecM05d%p}ji6DNLni*~>0W;@gZEd=RM(kdvohk`&?8xg zeNvC>8{fxQK;Sr@#D|FM*~=Z!T1l$ILcdc<I-r#J8DrO>1GG&a1q-4A;vpuI`BvJ2 z@$l@S<wn!~pOTHDk+O27>jE~hTpJ;q*pm7*aLh)l<-97JkIPx8I|9)A<`!_>oQY~| zT@APW0EU0D554E(nN$rtJ3Cg`#>fblb;g&eN7w-@v%VAAe+`&T-9XmH^ZPIzqt!AE zl46;snn~6PT|cyzvKA#3P?yBG2m05l^sGwnvJL-pQkG$TY*xi;Ah(ip{EeBjo_W<u zyUxf_JF%FjX0!TUD|Gqj1a;;37;2K?SFa}D)Chm=$3NPSV%iYnyw2aAI_136INivD zKtGZJ%smr}em^>D1^FXTWF|b`vc7cQPen%yR%p>NEI|6uIh(9{c@S+Kb!@`Navpi( zy*3o>2-Gg<QyP+6|B)IkF@7`A{xB^SfK}ViC@R&tGTC88t+(iREhACmJWnJ!^4Rm0 z`Cxy^86Koyt|dLXD6U6nRS&OXWCXylHlreFdQYAYTF6u56^)vMV8f01O4JKKT}&Pp ztRWehW(6HMFRt5I5;2dhb*tElRA=&8%n()NIne6nutnVEy@4)kcxcdbMyr2)MO8Z2 zUcJwsvBM6<8E63Uq;ZerHmQP9(+()lJMDj~7Y-^tsa)L8^SETQ8`^C07;Hx=k(39< zF>dCMAQ>;|G2P;60U{?eZA(SRkspR3*4fL^66Sx^qev&{)kro?(OfaP)8>A8<ABqz zOD;7(POLAZLJeY0y&K(}?t%G#^1uc<6DW}<RDczJG0MKR19<7nWjA+(5uFyxfmVMw z)$)<6PUg`njeB6Cr^D-Q&rjFzt|Om$FwC8Lc(_j_<q%X;)ZGdStW`c&#AIeuX}%M) z^ilBbINun^VeMv$tz*(Y+KLdH&)aO{W^WPOvr?4X8-<kjnVfx4Fxr|uvL?g^+(-1o z$lV?4cBfw52xkmuE~jhMs9g~${vUtgmDB?p(9|riT95D{k3iZT)FVfYv|@PgDyC)k zMCg3L1y!obw|_YVj#@DR;6-@prv`axh=$g5SO99B>=S@$qE*cVA#BZvmKcy$N4&+Z zwK#o4+N05l-}lYHD>ax!5rA1PP`D5-*8rZju}<ljF0TA*m5(WsOmPUlMfQIl&5jPm zU{!P%p3afxHUD;u!Z!kA<8W?i>z|ylv{jipyVy5r&0Tt)zS!A?&L^-mDd5@i?fb~; z^gfZcXG<SxWgCxQgn35Yw{SG|+NC~uc);NH6S|Y&)I7$9C@gowS1=a_*h|<>p6L58 zOn?n4-XPjz=5hOhl`K@ZXYqe2RpffVVJv3SH%ov#UG=)FhY!cqPO|lbFZ1GTBYJ}o z+DSy<&12N*_~FR;b>;@BhGgE&ahQEr`A~CT6K^(2Y>vQB$?(V<fIogYKPRZ=hZC)7 zc;8{kdrvka3A+!~JoiaWNB4Jc5{n*!FgiLqw#=1ufHar~p&R;jBJqDX|9G3ir4)50 zWuQf=hsvh}D(J+MWAQ82U3wLrVMVlt$nTzPiUeCV@?N3^a8Dpi4~<$k`)4AW2UgmO z7q$`S4H8Q-!Jk3jCmBCS#l6*Nj~tr{WN_N!uzdTPo<_`4@)^v_pL@e6RN0xJPHkh~ zNW{<^VPLKH|4}+zcs_r}qVhPTnc_Tz^d7#$y$O;>rgvByGs)=<A6nqveXUCFjml-y zb$4e&MyyheC*nlzWH2NFuDb0Asut28-*EIrPlF>cRrq<h+SP?sVx2V|0k-h{hHvVp zzE1FOtSx*K>!8U%Y1o5ECA4qzQ7C(cb+Wl`z6Y8>XyZx~|4Dxns=b>O8>#^6S&AHX z<Mw;I2F^KBXrzyUu2WhHMX@S|1C>?$n9+!lg$R(W?AxW46`hQ&$(t9e@`IeO+bVMF z1#&fXLo(}zPUIjImSKDc+4PVSGpaBdmw&0eQyku<cy0Jolc+4J8u?ad4n`-qJTf~q z9U5U<P#PNW3DAEo{1|rYXW*<imh!C;n_lO!wen`|IHkEaJ#vDB!MG;=KI^hgXpU(T zy<!44MBrkqzOf9Zhs-H84ag38v2>6z<jtE7>X{*A_f8ZD>b;j(wpwENzFQ`Vws4M3 zouiO_!O$w7#xqU;p~h)M%<=1Pb=l?0cj5q%qf0@mXP<vGh8_Wvy-5T6H%~T4DrO8s zO56>$7BqGklJw`0HqpP)7(!L(;d`qidhQuAksynRqH9Q3A=`Mc4TW-3=GspbhIsRA zszRHNX3MVdKO#})?uxqMVL}ZZR@}|9!xtXitW8S*1RMvaxB<iR5q>z=maSKMtemIp z^po^16$pQ76U#9hrI(>!<!>j~iwhgbrxCz-J(3=*TRpwScY2vulcuWImf(9SLSf#u z1M+p5czKo<l=fj26M85BYJ^b2DOq(DC<sA!ipxsOy)P_;G4EK;#j<T#K=SKf)lSGz z$4|+?#|u|rHczrU__5`3#cy&P0-hjEgv?wUX(oS*1C8k`XfXdkR-cPZIyCi}%i+Qs z^bT40Tns5CsHQ>j8v`M6&Vb<QLj^83I0R!W`)|cocD7E|Qs9Y~FY{SIz&#F=1Kc@U zNGFiTA}o=HPz9i%yeJWQWUxK9z|!YhA<D==?j|=Fb<-;VwyFyP9nJr4%ZM_EE^tDb zJZ*oP?Vci}j(T9dK%pck+^nmCf2{XJq3Je3#8?GMk1N1M2n${ha+;SH*k$14V$BOv zgdFumlLV#ohTlBwwc^SCU>1qAWnq~vT7f{<DbG#ID*U9<gdwZjfC~y<bTQ#mqm$&m z1DzDg;L;)!xs@ra+^rQtZp?}F!mrs=-En``()z65JHh@e-Ya#CxwSjXH!<+d3-^Ji ziwQ9a%SM6FjoKWg7J7gNm)rgQlabT1VMM6})N;i&{(D~Kd*ha}NR*s0!W*E$2&yp@ zoJ@|3g7kl-XBh5mpL*qtF2Ncp$!S|9S{W5Xn=qrPdbfvIphF5oor|yhA2J~pZUKKn z*Pj&O`v`v5rl)%f)Y^&VJ~ltQ9vRKU`zwu(GZNv}o0xy=%y0lOz*7_s^#?Sff&O_i zt;M&MD^fzRq0bVvu$g%(nlr1u6=DB$SBpZ_={w&OnQ_(L0wRw@z*)W!>hG^6!gvDf zP#^%m#DBJX&Bq|P)uQ>%wa7%ESzLdCM$JXHg*5b=2}BbwY_lY`IBgAwy~umgr>8Jz zbBlwmxGt(OB3*dQu0Cp?$nw9m$YHSMv{_IuD6gRh_-Y37Y&OFj&R0kjhGZu#T-bL~ z<Z`*D0(6TSh@&h5mrDFmuwn(}p3C>X5cHb#BEH}KRGlq5CwtQ*H1oEjEogt(tLl*< zu4!ky4AbuWvwlUpk-6#d94d*y%sTEhW#L_GI&{Bixu?0gsj!Fry(yno4K(L2<)^2x zF7zY%bJH&wp1x4toptvOlSO6T3y7sGBV&uTL2BNVFt-1Tr+=viPaUK!0=DbGAX5nU zK5^-2Fv;Joyyr>z=1_kT`XYaAtwNhPtdFJfgkl;X3N&4^B8=t7G*GAAC;O`D9cz~l znKP-cI^akOCrKhf7$=uSR0e~wC@>G}^4<l~EN{3=qE!lOMXg{8KfQ_3k<>=U*uQ9u z&b%3fqLQlus<RXMx}V5)47ja?*&Q2cWKYod$Pgk*=Fb?Aqog&h)Yg9z9}9l;z(A%I zW>!_pZON7H?970H9?*#H?ROfVlP<TPc?~l!z@w0(v5MPqFzfgR(xs;|!bCi3)f!P} za$m@ZH`v$u;vgZ})J=Y1A3HgNi#?|w^$-$_{cf{6MjoP?aWk9Bl(FbhxL#D>&XQ&d z9xj7`VJODe(74uKKZJiiK7w9>YzCRWithMz$l<A{V4LvKKbx3Ftz4@0Y2Eb8I<8aN zv9(!V!kY8I5KaI97D?g>%Bi!`Mj6&22I?|EeS@=)Mz9FaX98)n6CkO40+$&ui<=ZF zr6di-UBh#rQei2C+%WCF1Rmv@sE?~9eX+`hKx|yW$`xPPVXJ>$&j(jj%a<mq#N)3! zb*Q@YvmXGv=+H(wcW8HE#Xq5@Fj7%;=4r=p|4>-A#itSvCLtFQywbty$6s-_dVkb$ zbwjz+ve|LCUsV~gtb$4r9Xuw|;r_t5sp&`l?Io<JM0z?g=~ZXa2Q7KJD_lbE<NN>< zR^RM7Hr$>NE2MvvZ8i$Eu!_~~m;hOJ$%}+9;kgBD{f4>59A}&%L1fy2??Cj)R{4gL z7Rgg!t)T1_oyo|fl>z#~rQ_d(%JRFE3Uw7!*S8N^AI{HM50=sq;g(T7X!ygh3v`sv z6#Tj$1!A}~i`Z7ZwitBm(_9E)TAd4|u!CCD3My~_z+!)`!a8;`Uj9b7oV^W0r{CO6 zF^_$2joAdOmU<N^#1C3-;MF~Hj%yq{RzQg1)Xmdr-J6#$I9d>d-Y5OUB!hDllx6W+ zm~3h%i~dcGv&>9&2)=10ymG9*EB|7b(z4gO0L-lr>OC=L%u3AaN!C^=>W(CbGM-Ai zFI3~+SloZ#CbjesASF<+%H3P1)vG~zQXShgOYmCou<HK;Hx9EVJdsUagk5fr@k|2} zRP~o+1W_o>wu#qLV?hQ3!1Ai}<;_$Us$|b^FW5HBRg6TWI6-y-_BIWN>TFg>JMcPL zuI5qVRG&VIU~ERxo7ezsap}5i;OKazB-;B5BtL)bJw0A<*vr3&_SC!G%8p7q7NE&G zXL){SC_@0ch+HqY`#j38_2cHB^_c6er=n12id~c3RiGrc6B8reEKdVYbB^;+j6#FY zc_B*OiNd=)+yQ-rn6g;h|M95RIAHjl9<26tJu`a&GWI(?uL8pg>rd@Xga1A{RqNzw z2dsbWV?(QhkbbzGz!ak|+_WHDAT1Xv+vMs%b|_QGPS!;cMH~C6X*Td?+bxT}%A=Af z0#h-@nIymdqw_jXIt?NHmR2JcDgs0o2CkBg0YUbU>tV^eS$3WJ4%&F6JCTC1Y>nSQ z$0IksQL`F(d8if)Bug11|2cHCALQIP!1R9@y`_7X;8=N|xY;m$AN23162XO><JV?J zzUNA}Yk|zBM*ZZLmFndDzDjf;Oo9$fG<!vrGMWHWROOephOET4{3AiVSwK2Q64r&z zyLw)c{J{a=gg@&bXQ@GRh!Oj>BodRR`IYhi$g)KJZ-{g~Dz6YXD+!0AI>UYiAWwe> zXfMSD5N(W8EOZLIiD$J+G<+OD1yHbOh@F-j#adUg&Cz}vZu9)Fec6rLpvw|1`2v8z zFZ_zl1(sFpN8Rw~cdV-89(C=i85Qt|tb>O`6jE6O$}wwAwd*ozrxVc9<)1=-v30y9 z5=3oyw_C=f(zinl6#JA~dwoPh_4$7{<9BBZCyWH*Y?AqP+t%5R#PTo=(xFhb(rfxY z2j_I)PM*Hlm<FB!p`%djCz_gdhxK{tMcZE?j~%kL#%(GZ7iUP?mU=;XUQDx8H5=kp zZ`qy~NST`*-ur#X;Fvl-i2MuRs%dX5?Ck#R)qU|m#j;)hs>?OXg}>V@dux9VR3Pl! zw1xKkd6U|64?NS}NlFP^(GkTVk|hMAF!pMior6|ZmV)epcZIz10J-rA)2)cWJ~ea< z$>?&}FJurTXD~SS))5k#vFU}*@vC1eytfI7_z{BdLB;`@)TAxTz!EA1yqL;m-E=?9 z(|zyQ{$TYNy!=Z^-#lG8q9T7uemx?I#~)sPnSy>NtaTidS&Wv<+oW~$^ZR5h-DIM& zI9k9SlaxuRRxszNrd0a$q|8#m3Z4WStDBU$k%-T}_U8I@@~OcQG*U^{>2YRZA^SQT z!UuP;f0yr6(=BxayYiUVHSbnK&~d?k#^WWR5U7tCr~Uo^VfL>f=;MELDD%p!Hp6YC zIwbqwrb&GP%6dNMXo3KK<K4Q!GzL}FRx~zz3~DwK`~e4685tf+-^@-!&(Qj8YeupB zT@`i8Avdhqg&Rnlqz|^3;c9D83-p?UmD-22TaRyEPqj6o700m4XqDA?wE1^uSL!yP zF;q7+Qo_WK@b0*Xy&QiKr<u>!S~?Fg&m>C$U$Mdb$HFWmZjctXk7(E`;O!o(>+c+o zsv7*nMp`LB;CdghW=@7JHU>Zr2(TOwGwQjBWi-L=o}d;U9J#yt;Ge^Vaa<sf%pz9| z9QXAwh4*WJ=K~YJ6~qo$ZOhr2v{H9+E_QwffAK>G)k(qz<05|ouEzpw7|3I<QPZY) zO50~F@C9|9iXQVKT5xHY;F{NpZqEg}d=(Unl30*V*kJ*CQhFc`BI2Ntv`Pn#4FEei zc_+&m96=smM&WJ@t_wM&7joq}vS!8YwC%l&w@eL@XT?>28pOe);iYF6h4UL6uNzGr zQBNOYpI~DjuR?#a@A(Xk-j<<ENq}JEev^D<v{?b%xM+8WkC>>Od2)OPxb>IHtQ?;N z@vqExe+U1k+w9B`KnfzrrrAc_8RZhw?N<9TQw^8|*ELm~xq=lNvE*6W+e)V%_)+7~ zwW(U6&ERyxBJg}OUotULLN24g<$)lXheR>@nang%c-?=6rTZ=p8*t8Ei0&cTNAq8w z{20JmF)Sx_|F%v*FJ|Az0elK%1$qt^;1yJxX+l|*y=m!p30+IuMWk_h+)h(+>2W8> zYJdk~jFC?m`|}>~Uc)xsw6rc7kr?Qq0+4h8>IOjmIy2jTiUQxrAt?wgR?-GOqM)e0 zdwX+?ADe%TB{NX!5wxn-LyMh($~T;<0J3!#-1ZR<4@7|vvR!4@-fk4CFfzK%#*KmQ z6&^v~GsX26f;rZy@9l+YvYpSpp=XId(WOdu0u)aYa0usiUk(Tyf{qRF%)Tkyx&>H| zH<M6DP~DHcF1Zc>j=NDgc3CSot|tyW8LNvRXq$g+d$qi80E<6wop!KGX2$GoOl>*v zXUlj{7cod%COWtVzdbe5Ivc5F(oTw=`eOdyon*drs7fUt0#F@h>C4e+aVD@GX}6T4 zQ`J^6Apq-;l!Zi8+^UE&dSTL&=}vM-TVD1J*YM(Szj1MDXWYJ{jJ?n$@xFk#UZtIV zmK%SU919ZnSp#_#`+Cj<28_;zBAL$hZn)y~<~@AAkU9XYRSn`$%4UKk3X)vKb+2!K zhJVkkB<qJwFL`KJFZ<I&jAxPAQ`$2!@qWK@aII@gv%#h#eqlGcmzL*1Qc4+!Ll~Qi z#<D^jG=~T_iVmEL(k#^0O*R$~H8bOpI3#~ek?`0}L@<o#hDs<DoVx)j@w7sJ-&yb= z>U{av^qyuZFQTAQ<z}^XBvOiUB=d3|0fweSjd1YpV1k%!t-m!IFC7Pu-#)wuz+>;* z*_r?fd}e>Y{Nnz-0Wap!8QbCQI^%-dsW~linB_EEe(AF>j{WrMxex)$fjd2qTE~C$ zG>Y?PQrG7CD7VtMJ%8C@<<~sECRWziTO@>+JQfHm#cXTK>>lQTSfI1lJi>PF-KLS? zt8Gd<S~MY!&ELrmvth(?PbbkqYPwFDU7_7414Gd7U*21WD|p`DO0jTw?UR9}QDPO? zyhr5N^-kEZNkXMhJuFLPWxt;gAmx9m39r@hiBQPJ8cIw0F-Vn9f5W59lm%;h16JcN zSx?GmbDm9Vx@ipjHDj?TW0YmtOygN}z0je=*DCgB<RvQi%r_lcOQ1%!r`-?kBzJ_S zHP6}4q}6Hl%6i#4O895&i1U&f5EkgYMveG&Ca)JpZ}<a+xvLbl;aFUR)iZx~;`+lv z3v+Z>&gg`)LG*r8JC;yP79K_e6`1T1N7tz^PyK6Ri$|9!$NtIV`3JlT5TnRNoImj} z11HOp4WjKd!s51Ag3AvJQ|%d_=O0SAhgJ6XHFKhY15<FhQw-zhHnm!A`nE}~WpeiF z*3FXfe$e1|A7gmSn#?!1Ys7!9l=7<Yha&Ujcx`i>q&1?Uu$INGq=`OkI75t3HVaiJ zJHFsXn~^J@E+=pMy-R;`2Qw)6u;6tjdJMgV?}FO2r}n^NGnQ(XApoD-krP5XL0PSc zD?p56d1g-PvEaf>Y3Y)(p=Eg(gbge0b;X^|X!He}bvMy!LONn)UowBViwg-8>aPdm zH&?U$*YXAuQfkxlrfuSoJ$0|~Y%W9|3O&!^Nr`ya1JwBF&2AmNy7~KSpRJo}S(<<9 zBGah-<k$bPQq))H_@R9Osfu?0QUtb#Qk9*M=4BmIpKh!Vkx<i}xT}00oN0nc20ChX zi3ec-bu_zyz)2e%*T8?&ISF3gbL0ubdI#S&s{BiBR9xNUB5MtsR<G#?_GN(j==7sM z6bdSpKCm3P!nTPLDhn(PCe;?f%0?=TU(0>2{9}`ARUkcU%>obT5WkWCHE@Ki-9A<3 zD&(v2RV*26^}M?&S9|jJ_>uQujzm&6dQkw<bh9#YqZy|kn#+G*<pJ8?l=wvQ>7`r0 zfP`0-Eh&t(iLShbG4?pOwo%F8su#JLqmsY&@Ly_k2O-)kZ>HAe@58T!;%t2r<1`lI zb_($8bV3)LoK}R0Qo;E&)CqICy5i86LO?=~je>ua%jTt>S`#L#9l#tq=gA9><J5ic z7pMNb)ep2}{I-9lSM^&;2}ncmG9I@q*`|frSwNesh3;l%Q~wReKfww%nEul>bR3CQ zV9OFz5cq<zRe+02xzc)VX~w7#$q}ohmE=YIezXQ94#=%#H1?<Nf2fZYHJ^mU#>Q%i zmH|Aqp(MmwCok27`9Z!`fXn<&4y~V$ify1CXQ`DcrdoeR2|zh+BcJjK0Qsa<G)G^s zFO61uM3hgQa_!=W7XOuRWOr&!R`n|^NREeqGOYQd^R*~4H>$Xl$v2A<wI5yhro>Y# z``svHlw@_Klyoyn)4XyOWZSS7_K|oc*6{VZ#>Ta|^>MY1J2r#n6pE-1XS4`VZ(cEb z5HFTZl*WIwt7x_s9mw-`iq!`J?-gm<YR-5oFPMGl-SQE@E0G*g5=HsBZm<@4x|EC` zVS$o^FZJhYf99s-hTw<`!92Jorfvwy+FLLmN>67M2lwg50HG0l2M#d-2Mi2r4Sg8Y z9tSam6}2eL2`#ds7^~->0<fe-DTgH8{SQ$z+faYY$$_8N1fXQ+zAt{s;TW%pWltS9 z)xi}q7g#Cn3suzs$_{i;A4sR(Bq<pM+urv5HwGQ=h$Gu!^#6HE8<sZSWQ)E$f1n*K zcyIJQiIZh^^4SV@wxYp<<kkZrtARNUPf<_}E0p?ATW|`t+ksxE%m2hvq2#lNEBl>y zB)ET&W9u$Ch7(&L@pZ&rk;D_U#Uv<9G*bcJTB;G<tjrD^YMAW8J<~jA1xR8Z!33W^ zCWrtetui!5^NY*+8)ggh^pl${(Qc&~l-(kOx!fz|mc%R7B5%z71^lin-d^JETWlp% zP=rx6{$_&YN*^XN#ccN=T=`3i0)b-PF=2nFy7`&+KARr+UPNa$7__X9A6un17YysY zft6|g5%RmBwNfBG6_4f+2;A}D43pyps=spzP$xU?tFzLYzWSPtA2JXSaeyOH#J2q# z|AgOWt~el67yiZLw<WA_-|b{|h>opdO6Nu@qAgP~4iMfl_rff;GrFd#SoowVTkC(4 zBZNo=nDq-`r}y7TnmG>yxDir{dn}0F8mG}gh`+I&#*pHH0etp=_k(w;58Dq3i!8#I zQb}QDxk<i3=|37zmn>rb_<MILnHeFO9(lzio|~5N^qcXENZ7WxFZb;5Z8nN|l_r<y znk&d)X1!1+D3+*zGJbdEd)#!cCtH7Jsr?M%gPw=k*>xYQ__dTHFP|MOIVUd`bX%pi z9TgV*mtc_`DzmE7s`Zh5p?~!eMnPdq^6VRrD|U(5IaEPf5q!^QZo^gSYKMW_rbYzq z1Ul5+OW;;t@3|X8s#Ak)AHuqj3m)ne_@EYHrnmQ6M?SwWhfjh!J!QYsKb(KQEZb(f z=nsk<x==zF{2X=L4k|GNnMfi`*BCk?o^JV+9%7r>Zi5Akx6Mzx%qm*pMSA|l$m-cG zV)3)yP|<0i&}`!v+{fL|eim<&K1w@4)AosEo_Z7r>-=;aa#a7$AsYtDNhj@7ml7-U zl8L^*C7R6CuuRU2lx+m1INN_d^5icv_kN|LT`YUSwZZulvgMNSsvuj);B=reAj!v& zT@j>eBdtOanliSRtjs)MFD7HHdx*v6SLPPf`G_A<qZcE2+<RHt;6}>j7Jafi{!K@x zsd1FXcqJ7$ftTHDT$TCoD~by;-1wXIXT*iX&it-Khy;>>Ha4_!B2|B%)cJnPP79D8 zGd+Myw;)_aQ`x)DaJ4-ws=H-*L=N@Qjm_^5{^tTTeXY~X==8_|MRekecsB|ac{<l~ z?IMa)P<Li4b`9h0ZdrP*p%|qe9=r;=1mp`wXQ=;-(mt|^%GV2=&_qS4AT4ySBXh2X zA$Xd(WY`{UBPlZ$P0)Y65Q!M*246X>q{QLl<ePVZ6Rgf}#`a+nYQdz{CTsD6_~s7} zZV0HEkv%`(A>4eql{y3v@?tO&#Z&)dvZ9BD0jix9Gdx}LprNJ+Kmfrz@`Rnq4?u65 zNB^K|5^+D6uQ^byO{2)j7QxUFcEg9#xC1$v7&(=;*MZ7N{+WMesUh@1s)dS6j=pTX z(gqz>aQ|or=`0V7+?hF2Bbaf9krA2x5ROXa&D7>$uL>^PNE?#{g&}Viba#h*6`cFv zZ_f|EFAXL#3L3aI3TTXvh^OON|M$Pm=SM^OtYP&3@qRuWCuHt26%Pr3_(+7VZE$I1 z75@3a7umVqtK5IS=C;rVcltL(TJ_>Ws5B~cb3*iO3($m{uM0&O-WW6PP%F)dRe^kM zdN;Z|A3I{iMi#ETtj3qVo5(`WZ|uI_5Ar*b?AA*)8L<Cg;IK=AIYQLgJ>6_;U2a1H z;qE7^<!Heg0|m7CpHnGeihv>T{4dJ(w<%jsSmUe)0yTdn<u+1MKV-A$%<;I;v^-j_ z-;^Ulw#-y@h5H951L#^t0PWjLzs*grC_s<QoN84}@e$}`K$!tM1d%+?si3@WD*)9S ze$~6yU(E+K2ie1WuVfL^;wSZ>I8Qw7|2bg?=tTwjc*3+FoR1vNE}3RrriGBh0L{ZH zOKUsqPLF@8Ui7n{PE0ZazNnttk=5|M9gN%^%2#RVPN{j)f8ATk)%R2-IE!%Q7u|Ug z38)R2`<OOGyxA3uA^|?Xg=od_IMexQo4<SD1)nqan#jUuP_f-XdmY@y^v8Q1Tu?*c z$AKN#Uq_j77fPV<Fpl-jw)JtNK;1?D_f-7*r4)Z_u>}GaQ(OPDa|i8TTCd37f*)0x z^10BU;_4l|?CHd@vpGVtpY@wc`>)qnw*Ub5Oo3!$UINF>h<rmGuzEzB#~t)Wz}oN5 zLmByrnYpACHEialn;)=<k|#0_k#xf=$8zo+rN*#Z*oY72xGNfgQ~&^Q)jXb~j8BCC b0mBv10f3xfg*J03vBYQl0ssI200dcDZVMk2 delta 23034 zcmV(zK<2;b(E-@B0gxC4@>l;_7O@=%0e|4ia7XI=n9Z$jM>hIZ2-qwf!VW@S8Bgr> z8+}#WlHh0=#8DzzDB;Pas{`I`C}m|q(`$&GCkf0;G})mq14cu>1tSwees*X1!md~G z?8LhrsJ5#q8rG$*;&~WzH@TJ!gXGCxHwY7P0xK@#wC`H)RXhXkzn~*%b1=iaRewPJ zMX~u8l!wSOg6A2%X9R(MX5m0%RZIu^*yk(HrWHx({OuY%6Es05p+hs(QHuPoHSf_O zRQKbsRHM)jvMl>>xo8vmerDghShCeGsj*W;zCpGY9Ft%g0ufBoE|30Ie8?xy-Q!uY zX%s=pf8fAKJrC;K{+ql3@3Qw-MSm?vsy(Nbfqr_iZb2iwjLDNLWj#B{i)#iTi<cST zbT2$2D1Pt~{Wl<%Ubtl^gU5>|=1nssZN6{)gohJ0I<zEy68>6TMRLt5Q_5whwvW0f zz#nTKx>O@`jj^Kza<oC_y@Ew<wyL1ra%u|ivBDL>(nm)Zcku7QA(l0XaeuJruX9h2 z`KQu4zk6%C?cC$nsX+h5*O8ut1VEY`-2**(lRee<fvTGlaWe`>8zv#ug@<sI>3E)C z!IA!gCp5Wh8%hvJ+=%Xv`-8+paRs_*D}l)Zer1$@Ntd)W%Ts+{_P}!SNNbR|S$79l z-k5Np$QqT8{3_`QM}6fz-G573qnf!hKKVbslmvC(z>sVO^YQP?gUHmw)I~=}WtSVR zu+S0hS3b2<@b#^v`>N+1d$9CYiEx(|qmof|J0#c4P4KCh=sKqkNtkm66}<_aOvNmY zVWwe{6>sSQV(%)slgRE`uxrhbOB_t2DQDBdhVV7<CO-H^pZY(y<A0B_&I$pU7jPbz z2=nx}V6QkY_$-_)i03RFvnTlX*qbXf#)-LVZ2gwTr}F=$^#Qa1SQ@x^jBE#Kj55VO zZ$A7d4q_@c0+0tt@n&Gp<tuywj?B8b5N?@Lpr0f?1K=uKpA0WVzwmH&I`+<E*~f{M zhj7oE5jpBb(B7lo_J8=VE9v%=rB7ohA68ZmEYH}HEKqvD0hyj%lT#{QN5B<L31PEG zO*?G=XnuLCX=8qu@T(5bWV*&Ih*$CBqp%`=ra>6AKT4`rWgm&eSPUHV{!A{=QcbUH z*wP3%*SQay51-SBR-xMmDw2t~nr6FBJiqb9Q}EmEU8*`0SbrBkX%S7CgJEhbc|eKL z5NL?k_iMwR$?vv7WEG|<ujf60msH;Ct;rYOgsFL*UfXl}=XH0ag9D3G6+v=fHAaZz zX5fS3w4&|hk)(WBC1wZwQU_2|e2>vn747Our+B~h^qc6Z=$uN$Hl;c#V#C0?X)NX; z-qXG|g3Fapr+=-v3Dn;SDSu96lb-30`icrU9Nv`k{fq=w|5X&=@bnDmuF+{xh4p9_ z<5UzCxSnF^rYD$8R7Gm1mNk@9aIE@t=i%D7r!Q4FD@*<>Qe_#wp7NlrnG7cB4fb?- zMq!A87pyU<m*3+w9Q-~Buw?b8I<BUe0{tPgPZ^26MSsR8g6tT!Sc&y-T0up@8v>|; zRRa5y6|vr`!bMq^UyHnS?Dgbg2yVQQ6-V>|7=&@S6cyT<9o3B-yyIX{-zG_6ipTU$ zB!7(YxCcce3S3Y*uOJ1xeza19mfOCFwDjHlzNru>i|mLQ(cJt~(!0Z_l39;Xe1q-q zA9|TCmwyPHLZKo<yKX|ZP~b>En~6MYGV9~w(xc>B!|{LDq<sRF$HIe1eUx7nUcGb9 zW`<o``4#{4Gkvv(P|Z$Nhu=|}h#H=%_3@-4DzaWAPB!}-W1gr-!Dqotz!?MXWduPF zVdC)+zT1mFAQC4>(?#87=Y5`6g|6V!Bm=IK41ca`&LzrMpjsTMxai#A)E>pu+USNO z@x~GwzE-&hK5Ypf58snkEZMvW&mok53TeW?k;-1#e#ihUM7l)y0IDCv=;NUfpVfu* zoYx{RvA$00AHR(7pjJ6qee<2U?LHGw{(2|jI@;lDl6N$op%Axu{G&YmewLr$rHXN? zDu0k>nEqbjEt%T>rX~ob86I#&ErI}vR=t|v{6_v~!Z=6av~p{<({Uc!0^&~p_6k&a z{GSnom6umc#PdmxPQ6J}ijxAD>x*WP&#KIHST*`QYvCE*5vEXw)p$~EgaOchS+D-& z3^>~GR+SCIyNw=^zg-cx@~2;8XpM$X^MAB>N<AkEw@|R*`G8@VqJJe0dgu393sZHU zioqL_IM<G02t9R=x!O>M;_gz<IL9eF;!Hq!uw%BL?_GJ<+qHTX3WuhjpA^u2?7JY6 zGan=Pg>Wrt(ZM{z|FZGH`4XW*3+B>^$)^cHxAp`xf`xfzzuorbxF&Ww(xXr%g@2Uz z!Gp+qdJ=0!^Tv4B8M}<s8v`ew*a4cI$%PNdg`79Yg=$bmUrj60VtKq#EH&^&yMPgO zc?nLzbV)8oL{$vtZBUxEW8$r0Xst+NY3xu2acYUy-B6Iou?s?}BV?M<akGfc<R4)r zyKFE)4d-4((44KdFeII^c+PcR$bUVQMV9q+sTC0RJFeJ{QPvT8cgW~?FVj7&vW@qS z@qvJ(@AsC16NtcyKi?W1e%Ed+!5+Yl5;eu43L5D+o*l{6*$JkAe?;;yHmNt0eI%cI zTZ_fU@6r4}J`#xr8XHhK7$?oXq#v%6ZJ2!R=K-z@^_o?$(3+Ls={trmiGM@?P6H#f zNLFOwD_wR*X=mRfc7J(HzK$85OlRzThj*!!bO)DKnW45K$|3Z^vZ#4pFq54!Lh&O2 zI}^dUufCBgxCQRnz-T?)xYD!zrU#dicX;IO%Rj^6D7m!Qp2z7uTiW4c9**odnagH3 zN=MjgZ9)yZ_aDLHJ_~>mF@Nx=&hR3~Hthcz_8hxR^$JB14Q7`1!yKxiXK5|h%_Yq* zQA>MT$E`PNJVgO0eYOlxuO72b4l+ytH*G)hIO(86-EASFJW5Bfp?#f8AN{%g@=M&4 z8i=(#d@c_}G4g<uXUER}ll~VAR{v|VDQvR-80wQnkol;Qh1u)R!+)odu~A6>s(&)g zf*Nd~acbX+%lDV|kcuF{dI2pq0AiCep^Tf#LQ)}qL)ng2k}++|Cb1*J#APbqv_@5& z`Qgv@=i%Aio`xRi*>lshFYSo8bM4++x=<$-3KtT3itbR?G%RjTbDXPSnKgvWQthM; zWsB6dJIfT4Z&MWw@P7nr;xC5^wR*&ZWd>lvygge$xMO;ky$@Z{^TL-1sIYL{9X)kN zZR|S4cQ(kVflnRKjI^v%^rxJ>tYhRv&_3J_r@IyGOi1;Q-}EH15+SBzv-Ou~zDThN z2m3rM0L#qwp;cd~c(MNc7`yB3{GK(|aI*|~qtv(;<QOzE@_%$QAS?wZAQN5CqODmG z<u6mGZk^Qawf8?o*%RW8ApD{T>7=AMzo+@bR_S)Fzy5x>%cY|E^Opu=rjh#YTc}CD z^0i*n8PZyhSr)KA+4$rNm)CjvV^Rw|FG!N3=4ulw$?HOGlF{VkXmm&nc{2zL!u1=V z`khkz?79hZihus!09riIs~W>=9wsn=mDG+<_}`ed``m;`8NW!HSU`;CPCM+TnF{tA zWCAt`A2XB2O2m=QrywKr)7+5qA>WQ0&LI;wUAyxO8=Kfg<b5TYs42x=7!iYB_4>b^ z3hPX~fw;pnD_5+;9WE9ZY$50w5)Bv?fkf}Q8VE1m^nb??CMRKy5)v`vS>kn5X6>y1 zJyGJA>sURAf>k1xh>CCx2r%Vn-I)6W3#buI0mYW~_CwRP)RIQ9u-_3>$U<2+M^m?y zNWO7ZhquVpEc8=zmwBTl%N6Dwj@`@TSW82+S(&o+=YU`oYn8dA-JJS4h(6=%=&vl) z>k^$9?|*0}T~?n&;YA#LL=)iaa-^s~|0L*qGU-wUTY*frX!@1fMbkx>{wvl?sYr9_ zU#2_3+y42o4sdDRx)hOsn7WQ#&_<N?MP#O<{aUS@2H*4Q?bLY--Z#AJ_ohj_`Mk<Q z?c`Bn(XsYK*;yU{0I<LDt}K<3=YP;3xPXa-1b@~kzI@!D4(|9)(%DH^8LP+`A?n4{ z3c6Sr7<Kd9)&W6t-+ifJq!p{**>io7T)G2hh_pZr1b+(Crwy|Ym9|52yf%K#Yxe3h z7Mb&{O7>R@TgQym?8iDPOH^ugwdgxt861f0*NqLfei0a7!j(i9nb#jl>baeSxXJDV z4u7=WqI%wHc|CVhWBV8rpG{iM1U?t{d%un2y;WJ5L{Bto^R04>2=NfGY@9b27`Lp+ zLpA?P`_Xa#F-wyVmf@V^+=q8H$mT&^7(pW^c+*AN(F@p0-Zkfhn$HDHo-;{)%{Ct; z;KX3gb@`x|k_~azUzM5HdPcW)z>gd7qJN!}9^FGT{0>M}N4FIe#@GTF#BU3c4jjwr z-A_$=&s>+tX01O#_}~PAE#3(@urVcLXgj#$jgEceKJUlhSsBu){<oh63Qm_WT}OVL zx{xGhyeZpfj6knQdMP6U3WVXe^cSt9tbB-kwB1t&Yv+j#=stQE*Ti48<H=ZLuz!S0 zT|7QA)ef?Y8F61ICKLMb0N+iYWaa1z<lz5&W<utR&YPcZXT-cg!&~@;b+Q^W+8d-( zUl*lks88Oc4>&M98aZ*8pRp2Ab>1ZxX!J*D4cqwGXZr9UYF!_rh@~;*faJ>lCCDM> za0cd<dz6~J^e%@{A(z@lmIJM8H-BpQRJo~GRwF$l@&=7mH`wh_fAc|6PK&bNG^wQ~ z-33CqN#OIgC@Y7E!y7`YV`91zGX{6%Az8Tg+#f(d6D89nBFV5PFrx$27&>798r!^7 znmHIZc{h+_VdZgb^1-Xd8_!Cy!G6gT2Jwjz{*Z0gl_kK(!NC7lNa|8s4u7o)0wBsE zIJN5>d&q^!3^{4`0A^u)IPX0wPDq^QXP-N8)Alr@W8{PmXlvN5+68695-$d}@uE`P z51r<Ac`bw~<=}M;Im5^0my9)4ZA#|9BM7LnT5PjrsPE@FAr9@iIxHX-l`{FKPM+$$ z7Y+nz@+B}J;6Uj2xvTSP>wgZLRi>TH&*S>#OCoNyXCU-VL0^=n8UO}EIMAW`QH^%L zSO61VgeY`Gi8#Ql)cGZc^l@x-bZ_vExoF!!?IQU@LK<;1t@&9OAV(R(Q|JI$T7s35 z!OBVPr=Zrki1rbT)~84tFmlii?0wR-{|%|QN0f`1Z@>?Om+{Kpdw<}skh2|Q;lmp3 zV;ZqqF7V*nKk|K#YdpV^)i%q~$7n_|!O2b$cy9jr5+TH-oJc$;oy&CBKWlC8%~k^j z;f1GDZlcFEwATVR>ft29M>2RG^TGX=wpP(+8Bk!)0~jGo)e7IJ8TXjkydb5NM!IWN zcD)wlMsvW-rDx&sl79`!bsMZmTUOCknyMODSE<#Bo<)Db7L|z%?ofI#1^5D@COlI0 zge|{R+>#d<A9eToXh_UT3$77V)-&^GJ-Z(Q2q51z)_A*x;Q^&FXJT}}E>V<QSQZQ| z3zo47S5yW6QR<wJAeU4&7z$E}e-_S=I-9bp?)M>1QH!!YFMl}rN8`hKzF90&3B+q+ zD6hbPWBUP}iP)*37w0%_71qSWEv0ad<Hu;)Sj8P5G+Z513Gq4kHB2BeTitt+HTyA_ zM}2G4J{_BgnP)&<PMl$M{#i7$2o<`&^R#1pg#CpYEGt5rWYu_v1>Jr2hTXZe2STv5 zMoQrO0$%q56MtN5D_qiiT%47C6t$v2Z^`)ZWR+&*m(<`keVN*~vj&_C*b8=~JBrxw z<kvN3-=;?u@)DWEvFoJi9rPYub!gpxn3O-a!mL{8I}S|vfeq*S!%&4%@<UbQI-pwY zo*C#+&W8S0OJnP!2(K(YV4TBa4|{OPgI;(9yrfMGF@M|a`A(i}0T}4~_MWVVv>rmG z_BS1Ot&309s>kK&R^Q@E#4Zm%@lG~RhZ-O%F?i|%^+4+$O9D<;(x_eAnAyL5n$Z_s znw=57$u#XuN+kzW8BD*bnnA<8zgFvTcK0rXE{sXk@Iq(yyX0dj2YVmcFUjw{p|-r? z2VMN_FMsFyjIwiBbs9h?Q$qS%`7Zw;K8Zd$xHKo!g#Zu3?%A~*V&x~^_vs;lGA8~9 z9IuU9z5-5gN_Nwl`_90`Y#s~=pbm0|=X{RKQzm&z5}4#&I&9q`qGmA{nz8b7MT`Sj z_9bDOFp<X<Jij;{)F9BF^lIinm&YKaa~K0LVSn<1r?>!&Eg0SeY*ntuZ!;)z7oX1& zYj0H<GX;z>ez=G(;My)HB*%k34R}Q$oTo-v2M|vrP%z~^sosIad;0oybzQxkDiCc5 z|CV`B?*lnPMlg3i%?PpcQIn4Ks=$b8yNOtKs*WfyQX2lPqL_HC`iuRP^-fF2c|&^Y zl7G)7ILIUqwHna&mgVgY|CXneCzr3gfa7`QA?3fOK&Itk`UCZlHTa!VvBdo4n(WWo zdWq2Wo9twlL}t!?RtQ*ixWn7va)$02m%eL*;me;Okm?#YXO)-t@T!o(PYZ08C;#`V z_=vjY-muuNxdjkfbJp^d5Nr42(B6+Y_<u6Z1sS8;-O>gEU^JG2iHI7(>^l#kT?kCe zb#B~(A?3UI#c+Y;zh6^qrov+zEFa%-^F!bRTQzXK$9el+;6rpUeINju=p9bI68o0* zYWrO7lgGWMw|=MI*bvWE-D8TB98Q8zEPLeSM8?^^e1U@J=!)B<_bOMT%bJzE?te_Q z0i2fo)J~+Dr-zo;M@tifx(Sh%$|S*8f_5^rkIvkz*RH?z)|a>Aju{EscAK0MT=POt z<#1{L$IB4o{M^p><?1EK@$N5)(EKkcxaW;v)X=PBK(2^kG6AiEi9K*V+MX%x{?_W^ z)!f=={D-U0u(ZAkIXQz+i)2c{6MtP_|Mo!XoN>3$Frc4O5=#yp?tMAKZF(n2+_(kH zI(L8yg8_B3%uR{f*CYB^)_1>8Wi*8O&gnEPW0a_QKhpFoNiL!GVJL%3%m#0X-N(rd z!o`5zvP>2NVX&Osk8~K^Ws{lmU*cRLOX@U+^^_2SN2zeq7HB?gk$Jjbh<}7BpSOeC zb!cF!L&IchIZ1}YYQQ4qJY35(pgvYjFw|&aFrE&70Psc}vikgH-PKH&_9qLyCKcw^ zT%+Ga>2p#J$!H#{pu**P*QS|ZX$;n(b5%8}nF{+tEp910#t_`OBUe|s8$(5HP^mNT zG4-#L+$@6`dANMH$nE=C5r5v^+PSwZv&=`ztGcbhb{A8xW^&nOqM7c`M(k-!AkVB8 zerGAjO^M*yQEnb505R^9H?=Q^E74?M)6<RE62#^erfv8ECFVk`@T@c&IxXT51b}IW zHDG^U=_3t99dV4=Z>UyAw+3d!urm6!=$@i5PlH%o+K|Ps*25eZIDbl0Ug;Pq)1IWT zCqm@LkDrS-B<BD1fiem>w@`7+y0>D(5&b%T(2K>D6ez7$V(>d=W4I<@>yGE!dc9}W z|2zkT&`m}^0zV2GM`7?LfUJwKUbOIqRlCbZr{g;3To$-50;cPcuse=kgSmNr;ujU$ zuU9#YDY27?0POgA>3`<qzK}>wOx|C2OX#g^+nih`xrn5&h_FzgNGmckrhyqyJApVu zMW{M==1l*Dy7lzYdkxlu*&gNEMUV;Z@M!0e+5mv!QP>89Le|k6_7OoP=>A$v1J4$0 zU1@LYy_Tng4cB4_snM4qvR0(liOD|WgApqhv62-{(e?olT7N9SsU`o~21V6Wzj<va z!LaSze=Rw#+~Rj}Xmi6`8=<8_iR7Q!&AIIciDPwGZZp4w{2ln6c**&-z+VUaJ5Sm? znc`-s`&Iu1)<(6K903s@OUWcUqn+oM9jL~G(uK4J*H+Vn($75q&tX;0EAHF3&**{z zn+P}p;Q)OOAb<J9;*w-)5wo2GLBwO|WES8jEE)aZSnjk*Cddj5f(ce0uEZ3-v{eBx zk`|)j`KO<fs+Rc(R~g!ui3g{M!}fgQQqtc1Y$n!8_i(j-!rLj}Q3fAJ)4}&jY0rP0 zbdAO0YN9@!Hm1+tr%Dppt1VBu0?MTtWF~N3S9wfhsef)@EpUwua6G8zFSRP5MrURB z+(d~L3fNSgVb`RS>rw!7Xh^ii^Ipog^HMF-rC|6tFACO^^Wn)l<=J#oQkEz=Utq+{ zuq`#RgzZ?p7OMIv(;Qa^$9HE0lmuBjL85udrTz!_ozG~ZwfUIge`QZk;%acHM>~Av zk07?^`hOO~;@Sx%hUTSTjoDi!yN^y}l5W$gmF5JaAmwPRZQodhSqWKBI*GvcLiqGL zB}gJVG_|+i%+wuPi*nnDnjE8)Xph2Yc212nVTWu7i7AS(mun?Ue)Fl0T-0DQ6vC@a zHcQj#FWTL;w5u<?fnrvKl}{rSnlO<j5#3UzQGc3*><Tf--ITuswQKGktx2^=>VSBp zDaO7ks#nss-jQ_K%nP^$hV-tfz}IOCLk)pSqFDSwcrKX#XgFg4v$PSSfNkam;Q_^M zq8Vnxe030{*D*>*XkHinzt&zLz{1V-<m=iDn9l^Najs$X$CPBcyehelOW`TrxTP3Z z3V-FU^P8=ij|>R4?PE})TUHsfIob9AX*2G`=~mJ1zWD&dm?*T}_!YRLftqIELw11F z<$!6R8?eEreR_J;{DujOH-H7q8;`aaTp+bb++4+dOtLf>#QyM+!~@TNOj7!T<8+Vv znHoq-5I3%UbPOyhXd0r^jI8E7G0@X;oPP^<-VV+qQjT2h6tFdc0W6HlEnY87ve%Xw z9%=?Qdcoaip%F$6Pu$@|vnbf8ZZLd<d)egGF%=Z3EI=}H!I8c5Lh_VZf(Lk6Ah?*K zfm@_(mxT)U;#Nan3)%(OXQz@h#~*pxu*fP=&BZf)jO|woV<EcsB?GG4_#ZSVwtva# zw0`o8r@6o4L7a7iZM7;?496fe1va_1rYd;$=F3FDH9lPl?lVm8AoUva7_@2Ch9Zj- zm#Q&CB-NOD*&4Q#9v^b1=e^d#eE<LVTu*6D(^fvJxJFJoe9-z?f2`5d-wAJZz)^qu zC_J*JwTJQ>@&sBM-P!N2z^qsFuzwqy%H{zSzQdHdKryqPm>nc)RTrvIoYyMt9k<}W zs(a1YW1qz@7%+5Gk9rD+D!hp#|8rlC7Q#Vr*oevV&*lNfSzC6CLTj4hxW_bswlR(g zaN@4ePWbBOHE4CHbk#3CSpv7*7+TjcX|+HOz~<7Z8h<bfevybOC$xtzqkoY1UlfPZ z&l|%<Y0<(Nr)ToRG(aZbJ5ZQ)nl^~VG#PLNoD162dZHO;{|yw#QF6>|3(PJ4l)nH_ zN`8qG_5^~6BrStowQjQ8ZSgl03tV6a8I8vwCFX@rLRezHUe%z%2vP0wNeg?m)QNRb za{q*F)c2QLJ2s7MvAv9bM1ScewFeb?*A$>dx1QBv1xtDtKRGbK<QjjS?7xYgZ`X#g znCQdtwlIjqI*C@PV`~j9=OdgHi#g3+lD0speQdF{|CO5Q7ZQ@dEEnyoXVB8?H+s({ zq0^<0kY1G|pOTj5Ap%XW=rT*z<F+O-XvnG-#mi&%5E$RCyIgMv+JC}NbN;Bo#iUnA zq2UN?&Du{WbN!NZuPqxaB(a=tay+>t*B)%lDqzunuMxbXznUB8$=7{55FYP55V;_0 zsFWq+T_lVP_3U6(dFPqdB&@PpZvc*p%;Qym_<gpb<Q{yJJ7tjS5vxzh7c)yq04 zF=}c)+~5Qb56)jhOn<h17rxbdT%oxd9|3W)>4%g=43c-u-J`sT2C{Fs!|VJm8jB}* zXYYPSp`Fn`N>BY0o7}(c#4*$e6l5sS`CI-|oj<1$>)qYFxBxl6bQ^|AM|bE!bwhNO zUusi(RE}|LwqkAK_M7wF5C#tiv`N(^!(CjgP+oiEAnsCrOn*?l<&|)t!|Bh>;*Lhh zVa!z;PLM6XFuOOc$=BOjA_5DE+v~V3%Yod5e5xJRFX8Cyrw{T(WG+fh$%`+?l3<|V zoxv{qEwZbLtbRiere{43SlIH+wV_hvE0|O+=Jx~Sd@e}z#qWbe3sfYXUq@3c!+uwT zhfS^+WB$0wqkohJZ+>nc&z^0DW?|>rfYBR!sbl)h0MiyBe7APuX-;XwGwj*Z`k57L zKC|%4*J^5Zw@uXt&O^k=1HW1D11Z_XwtaGfm`i5GM44;ltl+RZWz4B8l+1HPqwFe} zmFCrQaI#zX$7tBIeIj06F;vppZYaJ{yFywz^hNblB!6GlEr5xv{j3R0uxU_*lH~R; zyYmn)Pk=(;C3gUderk7m%B(0rZ=_|eK+!x(N$k_A*OO|tUNT%gsT6Tzow0A95(kle z9t=nw6X#BZTNL#lIv+wtI3#3EKrckBd9s`I7#WwlFi>&;y??_2r*_6dh8>f&secKx zvSqnPrhm^f?hVU-u6+g>pFxO9LG=@`v9G@!?tY5k+(g}#rn}~2PGVz>7s*GK{IMvO ztRShzZVU?I(r0X&ebPCIc-J}CDj?p`8JtFa*oUZB+_E^mBZmwAFwZswX}f44JmSBb zDTU>_+g;kpiY_*H@u3>)#5rtkUSO`G`)%G#`hPBr(7E?N3#mUX2w0a}Lh98_g}6ky zF@$2A*%oQ4h}!jK&#P%xp)k3-UAD1E)3<ISdBr@|aF>g3C=_#3zGRoY4gm5UpkjqX z5#vD3;~I`8qj3@TyApM0Z%qGoX>SDjN=YX|g%DMyysP1z{_g&<Uzn!#>FI1NY=D<< z5`V=bZiNT-pOJts<-WF|wxB&BP+A0V5)rgDPnmlP@t2Dw>&MFmKL=nOW!@6qT|u@0 z+;CGX2Fjj|NcIbzJCx2e85b5ke;W;Pqjk>wieLIgr^42gsxGE+Z|avu-wr8ONq0h$ ze<l!DqoFQ9zKu4x-&_CK3SBL`7(6ILM}NW!ypv29?-kRYV|7qP-wnOs-Tf~Qz+EwY zjEO)?4zEwqMm}vrxtvkkG)(ZZJ)W>>?VM^vhdqN`ZRIWZQ$%KuN6xer&&<+a8*0NV zSpY_GsaQOUjkc4@GGvTwESVoxCuFQt^BmB1_ziTJ=HN2u$%!xq8qLwBaU7HoaetRi z;=GrJ<kotrCjXtT)DVlf0Q|pT%eD!|5^}VVNz_)YLqFU7t=x?+--2sfn<f9_pAzXZ z$=pKdX0~D9qEm6Lp5k09d<La=&jB8L<lqYGB8{o_j**4*7#rbuy#bIMsJd>m${!Rr zSAoVx6Ftqujh%kxuDVXS#^`r&Ab;cj93uXuX%IJsf9(Po_PK3c_U9J$V{`Z&UR5#o zfpsb<d?GD+P$>|%RIPM^e3?(<sD|{BAXIv-Wup)&ae7LCq{!F5Za9=hashVwa1%pu z-L1~YzO{r?u8D(M-qAMcx`XLzQ|M^mi1h}bn>{vp0ePkiM=+yRsJs&SZGRpdQ{C%> zas*>Wah_JA)*k+7{ssYWn%j?6ZW|ShC9XfTq7gXCzDzso9OR+k$@d7cMWn!^SpZ3T zVr*F0rK@FA8r3G$>v5nMP^W=-k%IUvKKT|>hkE`fD^J*m^o3MFz;HAsAQKu7H@U2X zd1ziWh4idax1fQ(xOvJITz^jI`PnqV5e&|YMP9E^t$tD*_#;8Uhx2MkQUKXumz<^z zT~TlI+lB$jCfZ})m9oc8#VGiW96*+zC)baSz0-3e)@8mdf-i+=*Txv$)yDC;enE=r z_8>^^|28>LE}e&+;T+9_TE=<1$PB8yJYma@V2uj}ca_w)Kzfmzy?-TTwDB@sx%B`F z!hVH2n$4bYF9gbcMysYgcQmtajZB&S_EL>;u38Dbw+yt#+$(_`m4-X<eXz7<C<iEs zZIxD-5o%o>!Z7KLP;yBuS*Mgc198Ln-Qs@=rG1qoQKp2f=RwzgXaYava586H`!ZZS z=4#w~$_&=b{VUE_CVzl{^=2IxFvyVRyguM(4gvsCA5%tmkgj-Fd+i3kF^Rk}3aC9@ z3~0LgA0zL%v|WI8=u`G3oe`^I0*E~#qZvpMymtGV8zuc*gNgrJpiMN+61AT=kamh? zw?s#i^^?mt*QIhqX~{Zli73rDs`qT0dF$4F6o}L$i+E93=6^uH7A77=imb8~GejkS zx)-QgBbQR!MzBD-b9yE3Ol8fzQe++f$>?DA1|Nvv&$gYCPq@lNl>#;~!-SoJnl#4W ze`V7G!gn-k5c@;5132e&T?xm;2E@xRQmmH7NH5FF+waO)uNZC;PY6x}sKl-9yCSbV zYsvZ_JE{~-M1M$y=h?PhsiEg>bZ3&+UZ(DwBCMp0W#tIGfD6!Nmwg@#BwI$9ic)=M z`^ePqD|`s6+4kjozkV4ju1PX)+VCwOE=82f;^x=NXC<)qYRGOV)*;5vuKyGUW$3O_ zOi^UhmRA_A^cs*okVU$hoLmcS0V|J%6VN9b=+?adaDROkT}-o`aFo_DHJT(;{gPCX z4M9Xw89~C3>oTG`wIp{&KOo~)ozQJaAd)k0Uel9Ht|<?V1)I=`+FVOqN;W6kNuL=A z+MpbjHHhdaV5imtQy4Kub4t*PoS+uO{jX<5@>w@IxISABdr8o#Fg&4qp|0csy)0Px zYjt%eJ%6Jtpp1`wCZO7wfrQV(Ch0xyUY0;W7gp_^4(n~JQsL>o*xj_`+e++6?^(O+ z<kDC7oUUFp8tWD!uB;^yM=*JWNO7BnB|RV%4tZ<uV)$u}W)d5RVB2Ri8W_i=N)hv` zFk}pE)%_*R9dm&eFouo#`=lxBUUg^jopQq-z<;4kyMeNFk(gLmcPSl|$AH)JD%1qA z1aZ&uTMjRXA@ebRF^DU`rFN3JTCEIyIO*$1B@)nRtuX-S=olJ^K->d-jA1QJR!1-k zL+&R(g!vj514BhwD_D-rph^Y*0Hu+?Mzz(_fux%nY2`RMH&&v*h;X$q_NM5e+H05Q z9DmxouEg$}>F0x`5k?GX(-z&JvdIwCs|Ce=^03j<99>0;w+^7Jk^3ekw8_Us;MlH7 znUvRWXxoHB+#l}AN!lFQ&p?gcvEgx-Cz_&U2_)x#U${6N0Q6Sx{djv4>rQQNo3GT( z=#lH#+ZQY4TevO0v#R2fSLnleDW+|{Mt}K~LX!J4Xz8;*Bd&PR8C;^#<+fZlh-4M| z;rqXS-Zyml?^HX|19fHQEljW0`A`eQo9|=ox3Wm~wc&jOQGDB{{FyiJ-Wl^cwq0A~ zQvQ3TN451pV`a6+qAqwWjZ7wcbCYunJNVux)4BTwU!mN@zn)8w?hy||Svac@D}QkF zfIUB~%@J+mPD#AgV|3KrAz4yuhJAuc#A&xfXw<UHdltu)*~x^VTE}0fR#I<eUZ}c> zoO*cLf@ecIqvumen4Dzn*J-fwDBR*nf#?53>9Zn6mPw#2{W+MRj|J3YDOfp2l1kPt z2&O~o$5p;~qv^@ZzaDAgp9o6#B7a5a(Jh)<`h139zEG_=j^v|3ZWABI$`K8oO<3R_ zMU|y($;AffB`0awK-3z=yxT%8Y)6+Xc}hhy7~B4{q6)DWD#9w!>Nen@SVaoCW2KdU zIh?+OTt2KbB`H2yud_SvJJrX6e5+Q5xJ*^hV)U;<rNOLEDG^7~CGj}*8h>F%efCn8 z-4}AjgLaVQa>-xb`||AyKjiv{f2TyX5Ai|;c8j+2rWR<c2{BuBu1jRv^1J$9W}oS* z;d5rugjnI!1sZ*yCTPx%0E<P@rqo9|=nCMBeToPwf2i|J&xwW!73cnICI9D0yUL5Y z4<Zh?{^qVOvMHj-y1kmqQh(9Y)7nY}Vup|KNl`YGm&ss)J7QDrcQ&`SP$g~<4dY3n z>`=Tzi5#<(Ci{1oN&_Bp0l3Y8pfr7v^(%jVcp>Pcf3t@%g*f<_8H&*#%&!^aPfdCd zcnIJ?xS)7_t$gl{;=4ALg*4|mXncBSnJE&a#jA>QHR6Gu&p|a97JsGXP(_E?8_r`X zT1Y=oeSaUV{(2|ASD2};QmJLHT%E?p51P7Q0T16qbY6iD^TuhU?)+=d^Ao|kef2kF z2TV)~4iHBsq@zWvYo@4eCL$ut1b-<EX~{gAA{m`<4fAJf1->9m6vWjuQBrTJDnI{s zz+CWA;7<7TGc2MW(tm*>p$Ndl>1Z%a13S(KY`doUD*)qvtYrfPzE;olYQc>kBy-JO zJZ1<eq1%5nJ9?kTKVegFd6=wykNTz0xExIG?Mx?w5X9Vn7(~r-?w2o+WeQXkOOZxT ziJ03p3H3lX?~Yw4obU6@B{Tj8o@SiTroDa7oPl#YI$nqv)qhReHsQa`LgNWrN5Q09 z7-`SnpjMZ=^ezP4woCr{&iJ$EVHrVD`2F7%o&Fy<jg?_GR=LV$A)RHYxu>H5{al4M z%v7+D<q8T$eAVz|a+v#%rX#kL>4waR$<60!Efmlx5_3!ape?>uKaV#KihfAHXFLRJ zbtm*_Om2jJXn%#F!=>_44ld&FPGIAPaEmV0>%c7k>U&MVzLIf>AIQ`j%sqsrYjLLg zq89Ec5qa{IVRbFkqkBD7{41c6VEgy89QE|Pc)p;{vSdUS*W}KF_mmOD4CbnNpm81; zciRo%!+P3M34)kDL5@#^S|+FKY4+I+$UH-XMK~1N$bXt)ui|Y+KAHqM<ce|CNO?%i zEC(cRy26?jGqqj7q&TdIFYnEpk;`Zu9j+2C&XL1jp?>_t-)s=n>UWV#2|Mz+uHg-T z!ziGKKwR3yb=*3{x!9m#uLt*fQ-OL6uZg1LWb6t?_QdTeh(7DY^RD_D6$!{s7UMj_ zXiZH)Cx2o0vpZW)9LU_CAM5n1(+YV6`fT`2&!pCo+vcaiN$KZ2{GPqieFH*09anyW z;~4iUY;NAr&hG<cf-*j~A_TqpG-EYiZ>A@`BPC3w_I{L$86H}aW<4vG0c)lp#{17{ zHN(9Li%xadcFsd<ecx&}cAk4_#`=4M7IV}f=6@%}O<NzKsM-h)y1Fe(-y3Bl6o@sY zGL<JG%rz9Tsgy^~k2G*mhDm}Q06VhvIxv%r{u!^EDPsBIz2`a@{~DgEsGE%LVep^H z=*~z$E}#~L;36|M=sT;j4H>+x%Fz(HU;|cC#%iY6&Jy7xXVC*P_Jc;}vg%(~CbA8I zmValc!_r{Io~3|`bVuo}#1c#opis%nMyAf|w+P5|Ky<vGO*Cucgy>Q5V!i~lhiNQ) z)fhomkh4~REe7sheNqFm-uo$%@D{DQ7_A~R9xBB1buV25%h!CR&a_~#z*<VDb}u?f z^|)$|^*6pVfD2c>8K&w3qx0V>f3b$Af`6lf%DUjSxy#8I`l6t0QhoT8Cg_xGMn?pl z)>#^kT4QVWI?&7SK(epr*rnX*+3LrZi3A3)^3KtLQ|W7Q@Dqpt&8BW3K=Z6i4vaA; zQ-<`XH-Es3u`kG*j8qlqa^P5+Cd%cNINDL!DQQoGoxr)#*@mP5w)LkvtJVwBM}HF} zL3BJ|ZyozDf*4?160YE}svFsB*3fAzgEVzu33d8bQt!O|P>#-h9`@klI1pC93sfm4 z`W2#RM;pTMf0zv<Di1^fA$=g-{0IiB|6RltBx3m%;<H%YDCeA3gA?#emlhORfYxh8 zEbmA3_X-)VOJ1xRX7nmU{+?v=tAE=N03ADiS0d=CP~5aY;>2C)B~CYl+?G{<>@78$ z@PLQ8FX8IMsLPN5KapSo%U7-k^rciR<iQr6CU!oV1O{axUNW+x$BSZ&D3Y;OlhoZs zqJ;3u?xBYp$iWVxe~KHSnq)rx6RP!7tY&a8^k$VqlQMhmMcv4yITW_{gMTDu$D?-Z zxN=(Hd%^>2?j*?vA1{I$G+f3~%{)OWHslFGzTVh|;8`Vu^IorZ$D+EZoWh&nR-CEL z@T=*KLsO`Uop0UO)lB`0C1FUCM*uOjP@*0Dn+MVEmMIC>h#UL1PA@)Qqm!`hBhjUW za~hdVtw}N@14bQW^FZ?POMgFgX`spMxGD|Xh*)JF^=@ujRLBA<^2+3;#FgJ=V0Q-S zI@})FYGNzI+AVg3!;LPnXYG0TThaJ|g4CCZlT`c^<~TR`6_++QX$$GgC*Lx*UYP2t z8~Ic<MkIZtXblJ#kI?B?|K}k}-#OK$nbi;DCwhj?U}O)tx^a9L8h_^=%(t94VA=l( zD54@z#E}5(LU2GZDYHa}ay4#W6<hXstA-ON82kCmKUFXJxgJfIWYS}gXnOvRr{3=N z#JY91L;q?jg_Lb+<%ZKU!d5vcT-wTyB5P-Ltn~Csc-}d<8YssxSib12Mt11ip3=Ky z$KWYw<kyglqrlNs7k{#qAnMr!74xpp^1AVAGZk(MDzk+F>pFt>n(RyRBAe6c?>I^A z`W$+~Ydh;shi(wP1IGw(hrDpgoE{3EshUIVgE5TW4C7s;XCy0!A8m*!7RA3bpLYJF z@e{OG&7hL5yy(iMYjM!RjRWDZ@vQza;DWeARUf;xUHzQew0|3%x-FrtMBu>Q!2Cm? zv4gULpH#*4B$W{I+e4P+U9kH%<dX;D*9))yE#+XTRaKgz=XIjfceXuUVK(H&_V+tx zceBGY^l9JHOxoL0wH>R&6$#5#rvl=h0bwe~t;oXls|y%HBEBA+zuGunDZeiKPRlm` z8lWj;P8bUmrhm9f4MTOvl+rAyOR3QhJa&J(&ynq-N|KPruIjSJi?(iMS(r&R${XjK zbCXNF<@s~^%igTntgHl49hId%dcb?`WEtWA%Asv&Oa|8Y)M#-f3$IogasZVnlxaO5 zq?bf%?AsF|#?)fyvy#ci1?HWc*^6#s0GaHTj+Ump$$tQP7`k~}hD0mxQCUX)_uXJU zw+|MQ!BDZ*NyOgPbZvbEhUrg7AO{(&aI02KxxRIeWIuHiET8t66@U9QrAc4GIej+8 zjAwg3;5QYK9cf1>iq&ClLDj9eTylP4`pC+()-N?8#d%E=cc9=-$RE>xJcvpa4m~?r zI^X`Xj(>Bzu}g#F(lcG$x2#a=#BI{h<!R1JK#c-2kE>iwIiiPTX%-*MChKo=S)lWU z*qYDh+mG%Psw)p(MXue>dBkEIGl}`Rm8e*00%TqL>9X8r5N_h4#}Z2k-t*rOa8!md zNFV!M@8s9QQYe>P3mlOX_xQ>Fn=w@f5_-T~(|`U!Pd0}xAQ5nnl;DCOE}Ji)um18x zaI%?8e9-}%iMF(|EQ_4qACS<)@QGl&UD$k#t#JK&jEZ#da<?^4PSJv;&*K>bmB|4* zfvI!BCleW4A*!OcHN4Sx&PBX(f=(HZ&EIb}KaeN;ru6>`twZHQ!fF8=++$Hd(kK%1 zS%2gep5(GlRmZDdVDZB29(j6~RJ7$9txpXLJOwk$l};6BOeJ{X;^XiPm&kkyF+W`5 z$7;Pkfg;Cr26~`;)Je{E9x8G*45^BfK+jqA;YPiNBKasa?J6>Ue|=x^GIX7LjKYVU zMGvk>Vqe2q?e;yb`DSkVR#UkDGdN}{$bXP<rTTN8KmAg>PWY1^GUoQyVHR5ge!}=1 zcCl<+@{-#m?=rA6B8GOEZ`D?WM22}ZO$kCg5%cy*MC_-1!N#NP*{3r{oZx&wr(<Si zL}`f-izy0Ic6?+m%E}bEp+?7M{t8&8R~~Jailo|p6Q{Kw{DsBTX3AKaW+XK&D1V7i zt7p+^-34ZISYc-9Hb!l!B82vE&mZ7Swo1IGJDcn1k?4!&=<KMNuO5HN*nD+wPWnoK zKYbFu8X&NSFyaubEUEA%I5$uA-X+e4$VgW;ta{|0oMclMSs<?^dJEk<TWP(@PSEAq z4Y^#HM)`sduF_iy2)3~Z{EDW_ynp;7!1(c9T|hplriIX=9FYLp!25^|w3hen^b|3p zvW+xG(FzBdxrv?*9fa8#b}A;nt!fvRq2lO>9xidoDOhOxYQh}*LxD5@JGznyR3o~! z>o=?Nn;pxzV~Ub)0h5a7FFPJ^l1@tlJfSSLcy(&vjV^}d-cy8sJcvm#s(*5VXZ+`i zT9w9e8VBS&<+b0W0|o+9m^3L66yZi0eFP<hT#|K8F`mh08fH+j9YJuMxnN=^L1`8H z;z=9eqT8x#fU<8LLaUuZ#~1RI9C|D$<w!Nij2YiC^Hx3u1IK0Z__*7}rmw2ZC$%%6 zA5+LP@25s4`koII(G20uqJMT#I(E@JlV3|gtvh3bGgZ7C3jex8Sa0T@yQ0nf{*>xv zcdjg&uIa4#duz<cvKAt0=Emq!gDE-siBClm<h8=}m@&VN?k}X-Il{yl8O_cR^y9RO zmPdF;K_3&fFo$Q0O~sh{<ck#PR7{&+$bZv2y(t(1JA&3G$8QE^=6_X|W9AgsvKcE$ zvR*FRz;#5T5^KBl&WN#81G+T&u`pI?N_>qSO%O;o^55?H3Ak4cLxnt^_vZq}w_9%^ zQ_(7U*g|yP-f<os#GKo&r~h|1inLT)wW`6%H_8FIhxzb`eOvx?ds=Skjb!{QX;f7Q z$!1tbaZKh8S7rMW=6}37a7K1fxsEgkGy;!kIawqUSowzOiNkd!7Iwj<GSHCU@^2cC zeJIo5qA;nI&`ZZ1l7+N(74*>akg*a%z34}@kTR5?sie91O_V+ZLL_SM7D_5SJFOWU z(GGM6Y<X?Yxm>3YFCZNpB@{aZROU3F{2MF){T{IpohlB?K!1&VB;pwgq%8n~ZjHU8 zK8#_1^YPtue1Y+%(}R>NG7JEkIo`w!I%tw#Lfm-{Gx9f==(ZCJx;#QceI#n~S0B6b z{Gz?J{~MvJ<%Ik#n3rfZAI<{M=WZw_tI!Jiqi^fr56JFoNZbOKj|1R<lpycZ8lZX8 z5WOv{np$UvnST!6va1K>IJ6onP6&3K){jgu+;(UbEA%DDJ2)XX15;NdJ_uKNs4igI zn%KObF<BDmcp(nz^ev-?CcZD>3Dn2La!r00s6cpr{e@<keWQ(S>M(C~1Jj}cJWEmz z=_^{7<XrRJ#=JpzEL*91r`YP&yYcELiHtWo^LllxWq%sB5`cZt6|}gFh>cJVyzY7R zyd9bg-||czin2$a<lufy?(XNHkzmA7!1WOxuRq#+-=l-?n}{y97BGYiuz=M>-Xgp` zqfsmq0Q={{9<%A7f8Z@lA%+--v9R`@A;iPFqj<qbYbOxz`h4d(qKG%A(-PL1?pY7n z;tdIjB!8b2Hm?E3g*1{YfeJC%*C^=_cMJ3W-?BT*-~KW3?xBm|b{i`CKiWy-Ao5bN zviF;n4|iFT7PAW=AkT&<TfQO1f)!>S)Y`Vm#*30nc8V5J=!m|oxflJ`tKbmIL?vAh zR_QQgODTX-)!uoKvf|W?Ksw@RN&c7vu3u?ZBY&F|7{g{noVg?vQRlH+@}3AD>+NGY z!LS`+QX&t$NVz8GbLW%RjTZ)R=v}f%PMJe*-t8X6MwT-VBWgW6JBr2?jII6V^kjpq z#=(nq=?JBu@wapkazAB=sXF1Iin7Gpq`{6$R{49lodnylfMrrYM^_JM4wKgG&xAZ6 z!+(%A)MK`Xbv*w2Sxlp&UptW^Ax%K)t*pir7RAY3iA;8>4fJYrhvjqsw7U63Qpo!^ zX=<l=yWY-Q*o<RyUX=U<u}|3~BKuqp+EATgPQN@$jC8#pGL!z(im;}MTTfq}g_N3D zA-f70_Wvj@2;i@RNJB*S5o%9OZ_^A{G=B>8%UsSBfYuN3Q8U#|me&4D06?W`8F~wr zbD2QtVQVw7$2m$qk5yz2AmNlUv9IJW;SK)=ML9m8PodFik}j0u8<ssAMewH18HhG~ zc4y!>6x<~yVr`RQJg!Cl$yjbFekZ3YSyS*R)h)#0L}llZ*#TH@z3Wt>bJ~PfYJZSH z3EVF(yEv<imH<`VeY-2fnZvZ?!HQ)3*ZvTjnozrvFDR6S7rGboa1LdEs9vRGn7AKQ zQpV+o%6;VA=u9>6ti^%i5&Fs+eQAZYn9HdHrnyMeWz`c3f5G`Iqa4g=sQ3bOq5fMV zJ}1S4RZMP8NWvW%^Fvr%%q>EeMt|B{SeEwN0Jw}+aL#<LNC86Mu*Q#R)D4xF`n8zb zQ06YYhoFqmK%y>cc#QCQW<{ng&QA^Mib46Yj3XY=s-9bfKbOGK>u+&1*N{z=e6{2~ z5XHeqOnqi$Ng9xWp{%Z!`4)~EUz@!L`&;PUbFe@wZX;yZPIc!!7|^XY+J7;K;l?YM zD%YA7ke=WBXa1&h3evR-0Pbiq`{|ud9%v>(JewTcZa`dmLnE?AmE0cC=C{yhRlRAn z>QQF038Xt6l-Y-FI7CAL<R3gL_+72GK*x+A1b6=8V}f9cWQPW^)?|TW?In}>)N@ne zD>%kR{L<S4qi60ps8%)++JAF=6;h|wv_jfvd}Phl%X3uXs9qZ)c#J6I1i8cfWfV6S zl1SqWHo#ptD&(Vg<#qM#Dt+k2yVGxvHZpOp_bivpri8!tQpV@05@)OiIyr;U%Zfch zP}LL8E^i$%pV<m;NVJI?$ADo5FU^2h@W1Jd$C7Uwlb4I6%5fKr_kaAP@cFE5qzHc< zNKWlC7IEsK+<yrSPf1K+U=dPfoO`4Y%D^te0C`tfE$Ry@NX(j%fp)@lmPrC5Uqc!j zt?Nj9X3frV1WoC$bvZ;*B~C&TYiw1)ioDgqyZ(O;Up1lnn(z%}mXQ7)W%ik@TD5As zcSX~-MqKi&dH$#5ynpn2F9M@pPFd0Zk-6@EwvislgLd<$_P}y^fyCn{jG#8wKk}x) z!i@mdECHpt2e6Kp_%O937dh}P>^W(1!}j-qO+a1%W)8{?L*yjBjWXDl5LA(Ku__fO z?!o}y#OGTImuf#F0zMnmaTXMH36&4)kFK)HaxdyLT2uolC4Vq^cU)^dI+$g|*iFdR zfHp4It*zd1uHS>oefS0gz1#;Q;kIvZ$~ogaEO3^r#M$#4m6Jt^D9U>2^SLT@{>rWq zYdKgy%=&rpW#pb}DD(|xm%M$&<|+c7>oJ7UyIaIr#MBCPy;l22*vb@A3ev!6R$lte zN_=&^yx1#D>3{H5Qs{Z|e%pz)Nf@HSNvZ6QI0;`B>f`zaymq^}GBP{RvuRb2zD4)d zs`F}*-<`tZ<y|s6K)#yqL%kIgrrs^<^iIb>AJcfv@ChesNTX3>Q1~7#cp5`RZDzwd z46naVAw!Fl7Eb>+6b<uezle-(7pAS}6s+EA?VmJB_kX95!2@1RmsjbBOG*;Z{%Bi+ z+RARZh^8B2cd!hKZe9En^5Q8O?27Cc4$>hTPwFTubdLV*iU8NL7≈@_W1vf49F9 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 NXZr#G00004Sz14|J@Wtn diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index af5b9c96786..7338f8bdbec 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -12,6 +12,36 @@ git init -q module1 git commit -q -am c2 ) +git init submodule-head-changed +(cd submodule-head-changed + git submodule add ../module1 m1 + git commit -m "add submodule" + + cd m1 && git checkout @~1 +) + +git init modified-and-untracked +(cd modified-and-untracked + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 + echo change >> this + touch new + ) +) + +git init submodule-head-changed-and-modified +(cd submodule-head-changed-and-modified + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 + git checkout @~1 + echo change >> this + ) +) + git init with-submodules (cd with-submodules mkdir dir diff --git a/gix/tests/submodule/mod.rs b/gix/tests/submodule/mod.rs index 1cdd27a9458..4aa6b5cb442 100644 --- a/gix/tests/submodule/mod.rs +++ b/gix/tests/submodule/mod.rs @@ -26,6 +26,7 @@ mod open { worktree_checkout: true, superproject_configuration: true, }, + Some(false), ), ( "dir/m1", @@ -35,6 +36,7 @@ mod open { worktree_checkout: true, superproject_configuration: true, }, + Some(false), ), ] as &[_], ), @@ -48,6 +50,7 @@ mod open { worktree_checkout: false, superproject_configuration: true, }, + None, )], ), ( @@ -60,11 +63,13 @@ mod open { worktree_checkout: false, superproject_configuration: true, }, + Some(true), )], ), ] { let repo = repo(name)?; - for (sm, (name, expected)) in repo.submodules()?.expect("modules present").zip(expected) { + for (sm, (name, expected, _expected_is_dirty)) in repo.submodules()?.expect("modules present").zip(expected) + { assert_eq!(sm.name(), name); let state = sm.state()?; assert_eq!(&state, expected); @@ -87,6 +92,19 @@ mod open { "there is a way to check for indicators that a submodule worktree isn't checked out though" ) } + #[cfg(all(feature = "status", feature = "parallel"))] + for check_dirty in [false, true] { + let status = sm.status(gix::submodule::config::Ignore::None, check_dirty)?; + assert_eq!( + &status.state, expected, + "no matter what status configuration, the state is always obtained" + ); + assert_eq!( + status.is_dirty(), + *_expected_is_dirty, + "none of these submodules are dirty, but some aren't checked out" + ) + } } assert_eq!( repo.modules()?.expect("present").names().count(), @@ -97,6 +115,146 @@ mod open { Ok(()) } + #[cfg(all(feature = "status", feature = "parallel"))] + mod status { + use crate::submodule::repo; + use crate::util::hex_to_id; + + #[test] + fn changed_head_compared_to_superproject_index() -> crate::Result { + let repo = repo("submodule-head-changed")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + let mut status = sm.status(gix::submodule::config::Ignore::None, false)?; + assert_eq!( + status.is_dirty(), + Some(true), + "we could decide that the submodule is dirty" + ); + assert_eq!( + status.index_id, + Some(hex_to_id("e046f3e51d955840619fc7d01fbd9a469663de22")) + ); + assert_eq!( + status.checked_out_head_id, + Some(hex_to_id("362cb5539acbd3c8ca355471f97c6a68d3db0da7")), + "the checked out head was reset to something else after the superproject commit" + ); + assert_eq!( + status.changes, + Some(Vec::new()), + "the status check ran, but there were no changes" + ); + // make it easier to compare this as baseline + status.changes.take(); + + let status_with_ignore = sm.status(gix::submodule::config::Ignore::Dirty, false)?; + assert_eq!( + status_with_ignore, status, + "The lowest status that makes these changes observable" + ); + + let status_with_ignore_check_only = sm.status(gix::submodule::config::Ignore::Dirty, true)?; + assert_eq!( + status_with_ignore_check_only, status, + "dirty-check has no observable influence here yet as there no 'more expensive' changes" + ); + + let status_with_ignore = sm.status(gix::submodule::config::Ignore::All, false)?; + assert_eq!( + status_with_ignore.is_dirty(), + Some(false), + "no dirty-information is retrieved, it seems clean" + ); + assert_eq!( + status_with_ignore.index_id, None, + "to avoid false-positives, we don't retrieve the value" + ); + assert_eq!( + status_with_ignore.checked_out_head_id, None, + "this check is ignored as it requires opening a repository" + ); + Ok(()) + } + + #[test] + fn modified_and_untracked() -> crate::Result { + let repo = repo("modified-and-untracked")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::Dirty, false)?; + assert_eq!(status.is_dirty(), Some(false), "Dirty skips worktree changes entirely"); + + let status = sm.status_opts( + gix::submodule::config::Ignore::None, + false, + &mut |status: gix::status::Platform<'_, gix::progress::Discard>| { + status.index_worktree_options_mut(|opts| { + opts.sorting = Some(gix_status::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + }, + )?; + assert_eq!( + status.is_dirty(), + Some(true), + "we could decide that the submodule is dirty" + ); + assert_eq!(status.index_id, status.checked_out_head_id, "the head didn't change"); + assert_eq!( + status.changes.as_ref().into_iter().flatten().count(), + 2, + "1 modified, 1 untracked" + ); + + let status_with_dirty_check = sm.status_opts( + gix::submodule::config::Ignore::None, + true, + &mut |status: gix::status::Platform<'_, gix::progress::Discard>| { + status.index_worktree_options_mut(|opts| { + opts.sorting = Some(gix_status::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + }, + )?; + assert_eq!( + status_with_dirty_check, status, + "it cannot abort early as the only change it sees is the modification check" + ); + + let status = sm.status(gix::submodule::config::Ignore::Untracked, false)?; + assert_eq!( + status.is_dirty(), + Some(true), + "we could decide that the submodule is dirty, even though untracked files are missing" + ); + assert_eq!(status.index_id, status.checked_out_head_id, "the head didn't change"); + assert_eq!(status.changes.as_ref().into_iter().flatten().count(), 1, "1 modified"); + + Ok(()) + } + + #[test] + fn is_dirty_skips_expensive_checks() -> crate::Result { + let repo = repo("submodule-head-changed-and-modified")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::None, true)?; + assert_eq!( + status.changes, None, + "computation was stopped on the first detected change (the index/head)" + ); + assert_eq!( + status.index_id, + Some(hex_to_id("e046f3e51d955840619fc7d01fbd9a469663de22")), + "the index id was obtained" + ); + assert_eq!( + status.checked_out_head_id, + Some(hex_to_id("362cb5539acbd3c8ca355471f97c6a68d3db0da7")), + "the checked out head was also obtained to be able to se if it's dirty" + ); + Ok(()) + } + } + #[test] fn not_a_submodule() -> crate::Result { let repo = repo("not-a-submodule")?; From 4a4989d5170173269dcdc19890827911d13e7a89 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 7 Mar 2024 18:38:30 +0100 Subject: [PATCH 09/26] Add submodule support for status iterator --- gix/src/status/index_worktree.rs | 81 +++++++++++++++--- gix/src/status/mod.rs | 4 - .../generated-archives/make_submodules.tar.xz | Bin 26856 -> 28940 bytes gix/tests/fixtures/make_submodules.sh | 16 ++++ gix/tests/gix.rs | 2 + gix/tests/status/mod.rs | 46 ++++++++++ 6 files changed, 134 insertions(+), 15 deletions(-) create mode 100644 gix/tests/status/mod.rs diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index 4739544daea..3a0a88aef4a 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -174,6 +174,10 @@ impl Repository { /// Note that depending on the underlying configuration, there might be a significant delay until the first /// item is received due to the buffering necessary to perform rename tracking and/or sorting. /// +/// ### Submodules +/// +/// Note that submodules can be set to 'inactive' which automatically excludes them from the status operation. +/// /// ### Index Changes /// /// Changes to the index are collected and it's possible to write the index back using [iter::Outcome::write_changes()]. @@ -200,6 +204,7 @@ pub mod iter { use crate::status::index_worktree::iter; use crate::status::{index_worktree, Platform}; use crate::worktree::IndexPersistedOrInMemory; + use crate::ThreadSafeRepository; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -420,9 +425,7 @@ pub mod iter { } } - /// The status of a submodule - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] - pub struct SubmoduleStatus {} + type SubmoduleStatus = crate::submodule::Status; /// The error returned by [Platform::into_index_worktree_iter()](crate::status::Platform::into_index_worktree_iter()). #[derive(Debug, thiserror::Error)] @@ -434,6 +437,8 @@ pub mod iter { SpawnThread(#[source] std::io::Error), #[error(transparent)] ConfigSkipHash(#[from] crate::config::boolean::Error), + #[error(transparent)] + PrepareSubmodules(#[from] crate::submodule::modules::Error), } /// Lifecycle @@ -454,7 +459,6 @@ pub mod iter { let should_interrupt = Arc::new(AtomicBool::default()); let (tx, rx) = std::sync::mpsc::channel(); let mut collect = Collect { tx }; - let submodule = ComputeSubmoduleStatus { _mode: self.submodules }; let skip_hash = self .repo .config @@ -464,6 +468,7 @@ pub mod iter { .transpose() .with_lenient_default(self.repo.config.lenient_config)? .unwrap_or_default(); + let submodule = ComputeSubmoduleStatus::new(self.repo.clone().into_sync(), self.submodules)?; let join = std::thread::Builder::new() .name("gix::status::index_worktree::iter::producer".into()) .spawn({ @@ -568,19 +573,53 @@ pub mod iter { #[derive(Clone)] struct ComputeSubmoduleStatus { - _mode: crate::status::Submodule, + mode: crate::status::Submodule, + repo: ThreadSafeRepository, + submodule_paths: Vec<BString>, } + /// mod submodule_status { + use crate::bstr; use crate::bstr::BStr; use crate::status::index_worktree::iter::{ComputeSubmoduleStatus, SubmoduleStatus}; + use crate::status::Submodule; + use std::borrow::Cow; + + impl ComputeSubmoduleStatus { + pub(super) fn new( + repo: crate::ThreadSafeRepository, + mode: crate::status::Submodule, + ) -> Result<Self, crate::submodule::modules::Error> { + let local_repo = repo.to_thread_local(); + let submodule_paths = match local_repo.submodules()? { + Some(sm) => { + let mut v: Vec<_> = sm + .filter(|sm| sm.is_active().unwrap_or_default()) + .filter_map(|sm| sm.path().ok().map(Cow::into_owned)) + .collect(); + v.sort(); + v + } + None => Vec::new(), + }; + Ok(Self { + mode, + repo, + submodule_paths, + }) + } + } - /// The error returned by the submodule status implementation - it will be turned into a box, hence it doesn't have to - /// be private. + /// The error returned submodule status checks. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] - #[error("TBD")] - pub enum Error {} + pub(super) enum Error { + #[error(transparent)] + SubmoduleStatus(#[from] crate::submodule::status::Error), + #[error(transparent)] + IgnoreConfig(#[from] crate::submodule::config::Error), + } impl gix_status::index_as_worktree::traits::SubmoduleStatus for ComputeSubmoduleStatus { type Output = SubmoduleStatus; @@ -589,9 +628,29 @@ pub mod iter { fn status( &mut self, _entry: &gix_index::Entry, - _rela_path: &BStr, + rela_path: &BStr, ) -> Result<Option<Self::Output>, Self::Error> { - todo!("impl in Submodule itself, it will be calling this exact implementation under the hood, maybe with reduced threads") + use bstr::ByteSlice; + if self + .submodule_paths + .binary_search_by(|path| path.as_bstr().cmp(rela_path)) + .is_err() + { + return Ok(None); + } + let repo = self.repo.to_thread_local(); + let Ok(Some(mut submodules)) = repo.submodules() else { + return Ok(None); + }; + let Some(sm) = submodules.find(|sm| sm.path().map_or(false, |path| path == rela_path)) else { + return Ok(None); + }; + let (ignore, check_dirty) = match self.mode { + Submodule::AsConfigured { check_dirty } => (sm.ignore()?.unwrap_or_default(), check_dirty), + Submodule::Given { ignore, check_dirty } => (ignore, check_dirty), + }; + let status = sm.status(ignore, check_dirty)?; + Ok(status.is_dirty().and_then(|dirty| dirty.then_some(status))) } } } diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs index f3cf1abce54..dcd23b67234 100644 --- a/gix/src/status/mod.rs +++ b/gix/src/status/mod.rs @@ -64,8 +64,6 @@ impl Repository { /// Whereas Git runs the index-modified check before the directory walk to set entries /// as up-to-date to (potentially) safe some disk-access, we run both in parallel which /// ultimately is much faster. - // TODO: if untracked and ignored entries are disabled, don't run a dirwalk at all. - // TODO: submodule support with reasonable configurability. pub fn status<P>(&self, progress: P) -> Result<Platform<'_, P>, config::boolean::Error> where P: gix_features::progress::Progress + 'static, @@ -83,8 +81,6 @@ impl Repository { }, }) } - - // TODO: submodule status, where the base of the operation is the list of submodules in the .gitmodules file. } mod platform; diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index 13238821c5177ab2f7008a84d00b08c105e158a9..bf397457db46deb62527f7e6811334269b0ff7cc 100644 GIT binary patch delta 28890 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 delta 26790 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 Z(E$^HoM44Eb1AXJXZr#G00004Sz4!{9qj-B diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index 7338f8bdbec..d6e03a64e32 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -42,6 +42,22 @@ git init submodule-head-changed-and-modified ) ) +git init modified-untracked-and-submodule-head-changed-and-modified +(cd modified-untracked-and-submodule-head-changed-and-modified + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 + git checkout @~1 + echo change >> this + ) + + touch this + git add this && git commit -m "this" + echo change >> this + touch untracked +) + git init with-submodules (cd with-submodules mkdir dir diff --git a/gix/tests/gix.rs b/gix/tests/gix.rs index b4b71c9db7f..d47cb2ccf07 100644 --- a/gix/tests/gix.rs +++ b/gix/tests/gix.rs @@ -16,5 +16,7 @@ mod remote; mod repository; #[cfg(feature = "revision")] mod revision; +#[cfg(feature = "status")] +mod status; #[cfg(feature = "attributes")] mod submodule; diff --git a/gix/tests/status/mod.rs b/gix/tests/status/mod.rs new file mode 100644 index 00000000000..d2463c7eb80 --- /dev/null +++ b/gix/tests/status/mod.rs @@ -0,0 +1,46 @@ +pub fn repo(name: &str) -> crate::Result<gix::Repository> { + use crate::util::named_subrepo_opts; + Ok(named_subrepo_opts( + "make_submodules.sh", + name, + gix::open::Options::isolated(), + )?) +} + +mod index_worktree { + mod iter { + use crate::status::repo; + + #[test] + fn submodule_modification() -> crate::Result { + let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let mut status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())?; + let items: Vec<_> = status.by_ref().filter_map(Result::ok).collect(); + assert_eq!(items.len(), 3, "1 untracked, 1 modified file, 1 submodule modification"); + Ok(()) + } + + #[test] + fn early_drop_for_is_dirty_emulation() -> crate::Result { + let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let is_dirty = repo + .status(gix::progress::Discard)? + .index_worktree_submodules(gix::status::Submodule::AsConfigured { check_dirty: true }) + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())? + .next() + .is_some(); + assert!(is_dirty, "this should abort the work as quickly as possible"); + Ok(()) + } + } +} From c20ad287128132cda995a47abac1dd18f415f02d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Fri, 8 Mar 2024 07:18:45 +0100 Subject: [PATCH 10/26] feat: add `Repository::is_dirty()` The simplest way to learn if the repository is dirty or not. --- gix/src/status/mod.rs | 46 ++++++++++++++++++ .../generated-archives/make_submodules.tar.xz | Bin 28940 -> 28996 bytes gix/tests/fixtures/make_submodules.sh | 1 + gix/tests/status/mod.rs | 42 ++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs index dcd23b67234..23726d514f2 100644 --- a/gix/src/status/mod.rs +++ b/gix/src/status/mod.rs @@ -83,6 +83,52 @@ impl Repository { } } +/// +#[cfg(feature = "parallel")] +pub mod is_dirty { + use crate::Repository; + + /// The error returned by [Repository::is_dirty()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + StatusPlatform(#[from] crate::config::boolean::Error), + #[error(transparent)] + CreateStatusIterator(#[from] crate::status::index_worktree::iter::Error), + } + + impl Repository { + /// Returns `true` if the repository is dirty. + /// This means it's changed in one of the following ways: + /// + /// * the index was changed in comparison to its working tree + /// * the working tree was changed in comparison to the index + /// * submodules are taken in consideration, along with their `ignore` and `isActive` configuration + /// + /// Note that *untracked files* do *not* affect this flag. + /// + /// ### Incomplete Implementation Warning + /// + /// Currently, this does not compute changes between the head and the index. + // TODO: use iterator which also tests for head->index changes. + pub fn is_dirty(&self) -> Result<bool, Error> { + let is_dirty = self + .status(gix_features::progress::Discard)? + .index_worktree_rewrites(None) + .index_worktree_submodules(crate::status::Submodule::AsConfigured { check_dirty: true }) + .index_worktree_options_mut(|opts| { + opts.dirwalk_options = None; + }) + .into_index_worktree_iter(Vec::new())? + .take_while(Result::is_ok) + .next() + .is_some(); + Ok(is_dirty) + } + } +} + mod platform; /// diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index bf397457db46deb62527f7e6811334269b0ff7cc..6498ecb2945fe775d2d9eb5410471e574844d3ab 100644 GIT binary patch delta 28945 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 delta 28889 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 diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index d6e03a64e32..740cc278ee5 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -10,6 +10,7 @@ git init -q module1 git commit -q -m c1 echo hello >> this git commit -q -am c2 + touch untracked ) git init submodule-head-changed diff --git a/gix/tests/status/mod.rs b/gix/tests/status/mod.rs index d2463c7eb80..6f39e56d3d5 100644 --- a/gix/tests/status/mod.rs +++ b/gix/tests/status/mod.rs @@ -44,3 +44,45 @@ mod index_worktree { } } } + +mod is_dirty { + use crate::status::repo; + + #[test] + fn various_changes_positive() -> crate::Result { + let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; + assert!(repo.is_dirty()?, "The repository has various changes"); + Ok(()) + } + + #[test] + fn submodule_changes_are_picked_up() -> crate::Result { + let repo = repo("submodule-head-changed")?; + assert!(repo.is_dirty()?, "head-changes are also discoverd"); + Ok(()) + } + + #[test] + fn untracked_files_are_excluded() -> crate::Result { + let repo = repo("module1")?; + assert_eq!( + repo.status(gix::progress::Discard)? + .into_index_worktree_iter(Vec::new())? + .count(), + 1, + "there is one untracked file" + ); + assert!( + !repo.is_dirty()?, + "untracked files aren't taken into consideration, just like `git describe` which ignores them" + ); + Ok(()) + } + + #[test] + fn no_changes() -> crate::Result { + let repo = repo("with-submodules")?; + assert!(!repo.is_dirty()?, "there are no changes"); + Ok(()) + } +} From c7ddd30fc9fde6cac55153fa8e7fd783c83b336f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Fri, 8 Mar 2024 07:09:07 +0100 Subject: [PATCH 11/26] feat: describing commits can now be done with conditional dirty-suffix using `commit::describe::Resolution::format_with_dirty_suffix()` --- gix/src/commit.rs | 35 +++++++++++++++++++++++++++------- gix/tests/commit/mod.rs | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/gix/src/commit.rs b/gix/src/commit.rs index ce5dee4d6f7..ee36c61cf28 100644 --- a/gix/src/commit.rs +++ b/gix/src/commit.rs @@ -40,11 +40,32 @@ pub mod describe { } impl<'repo> Resolution<'repo> { - /// Turn this instance into something displayable + /// Turn this instance into something displayable. pub fn format(self) -> Result<gix_revision::describe::Format<'static>, Error> { let prefix = self.id.shorten()?; Ok(self.outcome.into_format(prefix.hex_len())) } + + /// Turn this instance into something displayable, possibly with dirty-suffix. + /// + /// If `dirty_suffix` is `Some(suffix)`, a possibly expensive [dirty check](crate::Repository::is_dirty()) will be + /// performed so that the `suffix` is appended to the output. If it is `None`, no check will be performed and + /// there will be no suffix. + /// Note that obtaining the dirty-state of the repository can be expensive. + #[cfg(all(feature = "status", feature = "parallel"))] + pub fn format_with_dirty_suffix( + self, + dirty_suffix: impl Into<Option<String>>, + ) -> Result<gix_revision::describe::Format<'static>, Error> { + let prefix = self.id.shorten()?; + let mut dirty_suffix = dirty_suffix.into(); + if dirty_suffix.is_some() && !self.id.repo.is_dirty()? { + dirty_suffix.take(); + } + let mut format = self.outcome.into_format(prefix.hex_len()); + format.dirty_suffix = dirty_suffix; + Ok(format) + } } /// The error returned by [`try_format()`][Platform::try_format()]. @@ -59,6 +80,9 @@ pub mod describe { RefIter(#[from] crate::reference::iter::Error), #[error(transparent)] RefIterInit(#[from] crate::reference::iter::init::Error), + #[error(transparent)] + #[cfg(all(feature = "status", feature = "parallel"))] + DetermineIsDirty(#[from] crate::status::is_dirty::Error), } /// A selector to choose what kind of references should contribute to names. @@ -179,9 +203,7 @@ pub mod describe { } /// Try to find a name for the configured commit id using all prior configuration, returning `Some(describe::Format)` - /// if one was found. - /// - /// Note that there will always be `Some(format)` + /// if one was found, or `None` if that wasn't the case. pub fn try_format(&self) -> Result<Option<gix_revision::describe::Format<'static>>, Error> { self.try_resolve()?.map(Resolution::format).transpose() } @@ -193,10 +215,9 @@ pub mod describe { /// /// # Performance /// - /// It is greatly recommended to [assure an object cache is set][crate::Repository::object_cache_size_if_unset()] + /// It is greatly recommended to [assure an object cache is set](crate::Repository::object_cache_size_if_unset()) /// to save ~40% of time. pub fn try_resolve(&self) -> Result<Option<Resolution<'repo>>, Error> { - // TODO: dirty suffix with respective dirty-detection let mut graph = gix_revwalk::Graph::new( &self.repo.objects, gix_commitgraph::Graph::from_info_dir(self.repo.objects.store_ref().path().join("info").as_ref()).ok(), @@ -218,7 +239,7 @@ pub mod describe { })) } - /// Like [`try_format()`][Platform::try_format()], but turns `id_as_fallback()` on to always produce a format. + /// Like [`try_format()`](Self::try_format()), but turns `id_as_fallback()` on to always produce a format. pub fn format(&mut self) -> Result<gix_revision::describe::Format<'static>, Error> { self.id_as_fallback = true; Ok(self.try_format()?.expect("BUG: fallback must always produce a format")) diff --git a/gix/tests/commit/mod.rs b/gix/tests/commit/mod.rs index fd2440bf8ce..bdf951b5415 100644 --- a/gix/tests/commit/mod.rs +++ b/gix/tests/commit/mod.rs @@ -4,6 +4,48 @@ mod describe { use crate::named_repo; + #[cfg(all(feature = "status", feature = "parallel"))] + mod with_dirty_suffix { + use crate::util::named_subrepo_opts; + use gix::commit::describe::SelectRef; + + #[test] + fn dirty_suffix_applies_automatically_if_dirty() -> crate::Result { + let repo = named_subrepo_opts( + "make_submodules.sh", + "submodule-head-changed", + gix::open::Options::isolated(), + )?; + + let actual = repo + .head_commit()? + .describe() + .names(SelectRef::AllRefs) + .try_resolve()? + .expect("resolution") + .format_with_dirty_suffix("dirty".to_owned())? + .to_string(); + assert_eq!(actual, "main-dirty"); + Ok(()) + } + + #[test] + fn dirty_suffix_does_not_apply_if_not_dirty() -> crate::Result { + let repo = named_subrepo_opts("make_submodules.sh", "module1", gix::open::Options::isolated())?; + + let actual = repo + .head_commit()? + .describe() + .names(SelectRef::AllRefs) + .try_resolve()? + .expect("resolution") + .format_with_dirty_suffix("dirty".to_owned())? + .to_string(); + assert_eq!(actual, "main"); + Ok(()) + } + } + #[test] fn tags_are_sorted_by_date_and_lexicographically() -> crate::Result { let repo = named_repo("make_commit_describe_multiple_tags.sh")?; From 17bef301f2be29c8d0545b35d1581e57037e69df Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sat, 9 Mar 2024 12:57:07 +0100 Subject: [PATCH 12/26] provide a non-parallel version of the status iteration --- gix/src/commit.rs | 4 +- gix/src/status/index_worktree.rs | 346 +++++++++++++++++++------------ gix/src/status/mod.rs | 3 - gix/src/submodule/mod.rs | 32 +-- gix/tests/commit/mod.rs | 2 +- gix/tests/status/mod.rs | 9 + gix/tests/submodule/mod.rs | 20 +- 7 files changed, 259 insertions(+), 157 deletions(-) diff --git a/gix/src/commit.rs b/gix/src/commit.rs index ee36c61cf28..4101b1512ec 100644 --- a/gix/src/commit.rs +++ b/gix/src/commit.rs @@ -52,7 +52,7 @@ pub mod describe { /// performed so that the `suffix` is appended to the output. If it is `None`, no check will be performed and /// there will be no suffix. /// Note that obtaining the dirty-state of the repository can be expensive. - #[cfg(all(feature = "status", feature = "parallel"))] + #[cfg(feature = "status")] pub fn format_with_dirty_suffix( self, dirty_suffix: impl Into<Option<String>>, @@ -81,7 +81,7 @@ pub mod describe { #[error(transparent)] RefIterInit(#[from] crate::reference::iter::init::Error), #[error(transparent)] - #[cfg(all(feature = "status", feature = "parallel"))] + #[cfg(feature = "status")] DetermineIsDirty(#[from] crate::status::is_dirty::Error), } diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index 3a0a88aef4a..4200f6a6805 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -1,4 +1,4 @@ -use crate::bstr::BStr; +use crate::bstr::{BStr, BString}; use crate::{config, Repository}; use gix_status::index_as_worktree::traits::{CompareBlobs, SubmoduleStatus}; use std::sync::atomic::AtomicBool; @@ -169,6 +169,100 @@ impl Repository { } } +/// An implementation of a trait to use with [`Repository::index_worktree_status()`] to compute the submodule status +/// using [Submodule::status()](crate::Submodule::status()). +#[derive(Clone)] +pub struct BuiltinSubmoduleStatus { + mode: crate::status::Submodule, + #[cfg(feature = "parallel")] + repo: crate::ThreadSafeRepository, + #[cfg(not(feature = "parallel"))] + git_dir: std::path::PathBuf, + submodule_paths: Vec<BString>, +} + +/// +mod submodule_status { + use crate::bstr; + use crate::bstr::BStr; + use crate::status::index_worktree::BuiltinSubmoduleStatus; + use crate::status::Submodule; + use std::borrow::Cow; + + impl BuiltinSubmoduleStatus { + /// Create a new instance from a `repo` and a `mode` to control how the submodule status will be obtained. + pub fn new( + repo: crate::ThreadSafeRepository, + mode: Submodule, + ) -> Result<Self, crate::submodule::modules::Error> { + let local_repo = repo.to_thread_local(); + let submodule_paths = match local_repo.submodules()? { + Some(sm) => { + let mut v: Vec<_> = sm + .filter(|sm| sm.is_active().unwrap_or_default()) + .filter_map(|sm| sm.path().ok().map(Cow::into_owned)) + .collect(); + v.sort(); + v + } + None => Vec::new(), + }; + Ok(Self { + mode, + #[cfg(feature = "parallel")] + repo, + #[cfg(not(feature = "parallel"))] + git_dir: local_repo.git_dir().to_owned(), + submodule_paths, + }) + } + } + + /// The error returned submodule status checks. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + SubmoduleStatus(#[from] crate::submodule::status::Error), + #[error(transparent)] + IgnoreConfig(#[from] crate::submodule::config::Error), + } + + impl gix_status::index_as_worktree::traits::SubmoduleStatus for BuiltinSubmoduleStatus { + type Output = crate::submodule::Status; + type Error = Error; + + fn status(&mut self, _entry: &gix_index::Entry, rela_path: &BStr) -> Result<Option<Self::Output>, Self::Error> { + use bstr::ByteSlice; + if self + .submodule_paths + .binary_search_by(|path| path.as_bstr().cmp(rela_path)) + .is_err() + { + return Ok(None); + } + #[cfg(feature = "parallel")] + let repo = self.repo.to_thread_local(); + #[cfg(not(feature = "parallel"))] + let Ok(repo) = crate::open(&self.git_dir) else { + return Ok(None); + }; + let Ok(Some(mut submodules)) = repo.submodules() else { + return Ok(None); + }; + let Some(sm) = submodules.find(|sm| sm.path().map_or(false, |path| path == rela_path)) else { + return Ok(None); + }; + let (ignore, check_dirty) = match self.mode { + Submodule::AsConfigured { check_dirty } => (sm.ignore()?.unwrap_or_default(), check_dirty), + Submodule::Given { ignore, check_dirty } => (ignore, check_dirty), + }; + let status = sm.status(ignore, check_dirty)?; + Ok(status.is_dirty().and_then(|dirty| dirty.then_some(status))) + } + } +} + /// An iterator for changes between the index and the worktree. /// /// Note that depending on the underlying configuration, there might be a significant delay until the first @@ -182,14 +276,26 @@ impl Repository { /// /// Changes to the index are collected and it's possible to write the index back using [iter::Outcome::write_changes()]. /// Note that these changes are not observable, they will always be kept. -#[cfg(feature = "parallel")] +/// +/// ### Parallel Operation +/// +/// Note that without the `parallel` feature, the iterator becomes 'serial', which means all status will be computed in advance +/// and it's non-interruptable, yielding worse performance for is-dirty checks for instance as interruptions won't happen. +/// It's a crutch that is just there to make single-threaded applications possible at all, as it's not really an iterator +/// anymore. If this matters, better run [Repository::index_worktree_status()] by hand as it provides all control one would need, +/// just not as an iterator. pub struct Iter { + #[cfg(feature = "parallel")] #[allow(clippy::type_complexity)] rx_and_join: Option<( std::sync::mpsc::Receiver<iter::Item>, std::thread::JoinHandle<Result<iter::Outcome, crate::status::index_worktree::Error>>, )>, + #[cfg(feature = "parallel")] should_interrupt: std::sync::Arc<AtomicBool>, + /// Without parallelization, the iterator has to buffer all changes in advance. + #[cfg(not(feature = "parallel"))] + items: std::vec::IntoIter<iter::Item>, /// The outcome of the operation, only available once the operation has ended. out: Option<iter::Outcome>, /// The set of `(entry_index, change)` we extracted in order to potentially write back the index with the changes applied. @@ -197,15 +303,13 @@ pub struct Iter { } /// -#[cfg(feature = "parallel")] pub mod iter { use crate::bstr::BString; use crate::config::cache::util::ApplyLeniencyDefault; - use crate::status::index_worktree::iter; + use crate::status::index_worktree::{iter, BuiltinSubmoduleStatus}; use crate::status::{index_worktree, Platform}; use crate::worktree::IndexPersistedOrInMemory; - use crate::ThreadSafeRepository; - use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::atomic::AtomicBool; use std::sync::Arc; pub(super) enum ApplyChange { @@ -278,7 +382,7 @@ pub mod iter { /// The repository-relative path of the `source_entry`. source_rela_path: BString, /// The computed status of the `source_entry`. - source_status: gix_status::index_as_worktree::EntryStatus<(), SubmoduleStatus>, + source_status: gix_status::index_as_worktree::EntryStatus<(), crate::submodule::Status>, }, /// This source originates in the directory tree and is always the source of copies. CopyFromDirectoryEntry { @@ -434,8 +538,12 @@ pub mod iter { #[error(transparent)] Index(#[from] crate::worktree::open_index::Error), #[error("Failed to spawn producer thread")] + #[cfg(feature = "parallel")] SpawnThread(#[source] std::io::Error), #[error(transparent)] + #[cfg(not(feature = "parallel"))] + IndexWorktreeStatus(#[from] crate::status::index_worktree::Error), + #[error(transparent)] ConfigSkipHash(#[from] crate::config::boolean::Error), #[error(transparent)] PrepareSubmodules(#[from] crate::submodule::modules::Error), @@ -457,8 +565,6 @@ pub mod iter { }; let should_interrupt = Arc::new(AtomicBool::default()); - let (tx, rx) = std::sync::mpsc::channel(); - let mut collect = Collect { tx }; let skip_hash = self .repo .config @@ -468,42 +574,86 @@ pub mod iter { .transpose() .with_lenient_default(self.repo.config.lenient_config)? .unwrap_or_default(); - let submodule = ComputeSubmoduleStatus::new(self.repo.clone().into_sync(), self.submodules)?; - let join = std::thread::Builder::new() - .name("gix::status::index_worktree::iter::producer".into()) - .spawn({ - let repo = self.repo.clone().into_sync(); - let options = self.index_worktree_options; - let should_interrupt = should_interrupt.clone(); - let mut progress = self.progress; - move || -> Result<_, crate::status::index_worktree::Error> { - let repo = repo.to_thread_local(); - let out = repo.index_worktree_status( - &index, - patterns, - &mut collect, - gix_status::index_as_worktree::traits::FastEq, - submodule, - &mut progress, - &should_interrupt, - options, - )?; - Ok(Outcome { - index_worktree: out, - index, - changes: None, - skip_hash, - }) - } + let submodule = BuiltinSubmoduleStatus::new(self.repo.clone().into_sync(), self.submodules)?; + #[cfg(feature = "parallel")] + { + let (tx, rx) = std::sync::mpsc::channel(); + let mut collect = Collect { tx }; + let join = std::thread::Builder::new() + .name("gix::status::index_worktree::iter::producer".into()) + .spawn({ + let repo = self.repo.clone().into_sync(); + let options = self.index_worktree_options; + let should_interrupt = should_interrupt.clone(); + let mut progress = self.progress; + move || -> Result<_, crate::status::index_worktree::Error> { + let repo = repo.to_thread_local(); + let out = repo.index_worktree_status( + &index, + patterns, + &mut collect, + gix_status::index_as_worktree::traits::FastEq, + submodule, + &mut progress, + &should_interrupt, + options, + )?; + Ok(Outcome { + index_worktree: out, + index, + changes: None, + skip_hash, + }) + } + }) + .map_err(Error::SpawnThread)?; + + Ok(super::Iter { + rx_and_join: Some((rx, join)), + should_interrupt, + changes: Vec::new(), + out: None, }) - .map_err(Error::SpawnThread)?; - - Ok(super::Iter { - rx_and_join: Some((rx, join)), - should_interrupt, - changes: Vec::new(), - out: None, - }) + } + #[cfg(not(feature = "parallel"))] + { + let mut collect = Collect { items: Vec::new() }; + + let repo = self.repo.clone().into_sync(); + let options = self.index_worktree_options; + let mut progress = self.progress; + let repo = repo.to_thread_local(); + let out = repo.index_worktree_status( + &index, + patterns, + &mut collect, + gix_status::index_as_worktree::traits::FastEq, + submodule, + &mut progress, + &should_interrupt, + options, + )?; + let mut out = Outcome { + index_worktree: out, + index, + changes: None, + skip_hash, + }; + let mut iter = super::Iter { + items: Vec::new().into_iter(), + changes: Vec::new(), + out: None, + }; + let items = collect + .items + .into_iter() + .filter_map(|item| iter.maybe_keep_index_change(item)) + .collect::<Vec<_>>(); + out.changes = (!iter.changes.is_empty()).then(|| std::mem::take(&mut iter.changes)); + iter.items = items.into_iter(); + iter.out = Some(out); + Ok(iter) + } } } @@ -511,6 +661,7 @@ pub mod iter { type Item = Result<Item, crate::status::index_worktree::Error>; fn next(&mut self) -> Option<Self::Item> { + #[cfg(feature = "parallel")] loop { let (rx, _join) = self.rx_and_join.as_ref()?; match rx.recv().ok() { @@ -523,7 +674,8 @@ pub mod iter { None => { let (_rx, handle) = self.rx_and_join.take()?; break match handle.join().expect("no panic") { - Ok(out) => { + Ok(mut out) => { + out.changes = Some(std::mem::take(&mut self.changes)); self.out = Some(out); None } @@ -532,6 +684,15 @@ pub mod iter { } } } + #[cfg(not(feature = "parallel"))] + self.items.next().map(Ok) + } + } + + impl super::Iter { + /// Return the outcome of the iteration, or `None` if the iterator isn't fully consumed. + pub fn outcome_mut(&mut self) -> Option<&mut Outcome> { + self.out.as_mut() } } @@ -562,107 +723,27 @@ pub mod iter { } } + #[cfg(feature = "parallel")] impl Drop for super::Iter { fn drop(&mut self) { - self.should_interrupt.store(true, Ordering::Relaxed); + self.should_interrupt.store(true, std::sync::atomic::Ordering::Relaxed); // Allow to temporarily 'leak' the producer to not block on drop, nobody // is interested in the result of the thread anymore. drop(self.rx_and_join.take()); } } - #[derive(Clone)] - struct ComputeSubmoduleStatus { - mode: crate::status::Submodule, - repo: ThreadSafeRepository, - submodule_paths: Vec<BString>, - } - - /// - mod submodule_status { - use crate::bstr; - use crate::bstr::BStr; - use crate::status::index_worktree::iter::{ComputeSubmoduleStatus, SubmoduleStatus}; - use crate::status::Submodule; - use std::borrow::Cow; - - impl ComputeSubmoduleStatus { - pub(super) fn new( - repo: crate::ThreadSafeRepository, - mode: crate::status::Submodule, - ) -> Result<Self, crate::submodule::modules::Error> { - let local_repo = repo.to_thread_local(); - let submodule_paths = match local_repo.submodules()? { - Some(sm) => { - let mut v: Vec<_> = sm - .filter(|sm| sm.is_active().unwrap_or_default()) - .filter_map(|sm| sm.path().ok().map(Cow::into_owned)) - .collect(); - v.sort(); - v - } - None => Vec::new(), - }; - Ok(Self { - mode, - repo, - submodule_paths, - }) - } - } - - /// The error returned submodule status checks. - #[derive(Debug, thiserror::Error)] - #[allow(missing_docs)] - pub(super) enum Error { - #[error(transparent)] - SubmoduleStatus(#[from] crate::submodule::status::Error), - #[error(transparent)] - IgnoreConfig(#[from] crate::submodule::config::Error), - } - - impl gix_status::index_as_worktree::traits::SubmoduleStatus for ComputeSubmoduleStatus { - type Output = SubmoduleStatus; - type Error = Error; - - fn status( - &mut self, - _entry: &gix_index::Entry, - rela_path: &BStr, - ) -> Result<Option<Self::Output>, Self::Error> { - use bstr::ByteSlice; - if self - .submodule_paths - .binary_search_by(|path| path.as_bstr().cmp(rela_path)) - .is_err() - { - return Ok(None); - } - let repo = self.repo.to_thread_local(); - let Ok(Some(mut submodules)) = repo.submodules() else { - return Ok(None); - }; - let Some(sm) = submodules.find(|sm| sm.path().map_or(false, |path| path == rela_path)) else { - return Ok(None); - }; - let (ignore, check_dirty) = match self.mode { - Submodule::AsConfigured { check_dirty } => (sm.ignore()?.unwrap_or_default(), check_dirty), - Submodule::Given { ignore, check_dirty } => (ignore, check_dirty), - }; - let status = sm.status(ignore, check_dirty)?; - Ok(status.is_dirty().and_then(|dirty| dirty.then_some(status))) - } - } - } - struct Collect { + #[cfg(feature = "parallel")] tx: std::sync::mpsc::Sender<Item>, + #[cfg(not(feature = "parallel"))] + items: Vec<Item>, } impl<'index> gix_status::index_as_worktree_with_renames::VisitEntry<'index> for Collect { type ContentChange = <gix_status::index_as_worktree::traits::FastEq as gix_status::index_as_worktree::traits::CompareBlobs>::Output; type SubmoduleStatus = - <ComputeSubmoduleStatus as gix_status::index_as_worktree::traits::SubmoduleStatus>::Output; + <BuiltinSubmoduleStatus as gix_status::index_as_worktree::traits::SubmoduleStatus>::Output; fn visit_entry( &mut self, @@ -673,7 +754,10 @@ pub mod iter { >, ) { // NOTE: we assume that the receiver triggers interruption so the operation will stop if the receiver is down. + #[cfg(feature = "parallel")] self.tx.send(entry.into()).ok(); + #[cfg(not(feature = "parallel"))] + self.items.push(entry.into()); } } } diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs index 23726d514f2..bdb592ed477 100644 --- a/gix/src/status/mod.rs +++ b/gix/src/status/mod.rs @@ -6,9 +6,7 @@ pub struct Platform<'repo, Progress> where Progress: gix_features::progress::Progress + 'static, { - #[cfg_attr(not(feature = "parallel"), allow(dead_code))] repo: &'repo Repository, - #[cfg_attr(not(feature = "parallel"), allow(dead_code))] progress: Progress, index: Option<crate::worktree::IndexPersistedOrInMemory>, submodules: Submodule, @@ -84,7 +82,6 @@ impl Repository { } /// -#[cfg(feature = "parallel")] pub mod is_dirty { use crate::Repository; diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index b9e6976a7f8..6e85494c522 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -276,7 +276,7 @@ impl<'repo> Submodule<'repo> { } /// -#[cfg(all(feature = "status", feature = "parallel"))] +#[cfg(feature = "status")] pub mod status { use super::{head_id, index_id, open, Status}; use crate::Submodule; @@ -300,6 +300,8 @@ pub mod status { StatusPlatform(#[from] crate::config::boolean::Error), #[error(transparent)] Status(#[from] crate::status::index_worktree::iter::Error), + #[error(transparent)] + IndexWorktreeStatus(#[from] crate::status::index_worktree::Error), } impl<'repo> Submodule<'repo> { @@ -379,19 +381,19 @@ pub mod status { return Ok(status); } - status.changes = Some( - adjust_options(sm_repo.status(gix_features::progress::Discard)?) - .index_worktree_options_mut(|opts| { - assert!(opts.dirwalk_options.is_some(), "BUG: it's supposed to be the default"); - if ignore == config::Ignore::Untracked { - opts.dirwalk_options = None; - } - }) - .into_index_worktree_iter(Vec::new())? - .filter_map(Result::ok) - .collect(), - ); - + let statusses = adjust_options(sm_repo.status(gix_features::progress::Discard)?) + .index_worktree_options_mut(|opts| { + assert!(opts.dirwalk_options.is_some(), "BUG: it's supposed to be the default"); + if ignore == config::Ignore::Untracked { + opts.dirwalk_options = None; + } + }) + .into_index_worktree_iter(Vec::new())?; + let mut changes = Vec::new(); + for change in statusses { + changes.push(change?); + } + status.changes = Some(changes); Ok(status) } } @@ -444,7 +446,7 @@ pub mod status { } } } -#[cfg(all(feature = "status", feature = "parallel"))] +#[cfg(feature = "status")] pub use status::types::Status; /// A summary of the state of all parts forming a submodule, which allows to answer various questions about it. diff --git a/gix/tests/commit/mod.rs b/gix/tests/commit/mod.rs index bdf951b5415..3daf74852db 100644 --- a/gix/tests/commit/mod.rs +++ b/gix/tests/commit/mod.rs @@ -4,7 +4,7 @@ mod describe { use crate::named_repo; - #[cfg(all(feature = "status", feature = "parallel"))] + #[cfg(feature = "status")] mod with_dirty_suffix { use crate::util::named_subrepo_opts; use gix::commit::describe::SelectRef; diff --git a/gix/tests/status/mod.rs b/gix/tests/status/mod.rs index 6f39e56d3d5..7d3d7400341 100644 --- a/gix/tests/status/mod.rs +++ b/gix/tests/status/mod.rs @@ -11,6 +11,15 @@ mod index_worktree { mod iter { use crate::status::repo; + #[test] + fn item_size() { + assert_eq!( + std::mem::size_of::<gix::status::index_worktree::iter::Item>(), + 264, + "The size is pretty huge and goes down ideally" + ); + } + #[test] fn submodule_modification() -> crate::Result { let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; diff --git a/gix/tests/submodule/mod.rs b/gix/tests/submodule/mod.rs index 4aa6b5cb442..5434ac2b152 100644 --- a/gix/tests/submodule/mod.rs +++ b/gix/tests/submodule/mod.rs @@ -68,9 +68,10 @@ mod open { ), ] { let repo = repo(name)?; - for (sm, (name, expected, _expected_is_dirty)) in repo.submodules()?.expect("modules present").zip(expected) + for (sm, (sm_name, expected, _expected_is_dirty)) in + repo.submodules()?.expect("modules present").zip(expected) { - assert_eq!(sm.name(), name); + assert_eq!(sm.name(), sm_name); let state = sm.state()?; assert_eq!(&state, expected); @@ -92,9 +93,18 @@ mod open { "there is a way to check for indicators that a submodule worktree isn't checked out though" ) } - #[cfg(all(feature = "status", feature = "parallel"))] + #[cfg(feature = "status")] for check_dirty in [false, true] { - let status = sm.status(gix::submodule::config::Ignore::None, check_dirty)?; + let status = match sm.status(gix::submodule::config::Ignore::None, check_dirty) { + Ok(status) => status, + Err(err) => { + assert_eq!( + name, "not-a-submodule", + "{name}: BUG: only one submodule is expected to fail, got '{err:?}'" + ); + continue; + } + }; assert_eq!( &status.state, expected, "no matter what status configuration, the state is always obtained" @@ -115,7 +125,7 @@ mod open { Ok(()) } - #[cfg(all(feature = "status", feature = "parallel"))] + #[cfg(feature = "status")] mod status { use crate::submodule::repo; use crate::util::hex_to_id; From 58231b418fa39ea122ef41bb7691289f5b0be855 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sat, 9 Mar 2024 12:35:06 +0100 Subject: [PATCH 13/26] feat: add `gix commit describe --dirty-suffix` That way a suffix will be added depending on the dirty-state of the repository. --- gitoxide-core/src/repository/commit.rs | 4 +++- src/plumbing/main.rs | 2 ++ src/plumbing/options/mod.rs | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/gitoxide-core/src/repository/commit.rs b/gitoxide-core/src/repository/commit.rs index 17ce99e5f3c..e0070dc2ae3 100644 --- a/gitoxide-core/src/repository/commit.rs +++ b/gitoxide-core/src/repository/commit.rs @@ -52,6 +52,7 @@ pub fn describe( statistics, max_candidates, long_format, + dirty_suffix, }: describe::Options, ) -> Result<()> { repo.object_cache_size_if_unset(4 * 1024 * 1024); @@ -80,7 +81,7 @@ pub fn describe( writeln!(err, "traversed {} commits", resolution.outcome.commits_seen)?; } - let mut describe_id = resolution.format()?; + let mut describe_id = resolution.format_with_dirty_suffix(dirty_suffix)?; describe_id.long(long_format); writeln!(out, "{describe_id}")?; @@ -97,5 +98,6 @@ pub mod describe { pub long_format: bool, pub statistics: bool, pub max_candidates: usize, + pub dirty_suffix: Option<String>, } } diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index aeb589c584f..658fa4bc21b 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -1036,6 +1036,7 @@ pub fn main() -> Result<()> { statistics, max_candidates, rev_spec, + dirty_suffix, } => prepare_and_run( "commit-describe", trace, @@ -1057,6 +1058,7 @@ pub fn main() -> Result<()> { statistics, max_candidates, always, + dirty_suffix: dirty_suffix.map(|suffix| suffix.unwrap_or_else(|| "dirty".to_string())), }, ) }, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 690ea56ecaa..e4aff7ad5cb 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -627,6 +627,10 @@ pub mod commit { /// If there was no way to describe the commit, fallback to using the abbreviated input revision. always: bool, + /// Set the suffix to append if the repository is dirty (not counting untracked files). + #[clap(short = 'd', long)] + dirty_suffix: Option<Option<String>>, + /// A specification of the revision to use, or the current `HEAD` if unset. rev_spec: Option<String>, }, From afd20caadb40b6b793f2099b7232669f9a8f9086 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sat, 9 Mar 2024 16:10:08 +0100 Subject: [PATCH 14/26] feat: `gix submodules list --dirty-suffix` for dirty-information This is a submodule-centric and greatly simplified way of obtaining describe information with dirty-suffix. Note that `status` information is also possible, but it seems hard to display nicely, which this command isn't great at in the first place. --- gitoxide-core/src/repository/submodule.rs | 16 ++++++++++++---- src/plumbing/main.rs | 13 ++++++++++--- src/plumbing/options/mod.rs | 6 +++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/gitoxide-core/src/repository/submodule.rs b/gitoxide-core/src/repository/submodule.rs index c8e8c45eaa2..12ef64870e8 100644 --- a/gitoxide-core/src/repository/submodule.rs +++ b/gitoxide-core/src/repository/submodule.rs @@ -3,7 +3,12 @@ use gix::{commit::describe::SelectRef, prelude::ObjectIdExt, Repository, Submodu use crate::OutputFormat; -pub fn list(repo: Repository, mut out: impl std::io::Write, format: OutputFormat) -> anyhow::Result<()> { +pub fn list( + repo: Repository, + mut out: impl std::io::Write, + format: OutputFormat, + dirty_suffix: Option<String>, +) -> anyhow::Result<()> { if format != OutputFormat::Human { bail!("Only human output is supported for now") } @@ -12,12 +17,12 @@ pub fn list(repo: Repository, mut out: impl std::io::Write, format: OutputFormat return Ok(()); }; for sm in submodules { - print_sm(sm, &mut out)?; + print_sm(sm, dirty_suffix.as_deref(), &mut out)?; } Ok(()) } -fn print_sm(sm: Submodule<'_>, out: &mut impl std::io::Write) -> anyhow::Result<()> { +fn print_sm(sm: Submodule<'_>, dirty_suffix: Option<&str>, out: &mut impl std::io::Write) -> anyhow::Result<()> { let _span = gix::trace::coarse!("print_sm", path = ?sm.path()); let state = sm.state()?; let mut sm_repo = sm.open()?; @@ -48,7 +53,10 @@ fn print_sm(sm: Submodule<'_>, out: &mut impl std::io::Write) -> anyhow::Result< repo.head_commit()? .describe() .names(SelectRef::AllRefs) - .format()? + .id_as_fallback(true) + .try_resolve()? + .expect("resolution present if ID can be used as fallback") + .format_with_dirty_suffix(dirty_suffix.map(ToOwned::to_owned))? .to_string() } None => { diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index 658fa4bc21b..bdb826c9641 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -223,16 +223,23 @@ pub fn main() -> Result<()> { ), Subcommands::Submodule(platform) => match platform .cmds - .unwrap_or(crate::plumbing::options::submodule::Subcommands::List) + .unwrap_or(crate::plumbing::options::submodule::Subcommands::List { dirty_suffix: None }) { - crate::plumbing::options::submodule::Subcommands::List => prepare_and_run( + crate::plumbing::options::submodule::Subcommands::List { dirty_suffix } => prepare_and_run( "submodule-list", trace, verbose, progress, progress_keep_open, None, - move |_progress, out, _err| core::repository::submodule::list(repository(Mode::Lenient)?, out, format), + move |_progress, out, _err| { + core::repository::submodule::list( + repository(Mode::Lenient)?, + out, + format, + dirty_suffix.map(|suffix| suffix.unwrap_or_else(|| "dirty".to_string())), + ) + }, ), }, #[cfg(feature = "gitoxide-core-tools-archive")] diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index e4aff7ad5cb..5825359b8d9 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -875,7 +875,11 @@ pub mod submodule { #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Print all direct submodules to standard output - List, + List { + /// Set the suffix to append if the repository is dirty (not counting untracked files). + #[clap(short = 'd', long)] + dirty_suffix: Option<Option<String>>, + }, } } From 98b368095ec99d1bc287da7f9294a9fce424deed Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sat, 9 Mar 2024 16:50:59 +0100 Subject: [PATCH 15/26] feat: add `gix is-clean|is-changed` It's a good way to compare the time it takes to run a full status compared to a quick is-dirty check. --- gitoxide-core/src/repository/dirty.rs | 32 +++++++++++++++++++++++++++ gitoxide-core/src/repository/mod.rs | 1 + src/plumbing/main.rs | 18 +++++++++++++++ src/plumbing/options/mod.rs | 2 ++ 4 files changed, 53 insertions(+) create mode 100644 gitoxide-core/src/repository/dirty.rs diff --git a/gitoxide-core/src/repository/dirty.rs b/gitoxide-core/src/repository/dirty.rs new file mode 100644 index 00000000000..8981199d532 --- /dev/null +++ b/gitoxide-core/src/repository/dirty.rs @@ -0,0 +1,32 @@ +use crate::OutputFormat; +use anyhow::bail; + +pub enum Mode { + IsClean, + IsDirty, +} + +pub fn check( + repo: gix::Repository, + mode: Mode, + out: &mut dyn std::io::Write, + format: OutputFormat, +) -> anyhow::Result<()> { + if format != OutputFormat::Human { + bail!("JSON output isn't implemented yet"); + } + let is_dirty = repo.is_dirty()?; + let res = match (is_dirty, mode) { + (false, Mode::IsClean) => Ok("The repository is clean"), + (true, Mode::IsClean) => Err("The repository has changes"), + (false, Mode::IsDirty) => Err("The repository is clean"), + (true, Mode::IsDirty) => Ok("The repository has changes"), + }; + + let suffix = "(not counting untracked files)"; + match res { + Ok(msg) => writeln!(out, "{msg} {suffix}")?, + Err(msg) => bail!("{msg} {suffix}"), + } + Ok(()) +} diff --git a/gitoxide-core/src/repository/mod.rs b/gitoxide-core/src/repository/mod.rs index 35d0c156a95..55816dcb774 100644 --- a/gitoxide-core/src/repository/mod.rs +++ b/gitoxide-core/src/repository/mod.rs @@ -26,6 +26,7 @@ pub use credential::function as credential; pub mod attributes; #[cfg(feature = "clean")] pub mod clean; +pub mod dirty; #[cfg(feature = "clean")] pub use clean::function::clean; #[cfg(feature = "blocking-client")] diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index bdb826c9641..fb60a14a462 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -146,6 +146,24 @@ pub fn main() -> Result<()> { } match cmd { + Subcommands::IsClean | Subcommands::IsChanged => { + let mode = if matches!(cmd, Subcommands::IsClean) { + core::repository::dirty::Mode::IsClean + } else { + core::repository::dirty::Mode::IsDirty + }; + prepare_and_run( + "clean", + trace, + verbose, + progress, + progress_keep_open, + None, + move |_progress, out, _err| { + core::repository::dirty::check(repository(Mode::Lenient)?, mode, out, format) + }, + ) + } #[cfg(feature = "gitoxide-core-tools-clean")] Subcommands::Clean(crate::plumbing::options::clean::Command { debug, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 5825359b8d9..7a9c3c4f749 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -130,6 +130,8 @@ pub enum Subcommands { /// Interact with submodules. #[clap(alias = "submodules")] Submodule(submodule::Platform), + IsClean, + IsChanged, /// Show which git configuration values are used or planned. ConfigTree, Status(status::Platform), From f1ba7bd459390080052024920992054f1d11cd3e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sun, 10 Mar 2024 09:09:17 +0100 Subject: [PATCH 16/26] Allow configuration of interrupts in status iter Otherwise users might not have too much delay until an interrupt is possible, wasting a lot of time. --- gix/src/status/index_worktree.rs | 36 +++++++++++++++++++++++++------- gix/src/status/mod.rs | 35 +++++++++++++++++++++++++++++++ gix/src/status/platform.rs | 23 +++++++++++++++++++- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index 4200f6a6805..fb39a8c5eb9 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -284,6 +284,10 @@ mod submodule_status { /// It's a crutch that is just there to make single-threaded applications possible at all, as it's not really an iterator /// anymore. If this matters, better run [Repository::index_worktree_status()] by hand as it provides all control one would need, /// just not as an iterator. +/// +/// Also, even with `parallel` set, the first call to `next()` will block until there is an item available, without a chance +/// to interrupt unless [`status::Platform::should_interrupt_*()`](crate::status::Platform::should_interrupt_shared()) was +/// configured. pub struct Iter { #[cfg(feature = "parallel")] #[allow(clippy::type_complexity)] @@ -292,7 +296,7 @@ pub struct Iter { std::thread::JoinHandle<Result<iter::Outcome, crate::status::index_worktree::Error>>, )>, #[cfg(feature = "parallel")] - should_interrupt: std::sync::Arc<AtomicBool>, + should_interrupt: crate::status::OwnedOrStaticAtomic, /// Without parallelization, the iterator has to buffer all changes in advance. #[cfg(not(feature = "parallel"))] items: std::vec::IntoIter<iter::Item>, @@ -309,8 +313,6 @@ pub mod iter { use crate::status::index_worktree::{iter, BuiltinSubmoduleStatus}; use crate::status::{index_worktree, Platform}; use crate::worktree::IndexPersistedOrInMemory; - use std::sync::atomic::AtomicBool; - use std::sync::Arc; pub(super) enum ApplyChange { SetSizeToZero, @@ -564,7 +566,6 @@ pub mod iter { Some(index) => index, }; - let should_interrupt = Arc::new(AtomicBool::default()); let skip_hash = self .repo .config @@ -574,6 +575,7 @@ pub mod iter { .transpose() .with_lenient_default(self.repo.config.lenient_config)? .unwrap_or_default(); + let should_interrupt = self.should_interrupt.clone().unwrap_or_default(); let submodule = BuiltinSubmoduleStatus::new(self.repo.clone().into_sync(), self.submodules)?; #[cfg(feature = "parallel")] { @@ -726,10 +728,28 @@ pub mod iter { #[cfg(feature = "parallel")] impl Drop for super::Iter { fn drop(&mut self) { - self.should_interrupt.store(true, std::sync::atomic::Ordering::Relaxed); - // Allow to temporarily 'leak' the producer to not block on drop, nobody - // is interested in the result of the thread anymore. - drop(self.rx_and_join.take()); + use crate::status::OwnedOrStaticAtomic; + let Some((rx, handle)) = self.rx_and_join.take() else { + return; + }; + let prev = self.should_interrupt.swap(true, std::sync::atomic::Ordering::Relaxed); + let undo = match &self.should_interrupt { + OwnedOrStaticAtomic::Shared(flag) => *flag, + OwnedOrStaticAtomic::Owned { flag, private: false } => flag.as_ref(), + OwnedOrStaticAtomic::Owned { private: true, .. } => { + // Leak the handle to let it shut down in the background, so drop returns more quickly. + drop((rx, handle)); + return; + } + }; + // Wait until there is time to respond before we undo the change. + handle.join().ok(); + undo.fetch_update( + std::sync::atomic::Ordering::SeqCst, + std::sync::atomic::Ordering::SeqCst, + |current| current.then_some(prev), + ) + .ok(); } } diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs index bdb592ed477..483964e298d 100644 --- a/gix/src/status/mod.rs +++ b/gix/src/status/mod.rs @@ -1,5 +1,8 @@ use crate::{config, Repository}; pub use gix_status as plumbing; +use std::ops::Deref; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; /// A structure to hold options configuring the status request, which can then be turned into an iterator. pub struct Platform<'repo, Progress> @@ -11,6 +14,37 @@ where index: Option<crate::worktree::IndexPersistedOrInMemory>, submodules: Submodule, index_worktree_options: index_worktree::Options, + should_interrupt: Option<OwnedOrStaticAtomic>, +} + +#[derive(Clone)] +enum OwnedOrStaticAtomic { + Owned { + flag: Arc<AtomicBool>, + #[cfg_attr(not(feature = "parallel"), allow(dead_code))] + private: bool, + }, + Shared(&'static AtomicBool), +} + +impl Default for OwnedOrStaticAtomic { + fn default() -> Self { + OwnedOrStaticAtomic::Owned { + flag: Arc::new(AtomicBool::default()), + private: true, + } + } +} + +impl Deref for OwnedOrStaticAtomic { + type Target = std::sync::atomic::AtomicBool; + + fn deref(&self) -> &Self::Target { + match self { + OwnedOrStaticAtomic::Owned { flag, .. } => flag, + OwnedOrStaticAtomic::Shared(flag) => flag, + } + } } /// How to obtain a submodule's status. @@ -71,6 +105,7 @@ impl Repository { progress, index: None, submodules: Submodule::default(), + should_interrupt: None, index_worktree_options: index_worktree::Options { sorting: None, dirwalk_options: Some(self.dirwalk_options()?), diff --git a/gix/src/status/platform.rs b/gix/src/status/platform.rs index 7a8a1fc844e..58f81765ff6 100644 --- a/gix/src/status/platform.rs +++ b/gix/src/status/platform.rs @@ -1,4 +1,5 @@ -use crate::status::{index_worktree, Platform, Submodule}; +use crate::status::{index_worktree, OwnedOrStaticAtomic, Platform, Submodule}; +use std::sync::atomic::AtomicBool; /// Builder impl<'repo, Progress> Platform<'repo, Progress> @@ -16,6 +17,26 @@ where self } + /// Set the interrupt flag to `should_interrupt`, which typically is an application-wide flag + /// that is ultimately controlled by user interrupts. + /// + /// If it is `true`, the iteration will stop immediately. + pub fn should_interrupt_shared(mut self, should_interrupt: &'static AtomicBool) -> Self { + self.should_interrupt = Some(OwnedOrStaticAtomic::Shared(should_interrupt)); + self + } + + /// Set the interrupt flag to `should_interrupt`, as controlled by the caller. + /// + /// If it is `true`, the iteration will stop immediately. + pub fn should_interrupt_owned(mut self, should_interrupt: std::sync::Arc<AtomicBool>) -> Self { + self.should_interrupt = Some(OwnedOrStaticAtomic::Owned { + flag: should_interrupt, + private: false, + }); + self + } + /// Configure how the `submodule_status` is obtained when looking at submodules that are still mentioned in the index. // If `None` is given, no submodule status check is performed. pub fn index_worktree_submodules(mut self, submodules: impl Into<Option<Submodule>>) -> Self { From 61c002bc4ca5b5345c411e561fdcb492e7ae1d97 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sat, 9 Mar 2024 17:42:14 +0100 Subject: [PATCH 17/26] feat: `gix status` with submodule and rewrite support. Submodule changes are now picked up as long as the submodule is in the index. Further, it's possible to enable rename-tracking between index and worktree separately. --- gitoxide-core/src/repository/status.rs | 283 ++++++++----------------- src/plumbing/main.rs | 5 +- src/plumbing/options/mod.rs | 8 +- 3 files changed, 100 insertions(+), 196 deletions(-) diff --git a/gitoxide-core/src/repository/status.rs b/gitoxide-core/src/repository/status.rs index ee36b1b1f3f..fe7ac0174a7 100644 --- a/gitoxide-core/src/repository/status.rs +++ b/gitoxide-core/src/repository/status.rs @@ -1,12 +1,8 @@ -use anyhow::{bail, Context}; -use gix::bstr::ByteSlice; -use gix::{ - bstr::{BStr, BString}, - index::Entry, - Progress, -}; -use gix_status::index_as_worktree::{traits::FastEq, Change, Conflict, EntryStatus}; -use std::path::{Path, PathBuf}; +use anyhow::bail; +use gix::bstr::{BStr, BString}; +use gix::status::index_worktree::iter::Item; +use gix_status::index_as_worktree::{Change, Conflict, EntryStatus}; +use std::path::Path; use crate::OutputFormat; @@ -17,11 +13,13 @@ pub enum Submodules { RefChange, /// See if there are worktree modifications compared to the index, but do not check for untracked files. Modifications, + /// Ignore all submodule changes. + None, } pub struct Options { pub format: OutputFormat, - pub submodules: Submodules, + pub submodules: Option<Submodules>, pub thread_limit: Option<usize>, pub statistics: bool, pub allow_write: bool, @@ -30,13 +28,12 @@ pub struct Options { pub fn show( repo: gix::Repository, pathspecs: Vec<BString>, - out: impl std::io::Write, + mut out: impl std::io::Write, mut err: impl std::io::Write, - mut progress: impl gix::NestedProgress, + mut progress: impl gix::NestedProgress + 'static, Options { format, - // TODO: implement this - submodules: _, + submodules, thread_limit, allow_write, statistics, @@ -45,198 +42,102 @@ pub fn show( if format != OutputFormat::Human { bail!("Only human format is supported right now"); } - let mut index = repo.index_or_empty()?; - let index = gix::threading::make_mut(&mut index); - let mut progress = progress.add_child("traverse index"); + let start = std::time::Instant::now(); - let stack = repo - .attributes_only( - index, - gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, - )? - .detach(); - let pathspec = gix::Pathspec::new(&repo, false, pathspecs.iter().map(|p| p.as_bstr()), true, || { - Ok(stack.clone()) - })?; - let options = gix_status::index_as_worktree::Options { - fs: repo.filesystem_options()?, - thread_limit, - stat: repo.stat_options()?, - }; let prefix = repo.prefix()?.unwrap_or(Path::new("")); - let mut printer = Printer { - out, - changes: Vec::new(), - prefix: prefix.to_owned(), - }; - let filter_pipeline = repo - .filter_pipeline(Some(gix::hash::ObjectId::empty_tree(repo.object_hash())))? - .0 - .into_parts() - .0; - let ctx = gix_status::index_as_worktree::Context { - pathspec: pathspec.into_parts().0, - stack, - filter: filter_pipeline, - should_interrupt: &gix::interrupt::IS_INTERRUPTED, - }; - let mut collect = gix::dir::walk::delegate::Collect::default(); - let (outcome, walk_outcome) = gix::features::parallel::threads(|scope| -> anyhow::Result<_> { - // TODO: it's either this, or not running both in parallel and setting UPTODATE flags whereever - // there is no modification. This can save disk queries as dirwalk can then trust what's in - // the index regarding the type. - // NOTE: collect here as rename-tracking needs that anyway. - let walk_outcome = gix::features::parallel::build_thread() - .name("gix status::dirwalk".into()) - .spawn_scoped(scope, { - let repo = repo.clone().into_sync(); - let index = &index; - let collect = &mut collect; - move || -> anyhow::Result<_> { - let repo = repo.to_thread_local(); - let outcome = repo.dirwalk( - index, - pathspecs, - repo.dirwalk_options()? - .emit_untracked(gix::dir::walk::EmissionMode::CollapseDirectory), - collect, - )?; - Ok(outcome.dirwalk) + let index_progress = progress.add_child("traverse index"); + let mut iter = repo + .status(index_progress)? + .should_interrupt_shared(&gix::interrupt::IS_INTERRUPTED) + .index_worktree_options_mut(|opts| { + opts.thread_limit = thread_limit; + opts.sorting = Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + .index_worktree_submodules(match submodules { + Some(mode) => { + let ignore = match mode { + Submodules::All => gix::submodule::config::Ignore::None, + Submodules::RefChange => gix::submodule::config::Ignore::Dirty, + Submodules::Modifications => gix::submodule::config::Ignore::Untracked, + Submodules::None => gix::submodule::config::Ignore::All, + }; + gix::status::Submodule::Given { + ignore, + check_dirty: false, } - })?; - - let outcome = gix_status::index_as_worktree( - index, - repo.work_dir() - .context("This operation cannot be run on a bare repository")?, - &mut printer, - FastEq, - Submodule, - repo.objects.clone().into_arc()?, - &mut progress, - ctx, - options, - )?; - - let walk_outcome = walk_outcome.join().expect("no panic")?; - Ok((outcome, walk_outcome)) - })?; - - for entry in collect - .into_entries_by_path() - .into_iter() - .filter_map(|(entry, dir_status)| dir_status.is_none().then_some(entry)) - { - writeln!( - printer.out, - "{status: >3} {rela_path}", - status = "?", - rela_path = gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display() - )?; - } - - if outcome.entries_to_update != 0 && allow_write { - { - let entries = index.entries_mut(); - for (entry_index, change) in printer.changes { - let entry = &mut entries[entry_index]; - match change { - ApplyChange::SetSizeToZero => { - entry.stat.size = 0; - } - ApplyChange::NewStat(new_stat) => { - entry.stat = new_stat; - } + } + None => gix::status::Submodule::AsConfigured { check_dirty: false }, + }) + .into_index_worktree_iter(pathspecs)?; + for item in iter.by_ref() { + let item = item?; + match item { + Item::Modification { + entry: _, + entry_index: _, + rela_path, + status, + } => print_index_entry_status(&mut out, prefix, rela_path.as_ref(), status)?, + Item::DirectoryContents { + entry, + collapsed_directory_status, + } => { + if collapsed_directory_status.is_none() { + writeln!( + out, + "{status: >3} {rela_path}", + status = "?", + rela_path = + gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display() + )?; } } + Item::Rewrite { .. } => {} } - index.write(gix::index::write::Options { - extensions: Default::default(), - skip_hash: false, // TODO: make this based on configuration - })?; } - - if statistics { - writeln!(err, "{outcome:#?}").ok(); - writeln!(err, "{walk_outcome:#?}").ok(); + if gix::interrupt::is_triggered() { + bail!("interrupted by user"); } - writeln!(err, "\nhead -> index and untracked files aren't implemented yet")?; - progress.show_throughput(start); - Ok(()) -} - -#[derive(Clone)] -struct Submodule; + let out = iter.outcome_mut().expect("successful iteration has outcome"); -impl gix_status::index_as_worktree::traits::SubmoduleStatus for Submodule { - type Output = (); - type Error = std::convert::Infallible; - - fn status(&mut self, _entry: &Entry, _rela_path: &BStr) -> Result<Option<Self::Output>, Self::Error> { - Ok(None) + if out.has_changes() && allow_write { + out.write_changes().transpose()?; } -} -struct Printer<W> { - out: W, - changes: Vec<(usize, ApplyChange)>, - prefix: PathBuf, -} - -enum ApplyChange { - SetSizeToZero, - NewStat(gix::index::entry::Stat), -} - -impl<'index, W> gix_status::index_as_worktree::VisitEntry<'index> for Printer<W> -where - W: std::io::Write, -{ - type ContentChange = (); - type SubmoduleStatus = (); - - fn visit_entry( - &mut self, - _entries: &'index [Entry], - _entry: &'index Entry, - entry_index: usize, - rela_path: &'index BStr, - status: EntryStatus<Self::ContentChange>, - ) { - self.visit_inner(entry_index, rela_path, status).ok(); + if statistics { + writeln!(err, "{outcome:#?}", outcome = out.index_worktree).ok(); } + + writeln!(err, "\nhead -> index isn't implemented yet")?; + progress.init(Some(out.index.entries().len()), gix::progress::count("files")); + progress.set(out.index.entries().len()); + progress.show_throughput(start); + Ok(()) } -impl<W: std::io::Write> Printer<W> { - fn visit_inner(&mut self, entry_index: usize, rela_path: &BStr, status: EntryStatus<()>) -> std::io::Result<()> { - let char_storage; - let status = match status { - EntryStatus::Conflict(conflict) => as_str(conflict), - EntryStatus::Change(change) => { - if matches!( - change, - Change::Modification { - set_entry_stat_size_zero: true, - .. - } - ) { - self.changes.push((entry_index, ApplyChange::SetSizeToZero)) - } - char_storage = change_to_char(&change); - std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII") - } - EntryStatus::NeedsUpdate(stat) => { - self.changes.push((entry_index, ApplyChange::NewStat(stat))); - return Ok(()); - } - EntryStatus::IntentToAdd => "A", - }; +fn print_index_entry_status( + out: &mut dyn std::io::Write, + prefix: &Path, + rela_path: &BStr, + status: EntryStatus<(), gix::submodule::Status>, +) -> std::io::Result<()> { + let char_storage; + let status = match status { + EntryStatus::Conflict(conflict) => as_str(conflict), + EntryStatus::Change(change) => { + char_storage = change_to_char(&change); + std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII") + } + EntryStatus::NeedsUpdate(_stat) => { + return Ok(()); + } + EntryStatus::IntentToAdd => "A", + }; - let rela_path = gix::path::from_bstr(rela_path); - let display_path = gix::path::relativize_with_prefix(&rela_path, &self.prefix); - writeln!(&mut self.out, "{status: >3} {}", display_path.display()) - } + let rela_path = gix::path::from_bstr(rela_path); + let display_path = gix::path::relativize_with_prefix(&rela_path, prefix); + writeln!(out, "{status: >3} {}", display_path.display()) } fn as_str(c: Conflict) -> &'static str { @@ -251,7 +152,7 @@ fn as_str(c: Conflict) -> &'static str { } } -fn change_to_char(change: &Change<()>) -> u8 { +fn change_to_char(change: &Change<(), gix::submodule::Status>) -> u8 { // Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613 match change { Change::Removed => b'D', diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index fb60a14a462..678306f1073 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -230,11 +230,12 @@ pub fn main() -> Result<()> { statistics, thread_limit: thread_limit.or(cfg!(target_os = "macos").then_some(3)), // TODO: make this a configurable when in `gix`, this seems to be optimal on MacOS, linux scales though! MacOS also scales if reading a lot of files for refresh index allow_write: !no_write, - submodules: match submodules { + submodules: submodules.map(|submodules| match submodules { Submodules::All => core::repository::status::Submodules::All, Submodules::RefChange => core::repository::status::Submodules::RefChange, Submodules::Modifications => core::repository::status::Submodules::Modifications, - }, + Submodules::None => core::repository::status::Submodules::None, + }), }, ) }, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 7a9c3c4f749..2ea0fc440ae 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -213,14 +213,16 @@ pub mod status { RefChange, /// See if there are worktree modifications compared to the index, but do not check for untracked files. Modifications, + /// Ignore all submodule changes. + None, } #[derive(Debug, clap::Parser)] #[command(about = "compute repository status similar to `git status`")] pub struct Platform { - /// Define how to display submodule status. - #[clap(long, default_value = "all")] - pub submodules: Submodules, + /// Define how to display the submodule status. Defaults to git configuration if unset. + #[clap(long)] + pub submodules: Option<Submodules>, /// Print additional statistics to help understanding performance. #[clap(long, short = 's')] pub statistics: bool, From f8ce3d0721b6a53713a9392f2451874f520bc44c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sun, 10 Mar 2024 13:35:54 +0100 Subject: [PATCH 18/26] fix lints for nightly, and clippy --- .github/workflows/ci.yml | 34 +++++++++--------- gitoxide-core/src/repository/credential.rs | 2 -- gix-actor/src/lib.rs | 1 + gix-actor/src/signature/mod.rs | 1 + gix-attributes/src/lib.rs | 4 +++ gix-attributes/src/search/outcome.rs | 10 ++++-- gix-bitmap/src/ewah.rs | 5 +-- gix-bitmap/src/lib.rs | 2 -- gix-chunk/src/file/decode.rs | 2 +- gix-chunk/src/file/index.rs | 2 ++ gix-chunk/src/file/mod.rs | 3 ++ gix-chunk/src/lib.rs | 4 ++- gix-command/src/lib.rs | 1 + gix-commitgraph/src/file/access.rs | 1 - gix-commitgraph/src/file/commit.rs | 1 - gix-commitgraph/src/file/init.rs | 5 +-- gix-commitgraph/src/init.rs | 1 - gix-commitgraph/src/lib.rs | 1 + gix-commitgraph/src/verify.rs | 1 - gix-commitgraph/tests/commitgraph.rs | 1 - gix-config-value/src/boolean.rs | 2 +- gix-config-value/src/color.rs | 2 +- gix-config-value/src/integer.rs | 2 +- gix-config-value/src/lib.rs | 3 ++ gix-config-value/src/path.rs | 1 + gix-config-value/tests/value/boolean.rs | 2 -- gix-config-value/tests/value/color.rs | 2 -- gix-config-value/tests/value/integer.rs | 2 -- gix-config/benches/large_config_file.rs | 2 -- gix-config/src/file/access/comfort.rs | 2 +- gix-config/src/file/access/raw.rs | 2 +- gix-config/src/file/access/read_only.rs | 2 +- gix-config/src/file/impls.rs | 2 +- gix-config/src/file/includes/types.rs | 1 + gix-config/src/file/init/comfort.rs | 1 + gix-config/src/file/init/from_env.rs | 2 -- gix-config/src/file/init/mod.rs | 2 ++ gix-config/src/file/mod.rs | 5 +++ gix-config/src/file/tests.rs | 2 +- gix-config/src/file/util.rs | 3 +- gix-config/src/lib.rs | 3 ++ gix-config/src/lookup.rs | 1 + gix-config/src/parse/events.rs | 2 -- gix-config/src/parse/mod.rs | 2 ++ gix-config/src/parse/section/mod.rs | 2 ++ gix-config/src/parse/tests.rs | 2 +- gix-config/tests/file/access/mutate.rs | 4 +-- .../tests/file/access/raw/raw_multi_value.rs | 2 -- gix-config/tests/file/access/raw/raw_value.rs | 2 -- gix-config/tests/file/access/read_only.rs | 2 +- gix-config/tests/file/impls/mod.rs | 2 -- .../includes/conditional/onbranch.rs | 5 +-- gix-config/tests/file/mutable/section.rs | 6 +--- gix-config/tests/file/write.rs | 2 -- gix-config/tests/parse/from_bytes.rs | 2 -- gix-config/tests/parse/mod.rs | 2 +- gix-config/tests/parse/section.rs | 4 +-- .../examples/git-credential-lite.rs | 2 -- .../examples/invoke-git-credential.rs | 2 -- gix-credentials/src/helper/mod.rs | 2 -- gix-credentials/src/lib.rs | 3 ++ gix-credentials/src/program/main.rs | 4 +-- gix-credentials/src/program/mod.rs | 1 + gix-credentials/src/protocol/context/serde.rs | 3 +- gix-credentials/src/protocol/mod.rs | 1 + gix-credentials/tests/helper/cascade.rs | 2 -- gix-date/src/lib.rs | 2 ++ gix-date/src/parse.rs | 2 +- gix-date/src/time/mod.rs | 1 + gix-diff/src/blob/mod.rs | 2 ++ gix-diff/src/blob/pipeline.rs | 1 + gix-diff/src/blob/platform.rs | 4 +++ gix-diff/src/lib.rs | 1 + gix-diff/src/rewrites/tracker.rs | 1 + gix-diff/src/tree/mod.rs | 2 ++ gix-dir/src/lib.rs | 2 ++ gix-discover/src/lib.rs | 5 +++ gix-discover/src/parse.rs | 1 + gix-discover/src/path.rs | 1 + gix-discover/tests/path/mod.rs | 2 +- gix-features/src/fs.rs | 3 ++ gix-features/src/lib.rs | 4 +++ gix-features/src/parallel/mod.rs | 1 + gix-features/src/zlib/mod.rs | 1 + gix-features/src/zlib/stream/mod.rs | 2 ++ gix-filter/src/driver/delayed.rs | 2 ++ gix-filter/src/driver/mod.rs | 5 +++ gix-filter/src/driver/process/client.rs | 3 ++ gix-filter/src/driver/process/mod.rs | 2 ++ gix-filter/src/driver/process/server.rs | 2 ++ gix-filter/src/eol/mod.rs | 1 + gix-filter/src/lib.rs | 1 + gix-filter/src/pipeline/convert.rs | 3 ++ gix-filter/src/pipeline/mod.rs | 1 + gix-filter/src/worktree/encoding.rs | 1 + gix-filter/src/worktree/mod.rs | 3 ++ gix-fs/src/dir/mod.rs | 2 ++ gix-fs/src/lib.rs | 4 +++ gix-glob/src/lib.rs | 2 ++ gix-glob/src/search/mod.rs | 1 + gix-hash/src/kind.rs | 2 +- gix-hash/src/lib.rs | 3 +- gix-hash/src/oid.rs | 2 +- gix-hash/src/prefix.rs | 3 +- gix-hash/tests/prefix/mod.rs | 2 +- gix-hashtable/src/lib.rs | 1 + gix-ignore/src/lib.rs | 2 ++ gix-index/src/decode/entries.rs | 2 +- gix-index/src/decode/mod.rs | 1 + gix-index/src/entry/mod.rs | 2 ++ gix-index/src/entry/write.rs | 2 -- gix-index/src/extension/decode.rs | 2 -- gix-index/src/extension/iter.rs | 2 -- gix-index/src/extension/link.rs | 1 + gix-index/src/extension/mod.rs | 6 ++++ gix-index/src/extension/tree/decode.rs | 2 -- gix-index/src/extension/tree/mod.rs | 1 + gix-index/src/extension/tree/write.rs | 2 -- gix-index/src/extension/untracked_cache.rs | 2 -- gix-index/src/file/mod.rs | 3 ++ gix-index/src/lib.rs | 8 +++-- gix-index/src/verify.rs | 2 ++ gix-index/src/write.rs | 4 +-- gix-lock/src/lib.rs | 3 ++ gix-macros/src/momo.rs | 36 +++++++++---------- gix-mailmap/src/lib.rs | 2 ++ gix-object/src/commit/message/mod.rs | 1 + gix-object/src/commit/mod.rs | 2 ++ gix-object/src/find.rs | 3 ++ gix-object/src/lib.rs | 7 ++++ gix-object/src/object/convert.rs | 2 -- gix-object/src/parse.rs | 1 - gix-object/src/tag/mod.rs | 2 ++ gix-object/src/tree/mod.rs | 1 + gix-object/src/tree/ref_iter.rs | 4 --- gix-odb/src/alternate/mod.rs | 1 + gix-odb/src/lib.rs | 3 ++ gix-odb/src/store_impls/dynamic/find.rs | 2 +- gix-odb/src/store_impls/dynamic/handle.rs | 1 - gix-odb/src/store_impls/dynamic/init.rs | 2 +- gix-odb/src/store_impls/dynamic/mod.rs | 8 +++++ gix-odb/src/store_impls/dynamic/prefix.rs | 2 ++ gix-odb/src/store_impls/dynamic/verify.rs | 1 + gix-odb/src/store_impls/loose/mod.rs | 4 +++ gix-odb/src/store_impls/loose/verify.rs | 1 + gix-pack/src/bundle/mod.rs | 3 ++ gix-pack/src/cache/delta/from_offsets.rs | 1 - gix-pack/src/cache/delta/mod.rs | 2 ++ gix-pack/src/cache/mod.rs | 1 + gix-pack/src/data/file/decode/entry.rs | 2 +- gix-pack/src/data/file/decode/mod.rs | 2 ++ gix-pack/src/data/file/init.rs | 2 +- gix-pack/src/data/file/mod.rs | 2 ++ gix-pack/src/data/file/verify.rs | 1 + gix-pack/src/data/header.rs | 1 + .../data/input/lookup_ref_delta_objects.rs | 2 -- gix-pack/src/data/mod.rs | 5 ++- gix-pack/src/data/output/count/mod.rs | 1 + gix-pack/src/data/output/entry/mod.rs | 3 +- gix-pack/src/data/output/mod.rs | 3 ++ gix-pack/src/index/mod.rs | 3 ++ gix-pack/src/index/traverse/mod.rs | 2 ++ gix-pack/src/index/verify.rs | 2 ++ gix-pack/src/index/write/mod.rs | 2 +- gix-pack/src/lib.rs | 9 +++-- gix-pack/src/multi_index/chunk.rs | 5 ++- gix-pack/src/multi_index/init.rs | 2 +- gix-pack/src/multi_index/mod.rs | 5 +++ gix-pack/src/multi_index/verify.rs | 2 ++ gix-pack/src/multi_index/write.rs | 1 - gix-pack/src/verify.rs | 1 + gix-pack/tests/pack/data/header.rs | 2 -- gix-packetline/src/decode.rs | 1 + gix-packetline/src/lib.rs | 2 ++ gix-path/src/lib.rs | 1 + gix-pathspec/src/defaults.rs | 1 + gix-pathspec/src/lib.rs | 4 +++ gix-prompt/src/lib.rs | 1 + gix-prompt/src/types.rs | 2 +- gix-protocol/src/fetch/mod.rs | 2 ++ gix-protocol/src/handshake/mod.rs | 1 + gix-protocol/src/handshake/refs/mod.rs | 1 + gix-protocol/src/lib.rs | 3 ++ gix-protocol/src/remote_progress.rs | 2 -- gix-protocol/tests/fetch/mod.rs | 4 +-- gix-quote/src/ansi_c.rs | 1 + gix-quote/src/lib.rs | 1 + gix-ref/src/fullname.rs | 2 +- gix-ref/src/lib.rs | 6 ++++ gix-ref/src/namespace.rs | 5 +-- gix-ref/src/peel.rs | 1 + gix-ref/src/store/file/find.rs | 4 +-- gix-ref/src/store/file/log/iter.rs | 2 ++ gix-ref/src/store/file/log/line.rs | 6 ++-- gix-ref/src/store/file/log/mod.rs | 1 + gix-ref/src/store/file/loose/mod.rs | 3 ++ .../src/store/file/loose/reference/decode.rs | 2 -- gix-ref/src/store/file/loose/reference/mod.rs | 1 + gix-ref/src/store/file/loose/reflog.rs | 3 +- .../loose/reflog/create_or_update/tests.rs | 3 -- gix-ref/src/store/file/mod.rs | 7 ++++ gix-ref/src/store/file/packed.rs | 1 + gix-ref/src/store/file/transaction/mod.rs | 2 ++ gix-ref/src/store/general/handle/find.rs | 4 --- gix-ref/src/store/general/handle/mod.rs | 1 + gix-ref/src/store/mod.rs | 2 ++ gix-ref/src/store/packed/buffer.rs | 1 + gix-ref/src/store/packed/decode.rs | 2 -- gix-ref/src/store/packed/find.rs | 3 +- gix-ref/src/store/packed/mod.rs | 4 +++ gix-ref/src/store/packed/transaction.rs | 2 ++ gix-ref/src/target.rs | 2 +- gix-ref/tests/file/store/find.rs | 2 -- gix-ref/tests/file/store/iter.rs | 2 -- gix-ref/tests/file/transaction/mod.rs | 2 -- .../create_or_update/collisions.rs | 2 -- .../create_or_update/mod.rs | 2 -- .../transaction/prepare_and_commit/delete.rs | 2 -- gix-ref/tests/file/worktree.rs | 2 -- gix-ref/tests/fullname/mod.rs | 2 +- gix-ref/tests/packed/find.rs | 2 -- gix-ref/tests/packed/iter.rs | 2 -- gix-ref/tests/reference/mod.rs | 2 -- gix-ref/tests/transaction/mod.rs | 4 +-- gix-refspec/src/lib.rs | 3 ++ gix-refspec/src/match_group/mod.rs | 1 + gix-refspec/tests/impls/mod.rs | 5 +-- gix-revision/src/lib.rs | 2 ++ gix-revision/src/spec/mod.rs | 1 + gix-revision/src/spec/parse/function.rs | 2 +- gix-revision/src/spec/parse/mod.rs | 1 + gix-revwalk/src/graph/commit.rs | 2 ++ gix-revwalk/src/graph/mod.rs | 3 ++ gix-revwalk/src/lib.rs | 1 + gix-sec/src/lib.rs | 2 ++ gix-status/src/index_as_worktree/mod.rs | 2 ++ gix-status/src/index_as_worktree/traits.rs | 1 + gix-submodule/src/config.rs | 4 +++ gix-submodule/src/lib.rs | 3 ++ gix-tempfile/src/handle.rs | 2 ++ gix-tempfile/src/lib.rs | 1 + gix-tempfile/src/signal.rs | 1 + gix-trace/src/lib.rs | 1 + gix-transport/src/client/async_io/connect.rs | 2 -- gix-transport/src/client/async_io/mod.rs | 1 + .../src/client/blocking_io/connect.rs | 2 -- .../src/client/blocking_io/http/mod.rs | 2 ++ .../client/blocking_io/http/reqwest/mod.rs | 1 + .../client/blocking_io/http/reqwest/remote.rs | 1 - gix-transport/src/client/blocking_io/mod.rs | 3 ++ .../src/client/blocking_io/ssh/mod.rs | 2 ++ gix-transport/src/client/capabilities.rs | 2 ++ gix-transport/src/client/git/blocking_io.rs | 1 + gix-transport/src/client/mod.rs | 1 + gix-transport/src/lib.rs | 1 + gix-traverse/src/commit.rs | 1 + gix-traverse/src/tree/mod.rs | 3 ++ gix-url/src/impls.rs | 5 +-- gix-url/src/lib.rs | 2 ++ gix-utils/src/lib.rs | 4 +++ gix-utils/tests/backoff/mod.rs | 2 +- gix-validate/src/lib.rs | 3 ++ gix-validate/src/reference.rs | 1 + gix-validate/src/submodule.rs | 1 + gix-validate/src/tag.rs | 1 + gix-worktree-state/src/lib.rs | 1 + gix-worktree-stream/src/lib.rs | 1 + gix-worktree/src/lib.rs | 1 + gix-worktree/src/stack/mod.rs | 2 ++ gix-worktree/src/stack/state/mod.rs | 1 + gix/src/clone/checkout.rs | 1 + gix/src/clone/fetch/util.rs | 2 +- gix/src/clone/mod.rs | 2 -- gix/src/commit.rs | 2 ++ gix/src/config/cache/init.rs | 2 +- gix/src/config/mod.rs | 31 ++++++++++++++++ gix/src/config/overrides.rs | 2 -- gix/src/config/snapshot/credential_helpers.rs | 2 +- gix/src/config/tree/mod.rs | 3 ++ gix/src/config/tree/sections/branch.rs | 1 + gix/src/config/tree/sections/checkout.rs | 1 + gix/src/create.rs | 2 +- gix/src/diff.rs | 3 ++ gix/src/env.rs | 1 + gix/src/ext/reference.rs | 4 --- gix/src/ext/rev_spec.rs | 4 --- gix/src/filter.rs | 4 +++ gix/src/head/log.rs | 2 -- gix/src/head/mod.rs | 5 +-- gix/src/head/peel.rs | 3 ++ gix/src/id.rs | 2 ++ gix/src/init.rs | 2 +- gix/src/lib.rs | 17 +++++++++ gix/src/mailmap.rs | 1 + gix/src/object/blob.rs | 2 ++ gix/src/object/errors.rs | 4 +++ gix/src/object/impls.rs | 2 -- gix/src/object/mod.rs | 7 ++-- gix/src/object/peel.rs | 2 ++ gix/src/object/tree/diff/mod.rs | 2 ++ gix/src/object/tree/mod.rs | 2 ++ gix/src/open/repository.rs | 2 +- gix/src/pathspec.rs | 1 + gix/src/reference/edits.rs | 2 ++ gix/src/reference/errors.rs | 7 ++++ gix/src/reference/iter.rs | 2 ++ gix/src/reference/log.rs | 1 + gix/src/reference/mod.rs | 2 ++ gix/src/remote/build.rs | 2 -- gix/src/remote/connection/fetch/mod.rs | 2 ++ .../connection/fetch/update_refs/mod.rs | 3 +- .../connection/fetch/update_refs/tests.rs | 2 -- gix/src/remote/connection/mod.rs | 2 ++ gix/src/remote/errors.rs | 3 ++ gix/src/remote/fetch.rs | 1 + gix/src/remote/init.rs | 2 -- gix/src/remote/mod.rs | 5 +++ gix/src/remote/name.rs | 2 +- gix/src/remote/save.rs | 2 -- gix/src/remote/url/scheme_permission.rs | 2 +- gix/src/repository/config/branch.rs | 2 +- gix/src/repository/diff.rs | 1 + gix/src/repository/filter.rs | 1 + gix/src/repository/mod.rs | 3 ++ gix/src/repository/object.rs | 2 +- gix/src/repository/reference.rs | 2 -- gix/src/repository/remote.rs | 2 -- gix/src/revision/mod.rs | 2 ++ gix/src/revision/spec/mod.rs | 1 + gix/src/revision/spec/parse/mod.rs | 2 ++ gix/src/shallow.rs | 1 + gix/src/status/index_worktree.rs | 2 ++ gix/src/status/mod.rs | 2 ++ gix/src/submodule/errors.rs | 7 ++++ gix/src/submodule/mod.rs | 1 + gix/src/tag.rs | 1 + gix/src/worktree/mod.rs | 1 + gix/tests/reference/mod.rs | 2 -- gix/tests/remote/save.rs | 2 -- gix/tests/repository/config/remote.rs | 1 - src/plumbing/options/free.rs | 5 +++ src/plumbing/options/mod.rs | 6 ++-- 342 files changed, 606 insertions(+), 290 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ce6e9e5b7f..e2901d1a930 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,18 +47,18 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - name: Setup dependencies - run: - sudo apt-get install tree - - uses: extractions/setup-just@v1 - - name: test - env: - CI: true - GITOXIDE_TEST_IGNORE_ARCHIVES: 1 - run: just ci-test + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Setup dependencies + run: + sudo apt-get install tree + - uses: extractions/setup-just@v1 + - name: test + env: + CI: true + GITOXIDE_TEST_IGNORE_ARCHIVES: 1 + run: just ci-test test-fast: strategy: @@ -163,7 +163,7 @@ jobs: components: clippy,rustfmt - uses: extractions/setup-just@v1 - name: Run cargo clippy - run: just clippy -D warnings + run: just clippy -D warnings -A unknown-lints - name: Run cargo doc run: just doc - name: Run cargo fmt @@ -189,10 +189,10 @@ jobs: continue-on-error: ${{ matrix.checks == 'advisories' }} steps: - - uses: actions/checkout@v3 - - uses: EmbarkStudios/cargo-deny-action@v1 - with: - command: check ${{ matrix.checks }} + - uses: actions/checkout@v3 + - uses: EmbarkStudios/cargo-deny-action@v1 + with: + command: check ${{ matrix.checks }} wasm: name: WebAssembly runs-on: ubuntu-latest diff --git a/gitoxide-core/src/repository/credential.rs b/gitoxide-core/src/repository/credential.rs index b9b93b9d057..c901f06f2ba 100644 --- a/gitoxide-core/src/repository/credential.rs +++ b/gitoxide-core/src/repository/credential.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - #[derive(Debug, thiserror::Error)] enum Error { #[error(transparent)] diff --git a/gix-actor/src/lib.rs b/gix-actor/src/lib.rs index e582541cb1a..e7e94770098 100644 --- a/gix-actor/src/lib.rs +++ b/gix-actor/src/lib.rs @@ -22,6 +22,7 @@ use gix_date::Time; mod identity; /// +#[allow(clippy::empty_docs)] pub mod signature; /// A person with name and email. diff --git a/gix-actor/src/signature/mod.rs b/gix-actor/src/signature/mod.rs index 057d4293e7d..34a0f7e28f3 100644 --- a/gix-actor/src/signature/mod.rs +++ b/gix-actor/src/signature/mod.rs @@ -129,5 +129,6 @@ pub(crate) mod write { } /// +#[allow(clippy::empty_docs)] pub mod decode; pub use decode::function::decode; diff --git a/gix-attributes/src/lib.rs b/gix-attributes/src/lib.rs index 812ffe49a75..4b1090fa9bb 100644 --- a/gix-attributes/src/lib.rs +++ b/gix-attributes/src/lib.rs @@ -13,14 +13,18 @@ use kstring::{KString, KStringRef}; mod assignment; /// +#[allow(clippy::empty_docs)] pub mod name; /// +#[allow(clippy::empty_docs)] pub mod state; /// +#[allow(clippy::empty_docs)] pub mod search; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse attribute assignments line by line from `bytes`, and fail the operation on error. diff --git a/gix-attributes/src/search/outcome.rs b/gix-attributes/src/search/outcome.rs index 41d70e5806c..2a0df7e478a 100644 --- a/gix-attributes/src/search/outcome.rs +++ b/gix-attributes/src/search/outcome.rs @@ -26,7 +26,7 @@ impl Outcome { for (order, macro_attributes) in collection.iter().filter_map(|(_, meta)| { (!meta.macro_attributes.is_empty()).then_some((meta.id.0, &meta.macro_attributes)) }) { - self.matches_by_id[order].macro_attributes = macro_attributes.clone() + self.matches_by_id[order].macro_attributes.clone_from(macro_attributes) } for (name, id) in self.selected.iter_mut().filter(|(_, id)| id.is_none()) { @@ -88,7 +88,7 @@ impl Outcome { /// Note that it's safe to call it multiple times, so that it can be called after this instance was used to store a search result. pub fn copy_into(&self, collection: &MetadataCollection, dest: &mut Self) { dest.initialize(collection); - dest.matches_by_id = self.matches_by_id.clone(); + dest.matches_by_id.clone_from(&self.matches_by_id); if dest.patterns.len() != self.patterns.len() { dest.patterns = self.patterns.clone(); } @@ -325,7 +325,11 @@ impl MetadataCollection { }; self.assign_order_to_attributes(attrs); - self.name_to_meta.get_mut(name).expect("just added").macro_attributes = attrs.clone(); + self.name_to_meta + .get_mut(name) + .expect("just added") + .macro_attributes + .clone_from(attrs); order } diff --git a/gix-bitmap/src/ewah.rs b/gix-bitmap/src/ewah.rs index e2575f90500..64d363beb28 100644 --- a/gix-bitmap/src/ewah.rs +++ b/gix-bitmap/src/ewah.rs @@ -1,6 +1,5 @@ -use std::convert::TryInto; - /// +#[allow(clippy::empty_docs)] pub mod decode { /// The error returned by [`decode()`][super::decode()]. #[derive(Debug, thiserror::Error)] @@ -52,8 +51,6 @@ pub fn decode(data: &[u8]) -> Result<(Vec, &[u8]), decode::Error> { } mod access { - use std::convert::{TryFrom, TryInto}; - use super::Vec; impl Vec { diff --git a/gix-bitmap/src/lib.rs b/gix-bitmap/src/lib.rs index 83756d98897..0ccc5824843 100644 --- a/gix-bitmap/src/lib.rs +++ b/gix-bitmap/src/lib.rs @@ -7,8 +7,6 @@ pub mod ewah; pub(crate) mod decode { - use std::convert::TryInto; - #[inline] pub(crate) fn split_at_pos(data: &[u8], pos: usize) -> Option<(&[u8], &[u8])> { if data.len() < pos { diff --git a/gix-chunk/src/file/decode.rs b/gix-chunk/src/file/decode.rs index 22bdc169bd9..5f397d415df 100644 --- a/gix-chunk/src/file/decode.rs +++ b/gix-chunk/src/file/decode.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Range}; +use std::ops::Range; mod error { /// The value returned by [`crate::file::Index::from_bytes()`] diff --git a/gix-chunk/src/file/index.rs b/gix-chunk/src/file/index.rs index c9211ab6fec..e511781a0e4 100644 --- a/gix-chunk/src/file/index.rs +++ b/gix-chunk/src/file/index.rs @@ -3,6 +3,7 @@ use std::ops::Range; use crate::file::Index; /// +#[allow(clippy::empty_docs)] pub mod offset_by_kind { use std::fmt::{Display, Formatter}; @@ -27,6 +28,7 @@ pub mod offset_by_kind { } /// +#[allow(clippy::empty_docs)] pub mod data_by_kind { /// The error returned by [`Index::data_by_id()`][super::Index::data_by_id()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-chunk/src/file/mod.rs b/gix-chunk/src/file/mod.rs index 4ddd94999ab..46b82a7d256 100644 --- a/gix-chunk/src/file/mod.rs +++ b/gix-chunk/src/file/mod.rs @@ -1,9 +1,12 @@ /// +#[allow(clippy::empty_docs)] pub mod decode; /// +#[allow(clippy::empty_docs)] pub mod index; /// +#[allow(clippy::empty_docs)] pub mod write; /// The offset to a chunk as seen relative to the beginning of the file containing it. diff --git a/gix-chunk/src/lib.rs b/gix-chunk/src/lib.rs index 0647e320b1c..e7dc725c7d0 100644 --- a/gix-chunk/src/lib.rs +++ b/gix-chunk/src/lib.rs @@ -10,8 +10,9 @@ pub type Id = [u8; 4]; pub const SENTINEL: Id = [0u8; 4]; /// +#[allow(clippy::empty_docs)] pub mod range { - use std::{convert::TryInto, ops::Range}; + use std::ops::Range; use crate::file; @@ -33,4 +34,5 @@ pub mod range { } /// +#[allow(clippy::empty_docs)] pub mod file; diff --git a/gix-command/src/lib.rs b/gix-command/src/lib.rs index b1bcd87e881..b730f4eb6ba 100644 --- a/gix-command/src/lib.rs +++ b/gix-command/src/lib.rs @@ -330,6 +330,7 @@ pub fn extract_interpreter(executable: &Path) -> Option<shebang::Data> { } /// +#[allow(clippy::empty_docs)] pub mod shebang { use bstr::{BStr, ByteSlice}; use std::ffi::OsString; diff --git a/gix-commitgraph/src/file/access.rs b/gix-commitgraph/src/file/access.rs index 57c75ae569c..6ccb98cc322 100644 --- a/gix-commitgraph/src/file/access.rs +++ b/gix-commitgraph/src/file/access.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryInto, fmt::{Debug, Formatter}, path::Path, }; diff --git a/gix-commitgraph/src/file/commit.rs b/gix-commitgraph/src/file/commit.rs index 14fe15e8413..db47cfcead9 100644 --- a/gix-commitgraph/src/file/commit.rs +++ b/gix-commitgraph/src/file/commit.rs @@ -1,6 +1,5 @@ //! Low-level operations on individual commits. use std::{ - convert::TryInto, fmt::{Debug, Formatter}, slice::Chunks, }; diff --git a/gix-commitgraph/src/file/init.rs b/gix-commitgraph/src/file/init.rs index 51c2739501f..69311a274d9 100644 --- a/gix-commitgraph/src/file/init.rs +++ b/gix-commitgraph/src/file/init.rs @@ -1,8 +1,5 @@ +use std::path::Path; use std::path::PathBuf; -use std::{ - convert::{TryFrom, TryInto}, - path::Path, -}; use bstr::ByteSlice; use memmap2::Mmap; diff --git a/gix-commitgraph/src/init.rs b/gix-commitgraph/src/init.rs index 0b03ba9462d..a1f98f3d24a 100644 --- a/gix-commitgraph/src/init.rs +++ b/gix-commitgraph/src/init.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryFrom, io::{BufRead, BufReader}, path::{Path, PathBuf}, }; diff --git a/gix-commitgraph/src/lib.rs b/gix-commitgraph/src/lib.rs index d9f4d36bf2f..5d60b9c2ed4 100644 --- a/gix-commitgraph/src/lib.rs +++ b/gix-commitgraph/src/lib.rs @@ -51,6 +51,7 @@ pub fn at(path: impl AsRef<Path>) -> Result<Graph, init::Error> { mod access; pub mod file; /// +#[allow(clippy::empty_docs)] pub mod init; pub mod verify; diff --git a/gix-commitgraph/src/verify.rs b/gix-commitgraph/src/verify.rs index 56eb721e6e3..9a37cff83ad 100644 --- a/gix-commitgraph/src/verify.rs +++ b/gix-commitgraph/src/verify.rs @@ -2,7 +2,6 @@ use std::{ cmp::{max, min}, collections::BTreeMap, - convert::TryInto, path::PathBuf, }; diff --git a/gix-commitgraph/tests/commitgraph.rs b/gix-commitgraph/tests/commitgraph.rs index 94568fe5cfa..3444536ef8a 100644 --- a/gix-commitgraph/tests/commitgraph.rs +++ b/gix-commitgraph/tests/commitgraph.rs @@ -1,6 +1,5 @@ use std::{ collections::{HashMap, HashSet}, - convert::{TryFrom, TryInto}, hash::BuildHasher, io::{BufRead, Cursor}, path::Path, diff --git a/gix-config-value/src/boolean.rs b/gix-config-value/src/boolean.rs index 908e11a300a..ff146e7f17c 100644 --- a/gix-config-value/src/boolean.rs +++ b/gix-config-value/src/boolean.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, ffi::OsString, fmt::Display}; +use std::{borrow::Cow, ffi::OsString, fmt::Display}; use bstr::{BStr, BString, ByteSlice}; diff --git a/gix-config-value/src/color.rs b/gix-config-value/src/color.rs index 5cc0b997f40..884a346d37a 100644 --- a/gix-config-value/src/color.rs +++ b/gix-config-value/src/color.rs @@ -1,5 +1,5 @@ #![allow(missing_docs)] -use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt::Display, str::FromStr}; use bstr::{BStr, BString}; diff --git a/gix-config-value/src/integer.rs b/gix-config-value/src/integer.rs index 4286a83b5be..4864b4b3e41 100644 --- a/gix-config-value/src/integer.rs +++ b/gix-config-value/src/integer.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt::Display, str::FromStr}; use bstr::{BStr, BString}; diff --git a/gix-config-value/src/lib.rs b/gix-config-value/src/lib.rs index b3be069e328..7dd7e89c9e1 100644 --- a/gix-config-value/src/lib.rs +++ b/gix-config-value/src/lib.rs @@ -37,10 +37,13 @@ impl Error { mod boolean; /// +#[allow(clippy::empty_docs)] pub mod color; /// +#[allow(clippy::empty_docs)] pub mod integer; /// +#[allow(clippy::empty_docs)] pub mod path; mod types; diff --git a/gix-config-value/src/path.rs b/gix-config-value/src/path.rs index 58b5970eecd..23550f7cb16 100644 --- a/gix-config-value/src/path.rs +++ b/gix-config-value/src/path.rs @@ -5,6 +5,7 @@ use bstr::BStr; use crate::Path; /// +#[allow(clippy::empty_docs)] pub mod interpolate { use std::path::PathBuf; diff --git a/gix-config-value/tests/value/boolean.rs b/gix-config-value/tests/value/boolean.rs index 8fe8b43f0d4..7af370c8c97 100644 --- a/gix-config-value/tests/value/boolean.rs +++ b/gix-config-value/tests/value/boolean.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config_value::Boolean; use crate::b; diff --git a/gix-config-value/tests/value/color.rs b/gix-config-value/tests/value/color.rs index 18baaba4c92..7ff5b2bd649 100644 --- a/gix-config-value/tests/value/color.rs +++ b/gix-config-value/tests/value/color.rs @@ -110,8 +110,6 @@ mod attribute { } mod from_git { - use std::convert::TryFrom; - use bstr::BStr; use gix_config_value::Color; diff --git a/gix-config-value/tests/value/integer.rs b/gix-config-value/tests/value/integer.rs index 8a5d8016021..6caa7f3492e 100644 --- a/gix-config-value/tests/value/integer.rs +++ b/gix-config-value/tests/value/integer.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config_value::{integer::Suffix, Integer}; use crate::b; diff --git a/gix-config/benches/large_config_file.rs b/gix-config/benches/large_config_file.rs index 51f6e1fd86b..ff42afb6f1c 100644 --- a/gix-config/benches/large_config_file.rs +++ b/gix-config/benches/large_config_file.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use criterion::{black_box, criterion_group, criterion_main, Criterion}; use gix_config::{parse::Events, File}; diff --git a/gix-config/src/file/access/comfort.rs b/gix-config/src/file/access/comfort.rs index ed62e779269..0a18bd18491 100644 --- a/gix-config/src/file/access/comfort.rs +++ b/gix-config/src/file/access/comfort.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use bstr::BStr; diff --git a/gix-config/src/file/access/raw.rs b/gix-config/src/file/access/raw.rs index 3736bf3a286..a3d65ce2a45 100644 --- a/gix-config/src/file/access/raw.rs +++ b/gix-config/src/file/access/raw.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashMap, convert::TryInto}; +use std::{borrow::Cow, collections::HashMap}; use bstr::BStr; use smallvec::ToSmallVec; diff --git a/gix-config/src/file/access/read_only.rs b/gix-config/src/file/access/read_only.rs index eb1071fe2e1..6072776a7d6 100644 --- a/gix-config/src/file/access/read_only.rs +++ b/gix-config/src/file/access/read_only.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use bstr::{BStr, ByteSlice}; use gix_features::threading::OwnShared; diff --git a/gix-config/src/file/impls.rs b/gix-config/src/file/impls.rs index c26df5fb81a..884e7ce346b 100644 --- a/gix-config/src/file/impls.rs +++ b/gix-config/src/file/impls.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt::Display, str::FromStr}; use bstr::{BStr, BString, ByteVec}; diff --git a/gix-config/src/file/includes/types.rs b/gix-config/src/file/includes/types.rs index 64306bd9ca0..54b1eb5bfe0 100644 --- a/gix-config/src/file/includes/types.rs +++ b/gix-config/src/file/includes/types.rs @@ -115,6 +115,7 @@ impl Default for Options<'_> { } /// +#[allow(clippy::empty_docs)] pub mod conditional { /// Options to handle conditional includes like `includeIf.<condition>.path`. #[derive(Clone, Copy, Default)] diff --git a/gix-config/src/file/init/comfort.rs b/gix-config/src/file/init/comfort.rs index 4a5a1c68b25..dd772518366 100644 --- a/gix-config/src/file/init/comfort.rs +++ b/gix-config/src/file/init/comfort.rs @@ -141,6 +141,7 @@ impl File<'static> { } /// +#[allow(clippy::empty_docs)] pub mod from_git_dir { use crate::file::init; diff --git a/gix-config/src/file/init/from_env.rs b/gix-config/src/file/init/from_env.rs index 2c487e5957a..6a59f10324d 100644 --- a/gix-config/src/file/init/from_env.rs +++ b/gix-config/src/file/init/from_env.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::{BStr, ByteSlice}; use crate::{file, file::init, parse, parse::section, path::interpolate, File}; diff --git a/gix-config/src/file/init/mod.rs b/gix-config/src/file/init/mod.rs index 90fa70cbcf9..1076011fcc6 100644 --- a/gix-config/src/file/init/mod.rs +++ b/gix-config/src/file/init/mod.rs @@ -10,8 +10,10 @@ pub use types::{Error, Options}; mod comfort; /// +#[allow(clippy::empty_docs)] pub mod from_env; /// +#[allow(clippy::empty_docs)] pub mod from_paths; impl<'a> File<'a> { diff --git a/gix-config/src/file/mod.rs b/gix-config/src/file/mod.rs index e99c6eb94d9..8611ad0c7e1 100644 --- a/gix-config/src/file/mod.rs +++ b/gix-config/src/file/mod.rs @@ -13,19 +13,23 @@ mod mutable; pub use mutable::{multi_value::MultiValueMut, section::SectionMut, value::ValueMut}; /// +#[allow(clippy::empty_docs)] pub mod init; mod access; mod impls; /// +#[allow(clippy::empty_docs)] pub mod includes; mod meta; mod util; /// +#[allow(clippy::empty_docs)] pub mod section; /// +#[allow(clippy::empty_docs)] pub mod rename_section { /// The error returned by [`File::rename_section(…)`][crate::File::rename_section()]. #[derive(Debug, thiserror::Error)] @@ -39,6 +43,7 @@ pub mod rename_section { } /// +#[allow(clippy::empty_docs)] pub mod set_raw_value { /// The error returned by [`File::set_raw_value(…)`][crate::File::set_raw_value()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-config/src/file/tests.rs b/gix-config/src/file/tests.rs index 58f16ec1547..adb11e5a291 100644 --- a/gix-config/src/file/tests.rs +++ b/gix-config/src/file/tests.rs @@ -6,7 +6,7 @@ use crate::{ }; mod try_from { - use std::{borrow::Cow, collections::HashMap, convert::TryFrom}; + use std::{borrow::Cow, collections::HashMap}; use super::{bodies, headers}; use crate::{ diff --git a/gix-config/src/file/util.rs b/gix-config/src/file/util.rs index 5c60f1fd5a0..78bc4736f63 100644 --- a/gix-config/src/file/util.rs +++ b/gix-config/src/file/util.rs @@ -118,8 +118,7 @@ impl<'event> File<'event> { &'a self, section_name: &'a str, subsection_name: Option<&BStr>, - ) -> Result<impl Iterator<Item = SectionId> + ExactSizeIterator + DoubleEndedIterator + '_, lookup::existing::Error> - { + ) -> Result<impl ExactSizeIterator<Item = SectionId> + DoubleEndedIterator + '_, lookup::existing::Error> { let section_name = section::Name::from_str_unchecked(section_name); let section_ids = self .section_lookup_tree diff --git a/gix-config/src/lib.rs b/gix-config/src/lib.rs index b1740cdd879..bbbe36d8ded 100644 --- a/gix-config/src/lib.rs +++ b/gix-config/src/lib.rs @@ -40,13 +40,16 @@ pub mod file; /// +#[allow(clippy::empty_docs)] pub mod lookup; pub mod parse; /// +#[allow(clippy::empty_docs)] pub mod value; pub use gix_config_value::{color, integer, path, Boolean, Color, Integer, Path}; mod types; pub use types::{File, Source}; /// +#[allow(clippy::empty_docs)] pub mod source; diff --git a/gix-config/src/lookup.rs b/gix-config/src/lookup.rs index 7814978125d..25d8d9d1751 100644 --- a/gix-config/src/lookup.rs +++ b/gix-config/src/lookup.rs @@ -9,6 +9,7 @@ pub enum Error<E> { } /// +#[allow(clippy::empty_docs)] pub mod existing { /// The error when looking up a value that doesn't exist, for example via [`File::value()`][crate::File::value()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-config/src/parse/events.rs b/gix-config/src/parse/events.rs index d742bafe04c..5e0c8f06bf1 100644 --- a/gix-config/src/parse/events.rs +++ b/gix-config/src/parse/events.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use smallvec::SmallVec; use crate::{ diff --git a/gix-config/src/parse/mod.rs b/gix-config/src/parse/mod.rs index ac75853a0ca..e11e77c846a 100644 --- a/gix-config/src/parse/mod.rs +++ b/gix-config/src/parse/mod.rs @@ -23,9 +23,11 @@ pub use events_type::{Events, FrontMatterEvents}; mod comment; mod error; /// +#[allow(clippy::empty_docs)] pub mod section; /// +#[allow(clippy::empty_docs)] mod key; pub use key::{parse_unvalidated as key, Key}; diff --git a/gix-config/src/parse/section/mod.rs b/gix-config/src/parse/section/mod.rs index fade4fa147f..5c4489ea670 100644 --- a/gix-config/src/parse/section/mod.rs +++ b/gix-config/src/parse/section/mod.rs @@ -5,6 +5,7 @@ use bstr::BStr; use crate::parse::{Event, Section}; /// +#[allow(clippy::empty_docs)] pub mod header; pub(crate) mod unvalidated; @@ -48,6 +49,7 @@ mod types { macro_rules! generate_case_insensitive { ($name:ident, $module:ident, $err_doc:literal, $validate:ident, $cow_inner_type:ty, $comment:literal) => { /// + #[allow(clippy::empty_docs)] pub mod $module { /// The error returned when `TryFrom` is invoked to create an instance. #[derive(Debug, thiserror::Error, Copy, Clone)] diff --git a/gix-config/src/parse/tests.rs b/gix-config/src/parse/tests.rs index edb5dee6ee3..33b015173a5 100644 --- a/gix-config/src/parse/tests.rs +++ b/gix-config/src/parse/tests.rs @@ -109,7 +109,7 @@ pub(crate) mod util { //! This module is only included for tests, and contains common unit test helper //! functions. - use std::{borrow::Cow, convert::TryFrom}; + use std::borrow::Cow; use crate::parse::{section, Comment, Event}; diff --git a/gix-config/tests/file/access/mutate.rs b/gix-config/tests/file/access/mutate.rs index 0788a3a31a4..48826593f6c 100644 --- a/gix-config/tests/file/access/mutate.rs +++ b/gix-config/tests/file/access/mutate.rs @@ -1,6 +1,4 @@ mod remove_section { - use std::convert::TryFrom; - #[test] fn removal_of_all_sections_programmatically_with_sections_and_ids_by_name() { let mut file = gix_config::File::try_from("[core] \na = b\nb=c\n\n[core \"name\"]\nd = 1\ne = 2").unwrap(); @@ -47,7 +45,7 @@ mod remove_section { } } mod rename_section { - use std::{borrow::Cow, convert::TryFrom}; + use std::borrow::Cow; use gix_config::{file::rename_section, parse::section}; diff --git a/gix-config/tests/file/access/raw/raw_multi_value.rs b/gix-config/tests/file/access/raw/raw_multi_value.rs index 75fa1e20e74..3309713ea15 100644 --- a/gix-config/tests/file/access/raw/raw_multi_value.rs +++ b/gix-config/tests/file/access/raw/raw_multi_value.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config::{lookup, File}; use crate::file::cow_str; diff --git a/gix-config/tests/file/access/raw/raw_value.rs b/gix-config/tests/file/access/raw/raw_value.rs index 8b6c52bd149..e6f6d1c3a8b 100644 --- a/gix-config/tests/file/access/raw/raw_value.rs +++ b/gix-config/tests/file/access/raw/raw_value.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config::{lookup, File}; #[test] diff --git a/gix-config/tests/file/access/read_only.rs b/gix-config/tests/file/access/read_only.rs index a5d7fa8abd2..f8f8cb9e4f9 100644 --- a/gix-config/tests/file/access/read_only.rs +++ b/gix-config/tests/file/access/read_only.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use bstr::BStr; use gix_config::{ diff --git a/gix-config/tests/file/impls/mod.rs b/gix-config/tests/file/impls/mod.rs index 885f326e1bb..5247d2ea7a2 100644 --- a/gix-config/tests/file/impls/mod.rs +++ b/gix-config/tests/file/impls/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config::File; #[test] diff --git a/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs b/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs index 988a787094a..b6f3d9933c6 100644 --- a/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs +++ b/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs @@ -1,7 +1,4 @@ -use std::{ - convert::{TryFrom, TryInto}, - fs, -}; +use std::fs; use bstr::{BString, ByteSlice}; use gix_config::file::{ diff --git a/gix-config/tests/file/mutable/section.rs b/gix-config/tests/file/mutable/section.rs index 17ef1df586b..6ee00b30c87 100644 --- a/gix-config/tests/file/mutable/section.rs +++ b/gix-config/tests/file/mutable/section.rs @@ -90,8 +90,6 @@ mod pop { } mod set { - use std::convert::TryInto; - use super::multi_value_section; #[test] @@ -121,8 +119,6 @@ mod set { } mod push { - use std::convert::{TryFrom, TryInto}; - use gix_config::parse::section::Key; use crate::file::cow_str; @@ -230,7 +226,7 @@ mod push_with_comment { } mod set_leading_whitespace { - use std::{borrow::Cow, convert::TryFrom}; + use std::borrow::Cow; use bstr::BString; use gix_config::parse::section::Key; diff --git a/gix-config/tests/file/write.rs b/gix-config/tests/file/write.rs index 26c72c7f72b..224d800fed9 100644 --- a/gix-config/tests/file/write.rs +++ b/gix-config/tests/file/write.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::ByteVec; use gix_config::file::{init, Metadata}; diff --git a/gix-config/tests/parse/from_bytes.rs b/gix-config/tests/parse/from_bytes.rs index df58531ba01..736ddfedca0 100644 --- a/gix-config/tests/parse/from_bytes.rs +++ b/gix-config/tests/parse/from_bytes.rs @@ -1,5 +1,3 @@ -use gix_config::parse::Events; - use super::*; #[test] diff --git a/gix-config/tests/parse/mod.rs b/gix-config/tests/parse/mod.rs index a4bb1ab5bb8..01c4a53c90c 100644 --- a/gix-config/tests/parse/mod.rs +++ b/gix-config/tests/parse/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use gix_config::parse::{Event, Events, Section}; diff --git a/gix-config/tests/parse/section.rs b/gix-config/tests/parse/section.rs index 872052e9031..afdff7d9350 100644 --- a/gix-config/tests/parse/section.rs +++ b/gix-config/tests/parse/section.rs @@ -70,8 +70,6 @@ mod header { } } mod name { - use std::convert::TryFrom; - use gix_config::parse::section::Name; #[test] @@ -90,7 +88,7 @@ mod name { } mod key { - use std::{cmp::Ordering, convert::TryFrom}; + use std::cmp::Ordering; use gix_config::parse::section::Key; diff --git a/gix-credentials/examples/git-credential-lite.rs b/gix-credentials/examples/git-credential-lite.rs index c4e40c29177..c91dec18d3e 100644 --- a/gix-credentials/examples/git-credential-lite.rs +++ b/gix-credentials/examples/git-credential-lite.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - /// Run like this `echo url=https://example.com | cargo run --example git-credential-light -- fill` pub fn main() -> Result<(), gix_credentials::program::main::Error> { gix_credentials::program::main( diff --git a/gix-credentials/examples/invoke-git-credential.rs b/gix-credentials/examples/invoke-git-credential.rs index cba3f5f706f..5d4f780f7aa 100644 --- a/gix-credentials/examples/invoke-git-credential.rs +++ b/gix-credentials/examples/invoke-git-credential.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - /// Invokes `git credential` with the passed url as argument and prints obtained credentials. pub fn main() -> Result<(), Box<dyn std::error::Error>> { let out = gix_credentials::builtin(gix_credentials::helper::Action::get_for_url( diff --git a/gix-credentials/src/helper/mod.rs b/gix-credentials/src/helper/mod.rs index 107d6db7a16..015ca0aaf9f 100644 --- a/gix-credentials/src/helper/mod.rs +++ b/gix-credentials/src/helper/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::{BStr, BString}; use crate::{protocol, protocol::Context, Program}; diff --git a/gix-credentials/src/lib.rs b/gix-credentials/src/lib.rs index 4e3e21d99a3..89e72e53d96 100644 --- a/gix-credentials/src/lib.rs +++ b/gix-credentials/src/lib.rs @@ -21,12 +21,15 @@ pub struct Program { } /// +#[allow(clippy::empty_docs)] pub mod helper; /// +#[allow(clippy::empty_docs)] pub mod program; /// +#[allow(clippy::empty_docs)] pub mod protocol; /// Call the `git credential` helper program performing the given `action`, which reads all context from the git configuration diff --git a/gix-credentials/src/program/main.rs b/gix-credentials/src/program/main.rs index b3417f92372..34f4163a01e 100644 --- a/gix-credentials/src/program/main.rs +++ b/gix-credentials/src/program/main.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, ffi::OsString}; +use std::ffi::OsString; use bstr::BString; @@ -60,7 +60,7 @@ pub enum Error { } pub(crate) mod function { - use std::{convert::TryInto, ffi::OsString}; + use std::ffi::OsString; use crate::{ program::main::{Action, Error}, diff --git a/gix-credentials/src/program/mod.rs b/gix-credentials/src/program/mod.rs index b7b1150d3dd..213c0272128 100644 --- a/gix-credentials/src/program/mod.rs +++ b/gix-credentials/src/program/mod.rs @@ -144,5 +144,6 @@ impl Program { } /// +#[allow(clippy::empty_docs)] pub mod main; pub use main::function::main; diff --git a/gix-credentials/src/protocol/context/serde.rs b/gix-credentials/src/protocol/context/serde.rs index 0f3b4b02487..76d1673a776 100644 --- a/gix-credentials/src/protocol/context/serde.rs +++ b/gix-credentials/src/protocol/context/serde.rs @@ -49,9 +49,8 @@ mod write { } /// +#[allow(clippy::empty_docs)] pub mod decode { - use std::convert::TryFrom; - use bstr::{BString, ByteSlice}; use crate::protocol::{context, context::serde::validate, Context}; diff --git a/gix-credentials/src/protocol/mod.rs b/gix-credentials/src/protocol/mod.rs index ec168ffb3fc..2386af8fe98 100644 --- a/gix-credentials/src/protocol/mod.rs +++ b/gix-credentials/src/protocol/mod.rs @@ -83,4 +83,5 @@ pub fn helper_outcome_to_result(outcome: Option<helper::Outcome>, action: helper } /// +#[allow(clippy::empty_docs)] pub mod context; diff --git a/gix-credentials/tests/helper/cascade.rs b/gix-credentials/tests/helper/cascade.rs index f97a64dcb4e..32ffaaa7edb 100644 --- a/gix-credentials/tests/helper/cascade.rs +++ b/gix-credentials/tests/helper/cascade.rs @@ -1,6 +1,4 @@ mod invoke { - use std::convert::TryInto; - use bstr::{ByteSlice, ByteVec}; use gix_credentials::{ helper::{Action, Cascade}, diff --git a/gix-date/src/lib.rs b/gix-date/src/lib.rs index 0ada471473f..42dda4a533c 100644 --- a/gix-date/src/lib.rs +++ b/gix-date/src/lib.rs @@ -11,9 +11,11 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod time; /// +#[allow(clippy::empty_docs)] pub mod parse; pub use parse::function::parse; diff --git a/gix-date/src/parse.rs b/gix-date/src/parse.rs index 323292cba4c..da0691271b2 100644 --- a/gix-date/src/parse.rs +++ b/gix-date/src/parse.rs @@ -86,7 +86,7 @@ pub(crate) mod function { } mod relative { - use std::{convert::TryInto, str::FromStr, time::SystemTime}; + use std::{str::FromStr, time::SystemTime}; use time::{Duration, OffsetDateTime}; diff --git a/gix-date/src/time/mod.rs b/gix-date/src/time/mod.rs index 22c4e42f0a6..e2e1ff77afd 100644 --- a/gix-date/src/time/mod.rs +++ b/gix-date/src/time/mod.rs @@ -29,6 +29,7 @@ pub enum Format<'a> { } /// +#[allow(clippy::empty_docs)] pub mod format; mod init; mod write; diff --git a/gix-diff/src/blob/mod.rs b/gix-diff/src/blob/mod.rs index 0c76c2d918c..9af819c9877 100644 --- a/gix-diff/src/blob/mod.rs +++ b/gix-diff/src/blob/mod.rs @@ -6,9 +6,11 @@ use bstr::BString; pub use imara_diff::*; /// +#[allow(clippy::empty_docs)] pub mod pipeline; /// +#[allow(clippy::empty_docs)] pub mod platform; /// Information about the diff performed to detect similarity. diff --git a/gix-diff/src/blob/pipeline.rs b/gix-diff/src/blob/pipeline.rs index 45018218426..f67de77a14c 100644 --- a/gix-diff/src/blob/pipeline.rs +++ b/gix-diff/src/blob/pipeline.rs @@ -111,6 +111,7 @@ impl Mode { } /// +#[allow(clippy::empty_docs)] pub mod convert_to_diffable { use std::collections::TryReserveError; diff --git a/gix-diff/src/blob/platform.rs b/gix-diff/src/blob/platform.rs index 1c2d8fa0180..091c5a9cf3c 100644 --- a/gix-diff/src/blob/platform.rs +++ b/gix-diff/src/blob/platform.rs @@ -90,6 +90,7 @@ pub struct Resource<'a> { } /// +#[allow(clippy::empty_docs)] pub mod resource { use crate::blob::{ pipeline, @@ -150,6 +151,7 @@ pub mod resource { } /// +#[allow(clippy::empty_docs)] pub mod set_resource { use bstr::BString; @@ -179,6 +181,7 @@ pub mod set_resource { } /// +#[allow(clippy::empty_docs)] pub mod prepare_diff { use bstr::BStr; @@ -245,6 +248,7 @@ pub mod prepare_diff { } /// +#[allow(clippy::empty_docs)] pub mod prepare_diff_command { use std::ops::{Deref, DerefMut}; diff --git a/gix-diff/src/lib.rs b/gix-diff/src/lib.rs index 1fe8d2e6bb4..ac528ccb2be 100644 --- a/gix-diff/src/lib.rs +++ b/gix-diff/src/lib.rs @@ -44,6 +44,7 @@ pub struct Rewrites { pub mod rewrites; /// +#[allow(clippy::empty_docs)] pub mod tree; /// diff --git a/gix-diff/src/rewrites/tracker.rs b/gix-diff/src/rewrites/tracker.rs index 6652efb25e7..7a255c456a1 100644 --- a/gix-diff/src/rewrites/tracker.rs +++ b/gix-diff/src/rewrites/tracker.rs @@ -118,6 +118,7 @@ pub mod visit { } /// +#[allow(clippy::empty_docs)] pub mod emit { /// The error returned by [Tracker::emit()](super::Tracker::emit()). #[derive(Debug, thiserror::Error)] diff --git a/gix-diff/src/tree/mod.rs b/gix-diff/src/tree/mod.rs index 77e125e910e..0c87e203806 100644 --- a/gix-diff/src/tree/mod.rs +++ b/gix-diff/src/tree/mod.rs @@ -34,9 +34,11 @@ where } /// +#[allow(clippy::empty_docs)] pub mod changes; /// +#[allow(clippy::empty_docs)] pub mod visit; #[doc(inline)] pub use visit::Visit; diff --git a/gix-dir/src/lib.rs b/gix-dir/src/lib.rs index fffe2d46eb4..79565773bd2 100644 --- a/gix-dir/src/lib.rs +++ b/gix-dir/src/lib.rs @@ -59,8 +59,10 @@ pub struct Entry { } /// +#[allow(clippy::empty_docs)] pub mod entry; /// +#[allow(clippy::empty_docs)] pub mod walk; pub use walk::function::walk; diff --git a/gix-discover/src/lib.rs b/gix-discover/src/lib.rs index a035c5cf6bb..d86fa0e1f75 100644 --- a/gix-discover/src/lib.rs +++ b/gix-discover/src/lib.rs @@ -11,9 +11,11 @@ pub const DOT_GIT_DIR: &str = ".git"; pub const MODULES: &str = "modules"; /// +#[allow(clippy::empty_docs)] pub mod repository; /// +#[allow(clippy::empty_docs)] pub mod is_git { use std::path::PathBuf; @@ -46,11 +48,14 @@ mod is; pub use is::{bare as is_bare, git as is_git, submodule_git_dir as is_submodule_git_dir}; /// +#[allow(clippy::empty_docs)] pub mod upwards; pub use upwards::function::{discover as upwards, discover_opts as upwards_opts}; /// +#[allow(clippy::empty_docs)] pub mod path; /// +#[allow(clippy::empty_docs)] pub mod parse; diff --git a/gix-discover/src/parse.rs b/gix-discover/src/parse.rs index 58971832755..7ff11fb7da2 100644 --- a/gix-discover/src/parse.rs +++ b/gix-discover/src/parse.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use bstr::ByteSlice; /// +#[allow(clippy::empty_docs)] pub mod gitdir { use bstr::BString; diff --git a/gix-discover/src/path.rs b/gix-discover/src/path.rs index f1e446f15d2..2eb841b3d79 100644 --- a/gix-discover/src/path.rs +++ b/gix-discover/src/path.rs @@ -3,6 +3,7 @@ use std::{io::Read, path::PathBuf}; use crate::DOT_GIT_DIR; /// +#[allow(clippy::empty_docs)] pub mod from_gitdir_file { /// The error returned by [`from_gitdir_file()`][crate::path::from_gitdir_file()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-discover/tests/path/mod.rs b/gix-discover/tests/path/mod.rs index b047810bbbd..b0bac5468fa 100644 --- a/gix-discover/tests/path/mod.rs +++ b/gix-discover/tests/path/mod.rs @@ -4,7 +4,7 @@ mod from_git_dir_file { path::{Path, PathBuf}, }; - use gix_testtools::{tempfile, tempfile::NamedTempFile}; + use gix_testtools::tempfile::NamedTempFile; #[cfg(not(windows))] #[test] diff --git a/gix-features/src/fs.rs b/gix-features/src/fs.rs index 6f9d2191d67..97f40c367f9 100644 --- a/gix-features/src/fs.rs +++ b/gix-features/src/fs.rs @@ -116,6 +116,7 @@ mod walkdir_precompose { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "fs-read-dir")] pub mod read_dir { use std::borrow::Cow; @@ -142,6 +143,7 @@ pub mod read_dir { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "fs-walkdir-parallel")] pub mod walkdir { use std::borrow::Cow; @@ -263,6 +265,7 @@ pub mod walkdir { } /// +#[allow(clippy::empty_docs)] #[cfg(all(feature = "walkdir", not(feature = "fs-walkdir-parallel")))] pub mod walkdir { use std::borrow::Cow; diff --git a/gix-features/src/lib.rs b/gix-features/src/lib.rs index 18342b1d92e..a5674d28206 100644 --- a/gix-features/src/lib.rs +++ b/gix-features/src/lib.rs @@ -15,8 +15,10 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod cache; /// +#[allow(clippy::empty_docs)] pub mod decode; pub mod fs; pub mod hash; @@ -30,10 +32,12 @@ pub mod threading; pub use gix_trace as trace; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "zlib")] pub mod zlib; /// +#[allow(clippy::empty_docs)] pub mod iter { /// An iterator over chunks of input, producing `Vec<Item>` with a size of `size`, with the last chunk being the remainder and thus /// potentially smaller than `size`. diff --git a/gix-features/src/parallel/mod.rs b/gix-features/src/parallel/mod.rs index 5a0a4b5890d..a3000c86c31 100644 --- a/gix-features/src/parallel/mod.rs +++ b/gix-features/src/parallel/mod.rs @@ -174,5 +174,6 @@ where } /// +#[allow(clippy::empty_docs)] pub mod reduce; pub use reduce::Reduce; diff --git a/gix-features/src/zlib/mod.rs b/gix-features/src/zlib/mod.rs index f55660075eb..cc977b4faa8 100644 --- a/gix-features/src/zlib/mod.rs +++ b/gix-features/src/zlib/mod.rs @@ -49,4 +49,5 @@ impl Inflate { } /// +#[allow(clippy::empty_docs)] pub mod stream; diff --git a/gix-features/src/zlib/stream/mod.rs b/gix-features/src/zlib/stream/mod.rs index 7fb239d3644..c944412e093 100644 --- a/gix-features/src/zlib/stream/mod.rs +++ b/gix-features/src/zlib/stream/mod.rs @@ -1,4 +1,6 @@ /// +#[allow(clippy::empty_docs)] pub mod deflate; /// +#[allow(clippy::empty_docs)] pub mod inflate; diff --git a/gix-filter/src/driver/delayed.rs b/gix-filter/src/driver/delayed.rs index 4599ac61098..434afb38c81 100644 --- a/gix-filter/src/driver/delayed.rs +++ b/gix-filter/src/driver/delayed.rs @@ -6,6 +6,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod list { use crate::driver; @@ -23,6 +24,7 @@ pub mod list { } /// +#[allow(clippy::empty_docs)] pub mod fetch { use crate::driver; diff --git a/gix-filter/src/driver/mod.rs b/gix-filter/src/driver/mod.rs index 43d3edb2f8b..f85c996ee0c 100644 --- a/gix-filter/src/driver/mod.rs +++ b/gix-filter/src/driver/mod.rs @@ -3,18 +3,23 @@ use std::collections::HashMap; use bstr::{BStr, BString, ByteSlice, ByteVec}; /// +#[allow(clippy::empty_docs)] pub mod init; /// +#[allow(clippy::empty_docs)] pub mod apply; /// +#[allow(clippy::empty_docs)] pub mod shutdown; /// +#[allow(clippy::empty_docs)] pub mod delayed; /// +#[allow(clippy::empty_docs)] pub mod process; /// A literal driver process. diff --git a/gix-filter/src/driver/process/client.rs b/gix-filter/src/driver/process/client.rs index 912b1629091..40a3c97e3c8 100644 --- a/gix-filter/src/driver/process/client.rs +++ b/gix-filter/src/driver/process/client.rs @@ -8,6 +8,7 @@ use crate::driver::{ }; /// +#[allow(clippy::empty_docs)] pub mod handshake { /// The error returned by [Client::handshake()][super::Client::handshake()]. #[derive(Debug, thiserror::Error)] @@ -23,6 +24,7 @@ pub mod handshake { } /// +#[allow(clippy::empty_docs)] pub mod invoke { /// The error returned by [Client::invoke()][super::Client::invoke()]. #[derive(Debug, thiserror::Error)] @@ -33,6 +35,7 @@ pub mod invoke { } /// + #[allow(clippy::empty_docs)] pub mod without_content { /// The error returned by [Client::invoke_without_content()][super::super::Client::invoke_without_content()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-filter/src/driver/process/mod.rs b/gix-filter/src/driver/process/mod.rs index c2f62ddd260..d8e95806ef7 100644 --- a/gix-filter/src/driver/process/mod.rs +++ b/gix-filter/src/driver/process/mod.rs @@ -104,9 +104,11 @@ impl Status { } /// +#[allow(clippy::empty_docs)] pub mod client; /// +#[allow(clippy::empty_docs)] pub mod server; type PacketlineReader<'a, T = std::process::ChildStdout> = diff --git a/gix-filter/src/driver/process/server.rs b/gix-filter/src/driver/process/server.rs index 9a634e5ad38..7b2ad17afd7 100644 --- a/gix-filter/src/driver/process/server.rs +++ b/gix-filter/src/driver/process/server.rs @@ -14,6 +14,7 @@ pub struct Request<'a> { } /// +#[allow(clippy::empty_docs)] pub mod next_request { use bstr::BString; @@ -31,6 +32,7 @@ pub mod next_request { } /// +#[allow(clippy::empty_docs)] pub mod handshake { /// The error returned by [Server::handshake()][super::Server::handshake()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-filter/src/eol/mod.rs b/gix-filter/src/eol/mod.rs index ad1553826e0..7cd7be7eb3f 100644 --- a/gix-filter/src/eol/mod.rs +++ b/gix-filter/src/eol/mod.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod convert_to_git; pub use convert_to_git::function::convert_to_git; diff --git a/gix-filter/src/lib.rs b/gix-filter/src/lib.rs index fbe250607e5..b816237b80c 100644 --- a/gix-filter/src/lib.rs +++ b/gix-filter/src/lib.rs @@ -29,6 +29,7 @@ pub mod worktree; pub mod driver; /// +#[allow(clippy::empty_docs)] pub mod pipeline; /// The standard git filter pipeline comprised of multiple standard filters and support for external filters. diff --git a/gix-filter/src/pipeline/convert.rs b/gix-filter/src/pipeline/convert.rs index 0572dd451b1..8154f98e40b 100644 --- a/gix-filter/src/pipeline/convert.rs +++ b/gix-filter/src/pipeline/convert.rs @@ -5,6 +5,7 @@ use bstr::BStr; use crate::{driver, eol, ident, pipeline::util::Configuration, worktree, Pipeline}; /// +#[allow(clippy::empty_docs)] pub mod configuration { use bstr::BString; @@ -20,6 +21,7 @@ pub mod configuration { } /// +#[allow(clippy::empty_docs)] pub mod to_git { /// A function that fills `buf` `fn(&mut buf)` with the data stored in the index of the file that should be converted. pub type IndexObjectFn<'a> = dyn FnMut(&mut Vec<u8>) -> Result<Option<()>, gix_object::find::Error> + 'a; @@ -44,6 +46,7 @@ pub mod to_git { } /// +#[allow(clippy::empty_docs)] pub mod to_worktree { /// The error returned by [Pipeline::convert_to_worktree()][super::Pipeline::convert_to_worktree()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-filter/src/pipeline/mod.rs b/gix-filter/src/pipeline/mod.rs index 7dff070a4ce..a5f556ee8d5 100644 --- a/gix-filter/src/pipeline/mod.rs +++ b/gix-filter/src/pipeline/mod.rs @@ -110,6 +110,7 @@ impl Pipeline { } /// +#[allow(clippy::empty_docs)] pub mod convert; pub(crate) mod util; diff --git a/gix-filter/src/worktree/encoding.rs b/gix-filter/src/worktree/encoding.rs index 0b75adc96aa..df4b1ee7985 100644 --- a/gix-filter/src/worktree/encoding.rs +++ b/gix-filter/src/worktree/encoding.rs @@ -2,6 +2,7 @@ use bstr::BStr; use encoding_rs::Encoding; /// +#[allow(clippy::empty_docs)] pub mod for_label { use bstr::BString; diff --git a/gix-filter/src/worktree/mod.rs b/gix-filter/src/worktree/mod.rs index 3b13ea49ede..167d1266e32 100644 --- a/gix-filter/src/worktree/mod.rs +++ b/gix-filter/src/worktree/mod.rs @@ -5,12 +5,15 @@ //! can be taken, which we do not yet take unless there is specific examples or problems to solve. /// +#[allow(clippy::empty_docs)] pub mod encoding; /// +#[allow(clippy::empty_docs)] pub mod encode_to_git; pub use encode_to_git::function::encode_to_git; /// +#[allow(clippy::empty_docs)] pub mod encode_to_worktree; pub use encode_to_worktree::function::encode_to_worktree; diff --git a/gix-fs/src/dir/mod.rs b/gix-fs/src/dir/mod.rs index 4c709a6a88a..ae181a3f01f 100644 --- a/gix-fs/src/dir/mod.rs +++ b/gix-fs/src/dir/mod.rs @@ -1,4 +1,6 @@ /// +#[allow(clippy::empty_docs)] pub mod create; /// +#[allow(clippy::empty_docs)] pub mod remove; diff --git a/gix-fs/src/lib.rs b/gix-fs/src/lib.rs index 9d02ea33426..c63d6fe02ea 100644 --- a/gix-fs/src/lib.rs +++ b/gix-fs/src/lib.rs @@ -36,13 +36,16 @@ mod snapshot; pub use snapshot::{FileSnapshot, SharedFileSnapshot, SharedFileSnapshotMut}; /// +#[allow(clippy::empty_docs)] pub mod symlink; /// +#[allow(clippy::empty_docs)] pub mod read_dir; pub use read_dir::function::read_dir; /// +#[allow(clippy::empty_docs)] pub mod dir; /// Like [`std::env::current_dir()`], but it will `precompose_unicode` if that value is true, if the current directory @@ -90,4 +93,5 @@ pub fn is_executable(_metadata: &std::fs::Metadata) -> bool { } /// +#[allow(clippy::empty_docs)] pub mod stack; diff --git a/gix-glob/src/lib.rs b/gix-glob/src/lib.rs index ee8599fc8ec..4ddf98699de 100644 --- a/gix-glob/src/lib.rs +++ b/gix-glob/src/lib.rs @@ -25,11 +25,13 @@ pub struct Pattern { } /// +#[allow(clippy::empty_docs)] pub mod pattern; pub mod search; /// +#[allow(clippy::empty_docs)] pub mod wildmatch; pub use wildmatch::function::wildmatch; diff --git a/gix-glob/src/search/mod.rs b/gix-glob/src/search/mod.rs index b6fb2a49025..da58e1a1fcb 100644 --- a/gix-glob/src/search/mod.rs +++ b/gix-glob/src/search/mod.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; /// +#[allow(clippy::empty_docs)] pub mod pattern; /// A trait to convert bytes into patterns and their associated value. diff --git a/gix-hash/src/kind.rs b/gix-hash/src/kind.rs index ee8600b1b64..5c7a0316b8d 100644 --- a/gix-hash/src/kind.rs +++ b/gix-hash/src/kind.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, str::FromStr}; +use std::str::FromStr; use crate::{oid, Kind, ObjectId}; diff --git a/gix-hash/src/lib.rs b/gix-hash/src/lib.rs index 4c5b91231b7..baa5b9ea9a4 100644 --- a/gix-hash/src/lib.rs +++ b/gix-hash/src/lib.rs @@ -17,9 +17,10 @@ mod object_id; pub use object_id::{decode, ObjectId}; /// +#[allow(clippy::empty_docs)] pub mod prefix; -/// An partial, owned hash possibly identifying an object uniquely, whose non-prefix bytes are zeroed. +/// A partial, owned hash possibly identifying an object uniquely, whose non-prefix bytes are zeroed. /// /// An example would `0000000000000000000000000000000032bd3242`, where `32bd3242` is the prefix, /// which would be able to match all hashes that *start with* `32bd3242`. diff --git a/gix-hash/src/oid.rs b/gix-hash/src/oid.rs index 4bf6648e751..6360712c5b3 100644 --- a/gix-hash/src/oid.rs +++ b/gix-hash/src/oid.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, hash}; +use std::hash; use crate::{Kind, ObjectId, SIZE_OF_SHA1_DIGEST}; diff --git a/gix-hash/src/prefix.rs b/gix-hash/src/prefix.rs index b9d3849ab78..dc1752eba81 100644 --- a/gix-hash/src/prefix.rs +++ b/gix-hash/src/prefix.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, convert::TryFrom}; +use std::cmp::Ordering; use crate::{oid, ObjectId, Prefix}; @@ -16,6 +16,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod from_hex { /// The error returned by [`Prefix::from_hex`][super::Prefix::from_hex()]. #[derive(Debug, Eq, PartialEq, thiserror::Error)] diff --git a/gix-hash/tests/prefix/mod.rs b/gix-hash/tests/prefix/mod.rs index 14dbed67af3..357a18869a7 100644 --- a/gix-hash/tests/prefix/mod.rs +++ b/gix-hash/tests/prefix/mod.rs @@ -73,7 +73,7 @@ mod new { } mod try_from { - use std::{cmp::Ordering, convert::TryFrom}; + use std::cmp::Ordering; use gix_hash::{prefix::from_hex::Error, Prefix}; diff --git a/gix-hashtable/src/lib.rs b/gix-hashtable/src/lib.rs index 54935bcbca7..397b754df17 100644 --- a/gix-hashtable/src/lib.rs +++ b/gix-hashtable/src/lib.rs @@ -34,6 +34,7 @@ pub mod sync { } /// +#[allow(clippy::empty_docs)] pub mod hash { /// A Hasher for usage with `HashMap` keys that are already robust hashes (like an `ObjectId`). /// The first `8` bytes of the hash are used as the `HashMap` hash diff --git a/gix-ignore/src/lib.rs b/gix-ignore/src/lib.rs index a9ba2351e3c..5f7cc0c5d1c 100644 --- a/gix-ignore/src/lib.rs +++ b/gix-ignore/src/lib.rs @@ -12,6 +12,7 @@ pub use gix_glob as glob; /// +#[allow(clippy::empty_docs)] pub mod search; /// A grouping of lists of patterns while possibly keeping associated to their base path in order to find matches. /// @@ -45,6 +46,7 @@ pub enum Kind { } /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse git ignore patterns, line by line, from `bytes`. diff --git a/gix-index/src/decode/entries.rs b/gix-index/src/decode/entries.rs index 78f544691ae..d3bda1cb6a3 100644 --- a/gix-index/src/decode/entries.rs +++ b/gix-index/src/decode/entries.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Range}; +use std::ops::Range; use crate::{ decode::{self, header}, diff --git a/gix-index/src/decode/mod.rs b/gix-index/src/decode/mod.rs index aea189248bd..6c77109877a 100644 --- a/gix-index/src/decode/mod.rs +++ b/gix-index/src/decode/mod.rs @@ -4,6 +4,7 @@ use crate::{entry, extension, Entry, State, Version}; mod entries; /// +#[allow(clippy::empty_docs)] pub mod header; mod error { diff --git a/gix-index/src/entry/mod.rs b/gix-index/src/entry/mod.rs index b7fb13f7baf..15fc1d67e36 100644 --- a/gix-index/src/entry/mod.rs +++ b/gix-index/src/entry/mod.rs @@ -6,6 +6,7 @@ pub type Stage = u32; /// +#[allow(clippy::empty_docs)] pub mod mode; mod flags; @@ -13,6 +14,7 @@ pub(crate) use flags::at_rest; pub use flags::Flags; /// +#[allow(clippy::empty_docs)] pub mod stat; mod write; diff --git a/gix-index/src/entry/write.rs b/gix-index/src/entry/write.rs index 2a6ad55884c..66ef24d9a9e 100644 --- a/gix-index/src/entry/write.rs +++ b/gix-index/src/entry/write.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{entry, Entry, State}; impl Entry { diff --git a/gix-index/src/extension/decode.rs b/gix-index/src/extension/decode.rs index fa0a624104b..4114f8c8f20 100644 --- a/gix-index/src/extension/decode.rs +++ b/gix-index/src/extension/decode.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{extension, extension::Signature, util::from_be_u32}; pub(crate) fn header(data: &[u8]) -> (Signature, u32, &[u8]) { diff --git a/gix-index/src/extension/iter.rs b/gix-index/src/extension/iter.rs index f791b064eb4..ed0a457343d 100644 --- a/gix-index/src/extension/iter.rs +++ b/gix-index/src/extension/iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{extension, extension::Iter, util::from_be_u32}; impl<'a> Iter<'a> { diff --git a/gix-index/src/extension/link.rs b/gix-index/src/extension/link.rs index 722cbd17971..f2d52ca3d43 100644 --- a/gix-index/src/extension/link.rs +++ b/gix-index/src/extension/link.rs @@ -16,6 +16,7 @@ pub struct Bitmaps { } /// +#[allow(clippy::empty_docs)] pub mod decode { /// The error returned when decoding link extensions. diff --git a/gix-index/src/extension/mod.rs b/gix-index/src/extension/mod.rs index 33b91c5517b..3b51de6b20d 100644 --- a/gix-index/src/extension/mod.rs +++ b/gix-index/src/extension/mod.rs @@ -74,23 +74,29 @@ mod iter; pub(crate) mod fs_monitor; /// +#[allow(clippy::empty_docs)] pub mod decode; /// +#[allow(clippy::empty_docs)] pub mod tree; /// +#[allow(clippy::empty_docs)] pub mod end_of_index_entry; pub(crate) mod index_entry_offset_table; /// +#[allow(clippy::empty_docs)] pub mod link; pub(crate) mod resolve_undo; /// +#[allow(clippy::empty_docs)] pub mod untracked_cache; /// +#[allow(clippy::empty_docs)] pub mod sparse; diff --git a/gix-index/src/extension/tree/decode.rs b/gix-index/src/extension/tree/decode.rs index a55d12269a1..71e881ac38a 100644 --- a/gix-index/src/extension/tree/decode.rs +++ b/gix-index/src/extension/tree/decode.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use crate::{ diff --git a/gix-index/src/extension/tree/mod.rs b/gix-index/src/extension/tree/mod.rs index f2075939978..8831530e590 100644 --- a/gix-index/src/extension/tree/mod.rs +++ b/gix-index/src/extension/tree/mod.rs @@ -4,6 +4,7 @@ use crate::extension::Signature; pub const SIGNATURE: Signature = *b"TREE"; /// +#[allow(clippy::empty_docs)] pub mod verify; mod decode; diff --git a/gix-index/src/extension/tree/write.rs b/gix-index/src/extension/tree/write.rs index 2b1e3d94950..354ff0d393e 100644 --- a/gix-index/src/extension/tree/write.rs +++ b/gix-index/src/extension/tree/write.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::extension::{tree, Tree}; impl Tree { diff --git a/gix-index/src/extension/untracked_cache.rs b/gix-index/src/extension/untracked_cache.rs index f3b2d8afdcb..c47d6883745 100644 --- a/gix-index/src/extension/untracked_cache.rs +++ b/gix-index/src/extension/untracked_cache.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use bstr::BString; use gix_hash::ObjectId; diff --git a/gix-index/src/file/mod.rs b/gix-index/src/file/mod.rs index 40332abbd0e..68f6e6268c7 100644 --- a/gix-index/src/file/mod.rs +++ b/gix-index/src/file/mod.rs @@ -83,8 +83,11 @@ mod mutation { } /// +#[allow(clippy::empty_docs)] pub mod init; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod write; diff --git a/gix-index/src/lib.rs b/gix-index/src/lib.rs index 96a218c6355..580b8e59718 100644 --- a/gix-index/src/lib.rs +++ b/gix-index/src/lib.rs @@ -13,12 +13,15 @@ use filetime::FileTime; pub use gix_hash as hash; /// +#[allow(clippy::empty_docs)] pub mod file; /// +#[allow(clippy::empty_docs)] pub mod extension; /// +#[allow(clippy::empty_docs)] pub mod entry; mod access; @@ -26,12 +29,15 @@ mod access; mod init; /// +#[allow(clippy::empty_docs)] pub mod decode; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod write; pub mod fs; @@ -171,8 +177,6 @@ mod impls { } pub(crate) mod util { - use std::convert::TryInto; - #[inline] pub fn var_int(data: &[u8]) -> Option<(u64, &[u8])> { let (num, consumed) = gix_features::decode::leb64_from_read(data).ok()?; diff --git a/gix-index/src/verify.rs b/gix-index/src/verify.rs index 776f13b3c5f..e01a26fe306 100644 --- a/gix-index/src/verify.rs +++ b/gix-index/src/verify.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use crate::State; /// +#[allow(clippy::empty_docs)] pub mod entries { use bstr::BString; @@ -22,6 +23,7 @@ pub mod entries { } /// +#[allow(clippy::empty_docs)] pub mod extensions { use crate::extension; diff --git a/gix-index/src/write.rs b/gix-index/src/write.rs index 3e90d52e5be..d52ba640ee0 100644 --- a/gix-index/src/write.rs +++ b/gix-index/src/write.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io::Write}; +use std::io::Write; use crate::{entry, extension, write::util::CountBytes, State, Version}; @@ -183,8 +183,6 @@ fn entries<T: std::io::Write>(out: &mut CountBytes<T>, state: &State, header_siz } mod util { - use std::convert::TryFrom; - pub struct CountBytes<T> { pub count: u32, pub inner: T, diff --git a/gix-lock/src/lib.rs b/gix-lock/src/lib.rs index f07d48350cc..04cf1766fdb 100644 --- a/gix-lock/src/lib.rs +++ b/gix-lock/src/lib.rs @@ -24,10 +24,12 @@ use gix_tempfile::handle::{Closed, Writable}; const DOT_LOCK_SUFFIX: &str = ".lock"; /// +#[allow(clippy::empty_docs)] pub mod acquire; pub use gix_utils::backoff; /// +#[allow(clippy::empty_docs)] pub mod commit; /// Locks a resource to eventually be overwritten with the content of this file. @@ -53,4 +55,5 @@ pub struct Marker { } /// +#[allow(clippy::empty_docs)] pub mod file; diff --git a/gix-macros/src/momo.rs b/gix-macros/src/momo.rs index 802f4b5b024..74b1636d30c 100644 --- a/gix-macros/src/momo.rs +++ b/gix-macros/src/momo.rs @@ -98,18 +98,18 @@ pub(crate) fn inner(code: proc_macro2::TokenStream) -> proc_macro2::TokenStream #[derive(Copy, Clone)] // All conversions we support. Check references to this type for an idea how to add more. -enum Conversion<'a> { - Into(&'a Type), - AsRef(&'a Type), - AsMut(&'a Type), +enum Conversion { + Into, + AsRef, + AsMut, } -impl<'a> Conversion<'a> { +impl Conversion { fn conversion_expr(&self, i: &Ident) -> Expr { match *self { - Conversion::Into(_) => parse_quote!(#i.into()), - Conversion::AsRef(_) => parse_quote!(#i.as_ref()), - Conversion::AsMut(_) => parse_quote!(#i.as_mut()), + Conversion::Into => parse_quote!(#i.into()), + Conversion::AsRef => parse_quote!(#i.as_ref()), + Conversion::AsMut => parse_quote!(#i.as_mut()), } } } @@ -128,13 +128,13 @@ fn parse_bounds(bounds: &Punctuated<TypeParamBound, Token![+]>) -> Option<Conver if let TypeParamBound::Trait(ref tb) = bounds.first().unwrap() { if let Some(seg) = tb.path.segments.iter().last() { if let PathArguments::AngleBracketed(ref gen_args) = seg.arguments { - if let GenericArgument::Type(ref arg_ty) = gen_args.args.first().unwrap() { + if let GenericArgument::Type(_) = gen_args.args.first().unwrap() { if seg.ident == "Into" { - return Some(Conversion::Into(arg_ty)); + return Some(Conversion::Into); } else if seg.ident == "AsRef" { - return Some(Conversion::AsRef(arg_ty)); + return Some(Conversion::AsRef); } else if seg.ident == "AsMut" { - return Some(Conversion::AsMut(arg_ty)); + return Some(Conversion::AsMut); } } } @@ -144,7 +144,7 @@ fn parse_bounds(bounds: &Punctuated<TypeParamBound, Token![+]>) -> Option<Conver } // create a map from generic type to Conversion -fn parse_generics(decl: &Signature) -> HashMap<Ident, Conversion<'_>> { +fn parse_generics(decl: &Signature) -> HashMap<Ident, Conversion> { let mut ty_conversions = HashMap::new(); for gp in decl.generics.params.iter() { if let GenericParam::Type(ref tp) = gp { @@ -167,9 +167,9 @@ fn parse_generics(decl: &Signature) -> HashMap<Ident, Conversion<'_>> { ty_conversions } -fn convert<'a>( - inputs: &'a Punctuated<FnArg, Token![,]>, - ty_conversions: &HashMap<Ident, Conversion<'a>>, +fn convert( + inputs: &Punctuated<FnArg, Token![,]>, + ty_conversions: &HashMap<Ident, Conversion>, ) -> (bool, Punctuated<FnArg, Token![,]>, Punctuated<Expr, Token![,]>, bool) { let mut has_conversion_in_effect = false; let mut argtypes = Punctuated::new(); @@ -212,7 +212,7 @@ fn convert<'a>( if let Some(conv) = parse_bounds(bounds) { has_conversion_in_effect = true; argexprs.push(conv.conversion_expr(ident)); - if let Conversion::AsMut(_) = conv { + if let Conversion::AsMut = conv { pat_ident.mutability = Some(Default::default()); } } else { @@ -223,7 +223,7 @@ fn convert<'a>( if let Some(conv) = parse_bounded_type(&pat_type.ty).and_then(|ident| ty_conversions.get(&ident)) { has_conversion_in_effect = true; argexprs.push(conv.conversion_expr(ident)); - if let Conversion::AsMut(_) = conv { + if let Conversion::AsMut = conv { pat_ident.mutability = Some(Default::default()); } } else { diff --git a/gix-mailmap/src/lib.rs b/gix-mailmap/src/lib.rs index f6d9821eeba..64844666448 100644 --- a/gix-mailmap/src/lib.rs +++ b/gix-mailmap/src/lib.rs @@ -12,6 +12,7 @@ use bstr::BStr; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse the given `buf` of bytes line by line into mapping [Entries][Entry]. @@ -30,6 +31,7 @@ pub fn parse_ignore_errors(buf: &[u8]) -> impl Iterator<Item = Entry<'_>> { mod entry; /// +#[allow(clippy::empty_docs)] pub mod snapshot; /// A data-structure to efficiently store a list of entries for optimal, case-insensitive lookup by email and diff --git a/gix-object/src/commit/message/mod.rs b/gix-object/src/commit/message/mod.rs index ac5e3224c0d..5bb0046ee90 100644 --- a/gix-object/src/commit/message/mod.rs +++ b/gix-object/src/commit/message/mod.rs @@ -7,6 +7,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod body; mod decode; diff --git a/gix-object/src/commit/mod.rs b/gix-object/src/commit/mod.rs index 17dc12bebd6..dd79b24ae84 100644 --- a/gix-object/src/commit/mod.rs +++ b/gix-object/src/commit/mod.rs @@ -7,6 +7,7 @@ use crate::{Commit, CommitRef, TagRef}; mod decode; /// +#[allow(clippy::empty_docs)] pub mod message; /// A parsed commit message that assumes a title separated from the body by two consecutive newlines. @@ -53,6 +54,7 @@ impl From<SignedData<'_>> for BString { } /// +#[allow(clippy::empty_docs)] pub mod ref_iter; mod write; diff --git a/gix-object/src/find.rs b/gix-object/src/find.rs index 8471592cbc9..5a8c5e36cc2 100644 --- a/gix-object/src/find.rs +++ b/gix-object/src/find.rs @@ -1,6 +1,7 @@ /// The error type returned by the [`Find`](crate::Find) trait. pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>; /// +#[allow(clippy::empty_docs)] pub mod existing { use gix_hash::ObjectId; @@ -16,6 +17,7 @@ pub mod existing { } /// +#[allow(clippy::empty_docs)] pub mod existing_object { use gix_hash::ObjectId; @@ -42,6 +44,7 @@ pub mod existing_object { } /// +#[allow(clippy::empty_docs)] pub mod existing_iter { use gix_hash::ObjectId; diff --git a/gix-object/src/lib.rs b/gix-object/src/lib.rs index 43cb5f5bfd0..f8af6a81c94 100644 --- a/gix-object/src/lib.rs +++ b/gix-object/src/lib.rs @@ -19,18 +19,23 @@ pub use gix_date as date; use smallvec::SmallVec; /// +#[allow(clippy::empty_docs)] pub mod commit; mod object; /// +#[allow(clippy::empty_docs)] pub mod tag; /// +#[allow(clippy::empty_docs)] pub mod tree; mod blob; /// +#[allow(clippy::empty_docs)] pub mod data; /// +#[allow(clippy::empty_docs)] pub mod find; mod traits; @@ -40,6 +45,7 @@ pub mod encode; pub(crate) mod parse; /// +#[allow(clippy::empty_docs)] pub mod kind; /// The four types of objects that git differentiates. @@ -267,6 +273,7 @@ pub struct Header { } /// +#[allow(clippy::empty_docs)] pub mod decode { #[cfg(feature = "verbose-object-parsing-errors")] mod _decode { diff --git a/gix-object/src/object/convert.rs b/gix-object/src/object/convert.rs index 5e6e634869d..87ee35b1c48 100644 --- a/gix-object/src/object/convert.rs +++ b/gix-object/src/object/convert.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::{tree, Blob, BlobRef, Commit, CommitRef, Object, ObjectRef, Tag, TagRef, Tree, TreeRef}; impl From<TagRef<'_>> for Tag { diff --git a/gix-object/src/parse.rs b/gix-object/src/parse.rs index e241f79b45e..9401e505cea 100644 --- a/gix-object/src/parse.rs +++ b/gix-object/src/parse.rs @@ -4,7 +4,6 @@ use winnow::{ error::{AddContext, ParserError, StrContext}, prelude::*, token::{take_till, take_until, take_while}, - Parser, }; use crate::ByteSlice; diff --git a/gix-object/src/tag/mod.rs b/gix-object/src/tag/mod.rs index ecd56c9f7db..cc0d50e32ba 100644 --- a/gix-object/src/tag/mod.rs +++ b/gix-object/src/tag/mod.rs @@ -5,9 +5,11 @@ use crate::TagRef; mod decode; /// +#[allow(clippy::empty_docs)] pub mod write; /// +#[allow(clippy::empty_docs)] pub mod ref_iter; impl<'a> TagRef<'a> { diff --git a/gix-object/src/tree/mod.rs b/gix-object/src/tree/mod.rs index 1014fcfde29..0699d1a46db 100644 --- a/gix-object/src/tree/mod.rs +++ b/gix-object/src/tree/mod.rs @@ -7,6 +7,7 @@ use crate::{ mod ref_iter; /// +#[allow(clippy::empty_docs)] pub mod write; /// The mode of items storable in a tree, similar to the file mode on a unix file system. diff --git a/gix-object/src/tree/ref_iter.rs b/gix-object/src/tree/ref_iter.rs index bdec622d265..2b82a968732 100644 --- a/gix-object/src/tree/ref_iter.rs +++ b/gix-object/src/tree/ref_iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::BStr; use winnow::{error::ParserError, prelude::*}; @@ -124,8 +122,6 @@ impl TryFrom<u32> for tree::EntryMode { } mod decode { - use std::convert::TryFrom; - use bstr::ByteSlice; use winnow::{error::ParserError, prelude::*}; diff --git a/gix-odb/src/alternate/mod.rs b/gix-odb/src/alternate/mod.rs index c4e9fc8c040..919a3249ebd 100644 --- a/gix-odb/src/alternate/mod.rs +++ b/gix-odb/src/alternate/mod.rs @@ -21,6 +21,7 @@ use std::{fs, io, path::PathBuf}; use gix_path::realpath::MAX_SYMLINKS; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Returned by [`resolve()`] diff --git a/gix-odb/src/lib.rs b/gix-odb/src/lib.rs index 40d7c2aea90..f034fdaadc9 100644 --- a/gix-odb/src/lib.rs +++ b/gix-odb/src/lib.rs @@ -47,6 +47,7 @@ pub struct Cache<S> { } /// +#[allow(clippy::empty_docs)] pub mod cache; /// @@ -69,6 +70,7 @@ pub fn sink(object_hash: gix_hash::Kind) -> Sink { mod sink; /// +#[allow(clippy::empty_docs)] pub mod find; /// An object database equivalent to `/dev/null`, dropping all objects stored into it. @@ -77,6 +79,7 @@ mod traits; pub use traits::{Header, HeaderExt, Write}; /// +#[allow(clippy::empty_docs)] pub mod write { /// The error type returned by the [`Write`](crate::Write) trait. pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>; diff --git a/gix-odb/src/store_impls/dynamic/find.rs b/gix-odb/src/store_impls/dynamic/find.rs index 99b58d7477f..b420dc6ec73 100644 --- a/gix-odb/src/store_impls/dynamic/find.rs +++ b/gix-odb/src/store_impls/dynamic/find.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Deref}; +use std::ops::Deref; use gix_pack::cache::DecodeEntry; diff --git a/gix-odb/src/store_impls/dynamic/handle.rs b/gix-odb/src/store_impls/dynamic/handle.rs index 655bdfa430c..883e2c1ccb8 100644 --- a/gix-odb/src/store_impls/dynamic/handle.rs +++ b/gix-odb/src/store_impls/dynamic/handle.rs @@ -1,6 +1,5 @@ use std::{ cell::RefCell, - convert::{TryFrom, TryInto}, ops::Deref, rc::Rc, sync::{atomic::Ordering, Arc}, diff --git a/gix-odb/src/store_impls/dynamic/init.rs b/gix-odb/src/store_impls/dynamic/init.rs index 980bacf767b..2031e48c837 100644 --- a/gix-odb/src/store_impls/dynamic/init.rs +++ b/gix-odb/src/store_impls/dynamic/init.rs @@ -1,4 +1,4 @@ -use std::{iter::FromIterator, path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use arc_swap::ArcSwap; diff --git a/gix-odb/src/store_impls/dynamic/mod.rs b/gix-odb/src/store_impls/dynamic/mod.rs index 7ea462b24a1..65ea5361415 100644 --- a/gix-odb/src/store_impls/dynamic/mod.rs +++ b/gix-odb/src/store_impls/dynamic/mod.rs @@ -51,20 +51,25 @@ impl RefreshMode { } /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod prefix; mod header; /// +#[allow(clippy::empty_docs)] pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod write; /// +#[allow(clippy::empty_docs)] pub mod init; pub(crate) mod types; @@ -73,9 +78,11 @@ pub use types::Metrics; pub(crate) mod handle; /// +#[allow(clippy::empty_docs)] pub mod load_index; /// +#[allow(clippy::empty_docs)] pub mod verify; mod load_one; @@ -85,4 +92,5 @@ mod metrics; mod access; /// +#[allow(clippy::empty_docs)] pub mod structure; diff --git a/gix-odb/src/store_impls/dynamic/prefix.rs b/gix-odb/src/store_impls/dynamic/prefix.rs index 0d671400448..b6869d8c2b6 100644 --- a/gix-odb/src/store_impls/dynamic/prefix.rs +++ b/gix-odb/src/store_impls/dynamic/prefix.rs @@ -5,6 +5,7 @@ use gix_object::Exists; use crate::store::{load_index, Handle}; /// +#[allow(clippy::empty_docs)] pub mod lookup { use crate::loose; @@ -24,6 +25,7 @@ pub mod lookup { } /// +#[allow(clippy::empty_docs)] pub mod disambiguate { /// A potentially ambiguous prefix for use with `Handle::disambiguate_prefix()`. #[derive(Debug, Copy, Clone)] diff --git a/gix-odb/src/store_impls/dynamic/verify.rs b/gix-odb/src/store_impls/dynamic/verify.rs index a59ee2c5367..b9a1f605911 100644 --- a/gix-odb/src/store_impls/dynamic/verify.rs +++ b/gix-odb/src/store_impls/dynamic/verify.rs @@ -13,6 +13,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod integrity { use std::{marker::PhantomData, path::PathBuf}; diff --git a/gix-odb/src/store_impls/loose/mod.rs b/gix-odb/src/store_impls/loose/mod.rs index 17e4a33d65a..3dddf03660a 100644 --- a/gix-odb/src/store_impls/loose/mod.rs +++ b/gix-odb/src/store_impls/loose/mod.rs @@ -50,10 +50,13 @@ fn hash_path(id: &gix_hash::oid, mut root: PathBuf) -> PathBuf { } /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod verify; /// The type for an iterator over `Result<gix_hash::ObjectId, Error>)` @@ -63,4 +66,5 @@ pub struct Iter { } /// +#[allow(clippy::empty_docs)] pub mod write; diff --git a/gix-odb/src/store_impls/loose/verify.rs b/gix-odb/src/store_impls/loose/verify.rs index ae83c1d01cb..2bacfa0cd27 100644 --- a/gix-odb/src/store_impls/loose/verify.rs +++ b/gix-odb/src/store_impls/loose/verify.rs @@ -8,6 +8,7 @@ use gix_features::progress::{Count, DynNestedProgress, Progress}; use crate::{loose::Store, Write}; /// +#[allow(clippy::empty_docs)] pub mod integrity { /// The error returned by [`verify_integrity()`][super::Store::verify_integrity()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-pack/src/bundle/mod.rs b/gix-pack/src/bundle/mod.rs index d8ef1107d70..473e51dbf72 100644 --- a/gix-pack/src/bundle/mod.rs +++ b/gix-pack/src/bundle/mod.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod init; mod find; @@ -7,12 +8,14 @@ mod find; pub mod write; /// +#[allow(clippy::empty_docs)] pub mod verify { use std::sync::atomic::AtomicBool; use gix_features::progress::DynNestedProgress; /// + #[allow(clippy::empty_docs)] pub mod integrity { /// Returned by [`Bundle::verify_integrity()`][crate::Bundle::verify_integrity()]. pub struct Outcome { diff --git a/gix-pack/src/cache/delta/from_offsets.rs b/gix-pack/src/cache/delta/from_offsets.rs index d790dcc0f71..ee52f9ab9dd 100644 --- a/gix-pack/src/cache/delta/from_offsets.rs +++ b/gix-pack/src/cache/delta/from_offsets.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryFrom, fs, io, io::{BufRead, Read, Seek, SeekFrom}, sync::atomic::{AtomicBool, Ordering}, diff --git a/gix-pack/src/cache/delta/mod.rs b/gix-pack/src/cache/delta/mod.rs index c923b5d1078..161432e8c2b 100644 --- a/gix-pack/src/cache/delta/mod.rs +++ b/gix-pack/src/cache/delta/mod.rs @@ -12,9 +12,11 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod traverse; /// +#[allow(clippy::empty_docs)] pub mod from_offsets; /// Tree datastructure diff --git a/gix-pack/src/cache/mod.rs b/gix-pack/src/cache/mod.rs index eabb6a3ed85..d41467c05b9 100644 --- a/gix-pack/src/cache/mod.rs +++ b/gix-pack/src/cache/mod.rs @@ -52,6 +52,7 @@ pub mod lru; pub mod object; /// +#[allow(clippy::empty_docs)] pub(crate) mod delta; /// Replaces content of the given `Vec` with the slice. The vec will have the same length diff --git a/gix-pack/src/data/file/decode/entry.rs b/gix-pack/src/data/file/decode/entry.rs index 2bec3efbd68..dc306baf33a 100644 --- a/gix-pack/src/data/file/decode/entry.rs +++ b/gix-pack/src/data/file/decode/entry.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Range}; +use std::ops::Range; use gix_features::zlib; use smallvec::SmallVec; diff --git a/gix-pack/src/data/file/decode/mod.rs b/gix-pack/src/data/file/decode/mod.rs index aa4177424a5..3db3eec51ad 100644 --- a/gix-pack/src/data/file/decode/mod.rs +++ b/gix-pack/src/data/file/decode/mod.rs @@ -1,8 +1,10 @@ use std::collections::TryReserveError; /// +#[allow(clippy::empty_docs)] pub mod entry; /// +#[allow(clippy::empty_docs)] pub mod header; /// Returned by [`File::decode_header()`][crate::data::File::decode_header()], diff --git a/gix-pack/src/data/file/init.rs b/gix-pack/src/data/file/init.rs index b160724175d..a005e517fd7 100644 --- a/gix-pack/src/data/file/init.rs +++ b/gix-pack/src/data/file/init.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, path::Path}; +use std::path::Path; use crate::data; diff --git a/gix-pack/src/data/file/mod.rs b/gix-pack/src/data/file/mod.rs index 6bfe0e2721b..936ab51ecb9 100644 --- a/gix-pack/src/data/file/mod.rs +++ b/gix-pack/src/data/file/mod.rs @@ -1,8 +1,10 @@ mod init; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod decode; /// The bytes used as header in a pack data file. diff --git a/gix-pack/src/data/file/verify.rs b/gix-pack/src/data/file/verify.rs index ea99144a1e9..9b12209e634 100644 --- a/gix-pack/src/data/file/verify.rs +++ b/gix-pack/src/data/file/verify.rs @@ -5,6 +5,7 @@ use gix_features::progress::Progress; use crate::data::File; /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by [`data::File::verify_checksum()`][crate::data::File::verify_checksum()]. pub type Error = crate::verify::checksum::Error; diff --git a/gix-pack/src/data/header.rs b/gix-pack/src/data/header.rs index 348a4ca24ec..c6c7b8350fd 100644 --- a/gix-pack/src/data/header.rs +++ b/gix-pack/src/data/header.rs @@ -37,6 +37,7 @@ pub fn encode(version: data::Version, num_objects: u32) -> [u8; 12] { } /// +#[allow(clippy::empty_docs)] pub mod decode { /// Returned by [`decode()`][super::decode()]. #[derive(thiserror::Error, Debug)] diff --git a/gix-pack/src/data/input/lookup_ref_delta_objects.rs b/gix-pack/src/data/input/lookup_ref_delta_objects.rs index f6036e20f0e..6b1995293e1 100644 --- a/gix-pack/src/data/input/lookup_ref_delta_objects.rs +++ b/gix-pack/src/data/input/lookup_ref_delta_objects.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use crate::data::{entry::Header, input}; diff --git a/gix-pack/src/data/mod.rs b/gix-pack/src/data/mod.rs index 9808ae8530c..385aa3d719c 100644 --- a/gix-pack/src/data/mod.rs +++ b/gix-pack/src/data/mod.rs @@ -1,5 +1,5 @@ //! a pack data file -use std::{convert::TryInto, path::Path}; +use std::path::Path; /// The offset to an entry into the pack data file, relative to its beginning. pub type Offset = u64; @@ -26,14 +26,17 @@ pub struct Entry { mod file; pub use file::{decode, verify, Header}; /// +#[allow(clippy::empty_docs)] pub mod header; /// +#[allow(clippy::empty_docs)] pub mod init { pub use super::header::decode::Error; } /// +#[allow(clippy::empty_docs)] pub mod entry; /// diff --git a/gix-pack/src/data/output/count/mod.rs b/gix-pack/src/data/output/count/mod.rs index 481ff65d3c3..c10ec1e51a6 100644 --- a/gix-pack/src/data/output/count/mod.rs +++ b/gix-pack/src/data/output/count/mod.rs @@ -44,6 +44,7 @@ mod objects_impl; pub use objects_impl::{objects, objects_unthreaded}; /// +#[allow(clippy::empty_docs)] pub mod objects { pub use super::objects_impl::{Error, ObjectExpansion, Options, Outcome}; } diff --git a/gix-pack/src/data/output/entry/mod.rs b/gix-pack/src/data/output/entry/mod.rs index 4ab4879eb69..ed79a404d5a 100644 --- a/gix-pack/src/data/output/entry/mod.rs +++ b/gix-pack/src/data/output/entry/mod.rs @@ -1,10 +1,11 @@ -use std::{convert::TryFrom, io::Write}; +use std::io::Write; use gix_hash::ObjectId; use crate::{data, data::output, find}; /// +#[allow(clippy::empty_docs)] pub mod iter_from_counts; pub use iter_from_counts::function::iter_from_counts; diff --git a/gix-pack/src/data/output/mod.rs b/gix-pack/src/data/output/mod.rs index 338bb92b3ea..0e1b97256aa 100644 --- a/gix-pack/src/data/output/mod.rs +++ b/gix-pack/src/data/output/mod.rs @@ -1,6 +1,7 @@ use gix_hash::ObjectId; /// +#[allow(clippy::empty_docs)] pub mod count; /// An item representing a future Entry in the leanest way possible. @@ -35,7 +36,9 @@ pub struct Entry { } /// +#[allow(clippy::empty_docs)] pub mod entry; /// +#[allow(clippy::empty_docs)] pub mod bytes; diff --git a/gix-pack/src/index/mod.rs b/gix-pack/src/index/mod.rs index 8d880744241..f83eb51fd0e 100644 --- a/gix-pack/src/index/mod.rs +++ b/gix-pack/src/index/mod.rs @@ -136,6 +136,7 @@ impl File { const V2_SIGNATURE: &[u8] = b"\xfftOc"; /// +#[allow(clippy::empty_docs)] pub mod init; pub(crate) mod access; @@ -143,9 +144,11 @@ pub use access::Entry; pub(crate) mod encode; /// +#[allow(clippy::empty_docs)] pub mod traverse; mod util; /// +#[allow(clippy::empty_docs)] pub mod verify; /// #[cfg(feature = "streaming-input")] diff --git a/gix-pack/src/index/traverse/mod.rs b/gix-pack/src/index/traverse/mod.rs index 1edf0b1d5dd..29abeac9701 100644 --- a/gix-pack/src/index/traverse/mod.rs +++ b/gix-pack/src/index/traverse/mod.rs @@ -6,8 +6,10 @@ use crate::index; mod reduce; /// +#[allow(clippy::empty_docs)] pub mod with_index; /// +#[allow(clippy::empty_docs)] pub mod with_lookup; use reduce::Reducer; diff --git a/gix-pack/src/index/verify.rs b/gix-pack/src/index/verify.rs index 0ee4259a8fc..6dfa817e08e 100644 --- a/gix-pack/src/index/verify.rs +++ b/gix-pack/src/index/verify.rs @@ -6,6 +6,7 @@ use gix_object::WriteTo; use crate::index; /// +#[allow(clippy::empty_docs)] pub mod integrity { use std::marker::PhantomData; @@ -88,6 +89,7 @@ pub mod integrity { } /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by [`index::File::verify_checksum()`][crate::index::File::verify_checksum()]. pub type Error = crate::verify::checksum::Error; diff --git a/gix-pack/src/index/write/mod.rs b/gix-pack/src/index/write/mod.rs index 3ecb7347b8f..2247d8a1ebe 100644 --- a/gix-pack/src/index/write/mod.rs +++ b/gix-pack/src/index/write/mod.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io, sync::atomic::AtomicBool}; +use std::{io, sync::atomic::AtomicBool}; pub use error::Error; use gix_features::progress::{self, prodash::DynNestedProgress, Count, Progress}; diff --git a/gix-pack/src/lib.rs b/gix-pack/src/lib.rs index b56d1fe983e..7a611aa708b 100755 --- a/gix-pack/src/lib.rs +++ b/gix-pack/src/lib.rs @@ -19,6 +19,7 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod bundle; /// A bundle of pack data and the corresponding pack index pub struct Bundle { @@ -29,22 +30,28 @@ pub struct Bundle { } /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod cache; /// +#[allow(clippy::empty_docs)] pub mod data; mod find_traits; pub use find_traits::{Find, FindExt}; /// +#[allow(clippy::empty_docs)] pub mod index; /// +#[allow(clippy::empty_docs)] pub mod multi_index; /// +#[allow(clippy::empty_docs)] pub mod verify; mod mmap { @@ -60,8 +67,6 @@ mod mmap { } } -use std::convert::TryInto; - #[inline] fn read_u32(b: &[u8]) -> u32 { u32::from_be_bytes(b.try_into().unwrap()) diff --git a/gix-pack/src/multi_index/chunk.rs b/gix-pack/src/multi_index/chunk.rs index e9a9aac9cdb..81c640bbf57 100644 --- a/gix-pack/src/multi_index/chunk.rs +++ b/gix-pack/src/multi_index/chunk.rs @@ -8,6 +8,7 @@ pub mod index_names { pub const ID: gix_chunk::Id = *b"PNAM"; /// + #[allow(clippy::empty_docs)] pub mod decode { use gix_object::bstr::BString; @@ -105,8 +106,6 @@ pub mod index_names { /// Information for the chunk with the fanout table pub mod fanout { - use std::convert::TryInto; - use crate::multi_index; /// The size of the fanout table @@ -173,7 +172,7 @@ pub mod lookup { /// Information about the offsets table. pub mod offsets { - use std::{convert::TryInto, ops::Range}; + use std::ops::Range; use crate::multi_index; diff --git a/gix-pack/src/multi_index/init.rs b/gix-pack/src/multi_index/init.rs index 190b40a7b04..beccf78e403 100644 --- a/gix-pack/src/multi_index/init.rs +++ b/gix-pack/src/multi_index/init.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, path::Path}; +use std::path::Path; use crate::multi_index::{chunk, File, Version}; diff --git a/gix-pack/src/multi_index/mod.rs b/gix-pack/src/multi_index/mod.rs index d88ae0baed1..6fbb53676a9 100644 --- a/gix-pack/src/multi_index/mod.rs +++ b/gix-pack/src/multi_index/mod.rs @@ -37,16 +37,21 @@ pub struct File { } /// +#[allow(clippy::empty_docs)] pub mod write; /// +#[allow(clippy::empty_docs)] mod access; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod chunk; /// +#[allow(clippy::empty_docs)] pub mod init; diff --git a/gix-pack/src/multi_index/verify.rs b/gix-pack/src/multi_index/verify.rs index 0903b3568cf..1e38ae7d9cf 100644 --- a/gix-pack/src/multi_index/verify.rs +++ b/gix-pack/src/multi_index/verify.rs @@ -5,6 +5,7 @@ use gix_features::progress::{Count, DynNestedProgress, Progress}; use crate::{index, multi_index::File}; /// +#[allow(clippy::empty_docs)] pub mod integrity { use crate::multi_index::EntryIndex; @@ -68,6 +69,7 @@ pub mod integrity { } /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by [`multi_index::File::verify_checksum()`][crate::multi_index::File::verify_checksum()]. pub type Error = crate::verify::checksum::Error; diff --git a/gix-pack/src/multi_index/write.rs b/gix-pack/src/multi_index/write.rs index 881033091bc..3625cc47c7a 100644 --- a/gix-pack/src/multi_index/write.rs +++ b/gix-pack/src/multi_index/write.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryInto, path::PathBuf, sync::atomic::{AtomicBool, Ordering}, time::{Instant, SystemTime}, diff --git a/gix-pack/src/verify.rs b/gix-pack/src/verify.rs index fff99c75d12..3fad0e2467f 100644 --- a/gix-pack/src/verify.rs +++ b/gix-pack/src/verify.rs @@ -3,6 +3,7 @@ use std::{path::Path, sync::atomic::AtomicBool}; use gix_features::progress::Progress; /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by various methods to verify the checksum of a memory mapped file that might also exist on disk. #[derive(thiserror::Error, Debug)] diff --git a/gix-pack/tests/pack/data/header.rs b/gix-pack/tests/pack/data/header.rs index 45132c9f8a5..8c05e24302e 100644 --- a/gix-pack/tests/pack/data/header.rs +++ b/gix-pack/tests/pack/data/header.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::pack::fixture_path; #[test] diff --git a/gix-packetline/src/decode.rs b/gix-packetline/src/decode.rs index 35d2b5149ac..35cfb68cf86 100644 --- a/gix-packetline/src/decode.rs +++ b/gix-packetline/src/decode.rs @@ -21,6 +21,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod band { /// The error used in [`PacketLineRef::decode_band()`][super::PacketLineRef::decode_band()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-packetline/src/lib.rs b/gix-packetline/src/lib.rs index 95ecd59d25c..62978dd7bb1 100644 --- a/gix-packetline/src/lib.rs +++ b/gix-packetline/src/lib.rs @@ -31,9 +31,11 @@ pub enum Channel { mod line; /// +#[allow(clippy::empty_docs)] pub mod read; /// +#[allow(clippy::empty_docs)] #[cfg(any(feature = "async-io", feature = "blocking-io"))] mod write; #[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] diff --git a/gix-path/src/lib.rs b/gix-path/src/lib.rs index 888141d3c23..7f913042fac 100644 --- a/gix-path/src/lib.rs +++ b/gix-path/src/lib.rs @@ -57,6 +57,7 @@ mod util; pub use util::is_absolute; /// +#[allow(clippy::empty_docs)] pub mod realpath; pub use realpath::function::{realpath, realpath_opts}; diff --git a/gix-pathspec/src/defaults.rs b/gix-pathspec/src/defaults.rs index 81be53b38c1..c2bc641f8eb 100644 --- a/gix-pathspec/src/defaults.rs +++ b/gix-pathspec/src/defaults.rs @@ -3,6 +3,7 @@ use std::ffi::OsString; use crate::{Defaults, MagicSignature, SearchMode}; /// +#[allow(clippy::empty_docs)] pub mod from_environment { /// The error returned by [Defaults::from_environment()](super::Defaults::from_environment()). #[derive(Debug, thiserror::Error)] diff --git a/gix-pathspec/src/lib.rs b/gix-pathspec/src/lib.rs index e271cdb06f9..e31d5d50ba1 100644 --- a/gix-pathspec/src/lib.rs +++ b/gix-pathspec/src/lib.rs @@ -11,6 +11,7 @@ use bstr::BString; pub use gix_attributes as attributes; /// +#[allow(clippy::empty_docs)] pub mod normalize { use std::path::PathBuf; @@ -28,9 +29,11 @@ pub mod normalize { mod pattern; /// +#[allow(clippy::empty_docs)] pub mod search; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Default settings for some fields of a [`Pattern`]. @@ -51,6 +54,7 @@ pub struct Defaults { } /// +#[allow(clippy::empty_docs)] pub mod defaults; /// A lists of pathspec patterns, possibly from a file. diff --git a/gix-prompt/src/lib.rs b/gix-prompt/src/lib.rs index dba6cf80405..e25f6058fff 100644 --- a/gix-prompt/src/lib.rs +++ b/gix-prompt/src/lib.rs @@ -11,6 +11,7 @@ mod types; pub use types::{Error, Mode, Options}; /// +#[allow(clippy::empty_docs)] pub mod unix; #[cfg(unix)] use unix::imp; diff --git a/gix-prompt/src/types.rs b/gix-prompt/src/types.rs index 6f3bb2c0430..ccf0f7c9493 100644 --- a/gix-prompt/src/types.rs +++ b/gix-prompt/src/types.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, path::Path}; +use std::{borrow::Cow, path::Path}; /// The error returned by [ask()][crate::ask()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-protocol/src/fetch/mod.rs b/gix-protocol/src/fetch/mod.rs index 0828ea733a2..56e85f4babd 100644 --- a/gix-protocol/src/fetch/mod.rs +++ b/gix-protocol/src/fetch/mod.rs @@ -2,6 +2,7 @@ mod arguments; pub use arguments::Arguments; /// +#[allow(clippy::empty_docs)] pub mod delegate; #[cfg(any(feature = "async-client", feature = "blocking-client"))] pub use delegate::Delegate; @@ -10,6 +11,7 @@ pub use delegate::{Action, DelegateBlocking}; mod error; pub use error::Error; /// +#[allow(clippy::empty_docs)] pub mod response; pub use response::Response; diff --git a/gix-protocol/src/handshake/mod.rs b/gix-protocol/src/handshake/mod.rs index 28243e96da7..cc388e96d2e 100644 --- a/gix-protocol/src/handshake/mod.rs +++ b/gix-protocol/src/handshake/mod.rs @@ -98,4 +98,5 @@ pub use error::Error; pub(crate) mod function; /// +#[allow(clippy::empty_docs)] pub mod refs; diff --git a/gix-protocol/src/handshake/refs/mod.rs b/gix-protocol/src/handshake/refs/mod.rs index 39c6c85a942..cbefc358aab 100644 --- a/gix-protocol/src/handshake/refs/mod.rs +++ b/gix-protocol/src/handshake/refs/mod.rs @@ -3,6 +3,7 @@ use bstr::BStr; use super::Ref; /// +#[allow(clippy::empty_docs)] pub mod parse { use bstr::BString; diff --git a/gix-protocol/src/lib.rs b/gix-protocol/src/lib.rs index 6b4ee69d2b6..63f26e632aa 100644 --- a/gix-protocol/src/lib.rs +++ b/gix-protocol/src/lib.rs @@ -32,6 +32,7 @@ pub use gix_transport as transport; pub use maybe_async; /// +#[allow(clippy::empty_docs)] #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub mod fetch; @@ -48,11 +49,13 @@ compile_error!("Cannot set both 'blocking-client' and 'async-client' features as /// #[cfg(any(feature = "blocking-client", feature = "async-client"))] +#[allow(clippy::empty_docs)] pub mod handshake; #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub use handshake::function::handshake; /// +#[allow(clippy::empty_docs)] #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub mod ls_refs; #[cfg(any(feature = "blocking-client", feature = "async-client"))] diff --git a/gix-protocol/src/remote_progress.rs b/gix-protocol/src/remote_progress.rs index d7baa3bf9f5..deba3e6749d 100644 --- a/gix-protocol/src/remote_progress.rs +++ b/gix-protocol/src/remote_progress.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::ByteSlice; use winnow::{ combinator::{opt, preceded, terminated}, diff --git a/gix-protocol/tests/fetch/mod.rs b/gix-protocol/tests/fetch/mod.rs index a7c29fbd6ec..b579b080f80 100644 --- a/gix-protocol/tests/fetch/mod.rs +++ b/gix-protocol/tests/fetch/mod.rs @@ -89,7 +89,7 @@ impl fetch::DelegateBlocking for CloneRefInWantDelegate { _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, refs: &[handshake::Ref], ) -> io::Result<Action> { - self.refs = refs.to_owned(); + refs.clone_into(&mut self.refs); Ok(Action::Continue) } @@ -135,7 +135,7 @@ impl fetch::DelegateBlocking for LsRemoteDelegate { _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, refs: &[handshake::Ref], ) -> io::Result<fetch::Action> { - self.refs = refs.to_owned(); + refs.clone_into(&mut self.refs); Ok(fetch::Action::Cancel) } fn negotiate( diff --git a/gix-quote/src/ansi_c.rs b/gix-quote/src/ansi_c.rs index 43856f8992e..1b3bf3a66fd 100644 --- a/gix-quote/src/ansi_c.rs +++ b/gix-quote/src/ansi_c.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod undo { use bstr::{BStr, BString}; diff --git a/gix-quote/src/lib.rs b/gix-quote/src/lib.rs index b86336b3b0b..f0ed9785995 100644 --- a/gix-quote/src/lib.rs +++ b/gix-quote/src/lib.rs @@ -3,6 +3,7 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod ansi_c; mod single; diff --git a/gix-ref/src/fullname.rs b/gix-ref/src/fullname.rs index 5957943dc72..e74dd05dd04 100644 --- a/gix-ref/src/fullname.rs +++ b/gix-ref/src/fullname.rs @@ -1,4 +1,4 @@ -use std::{borrow::Borrow, convert::TryFrom, path::Path}; +use std::{borrow::Borrow, path::Path}; use gix_object::bstr::{BStr, BString, ByteSlice}; diff --git a/gix-ref/src/lib.rs b/gix-ref/src/lib.rs index 2a0b8987632..ecf54ed49fe 100644 --- a/gix-ref/src/lib.rs +++ b/gix-ref/src/lib.rs @@ -35,10 +35,13 @@ pub use store_impl::{file, packed}; mod fullname; /// +#[allow(clippy::empty_docs)] pub mod name; /// +#[allow(clippy::empty_docs)] pub mod namespace; /// +#[allow(clippy::empty_docs)] pub mod transaction; mod parse; @@ -49,12 +52,15 @@ pub use raw::Reference; mod target; /// +#[allow(clippy::empty_docs)] pub mod log; /// +#[allow(clippy::empty_docs)] pub mod peel; /// +#[allow(clippy::empty_docs)] pub mod store { /// The way a file store handles the reflog #[derive(Default, Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone, Copy)] diff --git a/gix-ref/src/namespace.rs b/gix-ref/src/namespace.rs index 34040f606a0..1483b1c47e8 100644 --- a/gix-ref/src/namespace.rs +++ b/gix-ref/src/namespace.rs @@ -1,7 +1,4 @@ -use std::{ - convert::TryInto, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use gix_object::bstr::{BStr, BString, ByteSlice, ByteVec}; diff --git a/gix-ref/src/peel.rs b/gix-ref/src/peel.rs index b119a907334..02bee162050 100644 --- a/gix-ref/src/peel.rs +++ b/gix-ref/src/peel.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod to_id { use std::path::PathBuf; diff --git a/gix-ref/src/store/file/find.rs b/gix-ref/src/store/file/find.rs index 6bf7767043b..b8a45e86b2e 100644 --- a/gix-ref/src/store/file/find.rs +++ b/gix-ref/src/store/file/find.rs @@ -1,6 +1,5 @@ use std::{ borrow::Cow, - convert::TryInto, io::{self, Read}, path::{Path, PathBuf}, }; @@ -271,9 +270,8 @@ impl file::Store { } /// +#[allow(clippy::empty_docs)] pub mod existing { - use std::convert::TryInto; - pub use error::Error; use crate::{ diff --git a/gix-ref/src/store/file/log/iter.rs b/gix-ref/src/store/file/log/iter.rs index d62df680014..e3147151513 100644 --- a/gix-ref/src/store/file/log/iter.rs +++ b/gix-ref/src/store/file/log/iter.rs @@ -8,6 +8,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod decode { use crate::store_impl::file::log; @@ -143,6 +144,7 @@ where } /// +#[allow(clippy::empty_docs)] pub mod reverse { use super::decode; diff --git a/gix-ref/src/store/file/log/line.rs b/gix-ref/src/store/file/log/line.rs index 01de31a7c60..ff8fe490c30 100644 --- a/gix-ref/src/store/file/log/line.rs +++ b/gix-ref/src/store/file/log/line.rs @@ -72,6 +72,7 @@ impl<'a> From<LineRef<'a>> for Line { } /// +#[allow(clippy::empty_docs)] pub mod decode { use gix_object::bstr::{BStr, ByteSlice}; use winnow::{ @@ -84,6 +85,7 @@ pub mod decode { use crate::{file::log::LineRef, parse::hex_hash}; /// + #[allow(clippy::empty_docs)] mod error { use gix_object::bstr::{BString, ByteSlice}; @@ -167,10 +169,8 @@ pub mod decode { #[cfg(test)] mod test { - use gix_date::{time::Sign, Time}; - use gix_object::bstr::ByteSlice; - use super::*; + use gix_date::{time::Sign, Time}; /// Convert a hexadecimal hash into its corresponding `ObjectId` or _panic_. fn hex_to_oid(hex: &str) -> gix_hash::ObjectId { diff --git a/gix-ref/src/store/file/log/mod.rs b/gix-ref/src/store/file/log/mod.rs index a9199be2b79..eae3abe7ef9 100644 --- a/gix-ref/src/store/file/log/mod.rs +++ b/gix-ref/src/store/file/log/mod.rs @@ -3,6 +3,7 @@ use gix_object::bstr::BStr; pub use super::loose::reflog::{create_or_update, Error}; /// +#[allow(clippy::empty_docs)] pub mod iter; mod line; diff --git a/gix-ref/src/store/file/loose/mod.rs b/gix-ref/src/store/file/loose/mod.rs index 0e271247793..f1fd8735163 100644 --- a/gix-ref/src/store/file/loose/mod.rs +++ b/gix-ref/src/store/file/loose/mod.rs @@ -17,11 +17,14 @@ impl Reference { } /// +#[allow(clippy::empty_docs)] pub(crate) mod reflog; /// +#[allow(clippy::empty_docs)] pub(crate) mod iter; /// +#[allow(clippy::empty_docs)] pub mod reference; mod init { diff --git a/gix-ref/src/store/file/loose/reference/decode.rs b/gix-ref/src/store/file/loose/reference/decode.rs index 7ba89837383..68fb4f65d70 100644 --- a/gix-ref/src/store/file/loose/reference/decode.rs +++ b/gix-ref/src/store/file/loose/reference/decode.rs @@ -1,5 +1,3 @@ -use std::convert::{TryFrom, TryInto}; - use gix_hash::ObjectId; use gix_object::bstr::BString; use winnow::{ diff --git a/gix-ref/src/store/file/loose/reference/mod.rs b/gix-ref/src/store/file/loose/reference/mod.rs index 3e5ce06838b..e79ff6b95bd 100644 --- a/gix-ref/src/store/file/loose/reference/mod.rs +++ b/gix-ref/src/store/file/loose/reference/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod logiter; /// +#[allow(clippy::empty_docs)] pub mod decode; diff --git a/gix-ref/src/store/file/loose/reflog.rs b/gix-ref/src/store/file/loose/reflog.rs index 614dd59a9e0..cdf342bcb88 100644 --- a/gix-ref/src/store/file/loose/reflog.rs +++ b/gix-ref/src/store/file/loose/reflog.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io::Read, path::PathBuf}; +use std::{io::Read, path::PathBuf}; use crate::{ store_impl::{file, file::log}, @@ -84,6 +84,7 @@ impl file::Store { } /// +#[allow(clippy::empty_docs)] pub mod create_or_update { use std::{ borrow::Cow, diff --git a/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs b/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs index 4173b175875..c2b487ad9e2 100644 --- a/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs +++ b/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs @@ -1,12 +1,9 @@ -use std::{convert::TryInto, path::Path}; - use gix_actor::Signature; use gix_date::{time::Sign, Time}; use gix_object::bstr::ByteSlice; use gix_testtools::tempfile::TempDir; use super::*; -use crate::{file::WriteReflog, FullNameRef}; type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error>>; diff --git a/gix-ref/src/store/file/mod.rs b/gix-ref/src/store/file/mod.rs index 758dcb438cc..19296b0af3f 100644 --- a/gix-ref/src/store/file/mod.rs +++ b/gix-ref/src/store/file/mod.rs @@ -95,29 +95,36 @@ pub(in crate::store_impl::file) fn path_to_name<'a>(path: impl Into<Cow<'a, Path } /// +#[allow(clippy::empty_docs)] pub mod loose; mod overlay_iter; /// +#[allow(clippy::empty_docs)] pub mod iter { pub use super::overlay_iter::{LooseThenPacked, Platform}; /// + #[allow(clippy::empty_docs)] pub mod loose_then_packed { pub use super::super::overlay_iter::Error; } } /// +#[allow(clippy::empty_docs)] pub mod log; /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod transaction; /// +#[allow(clippy::empty_docs)] pub mod packed; mod raw_ext; diff --git a/gix-ref/src/store/file/packed.rs b/gix-ref/src/store/file/packed.rs index 27645e0b20e..c5c42d5a357 100644 --- a/gix-ref/src/store/file/packed.rs +++ b/gix-ref/src/store/file/packed.rs @@ -57,6 +57,7 @@ impl file::Store { } /// +#[allow(clippy::empty_docs)] pub mod transaction { use crate::store_impl::packed; diff --git a/gix-ref/src/store/file/transaction/mod.rs b/gix-ref/src/store/file/transaction/mod.rs index f22493ad86e..7b1119fefd2 100644 --- a/gix-ref/src/store/file/transaction/mod.rs +++ b/gix-ref/src/store/file/transaction/mod.rs @@ -95,7 +95,9 @@ impl std::fmt::Debug for Transaction<'_, '_> { } /// +#[allow(clippy::empty_docs)] pub mod prepare; /// +#[allow(clippy::empty_docs)] pub mod commit; diff --git a/gix-ref/src/store/general/handle/find.rs b/gix-ref/src/store/general/handle/find.rs index 2f11f17557d..40be732dbac 100644 --- a/gix-ref/src/store/general/handle/find.rs +++ b/gix-ref/src/store/general/handle/find.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{store, PartialNameRef, Reference}; mod error { @@ -57,8 +55,6 @@ mod existing { } } - use std::convert::TryInto; - pub use error::Error; use crate::{store, PartialNameRef, Reference}; diff --git a/gix-ref/src/store/general/handle/mod.rs b/gix-ref/src/store/general/handle/mod.rs index 44d9e060d81..50264084088 100644 --- a/gix-ref/src/store/general/handle/mod.rs +++ b/gix-ref/src/store/general/handle/mod.rs @@ -34,6 +34,7 @@ impl crate::Store { } /// +#[allow(clippy::empty_docs)] pub mod find; mod iter { diff --git a/gix-ref/src/store/mod.rs b/gix-ref/src/store/mod.rs index 6691098e211..d8e0a01c432 100644 --- a/gix-ref/src/store/mod.rs +++ b/gix-ref/src/store/mod.rs @@ -1,5 +1,7 @@ /// +#[allow(clippy::empty_docs)] pub mod file; /// +#[allow(clippy::empty_docs)] pub mod packed; diff --git a/gix-ref/src/store/packed/buffer.rs b/gix-ref/src/store/packed/buffer.rs index 40fbf88e24a..f6287364f61 100644 --- a/gix-ref/src/store/packed/buffer.rs +++ b/gix-ref/src/store/packed/buffer.rs @@ -16,6 +16,7 @@ impl AsRef<[u8]> for packed::Backing { } /// +#[allow(clippy::empty_docs)] pub mod open { use std::path::PathBuf; diff --git a/gix-ref/src/store/packed/decode.rs b/gix-ref/src/store/packed/decode.rs index b4f8e8562e6..84856af13d0 100644 --- a/gix-ref/src/store/packed/decode.rs +++ b/gix-ref/src/store/packed/decode.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::{BStr, ByteSlice}; use winnow::{ combinator::{delimited, opt, preceded, terminated}, diff --git a/gix-ref/src/store/packed/find.rs b/gix-ref/src/store/packed/find.rs index 002f76b0f50..b2c2c7565a2 100644 --- a/gix-ref/src/store/packed/find.rs +++ b/gix-ref/src/store/packed/find.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::{BStr, BString, ByteSlice}; use winnow::prelude::*; @@ -131,6 +129,7 @@ mod error { pub use error::Error; /// +#[allow(clippy::empty_docs)] pub mod existing { /// The error returned by [`find_existing()`][super::packed::Buffer::find()] diff --git a/gix-ref/src/store/packed/mod.rs b/gix-ref/src/store/packed/mod.rs index 12aee9f815f..59a7ab02d24 100644 --- a/gix-ref/src/store/packed/mod.rs +++ b/gix-ref/src/store/packed/mod.rs @@ -84,13 +84,17 @@ pub struct Iter<'a> { mod decode; /// +#[allow(clippy::empty_docs)] pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod buffer; /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod transaction; diff --git a/gix-ref/src/store/packed/transaction.rs b/gix-ref/src/store/packed/transaction.rs index e03f9b987da..8c615a10d4b 100644 --- a/gix-ref/src/store/packed/transaction.rs +++ b/gix-ref/src/store/packed/transaction.rs @@ -273,6 +273,7 @@ pub(crate) fn buffer_into_transaction( } /// +#[allow(clippy::empty_docs)] pub mod prepare { /// The error used in [`Transaction::prepare(…)`][crate::file::Transaction::prepare()]. #[derive(Debug, thiserror::Error)] @@ -286,6 +287,7 @@ pub mod prepare { } /// +#[allow(clippy::empty_docs)] pub mod commit { use crate::store_impl::packed; diff --git a/gix-ref/src/target.rs b/gix-ref/src/target.rs index 6d4f69991dd..15d1e3fc0e9 100644 --- a/gix-ref/src/target.rs +++ b/gix-ref/src/target.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, fmt}; +use std::fmt; use gix_hash::{oid, ObjectId}; diff --git a/gix-ref/tests/file/store/find.rs b/gix-ref/tests/file/store/find.rs index b4003e3fc62..64c51c499bb 100644 --- a/gix-ref/tests/file/store/find.rs +++ b/gix-ref/tests/file/store/find.rs @@ -1,6 +1,4 @@ mod existing { - use std::convert::{TryFrom, TryInto}; - use gix_ref::{PartialName, PartialNameRef}; use crate::{file::store_at, hex_to_id}; diff --git a/gix-ref/tests/file/store/iter.rs b/gix-ref/tests/file/store/iter.rs index 4fa6b8e54db..0ed5a971843 100644 --- a/gix-ref/tests/file/store/iter.rs +++ b/gix-ref/tests/file/store/iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::ByteSlice; use crate::{ diff --git a/gix-ref/tests/file/transaction/mod.rs b/gix-ref/tests/file/transaction/mod.rs index aafdaeba66f..e7f8d344e38 100644 --- a/gix-ref/tests/file/transaction/mod.rs +++ b/gix-ref/tests/file/transaction/mod.rs @@ -1,6 +1,4 @@ pub(crate) mod prepare_and_commit { - use std::convert::TryInto; - use gix_date::{time::Sign, Time}; use gix_hash::ObjectId; use gix_object::bstr::BString; diff --git a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs index 97cdb1af335..5ef7a0ad1eb 100644 --- a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs +++ b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_lock::acquire::Fail; use gix_ref::{ file::transaction::PackedRefs, diff --git a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs index bbc42d54d85..8070385a63d 100644 --- a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs +++ b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use gix_lock::acquire::Fail; use gix_object::bstr::{BString, ByteSlice}; diff --git a/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs b/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs index 41114643df2..c4a8c94c4fd 100644 --- a/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs +++ b/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_lock::acquire::Fail; use gix_ref::{ file::ReferenceExt, diff --git a/gix-ref/tests/file/worktree.rs b/gix-ref/tests/file/worktree.rs index e9ee71edbe6..196ac3d1260 100644 --- a/gix-ref/tests/file/worktree.rs +++ b/gix-ref/tests/file/worktree.rs @@ -195,8 +195,6 @@ mod read_only { } mod writable { - use std::convert::TryInto; - use gix_lock::acquire::Fail; use gix_ref::{ file::{transaction::PackedRefs, Store}, diff --git a/gix-ref/tests/fullname/mod.rs b/gix-ref/tests/fullname/mod.rs index 94c99d9a483..5cb5b07f5a9 100644 --- a/gix-ref/tests/fullname/mod.rs +++ b/gix-ref/tests/fullname/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryInto}; +use std::borrow::Cow; use gix_ref::{Category, FullNameRef, PartialNameRef}; diff --git a/gix-ref/tests/packed/find.rs b/gix-ref/tests/packed/find.rs index f8146b66579..9cc6753c6c4 100644 --- a/gix-ref/tests/packed/find.rs +++ b/gix-ref/tests/packed/find.rs @@ -1,5 +1,3 @@ -use std::convert::{TryFrom, TryInto}; - use gix_ref::packed; use gix_testtools::fixture_path_standalone; diff --git a/gix-ref/tests/packed/iter.rs b/gix-ref/tests/packed/iter.rs index 3a546340695..28d15ab1ca3 100644 --- a/gix-ref/tests/packed/iter.rs +++ b/gix-ref/tests/packed/iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::ByteSlice; use gix_ref::packed; diff --git a/gix-ref/tests/reference/mod.rs b/gix-ref/tests/reference/mod.rs index d93729473a5..9662462818d 100644 --- a/gix-ref/tests/reference/mod.rs +++ b/gix-ref/tests/reference/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_ref::{FullName, Target}; #[test] diff --git a/gix-ref/tests/transaction/mod.rs b/gix-ref/tests/transaction/mod.rs index 59766eb1802..64d7627c0dd 100644 --- a/gix-ref/tests/transaction/mod.rs +++ b/gix-ref/tests/transaction/mod.rs @@ -1,5 +1,5 @@ mod refedit_ext { - use std::{cell::RefCell, collections::BTreeMap, convert::TryInto}; + use std::{cell::RefCell, collections::BTreeMap}; use gix_object::bstr::{BString, ByteSlice}; use gix_ref::{ @@ -96,7 +96,7 @@ mod refedit_ext { } mod splitting { - use std::{cell::Cell, convert::TryInto}; + use std::cell::Cell; use gix_ref::{ transaction::{Change, LogChange, PreviousValue, RefEdit, RefEditsExt, RefLog}, diff --git a/gix-refspec/src/lib.rs b/gix-refspec/src/lib.rs index 54d5f3057a9..ec5b4afca80 100644 --- a/gix-refspec/src/lib.rs +++ b/gix-refspec/src/lib.rs @@ -3,10 +3,12 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod parse; pub use parse::function::parse; /// +#[allow(clippy::empty_docs)] pub mod instruction; /// A refspec with references to the memory it was parsed from. @@ -32,6 +34,7 @@ mod spec; mod write; /// +#[allow(clippy::empty_docs)] pub mod match_group; pub use match_group::types::MatchGroup; diff --git a/gix-refspec/src/match_group/mod.rs b/gix-refspec/src/match_group/mod.rs index 2d859cde82f..da5b0f54165 100644 --- a/gix-refspec/src/match_group/mod.rs +++ b/gix-refspec/src/match_group/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod types; pub use types::{Item, Mapping, Outcome, Source, SourceRef}; /// +#[allow(clippy::empty_docs)] pub mod validate; /// Initialization diff --git a/gix-refspec/tests/impls/mod.rs b/gix-refspec/tests/impls/mod.rs index e64c29341bb..c32f5d92060 100644 --- a/gix-refspec/tests/impls/mod.rs +++ b/gix-refspec/tests/impls/mod.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{BTreeSet, HashSet}, - iter::FromIterator, -}; +use std::collections::{BTreeSet, HashSet}; use gix_refspec::{parse::Operation, RefSpec}; diff --git a/gix-revision/src/lib.rs b/gix-revision/src/lib.rs index 1593c25c1a8..9fcf73abfaa 100644 --- a/gix-revision/src/lib.rs +++ b/gix-revision/src/lib.rs @@ -9,12 +9,14 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] #[cfg(feature = "describe")] pub mod describe; #[cfg(feature = "describe")] pub use describe::function::describe; /// +#[allow(clippy::empty_docs)] pub mod spec; pub use gix_revwalk::{graph, Graph, PriorityQueue}; pub use spec::types::Spec; diff --git a/gix-revision/src/spec/mod.rs b/gix-revision/src/spec/mod.rs index 616ad3a26b2..e05b1bc807a 100644 --- a/gix-revision/src/spec/mod.rs +++ b/gix-revision/src/spec/mod.rs @@ -105,5 +105,6 @@ pub(crate) mod types { } /// +#[allow(clippy::empty_docs)] pub mod parse; pub use parse::function::parse; diff --git a/gix-revision/src/spec/parse/function.rs b/gix-revision/src/spec/parse/function.rs index fad845deb18..502ca3e7c8d 100644 --- a/gix-revision/src/spec/parse/function.rs +++ b/gix-revision/src/spec/parse/function.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, str::FromStr, time::SystemTime}; +use std::{str::FromStr, time::SystemTime}; use bstr::{BStr, BString, ByteSlice, ByteVec}; diff --git a/gix-revision/src/spec/parse/mod.rs b/gix-revision/src/spec/parse/mod.rs index 5a64012c666..bd5381fd200 100644 --- a/gix-revision/src/spec/parse/mod.rs +++ b/gix-revision/src/spec/parse/mod.rs @@ -46,6 +46,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod delegate; /// A delegate to be informed about parse events, with methods split into categories. diff --git a/gix-revwalk/src/graph/commit.rs b/gix-revwalk/src/graph/commit.rs index 8bfc8afc8a0..f7bb39109c5 100644 --- a/gix-revwalk/src/graph/commit.rs +++ b/gix-revwalk/src/graph/commit.rs @@ -123,6 +123,7 @@ impl<'graph> Iterator for Parents<'graph> { } /// +#[allow(clippy::empty_docs)] pub mod iter_parents { /// The error returned by the [`Parents`][super::Parents] iterator. #[derive(Debug, thiserror::Error)] @@ -136,6 +137,7 @@ pub mod iter_parents { } /// +#[allow(clippy::empty_docs)] pub mod to_owned { /// The error returned by [`to_owned()`][crate::graph::LazyCommit::to_owned()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-revwalk/src/graph/mod.rs b/gix-revwalk/src/graph/mod.rs index fa3b16870fe..bd004826bb4 100644 --- a/gix-revwalk/src/graph/mod.rs +++ b/gix-revwalk/src/graph/mod.rs @@ -9,10 +9,12 @@ use crate::Graph; pub type IdMap<T> = gix_hashtable::HashMap<gix_hash::ObjectId, T>; /// +#[allow(clippy::empty_docs)] pub mod commit; mod errors { /// + #[allow(clippy::empty_docs)] pub mod insert_parents { use crate::graph::commit::iter_parents; @@ -30,6 +32,7 @@ mod errors { } /// + #[allow(clippy::empty_docs)] pub mod try_lookup_or_insert_default { use crate::graph::commit::to_owned; diff --git a/gix-revwalk/src/lib.rs b/gix-revwalk/src/lib.rs index 67bfc6dea6b..a05b1bb2868 100644 --- a/gix-revwalk/src/lib.rs +++ b/gix-revwalk/src/lib.rs @@ -39,6 +39,7 @@ pub struct Graph<'find, T> { } /// +#[allow(clippy::empty_docs)] pub mod graph; /// A utility type implementing a queue which can be used to automatically sort data by its time in ascending order. diff --git a/gix-sec/src/lib.rs b/gix-sec/src/lib.rs index 35b7b34d524..309c734ed31 100644 --- a/gix-sec/src/lib.rs +++ b/gix-sec/src/lib.rs @@ -22,6 +22,7 @@ pub enum Trust { } /// +#[allow(clippy::empty_docs)] pub mod trust; /// Allow, deny or forbid using a resource or performing an action. @@ -37,6 +38,7 @@ pub enum Permission { } /// +#[allow(clippy::empty_docs)] pub mod permission; bitflags::bitflags! { diff --git a/gix-status/src/index_as_worktree/mod.rs b/gix-status/src/index_as_worktree/mod.rs index 7dce0c43306..9f1c38f6ab1 100644 --- a/gix-status/src/index_as_worktree/mod.rs +++ b/gix-status/src/index_as_worktree/mod.rs @@ -1,5 +1,6 @@ //! Changes between an index and a worktree. /// +#[allow(clippy::empty_docs)] mod types; pub use types::{Change, Conflict, Context, EntryStatus, Error, Options, Outcome, VisitEntry}; @@ -8,4 +9,5 @@ pub use recorder::{Record, Recorder}; pub(super) mod function; /// +#[allow(clippy::empty_docs)] pub mod traits; diff --git a/gix-status/src/index_as_worktree/traits.rs b/gix-status/src/index_as_worktree/traits.rs index 8d86b579aaa..9865d1514e2 100644 --- a/gix-status/src/index_as_worktree/traits.rs +++ b/gix-status/src/index_as_worktree/traits.rs @@ -50,6 +50,7 @@ pub trait ReadData<'a> { } /// +#[allow(clippy::empty_docs)] pub mod read_data { use std::sync::atomic::{AtomicU64, Ordering}; diff --git a/gix-submodule/src/config.rs b/gix-submodule/src/config.rs index 12194f4595f..98148b71cc8 100644 --- a/gix-submodule/src/config.rs +++ b/gix-submodule/src/config.rs @@ -149,6 +149,7 @@ pub struct Error { } /// +#[allow(clippy::empty_docs)] pub mod branch { use bstr::BString; @@ -164,6 +165,7 @@ pub mod branch { } /// +#[allow(clippy::empty_docs)] pub mod update { use bstr::BString; @@ -179,6 +181,7 @@ pub mod update { } /// +#[allow(clippy::empty_docs)] pub mod url { use bstr::BString; @@ -197,6 +200,7 @@ pub mod url { } /// +#[allow(clippy::empty_docs)] pub mod path { use bstr::BString; diff --git a/gix-submodule/src/lib.rs b/gix-submodule/src/lib.rs index 639af30fae3..7b3068b7739 100644 --- a/gix-submodule/src/lib.rs +++ b/gix-submodule/src/lib.rs @@ -19,9 +19,11 @@ pub struct File { mod access; /// +#[allow(clippy::empty_docs)] pub mod config; /// +#[allow(clippy::empty_docs)] pub mod is_active_platform; /// A platform to keep the state necessary to perform repeated active checks, created by [File::is_active_platform()]. @@ -85,6 +87,7 @@ impl File { } /// +#[allow(clippy::empty_docs)] mod init { use std::path::PathBuf; diff --git a/gix-tempfile/src/handle.rs b/gix-tempfile/src/handle.rs index a4b6627c806..4365a2ef849 100644 --- a/gix-tempfile/src/handle.rs +++ b/gix-tempfile/src/handle.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use std::{io, path::Path}; use tempfile::{NamedTempFile, TempPath}; @@ -249,6 +250,7 @@ mod io_impls { } /// +#[allow(clippy::empty_docs)] pub mod persist { use std::path::Path; diff --git a/gix-tempfile/src/lib.rs b/gix-tempfile/src/lib.rs index fc1580903b5..9a5d52405b6 100644 --- a/gix-tempfile/src/lib.rs +++ b/gix-tempfile/src/lib.rs @@ -108,6 +108,7 @@ pub mod handle; use crate::handle::{Closed, Writable}; /// +#[allow(clippy::empty_docs)] pub mod registry; static NEXT_MAP_INDEX: AtomicUsize = AtomicUsize::new(0); diff --git a/gix-tempfile/src/signal.rs b/gix-tempfile/src/signal.rs index d7c0f83684b..2591dc517e8 100644 --- a/gix-tempfile/src/signal.rs +++ b/gix-tempfile/src/signal.rs @@ -16,6 +16,7 @@ pub fn setup(mode: handler::Mode) { } /// +#[allow(clippy::empty_docs)] pub mod handler { use std::sync::atomic::AtomicUsize; diff --git a/gix-trace/src/lib.rs b/gix-trace/src/lib.rs index 283716405d1..db90f77ad03 100644 --- a/gix-trace/src/lib.rs +++ b/gix-trace/src/lib.rs @@ -54,6 +54,7 @@ mod disabled; pub use disabled::Span; /// +#[allow(clippy::empty_docs)] pub mod event { #[cfg(feature = "tracing")] pub use tracing_core::Level; diff --git a/gix-transport/src/client/async_io/connect.rs b/gix-transport/src/client/async_io/connect.rs index 4d09d0e98d2..72ba2289613 100644 --- a/gix-transport/src/client/async_io/connect.rs +++ b/gix-transport/src/client/async_io/connect.rs @@ -2,8 +2,6 @@ pub use crate::client::non_io_types::connect::{Error, Options}; #[cfg(feature = "async-std")] pub(crate) mod function { - use std::convert::TryInto; - use crate::client::{git, non_io_types::connect::Error}; /// A general purpose connector connecting to a repository identified by the given `url`. diff --git a/gix-transport/src/client/async_io/mod.rs b/gix-transport/src/client/async_io/mod.rs index 1ea85cdcf58..e2e2cab99a9 100644 --- a/gix-transport/src/client/async_io/mod.rs +++ b/gix-transport/src/client/async_io/mod.rs @@ -8,6 +8,7 @@ mod traits; pub use traits::{SetServiceResponse, Transport, TransportV2Ext}; /// +#[allow(clippy::empty_docs)] pub mod connect; #[cfg(feature = "async-std")] pub use connect::function::connect; diff --git a/gix-transport/src/client/blocking_io/connect.rs b/gix-transport/src/client/blocking_io/connect.rs index 557a5914b8e..59f9e02c33c 100644 --- a/gix-transport/src/client/blocking_io/connect.rs +++ b/gix-transport/src/client/blocking_io/connect.rs @@ -1,8 +1,6 @@ pub use crate::client::non_io_types::connect::{Error, Options}; pub(crate) mod function { - use std::convert::TryInto; - use crate::client::{non_io_types::connect::Error, Transport}; /// A general purpose connector connecting to a repository identified by the given `url`. diff --git a/gix-transport/src/client/blocking_io/http/mod.rs b/gix-transport/src/client/blocking_io/http/mod.rs index d42be09014f..de717006eae 100644 --- a/gix-transport/src/client/blocking_io/http/mod.rs +++ b/gix-transport/src/client/blocking_io/http/mod.rs @@ -27,6 +27,7 @@ compile_error!("Cannot set both 'http-client-reqwest' and 'http-client-curl' fea #[cfg(feature = "http-client-curl")] /// +#[allow(clippy::empty_docs)] pub mod curl; /// The experimental `reqwest` backend. @@ -39,6 +40,7 @@ pub mod reqwest; mod traits; /// +#[allow(clippy::empty_docs)] pub mod options { /// A function to authenticate a URL. pub type AuthenticateFn = diff --git a/gix-transport/src/client/blocking_io/http/reqwest/mod.rs b/gix-transport/src/client/blocking_io/http/reqwest/mod.rs index 7c68b166ea2..9f07c41f64b 100644 --- a/gix-transport/src/client/blocking_io/http/reqwest/mod.rs +++ b/gix-transport/src/client/blocking_io/http/reqwest/mod.rs @@ -25,4 +25,5 @@ pub struct Options { } /// +#[allow(clippy::empty_docs)] pub mod remote; diff --git a/gix-transport/src/client/blocking_io/http/reqwest/remote.rs b/gix-transport/src/client/blocking_io/http/reqwest/remote.rs index 7f8e8284604..1aa8bdb08e8 100644 --- a/gix-transport/src/client/blocking_io/http/reqwest/remote.rs +++ b/gix-transport/src/client/blocking_io/http/reqwest/remote.rs @@ -1,6 +1,5 @@ use std::{ any::Any, - convert::TryFrom, io::{Read, Write}, str::FromStr, sync::{atomic, Arc}, diff --git a/gix-transport/src/client/blocking_io/mod.rs b/gix-transport/src/client/blocking_io/mod.rs index dfb3752af95..087aa6cc0c4 100644 --- a/gix-transport/src/client/blocking_io/mod.rs +++ b/gix-transport/src/client/blocking_io/mod.rs @@ -1,7 +1,9 @@ /// +#[allow(clippy::empty_docs)] pub mod connect; /// +#[allow(clippy::empty_docs)] pub mod file; /// #[cfg(feature = "http-client")] @@ -14,6 +16,7 @@ mod request; pub use request::RequestWriter; /// +#[allow(clippy::empty_docs)] pub mod ssh; mod traits; diff --git a/gix-transport/src/client/blocking_io/ssh/mod.rs b/gix-transport/src/client/blocking_io/ssh/mod.rs index 3d83493ca85..16f47bd25f4 100644 --- a/gix-transport/src/client/blocking_io/ssh/mod.rs +++ b/gix-transport/src/client/blocking_io/ssh/mod.rs @@ -34,6 +34,7 @@ pub enum ProgramKind { mod program_kind; /// +#[allow(clippy::empty_docs)] pub mod invocation { use std::ffi::OsString; @@ -54,6 +55,7 @@ pub mod invocation { } /// +#[allow(clippy::empty_docs)] pub mod connect { use std::ffi::{OsStr, OsString}; diff --git a/gix-transport/src/client/capabilities.rs b/gix-transport/src/client/capabilities.rs index 21513ace664..408bf8bddeb 100644 --- a/gix-transport/src/client/capabilities.rs +++ b/gix-transport/src/client/capabilities.rs @@ -167,6 +167,7 @@ impl Capabilities { #[cfg(feature = "blocking-client")] /// +#[allow(clippy::empty_docs)] pub mod recv { use std::io; @@ -252,6 +253,7 @@ pub mod recv { #[cfg(feature = "async-client")] #[allow(missing_docs)] /// +#[allow(clippy::empty_docs)] pub mod recv { use bstr::ByteVec; use futures_io::AsyncRead; diff --git a/gix-transport/src/client/git/blocking_io.rs b/gix-transport/src/client/git/blocking_io.rs index 42f253dfb3a..eb072c8ec5d 100644 --- a/gix-transport/src/client/git/blocking_io.rs +++ b/gix-transport/src/client/git/blocking_io.rs @@ -132,6 +132,7 @@ where } /// +#[allow(clippy::empty_docs)] pub mod connect { use std::net::{TcpStream, ToSocketAddrs}; diff --git a/gix-transport/src/client/mod.rs b/gix-transport/src/client/mod.rs index b5296543e41..427a55f4683 100644 --- a/gix-transport/src/client/mod.rs +++ b/gix-transport/src/client/mod.rs @@ -23,6 +23,7 @@ pub use blocking_io::{ pub use connect::function::connect; /// +#[allow(clippy::empty_docs)] pub mod capabilities; #[doc(inline)] pub use capabilities::Capabilities; diff --git a/gix-transport/src/lib.rs b/gix-transport/src/lib.rs index 60f2d538403..6a698741f4f 100644 --- a/gix-transport/src/lib.rs +++ b/gix-transport/src/lib.rs @@ -82,6 +82,7 @@ mod traits { pub use traits::IsSpuriousError; /// +#[allow(clippy::empty_docs)] pub mod client; #[doc(inline)] diff --git a/gix-traverse/src/commit.rs b/gix-traverse/src/commit.rs index 4bf49d2a0b4..c1d18ebf6ff 100644 --- a/gix-traverse/src/commit.rs +++ b/gix-traverse/src/commit.rs @@ -87,6 +87,7 @@ pub struct Info { } /// +#[allow(clippy::empty_docs)] pub mod ancestors { use std::{ borrow::{Borrow, BorrowMut}, diff --git a/gix-traverse/src/tree/mod.rs b/gix-traverse/src/tree/mod.rs index 6a74ada28f3..2e41a1cfd50 100644 --- a/gix-traverse/src/tree/mod.rs +++ b/gix-traverse/src/tree/mod.rs @@ -41,6 +41,7 @@ pub struct Recorder { } /// +#[allow(clippy::empty_docs)] pub mod visit { /// What to do after an entry was [recorded][super::Visit::visit_tree()]. #[derive(Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)] @@ -62,8 +63,10 @@ pub mod visit { } /// +#[allow(clippy::empty_docs)] pub mod recorder; /// +#[allow(clippy::empty_docs)] pub mod breadthfirst; pub use breadthfirst::impl_::traverse as breadthfirst; diff --git a/gix-url/src/impls.rs b/gix-url/src/impls.rs index a579121cb65..d153d707fde 100644 --- a/gix-url/src/impls.rs +++ b/gix-url/src/impls.rs @@ -1,7 +1,4 @@ -use std::{ - convert::TryFrom, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use bstr::BStr; diff --git a/gix-url/src/lib.rs b/gix-url/src/lib.rs index a5b438901ec..a810ad66c7d 100644 --- a/gix-url/src/lib.rs +++ b/gix-url/src/lib.rs @@ -13,6 +13,7 @@ use std::{borrow::Cow, path::PathBuf}; use bstr::{BStr, BString}; /// +#[allow(clippy::empty_docs)] pub mod expand_path; mod scheme; @@ -20,6 +21,7 @@ pub use scheme::Scheme; mod impls; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse the given `bytes` as a [git url](Url). diff --git a/gix-utils/src/lib.rs b/gix-utils/src/lib.rs index 5e2e32d1365..bd61a08b0d1 100644 --- a/gix-utils/src/lib.rs +++ b/gix-utils/src/lib.rs @@ -5,15 +5,19 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod backoff; /// +#[allow(clippy::empty_docs)] pub mod buffers; /// +#[allow(clippy::empty_docs)] pub mod str; /// +#[allow(clippy::empty_docs)] pub mod btoi; /// A utility to do buffer-swapping with. diff --git a/gix-utils/tests/backoff/mod.rs b/gix-utils/tests/backoff/mod.rs index a84ed754360..ca9077841ca 100644 --- a/gix-utils/tests/backoff/mod.rs +++ b/gix-utils/tests/backoff/mod.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, time::Duration}; +use std::time::Duration; use gix_utils::backoff::Exponential; diff --git a/gix-validate/src/lib.rs b/gix-validate/src/lib.rs index f7b6cc4aa76..f0493960c73 100644 --- a/gix-validate/src/lib.rs +++ b/gix-validate/src/lib.rs @@ -3,10 +3,13 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod reference; /// +#[allow(clippy::empty_docs)] pub mod tag; /// +#[allow(clippy::empty_docs)] pub mod submodule; diff --git a/gix-validate/src/reference.rs b/gix-validate/src/reference.rs index fff87e3b40c..ac222ed105c 100644 --- a/gix-validate/src/reference.rs +++ b/gix-validate/src/reference.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod name { use std::convert::Infallible; diff --git a/gix-validate/src/submodule.rs b/gix-validate/src/submodule.rs index 6811f4ff2eb..c524cb22a5a 100644 --- a/gix-validate/src/submodule.rs +++ b/gix-validate/src/submodule.rs @@ -1,6 +1,7 @@ use bstr::{BStr, ByteSlice}; /// +#[allow(clippy::empty_docs)] pub mod name { /// The error used in [name()](super::name()). #[derive(Debug, thiserror::Error)] diff --git a/gix-validate/src/tag.rs b/gix-validate/src/tag.rs index 91ceec18549..eb5b7e12066 100644 --- a/gix-validate/src/tag.rs +++ b/gix-validate/src/tag.rs @@ -1,6 +1,7 @@ use bstr::BStr; /// +#[allow(clippy::empty_docs)] pub mod name { use bstr::BString; diff --git a/gix-worktree-state/src/lib.rs b/gix-worktree-state/src/lib.rs index 2c2cf67f64d..32ab5653bc3 100644 --- a/gix-worktree-state/src/lib.rs +++ b/gix-worktree-state/src/lib.rs @@ -2,5 +2,6 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod checkout; pub use checkout::function::checkout; diff --git a/gix-worktree-stream/src/lib.rs b/gix-worktree-stream/src/lib.rs index 8169f888d46..82c57b809cb 100644 --- a/gix-worktree-stream/src/lib.rs +++ b/gix-worktree-stream/src/lib.rs @@ -26,6 +26,7 @@ pub struct Stream { } /// +#[allow(clippy::empty_docs)] pub mod entry; pub(crate) mod protocol; diff --git a/gix-worktree/src/lib.rs b/gix-worktree/src/lib.rs index 240285a9128..7238538e73d 100644 --- a/gix-worktree/src/lib.rs +++ b/gix-worktree/src/lib.rs @@ -57,4 +57,5 @@ pub struct Stack { pub(crate) type PathIdMapping = (BString, gix_hash::ObjectId); /// +#[allow(clippy::empty_docs)] pub mod stack; diff --git a/gix-worktree/src/stack/mod.rs b/gix-worktree/src/stack/mod.rs index b6c03d175bf..4629a7a08ed 100644 --- a/gix-worktree/src/stack/mod.rs +++ b/gix-worktree/src/stack/mod.rs @@ -195,9 +195,11 @@ impl Stack { } /// +#[allow(clippy::empty_docs)] pub mod delegate; use delegate::StackDelegate; mod platform; /// +#[allow(clippy::empty_docs)] pub mod state; diff --git a/gix-worktree/src/stack/state/mod.rs b/gix-worktree/src/stack/state/mod.rs index 30e3c609f1b..ba6383fee85 100644 --- a/gix-worktree/src/stack/state/mod.rs +++ b/gix-worktree/src/stack/state/mod.rs @@ -53,6 +53,7 @@ pub struct Ignore { #[cfg(feature = "attributes")] pub mod attributes; /// +#[allow(clippy::empty_docs)] pub mod ignore; /// Initialization diff --git a/gix/src/clone/checkout.rs b/gix/src/clone/checkout.rs index 84a3fedbddb..e241adb18a1 100644 --- a/gix/src/clone/checkout.rs +++ b/gix/src/clone/checkout.rs @@ -1,6 +1,7 @@ use crate::{clone::PrepareCheckout, Repository}; /// +#[allow(clippy::empty_docs)] pub mod main_worktree { use std::{path::PathBuf, sync::atomic::AtomicBool}; diff --git a/gix/src/clone/fetch/util.rs b/gix/src/clone/fetch/util.rs index 627201301de..e50ec7509d0 100644 --- a/gix/src/clone/fetch/util.rs +++ b/gix/src/clone/fetch/util.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryInto, io::Write}; +use std::{borrow::Cow, io::Write}; use gix_ref::{ transaction::{LogChange, RefLog}, diff --git a/gix/src/clone/mod.rs b/gix/src/clone/mod.rs index a6022c1c6f6..03328253e78 100644 --- a/gix/src/clone/mod.rs +++ b/gix/src/clone/mod.rs @@ -1,6 +1,4 @@ #![allow(clippy::result_large_err)] -use std::convert::TryInto; - use crate::{bstr::BString, config::tree::gitoxide, remote}; type ConfigureRemoteFn = diff --git a/gix/src/commit.rs b/gix/src/commit.rs index 4101b1512ec..a6f4da4488d 100644 --- a/gix/src/commit.rs +++ b/gix/src/commit.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] /// An empty array of a type usable with the `gix::easy` API to help declaring no parents should be used pub const NO_PARENT_IDS: [gix_hash::ObjectId; 0] = []; @@ -22,6 +23,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "revision")] pub mod describe { use std::borrow::Cow; diff --git a/gix/src/config/cache/init.rs b/gix/src/config/cache/init.rs index 449eb1611e3..76e6dd81e41 100644 --- a/gix/src/config/cache/init.rs +++ b/gix/src/config/cache/init.rs @@ -303,7 +303,7 @@ impl crate::Repository { fn apply_changed_values(&mut self) { self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.work_dir().is_some()); - self.refs.namespace = self.config.refs_namespace.clone(); + self.refs.namespace.clone_from(&self.config.refs_namespace); } } diff --git a/gix/src/config/mod.rs b/gix/src/config/mod.rs index 6f00337b1b1..258d07e89ab 100644 --- a/gix/src/config/mod.rs +++ b/gix/src/config/mod.rs @@ -9,6 +9,7 @@ mod snapshot; pub use snapshot::credential_helpers; /// +#[allow(clippy::empty_docs)] pub mod overrides; pub mod tree; @@ -48,6 +49,7 @@ pub(crate) mod section { } /// +#[allow(clippy::empty_docs)] pub mod set_value { /// The error produced when calling [`SnapshotMut::set(_subsection)?_value()`][crate::config::SnapshotMut::set_value()] #[derive(Debug, thiserror::Error)] @@ -107,8 +109,10 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod diff { /// + #[allow(clippy::empty_docs)] pub mod algorithm { use crate::bstr::BString; @@ -124,6 +128,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod pipeline_options { /// The error produced when obtaining options needed to fill in [gix_diff::blob::pipeline::Options]. #[derive(Debug, thiserror::Error)] @@ -137,6 +142,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod drivers { use crate::bstr::BString; @@ -155,6 +161,7 @@ pub mod diff { } /// +#[allow(clippy::empty_docs)] pub mod stat_options { /// The error produced when collecting stat information, and returned by [Repository::stat_options()](crate::Repository::stat_options()). #[derive(Debug, thiserror::Error)] @@ -207,6 +214,7 @@ pub mod command_context { } /// +#[allow(clippy::empty_docs)] pub mod exclude_stack { use std::path::PathBuf; @@ -224,6 +232,7 @@ pub mod exclude_stack { } /// +#[allow(clippy::empty_docs)] pub mod attribute_stack { /// The error produced when setting up the attribute stack to query `gitattributes`. #[derive(Debug, thiserror::Error)] @@ -237,8 +246,10 @@ pub mod attribute_stack { } /// +#[allow(clippy::empty_docs)] pub mod protocol { /// + #[allow(clippy::empty_docs)] pub mod allow { use crate::bstr::BString; @@ -254,6 +265,7 @@ pub mod protocol { } /// +#[allow(clippy::empty_docs)] pub mod ssh_connect_options { /// The error produced when obtaining ssh connection configuration. #[derive(Debug, thiserror::Error)] @@ -263,6 +275,7 @@ pub mod ssh_connect_options { } /// +#[allow(clippy::empty_docs)] pub mod key { use crate::bstr::BString; @@ -363,6 +376,7 @@ pub mod key { } /// +#[allow(clippy::empty_docs)] pub mod encoding { use crate::bstr::BString; @@ -380,8 +394,10 @@ pub mod encoding { } /// +#[allow(clippy::empty_docs)] pub mod checkout { /// + #[allow(clippy::empty_docs)] pub mod workers { use crate::config; @@ -391,6 +407,7 @@ pub mod checkout { } /// +#[allow(clippy::empty_docs)] pub mod abbrev { use crate::bstr::BString; @@ -406,8 +423,10 @@ pub mod abbrev { } /// +#[allow(clippy::empty_docs)] pub mod remote { /// + #[allow(clippy::empty_docs)] pub mod symbolic_name { /// The error produced when failing to produce a symbolic remote name from configuration. pub type Error = super::super::key::Error<crate::remote::name::Error, 'v', 'i'>; @@ -415,66 +434,77 @@ pub mod remote { } /// +#[allow(clippy::empty_docs)] pub mod time { /// The error produced when failing to parse time from configuration. pub type Error = super::key::Error<gix_date::parse::Error, 't', 'i'>; } /// +#[allow(clippy::empty_docs)] pub mod lock_timeout { /// The error produced when failing to parse timeout for locks. pub type Error = super::key::Error<gix_config::value::Error, 'i', 'i'>; } /// +#[allow(clippy::empty_docs)] pub mod duration { /// The error produced when failing to parse durations (in milliseconds). pub type Error = super::key::Error<gix_config::value::Error, 'd', 'i'>; } /// +#[allow(clippy::empty_docs)] pub mod boolean { /// The error produced when failing to parse time from configuration. pub type Error = super::key::Error<gix_config::value::Error, 'b', 'i'>; } /// +#[allow(clippy::empty_docs)] pub mod unsigned_integer { /// The error produced when failing to parse a signed integer from configuration. pub type Error = super::key::Error<gix_config::value::Error, 'k', 'u'>; } /// +#[allow(clippy::empty_docs)] pub mod url { /// The error produced when failing to parse a url from the configuration. pub type Error = super::key::Error<gix_url::parse::Error, 'u', 'p'>; } /// +#[allow(clippy::empty_docs)] pub mod string { /// The error produced when failing to interpret configuration as UTF-8 encoded string. pub type Error = super::key::Error<crate::bstr::Utf8Error, 'w', 'd'>; } /// +#[allow(clippy::empty_docs)] pub mod refspec { /// The error produced when failing to parse a refspec from the configuration. pub type Error = super::key::Error<gix_refspec::parse::Error, 'r', 'p'>; } /// +#[allow(clippy::empty_docs)] pub mod refs_namespace { /// The error produced when failing to parse a refspec from the configuration. pub type Error = super::key::Error<gix_validate::reference::name::Error, 'v', 'i'>; } /// +#[allow(clippy::empty_docs)] pub mod ssl_version { /// The error produced when failing to parse a refspec from the configuration. pub type Error = super::key::Error<std::convert::Infallible, 's', 'i'>; } /// +#[allow(clippy::empty_docs)] pub mod transport { use std::borrow::Cow; @@ -514,6 +544,7 @@ pub mod transport { } /// + #[allow(clippy::empty_docs)] pub mod http { use std::borrow::Cow; diff --git a/gix/src/config/overrides.rs b/gix/src/config/overrides.rs index 6b3fc728cc6..7fbd43148bb 100644 --- a/gix/src/config/overrides.rs +++ b/gix/src/config/overrides.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::bstr::{BStr, BString, ByteSlice}; /// The error returned by [`SnapshotMut::apply_cli_overrides()`][crate::config::SnapshotMut::append_config()]. diff --git a/gix/src/config/snapshot/credential_helpers.rs b/gix/src/config/snapshot/credential_helpers.rs index 54499a1c34a..fdac608f541 100644 --- a/gix/src/config/snapshot/credential_helpers.rs +++ b/gix/src/config/snapshot/credential_helpers.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; pub use error::Error; diff --git a/gix/src/config/tree/mod.rs b/gix/src/config/tree/mod.rs index 6610a091fb3..6f85f803864 100644 --- a/gix/src/config/tree/mod.rs +++ b/gix/src/config/tree/mod.rs @@ -109,8 +109,10 @@ pub use sections::{diff, Diff}; pub mod keys; /// +#[allow(clippy::empty_docs)] pub mod key { /// + #[allow(clippy::empty_docs)] pub mod validate { /// The error returned by [`Key::validate()`][crate::config::tree::Key::validate()]. #[derive(Debug, thiserror::Error)] @@ -122,6 +124,7 @@ pub mod key { } } /// + #[allow(clippy::empty_docs)] pub mod validate_assignment { /// The error returned by [`Key::validated_assignment`*()][crate::config::tree::Key::validated_assignment_fmt()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/config/tree/sections/branch.rs b/gix/src/config/tree/sections/branch.rs index 8e1e0a4b862..e63227fc90f 100644 --- a/gix/src/config/tree/sections/branch.rs +++ b/gix/src/config/tree/sections/branch.rs @@ -49,6 +49,7 @@ mod merge { } /// +#[allow(clippy::empty_docs)] pub mod validate { use crate::{ bstr::BStr, diff --git a/gix/src/config/tree/sections/checkout.rs b/gix/src/config/tree/sections/checkout.rs index 27f31ee841f..03d274b918c 100644 --- a/gix/src/config/tree/sections/checkout.rs +++ b/gix/src/config/tree/sections/checkout.rs @@ -42,6 +42,7 @@ mod workers { } /// +#[allow(clippy::empty_docs)] pub mod validate { use crate::{bstr::BStr, config::tree::keys}; diff --git a/gix/src/create.rs b/gix/src/create.rs index b03045cddf7..f2e97aac57b 100644 --- a/gix/src/create.rs +++ b/gix/src/create.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryFrom, fs::{self, OpenOptions}, io::Write, path::{Path, PathBuf}, @@ -89,6 +88,7 @@ fn write_file(data: &[u8], path: &Path) -> Result<(), Error> { let mut file = OpenOptions::new() .write(true) .create(true) + .truncate(true) .append(false) .open(path) .map_err(|e| Error::IoOpen { diff --git a/gix/src/diff.rs b/gix/src/diff.rs index 5d15193b270..bfe3feb0635 100644 --- a/gix/src/diff.rs +++ b/gix/src/diff.rs @@ -1,6 +1,7 @@ pub use gix_diff::*; /// +#[allow(clippy::empty_docs)] pub mod rename { /// Determine how to do rename tracking. #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -28,6 +29,7 @@ mod utils { }; /// + #[allow(clippy::empty_docs)] pub mod new_rewrites { /// The error returned by [`new_rewrites()`](super::new_rewrites()). #[derive(Debug, thiserror::Error)] @@ -41,6 +43,7 @@ mod utils { } /// + #[allow(clippy::empty_docs)] pub mod resource_cache { /// The error returned by [`resource_cache()`](super::resource_cache()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/env.rs b/gix/src/env.rs index eddc5f71656..307138b8fb3 100644 --- a/gix/src/env.rs +++ b/gix/src/env.rs @@ -50,6 +50,7 @@ pub fn os_str_to_bstring(input: &OsStr) -> Option<BString> { pub mod collate { /// + #[allow(clippy::empty_docs)] pub mod fetch { /// An error which combines all possible errors when opening a repository, finding remotes and using them to fetch. /// diff --git a/gix/src/ext/reference.rs b/gix/src/ext/reference.rs index 57e4e4fe7c9..c479fa2ec8f 100644 --- a/gix/src/ext/reference.rs +++ b/gix/src/ext/reference.rs @@ -1,7 +1,3 @@ -pub trait Sealed {} - -impl Sealed for gix_ref::Reference {} - /// Extensions for [references][gix_ref::Reference]. pub trait ReferenceExt { /// Attach [`Repository`][crate::Repository] to the given reference. It can be detached later with [`detach()]`. diff --git a/gix/src/ext/rev_spec.rs b/gix/src/ext/rev_spec.rs index caa58e2c77f..f9b38e29cb4 100644 --- a/gix/src/ext/rev_spec.rs +++ b/gix/src/ext/rev_spec.rs @@ -1,7 +1,3 @@ -pub trait Sealed {} - -impl Sealed for gix_ref::Reference {} - /// Extensions for [revision specifications][gix_revision::Spec]. pub trait RevSpecExt { /// Attach [`Repository`][crate::Repository] to the given rev-spec. diff --git a/gix/src/filter.rs b/gix/src/filter.rs index f05e332a255..c856fe521da 100644 --- a/gix/src/filter.rs +++ b/gix/src/filter.rs @@ -14,8 +14,10 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod pipeline { /// + #[allow(clippy::empty_docs)] pub mod options { use crate::{bstr::BString, config}; @@ -38,6 +40,7 @@ pub mod pipeline { } /// + #[allow(clippy::empty_docs)] pub mod convert_to_git { /// The error returned by [Pipeline::convert_to_git()][crate::filter::Pipeline::convert_to_git()]. #[derive(Debug, thiserror::Error)] @@ -51,6 +54,7 @@ pub mod pipeline { } /// + #[allow(clippy::empty_docs)] pub mod convert_to_worktree { /// The error returned by [Pipeline::convert_to_worktree()][crate::filter::Pipeline::convert_to_worktree()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/head/log.rs b/gix/src/head/log.rs index 6aa7ed1d39e..88d90da890e 100644 --- a/gix/src/head/log.rs +++ b/gix/src/head/log.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use crate::{ diff --git a/gix/src/head/mod.rs b/gix/src/head/mod.rs index 399b872ba95..8740a932959 100644 --- a/gix/src/head/mod.rs +++ b/gix/src/head/mod.rs @@ -1,6 +1,5 @@ //! -use std::convert::TryInto; - +#![allow(clippy::empty_docs)] use gix_hash::ObjectId; use gix_ref::FullNameRef; @@ -118,7 +117,9 @@ mod remote { } /// +#[allow(clippy::empty_docs)] pub mod log; /// +#[allow(clippy::empty_docs)] pub mod peel; diff --git a/gix/src/head/peel.rs b/gix/src/head/peel.rs index 4ee116ed0db..2f16b8c6cf6 100644 --- a/gix/src/head/peel.rs +++ b/gix/src/head/peel.rs @@ -22,6 +22,7 @@ mod error { pub use error::Error; /// +#[allow(clippy::empty_docs)] pub mod into_id { use crate::object; @@ -39,6 +40,7 @@ pub mod into_id { } /// +#[allow(clippy::empty_docs)] pub mod to_commit { use crate::object; @@ -54,6 +56,7 @@ pub mod to_commit { } /// +#[allow(clippy::empty_docs)] pub mod to_object { /// The error returned by [`Head::peel_to_object_in_place()`](super::Head::peel_to_object_in_place()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/id.rs b/gix/src/id.rs index 7214ec320da..2cbe8a72e28 100644 --- a/gix/src/id.rs +++ b/gix/src/id.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use std::ops::Deref; use gix_hash::{oid, ObjectId}; @@ -68,6 +69,7 @@ fn calculate_auto_hex_len(num_packed_objects: u64) -> usize { } /// +#[allow(clippy::empty_docs)] pub mod shorten { /// Returned by [`Id::prefix()`][super::Id::shorten()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/init.rs b/gix/src/init.rs index f8e574eab70..624c4a248cc 100644 --- a/gix/src/init.rs +++ b/gix/src/init.rs @@ -1,5 +1,5 @@ #![allow(clippy::result_large_err)] -use std::{borrow::Cow, convert::TryInto, path::Path}; +use std::{borrow::Cow, path::Path}; use gix_macros::momo; use gix_ref::{ diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 689a0a8e732..96bd413f8ff 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -146,12 +146,14 @@ pub mod interrupt; mod ext; /// +#[allow(clippy::empty_docs)] pub mod prelude; #[cfg(feature = "excludes")] mod attribute_stack; /// +#[allow(clippy::empty_docs)] pub mod path; /// The standard type for a store to handle git references. @@ -172,10 +174,12 @@ pub use types::{ pub use types::{Pathspec, PathspecDetached, Submodule}; /// +#[allow(clippy::empty_docs)] pub mod clone; pub mod commit; #[cfg(feature = "dirwalk")] /// +#[allow(clippy::empty_docs)] pub mod dirwalk; pub mod head; pub mod id; @@ -189,11 +193,14 @@ pub mod submodule; pub mod tag; /// +#[allow(clippy::empty_docs)] pub mod progress; /// +#[allow(clippy::empty_docs)] pub mod push; /// +#[allow(clippy::empty_docs)] pub mod diff; /// See [`ThreadSafeRepository::discover()`], but returns a [`Repository`] instead. @@ -286,19 +293,24 @@ pub fn open_opts(directory: impl Into<std::path::PathBuf>, options: open::Option } /// +#[allow(clippy::empty_docs)] pub mod create; /// +#[allow(clippy::empty_docs)] pub mod open; /// +#[allow(clippy::empty_docs)] pub mod config; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "mailmap")] pub mod mailmap; /// +#[allow(clippy::empty_docs)] pub mod worktree; pub mod revision; @@ -307,22 +319,27 @@ pub mod revision; pub mod filter; /// +#[allow(clippy::empty_docs)] pub mod remote; /// +#[allow(clippy::empty_docs)] pub mod init; /// Not to be confused with 'status'. pub mod state; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "status")] pub mod status; /// +#[allow(clippy::empty_docs)] pub mod shallow; /// +#[allow(clippy::empty_docs)] pub mod discover; pub mod env; diff --git a/gix/src/mailmap.rs b/gix/src/mailmap.rs index fdf7ee948da..b7244549546 100644 --- a/gix/src/mailmap.rs +++ b/gix/src/mailmap.rs @@ -1,6 +1,7 @@ pub use gix_mailmap::*; /// +#[allow(clippy::empty_docs)] pub mod load { /// The error returned by [`crate::Repository::open_mailmap_into()`]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/object/blob.rs b/gix/src/object/blob.rs index 59629727f3a..8925685b018 100644 --- a/gix/src/object/blob.rs +++ b/gix/src/object/blob.rs @@ -20,6 +20,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod init { /// The error returned by [`Platform::from_tree_change()`][super::Platform::from_tree_change()]. pub type Error = gix_diff::blob::platform::set_resource::Error; @@ -115,6 +116,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod lines { use crate::bstr::BStr; diff --git a/gix/src/object/errors.rs b/gix/src/object/errors.rs index db81daacb23..4331c795d81 100644 --- a/gix/src/object/errors.rs +++ b/gix/src/object/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod conversion { /// The error returned by [`crate::object::try_to_()`][crate::Object::try_to_commit_ref()]. @@ -16,6 +17,7 @@ pub mod conversion { } /// +#[allow(clippy::empty_docs)] pub mod find { /// Indicate that an error occurred when trying to find an object. #[derive(Debug, thiserror::Error)] @@ -23,6 +25,7 @@ pub mod find { pub struct Error(#[from] pub gix_object::find::Error); /// + #[allow(clippy::empty_docs)] pub mod existing { /// An object could not be found in the database, or an error occurred when trying to obtain it. pub type Error = gix_object::find::existing::Error; @@ -30,6 +33,7 @@ pub mod find { } /// +#[allow(clippy::empty_docs)] pub mod write { /// An error to indicate writing to the loose object store failed. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/object/impls.rs b/gix/src/object/impls.rs index 5d2ad81604b..58e068e402d 100644 --- a/gix/src/object/impls.rs +++ b/gix/src/object/impls.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::{object, Blob, Commit, Object, ObjectDetached, Tag, Tree}; impl<'repo> From<Object<'repo>> for ObjectDetached { diff --git a/gix/src/object/mod.rs b/gix/src/object/mod.rs index cd047c4d3a4..c0815a19250 100644 --- a/gix/src/object/mod.rs +++ b/gix/src/object/mod.rs @@ -1,6 +1,5 @@ //! -use std::convert::TryInto; - +#![allow(clippy::empty_docs)] use gix_hash::ObjectId; pub use gix_object::Kind; @@ -12,16 +11,20 @@ pub(crate) mod cache { } pub use errors::{conversion, find, write}; /// +#[allow(clippy::empty_docs)] pub mod blob; /// +#[allow(clippy::empty_docs)] pub mod commit; mod impls; pub mod peel; mod tag; /// +#[allow(clippy::empty_docs)] pub mod tree; /// +#[allow(clippy::empty_docs)] pub mod try_into { #[derive(thiserror::Error, Debug)] #[allow(missing_docs)] diff --git a/gix/src/object/peel.rs b/gix/src/object/peel.rs index c906c0c7500..6ac77726927 100644 --- a/gix/src/object/peel.rs +++ b/gix/src/object/peel.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use crate::{ object, object::{peel, Kind}, @@ -6,6 +7,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod to_kind { mod error { diff --git a/gix/src/object/tree/diff/mod.rs b/gix/src/object/tree/diff/mod.rs index 85877561083..299075781fa 100644 --- a/gix/src/object/tree/diff/mod.rs +++ b/gix/src/object/tree/diff/mod.rs @@ -24,6 +24,7 @@ pub struct Change<'a, 'old, 'new> { } /// +#[allow(clippy::empty_docs)] pub mod change; /// Diffing @@ -86,4 +87,5 @@ impl<'a, 'repo> Platform<'a, 'repo> { } /// +#[allow(clippy::empty_docs)] pub mod for_each; diff --git a/gix/src/object/tree/mod.rs b/gix/src/object/tree/mod.rs index 0523477c9ff..0a1ed46d66d 100644 --- a/gix/src/object/tree/mod.rs +++ b/gix/src/object/tree/mod.rs @@ -174,9 +174,11 @@ impl<'repo> Tree<'repo> { pub mod diff; /// +#[allow(clippy::empty_docs)] pub mod traverse; /// +#[allow(clippy::empty_docs)] mod iter; pub use iter::EntryRef; diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 829e9dece45..7c5b065bd78 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -299,7 +299,7 @@ impl ThreadSafeRepository { } refs.write_reflog = config::cache::util::reflog_or_default(config.reflog, worktree_dir.is_some()); - refs.namespace = config.refs_namespace.clone(); + refs.namespace.clone_from(&config.refs_namespace); let replacements = replacement_objects_refs_prefix(&config.resolved, lenient_config, filter_config_section)? .and_then(|prefix| { let _span = gix_trace::detail!("find replacement objects"); diff --git a/gix/src/pathspec.rs b/gix/src/pathspec.rs index 1123b604c94..f501be621f8 100644 --- a/gix/src/pathspec.rs +++ b/gix/src/pathspec.rs @@ -5,6 +5,7 @@ pub use gix_pathspec::*; use crate::{bstr::BStr, AttributeStack, Pathspec, PathspecDetached, Repository}; /// +#[allow(clippy::empty_docs)] pub mod init { /// The error returned by [`Pathspec::new()`](super::Pathspec::new()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/reference/edits.rs b/gix/src/reference/edits.rs index cba652630ce..4d3d773ce68 100644 --- a/gix/src/reference/edits.rs +++ b/gix/src/reference/edits.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod set_target_id { use gix_macros::momo; use gix_ref::{transaction::PreviousValue, Target}; @@ -53,6 +54,7 @@ pub mod set_target_id { } /// +#[allow(clippy::empty_docs)] pub mod delete { use gix_ref::transaction::{Change, PreviousValue, RefEdit, RefLog}; diff --git a/gix/src/reference/errors.rs b/gix/src/reference/errors.rs index d5b09f78e0b..92714ec790c 100644 --- a/gix/src/reference/errors.rs +++ b/gix/src/reference/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod edit { use crate::config; @@ -21,6 +22,7 @@ pub mod edit { } /// +#[allow(clippy::empty_docs)] pub mod peel { /// The error returned by [`Reference::peel_to_id_in_place(…)`](crate::Reference::peel_to_id_in_place()) and /// [`Reference::into_fully_peeled_id(…)`](crate::Reference::into_fully_peeled_id()). @@ -35,6 +37,7 @@ pub mod peel { } /// +#[allow(clippy::empty_docs)] pub mod head_id { /// The error returned by [`Repository::head_id(…)`](crate::Repository::head_id()). #[derive(Debug, thiserror::Error)] @@ -48,6 +51,7 @@ pub mod head_id { } /// +#[allow(clippy::empty_docs)] pub mod head_commit { /// The error returned by [`Repository::head_commit`(…)](crate::Repository::head_commit()). #[derive(Debug, thiserror::Error)] @@ -61,6 +65,7 @@ pub mod head_commit { } /// +#[allow(clippy::empty_docs)] pub mod head_tree_id { /// The error returned by [`Repository::head_tree_id`(…)](crate::Repository::head_tree_id()). #[derive(Debug, thiserror::Error)] @@ -74,8 +79,10 @@ pub mod head_tree_id { } /// +#[allow(clippy::empty_docs)] pub mod find { /// + #[allow(clippy::empty_docs)] pub mod existing { /// The error returned by [`find_reference(…)`][crate::Repository::find_reference()], and others. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/reference/iter.rs b/gix/src/reference/iter.rs index 604a8ac4b05..995284a4824 100644 --- a/gix/src/reference/iter.rs +++ b/gix/src/reference/iter.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use std::path::Path; use gix_macros::momo; @@ -109,6 +110,7 @@ impl<'r> Iterator for Iter<'r> { } /// +#[allow(clippy::empty_docs)] pub mod init { /// The error returned by [`Platform::all()`][super::Platform::all()] or [`Platform::prefixed()`][super::Platform::prefixed()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/reference/log.rs b/gix/src/reference/log.rs index 2fea1782cc4..275eaf86850 100644 --- a/gix/src/reference/log.rs +++ b/gix/src/reference/log.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use gix_object::commit::MessageRef; use gix_ref::file::ReferenceExt; diff --git a/gix/src/reference/mod.rs b/gix/src/reference/mod.rs index ebdbf66a717..b145509cf92 100644 --- a/gix/src/reference/mod.rs +++ b/gix/src/reference/mod.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use gix_ref::file::ReferenceExt; @@ -6,6 +7,7 @@ use crate::{Id, Reference}; pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod remote; mod errors; diff --git a/gix/src/remote/build.rs b/gix/src/remote/build.rs index 452da66a006..16243900d9b 100644 --- a/gix/src/remote/build.rs +++ b/gix/src/remote/build.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{bstr::BStr, remote, Remote}; /// Builder methods diff --git a/gix/src/remote/connection/fetch/mod.rs b/gix/src/remote/connection/fetch/mod.rs index d4afd10234d..213a41bf7e4 100644 --- a/gix/src/remote/connection/fetch/mod.rs +++ b/gix/src/remote/connection/fetch/mod.rs @@ -90,6 +90,7 @@ pub mod outcome { } /// + #[allow(clippy::empty_docs)] pub mod negotiate { /// Key information about each round in the pack-negotiation. #[derive(Debug, Clone)] @@ -138,6 +139,7 @@ impl From<ProgressId> for gix_features::progress::Id { pub(crate) mod negotiate; /// +#[allow(clippy::empty_docs)] pub mod prepare { /// The error returned by [`prepare_fetch()`][super::Connection::prepare_fetch()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/remote/connection/fetch/update_refs/mod.rs b/gix/src/remote/connection/fetch/update_refs/mod.rs index c487e7f5c2c..06a74fb3cbc 100644 --- a/gix/src/remote/connection/fetch/update_refs/mod.rs +++ b/gix/src/remote/connection/fetch/update_refs/mod.rs @@ -1,5 +1,5 @@ #![allow(clippy::result_large_err)] -use std::{collections::BTreeMap, convert::TryInto, path::PathBuf}; +use std::{collections::BTreeMap, path::PathBuf}; use gix_object::Exists; use gix_ref::{ @@ -20,6 +20,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod update; /// Information about the update of a single reference, corresponding the respective entry in [`RefMap::mappings`][crate::remote::fetch::RefMap::mappings]. diff --git a/gix/src/remote/connection/fetch/update_refs/tests.rs b/gix/src/remote/connection/fetch/update_refs/tests.rs index a56e1218043..fbe5e2fcd17 100644 --- a/gix/src/remote/connection/fetch/update_refs/tests.rs +++ b/gix/src/remote/connection/fetch/update_refs/tests.rs @@ -8,8 +8,6 @@ fn hex_to_id(hex: &str) -> gix_hash::ObjectId { } mod update { - use std::convert::TryInto; - use gix_testtools::Result; use super::hex_to_id; diff --git a/gix/src/remote/connection/mod.rs b/gix/src/remote/connection/mod.rs index f9b8aa7e6d4..78c29344bed 100644 --- a/gix/src/remote/connection/mod.rs +++ b/gix/src/remote/connection/mod.rs @@ -23,7 +23,9 @@ pub struct Connection<'a, 'repo, T> { mod access; /// +#[allow(clippy::empty_docs)] pub mod ref_map; /// +#[allow(clippy::empty_docs)] pub mod fetch; diff --git a/gix/src/remote/errors.rs b/gix/src/remote/errors.rs index 34ed8246b24..37b211d28b2 100644 --- a/gix/src/remote/errors.rs +++ b/gix/src/remote/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod find { use crate::{bstr::BString, config, remote}; @@ -27,6 +28,7 @@ pub mod find { } /// + #[allow(clippy::empty_docs)] pub mod existing { use crate::bstr::BString; @@ -44,6 +46,7 @@ pub mod find { } /// + #[allow(clippy::empty_docs)] pub mod for_fetch { /// The error returned by [`Repository::find_fetch_remote(…)`](crate::Repository::find_fetch_remote()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/remote/fetch.rs b/gix/src/remote/fetch.rs index 4700201de51..ff483c893d7 100644 --- a/gix/src/remote/fetch.rs +++ b/gix/src/remote/fetch.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod negotiate { #[cfg(feature = "credentials")] pub use gix_negotiate::Algorithm; diff --git a/gix/src/remote/init.rs b/gix/src/remote/init.rs index 13b747eda7c..fd81604b257 100644 --- a/gix/src/remote/init.rs +++ b/gix/src/remote/init.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_refspec::RefSpec; use crate::{config, remote, Remote, Repository}; diff --git a/gix/src/remote/mod.rs b/gix/src/remote/mod.rs index 7937f8275ed..38b1ccf27ad 100644 --- a/gix/src/remote/mod.rs +++ b/gix/src/remote/mod.rs @@ -32,6 +32,7 @@ pub enum Name<'repo> { } /// +#[allow(clippy::empty_docs)] pub mod name; mod build; @@ -40,9 +41,11 @@ mod errors; pub use errors::find; /// +#[allow(clippy::empty_docs)] pub mod init; /// +#[allow(clippy::empty_docs)] pub mod fetch; /// @@ -55,8 +58,10 @@ mod connection; pub use connection::{ref_map, AuthenticateFn, Connection}; /// +#[allow(clippy::empty_docs)] pub mod save; mod access; /// +#[allow(clippy::empty_docs)] pub mod url; diff --git a/gix/src/remote/name.rs b/gix/src/remote/name.rs index 6c6afe745f1..f0081cc30df 100644 --- a/gix/src/remote/name.rs +++ b/gix/src/remote/name.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use super::Name; use crate::bstr::{BStr, BString, ByteSlice, ByteVec}; diff --git a/gix/src/remote/save.rs b/gix/src/remote/save.rs index 2a91dfa9c1d..63a2de90d71 100644 --- a/gix/src/remote/save.rs +++ b/gix/src/remote/save.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_macros::momo; use crate::{ diff --git a/gix/src/remote/url/scheme_permission.rs b/gix/src/remote/url/scheme_permission.rs index eed7be6185c..212742de2b6 100644 --- a/gix/src/remote/url/scheme_permission.rs +++ b/gix/src/remote/url/scheme_permission.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeMap, convert::TryFrom}; +use std::{borrow::Cow, collections::BTreeMap}; use crate::{ bstr::{BStr, BString, ByteSlice}, diff --git a/gix/src/repository/config/branch.rs b/gix/src/repository/config/branch.rs index 334db116786..81f6bc70e54 100644 --- a/gix/src/repository/config/branch.rs +++ b/gix/src/repository/config/branch.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeSet, convert::TryInto}; +use std::{borrow::Cow, collections::BTreeSet}; use gix_ref::{FullName, FullNameRef}; diff --git a/gix/src/repository/diff.rs b/gix/src/repository/diff.rs index 8ce53ec3410..87e28d0e9c5 100644 --- a/gix/src/repository/diff.rs +++ b/gix/src/repository/diff.rs @@ -1,6 +1,7 @@ use crate::Repository; /// +#[allow(clippy::empty_docs)] pub mod resource_cache { /// The error returned by [Repository::diff_resource_cache()](super::Repository::diff_resource_cache()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/repository/filter.rs b/gix/src/repository/filter.rs index 9c0cdcfa42f..d5dc5690ea2 100644 --- a/gix/src/repository/filter.rs +++ b/gix/src/repository/filter.rs @@ -1,6 +1,7 @@ use crate::{filter, worktree::IndexPersistedOrInMemory, Id, Repository}; /// +#[allow(clippy::empty_docs)] pub mod pipeline { /// The error returned by [Repository::filter_pipeline()](super::Repository::filter_pipeline()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index f635e8b2653..68b9b491c74 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] /// The kind of repository. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -73,6 +74,7 @@ mod thread_safe; mod worktree; /// +#[allow(clippy::empty_docs)] pub mod branch_remote_ref_name { /// The error returned by [Repository::branch_remote_ref_name()](crate::Repository::branch_remote_ref_name()). @@ -89,6 +91,7 @@ pub mod branch_remote_ref_name { } /// +#[allow(clippy::empty_docs)] pub mod branch_remote_tracking_ref_name { /// The error returned by [Repository::branch_remote_tracking_ref_name()](crate::Repository::branch_remote_tracking_ref_name()). diff --git a/gix/src/repository/object.rs b/gix/src/repository/object.rs index 77f188badf8..742a349e04f 100644 --- a/gix/src/repository/object.rs +++ b/gix/src/repository/object.rs @@ -1,5 +1,5 @@ #![allow(clippy::result_large_err)] -use std::{convert::TryInto, ops::DerefMut}; +use std::ops::DerefMut; use gix_hash::ObjectId; use gix_macros::momo; diff --git a/gix/src/repository/reference.rs b/gix/src/repository/reference.rs index b977c6ea872..9897f008012 100644 --- a/gix/src/repository/reference.rs +++ b/gix/src/repository/reference.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use gix_macros::momo; use gix_ref::{ diff --git a/gix/src/repository/remote.rs b/gix/src/repository/remote.rs index be0845178b9..76314ea6cf9 100644 --- a/gix/src/repository/remote.rs +++ b/gix/src/repository/remote.rs @@ -1,6 +1,4 @@ #![allow(clippy::result_large_err)] -use std::convert::TryInto; - use crate::{bstr::BStr, config, remote, remote::find, Remote}; impl crate::Repository { diff --git a/gix/src/revision/mod.rs b/gix/src/revision/mod.rs index 9bd23b994bd..0f45590bd9f 100644 --- a/gix/src/revision/mod.rs +++ b/gix/src/revision/mod.rs @@ -6,11 +6,13 @@ pub use gix_revision as plumbing; /// +#[allow(clippy::empty_docs)] pub mod walk; pub use walk::iter::Walk; /// #[cfg(feature = "revision")] +#[allow(clippy::empty_docs)] pub mod spec; /// The specification of a revision as parsed from a revision specification like `HEAD@{1}` or `v1.2.3...main`. diff --git a/gix/src/revision/spec/mod.rs b/gix/src/revision/spec/mod.rs index af58ecdff51..e7c9f244144 100644 --- a/gix/src/revision/spec/mod.rs +++ b/gix/src/revision/spec/mod.rs @@ -2,6 +2,7 @@ use crate::bstr::BStr; use crate::{ext::ReferenceExt, revision::Spec, Id, Reference}; /// +#[allow(clippy::empty_docs)] pub mod parse; mod impls { diff --git a/gix/src/revision/spec/parse/mod.rs b/gix/src/revision/spec/parse/mod.rs index e4584776363..d9c38276a91 100644 --- a/gix/src/revision/spec/parse/mod.rs +++ b/gix/src/revision/spec/parse/mod.rs @@ -11,6 +11,7 @@ use crate::bstr::BString; pub use types::{Error, ObjectKindHint, Options, RefsHint}; /// +#[allow(clippy::empty_docs)] pub mod single { use crate::bstr::BString; @@ -26,6 +27,7 @@ pub mod single { } /// +#[allow(clippy::empty_docs)] pub mod error; impl<'repo> Spec<'repo> { diff --git a/gix/src/shallow.rs b/gix/src/shallow.rs index d49653a6506..dfe376283dd 100644 --- a/gix/src/shallow.rs +++ b/gix/src/shallow.rs @@ -79,6 +79,7 @@ pub mod write { pub use write::function::write; /// +#[allow(clippy::empty_docs)] pub mod open { /// The error returned by [`Repository::shallow_commits()`][crate::Repository::shallow_commits()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index fb39a8c5eb9..7d7016c90c2 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -182,6 +182,7 @@ pub struct BuiltinSubmoduleStatus { } /// +#[allow(clippy::empty_docs)] mod submodule_status { use crate::bstr; use crate::bstr::BStr; @@ -307,6 +308,7 @@ pub struct Iter { } /// +#[allow(clippy::empty_docs)] pub mod iter { use crate::bstr::BString; use crate::config::cache::util::ApplyLeniencyDefault; diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs index 483964e298d..022275a01ed 100644 --- a/gix/src/status/mod.rs +++ b/gix/src/status/mod.rs @@ -117,6 +117,7 @@ impl Repository { } /// +#[allow(clippy::empty_docs)] pub mod is_dirty { use crate::Repository; @@ -164,4 +165,5 @@ pub mod is_dirty { mod platform; /// +#[allow(clippy::empty_docs)] pub mod index_worktree; diff --git a/gix/src/submodule/errors.rs b/gix/src/submodule/errors.rs index 4e41337de45..a27b02a7541 100644 --- a/gix/src/submodule/errors.rs +++ b/gix/src/submodule/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod open_modules_file { /// The error returned by [Repository::open_modules_file()](crate::Repository::open_modules_file()). #[derive(Debug, thiserror::Error)] @@ -12,6 +13,7 @@ pub mod open_modules_file { } /// +#[allow(clippy::empty_docs)] pub mod modules { /// The error returned by [Repository::modules()](crate::Repository::modules()). #[derive(Debug, thiserror::Error)] @@ -31,6 +33,7 @@ pub mod modules { } /// +#[allow(clippy::empty_docs)] pub mod is_active { /// The error returned by [Submodule::is_active()](crate::Submodule::is_active()). #[derive(Debug, thiserror::Error)] @@ -50,6 +53,7 @@ pub mod is_active { } /// +#[allow(clippy::empty_docs)] pub mod fetch_recurse { /// The error returned by [Submodule::fetch_recurse()](crate::Submodule::fetch_recurse()). #[derive(Debug, thiserror::Error)] @@ -63,6 +67,7 @@ pub mod fetch_recurse { } /// +#[allow(clippy::empty_docs)] pub mod open { /// The error returned by [Submodule::open()](crate::Submodule::open()). #[derive(Debug, thiserror::Error)] @@ -76,6 +81,7 @@ pub mod open { } /// +#[allow(clippy::empty_docs)] pub mod index_id { /// The error returned by [Submodule::index_id()](crate::Submodule::index_id()). #[derive(Debug, thiserror::Error)] @@ -89,6 +95,7 @@ pub mod index_id { } /// +#[allow(clippy::empty_docs)] pub mod head_id { /// The error returned by [Submodule::head_id()](crate::Submodule::head_id()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index 6e85494c522..db79776c9a6 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -276,6 +276,7 @@ impl<'repo> Submodule<'repo> { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "status")] pub mod status { use super::{head_id, index_id, open, Status}; diff --git a/gix/src/tag.rs b/gix/src/tag.rs index 84af3b43a7c..b22af2334cf 100644 --- a/gix/src/tag.rs +++ b/gix/src/tag.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] mod error { /// The error returned by [`tag(…)`][crate::Repository::tag()]. diff --git a/gix/src/worktree/mod.rs b/gix/src/worktree/mod.rs index 2a336b1e539..87be9ef32b4 100644 --- a/gix/src/worktree/mod.rs +++ b/gix/src/worktree/mod.rs @@ -94,6 +94,7 @@ pub(crate) fn id(git_dir: &std::path::Path, has_common_dir: bool) -> Option<&BSt } /// +#[allow(clippy::empty_docs)] pub mod proxy; /// diff --git a/gix/tests/reference/mod.rs b/gix/tests/reference/mod.rs index d168819eb64..7dd762f097e 100644 --- a/gix/tests/reference/mod.rs +++ b/gix/tests/reference/mod.rs @@ -18,8 +18,6 @@ mod log { } } mod find { - use std::convert::TryInto; - use gix_ref as refs; use gix_ref::{FullName, FullNameRef, Target}; diff --git a/gix/tests/remote/save.rs b/gix/tests/remote/save.rs index 44e5aca8316..69206763894 100644 --- a/gix/tests/remote/save.rs +++ b/gix/tests/remote/save.rs @@ -45,8 +45,6 @@ mod save_to { } mod save_as_to { - use std::convert::TryInto; - use crate::{basic_repo, remote::save::uniformize}; #[test] diff --git a/gix/tests/repository/config/remote.rs b/gix/tests/repository/config/remote.rs index 0bca87da755..ba1e5bdb373 100644 --- a/gix/tests/repository/config/remote.rs +++ b/gix/tests/repository/config/remote.rs @@ -1,6 +1,5 @@ use gix::bstr::BStr; use std::borrow::Cow; -use std::iter::FromIterator; use crate::remote; diff --git a/src/plumbing/options/free.rs b/src/plumbing/options/free.rs index 083dd943b34..8da1a6fffa3 100644 --- a/src/plumbing/options/free.rs +++ b/src/plumbing/options/free.rs @@ -19,6 +19,7 @@ pub enum Subcommands { } /// +#[allow(clippy::empty_docs)] pub mod commitgraph { use std::path::PathBuf; @@ -99,6 +100,7 @@ pub mod index { } /// +#[allow(clippy::empty_docs)] pub mod pack { use std::{ffi::OsString, path::PathBuf}; @@ -287,6 +289,7 @@ pub mod pack { } /// + #[allow(clippy::empty_docs)] pub mod multi_index { use std::path::PathBuf; @@ -321,6 +324,7 @@ pub mod pack { } /// + #[allow(clippy::empty_docs)] pub mod index { use std::path::PathBuf; @@ -458,6 +462,7 @@ pub mod pack { } /// +#[allow(clippy::empty_docs)] pub mod mailmap { use std::path::PathBuf; diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 2ea0fc440ae..14749704181 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -657,6 +657,7 @@ pub mod credential { } /// +#[allow(clippy::empty_docs)] pub mod commitgraph { #[derive(Debug, clap::Subcommand)] pub enum Subcommands { @@ -816,10 +817,10 @@ pub mod index { pub mod entries { #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] pub enum Format { - /// + /// Show only minimal information, useful for first glances. #[default] Simple, - /// Use the `.tar` file format, uncompressed. + /// Show much more information that is still human-readable. Rich, } } @@ -888,4 +889,5 @@ pub mod submodule { } /// +#[allow(clippy::empty_docs)] pub mod free; From 22abf605858404fcd38a5f4b8713358a526819ac Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Mon, 11 Mar 2024 16:02:08 +0100 Subject: [PATCH 19/26] add `status.showUntrackedFiles` to config-tree and use it in `status()` --- Cargo.lock | 1 + gix/Cargo.toml | 1 + gix/src/config/tree/mod.rs | 7 ++ gix/src/config/tree/sections/mod.rs | 7 ++ gix/src/config/tree/sections/status.rs | 58 +++++++++++ gix/src/status/mod.rs | 53 +++++++++- gix/src/status/platform.rs | 19 +++- gix/src/submodule/mod.rs | 3 +- gix/tests/config/tree.rs | 31 ++++++ .../make_status_repos.tar.xz | Bin 0 -> 10072 bytes .../generated-archives/make_submodules.tar.xz | Bin 28996 -> 28948 bytes gix/tests/fixtures/make_status_repos.sh | 16 +++ gix/tests/status/mod.rs | 95 ++++++++++++++++-- 13 files changed, 274 insertions(+), 17 deletions(-) create mode 100644 gix/src/config/tree/sections/status.rs create mode 100644 gix/tests/fixtures/generated-archives/make_status_repos.tar.xz create mode 100644 gix/tests/fixtures/make_status_repos.sh diff --git a/Cargo.lock b/Cargo.lock index 5dd0701ddd0..3d75fb71210 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1351,6 +1351,7 @@ dependencies = [ "is_ci", "once_cell", "parking_lot", + "pretty_assertions", "prodash 28.0.0", "regex", "reqwest", diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 583d800f87f..eaf2092b00a 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -323,6 +323,7 @@ parking_lot = "0.12.1" document-features = { version = "0.2.0", optional = true } [dev-dependencies] +pretty_assertions = "1.4.0" gix-testtools = { path = "../tests/tools" } is_ci = "1.1.1" anyhow = "1" diff --git a/gix/src/config/tree/mod.rs b/gix/src/config/tree/mod.rs index 6f85f803864..7340da3dd1b 100644 --- a/gix/src/config/tree/mod.rs +++ b/gix/src/config/tree/mod.rs @@ -59,6 +59,9 @@ pub(crate) mod root { pub const SAFE: sections::Safe = sections::Safe; /// The `ssh` section. pub const SSH: sections::Ssh = sections::Ssh; + /// The `status` section. + #[cfg(feature = "status")] + pub const STATUS: sections::Status = sections::Status; /// The `user` section. pub const USER: sections::User = sections::User; /// The `url` section. @@ -89,6 +92,8 @@ pub(crate) mod root { &Self::REMOTE, &Self::SAFE, &Self::SSH, + #[cfg(feature = "status")] + &Self::STATUS, &Self::USER, &Self::URL, ] @@ -104,6 +109,8 @@ pub use sections::{ }; #[cfg(feature = "blob-diff")] pub use sections::{diff, Diff}; +#[cfg(feature = "status")] +pub use sections::{status, Status}; /// Generic value implementations for static instantiation. pub mod keys; diff --git a/gix/src/config/tree/sections/mod.rs b/gix/src/config/tree/sections/mod.rs index de6b44d4c1d..ab2b9542a2c 100644 --- a/gix/src/config/tree/sections/mod.rs +++ b/gix/src/config/tree/sections/mod.rs @@ -106,6 +106,13 @@ mod safe; pub struct Ssh; pub mod ssh; +/// The `status` top-level section. +#[derive(Copy, Clone, Default)] +#[cfg(feature = "status")] +pub struct Status; +#[cfg(feature = "status")] +pub mod status; + /// The `user` top-level section. #[derive(Copy, Clone, Default)] pub struct User; diff --git a/gix/src/config/tree/sections/status.rs b/gix/src/config/tree/sections/status.rs new file mode 100644 index 00000000000..f60600e214b --- /dev/null +++ b/gix/src/config/tree/sections/status.rs @@ -0,0 +1,58 @@ +use crate::config; +use crate::config::tree::sections::Status; +use crate::config::tree::{keys, Key, Section}; + +impl Status { + /// The `status.showUntrackedFiles` key + pub const SHOW_UNTRACKED_FILES: ShowUntrackedFiles = ShowUntrackedFiles::new_with_validate( + "showUntrackedFiles", + &config::Tree::STATUS, + validate::ShowUntrackedFiles, + ); +} + +/// The `status.showUntrackedFiles` key. +pub type ShowUntrackedFiles = keys::Any<validate::ShowUntrackedFiles>; + +mod show_untracked_files { + use std::borrow::Cow; + + use crate::{bstr::BStr, config, config::tree::status::ShowUntrackedFiles, status}; + + impl ShowUntrackedFiles { + pub fn try_into_show_untracked_files( + &'static self, + value: Cow<'_, BStr>, + ) -> Result<status::UntrackedFiles, config::key::GenericErrorWithValue> { + use crate::bstr::ByteSlice; + Ok(match value.as_ref().as_bytes() { + b"no" => status::UntrackedFiles::None, + b"normal" => status::UntrackedFiles::Collapsed, + b"all" => status::UntrackedFiles::Files, + _ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())), + }) + } + } +} + +impl Section for Status { + fn name(&self) -> &str { + "status" + } + + fn keys(&self) -> &[&dyn Key] { + &[&Self::SHOW_UNTRACKED_FILES] + } +} + +mod validate { + use crate::{bstr::BStr, config::tree::keys}; + + pub struct ShowUntrackedFiles; + impl keys::Validate for ShowUntrackedFiles { + fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> { + super::Status::SHOW_UNTRACKED_FILES.try_into_show_untracked_files(value.into())?; + Ok(()) + } + } +} diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs index 022275a01ed..3a565f0ef13 100644 --- a/gix/src/status/mod.rs +++ b/gix/src/status/mod.rs @@ -1,3 +1,4 @@ +use crate::config::cache::util::ApplyLeniencyDefault; use crate::{config, Repository}; pub use gix_status as plumbing; use std::ops::Deref; @@ -71,12 +72,37 @@ pub enum Submodule { }, } +/// How untracked files should be handled. +#[derive(Default, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub enum UntrackedFiles { + /// Do not show any untracked files. + /// + /// This can mean no directory walk is performed. + None, + /// If possible, collapse files into their parent folders to reduce the amount of + /// emitted untracked files. + #[default] + Collapsed, + /// Show each individual untracked file or directory (if empty directories are emitted) that the dirwalk encountered . + Files, +} + impl Default for Submodule { fn default() -> Self { Submodule::AsConfigured { check_dirty: false } } } +/// The error returned by [status()](Repository::status). +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + DirwalkOptions(#[from] config::boolean::Error), + #[error(transparent)] + ConfigureUntrackedFiles(#[from] config::key::GenericErrorWithValue), +} + /// Status impl Repository { /// Obtain a platform for configuring iterators for traversing git repository status information. @@ -84,10 +110,14 @@ impl Repository { /// By default, this is set to the fastest and most immediate way of obtaining a status, /// which is most similar to /// - /// `git status --untracked=all --ignored=no --no-renames` + /// `git status --ignored=no` /// /// which implies that submodule information is provided by default. /// + /// Note that `status.showUntrackedFiles` is respected, which leads to untracked files being + /// collapsed by default. If that needs to be controlled, + /// [configure the directory walk explicitly](Platform::dirwalk_options) or more [implicitly](Platform::untracked_files). + /// /// Pass `progress` to receive progress information on file modifications on this repository. /// Use [`progress::Discard`](crate::progress::Discard) to discard all progress information. /// @@ -96,11 +126,11 @@ impl Repository { /// Whereas Git runs the index-modified check before the directory walk to set entries /// as up-to-date to (potentially) safe some disk-access, we run both in parallel which /// ultimately is much faster. - pub fn status<P>(&self, progress: P) -> Result<Platform<'_, P>, config::boolean::Error> + pub fn status<P>(&self, progress: P) -> Result<Platform<'_, P>, Error> where P: gix_features::progress::Progress + 'static, { - Ok(Platform { + let platform = Platform { repo: self, progress, index: None, @@ -112,7 +142,20 @@ impl Repository { rewrites: None, thread_limit: None, }, - }) + }; + + let untracked = self + .config + .resolved + .string("status", None, "showUntrackedFiles") + .map(|value| { + config::tree::Status::SHOW_UNTRACKED_FILES + .try_into_show_untracked_files(value) + .with_lenient_default(self.config.lenient_config) + }) + .transpose()? + .unwrap_or_default(); + Ok(platform.untracked_files(untracked)) } } @@ -126,7 +169,7 @@ pub mod is_dirty { #[allow(missing_docs)] pub enum Error { #[error(transparent)] - StatusPlatform(#[from] crate::config::boolean::Error), + StatusPlatform(#[from] crate::status::Error), #[error(transparent)] CreateStatusIterator(#[from] crate::status::index_worktree::iter::Error), } diff --git a/gix/src/status/platform.rs b/gix/src/status/platform.rs index 58f81765ff6..10f8844107f 100644 --- a/gix/src/status/platform.rs +++ b/gix/src/status/platform.rs @@ -1,4 +1,4 @@ -use crate::status::{index_worktree, OwnedOrStaticAtomic, Platform, Submodule}; +use crate::status::{index_worktree, OwnedOrStaticAtomic, Platform, Submodule, UntrackedFiles}; use std::sync::atomic::AtomicBool; /// Builder @@ -17,6 +17,23 @@ where self } + /// A simple way to explicitly set the desired way of listing `untracked_files`, overriding any value + /// set by the git configuration. + /// + /// Note that if [`None`](UntrackedFiles::None) is used, the directory walk will be disabled entirely + /// after this call. Further, if no dirwalk options are present anymore, this call has no effect. + pub fn untracked_files(mut self, untracked_files: UntrackedFiles) -> Self { + let mode = match untracked_files { + UntrackedFiles::None => { + self.index_worktree_options.dirwalk_options.take(); + return self; + } + UntrackedFiles::Collapsed => gix_dir::walk::EmissionMode::CollapseDirectory, + UntrackedFiles::Files => gix_dir::walk::EmissionMode::Matching, + }; + self.dirwalk_options(|cb| cb.emit_untracked(mode)) + } + /// Set the interrupt flag to `should_interrupt`, which typically is an application-wide flag /// that is ultimately controlled by user interrupts. /// diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index db79776c9a6..a2668a2bd39 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -298,7 +298,7 @@ pub mod status { #[error(transparent)] IgnoreConfiguration(#[from] config::Error), #[error(transparent)] - StatusPlatform(#[from] crate::config::boolean::Error), + StatusPlatform(#[from] crate::status::Error), #[error(transparent)] Status(#[from] crate::status::index_worktree::iter::Error), #[error(transparent)] @@ -384,7 +384,6 @@ pub mod status { let statusses = adjust_options(sm_repo.status(gix_features::progress::Discard)?) .index_worktree_options_mut(|opts| { - assert!(opts.dirwalk_options.is_some(), "BUG: it's supposed to be the default"); if ignore == config::Ignore::Untracked { opts.dirwalk_options = None; } diff --git a/gix/tests/config/tree.rs b/gix/tests/config/tree.rs index e8ab78d3643..8305fb20698 100644 --- a/gix/tests/config/tree.rs +++ b/gix/tests/config/tree.rs @@ -145,6 +145,37 @@ mod ssh { } } +#[cfg(feature = "status")] +mod status { + use crate::config::tree::bcow; + use gix::config::tree::Status; + use gix::status::UntrackedFiles; + + #[test] + fn default() -> crate::Result { + for (actual, expected) in [ + ("no", UntrackedFiles::None), + ("normal", UntrackedFiles::Collapsed), + ("all", UntrackedFiles::Files), + ] { + assert_eq!( + Status::SHOW_UNTRACKED_FILES.try_into_show_untracked_files(bcow(actual))?, + expected + ); + } + + assert_eq!( + Status::SHOW_UNTRACKED_FILES + .try_into_show_untracked_files(bcow("NO")) + .unwrap_err() + .to_string(), + "The key \"status.showUntrackedFiles=NO\" was invalid", + "case-sensitive comparisons" + ); + Ok(()) + } +} + mod push { use crate::config::tree::bcow; use gix::config::tree::Push; diff --git a/gix/tests/fixtures/generated-archives/make_status_repos.tar.xz b/gix/tests/fixtures/generated-archives/make_status_repos.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..102ad7dcc72eeea3b328614543ec91b58f2aeb4b GIT binary patch literal 10072 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 literal 0 HcmV?d00001 diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index 6498ecb2945fe775d2d9eb5410471e574844d3ab..e4c34d017c44a5f8d930c47362fccc089889e5c3 100644 GIT binary patch delta 28898 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 BN_7AL delta 28947 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 diff --git a/gix/tests/fixtures/make_status_repos.sh b/gix/tests/fixtures/make_status_repos.sh new file mode 100644 index 00000000000..68ff4a72669 --- /dev/null +++ b/gix/tests/fixtures/make_status_repos.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -eu -o pipefail + +git init -q untracked-only +(cd untracked-only + touch this + mkdir subdir + >subdir/that + + git add . + git commit -q -m init + + mkdir new + touch new/untracked subdir/untracked +) + diff --git a/gix/tests/status/mod.rs b/gix/tests/status/mod.rs index 7d3d7400341..51e1da8094c 100644 --- a/gix/tests/status/mod.rs +++ b/gix/tests/status/mod.rs @@ -1,4 +1,4 @@ -pub fn repo(name: &str) -> crate::Result<gix::Repository> { +pub fn submodule_repo(name: &str) -> crate::Result<gix::Repository> { use crate::util::named_subrepo_opts; Ok(named_subrepo_opts( "make_submodules.sh", @@ -7,9 +7,20 @@ pub fn repo(name: &str) -> crate::Result<gix::Repository> { )?) } +pub fn repo(name: &str) -> crate::Result<gix::Repository> { + use crate::util::named_subrepo_opts; + Ok(named_subrepo_opts( + "make_status_repos.sh", + name, + gix::open::Options::isolated(), + )?) +} + mod index_worktree { mod iter { - use crate::status::repo; + use crate::status::{repo, submodule_repo}; + use gix::status::index_worktree::iter::Item; + use pretty_assertions::assert_eq; #[test] fn item_size() { @@ -22,7 +33,7 @@ mod index_worktree { #[test] fn submodule_modification() -> crate::Result { - let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?; let mut status = repo .status(gix::progress::Discard)? .index_worktree_options_mut(|opts| { @@ -35,9 +46,75 @@ mod index_worktree { Ok(()) } + #[test] + fn untracked_files_collapse_by_default() -> crate::Result { + let repo = repo("untracked-only")?; + let status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())?; + let items: Vec<_> = status.filter_map(Result::ok).collect(); + assert_eq!( + items, + [ + Item::DirectoryContents { + entry: gix_dir::Entry { + rela_path: "new".into(), + status: gix_dir::entry::Status::Untracked, + property: None, + disk_kind: Some(gix_dir::entry::Kind::Directory), + index_kind: None, + pathspec_match: Some(gix_dir::entry::PathspecMatch::Always), + }, + collapsed_directory_status: None + }, + Item::DirectoryContents { + entry: gix_dir::Entry { + rela_path: "subdir/untracked".into(), + status: gix_dir::entry::Status::Untracked, + property: None, + disk_kind: Some(gix_dir::entry::Kind::File), + index_kind: None, + pathspec_match: Some(gix_dir::entry::PathspecMatch::Always), + }, + collapsed_directory_status: None + } + ], + "'new/untracked' gets collapsed, but the second untracked is in a folder with a tracked file.\ + This collapsing behaviour is the default." + ); + Ok(()) + } + + #[test] + fn untracked_files_settings_none() -> crate::Result { + let mut repo = repo("untracked-only")?; + repo.config_snapshot_mut() + .set_value(&gix::config::tree::Status::SHOW_UNTRACKED_FILES, "no")?; + + let mut status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())?; + let items: Vec<_> = status.by_ref().filter_map(Result::ok).collect(); + assert_eq!(items, [], "no untracked files are found…"); + assert_eq!( + status.outcome_mut().expect("iteration done").index_worktree.dirwalk, + None, + "…as there was no directory walk" + ); + Ok(()) + } + #[test] fn early_drop_for_is_dirty_emulation() -> crate::Result { - let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?; let is_dirty = repo .status(gix::progress::Discard)? .index_worktree_submodules(gix::status::Submodule::AsConfigured { check_dirty: true }) @@ -55,25 +132,25 @@ mod index_worktree { } mod is_dirty { - use crate::status::repo; + use crate::status::submodule_repo; #[test] fn various_changes_positive() -> crate::Result { - let repo = repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?; assert!(repo.is_dirty()?, "The repository has various changes"); Ok(()) } #[test] fn submodule_changes_are_picked_up() -> crate::Result { - let repo = repo("submodule-head-changed")?; + let repo = submodule_repo("submodule-head-changed")?; assert!(repo.is_dirty()?, "head-changes are also discoverd"); Ok(()) } #[test] fn untracked_files_are_excluded() -> crate::Result { - let repo = repo("module1")?; + let repo = submodule_repo("module1")?; assert_eq!( repo.status(gix::progress::Discard)? .into_index_worktree_iter(Vec::new())? @@ -90,7 +167,7 @@ mod is_dirty { #[test] fn no_changes() -> crate::Result { - let repo = repo("with-submodules")?; + let repo = submodule_repo("with-submodules")?; assert!(!repo.is_dirty()?, "there are no changes"); Ok(()) } From 66e87cd31c060c3f97ac685ee0541c408f600362 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Tue, 12 Mar 2024 11:23:55 +0100 Subject: [PATCH 20/26] feat: add `gix status --index-worktree-renames` This enables rename-tracking between worktree and index, something that Git also doesn't do or doesn't do by default. It is, however, available in `git2`. --- gitoxide-core/src/repository/status.rs | 42 +++++++++++++++++-- gix/src/dirwalk.rs | 53 ++++++++++++++++++++++++ gix/src/status/index_worktree.rs | 15 ++++++- gix/src/status/platform.rs | 8 +++- src/plumbing/main.rs | 2 + src/plumbing/options/mod.rs | 5 ++- src/shared.rs | 56 +++++++++++++++++++++++++- 7 files changed, 174 insertions(+), 7 deletions(-) diff --git a/gitoxide-core/src/repository/status.rs b/gitoxide-core/src/repository/status.rs index fe7ac0174a7..33a00166756 100644 --- a/gitoxide-core/src/repository/status.rs +++ b/gitoxide-core/src/repository/status.rs @@ -23,6 +23,7 @@ pub struct Options { pub thread_limit: Option<usize>, pub statistics: bool, pub allow_write: bool, + pub index_worktree_renames: Option<f32>, } pub fn show( @@ -37,6 +38,7 @@ pub fn show( thread_limit, allow_write, statistics, + index_worktree_renames, }: Options, ) -> anyhow::Result<()> { if format != OutputFormat::Human { @@ -50,6 +52,16 @@ pub fn show( .status(index_progress)? .should_interrupt_shared(&gix::interrupt::IS_INTERRUPTED) .index_worktree_options_mut(|opts| { + opts.rewrites = index_worktree_renames.map(|percentage| gix::diff::Rewrites { + copies: None, + percentage: Some(percentage), + limit: 0, + }); + if opts.rewrites.is_some() { + if let Some(opts) = opts.dirwalk_options.as_mut() { + opts.set_emit_untracked(gix::dir::walk::EmissionMode::Matching); + } + } opts.thread_limit = thread_limit; opts.sorting = Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); }) @@ -85,14 +97,38 @@ pub fn show( if collapsed_directory_status.is_none() { writeln!( out, - "{status: >3} {rela_path}", + "{status: >3} {rela_path}{slash}", status = "?", rela_path = - gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display() + gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display(), + slash = if entry.disk_kind.unwrap_or(gix::dir::entry::Kind::File).is_dir() { + "/" + } else { + "" + } )?; } } - Item::Rewrite { .. } => {} + Item::Rewrite { + source, + dirwalk_entry, + copy: _, // TODO: how to visualize copies? + .. + } => { + // TODO: handle multi-status characters, there can also be modifications at the same time as determined by their ID and potentially diffstats. + writeln!( + out, + "{status: >3} {source_rela_path} → {dest_rela_path}", + status = "R", + source_rela_path = + gix::path::relativize_with_prefix(&gix::path::from_bstr(source.rela_path()), prefix).display(), + dest_rela_path = gix::path::relativize_with_prefix( + &gix::path::from_bstr(dirwalk_entry.rela_path.as_ref()), + prefix + ) + .display(), + )?; + } } } if gix::interrupt::is_triggered() { diff --git a/gix/src/dirwalk.rs b/gix/src/dirwalk.rs index 59357662c63..ab159cff2f5 100644 --- a/gix/src/dirwalk.rs +++ b/gix/src/dirwalk.rs @@ -68,23 +68,43 @@ impl Options { self.empty_patterns_match_prefix = toggle; self } + /// Like [`empty_patterns_match_prefix()`](Self::empty_patterns_match_prefix), but only requires a mutably borrowed instance. + pub fn set_empty_patterns_match_prefix(&mut self, toggle: bool) -> &mut Self { + self.empty_patterns_match_prefix = toggle; + self + } /// If `toggle` is `true`, we will stop figuring out if any directory that is a candidate for recursion is also a nested repository, /// which saves time but leads to recurse into it. If `false`, nested repositories will not be traversed. pub fn recurse_repositories(mut self, toggle: bool) -> Self { self.recurse_repositories = toggle; self } + /// Like [`recurse_repositories()`](Self::recurse_repositories), but only requires a mutably borrowed instance. + pub fn set_recurse_repositories(&mut self, toggle: bool) -> &mut Self { + self.recurse_repositories = toggle; + self + } /// If `toggle` is `true`, entries that are pruned and whose [Kind](gix_dir::entry::Kind) is known will be emitted. pub fn emit_pruned(mut self, toggle: bool) -> Self { self.emit_pruned = toggle; self } + /// Like [`emit_pruned()`](Self::emit_pruned), but only requires a mutably borrowed instance. + pub fn set_emit_pruned(&mut self, toggle: bool) -> &mut Self { + self.emit_pruned = toggle; + self + } /// If `value` is `Some(mode)`, entries that are ignored will be emitted according to the given `mode`. /// If `None`, ignored entries will not be emitted at all. pub fn emit_ignored(mut self, value: Option<EmissionMode>) -> Self { self.emit_ignored = value; self } + /// Like [`emit_ignored()`](Self::emit_ignored), but only requires a mutably borrowed instance. + pub fn set_emit_ignored(&mut self, value: Option<EmissionMode>) -> &mut Self { + self.emit_ignored = value; + self + } /// When the walk is for deletion, `value` must be `Some(_)` to assure we don't collapse directories that have precious files in /// them, and otherwise assure that no entries are observable that shouldn't be deleted. /// If `None`, precious files are treated like expendable files, which is usually what you want when displaying them @@ -93,17 +113,32 @@ impl Options { self.for_deletion = value; self } + /// Like [`for_deletion()`](Self::for_deletion), but only requires a mutably borrowed instance. + pub fn set_for_deletion(&mut self, value: Option<ForDeletionMode>) -> &mut Self { + self.for_deletion = value; + self + } /// If `toggle` is `true`, we will also emit entries for tracked items. Otherwise these will remain 'hidden', /// even if a pathspec directly refers to it. pub fn emit_tracked(mut self, toggle: bool) -> Self { self.emit_tracked = toggle; self } + /// Like [`emit_tracked()`](Self::emit_tracked), but only requires a mutably borrowed instance. + pub fn set_emit_tracked(&mut self, toggle: bool) -> &mut Self { + self.emit_tracked = toggle; + self + } /// Controls the way untracked files are emitted. By default, this is happening immediately and without any simplification. pub fn emit_untracked(mut self, toggle: EmissionMode) -> Self { self.emit_untracked = toggle; self } + /// Like [`emit_untracked()`](Self::emit_untracked), but only requires a mutably borrowed instance. + pub fn set_emit_untracked(&mut self, toggle: EmissionMode) -> &mut Self { + self.emit_untracked = toggle; + self + } /// If `toggle` is `true`, emit empty directories as well. Note that a directory also counts as empty if it has any /// amount or depth of nested subdirectories, as long as none of them includes a file. /// Thus, this makes leaf-level empty directories visible, as those don't have any content. @@ -112,6 +147,12 @@ impl Options { self } + /// Like [`emit_empty_directories()`](Self::emit_empty_directories), but only requires a mutably borrowed instance. + pub fn set_emit_empty_directories(&mut self, toggle: bool) -> &mut Self { + self.emit_empty_directories = toggle; + self + } + /// If `toggle` is `true`, we will not only find non-bare repositories in untracked directories, but also bare ones. /// /// Note that this is very costly, but without it, bare repositories will appear like untracked directories when collapsed, @@ -121,10 +162,22 @@ impl Options { self } + /// Like [`classify_untracked_bare_repositories()`](Self::classify_untracked_bare_repositories), but only requires a mutably borrowed instance. + pub fn set_classify_untracked_bare_repositories(&mut self, toggle: bool) -> &mut Self { + self.classify_untracked_bare_repositories = toggle; + self + } + /// Control whether entries that are in an about-to-be collapsed directory will be emitted. The default is `None`, /// so entries in a collapsed directory are not observable. pub fn emit_collapsed(mut self, value: Option<CollapsedEntriesEmissionMode>) -> Self { self.emit_collapsed = value; self } + + /// Like [`emit_collapsed()`](Self::emit_collapsed), but only requires a mutably borrowed instance. + pub fn set_emit_collapsed(&mut self, value: Option<CollapsedEntriesEmissionMode>) -> &mut Self { + self.emit_collapsed = value; + self + } } diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index 7d7016c90c2..35342e3f575 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -310,7 +310,7 @@ pub struct Iter { /// #[allow(clippy::empty_docs)] pub mod iter { - use crate::bstr::BString; + use crate::bstr::{BStr, BString}; use crate::config::cache::util::ApplyLeniencyDefault; use crate::status::index_worktree::{iter, BuiltinSubmoduleStatus}; use crate::status::{index_worktree, Platform}; @@ -406,6 +406,19 @@ pub mod iter { }, } + /// Access + impl RewriteSource { + /// The repository-relative path of this source. + pub fn rela_path(&self) -> &BStr { + match self { + RewriteSource::RewriteFromIndex { source_rela_path, .. } => source_rela_path.as_ref(), + RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, .. + } => source_dirwalk_entry.rela_path.as_ref(), + } + } + } + impl<'index> From<gix_status::index_as_worktree_with_renames::RewriteSource<'index, (), SubmoduleStatus>> for RewriteSource { diff --git a/gix/src/status/platform.rs b/gix/src/status/platform.rs index 10f8844107f..318db07435b 100644 --- a/gix/src/status/platform.rs +++ b/gix/src/status/platform.rs @@ -16,7 +16,13 @@ where } self } - + /// Like [dirwalk_options()](Self::dirwalk_options), but taking a mutable instance instead. + pub fn dirwalk_options_mut(&mut self, cb: impl FnOnce(&mut crate::dirwalk::Options)) -> &mut Self { + if let Some(opts) = self.index_worktree_options.dirwalk_options.as_mut() { + cb(opts); + } + self + } /// A simple way to explicitly set the desired way of listing `untracked_files`, overriding any value /// set by the git configuration. /// diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index 678306f1073..40840d545ea 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -210,6 +210,7 @@ pub fn main() -> Result<()> { submodules, no_write, pathspec, + index_worktree_renames, }) => prepare_and_run( "status", trace, @@ -230,6 +231,7 @@ pub fn main() -> Result<()> { statistics, thread_limit: thread_limit.or(cfg!(target_os = "macos").then_some(3)), // TODO: make this a configurable when in `gix`, this seems to be optimal on MacOS, linux scales though! MacOS also scales if reading a lot of files for refresh index allow_write: !no_write, + index_worktree_renames: index_worktree_renames.map(|percentage| percentage.unwrap_or(0.5)), submodules: submodules.map(|submodules| match submodules { Submodules::All => core::repository::status::Submodules::All, Submodules::RefChange => core::repository::status::Submodules::RefChange, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 14749704181..65e62779482 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -201,7 +201,7 @@ pub mod archive { } pub mod status { - use gitoxide::shared::CheckPathSpec; + use gitoxide::shared::{CheckPathSpec, ParseRenameFraction}; use gix::bstr::BString; #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] @@ -229,6 +229,9 @@ pub mod status { /// Don't write back a changed index, which forces this operation to always be idempotent. #[clap(long)] pub no_write: bool, + /// Enable rename tracking between the index and the working tree, preventing the collapse of folders as well. + #[clap(long, value_parser = ParseRenameFraction)] + pub index_worktree_renames: Option<Option<f32>>, /// The git path specifications to list attributes for, or unset to read from stdin one per line. #[clap(value_parser = CheckPathSpec)] pub pathspec: Vec<BString>, diff --git a/src/shared.rs b/src/shared.rs index b3d159c853b..8a51aa75993 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -361,6 +361,28 @@ mod clap { } } + #[derive(Clone)] + pub struct ParseRenameFraction; + + impl TypedValueParser for ParseRenameFraction { + type Value = f32; + + fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result<Self::Value, Error> { + StringValueParser::new() + .try_map(|arg: String| -> Result<_, Box<dyn std::error::Error + Send + Sync>> { + if arg.ends_with('%') { + let val = u32::from_str(&arg[..arg.len() - 1])?; + Ok(val as f32 / 100.0) + } else { + let val = u32::from_str(&arg)?; + let num = format!("0.{val}"); + Ok(f32::from_str(&num)?) + } + }) + .parse_ref(cmd, arg, value) + } + } + #[derive(Clone)] pub struct AsTime; @@ -387,4 +409,36 @@ mod clap { } } } -pub use self::clap::{AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsTime, CheckPathSpec}; +pub use self::clap::{ + AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsTime, CheckPathSpec, ParseRenameFraction, +}; + +#[cfg(test)] +mod value_parser_tests { + use super::ParseRenameFraction; + use clap::Parser; + + #[test] + fn rename_fraction() { + #[derive(Debug, clap::Parser)] + pub struct Cmd { + #[clap(long, short='a', value_parser = ParseRenameFraction)] + pub arg: Option<Option<f32>>, + } + + let c = Cmd::parse_from(["cmd", "-a"]); + assert_eq!(c.arg, Some(None), "this means we need to fill in the default"); + + let c = Cmd::parse_from(["cmd", "-a=50%"]); + assert_eq!(c.arg, Some(Some(0.5)), "percentages become a fraction"); + + let c = Cmd::parse_from(["cmd", "-a=100%"]); + assert_eq!(c.arg, Some(Some(1.0))); + + let c = Cmd::parse_from(["cmd", "-a=5"]); + assert_eq!(c.arg, Some(Some(0.5)), "another way to specify fractions"); + + let c = Cmd::parse_from(["cmd", "-a=75"]); + assert_eq!(c.arg, Some(Some(0.75))); + } +} From 84c74ffa698d35f8395c63db6acd3d0e6700d07f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Tue, 12 Mar 2024 17:24:27 +0100 Subject: [PATCH 21/26] feat: add `gix status --ignored` support --- gitoxide-core/src/repository/status.rs | 17 +++++++++++++++++ src/plumbing/main.rs | 9 +++++++++ src/plumbing/options/mod.rs | 17 +++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/gitoxide-core/src/repository/status.rs b/gitoxide-core/src/repository/status.rs index 33a00166756..ddb86266f59 100644 --- a/gitoxide-core/src/repository/status.rs +++ b/gitoxide-core/src/repository/status.rs @@ -17,7 +17,14 @@ pub enum Submodules { None, } +#[derive(Copy, Clone)] +pub enum Ignored { + Collapsed, + Matching, +} + pub struct Options { + pub ignored: Option<Ignored>, pub format: OutputFormat, pub submodules: Option<Submodules>, pub thread_limit: Option<usize>, @@ -33,6 +40,7 @@ pub fn show( mut err: impl std::io::Write, mut progress: impl gix::NestedProgress + 'static, Options { + ignored, format, submodules, thread_limit, @@ -52,6 +60,12 @@ pub fn show( .status(index_progress)? .should_interrupt_shared(&gix::interrupt::IS_INTERRUPTED) .index_worktree_options_mut(|opts| { + if let Some((opts, ignored)) = opts.dirwalk_options.as_mut().zip(ignored) { + opts.set_emit_ignored(Some(match ignored { + Ignored::Collapsed => gix::dir::walk::EmissionMode::CollapseDirectory, + Ignored::Matching => gix::dir::walk::EmissionMode::Matching, + })); + } opts.rewrites = index_worktree_renames.map(|percentage| gix::diff::Rewrites { copies: None, percentage: Some(percentage), @@ -60,6 +74,9 @@ pub fn show( if opts.rewrites.is_some() { if let Some(opts) = opts.dirwalk_options.as_mut() { opts.set_emit_untracked(gix::dir::walk::EmissionMode::Matching); + if ignored.is_some() { + opts.set_emit_ignored(Some(gix::dir::walk::EmissionMode::Matching)); + } } } opts.thread_limit = thread_limit; diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index 40840d545ea..fc35426c76a 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -206,6 +206,7 @@ pub fn main() -> Result<()> { }, ), Subcommands::Status(crate::plumbing::options::status::Platform { + ignored, statistics, submodules, no_write, @@ -227,6 +228,14 @@ pub fn main() -> Result<()> { err, progress, core::repository::status::Options { + ignored: ignored.map(|ignored| match ignored.unwrap_or_default() { + crate::plumbing::options::status::Ignored::Matching => { + core::repository::status::Ignored::Matching + } + crate::plumbing::options::status::Ignored::Collapsed => { + core::repository::status::Ignored::Collapsed + } + }), format, statistics, thread_limit: thread_limit.or(cfg!(target_os = "macos").then_some(3)), // TODO: make this a configurable when in `gix`, this seems to be optimal on MacOS, linux scales though! MacOS also scales if reading a lot of files for refresh index diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 65e62779482..4bc58a0f335 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -217,9 +217,26 @@ pub mod status { None, } + #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] + pub enum Ignored { + /// display all ignored files and directories, but collapse them if possible to simplify. + #[default] + Collapsed, + /// Show exact matches. Note that this may show directories if these are a match as well. + /// + /// Simplification will not happen in this mode. + Matching, + // TODO: figure out how to implement traditional, which right now can't be done as it requires ignored folders + // to be fully expanded. This should probably be implemented in `gix_dir` which then simply works by not + // allowing to ignore directories, naturally traversing the entire content. + } + #[derive(Debug, clap::Parser)] #[command(about = "compute repository status similar to `git status`")] pub struct Platform { + /// If enabled, show ignored files and directories. + #[clap(long)] + pub ignored: Option<Option<Ignored>>, /// Define how to display the submodule status. Defaults to git configuration if unset. #[clap(long)] pub submodules: Option<Submodules>, From 23bea36f046a6f652cd1e06885ae132c85bb4f05 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Wed, 13 Mar 2024 10:20:47 +0100 Subject: [PATCH 22/26] add `gix status --format` to communicate the current format is very simple. --- gitoxide-core/src/repository/status.rs | 16 ++++++++++++++-- src/plumbing/main.rs | 11 ++++++++++- src/plumbing/options/mod.rs | 12 ++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/gitoxide-core/src/repository/status.rs b/gitoxide-core/src/repository/status.rs index ddb86266f59..d3180f9c251 100644 --- a/gitoxide-core/src/repository/status.rs +++ b/gitoxide-core/src/repository/status.rs @@ -23,9 +23,16 @@ pub enum Ignored { Matching, } +#[derive(Copy, Clone)] +pub enum Format { + Simplified, + PorcelainV2, +} + pub struct Options { pub ignored: Option<Ignored>, - pub format: OutputFormat, + pub format: Format, + pub output_format: OutputFormat, pub submodules: Option<Submodules>, pub thread_limit: Option<usize>, pub statistics: bool, @@ -42,6 +49,7 @@ pub fn show( Options { ignored, format, + output_format, submodules, thread_limit, allow_write, @@ -49,9 +57,12 @@ pub fn show( index_worktree_renames, }: Options, ) -> anyhow::Result<()> { - if format != OutputFormat::Human { + if output_format != OutputFormat::Human { bail!("Only human format is supported right now"); } + if !matches!(format, Format::Simplified) { + bail!("Only the simplified format is currently implemented"); + } let start = std::time::Instant::now(); let prefix = repo.prefix()?.unwrap_or(Path::new("")); @@ -98,6 +109,7 @@ pub fn show( None => gix::status::Submodule::AsConfigured { check_dirty: false }, }) .into_index_worktree_iter(pathspecs)?; + for item in iter.by_ref() { let item = item?; match item { diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index fc35426c76a..0f5a146d0ed 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -207,6 +207,7 @@ pub fn main() -> Result<()> { ), Subcommands::Status(crate::plumbing::options::status::Platform { ignored, + format: status_format, statistics, submodules, no_write, @@ -228,6 +229,14 @@ pub fn main() -> Result<()> { err, progress, core::repository::status::Options { + format: match status_format.unwrap_or_default() { + crate::plumbing::options::status::Format::Simplified => { + core::repository::status::Format::Simplified + } + crate::plumbing::options::status::Format::PorcelainV2 => { + core::repository::status::Format::PorcelainV2 + } + }, ignored: ignored.map(|ignored| match ignored.unwrap_or_default() { crate::plumbing::options::status::Ignored::Matching => { core::repository::status::Ignored::Matching @@ -236,7 +245,7 @@ pub fn main() -> Result<()> { core::repository::status::Ignored::Collapsed } }), - format, + output_format: format, statistics, thread_limit: thread_limit.or(cfg!(target_os = "macos").then_some(3)), // TODO: make this a configurable when in `gix`, this seems to be optimal on MacOS, linux scales though! MacOS also scales if reading a lot of files for refresh index allow_write: !no_write, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 4bc58a0f335..b5fa3649a68 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -231,9 +231,21 @@ pub mod status { // allowing to ignore directories, naturally traversing the entire content. } + #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] + pub enum Format { + /// A basic format that is easy to read, and useful for a first glimpse as flat list. + #[default] + Simplified, + /// Output very similar to `git status --porcelain=2`. + PorcelainV2, + } + #[derive(Debug, clap::Parser)] #[command(about = "compute repository status similar to `git status`")] pub struct Platform { + /// The way status data is displayed. + #[clap(long, short = 'f')] + pub format: Option<Format>, /// If enabled, show ignored files and directories. #[clap(long)] pub ignored: Option<Option<Ignored>>, From 917634fa694a1e91d37f6407e57ae96b3b0aec4b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Wed, 13 Mar 2024 11:01:47 +0100 Subject: [PATCH 23/26] Keep lower-bound of `thiserror` low in `gix-dir` This increases the compatibility when using a patched-in version of gix in other crates. --- gix-dir/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gix-dir/Cargo.toml b/gix-dir/Cargo.toml index aa532d5f654..a05672c1905 100644 --- a/gix-dir/Cargo.toml +++ b/gix-dir/Cargo.toml @@ -24,7 +24,7 @@ gix-ignore = { version = "^0.11.1", path = "../gix-ignore" } gix-utils = { version = "^0.1.10", path = "../gix-utils", features = ["bstr"] } bstr = { version = "1.5.0", default-features = false } -thiserror = "1.0.56" +thiserror = "1.0.34" [dev-dependencies] gix-testtools = { path = "../tests/tools" } From 2dc373f54988fb38b3a8f1e6100c46ece53b9f95 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 14 Mar 2024 15:55:26 +0100 Subject: [PATCH 24/26] feat: provide `Entry::worktree_summary()` That way it's possible to more easily and straight-forwardly understand the status of an entry, comparing index to worktree. --- .../src/index_as_worktree_with_renames/mod.rs | 2 +- .../index_as_worktree_with_renames/types.rs | 95 ++++++++++++++++++- .../status/index_as_worktree_with_renames.rs | 58 ++++++++++- 3 files changed, 151 insertions(+), 4 deletions(-) diff --git a/gix-status/src/index_as_worktree_with_renames/mod.rs b/gix-status/src/index_as_worktree_with_renames/mod.rs index 6185e05cd53..39f255f3788 100644 --- a/gix-status/src/index_as_worktree_with_renames/mod.rs +++ b/gix-status/src/index_as_worktree_with_renames/mod.rs @@ -1,6 +1,6 @@ //! Changes between the index and the worktree along with optional rename tracking. mod types; -pub use types::{Context, DirwalkContext, Entry, Error, Options, Outcome, RewriteSource, Sorting, VisitEntry}; +pub use types::{Context, DirwalkContext, Entry, Error, Options, Outcome, RewriteSource, Sorting, Summary, VisitEntry}; mod recorder; pub use recorder::Recorder; diff --git a/gix-status/src/index_as_worktree_with_renames/types.rs b/gix-status/src/index_as_worktree_with_renames/types.rs index c46462f5d0a..870a4e4e4cd 100644 --- a/gix-status/src/index_as_worktree_with_renames/types.rs +++ b/gix-status/src/index_as_worktree_with_renames/types.rs @@ -1,4 +1,4 @@ -use crate::index_as_worktree::EntryStatus; +use crate::index_as_worktree::{Change, EntryStatus}; use bstr::{BStr, ByteSlice}; use std::sync::atomic::AtomicBool; @@ -141,6 +141,58 @@ pub enum Entry<'index, ContentChange, SubmoduleStatus> { }, } +/// An easy to grasp summary of the changes of the worktree compared to the index. +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum Summary { + /// An entry exists in the index but doesn't in the worktree. + Removed, + /// A file exists in the worktree but doesn't have a corresponding entry in the index. + /// + /// In a `git status`, this would be an untracked file. + Added, + /// A file or submodule was modified, compared to the state recorded in the index. + /// On Unix, the change of executable bit also counts as modification. + /// + /// If the modification is a submodule, it could also stem from various other factors, like + /// having modified or untracked files, or changes in the index. + Modified, + /// The type of the entry in the worktree changed compared to the index. + /// + /// This can happen if a file in the worktree now is a directory, or a symlink, for example. + TypeChange, + /// A match between an entry in the index and a differently named file in the worktree was detected, + /// considering the index the source of a rename operation, and the worktree file the destination. + /// + /// Note that the renamed file may also have been modified, but is considered similar enough. + /// + /// To obtain this state, rewrite-tracking must have been enabled, as otherwise the source would be + /// considered `Removed` and the destination would be considered `Added`. + Renamed, + /// A match between an entry in the index and a differently named file in the worktree was detected, + /// considering the index the source of the copy of a worktree file. + /// + /// Note that the copied file may also have been modified, but is considered similar enough. + /// + /// To obtain this state, rewrite-and-copy-tracking must have been enabled, as otherwise the source would be + /// considered `Removed` and the destination would be considered `Added`. + Copied, + /// An index entry with a corresponding worktree file that corresponds to an untracked worktree + /// file marked with `git add --intent-to-add`. + /// + /// This means it's not available in the object database yet even though now an entry exists + /// that represents the worktree file. + /// The entry represents the promise of adding a new file, no matter the actual stat or content. + /// Effectively this means nothing changed. + /// This also means the file is still present, and that no detailed change checks were performed. + IntentToAdd, + /// Describes a conflicting entry in the index, which also means that + /// no further comparison to the worktree file was performed. + /// + /// As this variant only describes the state of the index, the corresponding worktree file may + /// or may not exist. + Conflict, +} + /// Access impl<ContentChange, SubmoduleStatus> RewriteSource<'_, ContentChange, SubmoduleStatus> { /// The repository-relative path of this source. @@ -156,6 +208,47 @@ impl<ContentChange, SubmoduleStatus> RewriteSource<'_, ContentChange, SubmoduleS /// Access impl<ContentChange, SubmoduleStatus> Entry<'_, ContentChange, SubmoduleStatus> { + /// Return a summary of the entry as digest of its status, or `None` if this entry is + /// created from the directory walk and is *not untracked*, or if it is merely to communicate + /// a needed update to the index entry. + pub fn summary(&self) -> Option<Summary> { + Some(match self { + Entry::Modification { + status: EntryStatus::Conflict(_), + .. + } => Summary::Conflict, + Entry::Modification { + status: EntryStatus::IntentToAdd, + .. + } => Summary::IntentToAdd, + Entry::Modification { + status: EntryStatus::NeedsUpdate(_), + .. + } => return None, + Entry::Modification { + status: EntryStatus::Change(change), + .. + } => match change { + Change::SubmoduleModification(_) | Change::Modification { .. } => Summary::Modified, + Change::Type => Summary::TypeChange, + Change::Removed => Summary::Removed, + }, + Entry::DirectoryContents { entry, .. } => { + if matches!(entry.status, gix_dir::entry::Status::Untracked) { + Summary::Added + } else { + return None; + } + } + Entry::Rewrite { copy, .. } => { + if *copy { + Summary::Copied + } else { + Summary::Renamed + } + } + }) + } /// The repository-relative path at which the source of a rewrite is located. /// /// If this isn't a rewrite, the path is the location of the entry itself. diff --git a/gix-status/tests/status/index_as_worktree_with_renames.rs b/gix-status/tests/status/index_as_worktree_with_renames.rs index 08414a9687f..0706362a8b9 100644 --- a/gix-status/tests/status/index_as_worktree_with_renames.rs +++ b/gix-status/tests/status/index_as_worktree_with_renames.rs @@ -5,7 +5,9 @@ use gix_diff::rewrites::CopySource; use gix_status::index_as_worktree::traits::FastEq; use gix_status::index_as_worktree::{Change, EntryStatus}; use gix_status::index_as_worktree_with_renames; -use gix_status::index_as_worktree_with_renames::{Context, DirwalkContext, Entry, Options, Outcome, Recorder, Sorting}; +use gix_status::index_as_worktree_with_renames::{ + Context, DirwalkContext, Entry, Options, Outcome, Recorder, Sorting, Summary, +}; use pretty_assertions::assert_eq; #[test] @@ -267,10 +269,31 @@ fn fixture_filtered_detailed( ) .unwrap(); - assert_eq!(records_to_expectations(&recorder.records), expected); + let actual = records_to_expectations(&recorder.records); + assert_eq!(actual, expected); + assert_summary(&recorder.records, expected); cleanup(outcome) } +fn assert_summary(entries: &[Entry<(), ()>], expected: &[Expectation]) { + let entries: Vec<_> = entries + .iter() + .filter(|r| { + !matches!( + r, + Entry::Modification { + status: EntryStatus::NeedsUpdate(..), + .. + } + ) + }) + .collect(); + assert_eq!(entries.len(), expected.len()); + for (entry, expected) in entries.iter().zip(expected) { + assert_eq!(entry.summary(), expected.summary()); + } +} + fn records_to_expectations<'a>(recs: &'a [Entry<'_, (), ()>]) -> Vec<Expectation<'a>> { recs.iter() .filter(|r| { @@ -328,3 +351,34 @@ enum Expectation<'a> { copy: bool, }, } + +impl Expectation<'_> { + pub fn summary(&self) -> Option<Summary> { + Some(match self { + Expectation::Modification { status, .. } => match status { + EntryStatus::Conflict(_) => Summary::Conflict, + EntryStatus::Change(change) => match change { + Change::Removed => Summary::Removed, + Change::Type => Summary::TypeChange, + Change::Modification { .. } | Change::SubmoduleModification(_) => Summary::Modified, + }, + EntryStatus::NeedsUpdate(_) => return None, + EntryStatus::IntentToAdd => Summary::IntentToAdd, + }, + Expectation::DirwalkEntry { status, .. } => { + if matches!(status, gix_dir::entry::Status::Untracked) { + Summary::Added + } else { + return None; + } + } + Expectation::Rewrite { copy, .. } => { + if *copy { + Summary::Copied + } else { + Summary::Renamed + } + } + }) + } +} From da45d92f844d670dd23712a031584a4c3352708b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 14 Mar 2024 17:20:59 +0100 Subject: [PATCH 25/26] make `summary` available for `Item`. --- gix/src/status/index_worktree.rs | 42 ++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index 35342e3f575..0248b3d27db 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -315,6 +315,9 @@ pub mod iter { use crate::status::index_worktree::{iter, BuiltinSubmoduleStatus}; use crate::status::{index_worktree, Platform}; use crate::worktree::IndexPersistedOrInMemory; + use gix_status::index_as_worktree::{Change, EntryStatus}; + + pub use gix_status::index_as_worktree_with_renames::Summary; pub(super) enum ApplyChange { SetSizeToZero, @@ -377,7 +380,7 @@ pub mod iter { /// This can also happen for copies. RewriteFromIndex { /// The entry that is the source of the rewrite, which means it was removed on disk, - /// equivalent to [Change::Removed](gix_status::index_as_worktree::Change::Removed). + /// equivalent to [Change::Removed]. /// /// Note that the [entry-id](gix_index::Entry::id) is the content-id of the source of the rewrite. source_entry: gix_index::Entry, @@ -505,6 +508,41 @@ pub mod iter { }, } + impl Item { + /// Return a simplified summary of the item as digest of its status, or `None` if this item is + /// created from the directory walk and is *not untracked*, or if it is merely to communicate + /// a needed update to the index entry. + pub fn summary(&self) -> Option<Summary> { + use gix_status::index_as_worktree_with_renames::Summary::*; + Some(match self { + Item::Modification { status, .. } => match status { + EntryStatus::Conflict(_) => Conflict, + EntryStatus::Change(change) => match change { + Change::Removed => Removed, + Change::Type => TypeChange, + Change::Modification { .. } | Change::SubmoduleModification(_) => Modified, + }, + EntryStatus::NeedsUpdate(_) => return None, + EntryStatus::IntentToAdd => IntentToAdd, + }, + Item::DirectoryContents { entry, .. } => { + if matches!(entry.status, gix_dir::entry::Status::Untracked) { + Added + } else { + return None; + } + } + Item::Rewrite { copy, .. } => { + if *copy { + Copied + } else { + Renamed + } + } + }) + } + } + impl<'index> From<gix_status::index_as_worktree_with_renames::Entry<'index, (), SubmoduleStatus>> for Item { fn from(value: gix_status::index_as_worktree_with_renames::Entry<'index, (), SubmoduleStatus>) -> Self { match value { @@ -675,7 +713,7 @@ pub mod iter { } impl Iterator for super::Iter { - type Item = Result<Item, crate::status::index_worktree::Error>; + type Item = Result<Item, index_worktree::Error>; fn next(&mut self) -> Option<Self::Item> { #[cfg(feature = "parallel")] From 3753592ef2e33f138544f761d8e77742b80680d2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Thu, 14 Mar 2024 19:38:41 +0100 Subject: [PATCH 26/26] assure submodule status doesn't operate if there is no worktree checkout --- gix/src/submodule/mod.rs | 5 ++- .../generated-archives/make_submodules.tar.xz | Bin 28948 -> 30012 bytes gix/tests/fixtures/make_submodules.sh | 9 +++++ gix/tests/submodule/mod.rs | 35 +++++++++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index a2668a2bd39..d71a21b9c86 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -382,6 +382,9 @@ pub mod status { return Ok(status); } + if !state.worktree_checkout { + return Ok(status); + } let statusses = adjust_options(sm_repo.status(gix_features::progress::Discard)?) .index_worktree_options_mut(|opts| { if ignore == config::Ignore::Untracked { @@ -408,7 +411,7 @@ pub mod status { /// Return `None` if the repository clone or the worktree are missing entirely, which would leave /// it to the caller to determine if that's considered dirty or not. pub fn is_dirty(&self) -> Option<bool> { - if !self.state.worktree_checkout && !self.state.repository_exists { + if !self.state.worktree_checkout || !self.state.repository_exists { return None; } let is_dirty = diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index e4c34d017c44a5f8d930c47362fccc089889e5c3..ac4c0109ac8686c27e3ee593842c315440712790 100644 GIT binary patch delta 29972 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 delta 28899 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 C9ZPQj diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index 740cc278ee5..4533033e6fb 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -21,6 +21,15 @@ git init submodule-head-changed cd m1 && git checkout @~1 ) +git init submodule-head-changed-no-worktree +(cd submodule-head-changed-no-worktree + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 && git checkout @~1) + rm -Rf m1 && mkdir m1 +) + git init modified-and-untracked (cd modified-and-untracked git submodule add ../module1 m1 diff --git a/gix/tests/submodule/mod.rs b/gix/tests/submodule/mod.rs index 5434ac2b152..0ff6480d88e 100644 --- a/gix/tests/submodule/mod.rs +++ b/gix/tests/submodule/mod.rs @@ -63,7 +63,7 @@ mod open { worktree_checkout: false, superproject_configuration: true, }, - Some(true), + None, )], ), ] { @@ -241,6 +241,39 @@ mod open { Ok(()) } + #[test] + fn changed_head_empty_worktree() -> crate::Result { + let repo = repo("submodule-head-changed-no-worktree")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::None, false)?; + assert_eq!( + status.state, + gix::submodule::State { + repository_exists: true, + is_old_form: false, + worktree_checkout: false, + superproject_configuration: true, + } + ); + assert_eq!( + status.is_dirty(), + None, + "a missing worktree counts as no-dirty, even though the checked out HEAD changed. \ + Git does the same, even though as we express it as 'not determined'" + ); + assert_ne!( + status.index_id, status.checked_out_head_id, + "not considered dirty despite head mismatch" + ); + assert!( + status.changes.is_none(), + "Detailed changes are never done if there is no worktree" + ); + + Ok(()) + } + #[test] fn is_dirty_skips_expensive_checks() -> crate::Result { let repo = repo("submodule-head-changed-and-modified")?;