From b35e544383b9a7571961ff0628ca8db1b232bf52 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 6 Sep 2023 20:51:39 +0200 Subject: [PATCH 01/23] Add example that tries to trip someone who reads something from the index This doesn't happen here though, so nothing special we could trigger. --- gix-submodule/tests/file/baseline.rs | 3 ++- gix-submodule/tests/file/mod.rs | 23 +++++++++++++++---- gix-submodule/tests/fixtures/basic.sh | 9 ++++++++ .../fixtures/generated-archives/basic.tar.xz | 4 ++-- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/gix-submodule/tests/file/baseline.rs b/gix-submodule/tests/file/baseline.rs index ad9934205c4..dc71ed3abe1 100644 --- a/gix-submodule/tests/file/baseline.rs +++ b/gix-submodule/tests/file/baseline.rs @@ -14,11 +14,12 @@ fn common_values_and_names_by_path() -> crate::Result { assert_eq!( modules .iter() - .map(|m| m.config_path().expect("present").to_owned()) + .map(|m| { m.config_path().expect("present").to_owned() }) .collect::>(), [ "empty-clone/.gitmodules", "multiple/.gitmodules", + "not-a-submodule/.gitmodules", "recursive-clone/.gitmodules", "recursive-clone/submodule/.gitmodules", "relative-clone/.gitmodules", diff --git a/gix-submodule/tests/file/mod.rs b/gix-submodule/tests/file/mod.rs index 43309c1d712..cecb7b759cc 100644 --- a/gix-submodule/tests/file/mod.rs +++ b/gix-submodule/tests/file/mod.rs @@ -5,11 +5,9 @@ fn submodule(bytes: &str) -> gix_submodule::File { mod is_active_platform { use std::str::FromStr; - use bstr::{BStr, ByteSlice}; - - fn multi_modules() -> crate::Result { + fn module_file(name: &str) -> crate::Result { let modules = gix_testtools::scripted_fixture_read_only("basic.sh")? - .join("multiple") + .join(name) .join(".gitmodules"); Ok(gix_submodule::File::from_bytes( std::fs::read(&modules)?.as_slice(), @@ -18,6 +16,12 @@ mod is_active_platform { )?) } + use bstr::{BStr, ByteSlice}; + + fn multi_modules() -> crate::Result { + module_file("multiple") + } + fn assume_valid_active_state<'a>( module: &'a gix_submodule::File, config: &'a gix_config::File<'static>, @@ -52,6 +56,17 @@ mod is_active_platform { .collect()) } + #[test] + fn without_submodule_in_index() -> crate::Result { + let module = module_file("not-a-submodule")?; + assert_eq!( + module.names().map(ToOwned::to_owned).collect::>(), + ["submodule"], + "entries can be read" + ); + Ok(()) + } + #[test] fn without_any_additional_settings_all_are_inactive_if_they_have_a_url() -> crate::Result { let module = multi_modules()?; diff --git a/gix-submodule/tests/fixtures/basic.sh b/gix-submodule/tests/fixtures/basic.sh index d8568b80813..203e6afdeea 100755 --- a/gix-submodule/tests/fixtures/basic.sh +++ b/gix-submodule/tests/fixtures/basic.sh @@ -37,3 +37,12 @@ git clone super recursive-clone (cd recursive-clone git submodule update --init --recursive ) + +git clone super not-a-submodule +(cd not-a-submodule + cp .gitmodules modules.bak + git rm submodule + echo fake > submodule + mv modules.bak .gitmodules + git add submodule && git commit -m "no submodule in index and commit, but in configuration" +) diff --git a/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz b/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz index 1074559a2bd..6204ba0313e 100644 --- a/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz +++ b/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c961fe67eb7af352064aeae412cabf8b8260782db003ddea7ca251c491c4963e -size 31404 +oid sha256:f1213af1023577c8c59e1a065530785e28ee10e4e04f731597aebc6a63e3dc86 +size 32472 From 4971a4837ff5ac6654aa75214bdd2243d4d864a5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 6 Sep 2023 21:30:30 +0200 Subject: [PATCH 02/23] fix: handle submodules whose entry in the index is a file. --- gix/src/submodule/mod.rs | 12 ++++++---- .../generated-archives/make_submodules.tar.xz | 4 ++-- gix/tests/fixtures/make_submodules.sh | 9 ++++++++ gix/tests/submodule/mod.rs | 22 +++++++++++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index 72a69dc81a5..52c5938fc28 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -160,17 +160,21 @@ impl<'repo> Submodule<'repo> { /// or `None` if it was deleted from the index. /// /// If `None`, but `Some()` when calling [`Self::head_id()`], then the submodule was just deleted but the change - /// wasn't yet committed. + /// wasn't yet committed. Note that `None` is also returned if the entry at the submodule path isn't a submodule. /// If `Some()`, but `None` when calling [`Self::head_id()`], then the submodule was just added without having committed the change. pub fn index_id(&self) -> Result, index_id::Error> { let path = self.path()?; - Ok(self.state.index()?.entry_by_path(&path).map(|entry| entry.id)) + Ok(self + .state + .index()? + .entry_by_path(&path) + .and_then(|entry| (entry.mode == gix_index::entry::Mode::COMMIT).then_some(entry.id))) } /// Return the object id of the submodule as stored in `HEAD^{tree}` of the superproject, or `None` if it wasn't yet committed. /// /// If `Some()`, but `None` when calling [`Self::index_id()`], then the submodule was just deleted but the change - /// wasn't yet committed. + /// wasn't yet committed. Note that `None` is also returned if the entry at the submodule path isn't a submodule. /// If `None`, but `Some()` when calling [`Self::index_id()`], then the submodule was just added without having committed the change. pub fn head_id(&self) -> Result, head_id::Error> { let path = self.path()?; @@ -180,7 +184,7 @@ impl<'repo> Submodule<'repo> { .head_commit()? .tree()? .peel_to_entry_by_path(gix_path::from_bstr(path.as_ref()))? - .map(|entry| entry.inner.oid)) + .and_then(|entry| (entry.mode() == gix_object::tree::EntryMode::Commit).then_some(entry.inner.oid))) } /// Return the path at which the repository of the submodule should be located. diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index 2c511780fd6..97135d2fc70 100644 --- a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz +++ b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c0437e6ee0730c0ffcea0368777daec45e75f0d0ef6e7464d6b5de19f822b83 -size 21428 +oid sha256:78fe07f2f55b0f90ed2b2b70cab81ba11bacaa71943990d4206b6fef04152110 +size 23128 diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index 8fc0584ce60..af5b9c96786 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -62,3 +62,12 @@ git clone --bare with-submodules with-submodules-after-clone.git git clone --bare ../module1 modules/m1 ) +git clone with-submodules not-a-submodule +(cd not-a-submodule + git submodule update --init + cp .gitmodules modules.bak + git rm m1 + echo fake > m1 + mv modules.bak .gitmodules + git add m1 && git commit -m "no submodule in index and commit, but in configuration" +) diff --git a/gix/tests/submodule/mod.rs b/gix/tests/submodule/mod.rs index 5f556deea61..89d2c29d8f2 100644 --- a/gix/tests/submodule/mod.rs +++ b/gix/tests/submodule/mod.rs @@ -46,6 +46,18 @@ mod open { }, )], ), + ( + "not-a-submodule", + &[( + "m1", + gix::submodule::State { + repository_exists: true, + is_old_form: false, + worktree_checkout: false, + superproject_configuration: true, + }, + )], + ), ] { let repo = repo(name)?; for (sm, (name, expected)) in repo.submodules()?.expect("modules present").zip(expected) { @@ -81,6 +93,16 @@ mod open { Ok(()) } + #[test] + fn not_a_submodule() -> crate::Result { + let repo = repo("not-a-submodule")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + assert!(sm.open()?.is_some(), "repo available as it was cloned"); + assert!(sm.index_id()?.is_none(), "no actual submodule"); + assert!(sm.head_id()?.is_none(), "no actual submodule"); + Ok(()) + } + #[test] fn old_form() -> crate::Result { for name in ["old-form-invalid-worktree-path", "old-form"] { From 0357b6cdb11b098fd54cd0c3df6c617d0b44a1c2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 6 Sep 2023 20:48:38 +0200 Subject: [PATCH 03/23] feat: Add `generate` and `streaming-input` feature toggles. That way, it's possible to not compile a bunch of code in `gix` if the writing of packs isn't required. --- gix-pack/Cargo.toml | 16 +- gix-pack/src/bundle/mod.rs | 2 +- gix-pack/src/data/input/entries_to_bytes.rs | 5 +- gix-pack/src/data/mod.rs | 2 + gix-pack/src/index/encode.rs | 158 ++++++++++++++++++++ gix-pack/src/index/mod.rs | 2 + gix-pack/src/index/util.rs | 28 +--- gix-pack/src/index/write/encode.rs | 124 --------------- gix-pack/src/index/write/mod.rs | 3 +- gix-pack/src/multi_index/chunk.rs | 6 +- gix-pack/tests/Cargo.toml | 2 +- justfile | 3 + 12 files changed, 186 insertions(+), 165 deletions(-) create mode 100644 gix-pack/src/index/encode.rs delete mode 100644 gix-pack/src/index/write/encode.rs diff --git a/gix-pack/Cargo.toml b/gix-pack/Cargo.toml index 930a3dfe8c6..6518df1e48f 100644 --- a/gix-pack/Cargo.toml +++ b/gix-pack/Cargo.toml @@ -14,7 +14,11 @@ autotests = false doctest = false [features] - +default = ["generate", "streaming-input"] +## generate new packs from a set of objects. +generate = ["dep:gix-traverse", "dep:gix-diff"] +## Receive a pack as datastream and resolve it +streaming-input = [] ## Provide a fixed-size allocation-free LRU cache for packs. It's useful if caching is desired while keeping the memory footprint ## for the LRU-cache itself low. pack-cache-lru-static = ["dep:uluru"] @@ -25,7 +29,7 @@ object-cache-dynamic = ["dep:clru"] ## Data structures implement `serde::Serialize` and `serde::Deserialize`. serde = ["dep:serde", "gix-object/serde"] ## Make it possible to compile to the `wasm32-unknown-unknown` target. -wasm = ["gix-diff/wasm"] +wasm = ["gix-diff?/wasm"] [dependencies] gix-features = { version = "^0.33.0", path = "../gix-features", features = ["crc32", "rustsha1", "progress", "zlib"] } @@ -33,14 +37,18 @@ gix-path = { version = "^0.9.0", path = "../gix-path" } gix-hash = { version = "^0.12.0", path = "../gix-hash" } gix-chunk = { version = "^0.4.4", path = "../gix-chunk" } gix-object = { version = "^0.35.0", path = "../gix-object" } -gix-traverse = { version = "^0.31.0", path = "../gix-traverse" } -gix-diff = { version = "^0.34.0", path = "../gix-diff" } gix-hashtable = { version = "^0.3.0", path = "../gix-hashtable" } +# for streaming of packs (input, output) +gix-traverse = { version = "^0.31.0", path = "../gix-traverse", optional = true } +gix-diff = { version = "^0.34.0", path = "../gix-diff", default-features = false, optional = true } + memmap2 = "0.7.0" smallvec = "1.3.0" parking_lot = { version = "0.12.0", default-features = false } thiserror = "1.0.26" + +# for caching uluru = { version = "3.0.0", optional = true } clru = { version = "0.6.1", optional = true } diff --git a/gix-pack/src/bundle/mod.rs b/gix-pack/src/bundle/mod.rs index fc6363f4d9b..d8ef1107d70 100644 --- a/gix-pack/src/bundle/mod.rs +++ b/gix-pack/src/bundle/mod.rs @@ -3,7 +3,7 @@ pub mod init; mod find; /// -#[cfg(not(feature = "wasm"))] +#[cfg(all(not(feature = "wasm"), feature = "streaming-input"))] pub mod write; /// diff --git a/gix-pack/src/data/input/entries_to_bytes.rs b/gix-pack/src/data/input/entries_to_bytes.rs index a8c21e653db..27cd046482f 100644 --- a/gix-pack/src/data/input/entries_to_bytes.rs +++ b/gix-pack/src/data/input/entries_to_bytes.rs @@ -73,12 +73,11 @@ where } self.num_entries += 1; entry.header.write_to(entry.decompressed_size, &mut self.output)?; - std::io::copy( - &mut entry + self.output.write_all( + entry .compressed .as_deref() .expect("caller must configure generator to keep compressed bytes"), - &mut self.output, )?; Ok(entry) } diff --git a/gix-pack/src/data/mod.rs b/gix-pack/src/data/mod.rs index 36e01d779ca..9808ae8530c 100644 --- a/gix-pack/src/data/mod.rs +++ b/gix-pack/src/data/mod.rs @@ -37,9 +37,11 @@ pub mod init { pub mod entry; /// +#[cfg(feature = "streaming-input")] pub mod input; /// Utilities to encode pack data entries and write them to a `Write` implementation to resemble a pack data file. +#[cfg(feature = "generate")] pub mod output; /// A slice into a pack file denoting a pack entry. diff --git a/gix-pack/src/index/encode.rs b/gix-pack/src/index/encode.rs new file mode 100644 index 00000000000..d9dad68ce42 --- /dev/null +++ b/gix-pack/src/index/encode.rs @@ -0,0 +1,158 @@ +use std::cmp::Ordering; + +pub(crate) const LARGE_OFFSET_THRESHOLD: u64 = 0x7fff_ffff; +pub(crate) const HIGH_BIT: u32 = 0x8000_0000; + +pub(crate) fn fanout(iter: &mut dyn ExactSizeIterator) -> [u32; 256] { + let mut fan_out = [0u32; 256]; + let entries_len = iter.len() as u32; + let mut iter = iter.enumerate(); + let mut idx_and_entry = iter.next(); + let mut upper_bound = 0; + + for (offset_be, byte) in fan_out.iter_mut().zip(0u8..=255) { + *offset_be = match idx_and_entry.as_ref() { + Some((_idx, first_byte)) => match first_byte.cmp(&byte) { + Ordering::Less => unreachable!("ids should be ordered, and we make sure to keep ahead with them"), + Ordering::Greater => upper_bound, + Ordering::Equal => { + if byte == 255 { + entries_len + } else { + idx_and_entry = iter.find(|(_, first_byte)| *first_byte != byte); + upper_bound = idx_and_entry.as_ref().map_or(entries_len, |(idx, _)| *idx as u32); + upper_bound + } + } + }, + None => entries_len, + }; + } + + fan_out +} + +#[cfg(feature = "streaming-input")] +mod function { + use gix_features::{ + hash, + progress::{self, DynNestedProgress}, + }; + use std::io; + + use super::{fanout, HIGH_BIT, LARGE_OFFSET_THRESHOLD}; + + use crate::index::V2_SIGNATURE; + + struct Count { + bytes: u64, + inner: W, + } + + impl Count { + fn new(inner: W) -> Self { + Count { bytes: 0, inner } + } + } + + impl io::Write for Count + where + W: io::Write, + { + fn write(&mut self, buf: &[u8]) -> io::Result { + let written = self.inner.write(buf)?; + self.bytes += written as u64; + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } + } + + pub(crate) fn write_to( + out: &mut dyn io::Write, + entries_sorted_by_oid: Vec>, + pack_hash: &gix_hash::ObjectId, + kind: crate::index::Version, + progress: &mut dyn DynNestedProgress, + ) -> io::Result { + use io::Write; + assert_eq!(kind, crate::index::Version::V2, "Can only write V2 packs right now"); + assert!( + entries_sorted_by_oid.len() <= u32::MAX as usize, + "a pack cannot have more than u32::MAX objects" + ); + + // Write header + let mut out = Count::new(std::io::BufWriter::with_capacity( + 8 * 4096, + hash::Write::new(out, kind.hash()), + )); + out.write_all(V2_SIGNATURE)?; + out.write_all(&(kind as u32).to_be_bytes())?; + + progress.init(Some(4), progress::steps()); + let start = std::time::Instant::now(); + let _info = progress.add_child_with_id("writing fan-out table".into(), gix_features::progress::UNKNOWN); + let fan_out = fanout(&mut entries_sorted_by_oid.iter().map(|e| e.data.id.first_byte())); + + for value in fan_out.iter() { + out.write_all(&value.to_be_bytes())?; + } + + progress.inc(); + let _info = progress.add_child_with_id("writing ids".into(), gix_features::progress::UNKNOWN); + for entry in &entries_sorted_by_oid { + out.write_all(entry.data.id.as_slice())?; + } + + progress.inc(); + let _info = progress.add_child_with_id("writing crc32".into(), gix_features::progress::UNKNOWN); + for entry in &entries_sorted_by_oid { + out.write_all(&entry.data.crc32.to_be_bytes())?; + } + + progress.inc(); + let _info = progress.add_child_with_id("writing offsets".into(), gix_features::progress::UNKNOWN); + { + let mut offsets64 = Vec::::new(); + for entry in &entries_sorted_by_oid { + let offset: u32 = if entry.offset > LARGE_OFFSET_THRESHOLD { + assert!( + offsets64.len() < LARGE_OFFSET_THRESHOLD as usize, + "Encoding breakdown - way too many 64bit offsets" + ); + offsets64.push(entry.offset); + ((offsets64.len() - 1) as u32) | HIGH_BIT + } else { + entry.offset as u32 + }; + out.write_all(&offset.to_be_bytes())?; + } + for value in offsets64 { + out.write_all(&value.to_be_bytes())?; + } + } + + out.write_all(pack_hash.as_slice())?; + + let bytes_written_without_trailer = out.bytes; + let out = out.inner.into_inner()?; + let index_hash: gix_hash::ObjectId = out.hash.digest().into(); + out.inner.write_all(index_hash.as_slice())?; + out.inner.flush()?; + + progress.inc(); + progress.show_throughput_with( + start, + (bytes_written_without_trailer + 20) as usize, + progress::bytes().expect("unit always set"), + progress::MessageLevel::Success, + ); + + Ok(index_hash) + } +} +#[cfg(feature = "streaming-input")] +pub(crate) use function::write_to; diff --git a/gix-pack/src/index/mod.rs b/gix-pack/src/index/mod.rs index 36be2d4293a..8d880744241 100644 --- a/gix-pack/src/index/mod.rs +++ b/gix-pack/src/index/mod.rs @@ -141,10 +141,12 @@ pub mod init; pub(crate) mod access; pub use access::Entry; +pub(crate) mod encode; /// pub mod traverse; mod util; /// pub mod verify; /// +#[cfg(feature = "streaming-input")] pub mod write; diff --git a/gix-pack/src/index/util.rs b/gix-pack/src/index/util.rs index b5477d443e4..2549429f9ce 100644 --- a/gix-pack/src/index/util.rs +++ b/gix-pack/src/index/util.rs @@ -1,4 +1,4 @@ -use std::{io, time::Instant}; +use std::time::Instant; use gix_features::progress::{self, Progress}; @@ -19,29 +19,3 @@ pub(crate) fn index_entries_sorted_by_offset_ascending( progress.show_throughput(start); v } - -pub(crate) struct Count { - pub bytes: u64, - pub inner: W, -} - -impl Count { - pub fn new(inner: W) -> Self { - Count { bytes: 0, inner } - } -} - -impl io::Write for Count -where - W: io::Write, -{ - fn write(&mut self, buf: &[u8]) -> io::Result { - let written = self.inner.write(buf)?; - self.bytes += written as u64; - Ok(written) - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} diff --git a/gix-pack/src/index/write/encode.rs b/gix-pack/src/index/write/encode.rs deleted file mode 100644 index 1eec974fc51..00000000000 --- a/gix-pack/src/index/write/encode.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::{cmp::Ordering, io}; - -pub(crate) const LARGE_OFFSET_THRESHOLD: u64 = 0x7fff_ffff; -pub(crate) const HIGH_BIT: u32 = 0x8000_0000; - -use gix_features::{ - hash, - progress::{self, DynNestedProgress}, -}; - -use crate::index::{util::Count, V2_SIGNATURE}; - -pub(crate) fn write_to( - out: &mut dyn io::Write, - entries_sorted_by_oid: Vec>, - pack_hash: &gix_hash::ObjectId, - kind: crate::index::Version, - progress: &mut dyn DynNestedProgress, -) -> io::Result { - use io::Write; - assert_eq!(kind, crate::index::Version::V2, "Can only write V2 packs right now"); - assert!( - entries_sorted_by_oid.len() <= u32::MAX as usize, - "a pack cannot have more than u32::MAX objects" - ); - - // Write header - let mut out = Count::new(std::io::BufWriter::with_capacity( - 8 * 4096, - hash::Write::new(out, kind.hash()), - )); - out.write_all(V2_SIGNATURE)?; - out.write_all(&(kind as u32).to_be_bytes())?; - - progress.init(Some(4), progress::steps()); - let start = std::time::Instant::now(); - let _info = progress.add_child_with_id("writing fan-out table".into(), gix_features::progress::UNKNOWN); - let fan_out = fanout(&mut entries_sorted_by_oid.iter().map(|e| e.data.id.first_byte())); - - for value in fan_out.iter() { - out.write_all(&value.to_be_bytes())?; - } - - progress.inc(); - let _info = progress.add_child_with_id("writing ids".into(), gix_features::progress::UNKNOWN); - for entry in &entries_sorted_by_oid { - out.write_all(entry.data.id.as_slice())?; - } - - progress.inc(); - let _info = progress.add_child_with_id("writing crc32".into(), gix_features::progress::UNKNOWN); - for entry in &entries_sorted_by_oid { - out.write_all(&entry.data.crc32.to_be_bytes())?; - } - - progress.inc(); - let _info = progress.add_child_with_id("writing offsets".into(), gix_features::progress::UNKNOWN); - { - let mut offsets64 = Vec::::new(); - for entry in &entries_sorted_by_oid { - let offset: u32 = if entry.offset > LARGE_OFFSET_THRESHOLD { - assert!( - offsets64.len() < LARGE_OFFSET_THRESHOLD as usize, - "Encoding breakdown - way too many 64bit offsets" - ); - offsets64.push(entry.offset); - ((offsets64.len() - 1) as u32) | HIGH_BIT - } else { - entry.offset as u32 - }; - out.write_all(&offset.to_be_bytes())?; - } - for value in offsets64 { - out.write_all(&value.to_be_bytes())?; - } - } - - out.write_all(pack_hash.as_slice())?; - - let bytes_written_without_trailer = out.bytes; - let out = out.inner.into_inner()?; - let index_hash: gix_hash::ObjectId = out.hash.digest().into(); - out.inner.write_all(index_hash.as_slice())?; - out.inner.flush()?; - - progress.inc(); - progress.show_throughput_with( - start, - (bytes_written_without_trailer + 20) as usize, - progress::bytes().expect("unit always set"), - progress::MessageLevel::Success, - ); - - Ok(index_hash) -} - -pub(crate) fn fanout(iter: &mut dyn ExactSizeIterator) -> [u32; 256] { - let mut fan_out = [0u32; 256]; - let entries_len = iter.len() as u32; - let mut iter = iter.enumerate(); - let mut idx_and_entry = iter.next(); - let mut upper_bound = 0; - - for (offset_be, byte) in fan_out.iter_mut().zip(0u8..=255) { - *offset_be = match idx_and_entry.as_ref() { - Some((_idx, first_byte)) => match first_byte.cmp(&byte) { - Ordering::Less => unreachable!("ids should be ordered, and we make sure to keep ahead with them"), - Ordering::Greater => upper_bound, - Ordering::Equal => { - if byte == 255 { - entries_len - } else { - idx_and_entry = iter.find(|(_, first_byte)| *first_byte != byte); - upper_bound = idx_and_entry.as_ref().map_or(entries_len, |(idx, _)| *idx as u32); - upper_bound - } - } - }, - None => entries_len, - }; - } - - fan_out -} diff --git a/gix-pack/src/index/write/mod.rs b/gix-pack/src/index/write/mod.rs index a6f9e6b4cef..d1402fa868b 100644 --- a/gix-pack/src/index/write/mod.rs +++ b/gix-pack/src/index/write/mod.rs @@ -6,7 +6,6 @@ use gix_features::progress::{self, Count, Progress}; use crate::cache::delta::{traverse, Tree}; -pub(crate) mod encode; mod error; pub(crate) struct TreeEntry { @@ -233,7 +232,7 @@ impl crate::index::File { } None => return Err(Error::IteratorInvariantTrailer), }; - let index_hash = encode::write_to( + let index_hash = crate::index::encode::write_to( out, sorted_pack_offsets_by_oid, &pack_hash, diff --git a/gix-pack/src/multi_index/chunk.rs b/gix-pack/src/multi_index/chunk.rs index b92e02253bd..86e43714dba 100644 --- a/gix-pack/src/multi_index/chunk.rs +++ b/gix-pack/src/multi_index/chunk.rs @@ -132,7 +132,7 @@ pub mod fanout { sorted_entries: &[multi_index::write::Entry], out: &mut dyn std::io::Write, ) -> std::io::Result<()> { - let fanout = crate::index::write::encode::fanout(&mut sorted_entries.iter().map(|e| e.id.first_byte())); + let fanout = crate::index::encode::fanout(&mut sorted_entries.iter().map(|e| e.id.first_byte())); for value in fanout.iter() { out.write_all(&value.to_be_bytes())?; @@ -190,7 +190,7 @@ pub mod offsets { large_offsets_needed: bool, out: &mut dyn std::io::Write, ) -> std::io::Result<()> { - use crate::index::write::encode::{HIGH_BIT, LARGE_OFFSET_THRESHOLD}; + use crate::index::encode::{HIGH_BIT, LARGE_OFFSET_THRESHOLD}; let mut num_large_offsets = 0u32; for entry in sorted_entries { @@ -226,7 +226,7 @@ pub mod offsets { pub mod large_offsets { use std::ops::Range; - use crate::{index::write::encode::LARGE_OFFSET_THRESHOLD, multi_index}; + use crate::{index::encode::LARGE_OFFSET_THRESHOLD, multi_index}; /// The id uniquely identifying the large offsets table (with 64 bit offsets) pub const ID: gix_chunk::Id = *b"LOFF"; diff --git a/gix-pack/tests/Cargo.toml b/gix-pack/tests/Cargo.toml index 1c2dc71a448..a00c6af8765 100644 --- a/gix-pack/tests/Cargo.toml +++ b/gix-pack/tests/Cargo.toml @@ -17,7 +17,7 @@ name = "pack" path = "integrate.rs" [dev-dependencies] -gix-pack = { path = ".." } +gix-pack = { path = "..", features = ["generate", "streaming-input"] } gix-features = { path = "../../gix-features" } gix-testtools = { path = "../../tests/tools"} gix-odb = { path = "../../gix-odb" } diff --git a/justfile b/justfile index 96b89f25e05..c60f118063a 100755 --- a/justfile +++ b/justfile @@ -47,6 +47,9 @@ check: cargo check --no-default-features --features max cargo check -p gitoxide-core --features blocking-client cargo check -p gitoxide-core --features async-client + cargo check -p gix-pack --no-default-features + cargo check -p gix-pack --no-default-features --features generate + cargo check -p gix-pack --no-default-features --features streaming-input cd gix-hash; \ set -ex; \ cargo check --all-features; \ From 6b27ffa18f0049321e7c1837acc5467f0966fbb5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 13:07:09 +0200 Subject: [PATCH 04/23] adapt to changes in features of `gix-pack` --- gix-odb/Cargo.toml | 2 +- gix-transport/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gix-odb/Cargo.toml b/gix-odb/Cargo.toml index 6227447dd61..52d9c394465 100644 --- a/gix-odb/Cargo.toml +++ b/gix-odb/Cargo.toml @@ -24,7 +24,7 @@ gix-date = { version = "^0.7.4", path = "../gix-date" } gix-path = { version = "^0.9.0", path = "../gix-path" } gix-quote = { version = "^0.4.7", path = "../gix-quote" } gix-object = { version = "^0.35.0", path = "../gix-object" } -gix-pack = { version = "^0.41.0", path = "../gix-pack" } +gix-pack = { version = "^0.41.0", path = "../gix-pack", default-features = false } serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} tempfile = "3.1.0" diff --git a/gix-transport/Cargo.toml b/gix-transport/Cargo.toml index 4f897343180..0516b7cd572 100644 --- a/gix-transport/Cargo.toml +++ b/gix-transport/Cargo.toml @@ -87,7 +87,7 @@ async-std = { version = "1.12.0", optional = true } document-features = { version = "0.2.0", optional = true } [dev-dependencies] -gix-pack = { path = "../gix-pack" } +gix-pack = { path = "../gix-pack", default-features = false, features = ["streaming-input"] } gix-hash = { path = "../gix-hash" } async-std = { version = "1.9.0", features = ["attributes"] } maybe-async = "0.2.6" From c5ec244979b7e6baf9a8237e4f12cb87809131ae Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 14:25:41 +0200 Subject: [PATCH 05/23] feat: improve feature documentation. This should make optimizing compile time and performance easier, while assuring these options aren't pre-determined by library providers. --- gix/Cargo.toml | 131 ++++++++++++++++++++++++++++++++----------------- gix/src/lib.rs | 12 ++--- 2 files changed, 91 insertions(+), 52 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index ae052aa527b..ca41d911aa8 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -32,30 +32,42 @@ required-features = ["blocking-network-client"] default = ["max-performance-safe", "comfort", "extras"] -#! ### Mutually Exclusive Network Client -#! Either `async-*` or `blocking-*` versions of these toggles may be enabled at a time. - -## Make `gix-protocol` available along with an async client. -async-network-client = ["gix-protocol/async-client"] -## Use this if your crate uses `async-std` as runtime, and enable basic runtime integration when connecting to remote servers. -async-network-client-async-std = ["async-std", "async-network-client", "gix-transport/async-std"] -## Make `gix-protocol` available along with a blocking client. -blocking-network-client = ["gix-protocol/blocking-client"] -## Stacks with `blocking-network-client` to provide support for HTTP/S using **curl**, and implies blocking networking as a whole. -blocking-http-transport-curl = ["blocking-network-client", "gix-transport/http-client-curl"] -## Stacks with `blocking-network-client` to provide support for HTTP/S using **reqwest**, and implies blocking networking as a whole. -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. Note that https isn't available without a selection. -blocking-http-transport-reqwest-rust-tls = ["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. Note that https isn't available without a selection. -blocking-http-transport-reqwest-native-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/default-tls" ] - - -#! ### Other +#! There are various categories of features which help to optimize performance and build times. `gix` comes with 'batteries included' and everything is +#! enabled as long as it doesn't sacrifice compatibility. Most users will be fine with that but will pay with higher compile times than necessary as they +#! probably don't use all of these features. +#! +#! **Thus it's recommended to take a moment and optimize build times by chosing only those 'Components' that you require.** *'Performance' relevant features should +#! be chosen next to maximize efficiency.* +#! +#! #### Application Developers +#! +#! These are considered the end-users, all they need to tune is `Performance` features to optimize the efficiency of their app, assuming they don't use `gix` +#! directly. Otherwise, see the `Library Developers` paragraph. +#! +#! In order to configure a crate that isn't a direct dependency, one has to make it a direct dependency. We recommend +#! `gix-for-configuration = { package = "gix", version = "X.Y.Z", features = […] }` to make clear this dependency isn't used in code. +#! +#! #### Library Developers +#! +#! As a developer of a library, you should start out with `gix = { version = "X.Y.Z", default-features = false }` and add components as you see fit. +#! For best compatibility, **do not activate `max-performance-safe`** or any other performance options. +#! +#! #### Bundles +#! +#! A bundle is a set of related feature toggles which can be activated with a single name that acts as a group. +#! Bundles are for convenience only and bear no further meaning beyond the cargo manifest file. ## Various additional features and capabilities that are not necessarily part of what most users would need. extras = ["worktree-stream", "worktree-archive"] +## Various progress-related features that improve the look of progress message units. +comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] + +#! #### Components +#! +#! A component is a distinct feature which may be comprised of one or more methods around a particular topic. +#! Providers of libraries should only activate + ## Make it possible to turn a tree into a stream of bytes, which can be decoded to entries and turned into various other formats. worktree-stream = ["gix-worktree-stream"] @@ -65,35 +77,32 @@ worktree-stream = ["gix-worktree-stream"] ## Your application should add it as dependency and re-activate the desired features. worktree-archive = ["gix-archive", "worktree-stream"] -## Various progress-related features that improve the look of progress message units. -comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] - -## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde = [ "dep:serde", - "gix-pack/serde", - "gix-object/serde", - "gix-protocol?/serde", - "gix-transport?/serde", - "gix-ref/serde", - "gix-odb/serde", - "gix-index/serde", - "gix-mailmap/serde", - "gix-url/serde", - "gix-attributes/serde", - "gix-ignore/serde", - "gix-revision/serde", - "gix-worktree/serde", - "gix-commitgraph/serde", - "gix-credentials/serde"] +#! #### Mutually Exclusive Network Client +#! +#! Either `async-*` or `blocking-*` versions of these toggles may be enabled at a time. +#! For this reason, these must be chosen by the user of the library and can't be pre-selected. +#! Making a choice here also affects which crypto-library ends up being used. -## Re-export the progress tree root which allows to obtain progress from various functions which take `impl gix::Progress`. -## Applications which want to display progress will probably need this implementation. -progress-tree = ["prodash/progress-tree"] +## Make `gix-protocol` available along with an async client. +async-network-client = ["gix-protocol/async-client"] +## Use this if your crate uses `async-std` as runtime, and enable basic runtime integration when connecting to remote servers via the `git://` protocol. +async-network-client-async-std = ["async-std", "async-network-client", "gix-transport/async-std"] +## Make `gix-protocol` available along with a blocking client, providing access to the `file://`, git://` and `ssh://` transports. +blocking-network-client = ["gix-protocol/blocking-client"] +## Stacks with `blocking-network-client` to provide support for HTTP/S using **curl**, and implies blocking networking as a whole, making the `https://` transport avaialble. +blocking-http-transport-curl = ["blocking-network-client", "gix-transport/http-client-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 avaialble. +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", "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" ] -## Print debugging information about usage of object database caches, useful for tuning cache sizes. -cache-efficiency-debug = ["gix-features/cache-efficiency-debug"] -#! ### Performance +#! #### Performance +#! +#! The reason these features exist is to allow optimization for compile time and optimize for compatibility by default. This means that some performance options around +#! SHA1 and ZIP might not compile on all platforms, so it depeneds on the end-user who compiles the application to chose these based on their needs. ## Activate features that maximize performance, like usage of threads, `zlib-ng` and access to caching in object databases, skipping the ones known to cause compile failures ## on some platforms. @@ -124,6 +133,36 @@ max-performance = [ "max-performance-safe", "gix-features/zlib-ng", "fast-sha1" ## This might cause compile failures as well which is why it can be turned off separately. fast-sha1 = [ "gix-features/fast-sha1" ] +#! #### Other +#! +#! The catch-all of feature toggles. + +## Data structures implement `serde::Serialize` and `serde::Deserialize`. +serde = [ "dep:serde", + "gix-pack/serde", + "gix-object/serde", + "gix-protocol?/serde", + "gix-transport?/serde", + "gix-ref/serde", + "gix-odb/serde", + "gix-index/serde", + "gix-mailmap/serde", + "gix-url/serde", + "gix-attributes/serde", + "gix-ignore/serde", + "gix-revision/serde", + "gix-worktree/serde", + "gix-commitgraph/serde", + "gix-credentials/serde"] + +## Re-export the progress tree root which allows to obtain progress from various functions which take `impl gix::Progress`. +## Applications which want to display progress will probably need this implementation. +progress-tree = ["prodash/progress-tree"] + +## Print debugging information about usage of object database caches, useful for tuning cache sizes. +cache-efficiency-debug = ["gix-features/cache-efficiency-debug"] + + [dependencies] gix-utils = { version = "^0.1.5", path = "../gix-utils" } diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 6fcc561894d..ca6082f7c56 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -4,7 +4,7 @@ //! individually. Sometimes it may hide complexity under the assumption that the performance difference doesn't matter //! for all but the fewest tools out there, which would be using the underlying crates directly or file an issue. //! -//! # The prelude and extensions +//! ### The prelude and extensions //! //! With `use git_repository::prelude::*` you should be ready to go as it pulls in various extension traits to make functionality //! available on objects that may use it. @@ -14,13 +14,13 @@ //! Most extensions to existing objects provide an `obj_with_extension.attach(&repo).an_easier_version_of_a_method()` for simpler //! call signatures. //! -//! ## `ThreadSafe` Mode +//! ### `ThreadSafe` Mode //! //! By default, the [`Repository`] isn't `Sync` and thus can't be used in certain contexts which require the `Sync` trait. //! //! To help with this, convert it with [`.into_sync()`][Repository::into_sync()] into a [`ThreadSafeRepository`]. //! -//! ## Object-Access Performance +//! ### Object-Access Performance //! //! Accessing objects quickly is the bread-and-butter of working with git, right after accessing references. Hence it's vital //! to understand which cache levels exist and how to leverage them. @@ -42,9 +42,9 @@ //! When reading the documentation of the canonical gix-worktree program one gets the impression work tree and working tree are used //! interchangeably. We use the term _work tree_ only and try to do so consistently as its shorter and assumed to be the same. //! -//! # Cargo-features +//! ### Plumbing Crates //! -//! To make using _sub-crates_ easier these are re-exported into the root of this crate. Here we list how to access nested plumbing +//! To make using _sub-crates_ and their types easier, these are re-exported into the root of this crate. Here we list how to access nested plumbing //! crates which are otherwise harder to discover: //! //! **`git_repository::`** @@ -64,7 +64,7 @@ //! * [`git2::build::CheckoutBuilder::disable_filters()](https://docs.rs/git2/*/git2/build/struct.CheckoutBuilder.html#method.disable_filters) ➡ ❌ *(filters are always applied during checkouts)* //! * [`git2::Repository::submodule_status()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.submodule_status) ➡ [`Submodule::state()`] - status provides more information and conveniences though, and an actual worktree status isn't performed. //! -//! ## Feature Flags +//! ### Feature Flags #![cfg_attr( feature = "document-features", cfg_attr(doc, doc = ::document_features::document_features!()) From fea044e5d09282a5772c8fe9a534d9ebf7f11bbc Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 13:24:12 +0200 Subject: [PATCH 06/23] feat: allow disabling the `blob-diff` capability This also removes all diff capabilities. --- gix/Cargo.toml | 8 ++++++-- gix/src/config/cache/access.rs | 5 ++++- gix/src/config/cache/init.rs | 9 +++++++-- gix/src/config/mod.rs | 2 ++ gix/src/config/tree/mod.rs | 8 ++++++-- gix/src/config/tree/sections/mod.rs | 2 ++ gix/src/object/blob.rs | 1 + gix/src/object/tree/mod.rs | 1 + gix/tests/config/tree.rs | 1 + gix/tests/object/tree/mod.rs | 1 + justfile | 2 ++ 11 files changed, 33 insertions(+), 7 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index ca41d911aa8..73d78c5037a 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -58,7 +58,7 @@ default = ["max-performance-safe", "comfort", "extras"] #! Bundles are for convenience only and bear no further meaning beyond the cargo manifest file. ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive"] +extras = ["worktree-stream", "worktree-archive", "blob-diff"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -68,6 +68,10 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. #! Providers of libraries should only activate +## Make it possible to diff blobs line by line. Note that this feature is integral for implementing tree-diffs as well due to the handling of rename-tracking, +## which relies on line-by-line diffs in some cases. +blob-diff = ["gix-diff/blob"] + ## Make it possible to turn a tree into a stream of bytes, which can be decoded to entries and turned into various other formats. worktree-stream = ["gix-worktree-stream"] @@ -189,7 +193,7 @@ gix-negotiate = { version = "^0.6.0", path = "../gix-negotiate" } gix-path = { version = "^0.9.0", path = "../gix-path" } gix-url = { version = "^0.22.0", path = "../gix-url" } gix-traverse = { version = "^0.31.0", path = "../gix-traverse" } -gix-diff = { version = "^0.34.0", path = "../gix-diff" } +gix-diff = { version = "^0.34.0", path = "../gix-diff", default-features = false } gix-mailmap = { version = "^0.17.0", path = "../gix-mailmap" } gix-features = { version = "^0.33.0", path = "../gix-features", features = ["progress", "once_cell"] } gix-trace = { version = "^0.1.3", path = "../gix-trace" } diff --git a/gix/src/config/cache/access.rs b/gix/src/config/cache/access.rs index 2b7250ebac0..3bc39169fb8 100644 --- a/gix/src/config/cache/access.rs +++ b/gix/src/config/cache/access.rs @@ -9,7 +9,7 @@ use crate::{ config, config::{ boolean, - cache::util::{ApplyLeniency, ApplyLeniencyDefault, ApplyLeniencyDefaultValue}, + cache::util::{ApplyLeniency, ApplyLeniencyDefaultValue}, checkout_options, tree::{gitoxide, Checkout, Core, Key}, Cache, @@ -21,7 +21,9 @@ use crate::{ /// Access impl Cache { + #[cfg(feature = "blob-diff")] pub(crate) fn diff_algorithm(&self) -> Result { + use crate::config::cache::util::ApplyLeniencyDefault; use crate::config::diff::algorithm::Error; self.diff_algorithm .get_or_try_init(|| { @@ -82,6 +84,7 @@ impl Cache { }) } + #[cfg(feature = "blob-diff")] pub(crate) fn diff_renames( &self, ) -> Result, crate::object::tree::diff::rewrites::Error> { diff --git a/gix/src/config/cache/init.rs b/gix/src/config/cache/init.rs index c9008b75131..6fb93b73dc1 100644 --- a/gix/src/config/cache/init.rs +++ b/gix/src/config/cache/init.rs @@ -174,9 +174,11 @@ impl Cache { user_agent: Default::default(), personas: Default::default(), url_rewrite: Default::default(), + #[cfg(feature = "blob-diff")] diff_renames: Default::default(), #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] url_scheme: Default::default(), + #[cfg(feature = "blob-diff")] diff_algorithm: Default::default(), }) } @@ -222,8 +224,11 @@ impl Cache { self.user_agent = Default::default(); self.personas = Default::default(); self.url_rewrite = Default::default(); - self.diff_renames = Default::default(); - self.diff_algorithm = Default::default(); + #[cfg(feature = "blob-diff")] + { + self.diff_renames = Default::default(); + self.diff_algorithm = Default::default(); + } ( self.static_pack_cache_limit_bytes, self.pack_cache_bytes, diff --git a/gix/src/config/mod.rs b/gix/src/config/mod.rs index 90ccfd864ee..2a6b6f5515d 100644 --- a/gix/src/config/mod.rs +++ b/gix/src/config/mod.rs @@ -495,11 +495,13 @@ pub(crate) struct Cache { /// A lazily loaded rewrite list for remote urls pub(crate) url_rewrite: OnceCell, /// The lazy-loaded rename information for diffs. + #[cfg(feature = "blob-diff")] pub(crate) diff_renames: OnceCell>, /// A lazily loaded mapping to know which url schemes to allow #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] pub(crate) url_scheme: OnceCell, /// The algorithm to use when diffing blobs + #[cfg(feature = "blob-diff")] pub(crate) diff_algorithm: OnceCell, /// The amount of bytes to use for a memory backed delta pack cache. If `Some(0)`, no cache is used, if `None` /// a standard cache is used which costs near to nothing and always pays for itself. diff --git a/gix/src/config/tree/mod.rs b/gix/src/config/tree/mod.rs index 3f69ccb9788..d8415154f1d 100644 --- a/gix/src/config/tree/mod.rs +++ b/gix/src/config/tree/mod.rs @@ -31,6 +31,7 @@ pub(crate) mod root { /// The `credential` section. pub const CREDENTIAL: sections::Credential = sections::Credential; /// The `diff` section. + #[cfg(feature = "blob-diff")] pub const DIFF: sections::Diff = sections::Diff; /// The `extensions` section. pub const EXTENSIONS: sections::Extensions = sections::Extensions; @@ -69,6 +70,7 @@ pub(crate) mod root { &Self::COMMITTER, &Self::CORE, &Self::CREDENTIAL, + #[cfg(feature = "blob-diff")] &Self::DIFF, &Self::EXTENSIONS, &Self::FETCH, @@ -90,10 +92,12 @@ pub(crate) mod root { mod sections; pub use sections::{ - branch, checkout, core, credential, diff, extensions, fetch, gitoxide, http, index, protocol, remote, ssh, Author, - Branch, Checkout, Clone, Committer, Core, Credential, Diff, Extensions, Fetch, Gitoxide, Http, Index, Init, Pack, + branch, checkout, core, credential, extensions, fetch, gitoxide, http, index, protocol, remote, ssh, Author, + Branch, Checkout, Clone, Committer, Core, Credential, Extensions, Fetch, Gitoxide, Http, Index, Init, Pack, Protocol, Remote, Safe, Ssh, Url, User, }; +#[cfg(feature = "blob-diff")] +pub use sections::{diff, Diff}; /// 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 ebf24a8b7f0..34929c5d146 100644 --- a/gix/src/config/tree/sections/mod.rs +++ b/gix/src/config/tree/sections/mod.rs @@ -37,7 +37,9 @@ pub mod credential; /// The `diff` top-level section. #[derive(Copy, Clone, Default)] +#[cfg(feature = "blob-diff")] pub struct Diff; +#[cfg(feature = "blob-diff")] pub mod diff; /// The `extension` top-level section. diff --git a/gix/src/object/blob.rs b/gix/src/object/blob.rs index f35605422b5..00b3519ed75 100644 --- a/gix/src/object/blob.rs +++ b/gix/src/object/blob.rs @@ -1,4 +1,5 @@ /// +#[cfg(feature = "blob-diff")] pub mod diff { use std::ops::Range; diff --git a/gix/src/object/tree/mod.rs b/gix/src/object/tree/mod.rs index 9d3f09953a4..567b1273361 100644 --- a/gix/src/object/tree/mod.rs +++ b/gix/src/object/tree/mod.rs @@ -168,6 +168,7 @@ impl<'repo> Tree<'repo> { } /// +#[cfg(feature = "blob-diff")] pub mod diff; /// diff --git a/gix/tests/config/tree.rs b/gix/tests/config/tree.rs index f98c82fea99..79bd9624d41 100644 --- a/gix/tests/config/tree.rs +++ b/gix/tests/config/tree.rs @@ -204,6 +204,7 @@ mod fetch { } } +#[cfg(feature = "blob-diff")] mod diff { use gix::{ config::tree::{Diff, Key}, diff --git a/gix/tests/object/tree/mod.rs b/gix/tests/object/tree/mod.rs index e178e66d327..b72b3b47073 100644 --- a/gix/tests/object/tree/mod.rs +++ b/gix/tests/object/tree/mod.rs @@ -1,5 +1,6 @@ use crate::util::{named_repo, named_subrepo_opts}; +#[cfg(feature = "blob-diff")] mod diff; #[test] diff --git a/justfile b/justfile index c60f118063a..b26ee90bd4a 100755 --- a/justfile +++ b/justfile @@ -42,6 +42,7 @@ check: if cargo check -p gix-packetline --all-features 2>/dev/null; then false; else true; fi if cargo check -p gix-transport --all-features 2>/dev/null; then false; else true; fi if cargo check -p gix-protocol --all-features 2>/dev/null; then false; else true; fi + if cargo tree -p gix --no-default-features -i imara-diff 2>/dev/null; then false; else true; fi cargo check --no-default-features --features lean cargo check --no-default-features --features lean-async cargo check --no-default-features --features max @@ -111,6 +112,7 @@ check: cargo check -p gix --no-default-features --features max-performance cargo check -p gix --no-default-features --features max-performance-safe cargo check -p gix --no-default-features --features progress-tree + cargo check -p gix --no-default-features --features blob-diff cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From 147528ff647dc74473ef8dd4ceac6fedebc0b15c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 17:11:51 +0200 Subject: [PATCH 07/23] feat: `gix` without connection support includes less code --- gix/Cargo.toml | 6 +++--- justfile | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 73d78c5037a..a20fa03af5d 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -88,11 +88,11 @@ worktree-archive = ["gix-archive", "worktree-stream"] #! Making a choice here also affects which crypto-library ends up being used. ## Make `gix-protocol` available along with an async client. -async-network-client = ["gix-protocol/async-client"] +async-network-client = ["gix-protocol/async-client", "gix-pack/streaming-input"] ## Use this if your crate uses `async-std` as runtime, and enable basic runtime integration when connecting to remote servers via the `git://` protocol. async-network-client-async-std = ["async-std", "async-network-client", "gix-transport/async-std"] ## Make `gix-protocol` available along with a blocking client, providing access to the `file://`, git://` and `ssh://` transports. -blocking-network-client = ["gix-protocol/blocking-client"] +blocking-network-client = ["gix-protocol/blocking-client", "gix-pack/streaming-input"] ## Stacks with `blocking-network-client` to provide support for HTTP/S using **curl**, and implies blocking networking as a whole, making the `https://` transport avaialble. blocking-http-transport-curl = ["blocking-network-client", "gix-transport/http-client-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 avaialble. @@ -186,7 +186,7 @@ gix-odb = { version = "^0.51.0", path = "../gix-odb" } gix-hash = { version = "^0.12.0", path = "../gix-hash" } gix-object = { version = "^0.35.0", path = "../gix-object" } gix-actor = { version = "^0.25.0", path = "../gix-actor" } -gix-pack = { version = "^0.41.0", path = "../gix-pack", features = ["object-cache-dynamic"] } +gix-pack = { version = "^0.41.0", path = "../gix-pack", default-features = false, features = ["object-cache-dynamic"] } gix-revision = { version = "^0.20.0", path = "../gix-revision" } gix-negotiate = { version = "^0.6.0", path = "../gix-negotiate" } diff --git a/justfile b/justfile index b26ee90bd4a..da12522a9c5 100755 --- a/justfile +++ b/justfile @@ -43,6 +43,7 @@ check: if cargo check -p gix-transport --all-features 2>/dev/null; then false; else true; fi if cargo check -p gix-protocol --all-features 2>/dev/null; then false; else true; fi if cargo tree -p gix --no-default-features -i imara-diff 2>/dev/null; then false; else true; fi + if cargo tree -p gix --no-default-features -i gix-protocol 2>/dev/null; then false; else true; fi cargo check --no-default-features --features lean cargo check --no-default-features --features lean-async cargo check --no-default-features --features max From 397024bb744af0c2e0fc66674e55998a40c24ae4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 19:02:26 +0200 Subject: [PATCH 08/23] feat: add `describe` feature That way users can more precisely decide what they want to use. Note that spec-parsing is so foundational that it's always included. Those who don't need it nor need describe don't need the crate in the fist place. --- gix-revision/Cargo.toml | 5 +++++ gix-revision/src/lib.rs | 2 ++ gix-revision/tests/revision.rs | 1 + justfile | 1 + 4 files changed, 9 insertions(+) diff --git a/gix-revision/Cargo.toml b/gix-revision/Cargo.toml index 538c18ef869..6f5438640e7 100644 --- a/gix-revision/Cargo.toml +++ b/gix-revision/Cargo.toml @@ -13,6 +13,11 @@ rust-version = "1.65" doctest = false [features] +default = ["describe"] + +## `git describe` functionality +describe = [] + ## Data structures implement `serde::Serialize` and `serde::Deserialize`. serde = [ "dep:serde", "gix-hash/serde", "gix-object/serde" ] diff --git a/gix-revision/src/lib.rs b/gix-revision/src/lib.rs index 9041f653a33..8929c0204fe 100644 --- a/gix-revision/src/lib.rs +++ b/gix-revision/src/lib.rs @@ -9,7 +9,9 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[cfg(feature = "describe")] pub mod describe; +#[cfg(feature = "describe")] pub use describe::function::describe; /// diff --git a/gix-revision/tests/revision.rs b/gix-revision/tests/revision.rs index 71d63cb4057..00b6735c012 100644 --- a/gix-revision/tests/revision.rs +++ b/gix-revision/tests/revision.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "describe")] mod describe; mod spec; pub type Result = std::result::Result>; diff --git a/justfile b/justfile index da12522a9c5..7fe437b2f5d 100755 --- a/justfile +++ b/justfile @@ -77,6 +77,7 @@ check: cargo check -p gix-credentials --features serde cargo check -p gix-sec --features serde cargo check -p gix-revision --features serde + 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-features --all-features From c42064d8ca382513c944f3df5c08e4ff66d5f804 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 18:14:34 +0200 Subject: [PATCH 09/23] feat: add `revision` component behind a feature toggle. --- Cargo.lock | 1 + gix/Cargo.toml | 8 ++++++-- gix/src/commit.rs | 3 ++- gix/src/config/cache/init.rs | 10 ++++++++-- gix/src/config/cache/util.rs | 4 ++-- gix/src/config/mod.rs | 5 +++-- gix/src/config/tree/sections/core.rs | 3 +++ gix/src/ext/mod.rs | 2 ++ gix/src/id.rs | 7 +++---- gix/src/lib.rs | 1 + gix/src/object/commit.rs | 5 +++-- gix/src/remote/connection/fetch/negotiate.rs | 4 ++-- gix/src/repository/graph.rs | 6 +++--- gix/src/repository/mod.rs | 1 + gix/src/revision/mod.rs | 3 +++ gix/tests/commit/mod.rs | 1 + gix/tests/config/tree.rs | 7 +++---- gix/tests/gix-with-regex.rs | 1 + gix/tests/gix.rs | 1 + gix/tests/object/tree/mod.rs | 2 +- gix/tests/reference/mod.rs | 4 ++-- justfile | 1 + 22 files changed, 53 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6458d8165e5..7c8f97b1e89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1220,6 +1220,7 @@ dependencies = [ "gix-ref 0.35.0", "gix-refspec", "gix-revision", + "gix-revwalk", "gix-sec 0.9.0", "gix-submodule", "gix-tempfile 8.0.0", diff --git a/gix/Cargo.toml b/gix/Cargo.toml index a20fa03af5d..084ea006b0d 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -58,7 +58,7 @@ default = ["max-performance-safe", "comfort", "extras"] #! Bundles are for convenience only and bear no further meaning beyond the cargo manifest file. ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "blob-diff"] +extras = ["worktree-stream", "worktree-archive", "blob-diff", "revision"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -68,6 +68,9 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. #! Providers of libraries should only activate +## Make revspec parsing possible, as well describing revision. +revision = ["gix-revision/describe"] + ## Make it possible to diff blobs line by line. Note that this feature is integral for implementing tree-diffs as well due to the handling of rename-tracking, ## which relies on line-by-line diffs in some cases. blob-diff = ["gix-diff/blob"] @@ -187,7 +190,8 @@ gix-hash = { version = "^0.12.0", path = "../gix-hash" } gix-object = { version = "^0.35.0", path = "../gix-object" } gix-actor = { version = "^0.25.0", path = "../gix-actor" } gix-pack = { version = "^0.41.0", path = "../gix-pack", default-features = false, features = ["object-cache-dynamic"] } -gix-revision = { version = "^0.20.0", path = "../gix-revision" } +gix-revision = { version = "^0.20.0", path = "../gix-revision", default-features = false } +gix-revwalk = { version = "^0.6.0", path = "../gix-revwalk" } gix-negotiate = { version = "^0.6.0", path = "../gix-negotiate" } gix-path = { version = "^0.9.0", path = "../gix-path" } diff --git a/gix/src/commit.rs b/gix/src/commit.rs index 8b178443a39..2cc8226f5a4 100644 --- a/gix/src/commit.rs +++ b/gix/src/commit.rs @@ -22,6 +22,7 @@ pub enum Error { } /// +#[cfg(feature = "revision")] pub mod describe { use std::borrow::Cow; @@ -197,7 +198,7 @@ pub mod describe { /// to save ~40% of time. pub fn try_resolve(&self) -> Result>, Error> { // TODO: dirty suffix with respective dirty-detection - let mut graph = gix_revision::Graph::new( + let mut graph = gix_revwalk::Graph::new( |id, buf| { self.repo .objects diff --git a/gix/src/config/cache/init.rs b/gix/src/config/cache/init.rs index 6fb93b73dc1..3c482b154ee 100644 --- a/gix/src/config/cache/init.rs +++ b/gix/src/config/cache/init.rs @@ -151,6 +151,7 @@ impl Cache { true, lenient_config, )?; + #[cfg(feature = "revision")] let object_kind_hint = util::disambiguate_hint(&config, lenient_config)?; let (static_pack_cache_limit_bytes, pack_cache_bytes, object_cache_bytes) = util::parse_object_caches(&config, lenient_config, filter_config_section)?; @@ -159,6 +160,7 @@ impl Cache { resolved: config.into(), use_multi_pack_index, object_hash, + #[cfg(feature = "revision")] object_kind_hint, static_pack_cache_limit_bytes, pack_cache_bytes, @@ -213,12 +215,16 @@ impl Cache { false, self.lenient_config, )?; - let object_kind_hint = util::disambiguate_hint(config, self.lenient_config)?; + + #[cfg(feature = "revision")] + { + let object_kind_hint = util::disambiguate_hint(config, self.lenient_config)?; + self.object_kind_hint = object_kind_hint; + } let reflog = util::query_refupdates(config, self.lenient_config)?; self.hex_len = hex_len; self.ignore_case = ignore_case; - self.object_kind_hint = object_kind_hint; self.reflog = reflog; self.user_agent = Default::default(); diff --git a/gix/src/config/cache/util.rs b/gix/src/config/cache/util.rs index a7c72a77136..1db074e989b 100644 --- a/gix/src/config/cache/util.rs +++ b/gix/src/config/cache/util.rs @@ -3,7 +3,6 @@ use super::Error; use crate::{ config, config::tree::{gitoxide, Core}, - revision::spec::parse::ObjectKindHint, }; pub(crate) fn interpolate_context<'a>( @@ -103,10 +102,11 @@ pub(crate) fn parse_core_abbrev( .flatten()) } +#[cfg(feature = "revision")] pub(crate) fn disambiguate_hint( config: &gix_config::File<'static>, lenient_config: bool, -) -> Result, config::key::GenericErrorWithValue> { +) -> Result, config::key::GenericErrorWithValue> { match config.string_by_key("core.disambiguate") { None => Ok(None), Some(value) => Core::DISAMBIGUATE diff --git a/gix/src/config/mod.rs b/gix/src/config/mod.rs index 2a6b6f5515d..b6eee0fa5e3 100644 --- a/gix/src/config/mod.rs +++ b/gix/src/config/mod.rs @@ -1,7 +1,7 @@ pub use gix_config::*; use gix_features::threading::OnceCell; -use crate::{bstr::BString, repository::identity, revision::spec, Repository}; +use crate::{bstr::BString, repository::identity, Repository}; pub(crate) mod cache; mod snapshot; @@ -513,7 +513,8 @@ pub(crate) struct Cache { /// The config section filter from the options used to initialize this instance. Keep these in sync! filter_config_section: fn(&gix_config::file::Metadata) -> bool, /// The object kind to pick if a prefix is ambiguous. - pub object_kind_hint: Option, + #[cfg(feature = "revision")] + pub object_kind_hint: Option, /// If true, we are on a case-insensitive file system. pub ignore_case: bool, /// If true, we should default what's possible if something is misconfigured, on case by case basis, to be more resilient. diff --git a/gix/src/config/tree/sections/core.rs b/gix/src/config/tree/sections/core.rs index 7253140c102..25da3f3057a 100644 --- a/gix/src/config/tree/sections/core.rs +++ b/gix/src/config/tree/sections/core.rs @@ -270,6 +270,7 @@ mod autocrlf { } } +#[cfg(feature = "revision")] mod disambiguate { use std::borrow::Cow; @@ -425,7 +426,9 @@ mod validate { pub struct Disambiguate; impl keys::Validate for Disambiguate { + #[cfg_attr(not(feature = "revision"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { + #[cfg(feature = "revision")] super::Core::DISAMBIGUATE.try_into_object_kind_hint(value.into())?; Ok(()) } diff --git a/gix/src/ext/mod.rs b/gix/src/ext/mod.rs index 880ac7efe9b..ad69fec0715 100644 --- a/gix/src/ext/mod.rs +++ b/gix/src/ext/mod.rs @@ -1,9 +1,11 @@ pub use object_id::ObjectIdExt; pub use reference::ReferenceExt; +#[cfg(feature = "revision")] pub use rev_spec::RevSpecExt; pub use tree::{TreeEntryExt, TreeEntryRefExt, TreeIterExt}; mod object_id; mod reference; +#[cfg(feature = "revision")] mod rev_spec; mod tree; diff --git a/gix/src/id.rs b/gix/src/id.rs index 027b4f4d51e..7214ec320da 100644 --- a/gix/src/id.rs +++ b/gix/src/id.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use gix_hash::{oid, ObjectId}; -use crate::{object::find, revision, Id, Object}; +use crate::{object::find, Id, Object}; /// An [object id][ObjectId] infused with a [`Repository`][crate::Repository]. impl<'repo> Id<'repo> { @@ -103,9 +103,8 @@ impl<'repo> Id<'repo> { impl<'repo> Id<'repo> { /// Obtain a platform for traversing ancestors of this commit. - /// - pub fn ancestors(&self) -> revision::walk::Platform<'repo> { - revision::walk::Platform::new(Some(self.inner), self.repo) + pub fn ancestors(&self) -> crate::revision::walk::Platform<'repo> { + crate::revision::walk::Platform::new(Some(self.inner), self.repo) } } diff --git a/gix/src/lib.rs b/gix/src/lib.rs index ca6082f7c56..9d30ed69ba3 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -105,6 +105,7 @@ pub use gix_prompt as prompt; pub use gix_protocol as protocol; pub use gix_ref as refs; pub use gix_refspec as refspec; +pub use gix_revwalk as revwalk; pub use gix_sec as sec; pub use gix_tempfile as tempfile; pub use gix_trace as trace; diff --git a/gix/src/object/commit.rs b/gix/src/object/commit.rs index 967228736dd..1fb9eff6709 100644 --- a/gix/src/object/commit.rs +++ b/gix/src/object/commit.rs @@ -1,4 +1,4 @@ -use crate::{bstr, bstr::BStr, revision, Commit, ObjectDetached, Tree}; +use crate::{bstr, bstr::BStr, Commit, ObjectDetached, Tree}; mod error { use crate::object; @@ -131,12 +131,13 @@ impl<'repo> Commit<'repo> { } /// Obtain a platform for traversing ancestors of this commit. - pub fn ancestors(&self) -> revision::walk::Platform<'repo> { + pub fn ancestors(&self) -> crate::revision::walk::Platform<'repo> { self.id().ancestors() } /// Create a platform to further configure a `git describe` operation to find a name for this commit by looking /// at the closest annotated tags (by default) in its past. + #[cfg(feature = "revision")] pub fn describe(&self) -> crate::commit::describe::Platform<'repo> { crate::commit::describe::Platform { id: self.id, diff --git a/gix/src/remote/connection/fetch/negotiate.rs b/gix/src/remote/connection/fetch/negotiate.rs index 81d0745818c..92a141f6fa2 100644 --- a/gix/src/remote/connection/fetch/negotiate.rs +++ b/gix/src/remote/connection/fetch/negotiate.rs @@ -7,7 +7,7 @@ use gix_pack::Find; use crate::remote::{fetch, fetch::Shallow}; -type Queue = gix_revision::PriorityQueue; +type Queue = gix_revwalk::PriorityQueue; /// The error returned during negotiation. #[derive(Debug, thiserror::Error)] @@ -16,7 +16,7 @@ pub enum Error { #[error("We were unable to figure out what objects the server should send after {rounds} round(s)")] NegotiationFailed { rounds: usize }, #[error(transparent)] - LookupCommitInGraph(#[from] gix_revision::graph::lookup::commit::Error), + LookupCommitInGraph(#[from] gix_revwalk::graph::lookup::commit::Error), #[error(transparent)] InitRefsIterator(#[from] crate::reference::iter::init::Error), #[error(transparent)] diff --git a/gix/src/repository/graph.rs b/gix/src/repository/graph.rs index eae92aaea07..f4f2b18cc69 100644 --- a/gix/src/repository/graph.rs +++ b/gix/src/repository/graph.rs @@ -12,10 +12,10 @@ impl crate::Repository { /// /// ### Performance /// - /// Note that the [Graph][gix_revision::Graph] can be sensitive to various object database settings that may affect the performance + /// Note that the [Graph][gix_revwalk::Graph] can be sensitive to various object database settings that may affect the performance /// of the commit walk. - pub fn revision_graph(&self) -> gix_revision::Graph<'_, T> { - gix_revision::Graph::new( + pub fn revision_graph(&self) -> gix_revwalk::Graph<'_, T> { + gix_revwalk::Graph::new( |id, buf| { self.objects .try_find(id, buf) diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index 98d1e43fc11..77b93849491 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -52,6 +52,7 @@ mod object; mod pathspec; mod reference; mod remote; +#[cfg(feature = "revision")] mod revision; mod shallow; mod state; diff --git a/gix/src/revision/mod.rs b/gix/src/revision/mod.rs index 4b11a8766b6..3de528ecde9 100644 --- a/gix/src/revision/mod.rs +++ b/gix/src/revision/mod.rs @@ -2,6 +2,7 @@ //! //! This module provides utilities to walk graphs of revisions and specify revisions and ranges of revisions. +#[cfg(feature = "revision")] pub use gix_revision as plumbing; /// @@ -9,6 +10,7 @@ pub mod walk; pub use walk::iter::Walk; /// +#[cfg(feature = "revision")] pub mod spec; /// The specification of a revision as parsed from a revision specification like `HEAD@{1}` or `v1.2.3...main`. @@ -17,6 +19,7 @@ pub mod spec; /// See the [official git documentation](https://git-scm.com/docs/git-rev-parse#_specifying_revisions) for reference on how /// to specify revisions and revision ranges. #[derive(Clone, Debug)] +#[cfg(feature = "revision")] pub struct Spec<'repo> { pub(crate) inner: gix_revision::Spec, /// The first name of a reference as seen while parsing a `RevSpec`, for completeness. diff --git a/gix/tests/commit/mod.rs b/gix/tests/commit/mod.rs index 053e26eac2c..fd2440bf8ce 100644 --- a/gix/tests/commit/mod.rs +++ b/gix/tests/commit/mod.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "revision")] mod describe { use gix::commit::describe::SelectRef::{AllRefs, AllTags, AnnotatedTags}; diff --git a/gix/tests/config/tree.rs b/gix/tests/config/tree.rs index 79bd9624d41..be925a9e13f 100644 --- a/gix/tests/config/tree.rs +++ b/gix/tests/config/tree.rs @@ -272,10 +272,7 @@ mod diff { mod core { use std::time::Duration; - use gix::{ - config::tree::{Core, Key}, - revision::spec::parse::ObjectKindHint, - }; + use gix::config::tree::{Core, Key}; use gix_lock::acquire::Fail; use crate::config::tree::bcow; @@ -313,7 +310,9 @@ mod core { } #[test] + #[cfg(feature = "revision")] fn disambiguate() -> crate::Result { + use gix::revision::spec::parse::ObjectKindHint; for (value, expected) in [ ("none", None), ("commit", Some(ObjectKindHint::Commit)), diff --git a/gix/tests/gix-with-regex.rs b/gix/tests/gix-with-regex.rs index b7294b917cd..1683ce4a660 100644 --- a/gix/tests/gix-with-regex.rs +++ b/gix/tests/gix-with-regex.rs @@ -11,5 +11,6 @@ mod object; mod reference; mod remote; mod repository; +#[cfg(feature = "revision")] mod revision; mod submodule; diff --git a/gix/tests/gix.rs b/gix/tests/gix.rs index cc9804a768b..bd2393e6490 100644 --- a/gix/tests/gix.rs +++ b/gix/tests/gix.rs @@ -24,6 +24,7 @@ mod remote; #[cfg(not(feature = "regex"))] mod repository; #[cfg(not(feature = "regex"))] +#[cfg(feature = "revision")] mod revision; #[cfg(not(feature = "regex"))] mod submodule; diff --git a/gix/tests/object/tree/mod.rs b/gix/tests/object/tree/mod.rs index b72b3b47073..a331adb2fa4 100644 --- a/gix/tests/object/tree/mod.rs +++ b/gix/tests/object/tree/mod.rs @@ -1,6 +1,6 @@ use crate::util::{named_repo, named_subrepo_opts}; -#[cfg(feature = "blob-diff")] +#[cfg(all(feature = "blob-diff", feature = "revision"))] mod diff; #[test] diff --git a/gix/tests/reference/mod.rs b/gix/tests/reference/mod.rs index c4a4821f94c..d168819eb64 100644 --- a/gix/tests/reference/mod.rs +++ b/gix/tests/reference/mod.rs @@ -1,5 +1,3 @@ -use crate::repo_rw; - mod log { #[test] @@ -86,7 +84,9 @@ mod find { } #[test] +#[cfg(feature = "revision")] fn set_target_id() { + use crate::repo_rw; let (repo, _tmp) = repo_rw("make_basic_repo.sh").unwrap(); let mut head_ref = repo.head_ref().unwrap().expect("present"); let target_id = repo.rev_parse_single(":/c1").unwrap(); diff --git a/justfile b/justfile index 7fe437b2f5d..a8adc47350d 100755 --- a/justfile +++ b/justfile @@ -115,6 +115,7 @@ check: cargo check -p gix --no-default-features --features max-performance-safe cargo check -p gix --no-default-features --features progress-tree cargo check -p gix --no-default-features --features blob-diff + cargo check -p gix --no-default-features --features revision cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From 374dee66b5023ee94db9b332b511e1b6943b51b0 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 19:01:35 +0200 Subject: [PATCH 10/23] refactor --- gix-refspec/Cargo.toml | 2 +- gix-refspec/src/parse.rs | 12 ++---------- gix-refspec/tests/parse/invalid.rs | 8 ++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gix-refspec/Cargo.toml b/gix-refspec/Cargo.toml index 1d85f5ad203..f5122d2f4de 100644 --- a/gix-refspec/Cargo.toml +++ b/gix-refspec/Cargo.toml @@ -13,7 +13,7 @@ rust-version = "1.65" doctest = false [dependencies] -gix-revision = { version = "^0.20.0", path = "../gix-revision" } +gix-revision = { version = "^0.20.0", path = "../gix-revision", default-features = false } gix-validate = { version = "^0.8.0", path = "../gix-validate" } gix-hash = { version = "^0.12.0", path = "../gix-hash" } diff --git a/gix-refspec/src/parse.rs b/gix-refspec/src/parse.rs index bba5c69ad9b..e957eee5fad 100644 --- a/gix-refspec/src/parse.rs +++ b/gix-refspec/src/parse.rs @@ -173,16 +173,8 @@ pub(crate) mod function { .map_err(Error::from) .or_else(|err| { if allow_revspecs { - match gix_revision::spec::parse(spec, &mut super::revparse::Noop) { - Ok(_) => { - if spec.iter().any(u8::is_ascii_whitespace) { - Err(err) - } else { - Ok(spec) - } - } - Err(err) => Err(err.into()), - } + gix_revision::spec::parse(spec, &mut super::revparse::Noop)?; + Ok(spec) } else { Err(err) } diff --git a/gix-refspec/tests/parse/invalid.rs b/gix-refspec/tests/parse/invalid.rs index fec09aa96eb..c1235caae27 100644 --- a/gix-refspec/tests/parse/invalid.rs +++ b/gix-refspec/tests/parse/invalid.rs @@ -15,6 +15,14 @@ fn empty_component() { )); } +#[test] +fn whitespace() { + assert!(matches!( + try_parse("refs/heads/test:refs/remotes/ /test", Operation::Fetch).unwrap_err(), + Error::ReferenceName(gix_validate::reference::name::Error::Tag(_)) + )); +} + #[test] fn complex_patterns_with_more_than_one_asterisk() { for op in [Operation::Fetch, Operation::Push] { From 741b41e6e6c6f283c1632a7de0da44a5e7842817 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 20:52:45 +0200 Subject: [PATCH 11/23] fix!: remove `regex` feature in favor of `revparse-regex`. `revparse-regex` is only used when parsing revspecs that use a special syntax. This feature is also enabled by default. --- gix/Cargo.toml | 6 +++- .../revision/spec/parse/delegate/navigate.rs | 4 +-- gix/src/revision/spec/parse/types.rs | 10 +++---- gix/tests/gix.rs | 28 +++++++++---------- gix/tests/revision/spec/from_bytes/regex.rs | 8 +++--- justfile | 2 +- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 084ea006b0d..7fab098baf3 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -58,7 +58,7 @@ default = ["max-performance-safe", "comfort", "extras"] #! Bundles are for convenience only and bear no further meaning beyond the cargo manifest file. ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "blob-diff", "revision"] +extras = ["worktree-stream", "worktree-archive", "blob-diff", "revision", "revparse-regex"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -71,6 +71,10 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human ## Make revspec parsing possible, as well describing revision. revision = ["gix-revision/describe"] +## If enabled, revspecs now support the regex syntax like `@^{/^.*x}`. Otherwise, only substring search is supported. +## This feature does increase compile time for niche-benefit, but is required for fully git-compatible revspec parsing. +revparse-regex = ["regex", "revision"] + ## Make it possible to diff blobs line by line. Note that this feature is integral for implementing tree-diffs as well due to the handling of rename-tracking, ## which relies on line-by-line diffs in some cases. blob-diff = ["gix-diff/blob"] diff --git a/gix/src/revision/spec/parse/delegate/navigate.rs b/gix/src/revision/spec/parse/delegate/navigate.rs index 889b3584c40..51feb1d76f0 100644 --- a/gix/src/revision/spec/parse/delegate/navigate.rs +++ b/gix/src/revision/spec/parse/delegate/navigate.rs @@ -157,9 +157,9 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { self.unset_disambiguate_call(); self.follow_refs_to_objects_if_needed()?; - #[cfg(not(feature = "regex"))] + #[cfg(not(feature = "revparse-regex"))] let matches = |message: &BStr| -> bool { message.contains_str(regex) ^ negated }; - #[cfg(feature = "regex")] + #[cfg(feature = "revparse-regex")] let matches = match regex::bytes::Regex::new(regex.to_str_lossy().as_ref()) { Ok(compiled) => { let needs_regex = regex::escape(compiled.as_str()) != regex; diff --git a/gix/src/revision/spec/parse/types.rs b/gix/src/revision/spec/parse/types.rs index d852c297ea7..5dc626eee45 100644 --- a/gix/src/revision/spec/parse/types.rs +++ b/gix/src/revision/spec/parse/types.rs @@ -100,15 +100,15 @@ pub enum Error { RevWalkIterInit(#[from] crate::reference::iter::init::Error), #[error(transparent)] RevWalkAllReferences(#[from] gix_ref::packed::buffer::open::Error), - #[cfg(feature = "regex")] + #[cfg(feature = "revparse-regex")] #[error(transparent)] InvalidRegex(#[from] regex::Error), #[cfg_attr( - feature = "regex", + feature = "revparse-regex", error("None of {commits_searched} commits from {oid} matched regex {regex:?}") )] #[cfg_attr( - not(feature = "regex"), + not(feature = "revparse-regex"), error("None of {commits_searched} commits from {oid} matched text {regex:?}") )] NoRegexMatch { @@ -117,11 +117,11 @@ pub enum Error { commits_searched: usize, }, #[cfg_attr( - feature = "regex", + feature = "revparse-regex", error("None of {commits_searched} commits reached from all references matched regex {regex:?}") )] #[cfg_attr( - not(feature = "regex"), + not(feature = "revparse-regex"), error("None of {commits_searched} commits reached from all references matched text {regex:?}") )] NoRegexMatchAllRefs { regex: BString, commits_searched: usize }, diff --git a/gix/tests/gix.rs b/gix/tests/gix.rs index bd2393e6490..3502a4a6dfd 100644 --- a/gix/tests/gix.rs +++ b/gix/tests/gix.rs @@ -1,30 +1,30 @@ -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod util; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] use util::*; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod clone; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod commit; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod config; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod head; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod id; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod init; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod object; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod reference; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod remote; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod repository; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] #[cfg(feature = "revision")] mod revision; -#[cfg(not(feature = "regex"))] +#[cfg(not(feature = "revparse-regex"))] mod submodule; diff --git a/gix/tests/revision/spec/from_bytes/regex.rs b/gix/tests/revision/spec/from_bytes/regex.rs index a5739b0275f..fe7b4a5446d 100644 --- a/gix/tests/revision/spec/from_bytes/regex.rs +++ b/gix/tests/revision/spec/from_bytes/regex.rs @@ -12,7 +12,7 @@ mod with_known_revision { use crate::revision::spec::from_bytes::parse_spec; #[test] - #[cfg(not(feature = "regex"))] + #[cfg(not(feature = "revparse-regex"))] fn contained_string_matches_in_unanchored_regex_and_disambiguates_automatically() { let repo = repo("ambiguous_blob_tree_commit").unwrap(); let expected = Spec::from_id(hex_to_id("0000000000e4f9fbd19cf1e932319e5ad0d1d00b").attach(&repo)); @@ -28,7 +28,7 @@ mod with_known_revision { } #[test] - #[cfg(feature = "regex")] + #[cfg(feature = "revparse-regex")] fn contained_string_matches_in_unanchored_regex_and_disambiguates_automatically() { let repo = repo("ambiguous_blob_tree_commit").unwrap(); let expected = Spec::from_id(hex_to_id("0000000000e4f9fbd19cf1e932319e5ad0d1d00b").attach(&repo)); @@ -63,7 +63,7 @@ mod find_youngest_matching_commit { use crate::revision::spec::from_bytes::parse_spec; #[test] - #[cfg(not(feature = "regex"))] + #[cfg(not(feature = "revparse-regex"))] fn contained_string_matches() { let repo = repo("complex_graph").unwrap(); @@ -91,7 +91,7 @@ mod find_youngest_matching_commit { } #[test] - #[cfg(feature = "regex")] + #[cfg(feature = "revparse-regex")] fn regex_matches() { let repo = repo("complex_graph").unwrap(); diff --git a/justfile b/justfile index a8adc47350d..1929c0b84b2 100755 --- a/justfile +++ b/justfile @@ -116,6 +116,7 @@ check: cargo check -p gix --no-default-features --features progress-tree cargo check -p gix --no-default-features --features blob-diff cargo check -p gix --no-default-features --features revision + cargo check -p gix --no-default-features --features revparse-regex cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control @@ -158,7 +159,6 @@ unit-tests: cargo test -p gix --no-default-features cargo test -p gix --features async-network-client cargo test -p gix --features blocking-network-client - cargo test -p gix --features regex cargo test -p gitoxide-core --lib # These tests aren't run by default as they are flaky (even locally) From 799a5152c7ca444a8240a022e049c14b0b61d22d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 21:04:02 +0200 Subject: [PATCH 12/23] simplify test-suite --- gix/Cargo.toml | 10 ---------- gix/tests/gix-with-regex.rs | 16 ---------------- gix/tests/gix.rs | 14 -------------- 3 files changed, 40 deletions(-) delete mode 100644 gix/tests/gix-with-regex.rs diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 7fab098baf3..307d21f16b6 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -13,16 +13,6 @@ rust-version = "1.65" doctest = false test = true -[[test]] -name = "gix" -path = "tests/gix.rs" -required-features = [] - -[[test]] -name = "gix-with-regex" -path = "tests/gix-with-regex.rs" -required-features = ["regex"] - [[example]] name = "clone" path = "examples/clone.rs" diff --git a/gix/tests/gix-with-regex.rs b/gix/tests/gix-with-regex.rs deleted file mode 100644 index 1683ce4a660..00000000000 --- a/gix/tests/gix-with-regex.rs +++ /dev/null @@ -1,16 +0,0 @@ -mod util; -use util::*; - -mod clone; -mod commit; -mod config; -mod head; -mod id; -mod init; -mod object; -mod reference; -mod remote; -mod repository; -#[cfg(feature = "revision")] -mod revision; -mod submodule; diff --git a/gix/tests/gix.rs b/gix/tests/gix.rs index 3502a4a6dfd..1683ce4a660 100644 --- a/gix/tests/gix.rs +++ b/gix/tests/gix.rs @@ -1,30 +1,16 @@ -#[cfg(not(feature = "revparse-regex"))] mod util; -#[cfg(not(feature = "revparse-regex"))] use util::*; -#[cfg(not(feature = "revparse-regex"))] mod clone; -#[cfg(not(feature = "revparse-regex"))] mod commit; -#[cfg(not(feature = "revparse-regex"))] mod config; -#[cfg(not(feature = "revparse-regex"))] mod head; -#[cfg(not(feature = "revparse-regex"))] mod id; -#[cfg(not(feature = "revparse-regex"))] mod init; -#[cfg(not(feature = "revparse-regex"))] mod object; -#[cfg(not(feature = "revparse-regex"))] mod reference; -#[cfg(not(feature = "revparse-regex"))] mod remote; -#[cfg(not(feature = "revparse-regex"))] mod repository; -#[cfg(not(feature = "revparse-regex"))] #[cfg(feature = "revision")] mod revision; -#[cfg(not(feature = "revparse-regex"))] mod submodule; From c4ffde013a62a38a6f63c4a160bc3cdb6aafd65a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 09:12:20 +0200 Subject: [PATCH 13/23] feat: add `mailmap` feature --- gix/Cargo.toml | 14 ++++++++++---- gix/src/lib.rs | 1 + gix/src/repository/mod.rs | 1 + justfile | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 307d21f16b6..ecb147c2cee 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -20,7 +20,7 @@ required-features = ["blocking-network-client"] [features] -default = ["max-performance-safe", "comfort", "extras"] +default = ["max-performance-safe", "comfort", "basic", "extras"] #! There are various categories of features which help to optimize performance and build times. `gix` comes with 'batteries included' and everything is #! enabled as long as it doesn't sacrifice compatibility. Most users will be fine with that but will pay with higher compile times than necessary as they @@ -47,8 +47,11 @@ default = ["max-performance-safe", "comfort", "extras"] #! A bundle is a set of related feature toggles which can be activated with a single name that acts as a group. #! Bundles are for convenience only and bear no further meaning beyond the cargo manifest file. +## More fundamental components that most will be able to make good use of. +basic = ["blob-diff", "revision"] + ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "blob-diff", "revision", "revparse-regex"] +extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -58,6 +61,9 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. #! Providers of libraries should only activate +## Add support for mailmaps, as way of determining the final name of commmiters and authors. +mailmap = ["dep:gix-mailmap"] + ## Make revspec parsing possible, as well describing revision. revision = ["gix-revision/describe"] @@ -147,7 +153,7 @@ serde = [ "dep:serde", "gix-ref/serde", "gix-odb/serde", "gix-index/serde", - "gix-mailmap/serde", + "gix-mailmap?/serde", "gix-url/serde", "gix-attributes/serde", "gix-ignore/serde", @@ -192,7 +198,7 @@ gix-path = { version = "^0.9.0", path = "../gix-path" } gix-url = { version = "^0.22.0", path = "../gix-url" } gix-traverse = { version = "^0.31.0", path = "../gix-traverse" } gix-diff = { version = "^0.34.0", path = "../gix-diff", default-features = false } -gix-mailmap = { version = "^0.17.0", path = "../gix-mailmap" } +gix-mailmap = { version = "^0.17.0", path = "../gix-mailmap", optional = true } gix-features = { version = "^0.33.0", path = "../gix-features", features = ["progress", "once_cell"] } gix-trace = { version = "^0.1.3", path = "../gix-trace" } diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 9d30ed69ba3..c00c5d5b915 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -257,6 +257,7 @@ pub mod open; pub mod config; /// +#[cfg(feature = "mailmap")] pub mod mailmap; /// diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index 77b93849491..651b95e2e27 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -47,6 +47,7 @@ mod index; pub(crate) mod init; mod kind; mod location; +#[cfg(feature = "mailmap")] mod mailmap; mod object; mod pathspec; diff --git a/justfile b/justfile index 1929c0b84b2..8bf870b10a2 100755 --- a/justfile +++ b/justfile @@ -117,6 +117,7 @@ check: cargo check -p gix --no-default-features --features blob-diff cargo check -p gix --no-default-features --features revision cargo check -p gix --no-default-features --features revparse-regex + cargo check -p gix --no-default-features --features mailmap cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From 58b0e6f860d4d3b5548c18d3eae97141bc6dc377 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 09:38:55 +0200 Subject: [PATCH 14/23] feat!: Use `stack` abstraction in `Repository::excludes()`. This makes it easier to use. --- gix/src/repository/attributes.rs | 22 ++++++++++++---------- gix/src/worktree/mod.rs | 4 +++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/gix/src/repository/attributes.rs b/gix/src/repository/attributes.rs index 6a982ee625c..108f3b10662 100644 --- a/gix/src/repository/attributes.rs +++ b/gix/src/repository/attributes.rs @@ -103,14 +103,13 @@ impl Repository { /// /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use /// [`Repository::attributes()`] for accessing both attributes and excludes. - // TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries - // by non-relative path. + // TODO: test pub fn excludes( &self, index: &gix_index::State, overrides: Option, source: gix_worktree::stack::state::ignore::Source, - ) -> Result { + ) -> Result, config::exclude_stack::Error> { let case = if self.config.ignore_case { gix_glob::pattern::Case::Fold } else { @@ -122,13 +121,16 @@ impl Repository { .assemble_exclude_globals(self.git_dir(), overrides, source, &mut buf)?; let state = gix_worktree::stack::State::IgnoreStack(ignore); let attribute_list = state.id_mappings_from_index(index, index.path_backing(), case); - Ok(gix_worktree::Stack::new( - // this is alright as we don't cause mutation of that directory, it's virtual. - self.work_dir().unwrap_or(self.git_dir()), - state, - case, - buf, - attribute_list, + Ok(AttributeStack::new( + gix_worktree::Stack::new( + // this is alright as we don't cause mutation of that directory, it's virtual. + self.work_dir().unwrap_or(self.git_dir()), + state, + case, + buf, + attribute_list, + ), + self, )) } } diff --git a/gix/src/worktree/mod.rs b/gix/src/worktree/mod.rs index 8365b3c7098..76dc24722bc 100644 --- a/gix/src/worktree/mod.rs +++ b/gix/src/worktree/mod.rs @@ -112,6 +112,8 @@ pub mod open_index { /// pub mod excludes { + use crate::AttributeStack; + /// The error returned by [`Worktree::excludes()`][crate::Worktree::excludes()]. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] @@ -132,7 +134,7 @@ pub mod excludes { /// /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use /// [`Worktree::attributes()`][crate::Worktree::attributes()] for accessing both attributes and excludes. - pub fn excludes(&self, overrides: Option) -> Result { + pub fn excludes(&self, overrides: Option) -> Result, Error> { let index = self.index()?; Ok(self.parent.excludes( &index, From 477a1d9089d831cd9ed81f109fc2c89333026965 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 09:50:56 +0200 Subject: [PATCH 15/23] feat: add `attributes` feature to allow ignore-only stacks. --- gix-worktree/Cargo.toml | 7 +++++-- gix-worktree/src/stack/delegate.rs | 20 ++++++++++++++++---- gix-worktree/src/stack/mod.rs | 4 ++++ gix-worktree/src/stack/platform.rs | 1 + gix-worktree/src/stack/state/mod.rs | 15 ++++++++++++--- gix-worktree/tests/Cargo.toml | 2 +- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/gix-worktree/Cargo.toml b/gix-worktree/Cargo.toml index 8d66efef872..788aabc9226 100644 --- a/gix-worktree/Cargo.toml +++ b/gix-worktree/Cargo.toml @@ -14,8 +14,11 @@ autotests = false doctest = false [features] +default = ["attributes"] +## Instantiate stacks that can access `.gitattributes` information. +attributes = ["dep:gix-attributes"] ## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde = [ "dep:serde", "bstr/serde", "gix-index/serde", "gix-hash/serde", "gix-object/serde", "gix-attributes/serde", "gix-ignore/serde" ] +serde = [ "dep:serde", "bstr/serde", "gix-index/serde", "gix-hash/serde", "gix-object/serde", "gix-attributes?/serde", "gix-ignore/serde" ] [dependencies] gix-index = { version = "^0.23.1", path = "../gix-index" } @@ -24,7 +27,7 @@ gix-hash = { version = "^0.12.0", path = "../gix-hash" } gix-object = { version = "^0.35.0", path = "../gix-object" } gix-glob = { version = "^0.11.0", path = "../gix-glob" } gix-path = { version = "^0.9.0", path = "../gix-path" } -gix-attributes = { version = "^0.17.0", path = "../gix-attributes" } +gix-attributes = { version = "^0.17.0", path = "../gix-attributes", optional = true } gix-ignore = { version = "^0.6.0", path = "../gix-ignore" } gix-features = { version = "^0.33.0", path = "../gix-features" } diff --git a/gix-worktree/src/stack/delegate.rs b/gix-worktree/src/stack/delegate.rs index 2e407db17b9..28d8ecf348d 100644 --- a/gix-worktree/src/stack/delegate.rs +++ b/gix-worktree/src/stack/delegate.rs @@ -27,6 +27,7 @@ pub(crate) type FindFn<'a> = dyn for<'b> FnMut( pub(crate) struct StackDelegate<'a, 'find> { pub state: &'a mut State, pub buf: &'a mut Vec, + #[cfg_attr(not(feature = "attributes"), allow(dead_code))] pub is_dir: bool, pub id_mappings: &'a Vec, pub find: &'find mut FindFn<'find>, @@ -54,6 +55,7 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { rela_dir_cow.as_ref() }; match &mut self.state { + #[cfg(feature = "attributes")] State::CreateDirectoryAndAttributesStack { attributes, .. } => { attributes.push_directory( stack.root(), @@ -65,6 +67,7 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { &mut self.statistics.attributes, )?; } + #[cfg(feature = "attributes")] State::AttributesAndIgnoreStack { ignore, attributes } => { attributes.push_directory( stack.root(), @@ -86,6 +89,7 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { &mut self.statistics.ignore, )? } + #[cfg(feature = "attributes")] State::AttributesStack(attributes) => attributes.push_directory( stack.root(), stack.current(), @@ -109,9 +113,11 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { Ok(()) } + #[cfg_attr(not(feature = "attributes"), allow(unused_variables))] fn push(&mut self, is_last_component: bool, stack: &gix_fs::Stack) -> std::io::Result<()> { self.statistics.delegate.push_element += 1; match &mut self.state { + #[cfg(feature = "attributes")] State::CreateDirectoryAndAttributesStack { unlink_on_collision, attributes: _, @@ -122,7 +128,9 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { &mut self.statistics.delegate.num_mkdir_calls, *unlink_on_collision, )?, - State::AttributesAndIgnoreStack { .. } | State::IgnoreStack(_) | State::AttributesStack(_) => {} + #[cfg(feature = "attributes")] + State::AttributesAndIgnoreStack { .. } | State::AttributesStack(_) => {} + State::IgnoreStack(_) => {} } Ok(()) } @@ -130,23 +138,27 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { fn pop_directory(&mut self) { self.statistics.delegate.pop_directory += 1; match &mut self.state { + #[cfg(feature = "attributes")] State::CreateDirectoryAndAttributesStack { attributes, .. } => { attributes.pop_directory(); } + #[cfg(feature = "attributes")] State::AttributesAndIgnoreStack { attributes, ignore } => { attributes.pop_directory(); ignore.pop_directory(); } - State::IgnoreStack(ignore) => { - ignore.pop_directory(); - } + #[cfg(feature = "attributes")] State::AttributesStack(attributes) => { attributes.pop_directory(); } + State::IgnoreStack(ignore) => { + ignore.pop_directory(); + } } } } +#[cfg(feature = "attributes")] fn create_leading_directory( is_last_component: bool, stack: &gix_fs::Stack, diff --git a/gix-worktree/src/stack/mod.rs b/gix-worktree/src/stack/mod.rs index a2f84960789..c103201996f 100644 --- a/gix-worktree/src/stack/mod.rs +++ b/gix-worktree/src/stack/mod.rs @@ -16,6 +16,7 @@ pub struct Statistics { /// Information about the stack delegate. pub delegate: delegate::Statistics, /// Information about attributes + #[cfg(feature = "attributes")] pub attributes: state::attributes::Statistics, /// Information about the ignore stack pub ignore: state::ignore::Statistics, @@ -24,6 +25,7 @@ pub struct Statistics { #[derive(Clone)] pub enum State { /// Useful for checkout where directories need creation, but we need to access attributes as well. + #[cfg(feature = "attributes")] CreateDirectoryAndAttributesStack { /// If there is a symlink or a file in our path, try to unlink it before creating the directory. unlink_on_collision: bool, @@ -31,6 +33,7 @@ pub enum State { attributes: state::Attributes, }, /// Used when adding files, requiring access to both attributes and ignore information, for example during add operations. + #[cfg(feature = "attributes")] AttributesAndIgnoreStack { /// State to handle attribute information attributes: state::Attributes, @@ -38,6 +41,7 @@ pub enum State { ignore: state::Ignore, }, /// Used when only attributes are required, typically with fully virtual worktrees. + #[cfg(feature = "attributes")] AttributesStack(state::Attributes), /// Used when providing worktree status information. IgnoreStack(state::Ignore), diff --git a/gix-worktree/src/stack/platform.rs b/gix-worktree/src/stack/platform.rs index 8d29566e8bb..3c6295f8975 100644 --- a/gix-worktree/src/stack/platform.rs +++ b/gix-worktree/src/stack/platform.rs @@ -40,6 +40,7 @@ impl<'a> Platform<'a> { /// # Panics /// /// If the cache was configured without attributes. + #[cfg(feature = "attributes")] pub fn matching_attributes(&self, out: &mut gix_attributes::search::Outcome) -> bool { let attrs = self.parent.state.attributes_or_panic(); let relative_path = diff --git a/gix-worktree/src/stack/state/mod.rs b/gix-worktree/src/stack/state/mod.rs index f353e13c42d..0b371425acd 100644 --- a/gix-worktree/src/stack/state/mod.rs +++ b/gix-worktree/src/stack/state/mod.rs @@ -1,15 +1,15 @@ -use std::path::PathBuf; - use bstr::{BString, ByteSlice}; use gix_glob::pattern::Case; use crate::{stack::State, PathIdMapping}; +#[cfg(feature = "attributes")] type AttributeMatchGroup = gix_attributes::Search; type IgnoreMatchGroup = gix_ignore::Search; /// State related to attributes associated with files in the repository. #[derive(Default, Clone)] +#[cfg(feature = "attributes")] pub struct Attributes { /// Attribute patterns which aren't tied to the repository root, hence are global, they contribute first. globals: AttributeMatchGroup, @@ -20,7 +20,7 @@ pub struct Attributes { stack: AttributeMatchGroup, /// The first time we push the root, we have to load additional information from this file if it exists along with the root attributes /// file if possible, and keep them there throughout. - info_attributes: Option, + info_attributes: Option, /// A lookup table to accelerate searches. collection: gix_attributes::search::MetadataCollection, /// Where to read `.gitattributes` data from. @@ -50,6 +50,7 @@ pub struct Ignore { } /// +#[cfg(feature = "attributes")] pub mod attributes; /// pub mod ignore; @@ -57,6 +58,7 @@ pub mod ignore; /// Initialization impl State { /// Configure a state to be suitable for checking out files, which only needs access to attribute files read from the index. + #[cfg(feature = "attributes")] pub fn for_checkout(unlink_on_collision: bool, attributes: Attributes) -> Self { State::CreateDirectoryAndAttributesStack { unlink_on_collision, @@ -65,6 +67,7 @@ impl State { } /// Configure a state for adding files, with support for ignore files and attribute files. + #[cfg(feature = "attributes")] pub fn for_add(attributes: Attributes, ignore: Ignore) -> Self { State::AttributesAndIgnoreStack { attributes, ignore } } @@ -96,6 +99,7 @@ impl State { case: Case, ) -> Vec { let a1_backing; + #[cfg(feature = "attributes")] let a2_backing; let names = match self { State::IgnoreStack(ignore) => { @@ -105,6 +109,7 @@ impl State { )]; a1_backing.as_ref() } + #[cfg(feature = "attributes")] State::AttributesAndIgnoreStack { ignore, .. } => { a2_backing = [ ( @@ -115,6 +120,7 @@ impl State { ]; a2_backing.as_ref() } + #[cfg(feature = "attributes")] State::CreateDirectoryAndAttributesStack { .. } | State::AttributesStack(_) => { a1_backing = [(".gitattributes".into(), None)]; a1_backing.as_ref() @@ -160,13 +166,16 @@ impl State { pub(crate) fn ignore_or_panic(&self) -> &Ignore { match self { State::IgnoreStack(v) => v, + #[cfg(feature = "attributes")] State::AttributesAndIgnoreStack { ignore, .. } => ignore, + #[cfg(feature = "attributes")] State::AttributesStack(_) | State::CreateDirectoryAndAttributesStack { .. } => { unreachable!("BUG: must not try to check excludes without it being setup") } } } + #[cfg(feature = "attributes")] pub(crate) fn attributes_or_panic(&self) -> &Attributes { match self { State::AttributesStack(attributes) diff --git a/gix-worktree/tests/Cargo.toml b/gix-worktree/tests/Cargo.toml index f70dca8205a..227ee3e36b4 100644 --- a/gix-worktree/tests/Cargo.toml +++ b/gix-worktree/tests/Cargo.toml @@ -17,7 +17,7 @@ path = "integrate.rs" gix-features-parallel = ["gix-features/parallel"] [dev-dependencies] -gix-worktree = { path = ".." } +gix-worktree = { path = "..", features = ["attributes"] } gix-index = { path = "../../gix-index" } gix-fs = { path = "../../gix-fs" } gix-hash = { path = "../../gix-hash" } From d7fc182156847752ee872016b6de0c78f5fb190b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 10:00:07 +0200 Subject: [PATCH 16/23] adapt to changes in `gix-worktree` --- gix-archive/Cargo.toml | 2 +- gix-filter/Cargo.toml | 2 +- gix-worktree-state/Cargo.toml | 2 +- gix-worktree-stream/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gix-archive/Cargo.toml b/gix-archive/Cargo.toml index f63bc1532b8..ba1d135522d 100644 --- a/gix-archive/Cargo.toml +++ b/gix-archive/Cargo.toml @@ -43,7 +43,7 @@ document-features = { version = "0.2.0", optional = true } [dev-dependencies] gix-testtools = { path = "../tests/tools"} gix-odb = { path = "../gix-odb"} -gix-worktree = { path = "../gix-worktree"} +gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"]} gix-hash = { path = "../gix-hash"} gix-attributes = { path = "../gix-attributes"} gix-object = { path = "../gix-object"} diff --git a/gix-filter/Cargo.toml b/gix-filter/Cargo.toml index 9e2007e35a1..080c547f194 100644 --- a/gix-filter/Cargo.toml +++ b/gix-filter/Cargo.toml @@ -30,4 +30,4 @@ smallvec = "1.10.0" [dev-dependencies] once_cell = "1.18.0" gix-testtools = { path = "../tests/tools" } -gix-worktree = { path = "../gix-worktree" } +gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"] } diff --git a/gix-worktree-state/Cargo.toml b/gix-worktree-state/Cargo.toml index 72f5d75f9cf..602a1564e11 100644 --- a/gix-worktree-state/Cargo.toml +++ b/gix-worktree-state/Cargo.toml @@ -14,7 +14,7 @@ autotests = false doctest = false [dependencies] -gix-worktree = { version = "^0.25.0", path = "../gix-worktree" } +gix-worktree = { version = "^0.25.0", path = "../gix-worktree", default-features = false, features = ["attributes"] } gix-index = { version = "^0.23.1", path = "../gix-index" } gix-fs = { version = "^0.5.0", path = "../gix-fs" } gix-hash = { version = "^0.12.0", path = "../gix-hash" } diff --git a/gix-worktree-stream/Cargo.toml b/gix-worktree-stream/Cargo.toml index 0bb01a4e88c..21ebb8eb17f 100644 --- a/gix-worktree-stream/Cargo.toml +++ b/gix-worktree-stream/Cargo.toml @@ -27,4 +27,4 @@ parking_lot = "0.12.1" [dev-dependencies] gix-testtools = { path = "../tests/tools"} gix-odb = { path = "../gix-odb"} -gix-worktree = { path = "../gix-worktree"} +gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"]} From 92dd18154b526b4c5132d770960a36ccf739dec8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 10:07:44 +0200 Subject: [PATCH 17/23] feat: add `excludes` feature to make exclude-checks possible. --- gix/Cargo.toml | 41 +++-- gix/src/clone/fetch/mod.rs | 2 +- gix/src/clone/mod.rs | 2 + gix/src/config/cache/access.rs | 24 ++- gix/src/config/mod.rs | 2 + gix/src/config/tree/sections/core.rs | 250 ++++++++++++++------------ gix/src/config/tree/sections/fetch.rs | 24 ++- gix/src/lib.rs | 13 +- gix/src/open/repository.rs | 1 + gix/src/repository/attributes.rs | 3 + gix/src/repository/impls.rs | 4 + gix/src/repository/init.rs | 3 +- gix/src/repository/location.rs | 1 + gix/src/repository/mod.rs | 5 + gix/src/types.rs | 13 +- gix/src/worktree/mod.rs | 5 + gix/tests/config/tree.rs | 7 +- gix/tests/gix.rs | 1 + gix/tests/repository/mod.rs | 3 + justfile | 20 ++- 20 files changed, 262 insertions(+), 162 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index ecb147c2cee..3de57ce22c3 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -51,7 +51,7 @@ default = ["max-performance-safe", "comfort", "basic", "extras"] basic = ["blob-diff", "revision"] ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap"] +extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -59,7 +59,16 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! #### Components #! #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. -#! Providers of libraries should only activate +#! Providers of libraries should only activate the components they need. + +## Various ways to alter the worktree makeup by checkout and reset. +worktree-mutation = ["attributes", "dep:gix-worktree-state"] + +## Retrieve a worktree stack for querying exclude information +excludes = ["dep:gix-ignore", "dep:gix-worktree"] + +## Query attributes and excludes. Enables access to pathspecs, worktree checkouts, filter-pipelines and submodules. +attributes = ["excludes", "dep:gix-filter", "dep:gix-pathspec", "dep:gix-attributes", "dep:gix-submodule", "gix-worktree?/attributes"] ## Add support for mailmaps, as way of determining the final name of commmiters and authors. mailmap = ["dep:gix-mailmap"] @@ -76,13 +85,13 @@ revparse-regex = ["regex", "revision"] blob-diff = ["gix-diff/blob"] ## Make it possible to turn a tree into a stream of bytes, which can be decoded to entries and turned into various other formats. -worktree-stream = ["gix-worktree-stream"] +worktree-stream = ["gix-worktree-stream", "attributes"] ## Create archives from a tree in the repository, similar to what `git archive` does. ## ## Note that we disable all default features which strips it off all container support, like `tar` and `zip`. ## Your application should add it as dependency and re-activate the desired features. -worktree-archive = ["gix-archive", "worktree-stream"] +worktree-archive = ["gix-archive", "worktree-stream", "attributes"] #! #### Mutually Exclusive Network Client #! @@ -91,11 +100,11 @@ worktree-archive = ["gix-archive", "worktree-stream"] #! Making a choice here also affects which crypto-library ends up being used. ## Make `gix-protocol` available along with an async client. -async-network-client = ["gix-protocol/async-client", "gix-pack/streaming-input"] +async-network-client = ["gix-protocol/async-client", "gix-pack/streaming-input", "attributes"] ## Use this if your crate uses `async-std` as runtime, and enable basic runtime integration when connecting to remote servers via the `git://` protocol. async-network-client-async-std = ["async-std", "async-network-client", "gix-transport/async-std"] ## Make `gix-protocol` available along with a blocking client, providing access to the `file://`, git://` and `ssh://` transports. -blocking-network-client = ["gix-protocol/blocking-client", "gix-pack/streaming-input"] +blocking-network-client = ["gix-protocol/blocking-client", "gix-pack/streaming-input", "attributes"] ## Stacks with `blocking-network-client` to provide support for HTTP/S using **curl**, and implies blocking networking as a whole, making the `https://` transport avaialble. blocking-http-transport-curl = ["blocking-network-client", "gix-transport/http-client-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 avaialble. @@ -155,10 +164,10 @@ serde = [ "dep:serde", "gix-index/serde", "gix-mailmap?/serde", "gix-url/serde", - "gix-attributes/serde", - "gix-ignore/serde", + "gix-attributes?/serde", + "gix-ignore?/serde", "gix-revision/serde", - "gix-worktree/serde", + "gix-worktree?/serde", "gix-commitgraph/serde", "gix-credentials/serde"] @@ -182,7 +191,7 @@ gix-validate = { version = "^0.8.0", path = "../gix-validate" } gix-sec = { version = "^0.9.0", path = "../gix-sec" } gix-date = { version = "^0.7.4", path = "../gix-date" } gix-refspec = { version = "^0.16.0", path = "../gix-refspec" } -gix-filter = { version = "^0.3.0", path = "../gix-filter" } +gix-filter = { version = "^0.3.0", path = "../gix-filter", optional = true } gix-config = { version = "^0.28.0", path = "../gix-config" } gix-odb = { version = "^0.51.0", path = "../gix-odb" } @@ -202,18 +211,18 @@ gix-mailmap = { version = "^0.17.0", path = "../gix-mailmap", optional = true } gix-features = { version = "^0.33.0", path = "../gix-features", features = ["progress", "once_cell"] } gix-trace = { version = "^0.1.3", path = "../gix-trace" } -gix-attributes = { version = "^0.17.0", path = "../gix-attributes" } -gix-ignore = { version = "^0.6.0", path = "../gix-ignore" } gix-glob = { version = "^0.11.0", path = "../gix-glob" } gix-credentials = { version = "^0.18.0", path = "../gix-credentials" } gix-prompt = { version = "^0.6.0", path = "../gix-prompt" } gix-index = { version = "^0.23.1", path = "../gix-index" } -gix-worktree = { version = "^0.25.0", path = "../gix-worktree" } -gix-worktree-state = { version = "^0.2.0", path = "../gix-worktree-state" } +gix-attributes = { version = "^0.17.0", path = "../gix-attributes", optional = true } +gix-ignore = { version = "^0.6.0", path = "../gix-ignore", optional = true } +gix-worktree = { version = "^0.25.0", path = "../gix-worktree", optional = true, default-features = false } +gix-worktree-state = { version = "^0.2.0", path = "../gix-worktree-state", optional = true } gix-hashtable = { version = "^0.3.0", path = "../gix-hashtable" } gix-commitgraph = { version = "^0.19.0", path = "../gix-commitgraph" } -gix-pathspec = { version = "^0.1.0", path = "../gix-pathspec" } -gix-submodule = { version = "^0.2.0", path = "../gix-submodule" } +gix-pathspec = { version = "^0.1.0", path = "../gix-pathspec", optional = true } +gix-submodule = { version = "^0.2.0", path = "../gix-submodule", optional = true } gix-worktree-stream = { version = "^0.3.0", path = "../gix-worktree-stream", optional = true } gix-archive = { version = "^0.3.0", path = "../gix-archive", default-features = false, optional = true } diff --git a/gix/src/clone/fetch/mod.rs b/gix/src/clone/fetch/mod.rs index 408fcca29cb..2a18efcbc97 100644 --- a/gix/src/clone/fetch/mod.rs +++ b/gix/src/clone/fetch/mod.rs @@ -149,7 +149,7 @@ impl PrepareFetch { } /// Similar to [`fetch_only()`][Self::fetch_only()`], but passes ownership to a utility type to configure a checkout operation. - #[cfg(feature = "blocking-network-client")] + #[cfg(all(feature = "worktree-mutation", feature = "blocking-network-client"))] pub fn fetch_then_checkout

( &mut self, progress: P, diff --git a/gix/src/clone/mod.rs b/gix/src/clone/mod.rs index 6f7ef0d888d..fb5eb418d5c 100644 --- a/gix/src/clone/mod.rs +++ b/gix/src/clone/mod.rs @@ -122,6 +122,7 @@ impl PrepareFetch { /// A utility to collect configuration on how to perform a checkout into a working tree, and when dropped without checking out successfully /// the fetched repository will be dropped. #[must_use] +#[cfg(feature = "worktree-mutation")] pub struct PrepareCheckout { /// A freshly initialized repository which is owned by us, or `None` if it was handed to the user pub(self) repo: Option, @@ -165,4 +166,5 @@ mod access_feat { pub mod fetch; /// +#[cfg(feature = "worktree-mutation")] pub mod checkout; diff --git a/gix/src/config/cache/access.rs b/gix/src/config/cache/access.rs index 3bc39169fb8..820dd51ff46 100644 --- a/gix/src/config/cache/access.rs +++ b/gix/src/config/cache/access.rs @@ -1,7 +1,6 @@ #![allow(clippy::result_large_err)] use std::{borrow::Cow, path::PathBuf, time::Duration}; -use gix_attributes::Source; use gix_lock::acquire::Fail; use crate::{ @@ -10,13 +9,11 @@ use crate::{ config::{ boolean, cache::util::{ApplyLeniency, ApplyLeniencyDefaultValue}, - checkout_options, - tree::{gitoxide, Checkout, Core, Key}, + tree::{Core, Key}, Cache, }, - filter, remote, + remote, repository::identity, - Repository, }; /// Access @@ -116,6 +113,7 @@ impl Cache { } /// The path to the user-level excludes file to ignore certain files in the worktree. + #[cfg(feature = "excludes")] pub(crate) fn excludes_file(&self) -> Option> { self.trusted_file_path("core", None, Core::EXCLUDES_FILE.name)? .map(std::borrow::Cow::into_owned) @@ -159,21 +157,23 @@ impl Cache { /// Collect everything needed to checkout files into a worktree. /// Note that some of the options being returned will be defaulted so safe settings, the caller might have to override them /// depending on the use-case. + #[cfg(feature = "worktree-mutation")] pub(crate) fn checkout_options( &self, - repo: &Repository, + repo: &crate::Repository, attributes_source: gix_worktree::stack::state::attributes::Source, - ) -> Result { + ) -> Result { + use crate::config::tree::gitoxide; let git_dir = repo.git_dir(); let thread_limit = self.apply_leniency( self.resolved .integer_filter_by_key("checkout.workers", &mut self.filter_config_section.clone()) - .map(|value| Checkout::WORKERS.try_from_workers(value)), + .map(|value| crate::config::tree::Checkout::WORKERS.try_from_workers(value)), )?; let capabilities = self.fs_capabilities()?; let filters = { let collection = Default::default(); - let mut filters = gix_filter::Pipeline::new(&collection, filter::Pipeline::options(repo)?); + let mut filters = gix_filter::Pipeline::new(&collection, crate::filter::Pipeline::options(repo)?); if let Ok(mut head) = repo.head() { let ctx = filters.driver_context_mut(); ctx.ref_name = head.referent_name().map(|name| name.as_bstr().to_owned()); @@ -217,6 +217,7 @@ impl Cache { }) } + #[cfg(feature = "excludes")] pub(crate) fn assemble_exclude_globals( &self, git_dir: &std::path::Path, @@ -236,12 +237,14 @@ impl Cache { )) } // TODO: at least one test, maybe related to core.attributesFile configuration. + #[cfg(feature = "attributes")] pub(crate) fn assemble_attribute_globals( &self, git_dir: &std::path::Path, source: gix_worktree::stack::state::attributes::Source, attributes: crate::open::permissions::Attributes, ) -> Result<(gix_worktree::stack::state::Attributes, Vec), config::attribute_stack::Error> { + use gix_attributes::Source; let configured_or_user_attributes = match self .trusted_file_path("core", None, Core::ATTRIBUTES_FILE.name) .transpose()? @@ -276,9 +279,11 @@ impl Cache { Ok((state, buf)) } + #[cfg(feature = "attributes")] pub(crate) fn pathspec_defaults( &self, ) -> Result { + use crate::config::tree::gitoxide; let res = gix_pathspec::Defaults::from_environment(&mut |name| { let key = [ &gitoxide::Pathspec::ICASE, @@ -303,6 +308,7 @@ impl Cache { } } + #[cfg(any(feature = "attributes", feature = "excludes"))] pub(crate) fn xdg_config_path( &self, resource_file_name: &str, diff --git a/gix/src/config/mod.rs b/gix/src/config/mod.rs index b6eee0fa5e3..76da686af3c 100644 --- a/gix/src/config/mod.rs +++ b/gix/src/config/mod.rs @@ -119,6 +119,7 @@ pub mod diff { } /// +#[cfg(feature = "attributes")] pub mod checkout_options { /// The error produced when collecting all information needed for checking out files into a worktree. #[derive(Debug, thiserror::Error)] @@ -520,6 +521,7 @@ pub(crate) struct Cache { /// If true, we should default what's possible if something is misconfigured, on case by case basis, to be more resilient. /// Also available in options! Keep in sync! pub lenient_config: bool, + #[cfg_attr(not(feature = "worktree-mutation"), allow(dead_code))] attributes: crate::open::permissions::Attributes, environment: crate::open::permissions::Environment, // TODO: make core.precomposeUnicode available as well. diff --git a/gix/src/config/tree/sections/core.rs b/gix/src/config/tree/sections/core.rs index 25da3f3057a..2ec5c279ea3 100644 --- a/gix/src/config/tree/sections/core.rs +++ b/gix/src/config/tree/sections/core.rs @@ -69,12 +69,16 @@ impl Core { /// The `core.commitGraph` key. pub const COMMIT_GRAPH: keys::Boolean = keys::Boolean::new_boolean("commitGraph", &config::Tree::CORE); /// The `core.safecrlf` key. + #[cfg(feature = "attributes")] pub const SAFE_CRLF: SafeCrlf = SafeCrlf::new_with_validate("safecrlf", &config::Tree::CORE, validate::SafeCrlf); /// The `core.autocrlf` key. + #[cfg(feature = "attributes")] pub const AUTO_CRLF: AutoCrlf = AutoCrlf::new_with_validate("autocrlf", &config::Tree::CORE, validate::AutoCrlf); /// The `core.eol` key. + #[cfg(feature = "attributes")] pub const EOL: Eol = Eol::new_with_validate("eol", &config::Tree::CORE, validate::Eol); /// The `core.checkRoundTripEncoding` key. + #[cfg(feature = "attributes")] pub const CHECK_ROUND_TRIP_ENCODING: CheckRoundTripEncoding = CheckRoundTripEncoding::new_with_validate( "checkRoundTripEncoding", &config::Tree::CORE, @@ -111,9 +115,13 @@ impl Section for Core { &Self::SSH_COMMAND, &Self::USE_REPLACE_REFS, &Self::COMMIT_GRAPH, + #[cfg(feature = "attributes")] &Self::SAFE_CRLF, + #[cfg(feature = "attributes")] &Self::AUTO_CRLF, + #[cfg(feature = "attributes")] &Self::EOL, + #[cfg(feature = "attributes")] &Self::CHECK_ROUND_TRIP_ENCODING, ] } @@ -131,144 +139,152 @@ pub type LogAllRefUpdates = keys::Any; /// The `core.disambiguate` key. pub type Disambiguate = keys::Any; -/// The `core.safecrlf` key. -pub type SafeCrlf = keys::Any; +#[cfg(feature = "attributes")] +mod filter { + use super::validate; + use crate::config::tree::keys; -/// The `core.autocrlf` key. -pub type AutoCrlf = keys::Any; - -/// The `core.eol` key. -pub type Eol = keys::Any; - -/// The `core.checkRoundTripEncoding` key. -pub type CheckRoundTripEncoding = keys::Any; + /// The `core.safecrlf` key. + pub type SafeCrlf = keys::Any; -mod check_round_trip_encoding { - use std::borrow::Cow; + /// The `core.autocrlf` key. + pub type AutoCrlf = keys::Any; - use crate::{ - bstr::{BStr, ByteSlice}, - config, - config::tree::{core::CheckRoundTripEncoding, Key}, - }; + /// The `core.eol` key. + pub type Eol = keys::Any; - impl CheckRoundTripEncoding { - /// Convert `value` into a list of encodings, which are either space or coma separated. Fail if an encoding is unknown. - /// If `None`, the default is returned. - pub fn try_into_encodings( - &'static self, - value: Option>, - ) -> Result, config::encoding::Error> { - Ok(match value { - None => vec![gix_filter::encoding::SHIFT_JIS], - Some(value) => { - let mut out = Vec::new(); - for encoding in value - .as_ref() - .split(|b| *b == b',' || *b == b' ') - .filter(|e| !e.trim().is_empty()) - { - out.push( - gix_filter::encoding::Encoding::for_label(encoding.trim()).ok_or_else(|| { - config::encoding::Error { - key: self.logical_name().into(), - value: value.as_ref().to_owned(), - encoding: encoding.into(), - } - })?, - ); + /// The `core.checkRoundTripEncoding` key. + pub type CheckRoundTripEncoding = keys::Any; + + mod check_round_trip_encoding { + use std::borrow::Cow; + + use crate::{ + bstr::{BStr, ByteSlice}, + config, + config::tree::{core::CheckRoundTripEncoding, Key}, + }; + + impl CheckRoundTripEncoding { + /// Convert `value` into a list of encodings, which are either space or coma separated. Fail if an encoding is unknown. + /// If `None`, the default is returned. + pub fn try_into_encodings( + &'static self, + value: Option>, + ) -> Result, config::encoding::Error> { + Ok(match value { + None => vec![gix_filter::encoding::SHIFT_JIS], + Some(value) => { + let mut out = Vec::new(); + for encoding in value + .as_ref() + .split(|b| *b == b',' || *b == b' ') + .filter(|e| !e.trim().is_empty()) + { + out.push( + gix_filter::encoding::Encoding::for_label(encoding.trim()).ok_or_else(|| { + config::encoding::Error { + key: self.logical_name().into(), + value: value.as_ref().to_owned(), + encoding: encoding.into(), + } + })?, + ); + } + out } - out - } - }) + }) + } } } -} - -mod eol { - use std::borrow::Cow; - - use crate::{ - bstr::{BStr, ByteSlice}, - config, - config::tree::core::Eol, - }; - impl Eol { - /// Convert `value` into the default end-of-line mode. - /// - /// ### Deviation - /// - /// git will allow any value and silently leaves it unset, we will fail if the value is not known. - pub fn try_into_eol( - &'static self, - value: Cow<'_, BStr>, - ) -> Result { - Ok(match value.to_str_lossy().as_ref() { - "lf" => gix_filter::eol::Mode::Lf, - "crlf" => gix_filter::eol::Mode::CrLf, - "native" => gix_filter::eol::Mode::default(), - _ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())), - }) + mod eol { + use std::borrow::Cow; + + use crate::{ + bstr::{BStr, ByteSlice}, + config, + config::tree::core::Eol, + }; + + impl Eol { + /// Convert `value` into the default end-of-line mode. + /// + /// ### Deviation + /// + /// git will allow any value and silently leaves it unset, we will fail if the value is not known. + pub fn try_into_eol( + &'static self, + value: Cow<'_, BStr>, + ) -> Result { + Ok(match value.to_str_lossy().as_ref() { + "lf" => gix_filter::eol::Mode::Lf, + "crlf" => gix_filter::eol::Mode::CrLf, + "native" => gix_filter::eol::Mode::default(), + _ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())), + }) + } } } -} -mod safecrlf { - use std::borrow::Cow; + mod safecrlf { + use std::borrow::Cow; - use gix_filter::pipeline::CrlfRoundTripCheck; + use gix_filter::pipeline::CrlfRoundTripCheck; - use crate::{bstr::BStr, config, config::tree::core::SafeCrlf}; + use crate::{bstr::BStr, config, config::tree::core::SafeCrlf}; - impl SafeCrlf { - /// Convert `value` into the safe-crlf enumeration, if possible. - pub fn try_into_safecrlf( - &'static self, - value: Cow<'_, BStr>, - ) -> Result { - if value.as_ref() == "warn" { - return Ok(CrlfRoundTripCheck::Warn); + impl SafeCrlf { + /// Convert `value` into the safe-crlf enumeration, if possible. + pub fn try_into_safecrlf( + &'static self, + value: Cow<'_, BStr>, + ) -> Result { + if value.as_ref() == "warn" { + return Ok(CrlfRoundTripCheck::Warn); + } + let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| { + config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err) + })?; + Ok(if value.into() { + CrlfRoundTripCheck::Fail + } else { + CrlfRoundTripCheck::Skip + }) } - let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| { - config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err) - })?; - Ok(if value.into() { - CrlfRoundTripCheck::Fail - } else { - CrlfRoundTripCheck::Skip - }) } } -} -mod autocrlf { - use std::borrow::Cow; + mod autocrlf { + use std::borrow::Cow; - use gix_filter::eol; + use gix_filter::eol; - use crate::{bstr::BStr, config, config::tree::core::AutoCrlf}; + use crate::{bstr::BStr, config, config::tree::core::AutoCrlf}; - impl AutoCrlf { - /// Convert `value` into the safe-crlf enumeration, if possible. - pub fn try_into_autocrlf( - &'static self, - value: Cow<'_, BStr>, - ) -> Result { - if value.as_ref() == "input" { - return Ok(eol::AutoCrlf::Input); + impl AutoCrlf { + /// Convert `value` into the safe-crlf enumeration, if possible. + pub fn try_into_autocrlf( + &'static self, + value: Cow<'_, BStr>, + ) -> Result { + if value.as_ref() == "input" { + return Ok(eol::AutoCrlf::Input); + } + let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| { + config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err) + })?; + Ok(if value.into() { + eol::AutoCrlf::Enabled + } else { + eol::AutoCrlf::Disabled + }) } - let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| { - config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err) - })?; - Ok(if value.into() { - eol::AutoCrlf::Enabled - } else { - eol::AutoCrlf::Disabled - }) } } } +#[cfg(feature = "attributes")] +pub use filter::*; #[cfg(feature = "revision")] mod disambiguate { @@ -462,7 +478,9 @@ mod validate { pub struct SafeCrlf; impl keys::Validate for SafeCrlf { + #[cfg_attr(not(feature = "attributes"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { + #[cfg(feature = "attributes")] super::Core::SAFE_CRLF.try_into_safecrlf(value.into())?; Ok(()) } @@ -470,7 +488,9 @@ mod validate { pub struct AutoCrlf; impl keys::Validate for AutoCrlf { + #[cfg_attr(not(feature = "attributes"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { + #[cfg(feature = "attributes")] super::Core::AUTO_CRLF.try_into_autocrlf(value.into())?; Ok(()) } @@ -478,7 +498,9 @@ mod validate { pub struct Eol; impl keys::Validate for Eol { + #[cfg_attr(not(feature = "attributes"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { + #[cfg(feature = "attributes")] super::Core::EOL.try_into_eol(value.into())?; Ok(()) } @@ -486,7 +508,9 @@ mod validate { pub struct CheckRoundTripEncoding; impl keys::Validate for CheckRoundTripEncoding { + #[cfg_attr(not(feature = "attributes"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { + #[cfg(feature = "attributes")] super::Core::CHECK_ROUND_TRIP_ENCODING.try_into_encodings(Some(value.into()))?; Ok(()) } diff --git a/gix/src/config/tree/sections/fetch.rs b/gix/src/config/tree/sections/fetch.rs index 2e046d5af5d..29ba1ddeabd 100644 --- a/gix/src/config/tree/sections/fetch.rs +++ b/gix/src/config/tree/sections/fetch.rs @@ -11,6 +11,7 @@ impl Fetch { validate::NegotiationAlgorithm, ); /// The `fetch.recurseSubmodules` key. + #[cfg(feature = "attributes")] pub const RECURSE_SUBMODULES: RecurseSubmodules = RecurseSubmodules::new_with_validate("recurseSubmodules", &config::Tree::FETCH, validate::RecurseSubmodules); } @@ -21,7 +22,11 @@ impl Section for Fetch { } fn keys(&self) -> &[&dyn Key] { - &[&Self::NEGOTIATION_ALGORITHM, &Self::RECURSE_SUBMODULES] + &[ + &Self::NEGOTIATION_ALGORITHM, + #[cfg(feature = "attributes")] + &Self::RECURSE_SUBMODULES, + ] } } @@ -29,6 +34,7 @@ impl Section for Fetch { pub type NegotiationAlgorithm = keys::Any; /// The `fetch.recurseSubmodules` key. +#[cfg(feature = "attributes")] pub type RecurseSubmodules = keys::Any; mod algorithm { @@ -38,10 +44,7 @@ mod algorithm { use crate::{ bstr::BStr, - config::{ - key::GenericErrorWithValue, - tree::sections::fetch::{NegotiationAlgorithm, RecurseSubmodules}, - }, + config::{key::GenericErrorWithValue, tree::sections::fetch::NegotiationAlgorithm}, remote::fetch::negotiate, }; @@ -60,7 +63,8 @@ mod algorithm { } } - impl RecurseSubmodules { + #[cfg(feature = "attributes")] + impl crate::config::tree::sections::fetch::RecurseSubmodules { /// Obtain the way submodules should be updated. pub fn try_into_recurse_submodules( &'static self, @@ -87,9 +91,13 @@ mod validate { pub struct RecurseSubmodules; impl keys::Validate for RecurseSubmodules { + #[cfg_attr(not(feature = "attributes"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { - let boolean = gix_config::Boolean::try_from(value).map(|b| b.0); - Fetch::RECURSE_SUBMODULES.try_into_recurse_submodules(boolean)?; + #[cfg(feature = "attributes")] + { + let boolean = gix_config::Boolean::try_from(value).map(|b| b.0); + Fetch::RECURSE_SUBMODULES.try_into_recurse_submodules(boolean)?; + } Ok(()) } } diff --git a/gix/src/lib.rs b/gix/src/lib.rs index c00c5d5b915..2718cd66ef6 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -77,6 +77,7 @@ // This also means that their major version changes affect our major version, but that's alright as we directly expose their // APIs/instances anyway. pub use gix_actor as actor; +#[cfg(feature = "attributes")] pub use gix_attributes as attrs; pub use gix_commitgraph as commitgraph; pub use gix_credentials as credentials; @@ -92,6 +93,7 @@ pub use gix_fs as fs; pub use gix_glob as glob; pub use gix_hash as hash; pub use gix_hashtable as hashtable; +#[cfg(feature = "excludes")] pub use gix_ignore as ignore; #[doc(inline)] pub use gix_index as index; @@ -122,6 +124,7 @@ mod ext; /// pub mod prelude; +#[cfg(feature = "excludes")] mod attribute_stack; /// @@ -135,10 +138,13 @@ pub type OdbHandle = gix_odb::Handle; pub(crate) type Config = OwnShared>; mod types; +#[cfg(any(feature = "excludes", feature = "attributes"))] +pub use types::AttributeStack; pub use types::{ - AttributeStack, Commit, Head, Id, Object, ObjectDetached, Pathspec, Reference, Remote, Repository, Submodule, Tag, - ThreadSafeRepository, Tree, Worktree, + Commit, Head, Id, Object, ObjectDetached, Reference, Remote, Repository, Tag, ThreadSafeRepository, Tree, Worktree, }; +#[cfg(feature = "attributes")] +pub use types::{Pathspec, Submodule}; /// pub mod clone; @@ -146,9 +152,11 @@ pub mod commit; pub mod head; pub mod id; pub mod object; +#[cfg(feature = "attributes")] pub mod pathspec; pub mod reference; pub mod repository; +#[cfg(feature = "attributes")] pub mod submodule; pub mod tag; @@ -265,6 +273,7 @@ pub mod worktree; pub mod revision; +#[cfg(feature = "attributes")] pub mod filter; /// diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index a8f3d464f1c..17bcce33a4f 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -294,6 +294,7 @@ impl ThreadSafeRepository { linked_worktree_options: options, index: gix_fs::SharedFileSnapshotMut::new().into(), shallow_commits: gix_fs::SharedFileSnapshotMut::new().into(), + #[cfg(feature = "attributes")] modules: gix_fs::SharedFileSnapshotMut::new().into(), }) } diff --git a/gix/src/repository/attributes.rs b/gix/src/repository/attributes.rs index 108f3b10662..7f747f7fd98 100644 --- a/gix/src/repository/attributes.rs +++ b/gix/src/repository/attributes.rs @@ -24,6 +24,7 @@ impl Repository { /// /// * `$XDG_CONFIG_HOME/…/ignore|attributes` if `core.excludesFile|attributesFile` is *not* set, otherwise use the configured file. /// * `$GIT_DIR/info/exclude|attributes` if present. + #[cfg(feature = "attributes")] pub fn attributes( &self, index: &gix_index::State, @@ -60,6 +61,7 @@ impl Repository { } /// Like [attributes()][Self::attributes()], but without access to exclude/ignore information. + #[cfg(feature = "attributes")] pub fn attributes_only( &self, index: &gix_index::State, @@ -104,6 +106,7 @@ impl Repository { /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use /// [`Repository::attributes()`] for accessing both attributes and excludes. // TODO: test + #[cfg(feature = "excludes")] pub fn excludes( &self, index: &gix_index::State, diff --git a/gix/src/repository/impls.rs b/gix/src/repository/impls.rs index 1340b38070b..e1aac6e1927 100644 --- a/gix/src/repository/impls.rs +++ b/gix/src/repository/impls.rs @@ -9,6 +9,7 @@ impl Clone for crate::Repository { self.options.clone(), self.index.clone(), self.shallow_commits.clone(), + #[cfg(feature = "attributes")] self.modules.clone(), ) } @@ -43,6 +44,7 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository { repo.linked_worktree_options.clone(), repo.index.clone(), repo.shallow_commits.clone(), + #[cfg(feature = "attributes")] repo.modules.clone(), ) } @@ -59,6 +61,7 @@ impl From for crate::Repository { repo.linked_worktree_options, repo.index, repo.shallow_commits, + #[cfg(feature = "attributes")] repo.modules.clone(), ) } @@ -74,6 +77,7 @@ impl From for crate::ThreadSafeRepository { config: r.config, linked_worktree_options: r.options, index: r.index, + #[cfg(feature = "attributes")] modules: r.modules, shallow_commits: r.shallow_commits, } diff --git a/gix/src/repository/init.rs b/gix/src/repository/init.rs index 676169f11e0..e3ddd753b2b 100644 --- a/gix/src/repository/init.rs +++ b/gix/src/repository/init.rs @@ -11,7 +11,7 @@ impl crate::Repository { linked_worktree_options: crate::open::Options, index: crate::worktree::IndexStorage, shallow_commits: crate::shallow::CommitsStorage, - modules: crate::submodule::ModulesFileStorage, + #[cfg(feature = "attributes")] modules: crate::submodule::ModulesFileStorage, ) -> Self { setup_objects(&mut objects, &config); crate::Repository { @@ -24,6 +24,7 @@ impl crate::Repository { options: linked_worktree_options, index, shallow_commits, + #[cfg(feature = "attributes")] modules, } } diff --git a/gix/src/repository/location.rs b/gix/src/repository/location.rs index 02df805f331..5811e7bf986 100644 --- a/gix/src/repository/location.rs +++ b/gix/src/repository/location.rs @@ -26,6 +26,7 @@ impl crate::Repository { } /// The path to the `.gitmodules` file in the worktree, if a worktree is available. + #[cfg(feature = "attributes")] pub fn modules_path(&self) -> Option { self.work_dir().map(|wtd| wtd.join(crate::submodule::MODULES_FILE)) } diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index 651b95e2e27..06a362c4847 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -35,10 +35,12 @@ impl crate::Repository { } } +#[cfg(any(feature = "attributes", feature = "excludes"))] pub mod attributes; mod cache; mod config; /// +#[cfg(feature = "attributes")] pub mod filter; mod graph; pub(crate) mod identity; @@ -50,6 +52,7 @@ mod location; #[cfg(feature = "mailmap")] mod mailmap; mod object; +#[cfg(feature = "attributes")] mod pathspec; mod reference; mod remote; @@ -57,6 +60,7 @@ mod remote; mod revision; mod shallow; mod state; +#[cfg(feature = "attributes")] mod submodule; mod thread_safe; mod worktree; @@ -72,6 +76,7 @@ pub enum IndexPersistedOrInMemory { } /// +#[cfg(feature = "attributes")] pub mod pathspec_defaults_ignore_case { /// The error returned by [Repository::pathspec_defaults_ignore_case()](crate::Repository::pathspec_defaults_inherit_ignore_case()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/types.rs b/gix/src/types.rs index 60629b45f38..5fd01c6e1f4 100644 --- a/gix/src/types.rs +++ b/gix/src/types.rs @@ -1,8 +1,8 @@ -use std::{cell::RefCell, path::PathBuf, rc::Rc}; +use std::{cell::RefCell, path::PathBuf}; use gix_hash::ObjectId; -use crate::{bstr::BString, head, remote}; +use crate::{head, remote}; /// A worktree checkout containing the files of the repository in consumable form. #[derive(Debug, Clone)] @@ -139,6 +139,7 @@ pub struct Repository { /// Particularly useful when following linked worktrees and instantiating new equally configured worktree repositories. pub(crate) options: crate::open::Options, pub(crate) index: crate::worktree::IndexStorage, + #[cfg(feature = "attributes")] pub(crate) modules: crate::submodule::ModulesFileStorage, pub(crate) shallow_commits: crate::shallow::CommitsStorage, } @@ -167,6 +168,7 @@ pub struct ThreadSafeRepository { pub(crate) linked_worktree_options: crate::open::Options, /// The index of this instances worktree. pub(crate) index: crate::worktree::IndexStorage, + #[cfg(feature = "attributes")] pub(crate) modules: crate::submodule::ModulesFileStorage, pub(crate) shallow_commits: crate::shallow::CommitsStorage, } @@ -205,6 +207,7 @@ pub struct Remote<'repo> { /// Should this potential duplication of effort to maintain attribute state be unacceptable, the user may fall back /// to the underlying plumbing. #[derive(Clone)] +#[cfg(feature = "attributes")] pub struct Pathspec<'repo> { pub(crate) repo: &'repo Repository, /// The cache to power attribute access. It's only initialized if we have a pattern with attributes. @@ -215,12 +218,14 @@ pub struct Pathspec<'repo> { /// A stand-in for the submodule of a particular name. #[derive(Clone)] +#[cfg(feature = "attributes")] pub struct Submodule<'repo> { - pub(crate) state: Rc>, - pub(crate) name: BString, + pub(crate) state: std::rc::Rc>, + pub(crate) name: crate::bstr::BString, } /// A utility to access `.gitattributes` and `.gitignore` information efficiently. +#[cfg(any(feature = "attributes", feature = "excludes"))] pub struct AttributeStack<'repo> { pub(crate) repo: &'repo Repository, pub(crate) inner: gix_worktree::Stack, diff --git a/gix/src/worktree/mod.rs b/gix/src/worktree/mod.rs index 76dc24722bc..0f2737a8149 100644 --- a/gix/src/worktree/mod.rs +++ b/gix/src/worktree/mod.rs @@ -2,7 +2,9 @@ use std::path::PathBuf; #[cfg(feature = "worktree-archive")] pub use gix_archive as archive; +#[cfg(feature = "excludes")] pub use gix_worktree::*; +#[cfg(feature = "worktree-mutation")] pub use gix_worktree_state as state; #[cfg(feature = "worktree-stream")] pub use gix_worktree_stream as stream; @@ -111,6 +113,7 @@ pub mod open_index { } /// +#[cfg(feature = "excludes")] pub mod excludes { use crate::AttributeStack; @@ -146,6 +149,7 @@ pub mod excludes { } /// +#[cfg(feature = "attributes")] pub mod attributes { use crate::{AttributeStack, Worktree}; @@ -190,6 +194,7 @@ pub mod attributes { } /// +#[cfg(feature = "attributes")] pub mod pathspec { use crate::{ bstr::BStr, diff --git a/gix/tests/config/tree.rs b/gix/tests/config/tree.rs index be925a9e13f..94667e87ae7 100644 --- a/gix/tests/config/tree.rs +++ b/gix/tests/config/tree.rs @@ -147,7 +147,6 @@ mod ssh { mod fetch { use gix::{ - bstr::ByteSlice, config::tree::{Fetch, Key}, remote::fetch::negotiate::Algorithm, }; @@ -179,7 +178,9 @@ mod fetch { } #[test] + #[cfg(feature = "attributes")] fn recurse_submodule() -> crate::Result { + use gix::bstr::ByteSlice; for (actual, expected) in [ ("true", gix_submodule::config::FetchRecurse::Always), ("false", gix_submodule::config::FetchRecurse::Never), @@ -435,6 +436,7 @@ mod core { } #[test] + #[cfg(feature = "attributes")] fn safecrlf() -> crate::Result { for (value, expected) in [ ("false", gix_filter::pipeline::CrlfRoundTripCheck::Skip), @@ -452,6 +454,7 @@ mod core { } #[test] + #[cfg(feature = "attributes")] fn autocrlf() -> crate::Result { for (value, expected) in [ ("false", gix_filter::eol::AutoCrlf::Disabled), @@ -472,6 +475,7 @@ mod core { } #[test] + #[cfg(feature = "attributes")] fn eol() -> crate::Result { for (value, expected) in [ ("lf", gix_filter::eol::Mode::Lf), @@ -489,6 +493,7 @@ mod core { } #[test] + #[cfg(feature = "attributes")] fn check_round_trip_encoding() -> crate::Result { for (value, expected) in [ ( diff --git a/gix/tests/gix.rs b/gix/tests/gix.rs index 1683ce4a660..4721e0d4eb2 100644 --- a/gix/tests/gix.rs +++ b/gix/tests/gix.rs @@ -13,4 +13,5 @@ mod remote; mod repository; #[cfg(feature = "revision")] mod revision; +#[cfg(feature = "attributes")] mod submodule; diff --git a/gix/tests/repository/mod.rs b/gix/tests/repository/mod.rs index c99bac633c7..461b18b14e3 100644 --- a/gix/tests/repository/mod.rs +++ b/gix/tests/repository/mod.rs @@ -1,14 +1,17 @@ use gix::Repository; mod config; +#[cfg(feature = "attributes")] mod filter; mod object; mod open; +#[cfg(feature = "attributes")] mod pathspec; mod reference; mod remote; mod shallow; mod state; +#[cfg(feature = "attributes")] mod submodule; mod worktree; diff --git a/justfile b/justfile index 8bf870b10a2..4fb0316bc38 100755 --- a/justfile +++ b/justfile @@ -44,6 +44,9 @@ check: if cargo check -p gix-protocol --all-features 2>/dev/null; then false; else true; fi if cargo tree -p gix --no-default-features -i imara-diff 2>/dev/null; then false; else true; fi if cargo tree -p gix --no-default-features -i gix-protocol 2>/dev/null; then false; else true; fi + if cargo tree -p gix --no-default-features -i gix-submodule 2>/dev/null; then false; else true; fi + if cargo tree -p gix --no-default-features -i gix-pathspec 2>/dev/null; then false; else true; fi + if cargo tree -p gix --no-default-features -i gix-filter 2>/dev/null; then false; else true; fi cargo check --no-default-features --features lean cargo check --no-default-features --features lean-async cargo check --no-default-features --features max @@ -111,13 +114,16 @@ check: cargo check -p gix --no-default-features --features blocking-network-client cargo check -p gix --no-default-features --features blocking-http-transport-curl cargo check -p gix --no-default-features --features blocking-http-transport-reqwest - cargo check -p gix --no-default-features --features max-performance - cargo check -p gix --no-default-features --features max-performance-safe - cargo check -p gix --no-default-features --features progress-tree - cargo check -p gix --no-default-features --features blob-diff - cargo check -p gix --no-default-features --features revision - cargo check -p gix --no-default-features --features revparse-regex - cargo check -p gix --no-default-features --features mailmap + cargo check -p gix --no-default-features --features max-performance --tests + cargo check -p gix --no-default-features --features max-performance-safe --tests + cargo check -p gix --no-default-features --features progress-tree --tests + cargo check -p gix --no-default-features --features blob-diff --tests + cargo check -p gix --no-default-features --features revision --tests + cargo check -p gix --no-default-features --features revparse-regex --tests + cargo check -p gix --no-default-features --features mailmap --tests + cargo check -p gix --no-default-features --features excludes --tests + cargo check -p gix --no-default-features --features attributes --tests + cargo check -p gix --no-default-features --features worktree-mutation --tests cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From a957478e0f623803bc6358d08b9ffaa2305e24d4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 13:15:56 +0200 Subject: [PATCH 18/23] fix: put `gix-credentials` and `gix-prompt` behind the 'credentials' feature toggle. They are also available when using https transports. --- gix/Cargo.toml | 18 ++++--- gix/src/config/mod.rs | 2 + gix/src/config/snapshot/mod.rs | 1 + gix/src/config/tree/sections/fetch.rs | 49 +++++++++---------- gix/src/env.rs | 1 + gix/src/lib.rs | 3 ++ gix/src/remote/fetch.rs | 1 + gix/tests/config/tree.rs | 13 ++--- .../repository/config/config_snapshot/mod.rs | 5 +- justfile | 2 + 10 files changed, 55 insertions(+), 40 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 3de57ce22c3..e530a1dbe75 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -51,7 +51,7 @@ default = ["max-performance-safe", "comfort", "basic", "extras"] basic = ["blob-diff", "revision"] ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation"] +extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -61,6 +61,10 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. #! Providers of libraries should only activate the components they need. +## Access to credential helpers, which provide credentials for URLs. +# Note that `gix-negotiate` just piggibacks here, as 'credentials' is equivalent to 'fetch & push' right now. +credentials = ["dep:gix-credentials", "dep:gix-prompt", "dep:gix-negotiate"] + ## Various ways to alter the worktree makeup by checkout and reset. worktree-mutation = ["attributes", "dep:gix-worktree-state"] @@ -100,11 +104,11 @@ worktree-archive = ["gix-archive", "worktree-stream", "attributes"] #! Making a choice here also affects which crypto-library ends up being used. ## Make `gix-protocol` available along with an async client. -async-network-client = ["gix-protocol/async-client", "gix-pack/streaming-input", "attributes"] +async-network-client = ["gix-protocol/async-client", "gix-pack/streaming-input", "attributes", "credentials"] ## Use this if your crate uses `async-std` as runtime, and enable basic runtime integration when connecting to remote servers via the `git://` protocol. async-network-client-async-std = ["async-std", "async-network-client", "gix-transport/async-std"] ## Make `gix-protocol` available along with a blocking client, providing access to the `file://`, git://` and `ssh://` transports. -blocking-network-client = ["gix-protocol/blocking-client", "gix-pack/streaming-input", "attributes"] +blocking-network-client = ["gix-protocol/blocking-client", "gix-pack/streaming-input", "attributes", "credentials"] ## Stacks with `blocking-network-client` to provide support for HTTP/S using **curl**, and implies blocking networking as a whole, making the `https://` transport avaialble. blocking-http-transport-curl = ["blocking-network-client", "gix-transport/http-client-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 avaialble. @@ -169,7 +173,7 @@ serde = [ "dep:serde", "gix-revision/serde", "gix-worktree?/serde", "gix-commitgraph/serde", - "gix-credentials/serde"] + "gix-credentials?/serde"] ## Re-export the progress tree root which allows to obtain progress from various functions which take `impl gix::Progress`. ## Applications which want to display progress will probably need this implementation. @@ -201,7 +205,7 @@ gix-actor = { version = "^0.25.0", path = "../gix-actor" } gix-pack = { version = "^0.41.0", path = "../gix-pack", default-features = false, features = ["object-cache-dynamic"] } gix-revision = { version = "^0.20.0", path = "../gix-revision", default-features = false } gix-revwalk = { version = "^0.6.0", path = "../gix-revwalk" } -gix-negotiate = { version = "^0.6.0", path = "../gix-negotiate" } +gix-negotiate = { version = "^0.6.0", path = "../gix-negotiate", optional = true } gix-path = { version = "^0.9.0", path = "../gix-path" } gix-url = { version = "^0.22.0", path = "../gix-url" } @@ -212,8 +216,8 @@ gix-features = { version = "^0.33.0", path = "../gix-features", features = ["pro gix-trace = { version = "^0.1.3", path = "../gix-trace" } gix-glob = { version = "^0.11.0", path = "../gix-glob" } -gix-credentials = { version = "^0.18.0", path = "../gix-credentials" } -gix-prompt = { version = "^0.6.0", path = "../gix-prompt" } +gix-credentials = { version = "^0.18.0", path = "../gix-credentials", optional = true } +gix-prompt = { version = "^0.6.0", path = "../gix-prompt", optional = true } gix-index = { version = "^0.23.1", path = "../gix-index" } gix-attributes = { version = "^0.17.0", path = "../gix-attributes", optional = true } gix-ignore = { version = "^0.6.0", path = "../gix-ignore", optional = true } diff --git a/gix/src/config/mod.rs b/gix/src/config/mod.rs index 76da686af3c..e8e0f87352e 100644 --- a/gix/src/config/mod.rs +++ b/gix/src/config/mod.rs @@ -5,6 +5,7 @@ use crate::{bstr::BString, repository::identity, Repository}; pub(crate) mod cache; mod snapshot; +#[cfg(feature = "credentials")] pub use snapshot::credential_helpers; /// @@ -461,6 +462,7 @@ pub mod transport { key: Cow<'static, BStr>, }, #[error("Could not configure the credential helpers for the authenticated proxy url")] + #[cfg(feature = "credentials")] ConfigureProxyAuthenticate(#[from] crate::config::snapshot::credential_helpers::Error), #[error(transparent)] InvalidSslVersion(#[from] crate::config::ssl_version::Error), diff --git a/gix/src/config/snapshot/mod.rs b/gix/src/config/snapshot/mod.rs index 80ec6f94889..de143ea1f07 100644 --- a/gix/src/config/snapshot/mod.rs +++ b/gix/src/config/snapshot/mod.rs @@ -2,4 +2,5 @@ mod _impls; mod access; /// +#[cfg(feature = "credentials")] pub mod credential_helpers; diff --git a/gix/src/config/tree/sections/fetch.rs b/gix/src/config/tree/sections/fetch.rs index 29ba1ddeabd..32db7be5fb9 100644 --- a/gix/src/config/tree/sections/fetch.rs +++ b/gix/src/config/tree/sections/fetch.rs @@ -38,27 +38,26 @@ pub type NegotiationAlgorithm = keys::Any; pub type RecurseSubmodules = keys::Any; mod algorithm { - use std::borrow::Cow; - - use gix_object::bstr::ByteSlice; - - use crate::{ - bstr::BStr, - config::{key::GenericErrorWithValue, tree::sections::fetch::NegotiationAlgorithm}, - remote::fetch::negotiate, - }; - - impl NegotiationAlgorithm { + #[cfg(feature = "credentials")] + impl crate::config::tree::sections::fetch::NegotiationAlgorithm { /// Derive the negotiation algorithm identified by `name`, case-sensitively. pub fn try_into_negotiation_algorithm( &'static self, - name: Cow<'_, BStr>, - ) -> Result { + name: std::borrow::Cow<'_, crate::bstr::BStr>, + ) -> Result { + use crate::bstr::ByteSlice; + use crate::remote::fetch::negotiate::Algorithm; + Ok(match name.as_ref().as_bytes() { - b"noop" => negotiate::Algorithm::Noop, - b"consecutive" | b"default" => negotiate::Algorithm::Consecutive, - b"skipping" => negotiate::Algorithm::Skipping, - _ => return Err(GenericErrorWithValue::from_value(self, name.into_owned())), + b"noop" => Algorithm::Noop, + b"consecutive" | b"default" => Algorithm::Consecutive, + b"skipping" => Algorithm::Skipping, + _ => { + return Err(crate::config::key::GenericErrorWithValue::from_value( + self, + name.into_owned(), + )) + } }) } } @@ -69,22 +68,22 @@ mod algorithm { pub fn try_into_recurse_submodules( &'static self, value: Result, - ) -> Result { - gix_submodule::config::FetchRecurse::new(value).map_err(|err| GenericErrorWithValue::from_value(self, err)) + ) -> Result { + gix_submodule::config::FetchRecurse::new(value) + .map_err(|err| crate::config::key::GenericErrorWithValue::from_value(self, err)) } } } mod validate { - use crate::{ - bstr::BStr, - config::tree::{keys, Fetch}, - }; + use crate::{bstr::BStr, config::tree::keys}; pub struct NegotiationAlgorithm; impl keys::Validate for NegotiationAlgorithm { + #[cfg_attr(not(feature = "credentials"), allow(unused_variables))] fn validate(&self, value: &BStr) -> Result<(), Box> { - Fetch::NEGOTIATION_ALGORITHM.try_into_negotiation_algorithm(value.into())?; + #[cfg(feature = "credentials")] + crate::config::tree::Fetch::NEGOTIATION_ALGORITHM.try_into_negotiation_algorithm(value.into())?; Ok(()) } } @@ -96,7 +95,7 @@ mod validate { #[cfg(feature = "attributes")] { let boolean = gix_config::Boolean::try_from(value).map(|b| b.0); - Fetch::RECURSE_SUBMODULES.try_into_recurse_submodules(boolean)?; + crate::config::tree::Fetch::RECURSE_SUBMODULES.try_into_recurse_submodules(boolean)?; } Ok(()) } diff --git a/gix/src/env.rs b/gix/src/env.rs index 915afe807c6..b4973b8d5bb 100644 --- a/gix/src/env.rs +++ b/gix/src/env.rs @@ -65,6 +65,7 @@ pub mod collate { #[error(transparent)] FindExistingRemote(#[from] crate::remote::find::existing::Error), #[error(transparent)] + #[cfg(feature = "credentials")] CredentialHelperConfig(#[from] crate::config::credential_helpers::Error), #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] #[error(transparent)] diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 2718cd66ef6..18e6445c854 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -80,6 +80,7 @@ pub use gix_actor as actor; #[cfg(feature = "attributes")] pub use gix_attributes as attrs; pub use gix_commitgraph as commitgraph; +#[cfg(feature = "credentials")] pub use gix_credentials as credentials; pub use gix_date as date; pub use gix_features as features; @@ -98,10 +99,12 @@ pub use gix_ignore as ignore; #[doc(inline)] pub use gix_index as index; pub use gix_lock as lock; +#[cfg(feature = "credentials")] pub use gix_negotiate as negotiate; pub use gix_object as objs; pub use gix_object::bstr; pub use gix_odb as odb; +#[cfg(feature = "credentials")] pub use gix_prompt as prompt; #[cfg(feature = "gix-protocol")] pub use gix_protocol as protocol; diff --git a/gix/src/remote/fetch.rs b/gix/src/remote/fetch.rs index 54eaea7ec6a..4700201de51 100644 --- a/gix/src/remote/fetch.rs +++ b/gix/src/remote/fetch.rs @@ -1,5 +1,6 @@ /// pub mod negotiate { + #[cfg(feature = "credentials")] pub use gix_negotiate::Algorithm; #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] diff --git a/gix/tests/config/tree.rs b/gix/tests/config/tree.rs index 94667e87ae7..fd844f8b0f0 100644 --- a/gix/tests/config/tree.rs +++ b/gix/tests/config/tree.rs @@ -146,15 +146,14 @@ mod ssh { } mod fetch { - use gix::{ - config::tree::{Fetch, Key}, - remote::fetch::negotiate::Algorithm, - }; - - use crate::config::tree::bcow; #[test] + #[cfg(feature = "credentials")] fn algorithm() -> crate::Result { + use crate::config::tree::bcow; + use gix::config::tree::{Fetch, Key}; + use gix::remote::fetch::negotiate::Algorithm; + for (actual, expected) in [ ("noop", Algorithm::Noop), ("consecutive", Algorithm::Consecutive), @@ -181,6 +180,8 @@ mod fetch { #[cfg(feature = "attributes")] fn recurse_submodule() -> crate::Result { use gix::bstr::ByteSlice; + use gix::config::tree::{Fetch, Key}; + for (actual, expected) in [ ("true", gix_submodule::config::FetchRecurse::Always), ("false", gix_submodule::config::FetchRecurse::Never), diff --git a/gix/tests/repository/config/config_snapshot/mod.rs b/gix/tests/repository/config/config_snapshot/mod.rs index 3e5faf016bd..fc988df0f17 100644 --- a/gix/tests/repository/config/config_snapshot/mod.rs +++ b/gix/tests/repository/config/config_snapshot/mod.rs @@ -2,6 +2,9 @@ use gix::config::tree::{Branch, Core, Key}; use crate::named_repo; +#[cfg(feature = "credentials")] +mod credential_helpers; + #[test] fn commit_auto_rollback() -> crate::Result { let mut repo: gix::Repository = named_repo("make_basic_repo.sh")?; @@ -127,5 +130,3 @@ fn apply_cli_overrides() -> crate::Result { Ok(()) } - -mod credential_helpers; diff --git a/justfile b/justfile index 4fb0316bc38..946e0242faf 100755 --- a/justfile +++ b/justfile @@ -47,6 +47,7 @@ check: if cargo tree -p gix --no-default-features -i gix-submodule 2>/dev/null; then false; else true; fi if cargo tree -p gix --no-default-features -i gix-pathspec 2>/dev/null; then false; else true; fi if cargo tree -p gix --no-default-features -i gix-filter 2>/dev/null; then false; else true; fi + if cargo tree -p gix --no-default-features -i gix-credentials 2>/dev/null; then false; else true; fi cargo check --no-default-features --features lean cargo check --no-default-features --features lean-async cargo check --no-default-features --features max @@ -124,6 +125,7 @@ check: cargo check -p gix --no-default-features --features excludes --tests cargo check -p gix --no-default-features --features attributes --tests cargo check -p gix --no-default-features --features worktree-mutation --tests + cargo check -p gix --no-default-features --features credentials --tests cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From 721c37722ca8b12a5f9c060061040c79f9da6aa9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 13:34:29 +0200 Subject: [PATCH 19/23] feat: Allow index access to be toggled with the `index` feature. --- gix/Cargo.toml | 13 ++++++++----- gix/src/lib.rs | 1 + gix/src/open/repository.rs | 1 + gix/src/repository/impls.rs | 4 ++++ gix/src/repository/init.rs | 3 ++- gix/src/repository/mod.rs | 3 +++ gix/src/types.rs | 2 ++ gix/src/worktree/mod.rs | 3 +++ gix/tests/repository/worktree.rs | 5 ++--- justfile | 1 + 10 files changed, 27 insertions(+), 9 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index e530a1dbe75..309fbcab10a 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -48,7 +48,7 @@ default = ["max-performance-safe", "comfort", "basic", "extras"] #! Bundles are for convenience only and bear no further meaning beyond the cargo manifest file. ## More fundamental components that most will be able to make good use of. -basic = ["blob-diff", "revision"] +basic = ["blob-diff", "revision", "index"] ## Various additional features and capabilities that are not necessarily part of what most users would need. extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials"] @@ -61,6 +61,9 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. #! Providers of libraries should only activate the components they need. +## Access to `.git/index` files. +index = ["dep:gix-index"] + ## Access to credential helpers, which provide credentials for URLs. # Note that `gix-negotiate` just piggibacks here, as 'credentials' is equivalent to 'fetch & push' right now. credentials = ["dep:gix-credentials", "dep:gix-prompt", "dep:gix-negotiate"] @@ -69,7 +72,7 @@ credentials = ["dep:gix-credentials", "dep:gix-prompt", "dep:gix-negotiate"] worktree-mutation = ["attributes", "dep:gix-worktree-state"] ## Retrieve a worktree stack for querying exclude information -excludes = ["dep:gix-ignore", "dep:gix-worktree"] +excludes = ["dep:gix-ignore", "dep:gix-worktree", "index"] ## Query attributes and excludes. Enables access to pathspecs, worktree checkouts, filter-pipelines and submodules. attributes = ["excludes", "dep:gix-filter", "dep:gix-pathspec", "dep:gix-attributes", "dep:gix-submodule", "gix-worktree?/attributes"] @@ -78,7 +81,7 @@ attributes = ["excludes", "dep:gix-filter", "dep:gix-pathspec", "dep:gix-attribu mailmap = ["dep:gix-mailmap"] ## Make revspec parsing possible, as well describing revision. -revision = ["gix-revision/describe"] +revision = ["gix-revision/describe", "index"] ## If enabled, revspecs now support the regex syntax like `@^{/^.*x}`. Otherwise, only substring search is supported. ## This feature does increase compile time for niche-benefit, but is required for fully git-compatible revspec parsing. @@ -165,7 +168,7 @@ serde = [ "dep:serde", "gix-transport?/serde", "gix-ref/serde", "gix-odb/serde", - "gix-index/serde", + "gix-index?/serde", "gix-mailmap?/serde", "gix-url/serde", "gix-attributes?/serde", @@ -218,7 +221,7 @@ gix-trace = { version = "^0.1.3", path = "../gix-trace" } gix-glob = { version = "^0.11.0", path = "../gix-glob" } gix-credentials = { version = "^0.18.0", path = "../gix-credentials", optional = true } gix-prompt = { version = "^0.6.0", path = "../gix-prompt", optional = true } -gix-index = { version = "^0.23.1", path = "../gix-index" } +gix-index = { version = "^0.23.1", path = "../gix-index", optional = true } gix-attributes = { version = "^0.17.0", path = "../gix-attributes", optional = true } gix-ignore = { version = "^0.6.0", path = "../gix-ignore", optional = true } gix-worktree = { version = "^0.25.0", path = "../gix-worktree", optional = true, default-features = false } diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 18e6445c854..dcf9b8a08aa 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -97,6 +97,7 @@ pub use gix_hashtable as hashtable; #[cfg(feature = "excludes")] pub use gix_ignore as ignore; #[doc(inline)] +#[cfg(feature = "index")] pub use gix_index as index; pub use gix_lock as lock; #[cfg(feature = "credentials")] diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 17bcce33a4f..ddae1134d04 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -292,6 +292,7 @@ impl ThreadSafeRepository { config, // used when spawning new repositories off this one when following worktrees linked_worktree_options: options, + #[cfg(feature = "index")] index: gix_fs::SharedFileSnapshotMut::new().into(), shallow_commits: gix_fs::SharedFileSnapshotMut::new().into(), #[cfg(feature = "attributes")] diff --git a/gix/src/repository/impls.rs b/gix/src/repository/impls.rs index e1aac6e1927..36fd788dc6c 100644 --- a/gix/src/repository/impls.rs +++ b/gix/src/repository/impls.rs @@ -7,6 +7,7 @@ impl Clone for crate::Repository { self.common_dir.clone(), self.config.clone(), self.options.clone(), + #[cfg(feature = "index")] self.index.clone(), self.shallow_commits.clone(), #[cfg(feature = "attributes")] @@ -42,6 +43,7 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository { repo.common_dir.clone(), repo.config.clone(), repo.linked_worktree_options.clone(), + #[cfg(feature = "index")] repo.index.clone(), repo.shallow_commits.clone(), #[cfg(feature = "attributes")] @@ -59,6 +61,7 @@ impl From for crate::Repository { repo.common_dir, repo.config, repo.linked_worktree_options, + #[cfg(feature = "index")] repo.index, repo.shallow_commits, #[cfg(feature = "attributes")] @@ -76,6 +79,7 @@ impl From for crate::ThreadSafeRepository { common_dir: r.common_dir, config: r.config, linked_worktree_options: r.options, + #[cfg(feature = "index")] index: r.index, #[cfg(feature = "attributes")] modules: r.modules, diff --git a/gix/src/repository/init.rs b/gix/src/repository/init.rs index e3ddd753b2b..65b3d59ab5d 100644 --- a/gix/src/repository/init.rs +++ b/gix/src/repository/init.rs @@ -9,7 +9,7 @@ impl crate::Repository { common_dir: Option, config: crate::config::Cache, linked_worktree_options: crate::open::Options, - index: crate::worktree::IndexStorage, + #[cfg(feature = "index")] index: crate::worktree::IndexStorage, shallow_commits: crate::shallow::CommitsStorage, #[cfg(feature = "attributes")] modules: crate::submodule::ModulesFileStorage, ) -> Self { @@ -22,6 +22,7 @@ impl crate::Repository { refs, config, options: linked_worktree_options, + #[cfg(feature = "index")] index, shallow_commits, #[cfg(feature = "attributes")] diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index 06a362c4847..e3742894b37 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -45,6 +45,7 @@ pub mod filter; mod graph; pub(crate) mod identity; mod impls; +#[cfg(feature = "index")] mod index; pub(crate) mod init; mod kind; @@ -66,6 +67,7 @@ mod thread_safe; mod worktree; /// 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), @@ -90,6 +92,7 @@ pub mod pathspec_defaults_ignore_case { } /// +#[cfg(feature = "index")] pub mod index_or_load_from_head { /// The error returned by [`Repository::index_or_load_from_head()`][crate::Repository::index_or_load_from_head()]. #[derive(thiserror::Error, Debug)] diff --git a/gix/src/types.rs b/gix/src/types.rs index 5fd01c6e1f4..0afdbbc519f 100644 --- a/gix/src/types.rs +++ b/gix/src/types.rs @@ -138,6 +138,7 @@ pub struct Repository { /// /// Particularly useful when following linked worktrees and instantiating new equally configured worktree repositories. pub(crate) options: crate::open::Options, + #[cfg(feature = "index")] pub(crate) index: crate::worktree::IndexStorage, #[cfg(feature = "attributes")] pub(crate) modules: crate::submodule::ModulesFileStorage, @@ -167,6 +168,7 @@ pub struct ThreadSafeRepository { /// options obtained when instantiating this repository for use when following linked worktrees. pub(crate) linked_worktree_options: crate::open::Options, /// The index of this instances worktree. + #[cfg(feature = "index")] pub(crate) index: crate::worktree::IndexStorage, #[cfg(feature = "attributes")] pub(crate) modules: crate::submodule::ModulesFileStorage, diff --git a/gix/src/worktree/mod.rs b/gix/src/worktree/mod.rs index 0f2737a8149..b0a1cc6f444 100644 --- a/gix/src/worktree/mod.rs +++ b/gix/src/worktree/mod.rs @@ -14,8 +14,10 @@ use crate::{ Repository, }; +#[cfg(feature = "index")] pub(crate) type IndexStorage = gix_features::threading::OwnShared>; /// A lazily loaded and auto-updated worktree index. +#[cfg(feature = "index")] pub type Index = gix_fs::SharedFileSnapshot; /// A stand-in to a worktree as result of a worktree iteration. @@ -84,6 +86,7 @@ pub(crate) fn id(git_dir: &std::path::Path, has_common_dir: bool) -> Option<&BSt pub mod proxy; /// +#[cfg(feature = "index")] pub mod open_index { /// The error returned by [`Worktree::open_index()`][crate::Worktree::open_index()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/tests/repository/worktree.rs b/gix/tests/repository/worktree.rs index 234c8805ec7..ddeba514274 100644 --- a/gix/tests/repository/worktree.rs +++ b/gix/tests/repository/worktree.rs @@ -34,9 +34,8 @@ fn archive() -> crate::Result { mod with_core_worktree_config { use std::io::BufRead; - use crate::repository::worktree::Baseline; - #[test] + #[cfg(feature = "index")] fn relative() -> crate::Result { for (name, is_relative) in [("absolute-worktree", false), ("relative-worktree", true)] { let repo = repo(name); @@ -61,7 +60,7 @@ mod with_core_worktree_config { "current worktree is based on work-tree dir" ); - let baseline = Baseline::collect(repo.git_dir())?; + let baseline = crate::repository::worktree::Baseline::collect(repo.git_dir())?; assert_eq!(baseline.len(), 1, "git lists the main worktree"); assert_eq!( baseline[0].root, diff --git a/justfile b/justfile index 946e0242faf..a262f14c273 100755 --- a/justfile +++ b/justfile @@ -126,6 +126,7 @@ check: cargo check -p gix --no-default-features --features attributes --tests cargo check -p gix --no-default-features --features worktree-mutation --tests cargo check -p gix --no-default-features --features credentials --tests + cargo check -p gix --no-default-features --features index --tests cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From 36d34bd7e8cd944c009cb7acbe39c1dc445b4adc Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 16:17:43 +0200 Subject: [PATCH 20/23] feat: add `interrupt` feature to reduce dependencies --- gix/Cargo.toml | 9 +- ...errupt-handler-allows-graceful-shutdown.rs | 11 +- gix/examples/reversible-interrupt-handlers.rs | 6 + gix/src/lib.rs | 1 + gix/tests/interrupt.rs | 119 +++++++++--------- justfile | 1 + 6 files changed, 82 insertions(+), 65 deletions(-) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 309fbcab10a..a04fb1fa831 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -51,7 +51,7 @@ default = ["max-performance-safe", "comfort", "basic", "extras"] basic = ["blob-diff", "revision", "index"] ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials"] +extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt"] ## Various progress-related features that improve the look of progress message units. comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] @@ -61,6 +61,9 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human #! A component is a distinct feature which may be comprised of one or more methods around a particular topic. #! Providers of libraries should only activate the components they need. +## Utilities for interrupting computations and cleaning up tempfiles. +interrupt = ["dep:signal-hook", "gix-tempfile/signals"] + ## Access to `.git/index` files. index = ["dep:gix-index"] @@ -192,7 +195,7 @@ gix-utils = { version = "^0.1.5", path = "../gix-utils" } gix-fs = { version = "^0.5.0", path = "../gix-fs" } gix-ref = { version = "^0.35.0", path = "../gix-ref" } gix-discover = { version = "^0.23.0", path = "../gix-discover" } -gix-tempfile = { version = "^8.0.0", path = "../gix-tempfile", default-features = false, features = ["signals"] } +gix-tempfile = { version = "^8.0.0", path = "../gix-tempfile", default-features = false } gix-lock = { version = "^8.0.0", path = "../gix-lock" } gix-validate = { version = "^0.8.0", path = "../gix-validate" } gix-sec = { version = "^0.9.0", path = "../gix-sec" } @@ -241,7 +244,7 @@ gix-transport = { version = "^0.35.0", path = "../gix-transport", optional = tru # Just to get the progress-tree feature prodash = { version = "26.2", optional = true, default-features = false, features = ["progress-tree"] } once_cell = "1.14.0" -signal-hook = { version = "0.3.9", default-features = false } +signal-hook = { version = "0.3.9", default-features = false, optional = true } thiserror = "1.0.26" log = "0.4.14" serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} diff --git a/gix/examples/interrupt-handler-allows-graceful-shutdown.rs b/gix/examples/interrupt-handler-allows-graceful-shutdown.rs index 89847fba46f..80f3d7eaf71 100644 --- a/gix/examples/interrupt-handler-allows-graceful-shutdown.rs +++ b/gix/examples/interrupt-handler-allows-graceful-shutdown.rs @@ -1,11 +1,14 @@ -use std::path::Path; - -use gix_tempfile::{AutoRemove, ContainingDirectory}; +#[cfg(not(feature = "interrupt"))] +fn main() -> anyhow::Result<()> { + anyhow::bail!("Needs 'interrupt' feature toggle to be enabled"); +} +#[cfg(feature = "interrupt")] fn main() -> anyhow::Result<()> { + use gix_tempfile::{AutoRemove, ContainingDirectory}; gix::interrupt::init_handler(1, || {})?; eprintln!("About to emit the first term signal"); - let tempfile_path = Path::new("example-file.tmp"); + let tempfile_path = std::path::Path::new("example-file.tmp"); let _keep_tempfile = gix_tempfile::mark_at(tempfile_path, ContainingDirectory::Exists, AutoRemove::Tempfile)?; signal_hook::low_level::raise(signal_hook::consts::SIGTERM)?; diff --git a/gix/examples/reversible-interrupt-handlers.rs b/gix/examples/reversible-interrupt-handlers.rs index 505eede63e7..0847299f87f 100644 --- a/gix/examples/reversible-interrupt-handlers.rs +++ b/gix/examples/reversible-interrupt-handlers.rs @@ -1,3 +1,9 @@ +#[cfg(not(feature = "interrupt"))] +fn main() -> anyhow::Result<()> { + anyhow::bail!("Needs 'interrupt' feature toggle to be enabled"); +} + +#[cfg(feature = "interrupt")] fn main() -> anyhow::Result<()> { { let _deregister_on_drop = gix::interrupt::init_handler(1, || {})?.auto_deregister(); diff --git a/gix/src/lib.rs b/gix/src/lib.rs index dcf9b8a08aa..10b59506b39 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -122,6 +122,7 @@ pub use gix_url::Url; pub use gix_utils as utils; pub use hash::{oid, ObjectId}; +#[cfg(feature = "interrupt")] pub mod interrupt; mod ext; diff --git a/gix/tests/interrupt.rs b/gix/tests/interrupt.rs index c84674867fc..1b130d301be 100644 --- a/gix/tests/interrupt.rs +++ b/gix/tests/interrupt.rs @@ -1,59 +1,62 @@ -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; - -use signal_hook::consts::SIGTERM; - -#[test] -fn multi_registration() -> gix_testtools::Result { - static V1: AtomicUsize = AtomicUsize::new(0); - static V2: AtomicBool = AtomicBool::new(false); - - let reg1 = gix::interrupt::init_handler(3, || { - V1.fetch_add(1, Ordering::SeqCst); - }) - .expect("succeeds"); - assert!(!gix::interrupt::is_triggered()); - assert_eq!(V1.load(Ordering::Relaxed), 0); - let reg2 = - gix::interrupt::init_handler(2, || V2.store(true, Ordering::SeqCst)).expect("multi-initialization is OK"); - assert!(!V2.load(Ordering::Relaxed)); - - signal_hook::low_level::raise(SIGTERM).expect("signal can be raised"); - assert!(gix::interrupt::is_triggered(), "this happens automatically"); - assert_eq!(V1.load(Ordering::Relaxed), 1, "the first trigger is invoked"); - assert!(!V2.load(Ordering::Relaxed), "the second trigger was ignored"); - - reg1.deregister()?; - signal_hook::low_level::raise(SIGTERM).expect("signal can be raised"); - assert_eq!(V1.load(Ordering::Relaxed), 2, "the first trigger is still invoked"); - - assert!(gix::interrupt::is_triggered(), "this happens automatically"); - // now the registration is actually removed. - reg2.with_reset(true).deregister()?; - assert!( - !gix::interrupt::is_triggered(), - "the deregistration succeeded and this is an optional side-effect" - ); - - let reg1 = gix::interrupt::init_handler(3, || { - V1.fetch_add(1, Ordering::SeqCst); - }) - .expect("succeeds"); - assert_eq!(V1.load(Ordering::Relaxed), 2, "nothing changed yet"); - let reg2 = - gix::interrupt::init_handler(2, || V2.store(true, Ordering::SeqCst)).expect("multi-initialization is OK"); - assert!(!V2.load(Ordering::Relaxed)); - - signal_hook::low_level::raise(SIGTERM).expect("signal can be raised"); - assert_eq!(V1.load(Ordering::Relaxed), 3, "the first trigger is invoked"); - assert!(!V2.load(Ordering::Relaxed), "the second trigger was ignored"); - - reg2.auto_deregister(); - reg1.with_reset(true).auto_deregister(); - - assert!( - !gix::interrupt::is_triggered(), - "the deregistration succeeded and this is an optional side-effect" - ); - - Ok(()) +#[cfg(feature = "interrupt")] +mod needs_feature { + use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + + use signal_hook::consts::SIGTERM; + + #[test] + fn multi_registration() -> gix_testtools::Result { + static V1: AtomicUsize = AtomicUsize::new(0); + static V2: AtomicBool = AtomicBool::new(false); + + let reg1 = gix::interrupt::init_handler(3, || { + V1.fetch_add(1, Ordering::SeqCst); + }) + .expect("succeeds"); + assert!(!gix::interrupt::is_triggered()); + assert_eq!(V1.load(Ordering::Relaxed), 0); + let reg2 = + gix::interrupt::init_handler(2, || V2.store(true, Ordering::SeqCst)).expect("multi-initialization is OK"); + assert!(!V2.load(Ordering::Relaxed)); + + signal_hook::low_level::raise(SIGTERM).expect("signal can be raised"); + assert!(gix::interrupt::is_triggered(), "this happens automatically"); + assert_eq!(V1.load(Ordering::Relaxed), 1, "the first trigger is invoked"); + assert!(!V2.load(Ordering::Relaxed), "the second trigger was ignored"); + + reg1.deregister()?; + signal_hook::low_level::raise(SIGTERM).expect("signal can be raised"); + assert_eq!(V1.load(Ordering::Relaxed), 2, "the first trigger is still invoked"); + + assert!(gix::interrupt::is_triggered(), "this happens automatically"); + // now the registration is actually removed. + reg2.with_reset(true).deregister()?; + assert!( + !gix::interrupt::is_triggered(), + "the deregistration succeeded and this is an optional side-effect" + ); + + let reg1 = gix::interrupt::init_handler(3, || { + V1.fetch_add(1, Ordering::SeqCst); + }) + .expect("succeeds"); + assert_eq!(V1.load(Ordering::Relaxed), 2, "nothing changed yet"); + let reg2 = + gix::interrupt::init_handler(2, || V2.store(true, Ordering::SeqCst)).expect("multi-initialization is OK"); + assert!(!V2.load(Ordering::Relaxed)); + + signal_hook::low_level::raise(SIGTERM).expect("signal can be raised"); + assert_eq!(V1.load(Ordering::Relaxed), 3, "the first trigger is invoked"); + assert!(!V2.load(Ordering::Relaxed), "the second trigger was ignored"); + + reg2.auto_deregister(); + reg1.with_reset(true).auto_deregister(); + + assert!( + !gix::interrupt::is_triggered(), + "the deregistration succeeded and this is an optional side-effect" + ); + + Ok(()) + } } diff --git a/justfile b/justfile index a262f14c273..be503bb193e 100755 --- a/justfile +++ b/justfile @@ -127,6 +127,7 @@ check: cargo check -p gix --no-default-features --features worktree-mutation --tests cargo check -p gix --no-default-features --features credentials --tests cargo check -p gix --no-default-features --features index --tests + cargo check -p gix --no-default-features --features interrupt --tests cargo check -p gix --no-default-features cargo check -p gix-odb --features serde cargo check --no-default-features --features max-control From 2b8d09f785f471aa12fc6793f0ea40c1f8d9ea4a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 16:29:05 +0200 Subject: [PATCH 21/23] feat: remove `log` dependency in favor of `gix-trace` --- Cargo.lock | 4 +--- gix-attributes/Cargo.toml | 2 +- gix-attributes/src/search/attributes.rs | 10 +++++----- gix-config/Cargo.toml | 1 - gix-config/src/file/init/from_paths.rs | 4 ++-- gix/Cargo.toml | 1 - gix/src/config/cache/incubate.rs | 4 ++-- 7 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c8f97b1e89..8d09ed59474 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,6 @@ dependencies = [ "gix-worktree-state", "gix-worktree-stream", "is_ci", - "log", "once_cell", "parking_lot", "prodash 26.2.1", @@ -1333,8 +1332,8 @@ dependencies = [ "gix-path 0.9.0", "gix-quote 0.4.7", "gix-testtools", + "gix-trace 0.1.3", "kstring", - "log", "serde", "smallvec", "thiserror", @@ -1402,7 +1401,6 @@ dependencies = [ "gix-path 0.9.0", "gix-ref 0.35.0", "gix-sec 0.9.0", - "log", "memchr", "once_cell", "serde", diff --git a/gix-attributes/Cargo.toml b/gix-attributes/Cargo.toml index 31da9af12e1..4d55c21a112 100644 --- a/gix-attributes/Cargo.toml +++ b/gix-attributes/Cargo.toml @@ -20,6 +20,7 @@ serde = ["dep:serde", "bstr/serde", "gix-glob/serde", "kstring/serde"] gix-path = { version = "^0.9.0", path = "../gix-path" } gix-quote = { version = "^0.4.7", path = "../gix-quote" } gix-glob = { version = "^0.11.0", path = "../gix-glob" } +gix-trace = { version = "^0.1.3", path = "../gix-trace" } bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"]} smallvec = "1.10.0" @@ -27,7 +28,6 @@ kstring = "2.0.0" unicode-bom = "2.0.2" thiserror = "1.0.26" serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} -log = "0.4.17" document-features = { version = "0.2.1", optional = true } diff --git a/gix-attributes/src/search/attributes.rs b/gix-attributes/src/search/attributes.rs index 2b48d1ed3be..88cc8356c27 100644 --- a/gix-attributes/src/search/attributes.rs +++ b/gix-attributes/src/search/attributes.rs @@ -124,7 +124,7 @@ impl Search { impl Pattern for Attributes { type Value = Value; - fn bytes_to_patterns(bytes: &[u8], source: &std::path::Path) -> Vec> { + fn bytes_to_patterns(bytes: &[u8], _source: &std::path::Path) -> Vec> { fn into_owned_assignments<'a>( attrs: impl Iterator, crate::name::Error>>, ) -> Option { @@ -138,8 +138,8 @@ impl Pattern for Attributes { .collect::>(); match res { Ok(res) => Some(res), - Err(err) => { - log::warn!("{}", err); + Err(_err) => { + gix_trace::warn!("{}", _err); None } } @@ -148,8 +148,8 @@ impl Pattern for Attributes { crate::parse(bytes) .filter_map(|res| match res { Ok(pattern) => Some(pattern), - Err(err) => { - log::warn!("{}: {}", source.display(), err); + Err(_err) => { + gix_trace::warn!("{}: {}", _source.display(), _err); None } }) diff --git a/gix-config/Cargo.toml b/gix-config/Cargo.toml index d02c2615347..8fedd221464 100644 --- a/gix-config/Cargo.toml +++ b/gix-config/Cargo.toml @@ -24,7 +24,6 @@ gix-sec = { version = "^0.9.0", path = "../gix-sec" } gix-ref = { version = "^0.35.0", path = "../gix-ref" } gix-glob = { version = "^0.11.0", path = "../gix-glob" } -log = "0.4.17" winnow = { version = "0.5.14", features = ["simd"] } memchr = "2" thiserror = "1.0.26" diff --git a/gix-config/src/file/init/from_paths.rs b/gix-config/src/file/init/from_paths.rs index 5d092f88a7c..c0412a95f70 100644 --- a/gix-config/src/file/init/from_paths.rs +++ b/gix-config/src/file/init/from_paths.rs @@ -92,7 +92,7 @@ impl File<'static> { Err(err) => { let err = Error::Io { source: err, path }; if options.ignore_io_errors { - log::warn!("ignoring: {err:#?}"); + gix_features::trace::warn!("ignoring: {err:#?}"); continue; } else { return Err(err); @@ -104,7 +104,7 @@ impl File<'static> { Ok(_) => {} Err(err) => { if options.ignore_io_errors { - log::warn!( + gix_features::trace::warn!( "ignoring: {:#?}", Error::Io { source: err, diff --git a/gix/Cargo.toml b/gix/Cargo.toml index a04fb1fa831..cf72d04ad80 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -246,7 +246,6 @@ prodash = { version = "26.2", optional = true, default-features = false, feature once_cell = "1.14.0" signal-hook = { version = "0.3.9", default-features = false, optional = true } thiserror = "1.0.26" -log = "0.4.14" 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/incubate.rs b/gix/src/config/cache/incubate.rs index 44c537b50dd..cf7c5dcdf82 100644 --- a/gix/src/config/cache/incubate.rs +++ b/gix/src/config/cache/incubate.rs @@ -102,7 +102,7 @@ fn load_config( path: config_path, }; if lenient { - log::warn!("ignoring: {err:#?}"); + gix_trace::warn!("ignoring: {err:#?}"); return Ok(gix_config::File::new(metadata)); } else { return Err(err); @@ -117,7 +117,7 @@ fn load_config( path: config_path, }; if lenient { - log::warn!("ignoring: {err:#?}"); + gix_trace::warn!("ignoring: {err:#?}"); buf.clear(); } else { return Err(err); From 9d33e2f5c6a1c370654ef0db90b29c0a023dcf3d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 8 Sep 2023 16:38:03 +0200 Subject: [PATCH 22/23] optimize internal `gix` usage for faster compile time Even though there is a chance that overall, it will compile multiple different versions of the same thing so it's not actually faster. But let's not think too much about that. --- gix-config/tests/Cargo.toml | 2 +- gix-index/tests/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gix-config/tests/Cargo.toml b/gix-config/tests/Cargo.toml index 43488f1f72f..8fc8775d239 100644 --- a/gix-config/tests/Cargo.toml +++ b/gix-config/tests/Cargo.toml @@ -17,7 +17,7 @@ path = "config.rs" [dev-dependencies] gix-config = { path = ".."} gix-testtools = { path = "../../tests/tools"} -gix = { path = "../../gix" } +gix = { path = "../../gix", default-features = false } gix-ref = { path = "../../gix-ref" } gix-path = { path = "../../gix-path" } gix-sec = { path = "../../gix-sec" } diff --git a/gix-index/tests/Cargo.toml b/gix-index/tests/Cargo.toml index 4d74a4ad13e..4d15e5aed4f 100644 --- a/gix-index/tests/Cargo.toml +++ b/gix-index/tests/Cargo.toml @@ -20,7 +20,7 @@ gix-features-parallel = ["gix-features/parallel"] gix-index = { path = ".." } gix-features = { path = "../../gix-features", features = ["rustsha1", "progress"] } gix-testtools = { path = "../../tests/tools"} -gix = { path = "../../gix"} +gix = { path = "../../gix", default-features = false, features = ["index"] } gix-hash = { path = "../../gix-hash"} filetime = "0.2.15" bstr = { version = "1.3.0", default-features = false } From 805b8aa74b064b7aa08d87094a994bb8c7aae6ed Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 7 Sep 2023 16:44:23 +0200 Subject: [PATCH 23/23] adapt to changes in `gix` --- Cargo.lock | 1 + Cargo.toml | 2 +- gitoxide-core/Cargo.toml | 4 ++-- gitoxide-core/src/repository/exclude.rs | 6 +++--- gix-packetline/Cargo.toml | 1 + gix/src/lib.rs | 2 +- tests/journey/gix.sh | 4 ++-- 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d09ed59474..8b09281548f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1957,6 +1957,7 @@ dependencies = [ "futures-lite", "gix-hash 0.12.0", "gix-odb", + "gix-pack", "maybe-async", "pin-project-lite", "serde", diff --git a/Cargo.toml b/Cargo.toml index 3bfc1bda5ee..f997bea91cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ max = ["max-control", "fast", "gitoxide-core-blocking-client", "http-client-curl max-pure = ["max-control", "gix-features/rustsha1", "gix-features/zlib-rust-backend", "http-client-reqwest", "gitoxide-core-blocking-client" ] ## Like `max`, but with more control for configuration. See the *Package Maintainers* headline for more information. -max-control = ["tracing", "fast-safe", "pretty-cli", "gitoxide-core-tools-query", "gitoxide-core-tools-corpus", "gitoxide-core-tools", "prodash-render-line", "prodash-render-tui", "prodash/render-line-autoconfigure", "gix/regex" ] +max-control = ["tracing", "fast-safe", "pretty-cli", "gitoxide-core-tools-query", "gitoxide-core-tools-corpus", "gitoxide-core-tools", "prodash-render-line", "prodash-render-tui", "prodash/render-line-autoconfigure", "gix/revparse-regex" ] ## All of the good stuff, with less fanciness for smaller binaries. ## diff --git a/gitoxide-core/Cargo.toml b/gitoxide-core/Cargo.toml index 2b1873d704d..c5941d43342 100644 --- a/gitoxide-core/Cargo.toml +++ b/gitoxide-core/Cargo.toml @@ -44,8 +44,8 @@ serde = ["gix/serde", "dep:serde_json", "dep:serde", "bytesize/serde"] [dependencies] # deselect everything else (like "performance") as this should be controllable by the parent application. -gix = { version = "^0.53.0", path = "../gix", default-features = false } -gix-pack-for-configuration-only = { package = "gix-pack", version = "^0.41.0", path = "../gix-pack", default-features = false, features = ["pack-cache-lru-dynamic", "pack-cache-lru-static"] } +gix = { version = "^0.53.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt"] } +gix-pack-for-configuration-only = { package = "gix-pack", version = "^0.41.0", path = "../gix-pack", default-features = false, features = ["pack-cache-lru-dynamic", "pack-cache-lru-static", "generate", "streaming-input"] } gix-transport-configuration-only = { package = "gix-transport", version = "^0.35.0", path = "../gix-transport", default-features = false } gix-archive-for-configuration-only = { package = "gix-archive", version = "^0.3.0", path = "../gix-archive", optional = true, features = ["tar", "tar_gz"] } serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } diff --git a/gitoxide-core/src/repository/exclude.rs b/gitoxide-core/src/repository/exclude.rs index f3ed2e3595a..b73af2a8767 100644 --- a/gitoxide-core/src/repository/exclude.rs +++ b/gitoxide-core/src/repository/exclude.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::io; use anyhow::{anyhow, bail}; -use gix::{bstr::BStr, prelude::FindExt}; +use gix::bstr::BStr; use crate::{repository::PathsOrPatterns, OutputFormat}; @@ -49,7 +49,7 @@ pub fn query( .metadata() .ok() .map(|m| m.is_dir()); - let entry = cache.at_entry(path.as_slice(), is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?; + let entry = cache.at_entry(path.as_slice(), is_dir)?; let match_ = entry .matching_exclude_pattern() .and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m)); @@ -68,7 +68,7 @@ pub fn query( .index_entries_with_paths(&index) .ok_or_else(|| anyhow!("Pathspec didn't yield any entry"))? { - let entry = cache.at_entry(path, Some(false), |oid, buf| repo.objects.find_blob(oid, buf))?; + let entry = cache.at_entry(path, Some(false))?; let match_ = entry .matching_exclude_pattern() .and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m)); diff --git a/gix-packetline/Cargo.toml b/gix-packetline/Cargo.toml index 863f4ee4c9b..0cd8753d1e1 100644 --- a/gix-packetline/Cargo.toml +++ b/gix-packetline/Cargo.toml @@ -52,6 +52,7 @@ document-features = { version = "0.2.0", optional = true } [dev-dependencies] gix-odb = { path = "../gix-odb" } +gix-pack-for-configuration = { package = "gix-pack", path = "../gix-pack", default-features = false, features = ["streaming-input"] } gix-hash = { path = "../gix-hash" } async-std = { version = "1.9.0", features = ["attributes"] } maybe-async = "0.2.6" diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 10b59506b39..27dc8154772 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -60,7 +60,7 @@ //! Entering `git2` into the search field will also surface all methods with such annotations. //! //! What follows is a list of methods you might be missing, along with workarounds if available. -//! * [`git2::Repository::open_bare()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_bare) ➡ ❌ - use [`open()`] and discard it is not bare. +//! * [`git2::Repository::open_bare()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_bare) ➡ ❌ - use [`open()`] and discard if it is not bare. //! * [`git2::build::CheckoutBuilder::disable_filters()](https://docs.rs/git2/*/git2/build/struct.CheckoutBuilder.html#method.disable_filters) ➡ ❌ *(filters are always applied during checkouts)* //! * [`git2::Repository::submodule_status()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.submodule_status) ➡ [`Submodule::state()`] - status provides more information and conveniences though, and an actual worktree status isn't performed. //! diff --git a/tests/journey/gix.sh b/tests/journey/gix.sh index d7d5dfd6050..9a4165677f9 100644 --- a/tests/journey/gix.sh +++ b/tests/journey/gix.sh @@ -38,7 +38,7 @@ title '`gix` crate' (when "running the example program to check order of signal handlers" it "fails as the process aborts" && { - expect_run $ABORTED cargo run --no-default-features --example interrupt-handler-allows-graceful-shutdown + expect_run $ABORTED cargo run --no-default-features --features interrupt --example interrupt-handler-allows-graceful-shutdown } it "cleans up the tempfile it created" && { expect_run $WITH_FAILURE test -e "example-file.tmp" @@ -46,7 +46,7 @@ title '`gix` crate' ) (when "running the example program to check reversibility of signal handlers" it "fails as the process aborts" && { - expect_run $ABORTED cargo run --no-default-features --example reversible-interrupt-handlers + expect_run $ABORTED cargo run --no-default-features --features interrupt --example reversible-interrupt-handlers } ) )